using System; using System.Collections.Generic; using ExtensionLoader; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenMetaverse.Packets; namespace Simian.Extensions { public class InventoryManager : IExtension, IInventoryProvider, IPersistable { Simian Server; /// Dictionary of inventories for each agent. Each inventory /// is also a dictionary itself Dictionary> Inventory = new Dictionary>(); /// Global shared inventory for all agent Dictionary Library = new Dictionary(); public InventoryManager(Simian server) { Server = server; } public void Start() { Server.UDP.RegisterPacketCallback(PacketType.CreateInventoryItem, new PacketCallback(CreateInventoryItemHandler)); Server.UDP.RegisterPacketCallback(PacketType.CreateInventoryFolder, new PacketCallback(CreateInventoryFolderHandler)); Server.UDP.RegisterPacketCallback(PacketType.UpdateInventoryItem, new PacketCallback(UpdateInventoryItemHandler)); Server.UDP.RegisterPacketCallback(PacketType.FetchInventoryDescendents, new PacketCallback(FetchInventoryDescendentsHandler)); Server.UDP.RegisterPacketCallback(PacketType.FetchInventory, new PacketCallback(FetchInventoryHandler)); Server.UDP.RegisterPacketCallback(PacketType.CopyInventoryItem, new PacketCallback(CopyInventoryItemHandler)); Server.UDP.RegisterPacketCallback(PacketType.MoveInventoryItem, new PacketCallback(MoveInventoryItemHandler)); Server.UDP.RegisterPacketCallback(PacketType.MoveInventoryFolder, new PacketCallback(MoveInventoryFolderHandler)); Server.UDP.RegisterPacketCallback(PacketType.PurgeInventoryDescendents, new PacketCallback(PurgeInventoryDescendentsHandler)); } public void Stop() { } Dictionary GetAgentInventory(UUID agentID) { Dictionary agentInventory; if (!Inventory.TryGetValue(agentID, out agentInventory)) { Logger.Log("Creating an empty inventory store for agent " + agentID.ToString(), Helpers.LogLevel.Info); agentInventory = new Dictionary(); lock (Inventory) Inventory[agentID] = agentInventory; } return agentInventory; } void CreateInventoryItemHandler(Packet packet, Agent agent) { CreateInventoryItemPacket create = (CreateInventoryItemPacket)packet; UUID assetID; if (create.InventoryBlock.TransactionID != UUID.Zero) assetID = UUID.Combine(create.InventoryBlock.TransactionID, agent.SecureSessionID); else assetID = UUID.Random(); UUID parentID = (create.InventoryBlock.FolderID != UUID.Zero) ? create.InventoryBlock.FolderID : agent.InventoryRoot; AssetType assetType = (AssetType)create.InventoryBlock.Type; switch (assetType) { case AssetType.Gesture: Logger.Log("Need to create a default gesture asset!", Helpers.LogLevel.Warning); break; } if (parentID == UUID.Zero) parentID = agent.InventoryRoot; // Create the inventory item CreateItem(agent.AgentID, Utils.BytesToString(create.InventoryBlock.Name), "Created in Simian", (InventoryType)create.InventoryBlock.InvType, assetType, assetID, parentID, PermissionMask.All, (PermissionMask)create.InventoryBlock.NextOwnerMask, agent.AgentID, agent.AgentID, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID); } void CreateInventoryFolderHandler(Packet packet, Agent agent) { CreateInventoryFolderPacket create = (CreateInventoryFolderPacket)packet; UUID folderID = create.FolderData.FolderID; if (folderID == UUID.Zero) folderID = agent.InventoryRoot; CreateFolder(agent.AgentID, folderID, Utils.BytesToString(create.FolderData.Name), (AssetType)create.FolderData.Type, create.FolderData.ParentID, agent.AgentID); } void UpdateInventoryItemHandler(Packet packet, Agent agent) { UpdateInventoryItemPacket update = (UpdateInventoryItemPacket)packet; // No packet is sent back to the client, we just need to update the // inventory item locally for (int i = 0; i < update.InventoryData.Length; i++) { UpdateInventoryItemPacket.InventoryDataBlock block = update.InventoryData[i]; Dictionary agentInventory = GetAgentInventory(agent.AgentID); InventoryObject obj; if (agentInventory.TryGetValue(block.ItemID, out obj) && obj is InventoryItem) { InventoryItem item = (InventoryItem)obj; //item.Permissions.BaseMask = (PermissionMask)block.BaseMask; item.Permissions.BaseMask = PermissionMask.All; //item.Permissions.EveryoneMask = (PermissionMask)block.EveryoneMask; item.Permissions.EveryoneMask = PermissionMask.All; //item.Permissions.GroupMask = (PermissionMask)block.GroupMask; item.Permissions.GroupMask = PermissionMask.All; //item.Permissions.NextOwnerMask = (PermissionMask)block.NextOwnerMask; item.Permissions.NextOwnerMask = PermissionMask.All; //item.Permissions.OwnerMask = (PermissionMask)block.OwnerMask; item.Permissions.OwnerMask = PermissionMask.All; //block.CRC; item.CreationDate = Utils.UnixTimeToDateTime(block.CreationDate); item.CreatorID = block.CreatorID; item.Name = Utils.BytesToString(block.Description); item.Flags = block.Flags; item.ParentID = block.FolderID; item.GroupID = block.GroupID; item.GroupOwned = block.GroupOwned; item.InventoryType = (InventoryType)block.InvType; item.Name = Utils.BytesToString(block.Name); item.OwnerID = block.OwnerID; item.SalePrice = block.SalePrice; item.SaleType = (SaleType)block.SaleType; item.AssetType = (AssetType)block.Type; Logger.DebugLog(String.Format( "UpdateInventoryItem: CallbackID: {0}, TransactionID: {1}", block.CallbackID, block.TransactionID)); } else { Logger.Log("Received an UpdateInventoryItem packet for unknown inventory item " + block.ItemID.ToString(), Helpers.LogLevel.Warning); } } } void FetchInventoryDescendentsHandler(Packet packet, Agent agent) { FetchInventoryDescendentsPacket fetch = (FetchInventoryDescendentsPacket)packet; bool sendFolders = fetch.InventoryData.FetchFolders; bool sendItems = fetch.InventoryData.FetchItems; InventorySortOrder order = (InventorySortOrder)fetch.InventoryData.SortOrder; Dictionary agentInventory = GetAgentInventory(agent.AgentID); // 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; 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]; if (sendItems || sendFolders) { List items = new List(); List folders = new List(); folder.Children.ForEach( delegate(InventoryObject obj) { if (obj is InventoryItem) items.Add((InventoryItem)obj); else 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; } } } Server.UDP.SendPacket(agent.AgentID, descendents, PacketCategory.Inventory); } } else { Logger.Log("FetchInventoryDescendents called for an unknown folder " + fetch.InventoryData.FolderID, Helpers.LogLevel.Warning); } } void FetchInventoryHandler(Packet packet, Agent agent) { FetchInventoryPacket fetch = (FetchInventoryPacket)packet; FetchInventoryReplyPacket reply = new FetchInventoryReplyPacket(); reply.AgentData.AgentID = agent.AgentID; reply.InventoryData = 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; 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), (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; } 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]; } } Server.UDP.SendPacket(agent.AgentID, reply, PacketCategory.Inventory); } void CopyInventoryItemHandler(Packet packet, Agent agent) { CopyInventoryItemPacket copy = (CopyInventoryItemPacket)packet; for (int i = 0; i < copy.InventoryData.Length; i++) { CopyInventoryItemPacket.InventoryDataBlock block = copy.InventoryData[i]; // TODO: This allows someone to copy objects from another // agent's inventory. Should we allow that, or do any // permission checks? Dictionary agentInventory = GetAgentInventory(block.OldAgentID); // Get the original object InventoryObject obj; if (agentInventory.TryGetValue(block.OldItemID, out obj) && obj is InventoryItem) { InventoryItem item = (InventoryItem)obj; // Get the new folder InventoryObject folderObj; if (agentInventory.TryGetValue(block.NewFolderID, out folderObj) && folderObj is InventoryFolder) { string newName = Utils.BytesToString(block.NewName); if (String.IsNullOrEmpty(newName)) newName = item.Name; // Create the copy CreateItem(agent.AgentID, newName, item.Description, item.InventoryType, item.AssetType, item.AssetID, folderObj.ID, item.Permissions.OwnerMask, item.Permissions.NextOwnerMask, agent.AgentID, item.CreatorID, UUID.Zero, block.CallbackID); } else { Logger.Log("CopyInventoryItem called with an unknown target folder " + block.NewFolderID, Helpers.LogLevel.Warning); } } else { Logger.Log("CopyInventoryItem called for an unknown item " + block.OldItemID, Helpers.LogLevel.Warning); } } } void MoveInventoryItemHandler(Packet packet, Agent agent) { MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet; // TODO: What is move.AgentData.Stamp for? Dictionary agentInventory = GetAgentInventory(agent.AgentID); for (int i = 0; i < move.InventoryData.Length; i++) { MoveInventoryItemPacket.InventoryDataBlock block = move.InventoryData[i]; UUID newFolderID = block.FolderID; if (newFolderID == UUID.Zero) newFolderID = agent.InventoryRoot; MoveInventory(agentInventory, block.ItemID, newFolderID, Utils.BytesToString(block.NewName)); } } void MoveInventoryFolderHandler(Packet packet, Agent agent) { MoveInventoryFolderPacket move = (MoveInventoryFolderPacket)packet; // TODO: What is move.AgentData.Stamp for? Dictionary agentInventory = GetAgentInventory(agent.AgentID); for (int i = 0; i < move.InventoryData.Length; i++) { MoveInventoryFolderPacket.InventoryDataBlock block = move.InventoryData[i]; UUID newFolderID = block.ParentID; if (newFolderID == UUID.Zero) newFolderID = agent.InventoryRoot; MoveInventory(agentInventory, block.FolderID, newFolderID, null); } } void MoveInventory(Dictionary agentInventory, UUID objectID, UUID newFolderID, string newName) { InventoryObject obj; if (agentInventory.TryGetValue(objectID, out obj)) { lock (agentInventory) { InventoryObject newParentObj; if (agentInventory.TryGetValue(newFolderID, out newParentObj) && newParentObj is InventoryFolder) { // Remove this item from the current parent if (obj.Parent != null) { InventoryFolder parent = (InventoryFolder)obj.Parent; lock (parent.Children.Dictionary) parent.Children.Dictionary.Remove(obj.ID); } // Update the new parent InventoryFolder newParent = (InventoryFolder)newParentObj; newParent.Children.Dictionary[obj.ID] = obj; // Update the object obj.ParentID = newParent.ID; obj.Parent = newParent; if (!String.IsNullOrEmpty(newName)) obj.Name = newName; } else { Logger.Log("MoveInventory called with an unknown destination folder " + newFolderID, Helpers.LogLevel.Warning); } } } else { Logger.Log("MoveInventory called for an unknown object " + objectID, Helpers.LogLevel.Warning); } } void PurgeInventoryDescendentsHandler(Packet packet, Agent agent) { PurgeInventoryDescendentsPacket purge = (PurgeInventoryDescendentsPacket)packet; Dictionary agentInventory = GetAgentInventory(agent.AgentID); InventoryObject obj; if (agentInventory.TryGetValue(purge.InventoryData.FolderID, out obj) && obj is InventoryFolder) { lock (agentInventory) PurgeFolder(agentInventory, (InventoryFolder)obj); } else { Logger.Log("PurgeInventoryDescendents called on a missing folder " + purge.InventoryData.FolderID, Helpers.LogLevel.Warning); } } void PurgeFolder(Dictionary inventory, InventoryFolder folder) { folder.Children.ForEach( delegate(InventoryObject child) { inventory.Remove(child.ID); if (child is InventoryFolder) { InventoryFolder childFolder = (InventoryFolder)child; PurgeFolder(inventory, childFolder); } } ); lock (folder.Children.Dictionary) folder.Children.Dictionary.Clear(); } public bool CreateRootFolder(UUID agentID, UUID folderID, string name, UUID ownerID) { Dictionary agentInventory = GetAgentInventory(agentID); lock (agentInventory) { if (!agentInventory.ContainsKey(folderID)) { InventoryFolder folder = new InventoryFolder(); folder.Name = name; folder.OwnerID = agentID; folder.ParentID = UUID.Zero; folder.Parent = null; folder.PreferredType = AssetType.Folder; folder.ID = folderID; folder.Version = 1; Logger.DebugLog("Creating root inventory folder " + folder.Name); agentInventory[folder.ID] = folder; return true; } else { Logger.Log(String.Format( "Cannot create root inventory folder, item {0} already exists", folderID), Helpers.LogLevel.Warning); } } return false; } public bool CreateFolder(UUID agentID, UUID folderID, string name, AssetType preferredType, UUID parentID, UUID ownerID) { Dictionary agentInventory = GetAgentInventory(agentID); lock (agentInventory) { InventoryObject parent; if (agentInventory.TryGetValue(parentID, out parent) && parent is InventoryFolder) { InventoryFolder parentFolder = (InventoryFolder)parent; if (!agentInventory.ContainsKey(folderID)) { InventoryFolder folder = new InventoryFolder(); folder.Name = name; folder.OwnerID = agentID; folder.ParentID = parentID; folder.Parent = parentFolder; folder.PreferredType = preferredType; folder.ID = folderID; folder.Version = 1; Logger.DebugLog("Creating inventory folder " + folder.Name); agentInventory[folder.ID] = folder; lock (parentFolder.Children.Dictionary) parentFolder.Children.Dictionary[folder.ID] = folder; return true; } else { Logger.Log(String.Format( "Cannot create new inventory folder, item {0} already exists", folderID), Helpers.LogLevel.Warning); } } else { Logger.Log(String.Format( "Cannot create new inventory folder, parent folder {0} does not exist", parentID), Helpers.LogLevel.Warning); } } return false; } public UUID CreateItem(UUID agentID, string name, string description, InventoryType invType, AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID) { Dictionary agentInventory = GetAgentInventory(agentID); lock (agentInventory) { InventoryObject parent; if (agentInventory.TryGetValue(parentID, out parent) && parent is InventoryFolder) { InventoryFolder parentFolder = (InventoryFolder)parent; // Create an item InventoryItem item = new InventoryItem(); item.ID = UUID.Random(); item.InventoryType = invType; item.ParentID = parentID; item.Parent = parentFolder; item.Name = name; item.Description = description; item.Permissions.BaseMask = PermissionMask.All; item.Permissions.EveryoneMask = PermissionMask.All; item.Permissions.GroupMask = PermissionMask.All; //item.Permissions.OwnerMask = ownerMask; //item.Permissions.NextOwnerMask = nextOwnerMask; item.Permissions.OwnerMask = PermissionMask.All; item.Permissions.NextOwnerMask = PermissionMask.All; item.AssetType = type; item.AssetID = assetID; item.OwnerID = agentID; item.CreatorID = agentID; item.CreationDate = DateTime.Now; Logger.DebugLog(String.Format("Creating inventory item {0} (InvType: {1}, AssetType: {2})", item.Name, item.InventoryType, item.AssetType)); // Store the inventory item agentInventory[item.ID] = item; lock (parentFolder.Children.Dictionary) parentFolder.Children.Dictionary[item.ID] = item; // Send a success response UpdateCreateInventoryItemPacket update = new UpdateCreateInventoryItemPacket(); update.AgentData.AgentID = agentID; update.AgentData.SimApproved = true; if (transactionID != UUID.Zero) update.AgentData.TransactionID = transactionID; else update.AgentData.TransactionID = UUID.Random(); update.InventoryData = new UpdateCreateInventoryItemPacket.InventoryDataBlock[1]; update.InventoryData[0] = new UpdateCreateInventoryItemPacket.InventoryDataBlock(); update.InventoryData[0].AssetID = assetID; update.InventoryData[0].BaseMask = (uint)PermissionMask.All; update.InventoryData[0].CallbackID = callbackID; update.InventoryData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); update.InventoryData[0].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); update.InventoryData[0].CreatorID = item.CreatorID; update.InventoryData[0].Description = Utils.StringToBytes(item.Description); update.InventoryData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask; update.InventoryData[0].Flags = item.Flags; update.InventoryData[0].FolderID = item.ParentID; update.InventoryData[0].GroupID = item.GroupID; update.InventoryData[0].GroupMask = (uint)item.Permissions.GroupMask; update.InventoryData[0].GroupOwned = item.GroupOwned; update.InventoryData[0].InvType = (sbyte)item.InventoryType; update.InventoryData[0].ItemID = item.ID; update.InventoryData[0].Name = Utils.StringToBytes(item.Name); update.InventoryData[0].NextOwnerMask = (uint)item.Permissions.NextOwnerMask; update.InventoryData[0].OwnerID = item.OwnerID; update.InventoryData[0].OwnerMask = (uint)item.Permissions.OwnerMask; update.InventoryData[0].SalePrice = item.SalePrice; update.InventoryData[0].SaleType = (byte)item.SaleType; update.InventoryData[0].Type = (sbyte)item.AssetType; Server.UDP.SendPacket(agentID, update, PacketCategory.Inventory); return item.ID; } else { Logger.Log(String.Format( "Cannot create new inventory item, folder {0} does not exist", parentID), Helpers.LogLevel.Warning); return UUID.Zero; } } } public bool TryGetInventory(UUID agentID, UUID objectID, out InventoryObject obj) { Dictionary inventory; if (Inventory.TryGetValue(agentID, out inventory)) { if (inventory.TryGetValue(objectID, out obj)) return true; } obj = null; return false; } public OpenMetaverse.InventoryFolder[] CreateInventorySkeleton(UUID agentID) { Dictionary inventory; if (Inventory.TryGetValue(agentID, out inventory)) { List folderList = new List(); lock (inventory) { foreach (InventoryObject obj in inventory.Values) { if (obj is InventoryFolder) folderList.Add((InventoryFolder)obj); } } OpenMetaverse.InventoryFolder[] folders = new OpenMetaverse.InventoryFolder[folderList.Count]; for (int i = 0; i < folderList.Count; i++) { InventoryFolder folder = folderList[i]; folders[i] = new OpenMetaverse.InventoryFolder(folder.ID); folders[i].DescendentCount = folder.Children.Count; folders[i].Name = folder.Name; folders[i].OwnerID = folder.OwnerID; folders[i].ParentUUID = folder.ParentID; folders[i].PreferredType = folder.PreferredType; folders[i].Version = folder.Version; } return folders; } else { Logger.Log("CreateInventorySkeleton() called with an unknown agent " + agentID.ToString(), Helpers.LogLevel.Warning); return null; } } #region Persistence LLSDMap SerializeItem(InventoryItem item) { LLSDMap itemMap = new LLSDMap(16); itemMap.Add("ID", LLSD.FromUUID(item.ID)); itemMap.Add("ParentID", LLSD.FromUUID(item.ParentID)); itemMap.Add("Name", LLSD.FromString(item.Name)); itemMap.Add("OwnerID", LLSD.FromUUID(item.OwnerID)); itemMap.Add("AssetID", LLSD.FromUUID(item.AssetID)); itemMap.Add("AssetType", LLSD.FromInteger((sbyte)item.AssetType)); itemMap.Add("InventoryType", LLSD.FromInteger((sbyte)item.InventoryType)); itemMap.Add("CreatorID", LLSD.FromUUID(item.CreatorID)); itemMap.Add("GroupID", LLSD.FromUUID(item.GroupID)); itemMap.Add("Description", LLSD.FromString(item.Description)); itemMap.Add("GroupOwned", LLSD.FromBoolean(item.GroupOwned)); itemMap.Add("Permissions", item.Permissions.GetLLSD()); itemMap.Add("SalePrice", LLSD.FromInteger(item.SalePrice)); itemMap.Add("SaleType", LLSD.FromInteger((byte)item.SaleType)); itemMap.Add("Flags", LLSD.FromUInteger((uint)item.Flags)); itemMap.Add("CreationDate", LLSD.FromDate(item.CreationDate)); return itemMap; } LLSDMap SerializeFolder(InventoryFolder folder) { LLSDMap folderMap = new LLSDMap(6); folderMap.Add("ID", LLSD.FromUUID(folder.ID)); folderMap.Add("ParentID", LLSD.FromUUID(folder.ParentID)); folderMap.Add("Name", LLSD.FromString(folder.Name)); folderMap.Add("OwnerID", LLSD.FromUUID(folder.OwnerID)); folderMap.Add("PreferredType", LLSD.FromInteger((sbyte)folder.PreferredType)); folderMap.Add("Version", LLSD.FromInteger(folder.Version)); return folderMap; } LLSDMap SerializeInventory(Dictionary agentInventory) { LLSDMap map = new LLSDMap(agentInventory.Count); foreach (KeyValuePair kvp in agentInventory) { LLSD value; if (kvp.Value is InventoryItem) value = SerializeItem((InventoryItem)kvp.Value); else value = SerializeFolder((InventoryFolder)kvp.Value); map.Add(kvp.Key.ToString(), value); } return map; } public LLSD Serialize() { LLSDMap map = new LLSDMap(Inventory.Count); int itemCount = 0; lock (Inventory) { foreach (KeyValuePair> kvp in Inventory) { map.Add(kvp.Key.ToString(), SerializeInventory(kvp.Value)); itemCount += kvp.Value.Count; } } Logger.Log(String.Format("Serializing the inventory store with {0} items", itemCount), Helpers.LogLevel.Info); return map; } InventoryItem DeserializeItem(LLSDMap itemMap) { InventoryItem item = new InventoryItem(); item.ID = itemMap["ID"].AsUUID(); item.ParentID = itemMap["ParentID"].AsUUID(); item.Name = itemMap["Name"].AsString(); item.OwnerID = itemMap["OwnerID"].AsUUID(); item.AssetID = itemMap["AssetID"].AsUUID(); item.AssetType = (AssetType)itemMap["AssetType"].AsInteger(); item.InventoryType = (InventoryType)itemMap["InventoryType"].AsInteger(); item.CreatorID = itemMap["CreatorID"].AsUUID(); item.GroupID = itemMap["GroupID"].AsUUID(); item.Description = itemMap["Description"].AsString(); item.GroupOwned = itemMap["GroupOwned"].AsBoolean(); item.Permissions = Permissions.FromLLSD(itemMap["Permissions"]); item.SalePrice = itemMap["SalePrice"].AsInteger(); item.SaleType = (SaleType)itemMap["SaleType"].AsInteger(); item.Flags = Utils.BytesToUInt(itemMap["Flags"].AsBinary()); item.CreationDate = itemMap["CreationDate"].AsDate(); return item; } InventoryFolder DeserializeFolder(LLSDMap folderMap) { InventoryFolder folder = new InventoryFolder(); folder.ID = folderMap["ID"].AsUUID(); folder.ParentID = folderMap["ParentID"].AsUUID(); folder.Name = folderMap["Name"].AsString(); folder.OwnerID = folderMap["OwnerID"].AsUUID(); folder.PreferredType = (AssetType)folderMap["PreferredType"].AsInteger(); folder.Version = folderMap["Version"].AsInteger(); return folder; } Dictionary DeserializeInventory(LLSDMap map) { Dictionary inventory = new Dictionary(); foreach (KeyValuePair kvp in map) { UUID objectID = (UUID)kvp.Key; LLSDMap objectMap = (LLSDMap)kvp.Value; InventoryObject obj; if (objectMap.ContainsKey("AssetID")) obj = DeserializeItem(objectMap); else obj = DeserializeFolder(objectMap); inventory[objectID] = obj; } return inventory; } public void Deserialize(LLSD serialized) { int itemCount = 0; LLSDMap map = (LLSDMap)serialized; lock (Inventory) { Inventory.Clear(); foreach (KeyValuePair kvp in map) { UUID agentID = (UUID)kvp.Key; Dictionary agentInventory = DeserializeInventory((LLSDMap)kvp.Value); itemCount += agentInventory.Count; Inventory[agentID] = agentInventory; } // Iterate over the inventory objects and connect them to each other foreach (Dictionary inventory in Inventory.Values) { foreach (InventoryObject obj in inventory.Values) { if (obj.ParentID != UUID.Zero) { InventoryObject parentObj; if (inventory.TryGetValue(obj.ParentID, out parentObj) && parentObj is InventoryFolder) { InventoryFolder parent = (InventoryFolder)parentObj; obj.Parent = parent; parent.Children.Dictionary[obj.ID] = obj; } else { Logger.Log(String.Format("Cannot find parent folder {0} for inventory item {1}", obj.ParentID, obj.ID), Helpers.LogLevel.Warning); } } } } } Logger.Log(String.Format("Deserialized the inventory store with {0} items", itemCount), Helpers.LogLevel.Info); } #endregion Persistence } }