* Moved TexturePipeline into libOpenMetaverse (still needs to be instantiated separately)

* Moved TextureCache into its own file
* New version of Periscope, major improvements. Check the Simian.ini file for usage, and change the const values in Periscope.cs
* Lots of miscellaneous cleanups and improvements in Simian

git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2383 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2008-12-17 03:49:42 +00:00
parent ee6211550e
commit 194752abc1
19 changed files with 1803 additions and 842 deletions

View File

@@ -62,6 +62,12 @@ namespace Simian.Extensions
return agent.Animations.Remove(animID);
}
public bool ClearAnimations(Agent agent)
{
agent.Animations.Clear();
return true;
}
public void SendAnimations(Agent agent)
{
AvatarAnimationPacket sendAnim = new AvatarAnimationPacket();
@@ -104,7 +110,7 @@ namespace Simian.Extensions
// Remove the avatar from the scene
SimulationObject obj;
if (server.Scene.TryGetObject(agent.AgentID, out obj))
server.Scene.ObjectRemove(this, obj);
server.Scene.ObjectRemove(this, obj.Prim.LocalID);
else
Logger.Log("Disconnecting an agent that is not in the scene", Helpers.LogLevel.Warning);

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Packets;
namespace Simian.Extensions
@@ -14,14 +13,16 @@ namespace Simian.Extensions
public const int IMAGE_PACKET_SIZE = 1000;
public AssetTexture Texture;
public Agent Agent;
public int DiscardLevel;
public float Priority;
public int CurrentPacket;
public int StopPacket;
public ImageDownload(AssetTexture texture, int discardLevel, float priority, int packet)
public ImageDownload(AssetTexture texture, Agent agent, int discardLevel, float priority, int packet)
{
Texture = texture;
Agent = agent;
Update(discardLevel, priority, packet);
}
@@ -35,9 +36,28 @@ namespace Simian.Extensions
public void Update(int discardLevel, float priority, int packet)
{
Priority = priority;
DiscardLevel = Utils.Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1);
StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End);
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
if (Texture != null)
{
if (Texture.LayerInfo != null && Texture.LayerInfo.Length > 0)
{
DiscardLevel = Utils.Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1);
StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End);
}
else
{
DiscardLevel = 0;
StopPacket = GetPacketForBytePosition(Texture.AssetData.Length);
}
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
}
else
{
DiscardLevel = discardLevel;
Priority = priority;
CurrentPacket = packet;
}
}
/// <summary>
@@ -94,7 +114,7 @@ namespace Simian.Extensions
{
this.server = server;
server.UDP.RegisterPacketCallback(PacketType.RequestImage, new PacketCallback(RequestImageHandler));
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
}
public void Stop()
@@ -151,91 +171,7 @@ namespace Simian.Extensions
Asset asset;
if (server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
{
download = new ImageDownload((AssetTexture)asset, block.DiscardLevel, block.DownloadPriority,
(int)block.Packet);
Logger.DebugLog(String.Format(
"Starting new download for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
block.Image, block.DiscardLevel, block.DownloadPriority, download.CurrentPacket, download.StopPacket,
download.TexturePacketCount()));
// Send initial data
ImageDataPacket data = new ImageDataPacket();
data.ImageID.Codec = (byte)ImageCodec.J2C;
data.ImageID.ID = download.Texture.AssetID;
data.ImageID.Packets = (ushort)download.TexturePacketCount();
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
// The first bytes of the image are always sent in the ImageData packet
data.ImageData = new ImageDataPacket.ImageDataBlock();
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
try
{
data.ImageData.Data = new byte[imageDataSize];
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
}
catch (Exception ex)
{
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
// Check if ImagePacket packets need to be sent to complete this transfer
if (download.CurrentPacket <= download.StopPacket)
{
// Insert this download into the dictionary
lock (CurrentDownloads)
CurrentDownloads[block.Image] = download;
// Send all of the remaining packets
ThreadPool.QueueUserWorkItem(
delegate(object obj)
{
while (download.CurrentPacket <= download.StopPacket)
{
if (download.Priority == 0.0f && download.DiscardLevel == -1)
break;
lock (download)
{
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
ImagePacketPacket transfer = new ImagePacketPacket();
transfer.ImageID.ID = block.Image;
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
transfer.ImageData.Data = new byte[imagePacketSize];
try
{
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
transfer.ImageData.Data, 0, imagePacketSize);
}
catch (Exception ex)
{
Logger.Log(String.Format(
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
imagePacketSize), Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
++download.CurrentPacket;
}
}
Logger.DebugLog("Completed image transfer for " + block.Image.ToString());
// Transfer is complete, remove the reference
lock (CurrentDownloads)
CurrentDownloads.Remove(block.Image);
}
);
}
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
}
else
{
@@ -248,5 +184,93 @@ namespace Simian.Extensions
}
}
}
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
{
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
Logger.DebugLog(String.Format(
"Starting new download for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket,
download.TexturePacketCount()));
// Send initial data
ImageDataPacket data = new ImageDataPacket();
data.ImageID.Codec = (byte)ImageCodec.J2C;
data.ImageID.ID = download.Texture.AssetID;
data.ImageID.Packets = (ushort)download.TexturePacketCount();
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
// The first bytes of the image are always sent in the ImageData packet
data.ImageData = new ImageDataPacket.ImageDataBlock();
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
try
{
data.ImageData.Data = new byte[imageDataSize];
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
}
catch (Exception ex)
{
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
// Check if ImagePacket packets need to be sent to complete this transfer
if (download.CurrentPacket <= download.StopPacket)
{
// Insert this download into the dictionary
lock (CurrentDownloads)
CurrentDownloads[texture.AssetID] = download;
// Send all of the remaining packets
ThreadPool.QueueUserWorkItem(
delegate(object obj)
{
while (download.CurrentPacket <= download.StopPacket)
{
if (download.Priority == 0.0f && download.DiscardLevel == -1)
break;
lock (download)
{
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
ImagePacketPacket transfer = new ImagePacketPacket();
transfer.ImageID.ID = texture.AssetID;
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
transfer.ImageData.Data = new byte[imagePacketSize];
try
{
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
transfer.ImageData.Data, 0, imagePacketSize);
}
catch (Exception ex)
{
Logger.Log(String.Format(
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
imagePacketSize), Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
++download.CurrentPacket;
}
}
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
// Transfer is complete, remove the reference
lock (CurrentDownloads)
CurrentDownloads.Remove(texture.AssetID);
}
);
}
}
}
}

View File

@@ -310,7 +310,6 @@ namespace Simian.Extensions
if (animsChanged)
server.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
// static acceleration when any control is held, otherwise none
@@ -339,7 +338,6 @@ namespace Simian.Extensions
else if (agent.Avatar.Position.Y > 255) agent.Avatar.Position.Y = 255f;
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
}
}
}
@@ -403,6 +401,5 @@ namespace Simian.Extensions
//Logger.Log(String.Format("Agent wants to set height={0}, width={1}",
// heightWidth.HeightWidthBlock.Height, heightWidth.HeightWidthBlock.Width), Helpers.LogLevel.Info);
}
}
}

