diff --git a/OpenMetaverse/Helpers.cs b/OpenMetaverse/Helpers.cs
index 7de1973b..e5f5ce59 100644
--- a/OpenMetaverse/Helpers.cs
+++ b/OpenMetaverse/Helpers.cs
@@ -185,18 +185,6 @@ namespace OpenMetaverse
return s;
}
- ///
- /// Convert a variable length field (byte array) to a string
- ///
- /// If the byte array has unprintable characters in it, a
- /// hex dump will be written instead
- /// The StringBuilder object to write to
- /// The byte array to convert to a string
- internal static void FieldToString(StringBuilder output, byte[] bytes)
- {
- FieldToString(output, bytes, String.Empty);
- }
-
///
/// Convert a variable length field (byte array) to a string, with a
/// field name prepended to each line of the output
@@ -543,5 +531,31 @@ namespace OpenMetaverse
uint fixedState = (((byte)state & ATTACHMENT_MASK) >> 4) | (((byte)state & ~ATTACHMENT_MASK) << 4);
return (AttachmentPoint)fixedState;
}
+
+ public static List SplitBlocks(PacketBlock[] blocks, int packetOverhead)
+ {
+ List splitPoints = new List();
+ int size = 0;
+
+ if (blocks != null && blocks.Length > 0)
+ {
+ splitPoints.Add(0);
+
+ for (int i = 0; i < blocks.Length; i++)
+ {
+ size += blocks[i].Length;
+
+ // If the next block will put this packet over the limit, add a split point
+ if (i < blocks.Length - 1 &&
+ size + blocks[i + 1].Length + packetOverhead >= Settings.MAX_PACKET_SIZE)
+ {
+ splitPoints.Add(i + 1);
+ size = 0;
+ }
+ }
+ }
+
+ return splitPoints;
+ }
}
}
diff --git a/Programs/Simian/Extensions/AssetManager.cs b/Programs/Simian/Extensions/AssetManager.cs
index 3b4192b9..a37ac230 100644
--- a/Programs/Simian/Extensions/AssetManager.cs
+++ b/Programs/Simian/Extensions/AssetManager.cs
@@ -96,7 +96,7 @@ namespace Simian.Extensions
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = request.AssetBlock.Type;
- complete.AssetBlock.UUID = request.AssetBlock.TransactionID;
+ complete.AssetBlock.UUID = assetID;
Server.UDP.SendPacket(agent.AgentID, complete, PacketCategory.Inventory);
}
else
@@ -394,6 +394,7 @@ namespace Simian.Extensions
string[] textures = Directory.GetFiles(path, "*.jp2", SearchOption.TopDirectoryOnly);
string[] clothing = Directory.GetFiles(path, "*.clothing", SearchOption.TopDirectoryOnly);
string[] bodyparts = Directory.GetFiles(path, "*.bodypart", SearchOption.TopDirectoryOnly);
+ string[] sounds = Directory.GetFiles(path, "*.ogg", SearchOption.TopDirectoryOnly);
for (int i = 0; i < textures.Length; i++)
{
@@ -413,6 +414,12 @@ namespace Simian.Extensions
StoreAsset(new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i])));
}
+ for (int i = 0; i < sounds.Length; i++)
+ {
+ UUID assetID = ParseUUIDFromFilename(sounds[i]);
+ StoreAsset(new AssetSound(assetID, File.ReadAllBytes(sounds[i])));
+ }
+
Logger.DebugLog(String.Format("Loaded {0} assets", AssetStore.Count));
}
diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs
index d3f9471b..9a5be626 100644
--- a/Programs/Simian/Extensions/AvatarManager.cs
+++ b/Programs/Simian/Extensions/AvatarManager.cs
@@ -307,42 +307,10 @@ namespace Simian.Extensions
SendWearables(agent);
}
- void ClearWearables(Agent agent)
- {
- agent.ShapeItem = UUID.Zero;
- agent.ShapeAsset = UUID.Zero;
- agent.SkinItem = UUID.Zero;
- agent.SkinAsset = UUID.Zero;
- agent.HairItem = UUID.Zero;
- agent.HairAsset = UUID.Zero;
- agent.EyesItem = UUID.Zero;
- agent.EyesAsset = UUID.Zero;
- agent.ShirtItem = UUID.Zero;
- agent.ShirtAsset = UUID.Zero;
- agent.PantsItem = UUID.Zero;
- agent.PantsAsset = UUID.Zero;
- agent.ShoesItem = UUID.Zero;
- agent.ShoesAsset = UUID.Zero;
- agent.SocksItem = UUID.Zero;
- agent.SocksAsset = UUID.Zero;
- agent.JacketItem = UUID.Zero;
- agent.JacketAsset = UUID.Zero;
- agent.GlovesItem = UUID.Zero;
- agent.GlovesAsset = UUID.Zero;
- agent.UndershirtItem = UUID.Zero;
- agent.UndershirtAsset = UUID.Zero;
- agent.UnderpantsItem = UUID.Zero;
- agent.UnderpantsAsset = UUID.Zero;
- agent.SkirtItem = UUID.Zero;
- agent.SkirtAsset = UUID.Zero;
- }
-
void AgentIsNowWearingHandler(Packet packet, Agent agent)
{
AgentIsNowWearingPacket wearing = (AgentIsNowWearingPacket)packet;
- ClearWearables(agent);
-
for (int i = 0; i < wearing.WearableData.Length; i++)
{
UUID assetID = UUID.Zero;
@@ -422,16 +390,6 @@ namespace Simian.Extensions
Logger.DebugLog("Updating avatar appearance");
- agent.Avatar.Textures = new Primitive.TextureEntry(set.ObjectData.TextureEntry, 0,
- set.ObjectData.TextureEntry.Length);
-
- // Update agent visual params
- if (agent.VisualParams == null)
- agent.VisualParams = new byte[set.VisualParam.Length];
-
- for (int i = 0; i < set.VisualParam.Length; i++)
- agent.VisualParams[i] = set.VisualParam[i].ParamValue;
-
//TODO: Store this for cached bake responses
for (int i = 0; i < set.WearableData.Length; i++)
{
@@ -441,20 +399,16 @@ namespace Simian.Extensions
Logger.DebugLog(String.Format("WearableData: {0} is now {1}", index, cacheID));
}
- ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar,
- Server.RegionHandle, agent.State, agent.Flags | PrimFlags.ObjectYouOwner);
- Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.State);
+ // Create a TextureEntry
+ Primitive.TextureEntry textureEntry = new Primitive.TextureEntry(set.ObjectData.TextureEntry, 0,
+ set.ObjectData.TextureEntry.Length);
- // Send out this appearance to all other connected avatars
- AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
- lock (Server.Agents)
- {
- foreach (Agent recipient in Server.Agents.Values)
- {
- if (recipient != agent)
- Server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
- }
- }
+ // Create a block of VisualParams
+ byte[] visualParams = new byte[set.VisualParam.Length];
+ for (int i = 0; i < set.VisualParam.Length; i++)
+ visualParams[i] = set.VisualParam[i].ParamValue;
+
+ Server.Scene.AvatarAppearance(this, agent, textureEntry, visualParams);
}
void AgentCachedTextureHandler(Packet packet, Agent agent)
@@ -516,22 +470,5 @@ namespace Simian.Extensions
Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Transaction);
}
-
- public static AvatarAppearancePacket BuildAppearancePacket(Agent agent)
- {
- AvatarAppearancePacket appearance = new AvatarAppearancePacket();
- appearance.ObjectData.TextureEntry = agent.Avatar.Textures.ToBytes();
- appearance.Sender.ID = agent.AgentID;
- appearance.Sender.IsTrial = false;
-
- appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218];
- for (int i = 0; i < 218; i++)
- {
- appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock();
- appearance.VisualParam[i].ParamValue = agent.VisualParams[i];
- }
-
- return appearance;
- }
}
}
diff --git a/Programs/Simian/Extensions/InventoryManager.cs b/Programs/Simian/Extensions/InventoryManager.cs
index d1310f0e..1bf459b7 100644
--- a/Programs/Simian/Extensions/InventoryManager.cs
+++ b/Programs/Simian/Extensions/InventoryManager.cs
@@ -152,6 +152,9 @@ namespace Simian.Extensions
void FetchInventoryDescendentsHandler(Packet packet, Agent agent)
{
+ // A very safe estimate of the fixed minimum packet size
+ const int PACKET_OVERHEAD = 96;
+
FetchInventoryDescendentsPacket fetch = (FetchInventoryDescendentsPacket)packet;
bool sendFolders = fetch.InventoryData.FetchFolders;
bool sendItems = fetch.InventoryData.FetchItems;
@@ -161,30 +164,29 @@ namespace Simian.Extensions
// TODO: Use OwnerID
// TODO: Do we need to obey InventorySortOrder?
- // FIXME: This packet can become huge very quickly. Add logic to break it up into multiple packets
-
InventoryObject invObject;
if (agentInventory.TryGetValue(fetch.InventoryData.FolderID, out invObject) && invObject is InventoryFolder)
{
InventoryFolder folder = (InventoryFolder)invObject;
+ int descendCount;
+ int version;
+
+ List items = new List();
+ List folders = new List();
+ InventoryDescendentsPacket.FolderDataBlock[] folderBlocks;
+ InventoryDescendentsPacket.ItemDataBlock[] itemBlocks;
+
lock (folder.Children.Dictionary)
{
- InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
- descendents.AgentData.AgentID = agent.AgentID;
- descendents.AgentData.Descendents = folder.Children.Count;
- descendents.AgentData.FolderID = folder.ID;
- descendents.AgentData.OwnerID = folder.OwnerID;
- descendents.AgentData.Version = folder.Version;
-
- descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
- descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
+ // These two are coupled to the actual items in the dictionary,
+ // so they are set inside the lock
+ descendCount = folder.Children.Count;
+ version = folder.Version;
if (sendItems || sendFolders)
{
- List items = new List();
- List folders = new List();
-
+ // Create a list of all of the folders and items under this folder
folder.Children.ForEach(
delegate(InventoryObject obj)
{
@@ -194,64 +196,142 @@ namespace Simian.Extensions
folders.Add((InventoryFolder)obj);
}
);
-
- if (sendItems)
- {
- descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[items.Count];
- for (int i = 0; i < items.Count; i++)
- {
- InventoryItem currentItem = items[i];
-
- descendents.ItemData[i] = new InventoryDescendentsPacket.ItemDataBlock();
- descendents.ItemData[i].AssetID = currentItem.AssetID;
- descendents.ItemData[i].BaseMask = (uint)currentItem.Permissions.BaseMask;
- descendents.ItemData[i].CRC = Helpers.InventoryCRC(
- (int)Utils.DateTimeToUnixTime(currentItem.CreationDate),
- (byte)currentItem.SaleType, (sbyte)currentItem.InventoryType,
- (sbyte)currentItem.AssetType, currentItem.AssetID, currentItem.GroupID,
- currentItem.SalePrice, currentItem.OwnerID, currentItem.CreatorID, currentItem.ID,
- currentItem.ParentID, (uint)currentItem.Permissions.EveryoneMask, currentItem.Flags,
- (uint)currentItem.Permissions.NextOwnerMask, (uint)currentItem.Permissions.GroupMask,
- (uint)currentItem.Permissions.OwnerMask);
- descendents.ItemData[i].CreationDate = (int)Utils.DateTimeToUnixTime(currentItem.CreationDate);
- descendents.ItemData[i].CreatorID = currentItem.CreatorID;
- descendents.ItemData[i].Description = Utils.StringToBytes(currentItem.Description);
- descendents.ItemData[i].EveryoneMask = (uint)currentItem.Permissions.EveryoneMask;
- descendents.ItemData[i].Flags = currentItem.Flags;
- descendents.ItemData[i].FolderID = currentItem.ParentID;
- descendents.ItemData[i].GroupID = currentItem.GroupID;
- descendents.ItemData[i].GroupMask = (uint)currentItem.Permissions.GroupMask;
- descendents.ItemData[i].GroupOwned = currentItem.GroupOwned;
- descendents.ItemData[i].InvType = (sbyte)currentItem.InventoryType;
- descendents.ItemData[i].ItemID = currentItem.ID;
- descendents.ItemData[i].Name = Utils.StringToBytes(currentItem.Name);
- descendents.ItemData[i].NextOwnerMask = (uint)currentItem.Permissions.NextOwnerMask;
- descendents.ItemData[i].OwnerID = currentItem.OwnerID;
- descendents.ItemData[i].OwnerMask = (uint)currentItem.Permissions.OwnerMask;
- descendents.ItemData[i].SalePrice = currentItem.SalePrice;
- descendents.ItemData[i].SaleType = (byte)currentItem.SaleType;
- descendents.ItemData[i].Type = (sbyte)currentItem.AssetType;
- }
- }
-
- if (sendFolders)
- {
- descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[folders.Count];
- for (int i = 0; i < folders.Count; i++)
- {
- InventoryFolder currentFolder = folders[i];
-
- descendents.FolderData[i] = new InventoryDescendentsPacket.FolderDataBlock();
- descendents.FolderData[i].FolderID = currentFolder.ID;
- descendents.FolderData[i].Name = Utils.StringToBytes(currentFolder.Name);
- descendents.FolderData[i].ParentID = currentFolder.ParentID;
- descendents.FolderData[i].Type = (sbyte)currentFolder.PreferredType;
- }
- }
}
+ }
+
+ if (sendFolders)
+ {
+ folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[folders.Count];
+ for (int i = 0; i < folders.Count; i++)
+ {
+ InventoryFolder currentFolder = folders[i];
+
+ folderBlocks[i] = new InventoryDescendentsPacket.FolderDataBlock();
+ folderBlocks[i].FolderID = currentFolder.ID;
+ folderBlocks[i].Name = Utils.StringToBytes(currentFolder.Name);
+ folderBlocks[i].ParentID = currentFolder.ParentID;
+ folderBlocks[i].Type = (sbyte)currentFolder.PreferredType;
+ }
+ }
+ else
+ {
+ folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[0];
+ }
+
+ if (sendItems)
+ {
+ itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[items.Count];
+ for (int i = 0; i < items.Count; i++)
+ {
+ InventoryItem currentItem = items[i];
+
+ itemBlocks[i] = new InventoryDescendentsPacket.ItemDataBlock();
+ itemBlocks[i].AssetID = currentItem.AssetID;
+ itemBlocks[i].BaseMask = (uint)currentItem.Permissions.BaseMask;
+ itemBlocks[i].CRC = Helpers.InventoryCRC(
+ (int)Utils.DateTimeToUnixTime(currentItem.CreationDate),
+ (byte)currentItem.SaleType, (sbyte)currentItem.InventoryType,
+ (sbyte)currentItem.AssetType, currentItem.AssetID, currentItem.GroupID,
+ currentItem.SalePrice, currentItem.OwnerID, currentItem.CreatorID, currentItem.ID,
+ currentItem.ParentID, (uint)currentItem.Permissions.EveryoneMask, currentItem.Flags,
+ (uint)currentItem.Permissions.NextOwnerMask, (uint)currentItem.Permissions.GroupMask,
+ (uint)currentItem.Permissions.OwnerMask);
+ itemBlocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(currentItem.CreationDate);
+ itemBlocks[i].CreatorID = currentItem.CreatorID;
+ itemBlocks[i].Description = Utils.StringToBytes(currentItem.Description);
+ itemBlocks[i].EveryoneMask = (uint)currentItem.Permissions.EveryoneMask;
+ itemBlocks[i].Flags = currentItem.Flags;
+ itemBlocks[i].FolderID = currentItem.ParentID;
+ itemBlocks[i].GroupID = currentItem.GroupID;
+ itemBlocks[i].GroupMask = (uint)currentItem.Permissions.GroupMask;
+ itemBlocks[i].GroupOwned = currentItem.GroupOwned;
+ itemBlocks[i].InvType = (sbyte)currentItem.InventoryType;
+ itemBlocks[i].ItemID = currentItem.ID;
+ itemBlocks[i].Name = Utils.StringToBytes(currentItem.Name);
+ itemBlocks[i].NextOwnerMask = (uint)currentItem.Permissions.NextOwnerMask;
+ itemBlocks[i].OwnerID = currentItem.OwnerID;
+ itemBlocks[i].OwnerMask = (uint)currentItem.Permissions.OwnerMask;
+ itemBlocks[i].SalePrice = currentItem.SalePrice;
+ itemBlocks[i].SaleType = (byte)currentItem.SaleType;
+ itemBlocks[i].Type = (sbyte)currentItem.AssetType;
+ }
+ }
+ else
+ {
+ itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[0];
+ }
+
+ // FolderDataBlock and ItemDataBlock are both variable and possibly very large,
+ // so we handle the splitting separately. This could be replaced by some custom
+ // splitting
+ if (folderBlocks.Length > 0)
+ {
+ List splitPoints = Helpers.SplitBlocks(folderBlocks, PACKET_OVERHEAD);
+ Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} folders",
+ splitPoints.Count, folderBlocks.Length));
+
+ for (int i = 0; i < splitPoints.Count; i++)
+ {
+ int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
+ folderBlocks.Length - splitPoints[i];
+
+ InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
+ descendents.AgentData.AgentID = agent.AgentID;
+ descendents.AgentData.FolderID = folder.ID;
+ descendents.AgentData.OwnerID = folder.OwnerID;
+ descendents.AgentData.Descendents = descendCount;
+ descendents.AgentData.Version = version;
+ descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[count];
+ descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
+
+ for (int j = 0; j < count; j++)
+ descendents.FolderData[j] = folderBlocks[splitPoints[i] + j];
+
+ Server.UDP.SendPacket(agent.AgentID, descendents, PacketCategory.Inventory);
+ }
+ }
+ else
+ {
+ Logger.DebugLog("Sending a single InventoryDescendents for folders");
+
+ InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
+ descendents.AgentData.AgentID = agent.AgentID;
+ descendents.AgentData.FolderID = folder.ID;
+ descendents.AgentData.OwnerID = folder.OwnerID;
+ descendents.AgentData.Descendents = descendCount;
+ descendents.AgentData.Version = version;
+ descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
+ descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
Server.UDP.SendPacket(agent.AgentID, descendents, PacketCategory.Inventory);
}
+
+ if (itemBlocks.Length > 0)
+ {
+ List splitPoints = Helpers.SplitBlocks(itemBlocks, PACKET_OVERHEAD);
+ Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} items",
+ splitPoints.Count, itemBlocks.Length));
+
+ for (int i = 0; i < splitPoints.Count; i++)
+ {
+ int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
+ itemBlocks.Length - splitPoints[i];
+
+ InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
+ descendents.AgentData.AgentID = agent.AgentID;
+ descendents.AgentData.FolderID = folder.ID;
+ descendents.AgentData.OwnerID = folder.OwnerID;
+ descendents.AgentData.Descendents = descendCount;
+ descendents.AgentData.Version = version;
+ descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
+ descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[count];
+
+ for (int j = 0; j < count; j++)
+ descendents.ItemData[j] = itemBlocks[splitPoints[i] + j];
+
+ Server.UDP.SendPacket(agent.AgentID, descendents, PacketCategory.Inventory);
+ }
+ }
}
else
{
@@ -262,61 +342,79 @@ namespace Simian.Extensions
void FetchInventoryHandler(Packet packet, Agent agent)
{
- FetchInventoryPacket fetch = (FetchInventoryPacket)packet;
+ // This is probably too large, but better to be on the safe side
+ const int PACKET_OVERHEAD = 32;
- FetchInventoryReplyPacket reply = new FetchInventoryReplyPacket();
- reply.AgentData.AgentID = agent.AgentID;
- reply.InventoryData = new FetchInventoryReplyPacket.InventoryDataBlock[fetch.InventoryData.Length];
+ FetchInventoryPacket fetch = (FetchInventoryPacket)packet;
+
+ // Create all of the blocks first. These will be split up into different packets
+ FetchInventoryReplyPacket.InventoryDataBlock[] blocks =
+ new FetchInventoryReplyPacket.InventoryDataBlock[fetch.InventoryData.Length];
for (int i = 0; i < fetch.InventoryData.Length; i++)
{
UUID itemID = fetch.InventoryData[i].ItemID;
Dictionary agentInventory = GetAgentInventory(agent.AgentID);
- reply.InventoryData[i] = new FetchInventoryReplyPacket.InventoryDataBlock();
- reply.InventoryData[i].ItemID = itemID;
+ blocks[i] = new FetchInventoryReplyPacket.InventoryDataBlock();
+ blocks[i].ItemID = itemID;
InventoryObject obj;
if (agentInventory.TryGetValue(itemID, out obj) && obj is InventoryItem)
{
InventoryItem item = (InventoryItem)obj;
- reply.InventoryData[i].AssetID = item.AssetID;
- reply.InventoryData[i].BaseMask = (uint)item.Permissions.BaseMask;
- reply.InventoryData[i].CRC = Helpers.InventoryCRC((int)Utils.DateTimeToUnixTime(item.CreationDate),
+ blocks[i].AssetID = item.AssetID;
+ blocks[i].BaseMask = (uint)item.Permissions.BaseMask;
+ blocks[i].CRC = Helpers.InventoryCRC((int)Utils.DateTimeToUnixTime(item.CreationDate),
(byte)item.SaleType, (sbyte)item.InventoryType, (sbyte)item.AssetType, item.AssetID, item.GroupID,
item.SalePrice, item.OwnerID, item.CreatorID, item.ID, item.ParentID,
(uint)item.Permissions.EveryoneMask, item.Flags, (uint)item.Permissions.NextOwnerMask,
(uint)item.Permissions.GroupMask, (uint)item.Permissions.OwnerMask);
- reply.InventoryData[i].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
- reply.InventoryData[i].CreatorID = item.CreatorID;
- reply.InventoryData[i].Description = Utils.StringToBytes(item.Description);
- reply.InventoryData[i].EveryoneMask = (uint)item.Permissions.EveryoneMask;
- reply.InventoryData[i].Flags = item.Flags;
- reply.InventoryData[i].FolderID = item.ParentID;
- reply.InventoryData[i].GroupID = item.GroupID;
- reply.InventoryData[i].GroupMask = (uint)item.Permissions.GroupMask;
- reply.InventoryData[i].GroupOwned = item.GroupOwned;
- reply.InventoryData[i].InvType = (sbyte)item.InventoryType;
- reply.InventoryData[i].Name = Utils.StringToBytes(item.Name);
- reply.InventoryData[i].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
- reply.InventoryData[i].OwnerID = item.OwnerID;
- reply.InventoryData[i].OwnerMask = (uint)item.Permissions.OwnerMask;
- reply.InventoryData[i].SalePrice = item.SalePrice;
- reply.InventoryData[i].SaleType = (byte)item.SaleType;
- reply.InventoryData[i].Type = (sbyte)item.AssetType;
+ blocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
+ blocks[i].CreatorID = item.CreatorID;
+ blocks[i].Description = Utils.StringToBytes(item.Description);
+ blocks[i].EveryoneMask = (uint)item.Permissions.EveryoneMask;
+ blocks[i].Flags = item.Flags;
+ blocks[i].FolderID = item.ParentID;
+ blocks[i].GroupID = item.GroupID;
+ blocks[i].GroupMask = (uint)item.Permissions.GroupMask;
+ blocks[i].GroupOwned = item.GroupOwned;
+ blocks[i].InvType = (sbyte)item.InventoryType;
+ blocks[i].Name = Utils.StringToBytes(item.Name);
+ blocks[i].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
+ blocks[i].OwnerID = item.OwnerID;
+ blocks[i].OwnerMask = (uint)item.Permissions.OwnerMask;
+ blocks[i].SalePrice = item.SalePrice;
+ blocks[i].SaleType = (byte)item.SaleType;
+ blocks[i].Type = (sbyte)item.AssetType;
}
else
{
Logger.Log("FetchInventory called for an unknown item " + itemID.ToString(),
Helpers.LogLevel.Warning);
- reply.InventoryData[i].Name = new byte[0];
- reply.InventoryData[i].Description = new byte[0];
+ blocks[i].Name = new byte[0];
+ blocks[i].Description = new byte[0];
}
}
- Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Inventory);
+ // Split the blocks up into multiple packets
+ List splitPoints = Helpers.SplitBlocks(blocks, PACKET_OVERHEAD);
+ for (int i = 0; i < splitPoints.Count; i++)
+ {
+ int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
+ blocks.Length - splitPoints[i];
+
+ FetchInventoryReplyPacket reply = new FetchInventoryReplyPacket();
+ reply.AgentData.AgentID = agent.AgentID;
+ reply.InventoryData = new FetchInventoryReplyPacket.InventoryDataBlock[count];
+
+ for (int j = 0; j < count; j++)
+ reply.InventoryData[j] = blocks[splitPoints[i] + j];
+
+ Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Inventory);
+ }
}
void CopyInventoryItemHandler(Packet packet, Agent agent)
diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs
index 4754b7e4..616ef07c 100644
--- a/Programs/Simian/Extensions/SceneManager.cs
+++ b/Programs/Simian/Extensions/SceneManager.cs
@@ -25,6 +25,7 @@ namespace Simian.Extensions
public event ObjectFlagsCallback OnObjectFlags;
public event ObjectImageCallback OnObjectImage;
public event ObjectModifyCallback OnObjectModify;
+ public event AvatarAppearanceCallback OnAvatarAppearance;
public event TerrainUpdatedCallback OnTerrainUpdated;
public float[] Heightmap
@@ -180,6 +181,35 @@ namespace Simian.Extensions
BroadcastObjectUpdate(obj);
}
+ public void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
+ {
+ if (OnAvatarAppearance != null)
+ {
+ OnAvatarAppearance(sender, agent, textures, visualParams);
+ }
+
+ // Update the avatar
+ agent.Avatar.Textures = textures;
+ if (visualParams != null)
+ agent.VisualParams = visualParams;
+
+ // Broadcast the object update
+ 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)
+ {
+ foreach (Agent recipient in server.Agents.Values)
+ {
+ if (recipient != agent)
+ server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
+ }
+ }
+ }
+
public bool TryGetObject(uint localID, out SimulationObject obj)
{
return sceneObjects.TryGetValue(localID, out obj);
@@ -284,7 +314,7 @@ namespace Simian.Extensions
if (otherAgent != agent)
{
// Send appearances for this avatar
- AvatarAppearancePacket appearance = AvatarManager.BuildAppearancePacket(otherAgent);
+ AvatarAppearancePacket appearance = BuildAppearancePacket(otherAgent);
server.UDP.SendPacket(agent.AgentID, appearance, PacketCategory.State);
}
}
@@ -343,5 +373,22 @@ namespace Simian.Extensions
}
}
}
+
+ static AvatarAppearancePacket BuildAppearancePacket(Agent agent)
+ {
+ AvatarAppearancePacket appearance = new AvatarAppearancePacket();
+ appearance.ObjectData.TextureEntry = agent.Avatar.Textures.ToBytes();
+ appearance.Sender.ID = agent.AgentID;
+ appearance.Sender.IsTrial = false;
+
+ appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218];
+ for (int i = 0; i < 218; i++)
+ {
+ appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock();
+ appearance.VisualParam[i].ParamValue = agent.VisualParams[i];
+ }
+
+ return appearance;
+ }
}
}
diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs
index b1b217a9..ecd94f6a 100644
--- a/Programs/Simian/Interfaces/ISceneProvider.cs
+++ b/Programs/Simian/Interfaces/ISceneProvider.cs
@@ -13,6 +13,8 @@ namespace Simian
string mediaURL, Primitive.TextureEntry textureEntry);
public delegate void ObjectModifyCallback(object sender, SimulationObject obj,
Primitive.ConstructionData data);
+ public delegate void AvatarAppearanceCallback(object sender, Agent agent,
+ Primitive.TextureEntry textures, byte[] visualParams);
// TODO: Convert terrain to a patch-based system
public delegate void TerrainUpdatedCallback(object sender);
@@ -23,6 +25,7 @@ namespace Simian
event ObjectTransformCallback OnObjectTransform;
event ObjectFlagsCallback OnObjectFlags;
event ObjectModifyCallback OnObjectModify;
+ event AvatarAppearanceCallback OnAvatarAppearance;
event TerrainUpdatedCallback OnTerrainUpdated;
// TODO: Convert to a patch-based system, and expose terrain editing
@@ -38,6 +41,9 @@ namespace Simian
void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags);
void ObjectImage(object sender, SimulationObject obj, string mediaURL, Primitive.TextureEntry textureEntry);
void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data);
+
+ void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams);
+
bool TryGetObject(uint localID, out SimulationObject obj);
bool TryGetObject(UUID id, out SimulationObject obj);
}
diff --git a/Programs/examples/TestClient/Commands/System/LoginCommand.cs b/Programs/examples/TestClient/Commands/System/LoginCommand.cs
index a2344d53..4a0253e7 100644
--- a/Programs/examples/TestClient/Commands/System/LoginCommand.cs
+++ b/Programs/examples/TestClient/Commands/System/LoginCommand.cs
@@ -11,7 +11,7 @@ namespace OpenMetaverse.TestClient
public LoginCommand(TestClient testClient)
{
Name = "login";
- Description = "Logs in another avatar. Usage: login firstname lastname [simname] [loginuri]";
+ Description = "Logs in another avatar. Usage: login firstname lastname password [simname] [loginuri]";
Category = CommandCategory.TestClient;
}
diff --git a/bin/SimianData/uialert-ed124764-705d-d497-167a-182cd9fa2e6c.ogg b/bin/SimianData/uialert-ed124764-705d-d497-167a-182cd9fa2e6c.ogg
new file mode 100644
index 00000000..c9b1d7b2
Binary files /dev/null and b/bin/SimianData/uialert-ed124764-705d-d497-167a-182cd9fa2e6c.ogg differ
diff --git a/bin/SimianData/uibadkeystroke-2ca849ba-2885-4bc3-90ef-d4987a5b983a.ogg b/bin/SimianData/uibadkeystroke-2ca849ba-2885-4bc3-90ef-d4987a5b983a.ogg
new file mode 100644
index 00000000..789ec92c
Binary files /dev/null and b/bin/SimianData/uibadkeystroke-2ca849ba-2885-4bc3-90ef-d4987a5b983a.ogg differ
diff --git a/bin/SimianData/uiclick-4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6.ogg b/bin/SimianData/uiclick-4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6.ogg
new file mode 100644
index 00000000..3e2336cb
Binary files /dev/null and b/bin/SimianData/uiclick-4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6.ogg differ
diff --git a/bin/SimianData/uihealthreduction-219c5d93-6c09-31c5-fb3f-c5fe7495c115.ogg b/bin/SimianData/uihealthreduction-219c5d93-6c09-31c5-fb3f-c5fe7495c115.ogg
new file mode 100644
index 00000000..f999364d
Binary files /dev/null and b/bin/SimianData/uihealthreduction-219c5d93-6c09-31c5-fb3f-c5fe7495c115.ogg differ
diff --git a/bin/SimianData/uihealthreduction_male-e057c244-5768-1056-c37e-1537454eeb62.ogg b/bin/SimianData/uihealthreduction_male-e057c244-5768-1056-c37e-1537454eeb62.ogg
new file mode 100644
index 00000000..2f8e21b8
Binary files /dev/null and b/bin/SimianData/uihealthreduction_male-e057c244-5768-1056-c37e-1537454eeb62.ogg differ
diff --git a/bin/SimianData/uiinvalidop-4174f859-0d3d-c517-c424-72923dc21f65.ogg b/bin/SimianData/uiinvalidop-4174f859-0d3d-c517-c424-72923dc21f65.ogg
new file mode 100644
index 00000000..6b0eaac4
Binary files /dev/null and b/bin/SimianData/uiinvalidop-4174f859-0d3d-c517-c424-72923dc21f65.ogg differ
diff --git a/bin/SimianData/uimoneychange_down-104974e3-dfda-428b-99ee-b0d4e748d3a3.ogg b/bin/SimianData/uimoneychange_down-104974e3-dfda-428b-99ee-b0d4e748d3a3.ogg
new file mode 100644
index 00000000..035d5eb1
Binary files /dev/null and b/bin/SimianData/uimoneychange_down-104974e3-dfda-428b-99ee-b0d4e748d3a3.ogg differ
diff --git a/bin/SimianData/uimoneychange_up-77a018af-098e-c037-51a6-178f05877c6f.ogg b/bin/SimianData/uimoneychange_up-77a018af-098e-c037-51a6-178f05877c6f.ogg
new file mode 100644
index 00000000..1b508dbb
Binary files /dev/null and b/bin/SimianData/uimoneychange_up-77a018af-098e-c037-51a6-178f05877c6f.ogg differ
diff --git a/bin/SimianData/uinewim-67cc2844-00f3-2b3c-b991-6418d01e1bb7.ogg b/bin/SimianData/uinewim-67cc2844-00f3-2b3c-b991-6418d01e1bb7.ogg
new file mode 100644
index 00000000..82c56ef5
Binary files /dev/null and b/bin/SimianData/uinewim-67cc2844-00f3-2b3c-b991-6418d01e1bb7.ogg differ
diff --git a/bin/SimianData/uiobjectcreate-f4a0660f-5446-dea2-80b7-6482a082803c.ogg b/bin/SimianData/uiobjectcreate-f4a0660f-5446-dea2-80b7-6482a082803c.ogg
new file mode 100644
index 00000000..a363ce35
Binary files /dev/null and b/bin/SimianData/uiobjectcreate-f4a0660f-5446-dea2-80b7-6482a082803c.ogg differ
diff --git a/bin/SimianData/uiobjectdelete-0cb7b00a-4c10-6948-84de-a93c09af2ba9.ogg b/bin/SimianData/uiobjectdelete-0cb7b00a-4c10-6948-84de-a93c09af2ba9.ogg
new file mode 100644
index 00000000..7f154e5c
Binary files /dev/null and b/bin/SimianData/uiobjectdelete-0cb7b00a-4c10-6948-84de-a93c09af2ba9.ogg differ
diff --git a/bin/SimianData/uiobjectrezin-3c8fc726-1fd6-862d-fa01-16c5b2568db6.ogg b/bin/SimianData/uiobjectrezin-3c8fc726-1fd6-862d-fa01-16c5b2568db6.ogg
new file mode 100644
index 00000000..225ed277
Binary files /dev/null and b/bin/SimianData/uiobjectrezin-3c8fc726-1fd6-862d-fa01-16c5b2568db6.ogg differ
diff --git a/bin/SimianData/uipiemenu0-d9f73cf8-17b4-6f7a-1565-7951226c305d.ogg b/bin/SimianData/uipiemenu0-d9f73cf8-17b4-6f7a-1565-7951226c305d.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu0-d9f73cf8-17b4-6f7a-1565-7951226c305d.ogg differ
diff --git a/bin/SimianData/uipiemenu1-f6ba9816-dcaf-f755-7b67-51b31b6233e5.ogg b/bin/SimianData/uipiemenu1-f6ba9816-dcaf-f755-7b67-51b31b6233e5.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu1-f6ba9816-dcaf-f755-7b67-51b31b6233e5.ogg differ
diff --git a/bin/SimianData/uipiemenu2-7aff2265-d05b-8b72-63c7-dbf96dc2f21f.ogg b/bin/SimianData/uipiemenu2-7aff2265-d05b-8b72-63c7-dbf96dc2f21f.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu2-7aff2265-d05b-8b72-63c7-dbf96dc2f21f.ogg differ
diff --git a/bin/SimianData/uipiemenu3-09b2184e-8601-44e2-afbb-ce37434b8ba1.ogg b/bin/SimianData/uipiemenu3-09b2184e-8601-44e2-afbb-ce37434b8ba1.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu3-09b2184e-8601-44e2-afbb-ce37434b8ba1.ogg differ
diff --git a/bin/SimianData/uipiemenu4-bbe4c7fc-7044-b05e-7b89-36924a67593c.ogg b/bin/SimianData/uipiemenu4-bbe4c7fc-7044-b05e-7b89-36924a67593c.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu4-bbe4c7fc-7044-b05e-7b89-36924a67593c.ogg differ
diff --git a/bin/SimianData/uipiemenu5-d166039b-b4f5-c2ec-4911-c85c727b016c.ogg b/bin/SimianData/uipiemenu5-d166039b-b4f5-c2ec-4911-c85c727b016c.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu5-d166039b-b4f5-c2ec-4911-c85c727b016c.ogg differ
diff --git a/bin/SimianData/uipiemenu6-242af82b-43c2-9a3b-e108-3b0c7e384981.ogg b/bin/SimianData/uipiemenu6-242af82b-43c2-9a3b-e108-3b0c7e384981.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu6-242af82b-43c2-9a3b-e108-3b0c7e384981.ogg differ
diff --git a/bin/SimianData/uipiemenu7-c1f334fb-a5be-8fe7-22b3-29631c21cf0b.ogg b/bin/SimianData/uipiemenu7-c1f334fb-a5be-8fe7-22b3-29631c21cf0b.ogg
new file mode 100644
index 00000000..a1b410b5
Binary files /dev/null and b/bin/SimianData/uipiemenu7-c1f334fb-a5be-8fe7-22b3-29631c21cf0b.ogg differ
diff --git a/bin/SimianData/uipiemenuappear-8eaed61f-92ff-6485-de83-4dcc938a478e.ogg b/bin/SimianData/uipiemenuappear-8eaed61f-92ff-6485-de83-4dcc938a478e.ogg
new file mode 100644
index 00000000..5ea159ba
Binary files /dev/null and b/bin/SimianData/uipiemenuappear-8eaed61f-92ff-6485-de83-4dcc938a478e.ogg differ
diff --git a/bin/SimianData/uisnapshot-3d09f582-3851-c0e0-f5ba-277ac5c73fb4.ogg b/bin/SimianData/uisnapshot-3d09f582-3851-c0e0-f5ba-277ac5c73fb4.ogg
new file mode 100644
index 00000000..5f5fd2d4
Binary files /dev/null and b/bin/SimianData/uisnapshot-3d09f582-3851-c0e0-f5ba-277ac5c73fb4.ogg differ
diff --git a/bin/SimianData/uistartim-c825dfbc-9827-7e02-6507-3713d18916c1.ogg b/bin/SimianData/uistartim-c825dfbc-9827-7e02-6507-3713d18916c1.ogg
new file mode 100644
index 00000000..82f521d5
Binary files /dev/null and b/bin/SimianData/uistartim-c825dfbc-9827-7e02-6507-3713d18916c1.ogg differ
diff --git a/bin/SimianData/uiteleportout-d7a9a565-a013-2a69-797d-5332baa1a947.ogg b/bin/SimianData/uiteleportout-d7a9a565-a013-2a69-797d-5332baa1a947.ogg
new file mode 100644
index 00000000..6d712ea2
Binary files /dev/null and b/bin/SimianData/uiteleportout-d7a9a565-a013-2a69-797d-5332baa1a947.ogg differ
diff --git a/bin/SimianData/uityping-5e191c7b-8996-9ced-a177-b2ac32bfea06.ogg b/bin/SimianData/uityping-5e191c7b-8996-9ced-a177-b2ac32bfea06.ogg
new file mode 100644
index 00000000..af3da540
Binary files /dev/null and b/bin/SimianData/uityping-5e191c7b-8996-9ced-a177-b2ac32bfea06.ogg differ
diff --git a/bin/SimianData/uiwindowclose-2c346eda-b60c-ab33-1119-b8941916a499.ogg b/bin/SimianData/uiwindowclose-2c346eda-b60c-ab33-1119-b8941916a499.ogg
new file mode 100644
index 00000000..1742cbd8
Binary files /dev/null and b/bin/SimianData/uiwindowclose-2c346eda-b60c-ab33-1119-b8941916a499.ogg differ
diff --git a/bin/SimianData/uiwindowopen-c80260ba-41fd-8a46-768a-6bf236360e3a.ogg b/bin/SimianData/uiwindowopen-c80260ba-41fd-8a46-768a-6bf236360e3a.ogg
new file mode 100644
index 00000000..862823e2
Binary files /dev/null and b/bin/SimianData/uiwindowopen-c80260ba-41fd-8a46-768a-6bf236360e3a.ogg differ