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