View File

@@ -440,7 +440,7 @@ namespace Simian.Extensions
data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd);
data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow);
server.Scene.ObjectModify(this, obj, data);
server.Scene.ObjectModify(this, obj.Prim.LocalID, data);
}
else
{
@@ -569,7 +569,7 @@ namespace Simian.Extensions
server.Inventory.CreateItem(agent.AgentID, obj.Prim.Properties.Name, obj.Prim.Properties.Description, InventoryType.Object,
AssetType.Object, obj.Prim.ID, trash.ID, PermissionMask.All, PermissionMask.All, agent.AgentID,
obj.Prim.Properties.CreatorID, derez.AgentBlock.TransactionID, 0, true);
server.Scene.ObjectRemove(this, obj);
server.Scene.ObjectRemove(this, obj.Prim.LocalID);
Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID));
}
@@ -606,6 +606,7 @@ namespace Simian.Extensions
for (int i = 0; i < update.ObjectData.Length; i++)
{
bool scaled = false;
MultipleObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[i];
SimulationObject obj;
@@ -630,6 +631,7 @@ namespace Simian.Extensions
}
if ((type & UpdateType.Scale) != 0)
{
scaled = true;
scale = new Vector3(block.Data, pos);
pos += 12;
@@ -637,12 +639,19 @@ namespace Simian.Extensions
bool uniform = ((type & UpdateType.Uniform) != 0);
}
// Although the object has already been modified, we need
// to inform the scene manager of the changes so they are
// sent to clients and propagated to other extensions
server.Scene.ObjectTransform(this, obj, position, rotation,
obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity,
scale);
if (scaled)
{
obj.Prim.Position = position;
obj.Prim.Rotation = rotation;
obj.Prim.Scale = scale;
server.Scene.ObjectAdd(this, obj, PrimFlags.None);
}
else
{
server.Scene.ObjectTransform(this, obj.Prim.LocalID, position, rotation,
obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity);
}
}
else
{

View File

@@ -12,45 +12,54 @@ namespace Simian.Extensions
const string FIRST_NAME = "Testing";
const string LAST_NAME = "Anvil";
const string PASSWORD = "testinganvil";
const string SIMULATOR = "Svarga";
public Agent MasterAgent = null;
Simian server;
GridClient client;
PeriscopeImageDelivery imageDelivery;
PeriscopeMovement movement;
object loginLock = new object();
public Periscope()
{
client = new GridClient();
client.Settings.SEND_AGENT_UPDATES = false;
client.Objects.OnNewPrim += new OpenMetaverse.ObjectManager.NewPrimCallback(Objects_OnNewPrim);
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
}
public void Start(Simian server)
{
this.server = server;
client = new GridClient();
Settings.LOG_LEVEL = Helpers.LogLevel.Info;
client.Settings.MULTIPLE_SIMS = false;
client.Settings.SEND_AGENT_UPDATES = false;
client.Network.OnCurrentSimChanged += new NetworkManager.CurrentSimChangedCallback(Network_OnCurrentSimChanged);
client.Objects.OnNewPrim += new OpenMetaverse.ObjectManager.NewPrimCallback(Objects_OnNewPrim);
client.Objects.OnNewFoliage += new OpenMetaverse.ObjectManager.NewFoliageCallback(Objects_OnNewFoliage);
client.Objects.OnNewAvatar += new OpenMetaverse.ObjectManager.NewAvatarCallback(Objects_OnNewAvatar);
client.Objects.OnNewAttachment += new OpenMetaverse.ObjectManager.NewAttachmentCallback(Objects_OnNewAttachment);
client.Objects.OnObjectKilled += new OpenMetaverse.ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
client.Objects.OnObjectUpdated += new OpenMetaverse.ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);
client.Avatars.OnAvatarAppearance += new OpenMetaverse.AvatarManager.AvatarAppearanceCallback(Avatars_OnAvatarAppearance);
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
client.Self.OnChat += new AgentManager.ChatCallback(Self_OnChat);
client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler);
client.Network.RegisterCallback(PacketType.RegionHandshake, RegionHandshakeHandler);
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
// Start the login process
Thread loginThread = new Thread(new ThreadStart(
delegate()
{
client.Network.Login(FIRST_NAME, LAST_NAME, PASSWORD, "Simian Periscope", "1.0.0");
if (client.Network.Connected)
{
Logger.Log("Periscope is connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
}
else
{
Logger.Log("Periscope failed to connect to the foreign grid: " + client.Network.LoginErrorKey, Helpers.LogLevel.Error);
}
}
));
loginThread.Start();
imageDelivery = new PeriscopeImageDelivery(server, client);
movement = new PeriscopeMovement(server, this);
}
public void Stop()
{
movement.Stop();
imageDelivery.Stop();
if (client.Network.Connected)
client.Network.Logout();
}
@@ -58,22 +67,211 @@ namespace Simian.Extensions
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
{
SimulationObject simObj = new SimulationObject(prim, server);
server.Scene.ObjectAdd(this, simObj, prim.Flags);
server.Scene.ObjectAdd(this, simObj, PrimFlags.None);
}
void Objects_OnNewAttachment(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
{
SimulationObject simObj = new SimulationObject(prim, server);
server.Scene.ObjectAdd(this, simObj, PrimFlags.None);
}
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
{
// Add the avatar to both the agents list and the scene objects
Agent agent = new Agent();
agent.AgentID = avatar.ID;
agent.Avatar = avatar;
agent.CurrentRegionHandle = server.RegionHandle;
agent.FirstName = avatar.FirstName;
agent.LastName = avatar.LastName;
lock (server.Agents)
server.Agents[agent.AgentID] = agent;
SimulationObject simObj = new SimulationObject(avatar, server);
server.Scene.ObjectAdd(this, simObj, avatar.Flags);
}
void Objects_OnNewFoliage(Simulator simulator, Primitive foliage, ulong regionHandle, ushort timeDilation)
{
SimulationObject simObj = new SimulationObject(foliage, server);
server.Scene.ObjectAdd(this, simObj, foliage.Flags);
}
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
{
server.Scene.ObjectTransform(this, update.LocalID, update.Position, update.Rotation, update.Velocity,
update.Acceleration, update.AngularVelocity);
if (update.LocalID == client.Self.LocalID)
{
MasterAgent.Avatar.Acceleration = update.Acceleration;
MasterAgent.Avatar.AngularVelocity = update.AngularVelocity;
MasterAgent.Avatar.CollisionPlane = update.CollisionPlane;
MasterAgent.Avatar.Position = update.Position;
MasterAgent.Avatar.Rotation = update.Rotation;
MasterAgent.Avatar.Velocity = update.Velocity;
if (update.Textures != null)
MasterAgent.Avatar.Textures = update.Textures;
}
}
void Objects_OnObjectKilled(Simulator simulator, uint objectID)
{
server.Scene.ObjectRemove(this, objectID);
}
void Avatars_OnAvatarAppearance(UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture,
Primitive.TextureEntryFace[] faceTextures, List<byte> visualParams)
{
Agent agent;
if (server.Agents.TryGetValue(avatarID, out agent))
{
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
te.FaceTextures = faceTextures;
byte[] vp = (visualParams != null && visualParams.Count > 1 ? visualParams.ToArray() : null);
Logger.Log("[Periscope] Updating foreign avatar appearance for " + avatarID.ToString(), Helpers.LogLevel.Info);
server.Scene.AvatarAppearance(this, agent, te, vp);
}
else
{
Logger.Log("[Periscope] Received a foreign avatar appearance for an unknown avatar", Helpers.LogLevel.Warning);
}
}
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
{
//throw new NotImplementedException();
// TODO: When Simian gets a terrain editing interface, switch this over to
// edit the scene heightmap instead of sending packets direct to clients
int[] patches = new int[1];
patches[0] = (y * 16) + x;
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(data, x, y);
server.UDP.BroadcastPacket(layer, PacketCategory.Terrain);
}
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
string fromName, UUID id, UUID ownerid, Vector3 position)
{
// TODO: Inject chat into the Scene instead of relaying it
ChatFromSimulatorPacket chat = new ChatFromSimulatorPacket();
chat.ChatData.Audible = (byte)ChatAudibleLevel.Fully;
chat.ChatData.ChatType = (byte)type;
chat.ChatData.OwnerID = ownerid;
chat.ChatData.SourceID = id;
chat.ChatData.SourceType = (byte)sourceType;
chat.ChatData.Position = position;
chat.ChatData.FromName = Utils.StringToBytes(fromName);
chat.ChatData.Message = Utils.StringToBytes(message);
server.UDP.BroadcastPacket(chat, PacketCategory.Transaction);
}
void Network_OnCurrentSimChanged(Simulator PreviousSimulator)
{
Logger.Log("[Periscope] Sending bot appearance", Helpers.LogLevel.Info);
client.Appearance.SetPreviousAppearance(false);
}
void AvatarAnimationHandler(Packet packet, Simulator simulator)
{
AvatarAnimationPacket animations = (AvatarAnimationPacket)packet;
Agent agent;
if (server.Agents.TryGetValue(animations.Sender.ID, out agent))
{
agent.Animations.Clear();
for (int i = 0; i < animations.AnimationList.Length; i++)
{
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
agent.Animations.Add(block.AnimID, block.AnimSequenceID);
}
server.Avatars.SendAnimations(agent);
}
if (animations.Sender.ID == client.Self.AgentID)
{
MasterAgent.Animations.Clear();
for (int i = 0; i < animations.AnimationList.Length; i++)
{
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
MasterAgent.Animations.Add(block.AnimID, block.AnimSequenceID);
}
server.Avatars.SendAnimations(MasterAgent);
}
}
void RegionHandshakeHandler(Packet packet, Simulator simulator)
{
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.AgentID : UUID.Zero);
// TODO: Need more methods to manipulate the scene so we can apply these properties.
// Right now this only gets sent out to people who are logged in when the master avatar
// is already logged in
server.UDP.BroadcastPacket(handshake, PacketCategory.Transaction);
}
#region Simian client packet handlers
void ChatFromViewerHandler(Packet packet, Agent agent)
{
ChatFromViewerPacket chat = (ChatFromViewerPacket)packet;
// Forward chat from the viewer to the foreign simulator
string message = String.Format("<{0} {1}> {2}", agent.FirstName, agent.LastName,
Utils.BytesToString(chat.ChatData.Message));
client.Self.Chat(message, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
// Forward AgentUpdate packets with the AgentID/SessionID set to the bots ID
update.AgentData.AgentID = client.Self.AgentID;
update.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(update);
if (MasterAgent == null)
{
lock (loginLock)
{
// Double-checked locking to avoid hitting the loginLock each time
if (MasterAgent == null &&
server.Agents.TryGetValue(update.AgentData.AgentID, out MasterAgent))
{
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
MasterAgent.FirstName, MasterAgent.LastName), Helpers.LogLevel.Info);
LoginParams login = client.Network.DefaultLoginParams(FIRST_NAME, LAST_NAME, PASSWORD, "Simian Periscope",
"1.0.0");
login.Start = NetworkManager.StartLocation(SIMULATOR, 128, 128, 128);
client.Network.Login(login);
if (client.Network.Connected)
Logger.Log("[Periscope] Connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
else
Logger.Log("[Periscope] Failed to connect to the foreign grid: " + client.Network.LoginErrorKey, Helpers.LogLevel.Error);
}
}
}
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.AgentID)
{
// Forward AgentUpdate packets with the AgentID/SessionID set to the bots ID
update.AgentData.AgentID = client.Self.AgentID;
update.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(update);
}
}
#endregion Simian client packet handlers
}
}

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class PeriscopeImageDelivery
{
Simian server;
GridClient client;
TexturePipeline pipeline;
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
public PeriscopeImageDelivery(Simian server, GridClient client)
{
this.server = server;
this.client = client;
pipeline = new TexturePipeline(client, 10);
pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
}
public void Stop()
{
pipeline.Shutdown();
}
void RequestImageHandler(Packet packet, Agent agent)
{
RequestImagePacket request = (RequestImagePacket)packet;
for (int i = 0; i < request.RequestImage.Length; i++)
{
RequestImagePacket.RequestImageBlock block = request.RequestImage[i];
ImageDownload download;
bool downloadFound = currentDownloads.TryGetValue(block.Image, out download);
if (downloadFound)
{
lock (download)
{
if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
Logger.DebugLog(String.Format("Image download {0} is aborting", block.Image));
// Update download
download.Update(block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
}
}
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
{
// Aborting a download we are not tracking, ignore
}
else
{
bool bake = ((ImageType)block.Type == ImageType.Baked);
// New download, check if we have this image
Asset asset;
if (server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
{
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
}
else
{
// We don't have this texture, add it to the download queue and see if the bot can get it for us
download = new ImageDownload(null, agent, block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
lock (currentDownloads)
currentDownloads[block.Image] = download;
pipeline.RequestTexture(block.Image, (ImageType)block.Type);
}
}
}
}
void pipeline_OnDownloadFinished(UUID id, bool success)
{
ImageDownload download;
if (currentDownloads.TryGetValue(id, out download))
{
lock (currentDownloads)
currentDownloads.Remove(id);
if (success)
{
// Set the texture to the downloaded texture data
AssetTexture texture = new AssetTexture(id, pipeline.GetTextureToRender(id).AssetData);
download.Texture = texture;
pipeline.RemoveFromPipeline(id);
// Store this texture in the local asset store for later
server.Assets.StoreAsset(texture);
SendTexture(download.Agent, download.Texture, download.DiscardLevel, download.CurrentPacket, download.Priority);
}
else
{
Logger.Log("[Periscope] Failed to download texture " + id.ToString(), Helpers.LogLevel.Warning);
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
notfound.ImageID.ID = id;
server.UDP.SendPacket(download.Agent.AgentID, notfound, PacketCategory.Texture);
}
}
else
{
Logger.Log("[Periscope] Pipeline downloaded a texture we're not tracking, " + id.ToString(), Helpers.LogLevel.Warning);
}
}
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
{
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
Logger.DebugLog(String.Format(
"[Periscope] Starting new texture transfer for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket, download.TexturePacketCount()));
// Send initial data
ImageDataPacket data = new ImageDataPacket();
data.ImageID.Codec = (byte)ImageCodec.J2C;
data.ImageID.ID = download.Texture.AssetID;
data.ImageID.Packets = (ushort)download.TexturePacketCount();
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
// The first bytes of the image are always sent in the ImageData packet
data.ImageData = new ImageDataPacket.ImageDataBlock();
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
try
{
data.ImageData.Data = new byte[imageDataSize];
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
}
catch (Exception ex)
{
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
// Check if ImagePacket packets need to be sent to complete this transfer
if (download.CurrentPacket <= download.StopPacket)
{
// Insert this download into the dictionary
lock (currentDownloads)
currentDownloads[texture.AssetID] = download;
// Send all of the remaining packets
ThreadPool.QueueUserWorkItem(
delegate(object obj)
{
while (download.CurrentPacket <= download.StopPacket)
{
if (download.Priority == 0.0f && download.DiscardLevel == -1)
break;
lock (download)
{
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
ImagePacketPacket transfer = new ImagePacketPacket();
transfer.ImageID.ID = texture.AssetID;
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
transfer.ImageData.Data = new byte[imagePacketSize];
try
{
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
transfer.ImageData.Data, 0, imagePacketSize);
}
catch (Exception ex)
{
Logger.Log(String.Format(
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
imagePacketSize), Helpers.LogLevel.Error);
}
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
++download.CurrentPacket;
}
}
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
// Transfer is complete, remove the reference
lock (currentDownloads)
currentDownloads.Remove(texture.AssetID);
}
);
}
}
}
}

View File

@@ -0,0 +1,371 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian.Extensions
{
public class PeriscopeMovement
{
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
const float GRAVITY = 9.8f; //meters/sec
const float WALK_SPEED = 3f; //meters/sec
const float RUN_SPEED = 5f; //meters/sec
const float FLY_SPEED = 10f; //meters/sec
const float FALL_DELAY = 0.33f; //seconds before starting animation
const float FALL_FORGIVENESS = 0.25f; //fall buffer in meters
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec (no clue why this is so high)
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
const float SQRT_TWO = 1.41421356f;
Simian server;
Periscope periscope;
Timer updateTimer;
long lastTick;
public int LastTick
{
get { return (int)Interlocked.Read(ref lastTick); }
set { Interlocked.Exchange(ref lastTick, value); }
}
public PeriscopeMovement(Simian server, Periscope periscope)
{
this.server = server;
this.periscope = periscope;
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
LastTick = Environment.TickCount;
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
}
public void Stop()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
}
void UpdateTimer_Elapsed(object sender)
{
int tick = Environment.TickCount;
float seconds = (float)((tick - LastTick) / 1000f);
LastTick = tick;
lock (server.Agents)
{
foreach (Agent agent in server.Agents.Values)
{
// Don't handle movement for the master agent or foreign agents
if (agent != periscope.MasterAgent && agent.SessionID != UUID.Zero)
{
bool animsChanged = false;
// Create forward and left vectors from the current avatar rotation
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Rotation);
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
// Check control flags
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
// direction in which the avatar is trying to move
Vector3 move = Vector3.Zero;
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
if (heldUp) { move.Z += 1; }
if (heldDown) { move.Z -= 1; }
// is the avatar trying to move?
bool moving = move != Vector3.Zero;
bool jumping = agent.TickJump != 0;
// 2-dimensional speed multipler
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
if ((heldForward || heldBack) && (heldLeft || heldRight))
speed /= SQRT_TWO;
// adjust multiplier for Z dimension
float oldFloor = GetLandHeightAt(agent.Avatar.Position);
float newFloor = GetLandHeightAt(agent.Avatar.Position + (move * speed));
if (!flying && newFloor != oldFloor)
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
// least possible distance from avatar to the ground
// TODO: calculate to get rid of "bot squat"
float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
// Z acceleration resulting from gravity
float gravity = 0f;
float waterChestHeight = server.Scene.WaterHeight - (agent.Avatar.Scale.Z * .33f);
if (flying)
{
agent.TickFall = 0;
agent.TickJump = 0;
//velocity falloff while flying
agent.Avatar.Velocity.X *= 0.66f;
agent.Avatar.Velocity.Y *= 0.66f;
agent.Avatar.Velocity.Z *= 0.33f;
if (agent.Avatar.Position.Z == lowerLimit)
agent.Avatar.Velocity.Z += INITIAL_HOVER_IMPULSE;
if (move.X != 0 || move.Y != 0)
{ //flying horizontally
if (server.Avatars.SetDefaultAnimation(agent, Animations.FLY))
animsChanged = true;
}
else if (move.Z > 0)
{ //flying straight up
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.Z < 0)
{ //flying straight down
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
animsChanged = true;
}
else
{ //hovering in the air
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else if (agent.Avatar.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Position.Z <= waterChestHeight)
{ //falling, floating, or landing from a jump
if (agent.Avatar.Position.Z > server.Scene.WaterHeight)
{ //above water
move = Vector3.Zero; //override controls while drifting
agent.Avatar.Velocity *= 0.95f; //keep most of our inertia
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Velocity.Z >= 0f))
{ //just started falling
agent.TickFall = Environment.TickCount;
}
else
{
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
if (!jumping)
{ //falling
if (fallElapsed > FALL_DELAY)
{ //falling long enough to trigger the animation
if (server.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
}
}
}
else
{ //on the ground
agent.TickFall = 0;
//friction
agent.Avatar.Acceleration *= 0.2f;
agent.Avatar.Velocity *= 0.2f;
agent.Avatar.Position.Z = lowerLimit;
if (move.Z > 0)
{ //jumping
if (!jumping)
{ //begin prejump
move.Z = 0; //override Z control
if (server.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
animsChanged = true;
agent.TickJump = Environment.TickCount;
}
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
{ //start actual jump
if (agent.TickJump == -1)
{
//already jumping! end current jump
agent.TickJump = 0;
return;
}
if (server.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
animsChanged = true;
agent.Avatar.Velocity.X += agent.Avatar.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Velocity.Y += agent.Avatar.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
agent.TickJump = -1; //flag that we are currently jumping
}
else
{
move.Z = 0; //override Z control
}
}
else
{ //not jumping
agent.TickJump = 0;
if (move.X != 0 || move.Y != 0)
{ //not walking
if (move.Z < 0)
{ //crouchwalking
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
animsChanged = true;
}
else if (agent.Running)
{ //running
if (server.Avatars.SetDefaultAnimation(agent, Animations.RUN))
animsChanged = true;
}
else
{ //walking
if (server.Avatars.SetDefaultAnimation(agent, Animations.WALK))
animsChanged = true;
}
}
else
{ //walking
if (move.Z < 0)
{ //crouching
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
animsChanged = true;
}
else
{ //standing
if (server.Avatars.SetDefaultAnimation(agent, Animations.STAND))
animsChanged = true;
}
}
}
}
if (animsChanged)
server.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
// static acceleration when any control is held, otherwise none
if (moving)
{
agent.Avatar.Acceleration = move * speed;
if (agent.Avatar.Acceleration.Z < -maxVel)
agent.Avatar.Acceleration.Z = -maxVel;
else if (agent.Avatar.Acceleration.Z > maxVel)
agent.Avatar.Acceleration.Z = maxVel;
}
else
{
agent.Avatar.Acceleration = Vector3.Zero;
}
agent.Avatar.Velocity += agent.Avatar.Acceleration - new Vector3(0f, 0f, gravity);
if (agent.Avatar.Velocity.Z < -maxVel)
agent.Avatar.Velocity.Z = -maxVel;
else if (agent.Avatar.Velocity.Z > maxVel)
agent.Avatar.Velocity.Z = maxVel;
agent.Avatar.Position += agent.Avatar.Velocity;
if (agent.Avatar.Position.X < 0) agent.Avatar.Position.X = 0f;
else if (agent.Avatar.Position.X > 255) agent.Avatar.Position.X = 255f;
if (agent.Avatar.Position.Y < 0) agent.Avatar.Position.Y = 0f;
else if (agent.Avatar.Position.Y > 255) agent.Avatar.Position.Y = 255f;
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
}
}
}
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
// Don't use the local physics to update the master agent
if (agent != periscope.MasterAgent)
{
agent.Avatar.Rotation = update.AgentData.BodyRotation;
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
agent.State = update.AgentData.State;
agent.Flags = (PrimFlags)update.AgentData.Flags;
}
ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar,
server.RegionHandle, agent.State, agent.Flags);
server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
{
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
agent.Running = run.AgentData.AlwaysRun;
}
float GetLandHeightAt(Vector3 position)
{
int x = (int)position.X;
int y = (int)position.Y;
if (x > 255) x = 255;
else if (x < 0) x = 0;
if (y > 255) y = 255;
else if (y < 0) y = 0;
float center = server.Scene.Heightmap[y * 256 + x];
float distX = position.X - (int)position.X;
float distY = position.Y - (int)position.Y;
float nearestX;
float nearestY;
if (distX > 0) nearestX = server.Scene.Heightmap[y * 256 + x + (x < 255 ? 1 : 0)];
else nearestX = server.Scene.Heightmap[y * 256 + x - (x > 0 ? 1 : 0)];
if (distY > 0) nearestY = server.Scene.Heightmap[(y + (y < 255 ? 1 : 0)) * 256 + x];
else nearestY = server.Scene.Heightmap[(y - (y > 0 ? 1 : 0)) * 256 + x];
float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
return ((lerpX + lerpY) / 2);
}
}
}

View File

@@ -61,25 +61,23 @@ namespace Simian.Extensions
{
// Check if the object already exists in the scene
if (sceneObjects.ContainsKey(obj.Prim.ID))
{
ObjectModify(sender, obj, obj.Prim.PrimData);
return false;
}
sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID);
// Assign a unique LocalID to this object
obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
if (obj.Prim.LocalID == 0)
{
// Assign a unique LocalID to this object
obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
}
if (OnObjectAdd != null)
{
OnObjectAdd(sender, obj, creatorFlags);
}
// Add the object to the scene dictionary
sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj);
// Send an update out to the creator
if (server.Agents.ContainsKey(obj.Prim.OwnerID))
{
// Send an update out to the creator
ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, 0,
obj.Prim.Flags | creatorFlags);
server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
@@ -100,45 +98,52 @@ namespace Simian.Extensions
return true;
}
public bool ObjectRemove(object sender, SimulationObject obj)
public bool ObjectRemove(object sender, uint localID)
{
if (OnObjectRemove != null)
SimulationObject obj;
if (sceneObjects.TryGetValue(localID, out obj))
{
OnObjectRemove(sender, obj);
if (OnObjectRemove != null)
OnObjectRemove(sender, obj);
sceneObjects.Remove(localID, obj.Prim.ID);
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = obj.Prim.LocalID;
server.UDP.BroadcastPacket(kill, PacketCategory.State);
return true;
}
else
{
return false;
}
sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID);
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = obj.Prim.LocalID;
server.UDP.BroadcastPacket(kill, PacketCategory.State);
return true;
}
public void ObjectTransform(object sender, SimulationObject obj, Vector3 position,
Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity,
Vector3 scale)
public void ObjectTransform(object sender, uint localID, Vector3 position, Quaternion rotation,
Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity)
{
if (OnObjectTransform != null)
SimulationObject obj;
if (sceneObjects.TryGetValue(localID, out obj))
{
OnObjectTransform(sender, obj, position, rotation, velocity,
acceleration, angularVelocity, scale);
if (OnObjectTransform != null)
{
OnObjectTransform(sender, obj, position, rotation, velocity,
acceleration, angularVelocity);
}
// Update the object
obj.Prim.Position = position;
obj.Prim.Rotation = rotation;
obj.Prim.Velocity = velocity;
obj.Prim.Acceleration = acceleration;
obj.Prim.AngularVelocity = angularVelocity;
// Inform clients
BroadcastObjectUpdate(obj);
}
// Update the object
obj.Prim.Position = position;
obj.Prim.Rotation = rotation;
obj.Prim.Velocity = velocity;
obj.Prim.Acceleration = acceleration;
obj.Prim.AngularVelocity = angularVelocity;
obj.Prim.Scale = scale;
// Inform clients
BroadcastObjectUpdate(obj);
}
public void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags)
@@ -170,18 +175,22 @@ namespace Simian.Extensions
BroadcastObjectUpdate(obj);
}
public void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data)
public void ObjectModify(object sender, uint localID, Primitive.ConstructionData data)
{
if (OnObjectModify != null)
SimulationObject obj;
if (sceneObjects.TryGetValue(localID, out obj))
{
OnObjectModify(sender, obj, data);
if (OnObjectModify != null)
{
OnObjectModify(sender, obj, data);
}
// Update the object
obj.Prim.PrimData = data;
// Inform clients
BroadcastObjectUpdate(obj);
}
// Update the object
obj.Prim.PrimData = data;
// Inform clients
BroadcastObjectUpdate(obj);
}
public void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
@@ -191,24 +200,28 @@ namespace Simian.Extensions
OnAvatarAppearance(sender, agent, textures, visualParams);
}
// Update the avatar
agent.Avatar.Textures = textures;
if (visualParams != null)
agent.VisualParams = visualParams;
// Broadcast the object update
// Broadcast an object update for this avatar
// TODO: Is this necessary here?
ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar,
server.RegionHandle, agent.State, agent.Flags);
server.UDP.BroadcastPacket(update, PacketCategory.State);
// Send the appearance packet to all other clients
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
lock (server.Agents)
// Update the avatar
agent.Avatar.Textures = textures;
if (visualParams != null && visualParams.Length > 1)
agent.VisualParams = visualParams;
if (agent.VisualParams != null)
{
foreach (Agent recipient in server.Agents.Values)
// Send the appearance packet to all other clients
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
lock (server.Agents)
{
if (recipient != agent)
server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
foreach (Agent recipient in server.Agents.Values)
{
if (recipient != agent)
server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
}
}
}
}
@@ -384,13 +397,17 @@ namespace Simian.Extensions
appearance.Sender.ID = agent.AgentID;
appearance.Sender.IsTrial = false;
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218];
for (int i = 0; i < 218; i++)
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[agent.VisualParams.Length];
for (int i = 0; i < agent.VisualParams.Length; i++)
{
appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock();
appearance.VisualParam[i].ParamValue = agent.VisualParams[i];
}
if (agent.VisualParams.Length != 218)
Logger.Log("Built an appearance packet with VisualParams.Length=" + agent.VisualParams.Length,
Helpers.LogLevel.Warning);
return appearance;
}
}

View File

@@ -197,14 +197,8 @@ namespace Simian
{
// Look up the UDPClient this is going to
UDPClient client;
if (!clients.TryGetValue(agentID, out client))
{
Logger.Log("Attempted to send a packet to unknown UDP client " +
agentID.ToString(), Helpers.LogLevel.Warning);
return;
}
SendPacket(client, new OutgoingPacket(packet, category));
if (clients.TryGetValue(agentID, out client))
SendPacket(client, new OutgoingPacket(packet, category));
}
void SendPacket(UDPClient client, OutgoingPacket outgoingPacket)