diff --git a/OpenMetaverse.Tests/LocalInventoryTests.cs b/OpenMetaverse.Tests/LocalInventoryTests.cs deleted file mode 100644 index 3a2040cd..00000000 --- a/OpenMetaverse.Tests/LocalInventoryTests.cs +++ /dev/null @@ -1,384 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections; -using System.Net; -using OpenMetaverse; -using OpenMetaverse.Packets; -using NUnit.Framework; - -namespace OpenMetaverse.Tests -{ - public class ExposedInventory : Inventory - { - public Dictionary GetItems() - { - return Items; - } - - public void InjectUpdate(ItemData data) - { - base.manager_OnItemUpdate(data); - } - - public void InjectUpdate(FolderData data) - { - base.manager_OnFolderUpdate(data); - } - - public ExposedInventory(InventoryManager manager, InventorySkeleton skeleton) - : base(manager, skeleton) { } - } - - [TestFixture] - public class LocalInventoryTests - { - public NetworkManager Network; - public InventorySkeleton Skeleton; - public InventoryManager Manager; - public ExposedInventory Inventory; - - public UUID RootUUID; - public UUID OwnerUUID; - public GridClient Client; - - public LocalInventoryTests() - { - Client = new GridClient(); - Console.WriteLine("Logging in..."); - //string startLoc = NetworkManager.StartLocation("Hooper", 179, 18, 32); - Client.Network.Login("Testing", "Anvil", "testinganvil", "Unit Test Framework", "last", - "contact@libsecondlife.org"); - } - - ~LocalInventoryTests() - { - if (Client.Network.Connected) - Client.Network.Logout(); - } - - [SetUp] - public void Init() - { - Network = Client.Network; - Manager = Client.Inventory; - Skeleton = Manager.InventorySkeleton; - Inventory = new ExposedInventory(Manager, Skeleton); - OwnerUUID = Client.Self.AgentID; - RootUUID = Skeleton.RootUUID; - Assert.IsNotNull(Inventory.RootFolder, "Root folder is not managed."); - Assert.AreEqual(Skeleton.Folders.Length, Inventory.GetItems().Count, "Inventory skeleton partially managed."); - Assert.AreEqual(Skeleton.RootUUID, Inventory.RootFolder.UUID, "Skeleton root does not match inventory root."); - } - - [Test] - public void StructAbstractions() - { - FolderData newFolder = ValidFolder(RootUUID); - ItemData newItem = ValidItem(RootUUID); - - InventoryBase item = Inventory.Manage(newItem); - InventoryBase folder = Inventory.Manage(newFolder); - - // ItemData abstractions: - Assert.AreEqual(item.UUID, (item as InventoryItem).Data.UUID, "ItemData UUID abstraction failed."); - Assert.AreEqual(item.ParentUUID, (item as InventoryItem).Data.ParentUUID, "ItemData ParentUUID abstraction failed."); - Assert.AreEqual(item.Name, (item as InventoryItem).Data.Name, "ItemData Name abstraction failed."); - Assert.AreEqual(item.OwnerUUID, (item as InventoryItem).Data.OwnerID, "ItemData OwnerID abstraction failed."); - - // FolderData abstractions: - Assert.AreEqual(folder.UUID, (folder as InventoryFolder).Data.UUID, "FolderData UUID abstraction failed."); - Assert.AreEqual(folder.ParentUUID, (folder as InventoryFolder).Data.ParentUUID, "FolderData ParentUUID abstraction failed."); - Assert.AreEqual(folder.Name, (folder as InventoryFolder).Data.Name, "FolderData Name abstraction failed."); - Assert.AreEqual(folder.OwnerUUID, (folder as InventoryFolder).Data.OwnerID, "FolderData OwnerID abstraction failed."); - } - - [Test] - public void RejectUnowned() - { - FolderData badFolder = ValidFolder(RootUUID); - badFolder.OwnerID = UUID.Random(); - - ItemData badItem = ValidItem(RootUUID); - badItem.OwnerID = UUID.Random(); - - // Test folder: - Assert.IsNull(Inventory.Manage(badFolder), "Inventory managed folder it doesn't own."); - Assert.IsNull(Inventory[badFolder.UUID], "Inventory stored folder it doesn't own."); - - Inventory.InjectUpdate(badFolder); - Assert.IsNull(Inventory[badFolder.UUID], "Inventory stored and managed folder it doesn't own from update."); - - // Test item: - Assert.IsNull(Inventory.Manage(badItem), "Inventory managed item it doesn't own."); - Assert.IsNull(Inventory[badItem.UUID], "Inventory stored item it doesn't own."); - - Inventory.InjectUpdate(badItem); - Assert.IsNull(Inventory[badFolder.UUID], "Inventory stored and managed item it doesn't own from update."); - } - - [Test] - public void ExplicitManagment() - { - // Parent arrives before item. - { - FolderData newFolder = ValidFolder(RootUUID); - - ItemData newItem = ValidItem(newFolder.UUID); - - // Test folder, explicit manage: - - InventoryBase folder = Inventory.Manage(newFolder); - Assert.IsNotNull(folder, "Folder is not managed."); - Assert.IsInstanceOfType(typeof(InventoryFolder), folder, "Managed FolderData did not result in InventoryFolder wrapper."); - Assert.IsNotNull(Inventory[newFolder.UUID], "Folder not stored."); - Assert.IsEmpty((ICollection)((folder as InventoryFolder).Contents), "Folder not empty."); - Assert.AreSame(Inventory.RootFolder, folder.Parent, "Folder parent is not the root folder."); - - // Test item, explicit manage: - - InventoryBase item = Inventory.Manage(newItem); - Assert.IsNotNull(item, "Item is not managed."); - Assert.IsInstanceOfType(typeof(InventoryItem), item, "Managed ItemData did not result in InventoryItem wrapper."); - Assert.IsNotNull(Inventory[newItem.UUID], "Item not stored."); - Assert.IsNotNull(item.Parent, "Item's parent reference not updated."); - CollectionAssert.Contains(folder as InventoryFolder, item, "Folder not updated with managed item."); - Assert.AreEqual(1, (folder as InventoryFolder).Contents.Count, "Folder contains more then 1 item."); - } - - // Opposite scenerio, item arrives before parent. - { - FolderData newFolder = ValidFolder(RootUUID); - - ItemData newItem = ValidItem(newFolder.UUID); - - InventoryItem item = Inventory.Manage(newItem) as InventoryItem; - Assert.IsNotNull(item, "Parentless item not managed."); - Assert.IsNotNull(Inventory[newItem.UUID], "Parentless item not stored."); - Assert.IsNull(Inventory[newFolder.UUID], "Parent stored before FolderData for it obtained."); - Assert.IsNull(item.Parent, "Parent referenced before FolderData for it obtained."); - - InventoryFolder folder = Inventory.Manage(newFolder) as InventoryFolder; - CollectionAssert.Contains(folder, item, "Early item not added to late parent folder."); - } - } - - [Test] - public void UpdateManagment() - { - FolderData newFolder = ValidFolder(RootUUID); - - ItemData newItem = ValidItem(newFolder.UUID); - newItem.Name = "Foo"; - - // Reject updates from unmanaged items: - Inventory.InjectUpdate(newItem); - Assert.IsNull(Inventory[newItem.UUID], "Update for unmanaged item stored."); - - // Test new folder from update, child of managed folder: - Inventory.InjectUpdate(newFolder); - InventoryFolder folder = Inventory[newFolder.UUID] as InventoryFolder; - Assert.IsNotNull(folder, "New child of managed folder not stored."); - CollectionAssert.Contains(Inventory.RootFolder, folder, "Managed folder does not contain new child folder."); - - // Test new item from update: - Inventory.InjectUpdate(newItem); - InventoryItem item = Inventory[newItem.UUID] as InventoryItem; - Assert.IsNotNull(item, "New child of new folder not stored."); - CollectionAssert.Contains(folder, item, "New folder does not have reference to new item."); - - // Test wrapper data struct update: - Assert.AreEqual(item.Name, "Foo", "New folder ItemData wrap failue."); - newItem.Name = "Bar"; - Inventory.InjectUpdate(newItem); - Assert.AreEqual(item.Name, "Bar", "Item wrapper update failure."); - - Assert.AreEqual(0, folder.Data.DescendentCount, "New folder DescendentCount set from non-update."); - newFolder.DescendentCount = 1; - Inventory.InjectUpdate(newFolder); - Assert.AreEqual(1, folder.Data.DescendentCount, "Folder wrapper update failure."); - - // Test parent change update: - newItem.ParentUUID = RootUUID; - Inventory.InjectUpdate(newItem); - Assert.AreSame(item.Parent, Inventory.RootFolder, "New parent reference not set."); - CollectionAssert.DoesNotContain(folder, item, "Removal from old parent failed."); - CollectionAssert.Contains(Inventory.RootFolder, item, "Add to new parent failed."); - - } - - [Test] - public void Removal() - { - FolderData newFolder = ValidFolder(RootUUID); - ItemData newItem = ValidItem(newFolder.UUID); - - InventoryFolder folder = Inventory.Manage(newFolder); - InventoryItem item = Inventory.Manage(newItem); - - folder.Remove(); - CollectionAssert.DoesNotContain(Inventory, folder, "Delete failed to unmanage item."); - CollectionAssert.DoesNotContain(Inventory, item, "Delete failed to recurse."); - } - - [Test] - public void InventoryFromPath() - { - // Null path: - CollectionAssert.Contains(Inventory.InventoryFromPath(null), Inventory.RootFolder, "Null path does not return root"); - CollectionAssert.Contains(Inventory.InventoryFromPath(new string[0]), Inventory.RootFolder, "Empty path does not return root."); - - FolderData folder1 = ValidFolder(RootUUID); - folder1.Name = "Objects"; - FolderData folder1_1 = ValidFolder(folder1.UUID); - folder1_1.Name = "Trees"; - FolderData folder1_2 = ValidFolder(folder1.UUID); - folder1_2.Name = "Gizmos"; - FolderData folder1_3 = ValidFolder(folder1.UUID); - folder1_3.Name = "Purchases"; - - ItemData item1_1 = ValidItem(folder1.UUID); - item1_1.Name = "Object"; - ItemData item1_2 = ValidItem(folder1.UUID); - item1_2.Name = "Object"; - ItemData item1_3 = ValidItem(folder1.UUID); - item1_3.Name = "Tilly"; - ItemData item1_1_1 = ValidItem(folder1_1.UUID); - item1_1_1.Name = "Pine"; - ItemData item1_1_2 = ValidItem(folder1_1.UUID); - item1_1_2.Name = "Maple"; - ItemData item1_1_3 = ValidItem(folder1_1.UUID); - item1_1_3.Name = "Maple"; - ItemData item1_3_1 = ValidItem(folder1_3.UUID); - item1_3_1.Name = "Death Ray"; - ItemData item1_3_2 = ValidItem(folder1_3.UUID); - item1_3_2.Name = "Maple"; - - // Add all of these to the inventory: - FolderData[] folders = new FolderData[] { folder1, folder1_1, folder1_2, folder1_3 }; - ItemData[] items = new ItemData[] { item1_1, item1_1_1, item1_1_2, item1_1_3, item1_2, item1_3, item1_3_1, item1_3_2 }; - - foreach (FolderData folder in folders) - Inventory.Manage(folder); - foreach (ItemData item in items) - Inventory.Manage(item); - - /* Inventory looks like: - * Root - * folder1 - * item1_1 - * item1_2 - * item1_3 - * folder1_1 - * item1_1_1 - * item1_1_2 - * item1_1_3 - * folder1_2 - * folder1_3 - * item1_3_1 - * item1_3_2 - */ - List results = null; - // Test one item path. - results = Inventory.InventoryFromPath(new string[] { "Objects", "Tilly" }); - CollectionAssert.Contains(results, Inventory[item1_3.UUID], "Path search did not find item at path to it."); - Assert.AreEqual(results.Count, 1, "Unexpected results returned for single item path search."); - - // Test multiple items in same folder, with same path. - results = Inventory.InventoryFromPath(new string[] { "Objects", "Object" }); - CollectionAssert.AreEquivalent(results, new InventoryBase[] { Inventory[item1_2.UUID], Inventory[item1_1.UUID] }, "Multiple result, same folders path failed."); - - folder1_1.Name = "Purchases"; // Rename folder1_1 from "Trees" to "Purchases" - Inventory.InjectUpdate(folder1_1); - // folder1_1 and folder1_3 now have the same name. - - // Test items from different folders with the same path. - results = Inventory.InventoryFromPath(new string[] { "Objects", "Purchases", "Maple" }); - // This should return item1_3_2 "Maple" in folder1_3 and item 1_1_2 "Maple" and item1_1_3 "Maple" in folder1_1. - CollectionAssert.AreEquivalent(results, - new InventoryBase[] { Inventory[item1_3_2.UUID], Inventory[item1_1_2.UUID], Inventory[item1_1_3.UUID] }, - "Multiple result, different folders path failed."); - } - - [Test] - public void Move() - { - FolderData newFolder1 = ValidFolder(RootUUID); - ItemData newItem = ValidItem(newFolder1.UUID); - FolderData newFolder2 = ValidFolder(RootUUID); - - - InventoryFolder folder1 = Inventory.Manage(newFolder1); - InventoryFolder folder2 = Inventory.Manage(newFolder2); - InventoryItem item = Inventory.Manage(newItem); - - CollectionAssert.Contains(folder1, item); - - // Move item into folder2. - item.Move(folder2); - CollectionAssert.Contains(folder2, item, "Item not added to new parent's contents."); - Assert.AreSame(item.Parent, folder2, "Item parent reference not updated."); - Assert.AreEqual(item.ParentUUID, folder2.UUID, "Reference to parent's UUID not updated."); - - // Move folder2 into folder1 - folder2.Move(folder1); - CollectionAssert.Contains(folder1, folder2, "Folder2 not added to new parent's contents."); - Assert.AreSame(folder2.Parent, folder1, "Folder parent reference not updated."); - Assert.AreEqual(folder2.ParentUUID, folder1.UUID, "Reference to parent's UUID not updated"); - } - - [Test] - public void DataStructParsing() - { - ItemData item = new ItemData(); - item.AssetType = AssetType.Object; - item.AssetUUID = UUID.Random(); - item.CreationDate = DateTime.Today; - item.CreatorID = UUID.Random(); - item.Description = "Madonna"; - item.Flags = 0xF00; - item.GroupID = UUID.Random(); - item.InventoryType = InventoryType.Object; - item.Name = "Jimmy Jimmy"; - item.OwnerID = UUID.Random(); - item.ParentUUID = UUID.Random(); - item.Permissions = new Permissions(0xF00, 0xBA0, 0xCAF, 0xEBA, 0xBE); - item.SalePrice = 402; - item.SaleType = SaleType.Copy; - item.UUID = UUID.Random(); - - ItemData reconstituted; - Assert.IsTrue(ItemData.TryParse(item.ToString(), out reconstituted), "ItemData.ToString output parsing failed."); - Assert.IsTrue(item == reconstituted, "Reconstituted item is not identical to original."); - - FolderData folder = new FolderData(); - folder.Name = "What?"; - folder.OwnerID = UUID.Random(); - folder.ParentUUID = UUID.Random(); - folder.PreferredType = AssetType.LSLText; - folder.UUID = UUID.Random(); - folder.Version = 24; - // folder.DescendentCount = 2; // Not recorded in string form. - - FolderData reconFolder; - Assert.IsTrue(FolderData.TryParse(folder.ToString(), out reconFolder), "FolderData.ToString output parsing failed."); - Assert.IsTrue(folder == reconFolder, "Reconstituted folder is not identical to original."); - } - - private ItemData ValidItem(UUID parent) - { - ItemData item = new ItemData(UUID.Random()); - item.ParentUUID = parent; - item.OwnerID = OwnerUUID; - return item; - } - - private FolderData ValidFolder(UUID parent) - { - FolderData folder = new FolderData(UUID.Random()); - folder.ParentUUID = parent; - folder.OwnerID = OwnerUUID; - return folder; - } - - } -} \ No newline at end of file diff --git a/OpenMetaverse.Tests/MockNetworkManager.cs b/OpenMetaverse.Tests/MockNetworkManager.cs deleted file mode 100644 index 39a33f5b..00000000 --- a/OpenMetaverse.Tests/MockNetworkManager.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections; -using System.Net; -using OpenMetaverse; -using OpenMetaverse.Packets; -using NUnit.Framework; - -namespace OpenMetaverse.Tests -{ - public class MockNetworkManager : INetworkManager - { - public delegate void PacketSent(Packet packet, Simulator sim); - public event PacketSent OnPacketSent; - - #region INetworkManager Members - - public event NetworkManager.LoginCallback OnLogin; - - public event NetworkManager.ConnectedCallback OnConnected; - - public event NetworkManager.DisconnectedCallback OnDisconnected; - - - public Simulator CurrentSim - { - get { return null; } - } - - public uint CircuitCode - { - get { return 0; } - } - - private bool _Connected; - public bool Connected - { - get { return _Connected; } - } - - public UUID SessionID - { - get { return UUID.Zero; } - } - - public UUID SecureSessionID - { - get { return UUID.Zero; } - } - - public UUID AgentID - { - get { return UUID.Zero; } - } - - - public void RegisterCallback(PacketType type, NetworkManager.PacketCallback callback) - { - return; - } - - public void RegisterEventCallback(string capsEvent, Caps.EventQueueCallback callback) - { - return; - } - - public void RegisterLoginResponseCallback(NetworkManager.LoginResponseCallback callback, string[] options) - { - return; - } - - public void SendPacket(Packet packet, Simulator sim) - { - if (OnPacketSent != null) - { - OnPacketSent(packet, sim); - } - } - - public void SendPacket(Packet packet) - { - if (OnPacketSent != null) - { - OnPacketSent(packet, CurrentSim); - } - } - - public bool Login(LoginParams loginParams) - { - _Connected = true; - OnLogin(LoginStatus.Success, "Testing"); - OnConnected(null); - return true; - } - - public void Logout() - { - return; - } - - #endregion - } -} diff --git a/OpenMetaverse.Tests/RemoteInventoryTests.cs b/OpenMetaverse.Tests/RemoteInventoryTests.cs deleted file mode 100644 index cbe172e6..00000000 --- a/OpenMetaverse.Tests/RemoteInventoryTests.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using OpenMetaverse; -using OpenMetaverse.Packets; -using System.Threading; -using NUnit.Framework; - - - -namespace OpenMetaverse.Tests -{ - [TestFixture] - public class RemoteInventoryTests - { - public GridClient Client; - public Inventory Inventory; - public RemoteInventoryTests() - { - Client = new GridClient(); - Console.WriteLine("Logging in..."); - //string startLoc = NetworkManager.StartLocation("Hooper", 179, 18, 32); - Client.Network.Login("Testing", "Anvil", "testinganvil", "Unit Test Framework", "last", - "contact@libsecondlife.org"); - } - - [SetUp] - public void Init() - { - Assert.IsTrue(Client.Network.Connected, "Client is not connected to the grid"); - - Inventory = new Inventory(Client.Inventory, Client.Inventory.InventorySkeleton); - Assert.IsNotNull(Inventory.RootFolder, "Root folder is null."); - Assert.AreNotEqual(UUID.Zero, Inventory.RootUUID, "Root UUID is zero."); - } - - ~RemoteInventoryTests() - { - if (Client.Network.Connected) - Client.Network.Logout(); - } - - [Test] - public void Rename() - { - InventoryFolder objects = Inventory[Client.Inventory.FindFolderForType(AssetType.Object)] as InventoryFolder; - Assert.IsTrue(objects.DownloadContents(TimeSpan.FromSeconds(30)), "Initial contents request failure."); - List contents = objects.Contents; - CollectionAssert.IsNotEmpty(contents, "Objects folder does not contain any items"); - - InventoryBase randomthing = contents[0]; - UUID uuid = randomthing.UUID; - string oldName = randomthing.Name; - Random rand = new Random(); - string newName = rand.Next().ToString(); - Console.WriteLine("Renaming {0} to {1}", oldName, newName); - randomthing.Rename(newName); - - Assert.AreEqual(randomthing.Name, newName, "Local name update failed."); - - // Redownload the contents: - Assert.IsTrue(objects.DownloadContents(TimeSpan.FromSeconds(30)), "Post-update contents request failure."); - Assert.AreEqual(newName, Inventory[uuid].Name, "Remote update failed."); - } - - [Test] - public void Copy() - { - InventoryFolder notecards = Inventory[Client.Inventory.FindFolderForType(AssetType.Notecard)] as InventoryFolder; - Assert.IsTrue(notecards.DownloadContents(TimeSpan.FromSeconds(30)), "Initial contents request failure"); - List contents = notecards.Contents; - - Console.WriteLine("Notecards folder contents:"); - InventoryItem note = null; - foreach (InventoryBase ib in notecards) - { - if (ib is InventoryItem) - { - if (note == null || note.Data.InventoryType != InventoryType.Notecard) - { - note = ib as InventoryItem; - } - } - Console.WriteLine(ib.Name); - } - Console.WriteLine("Chosen note: {0}", note.Name); - - InventoryItem copy = note.Copy(Inventory.RootFolder, TimeSpan.FromSeconds(30)); - Assert.IsNotNull(copy, "Copied ItemData not wrapped."); - - Console.WriteLine("Copied item:"); - Console.WriteLine(copy.Data.ToString()); - - Console.WriteLine("Original item:"); - Console.WriteLine(note.Data.ToString()); - - ItemData theoretical = note.Data; - // All other properties should be identical. - theoretical.ParentUUID = Inventory.RootUUID; - theoretical.CreationDate = copy.Data.CreationDate; - theoretical.UUID = copy.UUID; - - Assert.IsNotNull(Inventory[copy.UUID], "Copy not managed."); - Assert.IsTrue(copy.Data == theoretical, "Copied item is not identical to original."); - } - } -} diff --git a/OpenMetaverse/AppearanceManager.cs b/OpenMetaverse/AppearanceManager.cs index f5f9589d..53cf2327 100644 --- a/OpenMetaverse/AppearanceManager.cs +++ b/OpenMetaverse/AppearanceManager.cs @@ -88,13 +88,8 @@ namespace OpenMetaverse public class WearableData { - public ItemData Item; + public InventoryWearable Item; public AssetWearable Asset; - public WearableType WearableType - { - get { return (WearableType)Item.Flags; } - set { Item.Flags = (uint)value; } - } } /// @@ -283,7 +278,7 @@ namespace OpenMetaverse /// Replace the current outfit with a list of wearables and set appearance /// /// List of wearables that define the new outfit - public void WearOutfit(List ibs) + public void WearOutfit(List ibs) { WearOutfit(ibs, true); } @@ -293,7 +288,7 @@ namespace OpenMetaverse /// /// List of wearables that define the new outfit /// Whether to bake textures for the avatar or not - public void WearOutfit(List ibs, bool bake) + public void WearOutfit(List ibs, bool bake) { _wearParams = new WearParams(ibs, bake); Thread appearanceThread = new Thread(new ThreadStart(StartWearOutfit)); @@ -303,18 +298,19 @@ namespace OpenMetaverse private WearParams _wearParams; private void StartWearOutfit() { - List ibs = (List)_wearParams.Param; - List wearables = new List(); - List attachments = new List(); + List ibs = (List)_wearParams.Param; + List wearables = new List(); + List attachments = new List(); - foreach (ItemData ib in ibs) + foreach (InventoryBase ib in ibs) { - if (ib.InventoryType == InventoryType.Wearable) - wearables.Add(ib); - else if (ib.InventoryType == InventoryType.Attachment || ib.InventoryType == InventoryType.Object) + if (ib is InventoryWearable) + wearables.Add((InventoryWearable)ib); + else if (ib is InventoryAttachment || ib is InventoryObject) attachments.Add(ib); } + SendAgentWearablesRequest(); WearablesRequestEvent.WaitOne(); ReplaceOutfitWearables(wearables); @@ -374,77 +370,67 @@ namespace OpenMetaverse private void StartWearOutfitFolder() { SendAgentWearablesRequest(); // request current wearables async - List wearables; - List attachments; + List wearables; + List attachments; if (!GetFolderWearables(_wearOutfitParams.Param, out wearables, out attachments)) // get wearables in outfit folder return; // TODO: this error condition should be passed back to the client somehow - + WearablesRequestEvent.WaitOne(); // wait for current wearables ReplaceOutfitWearables(wearables); // replace current wearables with outfit folder UpdateAppearanceFromWearables(_wearOutfitParams.Bake); AddAttachments(attachments, true); } - private bool GetFolderWearables(object _folder, out List wearables, out List attachments) + private bool GetFolderWearables(object _folder, out List wearables, out List attachments) { UUID folder; wearables = null; attachments = null; - if (_folder is string[]) + if (_folder.GetType() == typeof(string[])) { string[] path = (string[])_folder; - List results = Client.InventoryStore.InventoryFromPath(path, true); + folder = Client.Inventory.FindObjectByPath( + Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID, String.Join("/", path), 1000 * 20); - if (results.Count == 0) + if (folder == UUID.Zero) { Logger.Log("Outfit path " + path + " not found", Helpers.LogLevel.Error, Client); return false; } - folder = results[0].UUID; - } - else if (_folder is InventoryFolder) - { - folder = (_folder as InventoryFolder).UUID; } else folder = (UUID)_folder; - wearables = new List(); - attachments = new List(); + wearables = new List(); + attachments = new List(); + List objects = Client.Inventory.FolderContents(folder, Client.Self.AgentID, + false, true, InventorySortOrder.ByName, 1000 * 20); - InventoryFolder invFolder = Client.InventoryStore[folder] as InventoryFolder; - if (invFolder != null) + if (objects != null) { - if (invFolder.IsStale) - invFolder.DownloadContents(TimeSpan.FromSeconds(20)); - - foreach (InventoryBase ibase in invFolder) + foreach (InventoryBase ib in objects) { - if (ibase is InventoryItem) + if (ib is InventoryWearable) { - ItemData ib = (ibase as InventoryItem).Data; - if (ib.InventoryType == InventoryType.Wearable) - { - Logger.DebugLog("Adding wearable " + ib.Name, Client); - wearables.Add(ib); - } - else if (ib.InventoryType == InventoryType.Attachment) - { - Logger.DebugLog("Adding attachment (attachment) " + ib.Name, Client); - attachments.Add(ib); - } - else if (ib.InventoryType == InventoryType.Object) - { - Logger.DebugLog("Adding attachment (object) " + ib.Name, Client); - attachments.Add(ib); - } - else - { - Logger.DebugLog("Ignoring inventory item " + ib.Name, Client); - } + Logger.DebugLog("Adding wearable " + ib.Name, Client); + wearables.Add((InventoryWearable)ib); + } + else if (ib is InventoryAttachment) + { + Logger.DebugLog("Adding attachment (attachment) " + ib.Name, Client); + attachments.Add(ib); + } + else if (ib is InventoryObject) + { + Logger.DebugLog("Adding attachment (object) " + ib.Name, Client); + attachments.Add(ib); + } + else + { + Logger.DebugLog("Ignoring inventory item " + ib.Name, Client); } } } @@ -459,7 +445,7 @@ namespace OpenMetaverse } // this method will download the assets for all inventory items in iws - private void ReplaceOutfitWearables(List iws) + private void ReplaceOutfitWearables(List iws) { lock (Wearables.Dictionary) { @@ -473,11 +459,11 @@ namespace OpenMetaverse Wearables.Dictionary = preserve; - foreach (ItemData iw in iws) + foreach (InventoryWearable iw in iws) { WearableData wd = new WearableData(); wd.Item = iw; - Wearables.Dictionary[wd.WearableType] = wd; + Wearables.Dictionary[wd.Item.WearableType] = wd; } } } @@ -488,7 +474,7 @@ namespace OpenMetaverse /// A List containing the attachments to add /// If true, tells simulator to remove existing attachment /// first - public void AddAttachments(List attachments, bool removeExistingFirst) + public void AddAttachments(List attachments, bool removeExistingFirst) { // FIXME: Obey this const int OBJECTS_PER_PACKET = 4; @@ -505,9 +491,9 @@ namespace OpenMetaverse attachmentsPacket.ObjectData = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[attachments.Count]; for (int i = 0; i < attachments.Count; i++) { - if (attachments[i].InventoryType == InventoryType.Attachment) + if (attachments[i] is InventoryAttachment) { - ItemData attachment = attachments[i]; + InventoryAttachment attachment = (InventoryAttachment)attachments[i]; attachmentsPacket.ObjectData[i] = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock(); attachmentsPacket.ObjectData[i].AttachmentPt = 0; attachmentsPacket.ObjectData[i].EveryoneMask = (uint)attachment.Permissions.EveryoneMask; @@ -519,9 +505,9 @@ namespace OpenMetaverse attachmentsPacket.ObjectData[i].NextOwnerMask = (uint)attachment.Permissions.NextOwnerMask; attachmentsPacket.ObjectData[i].OwnerID = attachment.OwnerID; } - else if (attachments[i].InventoryType == InventoryType.Object) + else if (attachments[i] is InventoryObject) { - ItemData attachment = attachments[i]; + InventoryObject attachment = (InventoryObject)attachments[i]; attachmentsPacket.ObjectData[i] = new RezMultipleAttachmentsFromInvPacket.ObjectDataBlock(); attachmentsPacket.ObjectData[i].AttachmentPt = 0; attachmentsPacket.ObjectData[i].EveryoneMask = (uint)attachment.Permissions.EveryoneMask; @@ -549,7 +535,7 @@ namespace OpenMetaverse /// A to attach /// the on the avatar /// to attach the item to - public void Attach(ItemData item, AttachmentPoint attachPoint) + public void Attach(InventoryItem item, AttachmentPoint attachPoint) { Attach(item.UUID, item.OwnerID, item.Name, item.Description, item.Permissions, item.Flags, attachPoint); @@ -594,7 +580,7 @@ namespace OpenMetaverse /// Detach an item from avatar using an object /// /// An object - public void Detach(ItemData item) + public void Detach(InventoryItem item) { Detach(item.UUID); } @@ -816,11 +802,10 @@ namespace OpenMetaverse { WearableType type = (WearableType)update.WearableData[i].WearableType; WearableData data = new WearableData(); - ItemData itemData = new ItemData(update.WearableData[i].ItemID, InventoryType.Wearable); - itemData.AssetType = WearableTypeToAssetType(type); - itemData.AssetUUID = update.WearableData[i].AssetID; - data.Item = itemData; - data.WearableType = type; + data.Item = new InventoryWearable(update.WearableData[i].ItemID); + data.Item.WearableType = type; + data.Item.AssetType = WearableTypeToAssetType(type); + data.Item.AssetUUID = update.WearableData[i].AssetID; // Add this wearable to our collection lock (Wearables.Dictionary) Wearables.Dictionary[type] = data; @@ -1033,7 +1018,7 @@ namespace OpenMetaverse { foreach (KeyValuePair kvp in Wearables.Dictionary) { - Logger.DebugLog("Requesting asset for wearable item " + kvp.Value.WearableType + " (" + kvp.Value.Item.AssetUUID + ")", Client); + Logger.DebugLog("Requesting asset for wearable item " + kvp.Value.Item.WearableType + " (" + kvp.Value.Item.AssetUUID + ")", Client); AssetDownloads.Enqueue(new PendingAssetDownload(kvp.Value.Item.AssetUUID, kvp.Value.Item.AssetType)); } diff --git a/OpenMetaverse/AssetManager.cs b/OpenMetaverse/AssetManager.cs index 86cfc917..a9767840 100644 --- a/OpenMetaverse/AssetManager.cs +++ b/OpenMetaverse/AssetManager.cs @@ -193,7 +193,7 @@ namespace OpenMetaverse } #endregion Enums - + /* public static class AssetTypeParser { private static readonly ReversableDictionary AssetTypeMap = new ReversableDictionary(); @@ -231,7 +231,7 @@ namespace OpenMetaverse return "unknown"; } } - + */ #region Transfer Classes /// @@ -574,7 +574,7 @@ namespace OpenMetaverse return transfer.ID; } - public UUID RequestInventoryAsset(ItemData item, bool priority) + public UUID RequestInventoryAsset(InventoryItem item, bool priority) { return RequestInventoryAsset(item.AssetUUID, item.UUID, UUID.Zero, item.OwnerID, item.AssetType, priority); } diff --git a/OpenMetaverse/GridClient.cs b/OpenMetaverse/GridClient.cs index 1cbc0335..d1843490 100644 --- a/OpenMetaverse/GridClient.cs +++ b/OpenMetaverse/GridClient.cs @@ -135,19 +135,19 @@ namespace OpenMetaverse Sound = new SoundManager(this); Throttle = new AgentThrottle(this); - if (Settings.ENABLE_INVENTORY_STORE) - InventoryStore = new Inventory(Inventory); - if (Settings.ENABLE_LIBRARY_STORE) - LibraryStore = new Inventory(Inventory); + //if (Settings.ENABLE_INVENTORY_STORE) + // InventoryStore = new Inventory(Inventory); + //if (Settings.ENABLE_LIBRARY_STORE) + // LibraryStore = new Inventory(Inventory); - Inventory.OnSkeletonsReceived += - delegate(InventoryManager manager) - { - if (Settings.ENABLE_INVENTORY_STORE) - InventoryStore.InitializeFromSkeleton(Inventory.InventorySkeleton); - if (Settings.ENABLE_LIBRARY_STORE) - LibraryStore.InitializeFromSkeleton(Inventory.LibrarySkeleton); - }; + //Inventory.OnSkeletonsReceived += + // delegate(InventoryManager manager) + // { + // if (Settings.ENABLE_INVENTORY_STORE) + // InventoryStore.InitializeFromSkeleton(Inventory.InventorySkeleton); + // if (Settings.ENABLE_LIBRARY_STORE) + // LibraryStore.InitializeFromSkeleton(Inventory.LibrarySkeleton); + // }; } /// diff --git a/OpenMetaverse/Inventory.cs b/OpenMetaverse/Inventory.cs index 3089ca00..4d18b2b6 100644 --- a/OpenMetaverse/Inventory.cs +++ b/OpenMetaverse/Inventory.cs @@ -26,1197 +26,357 @@ using System; using System.Collections.Generic; -using System.Collections; -using System.Text; -using System.Threading; namespace OpenMetaverse { /// - /// Inventory is responsible for managing inventory items and folders. - /// It updates the InventoryFolders and InventoryItems with new FolderData - /// and ItemData received from the InventoryManager. Updates to inventory - /// folders/items that are not explicitly managed (via the Manage method) - /// are ignored. - /// - /// When the FolderData and/or ItemData indicates an inventory change that is not - /// reflected locally, then the Inventory will update the local inventory state - /// accordingly. Under normal circumstances (when inventory is modified - /// by the local client via the InventoryBase, InventoryFolder and InventoryItem - /// interfaces) it doesn't have to do this. + /// Exception class to identify inventory exceptions /// - public class Inventory : IEnumerable + public class InventoryException : Exception + { + public InventoryException(string message) + : base(message) { } + } + + /// + /// Responsible for maintaining inventory structure. Inventory constructs nodes + /// and manages node children as is necessary to maintain a coherant hirarchy. + /// Other classes should not manipulate or create InventoryNodes explicitly. When + /// A node's parent changes (when a folder is moved, for example) simply pass + /// Inventory the updated InventoryFolder and it will make the appropriate changes + /// to its internal representation. + /// + public class Inventory { /// - /// Delegate for . + /// Delegate to use for the OnInventoryObjectUpdated event. /// - /// Inventory that was updated. - public delegate void InventoryUpdate(InventoryBase inventory); + /// The state of the InventoryObject before the update occured. + /// The state of the InventoryObject after the update occured. + public delegate void InventoryObjectUpdated(InventoryBase oldObject, InventoryBase newObject); + /// + /// Delegate to use for the OnInventoryObjectRemoved event. + /// + /// The InventoryObject that was removed. + public delegate void InventoryObjectRemoved(InventoryBase obj); + /// + /// Delegate to use for the OnInventoryObjectUpdated event. + /// + /// The InventoryObject that has been stored. + public delegate void InventoryObjectAdded(InventoryBase obj); /// - /// Triggered when a managed inventory item or folder is updated. + /// Called when an InventoryObject's state is changed. /// - public event InventoryUpdate OnInventoryUpdate; - + public event InventoryObjectUpdated OnInventoryObjectUpdated; + /// + /// Called when an item or folder is removed from inventory. + /// + public event InventoryObjectRemoved OnInventoryObjectRemoved; + /// + /// Called when an item is first added to the local inventory store. + /// This will occur most frequently when we're initially downloading + /// the inventory from the server. + /// + /// This will also fire when another avatar or object offers us inventory + /// + public event InventoryObjectAdded OnInventoryObjectAdded; /// - /// Delegate for + /// The root folder of this avatars inventory /// - /// The Inventory that is managing - /// The inventory item or folder that was managed. - public delegate void InventoryManaged(Inventory inventory, InventoryBase ibase); + public InventoryFolder RootFolder + { + get { return RootNode.Data as InventoryFolder; } + set + { + UpdateNodeFor(value); + _RootNode = Items[value.UUID]; + } + } /// - /// Triggered when an inventory item is first managed. + /// The default shared library folder /// - public event InventoryManaged OnInventoryManaged; + public InventoryFolder LibraryFolder + { + get { return LibraryRootNode.Data as InventoryFolder; } + set + { + UpdateNodeFor(value); + _LibraryRootNode = Items[value.UUID]; + } + } + + private InventoryNode _LibraryRootNode; + private InventoryNode _RootNode; + + /// + /// The root node of the avatars inventory + /// + public InventoryNode RootNode + { + get + { + if (_RootNode == null) + throw new InventoryException("Root node unknown. Are you completely logged in?"); + return _RootNode; + } + } /// - /// Retrieves a managed InventoryBase from the Inventory. - /// Returns null if the UUID isn't managed by this Inventory. + /// The root node of the default shared library /// - /// The UUID of the InventoryBase to retrieve. - /// A managed InventoryBase. + public InventoryNode LibraryRootNode + { + get + { + if (_LibraryRootNode == null) + throw new InventoryException("Library Root node unknown. Are you completely logged in?"); + return _LibraryRootNode; + } + } + + public UUID Owner { + get { return _Owner; } + } + + private UUID _Owner; + + private GridClient Client; + private InventoryManager Manager; + private Dictionary Items = new Dictionary(); + + public Inventory(GridClient client, InventoryManager manager) + : this(client, manager, client.Self.AgentID) { } + + public Inventory(GridClient client, InventoryManager manager, UUID owner) + { + Client = client; + Manager = manager; + _Owner = owner; + if (owner == UUID.Zero) + Logger.Log("Inventory owned by nobody!", Helpers.LogLevel.Warning, Client); + Items = new Dictionary(); + } + + public List GetContents(InventoryFolder folder) + { + return GetContents(folder.UUID); + } + + /// + /// Returns the contents of the specified folder + /// + /// A folder's UUID + /// The contents of the folder corresponding to folder + /// When folder does not exist in the inventory + public List GetContents(UUID folder) + { + InventoryNode folderNode; + if (!Items.TryGetValue(folder, out folderNode)) + throw new InventoryException("Unknown folder: " + folder); + lock (folderNode.Nodes.SyncRoot) + { + List contents = new List(folderNode.Nodes.Count); + foreach (InventoryNode node in folderNode.Nodes.Values) + { + contents.Add(node.Data); + } + return contents; + } + } + + /// + /// Updates the state of the InventoryNode and inventory data structure that + /// is responsible for the InventoryObject. If the item was previously not added to inventory, + /// it adds the item, and updates structure accordingly. If it was, it updates the + /// InventoryNode, changing the parent node if item.parentUUID does + /// not match node.Parent.Data.UUID. + /// + /// You can not set the inventory root folder using this method + /// + /// The InventoryObject to store + public void UpdateNodeFor(InventoryBase item) + { + lock (Items) + { + InventoryNode itemParent = null; + if (item.ParentUUID != UUID.Zero && !Items.TryGetValue(item.ParentUUID, out itemParent)) + { + // OK, we have no data on the parent, let's create a fake one. + InventoryFolder fakeParent = new InventoryFolder(item.ParentUUID); + fakeParent.DescendentCount = 1; // Dear god, please forgive me. + itemParent = new InventoryNode(fakeParent); + Items[item.ParentUUID] = itemParent; + // Unfortunately, this breaks the nice unified tree + // while we're waiting for the parent's data to come in. + // As soon as we get the parent, the tree repairs itself. + Logger.DebugLog("Attempting to update inventory child of " + + item.ParentUUID.ToString() + " when we have no local reference to that folder", Client); + + if (Client.Settings.FETCH_MISSING_INVENTORY) + { + // Fetch the parent + List fetchreq = new List(1); + fetchreq.Add(item.ParentUUID); + //Manager.FetchInventory(fetchreq); // we cant fetch folder data! :-O + } + } + + InventoryNode itemNode; + if (Items.TryGetValue(item.UUID, out itemNode)) // We're updating. + { + InventoryNode oldParent = itemNode.Parent; + // Handle parent change + if (oldParent == null || itemParent == null || itemParent.Data.UUID != oldParent.Data.UUID) + { + if (oldParent != null) + { + lock (oldParent.Nodes.SyncRoot) + oldParent.Nodes.Remove(item.UUID); + } + if (itemParent != null) + { + lock (itemParent.Nodes.SyncRoot) + itemParent.Nodes[item.UUID] = itemNode; + } + } + + itemNode.Parent = itemParent; + + if (item != itemNode.Data) + FireOnInventoryObjectUpdated(itemNode.Data, item); + + itemNode.Data = item; + } + else // We're adding. + { + itemNode = new InventoryNode(item, itemParent); + Items.Add(item.UUID, itemNode); + FireOnInventoryObjectAdded(item); + } + } + } + + public InventoryNode GetNodeFor(UUID uuid) + { + return Items[uuid]; + } + + /// + /// Removes the InventoryObject and all related node data from Inventory. + /// + /// The InventoryObject to remove. + public void RemoveNodeFor(InventoryBase item) + { + lock (Items) + { + InventoryNode node; + if (Items.TryGetValue(item.UUID, out node)) + { + if (node.Parent != null) + lock (node.Parent.Nodes.SyncRoot) + node.Parent.Nodes.Remove(item.UUID); + Items.Remove(item.UUID); + FireOnInventoryObjectRemoved(item); + } + + // In case there's a new parent: + InventoryNode newParent; + if (Items.TryGetValue(item.ParentUUID, out newParent)) + { + lock (newParent.Nodes.SyncRoot) + newParent.Nodes.Remove(item.UUID); + } + } + } + + /// + /// Used to find out if Inventory contains the InventoryObject + /// specified by uuid. + /// + /// The UUID to check. + /// true if inventory contains uuid, false otherwise + public bool Contains(UUID uuid) + { + return Items.ContainsKey(uuid); + } + + public bool Contains(InventoryBase obj) + { + return Contains(obj.UUID); + } + + #region Operators + + /// + /// By using the bracket operator on this class, the program can get the + /// InventoryObject designated by the specified uuid. If the value for the corresponding + /// UUID is null, the call is equivelant to a call to RemoveNodeFor(this[uuid]). + /// If the value is non-null, it is equivelant to a call to UpdateNodeFor(value), + /// the uuid parameter is ignored. + /// + /// The UUID of the InventoryObject to get or set, ignored if set to non-null value. + /// The InventoryObject corresponding to uuid. public InventoryBase this[UUID uuid] { get { - InventoryBase item; - if (Items.TryGetValue(uuid, out item)) - return item; - return null; + InventoryNode node = Items[uuid]; + return node.Data; } - } - - protected Dictionary Items; - private InventoryManager _Manager; - - private UUID _Owner; - /// - /// The owner of this inventory. Inventorys can only manage items - /// owned by the same agent. - /// - public UUID Owner - { - get { return _Owner; } - private set { _Owner = value; } - } - - private UUID _RootUUID; - /// - /// The UUID of the root folder. - /// - public UUID RootUUID - { - get { return _RootUUID; } - private set { _RootUUID = value; } - } - - /// - /// Reference to the InventoryFolder representing the - /// inventory root folder, if it has been managed. - /// - public InventoryFolder RootFolder - { - get { return this[RootUUID] as InventoryFolder; } - } - - /// - /// Initializes an empty, rootless, ownerless inventory. - /// This is used so that we can have an Inventory instance before - /// the owner and root data is known. - /// - /// Manager for remote updates. - public Inventory(InventoryManager manager) - : this(manager, UUID.Zero, UUID.Zero) { } - - /// - /// Creates a new Inventory. Remote updates are sent via the manager - /// passed to this constructor. All folders contained within the InventorySkeleton - /// are automatically managed. The inventory takes on the owner of the skeleton. - /// - /// Manager for remote updates. - /// Skeleton of folders, inventory owner. - public Inventory(InventoryManager manager, InventorySkeleton skeleton) - : this(manager, skeleton.Owner, skeleton.RootUUID) - { - ManageSkeleton(skeleton); - } - - /// - /// Creates a new inventory. Remote updates are sent via the manager - /// passed to this constructor. This creates an empty inventory, with no managed items. - /// - /// Manager for remote updates. - /// Owner of this inventory. - /// - public Inventory(InventoryManager manager, UUID owner, UUID root) - { - _Manager = manager; - Owner = owner; - _RootUUID = root; - if (Items == null) - Items = new Dictionary(); - RegisterInventoryCallbacks(); - } - - protected internal void InitializeFromSkeleton(InventorySkeleton skeleton) - { - Owner = skeleton.Owner; - RootUUID = skeleton.RootUUID; - Items = new Dictionary(skeleton.Folders.Length); - ManageSkeleton(skeleton); - } - - /// - /// Manages all the folders in the skeleton, if the skeleton is owned - /// by the same agent. - /// - /// The skeleton with folders to manage. - /// true if Inventory's owner is skeleton's owner and management succeeded, false otherwise. - protected bool ManageSkeleton(InventorySkeleton skeleton) - { - if (skeleton.Owner != Owner) - return false; - foreach (FolderData folder in skeleton.Folders) + set { - Manage(folder); - } - return true; - } - - /// - /// Registers InventoryManager callbacks for inventory updates. - /// - protected virtual void RegisterInventoryCallbacks() - { - _Manager.OnFolderUpdate += new InventoryManager.FolderUpdate(manager_OnFolderUpdate); - _Manager.OnItemUpdate += new InventoryManager.ItemUpdate(manager_OnItemUpdate); - _Manager.OnAssetUpdate += new InventoryManager.AssetUpdate(manager_OnAssetUpdate); - _Manager.OnItemCreated += new InventoryManager.ItemCreatedCallback(_Manager_OnItemCreated); - } - - void _Manager_OnItemCreated(bool success, ItemData itemData) - { - if (Items.ContainsKey(itemData.ParentUUID)) - Manage(itemData); - } - - /// - /// Updates the AssetUUID of a managed InventoryItem's ItemData. - /// If the InventoryItem is not managed, the update is ignored. - /// - /// UUID of the item to update. - /// The item's new asset UUID. - protected void manager_OnAssetUpdate(UUID itemID, UUID newAssetID) - { - InventoryBase b; - if (Items.TryGetValue(itemID, out b)) - { - if (b is InventoryItem) + if (value != null) { - (b as InventoryItem).Data.AssetUUID = newAssetID; - } - } - } + // Log a warning if there is a UUID mismatch, this will cause problems + if (value.UUID != uuid) + Logger.Log("Inventory[uuid]: uuid " + uuid.ToString() + " is not equal to value.UUID " + + value.UUID.ToString(), Helpers.LogLevel.Warning, Client); - /// - /// Updates the ItemData of a managed InventoryItem. This may - /// change local inventory state if the local inventory is not - /// consistant with the new data. (Parent change, rename, etc) - /// If the item is not managed, the update is ignored. - /// - /// The updated ItemData. - protected void manager_OnItemUpdate(ItemData itemData) - { - InventoryBase item; - if (Items.TryGetValue(itemData.UUID, out item)) - { - if (item is InventoryItem) - { - Update(item as InventoryItem, itemData); - } - } - else - { - // Check if it's a child of a managed folder. - if (Items.ContainsKey(itemData.ParentUUID)) - Manage(itemData); - } - } - - /// - /// Updates the FolderData of a managed InventoryFolder. This - /// may change local inventory state if the local inventory is not - /// consistant with the new data (Parent change, rename, etc) - /// If the folder is not managed, the update is ignored. - /// - /// The updated FolderData. - protected void manager_OnFolderUpdate(FolderData folderData) - { - InventoryBase folder; - if (Items.TryGetValue(folderData.UUID, out folder)) - { - if (folder is InventoryFolder) - { - Update(folder as InventoryFolder, folderData); - } - } - else - { - // Check if it's a child of a managed folder. - if (Items.ContainsKey(folderData.ParentUUID)) - Manage(folderData); - } - } - - /// - /// Wraps the ItemData in a new InventoryItem. - /// You may override this method to use your own subclass of - /// InventoryItem. - /// - /// The ItemData to wrap. - /// A new InventoryItem wrapper for the ItemData. - protected virtual InventoryItem WrapItemData(ItemData data) - { - return new InventoryItem(_Manager, this, data); - } - - /// - /// Wraps the FolderData in a new InventoryFolder. - /// You may override this method to use your own subclass of - /// InventoryFolder. - /// - /// The FolderData to wrap. - /// A new InventoryFolder wrapper for the FolderData. - protected virtual InventoryFolder WrapFolderData(FolderData data) - { - return new InventoryFolder(_Manager, this, data); - } - - /// - /// Attempts to fetch and manage an inventory item from its item UUID. - /// This method will block until the item's ItemData is fetched from - /// the remote inventory. If the item is already managed by the inventory - /// returns the local managed InventoryItem wrapper. - /// - /// The ItemID of the inventory item to fetch. - /// Managed InventoryItem, null if fetch fails. - public InventoryItem Manage(UUID itemID) - { - InventoryBase ib; - if (Items.TryGetValue(itemID, out ib)) - { - // This method shouldn't really be used for retrieving items. - return ib as InventoryItem; - } - else - { - ItemData item; - if (_Manager.FetchItem(itemID, Owner, TimeSpan.FromSeconds(30), out item)) - { - return Manage(item); + UpdateNodeFor(value); } else { - return null; - } - } - } - - /// - /// Explicitly manage the inventory item given its ItemData. - /// If the item isn't managed, a new wrapper for it is created - /// and it is added to the local inventory. - /// If the item is already managed, this method returns its wrapper, - /// updating it with the ItemData passed to this method. - /// - /// The ItemData of the item to manage. - /// The managed InventoryItem wrapper. - public virtual InventoryItem Manage(ItemData item) - { - if (item.OwnerID == Owner) - { - InventoryBase b; - if (Items.TryGetValue(item.UUID, out b)) - { - //Logger.DebugLog(String.Format("{0}: {1} already managed, updating.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, item.Name)); - Update(b as InventoryItem, item); - return b as InventoryItem; - } - else - { - InventoryItem wrapper = WrapItemData(item); - //Logger.DebugLog(String.Format("{0}: {1} managed, {2} total.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, item.Name, Items.Count)); - lock (Items) - Items.Add(item.UUID, wrapper); - - if (OnInventoryManaged != null) + InventoryNode node; + if (Items.TryGetValue(uuid, out node)) { - try { OnInventoryManaged(this, wrapper); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, e); } - } - - return wrapper; - } - } - else - { - //Logger.DebugLog(String.Format("{0}: {1} is not owned by this inventory.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, item.Name)); - return null; - } - } - - - /// - /// Explicitly manage the inventory folder given its FolderData. - /// If the folder isn't managed, a new wrapper for it is created, - /// it is added to the local inventory, and any known children of - /// the folder are added to the folder's Contents. - /// If the folder is already managed, this method returns the folder's - /// wrapper, updating it with the FolderData passed to this method. - /// - /// The FolderData of the folder to manage. - /// The managed InventoryFolder wrapper. - public virtual InventoryFolder Manage(FolderData folder) - { - if (folder.OwnerID == Owner) - { - InventoryBase b; - if (Items.TryGetValue(folder.UUID, out b)) - { - //Logger.DebugLog(String.Format("{0}: {1} already managed, updating.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, folder.Name)); - Update(b as InventoryFolder, folder); - return b as InventoryFolder; - } - else - { - InventoryFolder wrapper = WrapFolderData(folder); - lock (Items) - Items.Add(folder.UUID, wrapper); - //Logger.DebugLog(String.Format("{0}: {1} managed, {2} total.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, folder.Name, Items.Count)); - // Folder is now managed, update its contents with known children. - foreach (InventoryBase item in Items.Values) - { - if (item.ParentUUID == folder.UUID) - { - wrapper.AddChild(item); - } - } - if (OnInventoryManaged != null) - { - try { OnInventoryManaged(this, wrapper); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, e); } - } - return wrapper; - } - } - else - { - //Logger.DebugLog(String.Format("{0}: {1} is not owned by this inventory.", (RootFolder == null) ? RootUUID.ToString() : RootFolder.Name, folder.Name)); - return null; - } - } - - /// - /// Unmanages an inventory item or folder. The item or folder will no - /// longer be automatically updated. - /// - /// - public virtual void Unmanage(InventoryBase item) - { - lock (Items) - Items.Remove(item.UUID); - //Logger.DebugLog("Unmanaging " + item.Name); - } - - protected void Update(InventoryItem item, ItemData update) - { - if (item.Data != update) - { - // Check for parent change: - if (item.Data.ParentUUID != update.ParentUUID) - { - item.LocalMove(update.ParentUUID, this[update.ParentUUID] as InventoryFolder); - } - item.Data = update; - FireInventoryUpdate(item); - } - } - - protected void Update(InventoryFolder folder, FolderData update) - { - if (folder.Data != update) - { - // Check for parent change: - if (folder.Data.ParentUUID != update.ParentUUID) - { - folder.LocalMove(update.ParentUUID, this[update.ParentUUID] as InventoryFolder); - } - folder.Data = update; - FireInventoryUpdate(folder); - } - } - - protected void FireInventoryUpdate(InventoryBase updatedInventory) - { - if (OnInventoryUpdate != null) - { - try { OnInventoryUpdate(updatedInventory); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, e); } - } - } - - #region Pathing - - /// - /// Fetches an inventory item or folder by path. - /// If the path starts with /, is ignored - /// and the path is located from the root. - /// - /// If is true, this method may take a while to return. - /// A "/"-seperated path, UNIX style. Accepts UUIDs or folder names. - /// The directory to begin in if the path does not start at the root. - /// Whether to fetch folder contents when they're needed. - /// Multiple items if the path is ambiguous. - public List InventoryFromPath(string path, InventoryFolder currentDirectory, bool fetchStale) - { - path = path.Trim(); - if (path.StartsWith("/")) - { - currentDirectory = RootFolder; - path = path.Remove(0, 1); - } - return InventoryFromPath(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries), currentDirectory, fetchStale); - } - - /// - /// Fetches an inventory item or folder by path, without fetching anything - /// from the remote inventory. - /// - /// If is true, this method may take a while to return. - /// Array whose elements are names or UUIDs of folders, representing the path to the desired item or folder. - /// Multiple items if the path is ambiguous. - public List InventoryFromPath(string[] path) - { - return InventoryFromPath(path, false); - } - - /// - /// Fetches an inventory item or folder by path, starting at the inventory's root folder. - /// - /// If is true, this method may take a while to return. - /// Array whose elements are names or UUIDs of folders, representing the path to the desired item or folder. - /// If a folder is stale, fetch its contents. - /// Multiple items if the path is ambiguous. - public List InventoryFromPath(string[] path, bool fetchStale) - { - return InventoryFromPath(path, RootFolder, fetchStale); - } - - /// - /// Fetches an inventory item or folder by path, starting at . - /// - /// If is true, this method may take a while to return. - /// Array whose elements are names or UUIDs of folders, representing the path to the desired item or folder. - /// Folder to start the path from. - /// Whether to fetch folder contents when they're needed. - /// Multiple items if the path is ambiguous. - public List InventoryFromPath(string[] path, InventoryFolder baseFolder, bool fetchStale) - { - if (path == null || path.Length == 0) - { - List one = new List(1); - one.Add(baseFolder); - return one; - } - - // Agenda stores an object[] which contains an InventoryFolder and an int - // the int represents the level in the path that the children of the InventoryFolder - // should satasfy. - List results = new List(); - Stack agenda = new Stack(); - agenda.Push(new object[] { baseFolder, 0 }); - while (agenda.Count > 0) - { - object[] currentData = agenda.Pop(); - InventoryFolder currentFolder = currentData[0] as InventoryFolder; - int goalLevel = (int)currentData[1]; - - if (path[goalLevel] == "..") - { - // The unix behavior at root is that it's its own parent. - InventoryFolder parent = currentFolder != RootFolder ? currentFolder.Parent : RootFolder; - if (goalLevel == path.Length - 1) // End of the path. - { - results.Add(parent); - } - else - { - agenda.Push(new object[] { parent, goalLevel + 1 }); - } - } - else if (path[goalLevel] == "." || String.IsNullOrEmpty(path[goalLevel])) - { - if (goalLevel == path.Length - 1) // End of the path. - { - results.Add(currentFolder); - } - else - { - agenda.Push(new object[] { currentFolder, goalLevel + 1 }); - } - } - else // We need to look at the children - { - if (fetchStale && currentFolder.IsStale) - currentFolder.DownloadContents(TimeSpan.FromSeconds(10)); - - foreach (InventoryBase child in currentFolder) - { - if (child.Name == path[goalLevel] || child.UUID.ToString() == path[goalLevel]) - { - if (goalLevel == path.Length - 1) // End of the path. - { - results.Add(child); - } - else - { - if (child is InventoryFolder) - { - agenda.Push(new object[] { child, goalLevel + 1 }); - } - } - } + RemoveNodeFor(node.Data); } } } - return results; } - #endregion Pathing + #endregion Operators + #region Event Firing - #region IEnumerable Members - - public IEnumerator GetEnumerator() + protected void FireOnInventoryObjectUpdated(InventoryBase oldObject, InventoryBase newObject) { - lock (Items) + if (OnInventoryObjectUpdated != null) { - foreach (KeyValuePair kvp in Items) - { - yield return kvp.Value; - } + try { OnInventoryObjectUpdated(oldObject, newObject); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() + protected void FireOnInventoryObjectRemoved(InventoryBase obj) { - return GetEnumerator(); - } - - #endregion - } - - public abstract class InventoryBase - { - /// - /// InventoryBase's item UUID as obtained from ItemData or FolderData. - /// - public abstract UUID UUID - { - get; - } - - /// - /// InventoryBase parent's item UUID, as obtained from ItemData or FolderData. - /// Setting this will not modify the remote inventory, it will only modify the local - /// ItemData or FolderData struct. - /// - public abstract UUID ParentUUID - { - get; - set; - } - - /// - /// Inventory base's name, as obtained from ItemData or FolderData. - /// Setting this will not modify the remote inventory, it will only modify - /// the local ItemData or FolderData struct. - /// - public abstract string Name - { - get; - set; - } - - /// - /// Inventory base's owner ID, as obtained from ItemData or FolderData. - /// - public abstract UUID OwnerUUID - { - get; - } - - /// - /// Gets the parent InventoryFolder referenced by ParentUUID. Returns null - /// if parent is not managed by the Inventory. - /// - public InventoryFolder Parent - { - get + if (OnInventoryObjectRemoved != null) { - return Inventory[ParentUUID] as InventoryFolder; + try { OnInventoryObjectRemoved(obj); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } } - private Inventory _Inventory; - protected Inventory Inventory + protected void FireOnInventoryObjectAdded(InventoryBase obj) { - get { return _Inventory; } - private set { _Inventory = value; } - } - - private InventoryManager _Manager; - protected InventoryManager Manager - { - get { return _Manager; } - private set { _Manager = value; } - } - - public InventoryBase(InventoryManager manager, Inventory inventory) - { - Inventory = inventory; - Manager = manager; - } - - /// - /// Moves this InventoryBase to a new folder. Updates local and - /// remote inventory. - /// - /// The folder to move this InventoryBase to. - public virtual void Move(InventoryFolder destination) - { - if (destination.UUID != ParentUUID) + if (OnInventoryObjectAdded != null) { - if (Parent != null) - { - Parent.RemoveChild(this); - } - ParentUUID = destination.UUID; - destination.AddChild(this); + try { OnInventoryObjectAdded(obj); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } } - // Subclass will call the InventoryManager method. - } - - /// - /// Removes this InventoryBase from the local Inventory and its parent's - /// Contents. - /// - protected internal virtual void LocalRemove() - { - if (Parent != null) - Parent.RemoveChild(this); - Inventory.Unmanage(this); - } - - /// - /// Changes the parent of this InventoryBase. Removing it from old parent's contents - /// and adding it to new parent's contents. - /// - /// The UUID of the new parent. - /// The InventoryFolder of the new parent. (may be null, if unmanaged) - protected internal virtual void LocalMove(UUID newParentUUID, InventoryFolder newParent) - { - if (ParentUUID != newParentUUID) - { - if (Parent != null) - { - Parent.RemoveChild(this); - } - if (newParent != null) - { - newParent.AddChild(this); - } - ParentUUID = newParentUUID; - } - } - - /// - /// Removes this InventoryBase from the remote and local inventory. - /// - public virtual void Remove() - { - LocalRemove(); - // Subclass will call the InventoryManager method. - } - - /// - /// Renames this InventoryBase, without moving it. - /// - /// The InventoryBase's new name. - public virtual void Rename(string newName) - { - Name = newName; - // Subclass will call InventoryManager method. - } - - public abstract void Give(UUID recipiant, bool particleEffect); - - public void Give(UUID recipiant) - { - Give(recipiant, false); - } - - public override bool Equals(object obj) - { - return (obj is InventoryBase) ? (obj as InventoryBase).UUID == UUID : false; - } - - public override int GetHashCode() - { - return UUID.GetHashCode(); - } - } - - public class InventoryItem : InventoryBase - { - public ItemData Data; - private Asset _Asset; - public virtual Asset Asset - { - get { return _Asset; } - protected set { _Asset = value; } - } - - public InventoryItem(InventoryManager manager, Inventory inv, UUID uuid, InventoryType type) - : this(manager, inv, new ItemData(uuid, type)) { } - - public InventoryItem(InventoryManager manager, Inventory inv, ItemData data) - : base(manager, inv) - { - Data = data; - if (Parent != null) - Parent.AddChild(this); - } - - #region InventoryBase Members - - public override UUID UUID - { - get { return Data.UUID; } - } - - public override UUID ParentUUID - { - get { return Data.ParentUUID; } - set { Data.ParentUUID = value; } - } - - public override string Name - { - get { return Data.Name; } - set { Data.Name = value; } - } - - public override UUID OwnerUUID - { - get { return Data.OwnerID; } - } - - /// - /// Requests that a copy of this item be made and placed in the - /// folder. This method is not synchronous, and returns immediately. The callback is called - /// with the new item's inventory data. - /// - /// The InventoryFolder to copy this item to. - /// The callback to call when the copy is complete. - public void Copy(InventoryFolder destination, InventoryManager.ItemCopiedCallback callback) - { - Manager.RequestCopyItem(UUID, destination.UUID, Data.Name, callback); - } - - /// - /// Synchronously requests a copy of this item be made and placed in the - /// folder. The copy is automatically managed. - /// - /// Location for the new copy. - /// Amount of time to wait for a server response. - /// A managed InventoryItem if copy successful, null if not. - public InventoryItem Copy(InventoryFolder destination, TimeSpan timeout) - { - ItemData copy; - if (Manager.CopyItem(UUID, destination.UUID, Name, timeout, out copy)) - return Inventory.Manage(copy) as InventoryItem; - else - return null; - } - - public override void Move(InventoryFolder destination) - { - base.Move(destination); - Manager.MoveItem(UUID, destination.UUID); - } - - public override void Remove() - { - base.Remove(); - Manager.RemoveItem(UUID); - } - - public override void Rename(string newName) - { - base.Rename(newName); - Manager.RenameItem(UUID, ParentUUID, newName); - } - - public override void Give(UUID recipiant, bool particleEffect) - { - Manager.GiveItem(UUID, Data.Name, Data.AssetType, recipiant, particleEffect); - } - - #endregion - - /// - /// Updates the remote inventory item with the local inventory - /// ItemData. - /// - public void Update() - { - Manager.RequestUpdateItem(Data); - } - } - - /// - /// - /// - public class InventoryFolder : InventoryBase, IEnumerable - { - /// - /// Delegate for InventoryFolder.OnContentsRetrieved - /// - /// The folder whose contents were retrieved. - public delegate void ContentsRetrieved(InventoryFolder folder); - - /// - /// Delegate for InventoryFolder.OnPartialContents - /// - /// The folder we're concerned with. - /// The contents that were just retrieved. - /// Number of contents remaining to be retrieved. - public delegate void PartialContents(InventoryFolder folder, ICollection contents, int remaining); - - /// - /// Triggered when the InventoryFolder's contents have been completely retrieved. - /// - public event ContentsRetrieved OnContentsRetrieved; - - /// - /// Triggered when the InventoryFolder's contents have been partially retrieved. - /// - public event PartialContents OnPartialContents; - - public FolderData Data; - - /// - /// The local contents of this InventoryFolder. This returns a copy of the - /// internal collection, so incurs a memory and CPU penalty. Consider enumerating - /// directly over the InventoryFolder. - /// - protected Dictionary _Contents; - public List Contents - { - get - { - lock (_Contents) - { - return new List(_Contents.Values); - } - } - } - - /// - /// true if all the folder's contents have been downloaded and managed. - /// false otherwise. - /// - public bool IsStale - { - get { return Data.DescendentCount == 0 || _Contents.Count != Data.DescendentCount; } - } - - public InventoryFolder(InventoryManager manager, Inventory inv, UUID uuid) - : this(manager, inv, new FolderData(uuid)) { } - - public InventoryFolder(InventoryManager manager, Inventory inv, FolderData data) - : base(manager, inv) - { - Data = data; - if (data.DescendentCount > 0) - _Contents = new Dictionary(data.DescendentCount); - else - _Contents = new Dictionary(); - - if (Parent != null) - Parent.AddChild(this); - } - - protected internal void AddChild(InventoryBase child) - { - lock (_Contents) - { - _Contents[child.UUID] = child; - } - } - - protected internal void RemoveChild(InventoryBase child) - { - lock (_Contents) - { - _Contents.Remove(child.UUID); - } - } - - #region InventoryBase Members - - public override UUID UUID - { - get { return Data.UUID; } - } - - public override UUID ParentUUID - { - get { return Data.ParentUUID; } - set { Data.ParentUUID = value; } - } - - public override string Name - { - get { return Data.Name; } - set { Data.Name = value; } - } - - public override UUID OwnerUUID - { - get { return Data.OwnerID; } - } - - public override void Move(InventoryFolder destination) - { - base.Move(destination); - Manager.MoveFolder(UUID, destination.UUID); - } - - public override void Remove() - { - base.Remove(); - Manager.RemoveFolder(UUID); - } - - protected internal override void LocalRemove() - { - // Recursively remove all children. - - // First we need to copy the children into our own list, because - // calling LocalRemove on the child causes the child to modify - // our Contents dictionary (through our RemoveChild method) - // and C# doesn't like iterating through a modified collection. - List children = new List(_Contents.Count); - lock (_Contents) - { - foreach (KeyValuePair child in _Contents) - { - children.Add(child.Value); - } - } - // Now actually do the removal: - foreach (InventoryBase child in children) - { - child.LocalRemove(); - } - base.LocalRemove(); - } - - public override void Rename(string newName) - { - base.Rename(newName); - Manager.RenameFolder(UUID, ParentUUID, newName); - } - - public override void Give(UUID recipiant, bool particleEffect) - { - // Attempt to use local copy of contents, so we dont block waiting to - // download contents. - if (!IsStale) - { - List itemContents = new List(_Contents.Count); - foreach (InventoryBase ib in this) - { - if (ib is InventoryItem) - { - itemContents.Add((ib as InventoryItem).Data); - } - } - //FIXME: Will we ever want to pass anything other then AssetType.Folder? - Manager.GiveFolder(UUID, Name, AssetType.Folder, recipiant, particleEffect, itemContents); - } - else - { - Manager.GiveFolder(UUID, Name, AssetType.Folder, recipiant, particleEffect); - } - } - - #endregion - - /// - /// Empties the folder, remotely and locally removing all items - /// in the folder RECURSIVELY. Be careful with this! - /// - public void Empty() - { - Manager.RemoveDescendants(UUID); - - - List children = null; - lock (_Contents) - { - // We need to copy the collection before removing, see comment - // in LocalRemove method. - children = new List(_Contents.Values); - } - - foreach (InventoryBase child in children) - { - child.LocalRemove(); - } - } - - /// - /// Retrieves the contents of this folder from the remote inventory. - /// This method is synchronous, and blocks until the contents are retrieved or - /// the timeout has expired. The contents are written to the - /// InventoryFolder.Contents dictionary. If the method times out, - /// InventoryFolder.Contents is left unchanged. - /// The contents retrieved (if successful) are automatically managed. - /// - /// TimeSpan to wait for a reply. - /// true if the contents were retrieved, false if timed out. - public bool DownloadContents(TimeSpan timeout) - { - List items; - List folders; - bool success = Manager.FolderContents(UUID, Data.OwnerID, true, true, InventorySortOrder.ByName, - timeout, out items, out folders); - if (success) - { - Dictionary contents = new Dictionary(items.Count + folders.Count); - foreach (ItemData item in items) - contents.Add(item.UUID, Inventory.Manage(item)); - foreach (FolderData folder in folders) - contents.Add(folder.UUID, Inventory.Manage(folder)); - - lock (_Contents) - _Contents = contents; - - Data.DescendentCount = Contents.Count; - } - return success; - } - - /// - /// Override for RequestContents that retrieves results - /// in order by name. - /// - public void RequestContents() - { - RequestContents(InventorySortOrder.ByName); - } - - /// - /// Asynchronously requests the folder's contents from the remote inventory. - /// The InventoryFolder.OnContentsRetrieved event - /// is raised when the new contents are written to the - /// InventoryFolder.Contents Dictionary. - /// The contents retrieved are automatically managed. - /// - /// The order in which results are returned. - public void RequestContents(InventorySortOrder sortOrder) - { - - Dictionary contents = null; - if (Data.DescendentCount > 0) - contents = new Dictionary(Data.DescendentCount); - else - contents = new Dictionary(); - - InventoryManager.PartialContentsCallback callback = - delegate(UUID folderid, ItemData[] items, FolderData[] folders, int remaining) - { - if (folderid != UUID) - return; - - if (items != null) - foreach (ItemData item in items) - contents.Add(item.UUID, Inventory.Manage(item)); - - if (folders != null) - foreach (FolderData folder in folders) - contents.Add(folder.UUID, Inventory.Manage(folder)); - - if (OnPartialContents != null) - { - try { OnPartialContents(this, contents.Values, remaining); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, e); } - } - if (remaining == 0) - { - lock (_Contents) - _Contents = contents; - Data.DescendentCount = contents.Count; - if (OnContentsRetrieved != null) - { - try { OnContentsRetrieved(this); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, e); } - } - } - }; - Manager.RequestFolderContents(UUID, Data.OwnerID, true, true, sortOrder, - null, callback); - } - - #region IEnumerable Members - - /// - /// Enumerates over the local contents of this folder. - /// Consider calling GetContents or RequestContents before enumerating - /// to synchronize the local folder contents with the remote folder contents. - /// - /// An enumerator for this InventoryFolder. - public IEnumerator GetEnumerator() - { - lock (_Contents) - { - foreach (KeyValuePair child in _Contents) - { - yield return child.Value; - } - } - } - - #endregion - - #region IEnumerable Members - - /// - /// - /// - /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); } #endregion diff --git a/OpenMetaverse/InventoryManager.cs b/OpenMetaverse/InventoryManager.cs index 14d9befb..3fcc2ac9 100644 --- a/OpenMetaverse/InventoryManager.cs +++ b/OpenMetaverse/InventoryManager.cs @@ -1,26 +1,26 @@ /* - * Copyright (c) 2007-2008, openmetaverse.org + * Copyright (c) 2006-2008, openmetaverse.org * All rights reserved. * - * - Redistribution and use in source and binary forms, with or without + * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names + * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ @@ -28,12 +28,10 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Threading; -using System.Globalization; using System.Text; using OpenMetaverse.Capabilities; using OpenMetaverse.StructuredData; using OpenMetaverse.Packets; -using System.IO; namespace OpenMetaverse { @@ -54,11 +52,9 @@ namespace OpenMetaverse /// Landmark Landmark = 3, /// Script - [Obsolete("See LSL")] - Script = 4, + //[Obsolete("See LSL")] Script = 4, /// Clothing - [Obsolete("See Wearable")] - Clothing = 5, + //[Obsolete("See Wearable")] Clothing = 5, /// Object, both single and coalesced Object = 6, /// Notecard @@ -72,22 +68,17 @@ namespace OpenMetaverse /// an LSL Script LSL = 10, /// - [Obsolete("See LSL")] - LSLBytecode = 11, + //[Obsolete("See LSL")] LSLBytecode = 11, /// - [Obsolete("See Texture")] - TextureTGA = 12, + //[Obsolete("See Texture")] TextureTGA = 12, /// - [Obsolete] - Bodypart = 13, + //[Obsolete] Bodypart = 13, /// - [Obsolete] - Trash = 14, + //[Obsolete] Trash = 14, /// Snapshot = 15, /// - [Obsolete] - LostAndFound = 16, + //[Obsolete] LostAndFound = 16, /// Attachment = 17, /// @@ -98,45 +89,6 @@ namespace OpenMetaverse Gesture = 20 } - public static class InventoryTypeParser - { - private static readonly ReversableDictionary InventoryTypeMap = new ReversableDictionary(); - - static InventoryTypeParser() - { - InventoryTypeMap.Add("sound", InventoryType.Sound); - InventoryTypeMap.Add("wearable", InventoryType.Wearable); - InventoryTypeMap.Add("gesture", InventoryType.Gesture); - InventoryTypeMap.Add("script", InventoryType.LSL); - InventoryTypeMap.Add("texture", InventoryType.Texture); - InventoryTypeMap.Add("landmark", InventoryType.Landmark); - InventoryTypeMap.Add("notecard", InventoryType.Notecard); - InventoryTypeMap.Add("object", InventoryType.Object); - InventoryTypeMap.Add("animation", InventoryType.Animation); - InventoryTypeMap.Add("snapshot", InventoryType.Snapshot); - InventoryTypeMap.Add("attach", InventoryType.Attachment); - InventoryTypeMap.Add("callcard", InventoryType.CallingCard); - } - - public static InventoryType Parse(string str) - { - InventoryType t; - if (InventoryTypeMap.TryGetValue(str, out t)) - return t; - else - return InventoryType.Unknown; - } - - public static string StringValueOf(InventoryType type) - { - string str; - if (InventoryTypeMap.TryGetKey(type, out str)) - return str; - else - return "unknown"; - } - } - /// /// Item Sale Status /// @@ -152,35 +104,6 @@ namespace OpenMetaverse Contents = 3 } - public static class SaleTypeParser - { - private static readonly ReversableDictionary SaleTypeMap = new ReversableDictionary(); - - static SaleTypeParser() - { - SaleTypeMap.Add("not", SaleType.Not); - SaleTypeMap.Add("cntn", SaleType.Contents); - SaleTypeMap.Add("copy", SaleType.Copy); - SaleTypeMap.Add("orig", SaleType.Original); - } - public static SaleType Parse(string str) - { - SaleType t; - if (SaleTypeMap.TryGetValue(str, out t)) - return t; - else - return SaleType.Not; - } - public static string StringValueOf(SaleType type) - { - string str; - if (SaleTypeMap.TryGetKey(type, out str)) - return str; - else - return "not"; - } - } - [Flags] public enum InventorySortOrder : int { @@ -200,10 +123,6 @@ namespace OpenMetaverse /// public enum DeRezDestination : byte { - /// - /// Take a copy of the item - /// - TakeCopy = 1, /// Derez to TaskInventory TaskInventory = 2, /// Take Object @@ -214,19 +133,72 @@ namespace OpenMetaverse #endregion Enums + #region Inventory Object Classes /// - /// Struct containing entire inventory state for an item. + /// Base Class for Inventory Items /// - public struct ItemData + public abstract class InventoryBase { /// of item/folder - public UUID UUID; + public readonly UUID UUID; /// of parent folder public UUID ParentUUID; /// Name of item/folder public string Name; /// Item/Folder Owners public UUID OwnerID; + + /// + /// Constructor, takes an itemID as a parameter + /// + /// The of the item + public InventoryBase(UUID itemID) + { + if (itemID == UUID.Zero) + Logger.Log("Initializing an InventoryBase with UUID.Zero", Helpers.LogLevel.Warning); + UUID = itemID; + } + + /// + /// Generates a number corresponding to the value of the object to support the use of a hash table, + /// suitable for use in hashing algorithms and data structures such as a hash table + /// + /// A Hashcode of all the combined InventoryBase fields + public override int GetHashCode() + { + return UUID.GetHashCode() ^ ParentUUID.GetHashCode() ^ Name.GetHashCode() ^ OwnerID.GetHashCode(); + } + + /// + /// Determine whether the specified object is equal to the current object + /// + /// InventoryBase object to compare against + /// true if objects are the same + public override bool Equals(object o) + { + InventoryBase inv = o as InventoryBase; + return inv != null && Equals(inv); + } + + /// + /// Determine whether the specified object is equal to the current object + /// + /// InventoryBase object to compare against + /// true if objects are the same + public virtual bool Equals(InventoryBase o) + { + return o.UUID == UUID + && o.ParentUUID == ParentUUID + && o.Name == Name + && o.OwnerID == OwnerID; + } + } + + /// + /// An Item in Inventory + /// + public class InventoryItem : InventoryBase + { /// The of this item public UUID AssetUUID; /// The combined of this item @@ -253,45 +225,63 @@ namespace OpenMetaverse /// UTC (Coordinated Universal Time) public DateTime CreationDate; - public ItemData(InventoryType type) - : this(UUID.Zero, type) { } - public ItemData(UUID uuid) - : this(uuid, InventoryType.Unknown) { } - public ItemData(UUID uuid, InventoryType type) - { - UUID = uuid; - InventoryType = type; - ParentUUID = UUID.Zero; - Name = String.Empty; - OwnerID = UUID.Zero; - AssetUUID = UUID.Zero; - Permissions = new Permissions(); - AssetType = AssetType.Unknown; - CreatorID = UUID.Zero; - Description = String.Empty; - GroupID = UUID.Zero; - GroupOwned = false; - SalePrice = 0; - SaleType = SaleType.Not; - Flags = 0; - CreationDate = DateTime.Now; - } + /// + /// Construct a new InventoryItem object + /// + /// The of the item + public InventoryItem(UUID itemID) + : base(itemID) { } + /// + /// Construct a new InventoryItem object of a specific Type + /// + /// The type of item from + /// of the item + public InventoryItem(InventoryType type, UUID itemID) : base(itemID) { InventoryType = type; } + + /// + /// Generates a number corresponding to the value of the object to support the use of a hash table. + /// Suitable for use in hashing algorithms and data structures such as a hash table + /// + /// A Hashcode of all the combined InventoryItem fields public override int GetHashCode() { - return UUID.GetHashCode(); + return AssetUUID.GetHashCode() ^ Permissions.GetHashCode() ^ AssetType.GetHashCode() ^ + InventoryType.GetHashCode() ^ Description.GetHashCode() ^ GroupID.GetHashCode() ^ + GroupOwned.GetHashCode() ^ SalePrice.GetHashCode() ^ SaleType.GetHashCode() ^ + Flags.GetHashCode() ^ CreationDate.GetHashCode(); } - public override bool Equals(object obj) + /// + /// Compares an object + /// + /// The object to compare + /// true if comparison object matches + public override bool Equals(object o) { - if (!(obj is ItemData)) - return false; + InventoryItem item = o as InventoryItem; + return item != null && Equals(item); + } - ItemData o = (ItemData)obj; - return o.UUID == UUID - && o.ParentUUID == ParentUUID - && o.Name == Name - && o.OwnerID == OwnerID + /// + /// Determine whether the specified object is equal to the current object + /// + /// The object to compare against + /// true if objects are the same + public override bool Equals(InventoryBase o) + { + InventoryItem item = o as InventoryItem; + return item != null && Equals(item); + } + + /// + /// Determine whether the specified object is equal to the current object + /// + /// The object to compare against + /// true if objects are the same + public bool Equals(InventoryItem o) + { + return base.Equals(o as InventoryBase) && o.AssetType == AssetType && o.AssetUUID == AssetUUID && o.CreationDate == CreationDate @@ -304,179 +294,260 @@ namespace OpenMetaverse && o.SalePrice == SalePrice && o.SaleType == SaleType; } - public static bool operator ==(ItemData lhs, ItemData rhs) - { - return lhs.Equals(rhs); - } - - public static bool operator !=(ItemData lhs, ItemData rhs) - { - return !(lhs == rhs); - } + } + /// + /// InventoryTexture Class representing a graphical image + /// + /// + public class InventoryTexture : InventoryItem + { /// - /// Returns the ItemData in the hierarchical bracket format - /// used in the Second Life client's notecards and inventory cache. + /// Construct an InventoryTexture object /// - /// a string representation of this ItemData - public override string ToString() - { - StringWriter writer = new StringWriter(); - ToString(writer); - return writer.ToString(); - } + /// A which becomes the + /// objects AssetUUID + public InventoryTexture(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Texture; + } + } + /// + /// InventorySound Class representing a playable sound + /// + public class InventorySound : InventoryItem + { /// - /// Writes the inventory item to the TextWriter in the hierarchical bracket format - /// used in the Second Life client's notecards and inventory cache. + /// Construct an InventorySound object /// - /// Writer to write to. - public void ToString(TextWriter writer) - { - writer.WriteLine("inv_item\t0"); - writer.WriteLine('{'); - writer.WriteLine("\titem_id\t{0}", UUID.ToString()); - writer.WriteLine("\tparent_id\t{0}", ParentUUID.ToString()); - // Permissions: - writer.WriteLine("permissions\t0"); - writer.WriteLine('{'); - writer.WriteLine("\tbase_mask\t{0}", String.Format("{0:x}", (uint)Permissions.BaseMask).PadLeft(8, '0')); - writer.WriteLine("\towner_mask\t{0}", String.Format("{0:x}", (uint)Permissions.OwnerMask).PadLeft(8, '0')); - writer.WriteLine("\tgroup_mask\t{0}", String.Format("{0:x}", (uint)Permissions.GroupMask).PadLeft(8, '0')); - writer.WriteLine("\teveryone_mask\t{0}", String.Format("{0:x}", (uint)Permissions.EveryoneMask).PadLeft(8, '0')); - writer.WriteLine("\tnext_owner_mask\t{0}", String.Format("{0:x}", (uint)Permissions.NextOwnerMask).PadLeft(8, '0')); - writer.WriteLine("\tcreator_id\t{0}", CreatorID.ToString()); - writer.WriteLine("\towner_id\t{0}", OwnerID.ToString()); - writer.WriteLine("\tlast_owner_id\t{0}", UUID.Zero); // FIXME? - writer.WriteLine("\tgroup_id\t{0}", GroupID.ToString()); - writer.WriteLine('}'); - - writer.WriteLine("\tasset_id\t{0}", AssetUUID.ToString()); - writer.WriteLine("\ttype\t{0}", AssetTypeParser.StringValueOf(AssetType)); - writer.WriteLine("\tinv_type\t{0}", InventoryTypeParser.StringValueOf(InventoryType)); - writer.WriteLine("\tflags\t{0}", string.Format("{0:x}", Flags).PadLeft(8, '0')); - - // Sale info: - writer.WriteLine("sale_info\t0"); - writer.WriteLine('{'); - writer.WriteLine("\tsale_type\t{0}", SaleTypeParser.StringValueOf(SaleType)); - writer.WriteLine("\tsale_price\t{0}", SalePrice); - writer.WriteLine('}'); - - writer.WriteLine("\tname\t{0}|", Name); - writer.WriteLine("\tdesc\t{0}|", Description); - writer.WriteLine("\tcreation_date\t{0}", Utils.DateTimeToUnixTime(CreationDate)); - writer.WriteLine('}'); - } + /// A which becomes the + /// objects AssetUUID + public InventorySound(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Sound; + } + } + /// + /// InventoryCallingCard Class, contains information on another avatar + /// + public class InventoryCallingCard : InventoryItem + { /// - /// Reads the ItemData from a string source. The string is wrapped - /// in a and passed to the - /// other method. + /// Construct an InventoryCallingCard object /// - /// String to parse ItemData from. - /// Parsed ItemData - public static ItemData Parse(string src) - { - return Parse(new StringReader(src)); - } - - /// - /// Reads an ItemData from a TextReader source. The format of the text - /// should be the same as the one used by Second Life Notecards. The TextReader should - /// be placed ideally on the line containing "inv_item" but parsing will succeed as long - /// as it is before the opening bracket immediately following the inv_item line. - /// The TextReader will be placed on the line following the inv_item's closing bracket. - /// - /// text source - /// Parsed item. - public static ItemData Parse(TextReader reader) - { - ItemData item = new ItemData(); - #region Parsing - TextData invItem = TextHierarchyParser.Parse(reader); - Console.WriteLine(invItem); - //if (invItem.Name == "inv_item") // YAY - item.UUID = new UUID(invItem.Nested["item_id"].Value); - item.ParentUUID = new UUID(invItem.Nested["parent_id"].Value); - item.AssetUUID = new UUID(invItem.Nested["asset_id"].Value); - item.AssetType = AssetTypeParser.Parse(invItem.Nested["type"].Value); - item.InventoryType = InventoryTypeParser.Parse(invItem.Nested["inv_type"].Value); - item.Flags = uint.Parse(invItem.Nested["flags"].Value, NumberStyles.HexNumber); - string rawName = invItem.Nested["name"].Value; - item.Name = rawName.Substring(0, rawName.LastIndexOf('|')); - string rawDesc = invItem.Nested["desc"].Value; - item.Description = rawDesc.Substring(0, rawDesc.LastIndexOf('|')); - item.CreationDate = Utils.UnixTimeToDateTime(uint.Parse(invItem.Nested["creation_date"].Value)); - - // Sale info: - TextData saleInfo = invItem.Nested["sale_info"]; - item.SalePrice = int.Parse(saleInfo.Nested["sale_price"].Value); - item.SaleType = SaleTypeParser.Parse(saleInfo.Nested["sale_type"].Value); - - TextData permissions = invItem.Nested["permissions"]; - item.Permissions = new Permissions(); - item.Permissions.BaseMask = (PermissionMask)uint.Parse(permissions.Nested["base_mask"].Value, NumberStyles.HexNumber); - item.Permissions.EveryoneMask = (PermissionMask)uint.Parse(permissions.Nested["everyone_mask"].Value, NumberStyles.HexNumber); - item.Permissions.GroupMask = (PermissionMask)uint.Parse(permissions.Nested["group_mask"].Value, NumberStyles.HexNumber); - item.Permissions.OwnerMask = (PermissionMask)uint.Parse(permissions.Nested["owner_mask"].Value, NumberStyles.HexNumber); - item.Permissions.NextOwnerMask = (PermissionMask)uint.Parse(permissions.Nested["next_owner_mask"].Value, NumberStyles.HexNumber); - item.CreatorID = new UUID(permissions.Nested["creator_id"].Value); - item.OwnerID = new UUID(permissions.Nested["owner_id"].Value); - item.GroupID = new UUID(permissions.Nested["group_id"].Value); - // permissions.Nested["last_owner_id"] // FIXME? - #endregion - return item; - } - - /// - /// - /// - /// String to parse from. - /// Parsed ItemData. - /// true if successful, false otherwise. - public static bool TryParse(string str, out ItemData item) - { - return TryParse(new StringReader(str), out item); - } - - - /// - /// - /// - /// Text source. - /// Parsed ItemData. - /// true if successful false otherwise. - public static bool TryParse(TextReader reader, out ItemData item) - { - try - { - item = Parse(reader); - } - catch (Exception e) - { - item = new ItemData(); - Logger.Log(e.Message, Helpers.LogLevel.Error, e); - return false; - } - - return true; + /// A which becomes the + /// objects AssetUUID + public InventoryCallingCard(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.CallingCard; } } /// - /// Struct containing all inventory state for a folder. + /// InventoryLandmark Class, contains details on a specific location /// - public struct FolderData + public class InventoryLandmark : InventoryItem + { + /// + /// Construct an InventoryLandmark object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryLandmark(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Landmark; + } + + /// + /// Landmarks use the ObjectType struct and will have a flag of 1 set if they have been visited + /// + public ObjectType LandmarkType + { + get { return (ObjectType)Flags; } + set { Flags = (uint)value; } + } + } + + /// + /// InventoryObject Class contains details on a primitive or coalesced set of primitives + /// + public class InventoryObject : InventoryItem + { + /// + /// Construct an InventoryObject object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryObject(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Object; + } + + /// + /// Get the Objects permission override settings + /// + /// These will indicate the which permissions that + /// will be overwritten when the object is rezzed in-world + /// + public ObjectType ObjectType + { + get { return (ObjectType)Flags; } + set { Flags = (uint)value; } + } + } + + /// + /// InventoryNotecard Class, contains details on an encoded text document + /// + public class InventoryNotecard : InventoryItem + { + /// + /// Construct an InventoryNotecard object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryNotecard(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Notecard; + } + } + + /// + /// InventoryCategory Class + /// + /// TODO: Is this even used for anything? + public class InventoryCategory : InventoryItem + { + /// + /// Construct an InventoryCategory object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryCategory(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Category; + } + } + + /// + /// InventoryLSL Class, represents a Linden Scripting Language object + /// + public class InventoryLSL : InventoryItem + { + /// + /// Construct an InventoryLSL object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryLSL(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.LSL; + } + } + + /// + /// InventorySnapshot Class, an image taken with the viewer + /// + public class InventorySnapshot : InventoryItem + { + /// + /// Construct an InventorySnapshot object + /// + /// A which becomes the + /// objects AssetUUID + public InventorySnapshot(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Snapshot; + } + } + + /// + /// InventoryAttachment Class, contains details on an attachable object + /// + public class InventoryAttachment : InventoryItem + { + /// + /// Construct an InventoryAttachment object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryAttachment(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Attachment; + } + + /// + /// Get the last AttachmentPoint this object was attached to + /// + public AttachmentPoint AttachmentPoint + { + get { return (AttachmentPoint)Flags; } + set { Flags = (uint)value; } + } + } + + /// + /// InventoryWearable Class, details on a clothing item or body part + /// + public class InventoryWearable : InventoryItem + { + /// + /// Construct an InventoryWearable object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryWearable(UUID itemID) : base(itemID) { InventoryType = InventoryType.Wearable; } + + /// + /// The , Skin, Shape, Skirt, Etc + /// + public WearableType WearableType + { + get { return (WearableType)Flags; } + set { Flags = (uint)value; } + } + } + + /// + /// InventoryAnimation Class, A bvh encoded object which animates an avatar + /// + public class InventoryAnimation : InventoryItem + { + /// + /// Construct an InventoryAnimation object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryAnimation(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Animation; + } + } + + /// + /// InventoryGesture Class, details on a series of animations, sounds, and actions + /// + public class InventoryGesture : InventoryItem + { + /// + /// Construct an InventoryGesture object + /// + /// A which becomes the + /// objects AssetUUID + public InventoryGesture(UUID itemID) : base(itemID) + { + InventoryType = InventoryType.Gesture; + } + } + + /// + /// A folder contains s and has certain attributes specific + /// to itself + /// + public class InventoryFolder : InventoryBase { - /// The of this item - public UUID UUID; - /// of parent folder - public UUID ParentUUID; - /// Name of item/folder - public string Name; - /// Item/Folder Owners - public UUID OwnerID; /// The Preferred for a folder. public AssetType PreferredType; /// The Version of this folder @@ -484,212 +555,84 @@ namespace OpenMetaverse /// Number of child items this folder contains. public int DescendentCount; - public FolderData(UUID uuid) + /// + /// Constructor + /// + /// UUID of the folder + public InventoryFolder(UUID itemID) + : base(itemID) { } + + /// + /// + /// + /// + public override string ToString() { - UUID = uuid; - ParentUUID = UUID.Zero; - Name = String.Empty; - OwnerID = UUID.Zero; - PreferredType = AssetType.Unknown; - Version = 0; - DescendentCount = 0; + return Name; } + /// + /// + /// + /// public override int GetHashCode() { - return ParentUUID.GetHashCode() ^ Name.GetHashCode() ^ OwnerID.GetHashCode() ^ - PreferredType.GetHashCode() ^ Version.GetHashCode() ^ DescendentCount.GetHashCode(); + return PreferredType.GetHashCode() ^ Version.GetHashCode() ^ DescendentCount.GetHashCode(); } - public override bool Equals(object obj) + /// + /// + /// + /// + /// + public override bool Equals(object o) { - if (!(obj is FolderData)) - return false; + InventoryFolder folder = o as InventoryFolder; + return folder != null && Equals(folder); + } - FolderData o = (FolderData)obj; - return o.UUID == UUID - && o.ParentUUID == ParentUUID - && o.Name == Name - && o.OwnerID == OwnerID + /// + /// + /// + /// + /// + public override bool Equals(InventoryBase o) + { + InventoryFolder folder = o as InventoryFolder; + return folder != null && Equals(folder); + } + + /// + /// + /// + /// + /// + public bool Equals(InventoryFolder o) + { + return base.Equals(o as InventoryBase) && o.DescendentCount == DescendentCount && o.PreferredType == PreferredType && o.Version == Version; } - - public static bool operator ==(FolderData lhs, FolderData rhs) - { - return lhs.Equals(rhs); - } - - public static bool operator !=(FolderData lhs, FolderData rhs) - { - return !(lhs == rhs); - } - - /// - /// Returns the FolderData in the hierarchical bracket format - /// used in the Second Life client's notecards and inventory cache. - /// - /// a string representation of this FolderData - public override string ToString() - { - StringWriter writer = new StringWriter(); - ToString(writer); - return writer.ToString(); - } - - /// - /// Writes the FolderData to the TextWriter in the hierarchical bracket format - /// used in the Second Life client's notecards and inventory cache. - /// - /// Writer to write to. - public void ToString(TextWriter writer) - { - writer.WriteLine("inv_category\t0"); - writer.WriteLine('{'); - writer.WriteLine("\tcat_id\t{0}", UUID.ToString()); - writer.WriteLine("\tparent_id\t{0}", ParentUUID.ToString()); - writer.WriteLine("\ttype\tcategory"); - // TODO: Some folders have "-1" as their perf_type, investigate this. - writer.WriteLine("\tpref_type\t{0}", AssetTypeParser.StringValueOf(PreferredType)); - writer.WriteLine("\tname\t{0}|", Name); - writer.WriteLine("\towner_id\t{0}", OwnerID.ToString()); - writer.WriteLine("\tversion\t{0}", Version); - writer.WriteLine('}'); - } - - /// - /// Reads the FolderData from a string source. The string is wrapped - /// in a and passed to the - /// other method. - /// - /// String to parse FolderData from. - /// Parsed FolderData - public static FolderData Parse(string src) - { - return Parse(new StringReader(src)); - } - - /// - /// Reads an InventoryItem from a TextReader source. The format of the text - /// should be the same as the one used by Second Life Notecards. The TextReader should - /// be placed ideally on the line containing "inv_category" but parsing will succeed as long - /// as it is before the opening bracket immediately following the inv_category line. - /// The TextReader will be placed on the line following the inv_category's closing bracket. - /// - /// text source - /// Parsed item. - public static FolderData Parse(TextReader reader) - { - FolderData folder = new FolderData(); - #region Parsing - TextData invCategory = TextHierarchyParser.Parse(reader); - - //if (invCategory.Name == "inv_category") // YAY - folder.UUID = new UUID(invCategory.Nested["cat_id"].Value); - string rawName = invCategory.Nested["name"].Value; - folder.Name = rawName.Substring(0, rawName.LastIndexOf('|')); - folder.OwnerID = new UUID(invCategory.Nested["owner_id"].Value); - folder.ParentUUID = new UUID(invCategory.Nested["parent_id"].Value); - folder.PreferredType = AssetTypeParser.Parse(invCategory.Nested["pref_type"].Value); - folder.Version = int.Parse(invCategory.Nested["version"].Value); - // TODO: Investigate invCategory.Nested["type"] - #endregion - return folder; - } - - public static bool TryParse(string str, out FolderData folder) - { - return TryParse(new StringReader(str), out folder); - } - - public static bool TryParse(TextReader reader, out FolderData folder) - { - try - { - folder = Parse(reader); - } - catch (Exception e) - { - folder = new FolderData(); - Logger.Log(e.Message, Helpers.LogLevel.Error, e); - return false; - } - return true; - } } - public class InventorySkeleton - { - public UUID RootUUID; - public UUID Owner; - public FolderData[] Folders; - public InventorySkeleton(UUID rootFolder, UUID owner) - { - RootUUID = rootFolder; - Owner = owner; - Folders = new FolderData[0]; - } - } + #endregion Inventory Object Classes /// /// Tools for dealing with agents inventory /// public class InventoryManager { - protected struct DescendentsRequest + protected struct InventorySearch { public UUID Folder; - public bool ReceivedResponse; - public FolderContentsCallback Callback; - public PartialContentsCallback PartialCallback; - public int Descendents; - public List FolderContents; - public List ItemContents; - public DescendentsRequest(UUID folder, FolderContentsCallback callback) - { - Folder = folder; - Callback = callback; - ReceivedResponse = false; - Descendents = 0; - FolderContents = new List(); - ItemContents = new List(); - PartialCallback = null; - } - } - - protected struct FetchRequest - { - public int ItemsFetched; - public Dictionary RequestedItems; - public FetchItemsCallback Callback; - - public FetchRequest(FetchItemsCallback callback, ICollection requestedItems) - { - ItemsFetched = 0; - Callback = callback; - RequestedItems = new Dictionary(requestedItems.Count); - foreach (UUID uuid in requestedItems) - RequestedItems.Add(uuid, null); - } - - public void StoreFetchedItem(ItemData item) - { - if (RequestedItems.ContainsKey(item.UUID) && RequestedItems[item.UUID] == null) - { - ++ItemsFetched; - RequestedItems[item.UUID] = item; - } - } + public UUID Owner; + public string[] Path; + public int Level; } #region Delegates - /// - /// Delegate for - /// - public delegate void SkeletonsReceived(InventoryManager manager); - /// /// Callback for inventory item creation finishing /// @@ -697,7 +640,7 @@ namespace OpenMetaverse /// item succeeded or not /// Inventory item being created. If success is /// false this will be null - public delegate void ItemCreatedCallback(bool success, ItemData item); + public delegate void ItemCreatedCallback(bool success, InventoryItem item); /// /// Callback for an inventory item being create from an uploaded asset @@ -711,43 +654,20 @@ namespace OpenMetaverse /// /// /// - /// - public delegate void ItemCopiedCallback(ItemData itemData); + /// + public delegate void ItemCopiedCallback(InventoryBase item); /// - /// Use this delegate to create a callback for RequestFolderContents. + /// /// - /// The folder whose contents were received. - /// The items in - /// The folders in - /// - public delegate void FolderContentsCallback(UUID folder, List Items, List Folders); + /// + public delegate void ItemReceivedCallback(InventoryItem item); /// - /// Use this delegate to create a callback for RequestFolderContents + /// Callback for an inventory folder updating /// - /// The folder whose contents were received. - /// The items in that were just received, null if none received. - /// The folders in that were just received, null if none received. - /// Number of item or folders that remain to be downloaded. - public delegate void PartialContentsCallback(UUID folder, ItemData[] items, FolderData[] folders, int remaining); - - /// - /// Use this delegate to create a callback for RequestFetchItems. - /// - /// The items retrieved. - /// - public delegate void FetchItemsCallback(List items); - - /// - /// The updated item data. - public delegate void ItemUpdate(ItemData itemData); - - /// - /// The updated folder data. - public delegate void FolderUpdate(FolderData folderData); - - public delegate void AssetUpdate(UUID itemID, UUID newAssetID); + /// UUID of the folder that was updated + public delegate void FolderUpdatedCallback(UUID folderID); /// /// Callback for when an inventory item is offered to us by another avatar or an object @@ -757,8 +677,8 @@ namespace OpenMetaverse /// The AssetType being offered /// Will be null if item is offered from an object /// will be true of item is offered from an object - /// Return UUID of destination folder to accept offer, UUID.Zero to decline it. - public delegate UUID ObjectOfferedCallback(InstantMessage offerDetails, AssetType type, UUID objectID, bool fromTask); + /// Return true to accept the offer, or false to decline it + public delegate bool ObjectOfferedCallback(InstantMessage offerDetails, AssetType type, UUID objectID, bool fromTask); /// /// Callback when an inventory object is accepted and received from a @@ -766,21 +686,18 @@ namespace OpenMetaverse /// the ItemID, as in ObjectOfferedCallback it is null when received /// from a task. /// - /// - /// - /// - /// - /// - public delegate void TaskItemReceivedCallback(UUID itemID, UUID folderID, UUID creatorID, + /// + /// + /// + /// + public delegate void TaskItemReceivedCallback(UUID itemID, UUID folderID, UUID creatorID, UUID assetID, InventoryType type); /// - /// Delegate for use with the and - /// methods. Raised when the path - /// is resolved to a UUID. + /// /// - /// A string representing the path to the UUID, with '/' seperators. - /// The item's UUID. + /// + /// public delegate void FindObjectByPathCallback(string path, UUID inventoryObjectID); /// @@ -804,30 +721,29 @@ namespace OpenMetaverse #endregion Delegates #region Events - /// - /// Raised when the inventory and library skeletons are received. - /// and - /// - public event SkeletonsReceived OnSkeletonsReceived; - public event AssetUpdate OnAssetUpdate; - - public event ItemCreatedCallback OnItemCreated; /// - /// Fired when a BulkUpdateInventory packet is received containing item data. + /// Fired when a reply to a RequestFetchInventory() is received /// /// - public event ItemUpdate OnItemUpdate; + public event ItemReceivedCallback OnItemReceived; /// - /// Fired when a BulkUpdateInventory packet is received containing folder data. + /// Fired when a response to a RequestFolderContents() is received /// - public event FolderUpdate OnFolderUpdate; + /// + public event FolderUpdatedCallback OnFolderUpdated; /// /// Fired when an object or another avatar offers us an inventory item /// public event ObjectOfferedCallback OnObjectOffered; + + /// + /// Fired when a response to FindObjectByPath() is received + /// + /// + public event FindObjectByPathCallback OnFindObjectByPath; /// /// Fired when a task inventory item is received @@ -848,17 +764,13 @@ namespace OpenMetaverse #endregion Events private GridClient _Client; - private NetworkManager _Network; - private AgentManager _Agents; - private InventorySkeleton _InventorySkeleton; - private InventorySkeleton _LibrarySkeleton; + private Inventory _Store; private Random _RandNumbers = new Random(); private object _CallbacksLock = new object(); private uint _CallbackPos; private Dictionary _ItemCreatedCallbacks = new Dictionary(); - private Dictionary _ItemCopiedCallbacks = new Dictionary(); - private List _DescendentsRequests = new List(); - private List _FetchRequests = new List(); + private Dictionary _ItemCopiedCallbacks = new Dictionary(); + private List _Searches = new List(); #region String Arrays @@ -953,143 +865,120 @@ namespace OpenMetaverse #region Properties - public InventorySkeleton LibrarySkeleton - { - get { return _LibrarySkeleton; } - set { _LibrarySkeleton = value; } - } - public InventorySkeleton InventorySkeleton - { - get { return _InventorySkeleton; } - set { _InventorySkeleton = value; } - } + /// + /// Get this agents Inventory data + /// + public Inventory Store { get { return _Store; } } #endregion Properties /// /// Default constructor /// - /// Reference to the SecondLife client - /// + /// Reference to the GridClient object public InventoryManager(GridClient client) - : this(client, client.Network, client.Self) { } - - public InventoryManager(GridClient client, NetworkManager network, AgentManager agents) { _Client = client; - _Network = network; - _Agents = agents; - _Network.RegisterCallback(PacketType.UpdateCreateInventoryItem, new NetworkManager.PacketCallback(UpdateCreateInventoryItemHandler)); - _Network.RegisterCallback(PacketType.SaveAssetIntoInventory, new NetworkManager.PacketCallback(SaveAssetIntoInventoryHandler)); - _Network.RegisterCallback(PacketType.BulkUpdateInventory, new NetworkManager.PacketCallback(BulkUpdateInventoryHandler)); - _Network.RegisterCallback(PacketType.InventoryDescendents, new NetworkManager.PacketCallback(InventoryDescendentsHandler)); - _Network.RegisterCallback(PacketType.FetchInventoryReply, new NetworkManager.PacketCallback(FetchInventoryReplyHandler)); - _Network.RegisterCallback(PacketType.ReplyTaskInventory, new NetworkManager.PacketCallback(ReplyTaskInventoryHandler)); + _Client.Network.RegisterCallback(PacketType.UpdateCreateInventoryItem, new NetworkManager.PacketCallback(UpdateCreateInventoryItemHandler)); + _Client.Network.RegisterCallback(PacketType.SaveAssetIntoInventory, new NetworkManager.PacketCallback(SaveAssetIntoInventoryHandler)); + _Client.Network.RegisterCallback(PacketType.BulkUpdateInventory, new NetworkManager.PacketCallback(BulkUpdateInventoryHandler)); + _Client.Network.RegisterCallback(PacketType.MoveInventoryItem, new NetworkManager.PacketCallback(MoveInventoryItemHandler)); + _Client.Network.RegisterCallback(PacketType.InventoryDescendents, new NetworkManager.PacketCallback(InventoryDescendentsHandler)); + _Client.Network.RegisterCallback(PacketType.FetchInventoryReply, new NetworkManager.PacketCallback(FetchInventoryReplyHandler)); + _Client.Network.RegisterCallback(PacketType.ReplyTaskInventory, new NetworkManager.PacketCallback(ReplyTaskInventoryHandler)); + // Watch for inventory given to us through instant message - _Agents.OnInstantMessage += new AgentManager.InstantMessageCallback(Self_OnInstantMessage); + _Client.Self.OnInstantMessage += new AgentManager.InstantMessageCallback(Self_OnInstantMessage); // Register extra parameters with login and parse the inventory data that comes back - List options = new List(5); - if (Settings.ENABLE_INVENTORY_STORE) - { - options.Add("inventory-root"); - options.Add("inventory-skeleton"); - } - if (Settings.ENABLE_LIBRARY_STORE) - { - options.Add("inventory-lib-root"); - options.Add("inventory-lib-owner"); - options.Add("inventory-skel-lib"); - } - if (Settings.ENABLE_INVENTORY_STORE || Settings.ENABLE_LIBRARY_STORE) - { - // Register extra parameters with login and parse the inventory data that comes back - _Network.RegisterLoginResponseCallback( - new NetworkManager.LoginResponseCallback(Network_OnLoginResponse), - options.ToArray()); - } + _Client.Network.RegisterLoginResponseCallback( + new NetworkManager.LoginResponseCallback(Network_OnLoginResponse), + new string[] { + "inventory-root", "inventory-skeleton", "inventory-lib-root", + "inventory-lib-owner", "inventory-skel-lib"}); } #region Fetch /// - /// Fetch a single inventory item. + /// Fetch an inventory item from the dataserver /// - /// The item's - /// The item owner's - /// The amount of time to wait for results. - /// The item retrieved. - /// true if successful, false if not. - public bool FetchItem(UUID itemID, UUID ownerID, TimeSpan timeout, out ItemData item) + /// The items + /// The item Owners + /// a integer representing the number of milliseconds to wait for results + /// An object on success, or null if no item was found + /// Items will also be sent to the event + public InventoryItem FetchItem(UUID itemID, UUID ownerID, int timeoutMS) { - List items = FetchItems(new UUID[] { itemID }, ownerID, timeout); - if (items == null || items.Count == 0) - { - item = new ItemData(); - return false; - } - else - { - item = items[0]; - return true; - } + AutoResetEvent fetchEvent = new AutoResetEvent(false); + InventoryItem fetchedItem = null; + + ItemReceivedCallback callback = + delegate(InventoryItem item) + { + if (item.UUID == itemID) + { + fetchedItem = item; + fetchEvent.Set(); + } + }; + + OnItemReceived += callback; + RequestFetchInventory(itemID, ownerID); + + fetchEvent.WaitOne(timeoutMS, false); + OnItemReceived -= callback; + + return fetchedItem; } /// - /// Fetch an inventory item from the dataserver + /// Request A single inventory item /// - /// The items + /// The items /// The item Owners - /// a TimeSpan representing the amount of time to wait for results - /// An object on success, or null if no item was found - /// Items will also be sent to the event - public List FetchItems(ICollection itemIDs, UUID ownerID, TimeSpan timeout) + /// + public void RequestFetchInventory(UUID itemID, UUID ownerID) { - AutoResetEvent fetchEvent = new AutoResetEvent(false); + FetchInventoryPacket fetch = new FetchInventoryPacket(); + fetch.AgentData = new FetchInventoryPacket.AgentDataBlock(); + fetch.AgentData.AgentID = _Client.Self.AgentID; + fetch.AgentData.SessionID = _Client.Self.SessionID; - List items = null; - FetchItemsCallback callback = - delegate(List fetchedItems) - { - items = fetchedItems; - fetchEvent.Set(); - }; - RequestFetchItems(itemIDs, ownerID, callback); - fetchEvent.WaitOne(timeout, false); - return items; + fetch.InventoryData = new FetchInventoryPacket.InventoryDataBlock[1]; + fetch.InventoryData[0] = new FetchInventoryPacket.InventoryDataBlock(); + fetch.InventoryData[0].ItemID = itemID; + fetch.InventoryData[0].OwnerID = ownerID; + + _Client.Network.SendPacket(fetch); } /// /// Request inventory items /// /// Inventory items to request - /// Owners of the inventory items - /// + /// Owners of the inventory items /// - public void RequestFetchItems(ICollection itemIDs, UUID ownerID, FetchItemsCallback callback) + public void RequestFetchInventory(List itemIDs, List ownerIDs) { - FetchRequest request = new FetchRequest(callback, itemIDs); - lock (_FetchRequests) - _FetchRequests.Add(request); + if (itemIDs.Count != ownerIDs.Count) + throw new ArgumentException("itemIDs and ownerIDs must contain the same number of entries"); - // Send the packet: FetchInventoryPacket fetch = new FetchInventoryPacket(); fetch.AgentData = new FetchInventoryPacket.AgentDataBlock(); - fetch.AgentData.AgentID = _Agents.AgentID; - fetch.AgentData.SessionID = _Agents.SessionID; + fetch.AgentData.AgentID = _Client.Self.AgentID; + fetch.AgentData.SessionID = _Client.Self.SessionID; fetch.InventoryData = new FetchInventoryPacket.InventoryDataBlock[itemIDs.Count]; - int i = 0; - foreach (UUID item in itemIDs) + for (int i = 0; i < itemIDs.Count; i++) { fetch.InventoryData[i] = new FetchInventoryPacket.InventoryDataBlock(); - fetch.InventoryData[i].ItemID = item; - fetch.InventoryData[i].OwnerID = ownerID; - ++i; + fetch.InventoryData[i].ItemID = itemIDs[i]; + fetch.InventoryData[i].OwnerID = ownerIDs[i]; } - _Network.SendPacket(fetch); + _Client.Network.SendPacket(fetch); } /// @@ -1100,72 +989,62 @@ namespace OpenMetaverse /// true to retrieve folders /// true to retrieve items /// sort order to return results in - /// a TimeSpan representing the amount of time to wait for results - /// A list of FolderData representing the folders contained in the parent folder. - /// A list of ItemData representing the items contained in the parent folder. - /// true if successful, false if timed out + /// a integer representing the number of milliseconds to wait for results + /// A list of inventory items matching search criteria within folder /// - public bool FolderContents(UUID folder, UUID owner, bool folders, bool items, - InventorySortOrder order, TimeSpan timeout, out List itemContents, out List folderContents) + /// InventoryFolder.DescendentCount will only be accurate if both folders and items are + /// requested + public List FolderContents(UUID folder, UUID owner, bool folders, bool items, + InventorySortOrder order, int timeoutMS) { - AutoResetEvent lockEvent = new AutoResetEvent(false); - List _folders = null; - List _items = null; - FolderContentsCallback callback = new FolderContentsCallback( - delegate(UUID folderID, List __items, List __folders) + List objects = null; + AutoResetEvent fetchEvent = new AutoResetEvent(false); + + FolderUpdatedCallback callback = + delegate(UUID folderID) { - if (folderID == folder) + if (folderID == folder + && _Store[folder] is InventoryFolder) { - _folders = __folders; - _items = __items; - lockEvent.Set(); + // InventoryDescendentsHandler only stores DescendendCount if both folders and items are fetched. + if (_Store.GetContents(folder).Count >= ((InventoryFolder)_Store[folder]).DescendentCount) + { + + fetchEvent.Set(); + } } - }); - RequestFolderContents(folder, owner, folders, items, order, callback); + else + { + fetchEvent.Set(); + } + }; - bool success = lockEvent.WaitOne(timeout, false); - itemContents = _items; - folderContents = _folders; - return success; + OnFolderUpdated += callback; + + RequestFolderContents(folder, owner, folders, items, order); + if (fetchEvent.WaitOne(timeoutMS, false)) + objects = _Store.GetContents(folder); + + OnFolderUpdated -= callback; + + return objects; } /// - /// - /// - /// - /// - /// - /// - /// - /// - public void RequestFolderContents(UUID folder, UUID owner, bool folders, bool items, InventorySortOrder order, FolderContentsCallback callback) - { - RequestFolderContents(folder, owner, folders, items, order, callback, null); - } - - /// - /// Request the contents of an inventory folder, is fired when all of the contents are retrieved. - /// is fired as the contents trickle in from the server. + /// Request the contents of an inventory folder /// /// The folder to search /// The folder owners /// true to return s contained in folder /// true to return s containd in folder /// the sort order to return items in - /// The callback to fire when all the contents are received. - /// The callback to fire as the contents are received. /// - public void RequestFolderContents(UUID folder, UUID owner, bool folders, bool items, InventorySortOrder order, FolderContentsCallback callback, PartialContentsCallback partialCallback) + public void RequestFolderContents(UUID folder, UUID owner, bool folders, bool items, + InventorySortOrder order) { - DescendentsRequest request = new DescendentsRequest(folder, callback); - request.PartialCallback = partialCallback; - lock (_DescendentsRequests) - _DescendentsRequests.Add(request); - - // Send the packet: FetchInventoryDescendentsPacket fetch = new FetchInventoryDescendentsPacket(); - fetch.AgentData.AgentID = _Agents.AgentID; - fetch.AgentData.SessionID = _Agents.SessionID; + fetch.AgentData.AgentID = _Client.Self.AgentID; + fetch.AgentData.SessionID = _Client.Self.SessionID; fetch.InventoryData.FetchFolders = folders; fetch.InventoryData.FetchItems = items; @@ -1173,7 +1052,7 @@ namespace OpenMetaverse fetch.InventoryData.OwnerID = owner; fetch.InventoryData.SortOrder = (int)order; - _Network.SendPacket(fetch); + _Client.Network.SendPacket(fetch); } #endregion Fetch @@ -1191,27 +1070,33 @@ namespace OpenMetaverse /// if not found, or UUID.Zero on failure public UUID FindFolderForType(AssetType type) { - if (_InventorySkeleton == null) + if (_Store == null) { - Logger.Log("Inventory skeleton is null, FindFolderForType() lookup cannot continue", + Logger.Log("Inventory is null, FindFolderForType() lookup cannot continue", Helpers.LogLevel.Error, _Client); return UUID.Zero; } // Folders go in the root if (type == AssetType.Folder) - return _InventorySkeleton.RootUUID; + return _Store.RootFolder.UUID; // Loop through each top-level directory and check if PreferredType // matches the requested type - foreach (FolderData folder in _InventorySkeleton.Folders) + List contents = _Store.GetContents(_Store.RootFolder.UUID); + foreach (InventoryBase inv in contents) { - if (folder.PreferredType == type) - return folder.UUID; + if (inv is InventoryFolder) + { + InventoryFolder folder = inv as InventoryFolder; + + if (folder.PreferredType == type) + return folder.UUID; + } } // No match found, return Root Folder ID - return _InventorySkeleton.RootUUID; + return _Store.RootFolder.UUID; } /// @@ -1220,10 +1105,10 @@ namespace OpenMetaverse /// The folder to begin the search in /// The object owners /// A string path to search - /// Time to wait for a reply + /// milliseconds to wait for a reply /// Found items or if /// timeout occurs or item is not found - public UUID FindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path, TimeSpan timeout) + public UUID FindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path, int timeoutMS) { AutoResetEvent findEvent = new AutoResetEvent(false); UUID foundItem = UUID.Zero; @@ -1238,8 +1123,12 @@ namespace OpenMetaverse } }; - RequestFindObjectByPath(baseFolder, inventoryOwner, path, callback); - findEvent.WaitOne(timeout, false); + OnFindObjectByPath += callback; + + RequestFindObjectByPath(baseFolder, inventoryOwner, path); + findEvent.WaitOne(timeoutMS, false); + + OnFindObjectByPath -= callback; return foundItem; } @@ -1250,169 +1139,191 @@ namespace OpenMetaverse /// The folder to begin the search in /// The object owners /// A string path to search, folders/objects separated by a '/' - /// The callback to fire when the path has been found. - public void RequestFindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path, FindObjectByPathCallback callback) + /// Results are sent to the event + public void RequestFindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path) { if (path == null || path.Length == 0) throw new ArgumentException("Empty path is not supported"); - string[] pathArray = path.Split('/'); - RequestFindObjectByPath(baseFolder, inventoryOwner, pathArray, callback); - } - /// - /// Find inventory items by path. - /// - /// The folder to begin the search in. - /// The object owner's - /// A string array representing a path already split into individual folder names. - /// The callback to fire when the path has been found. - public void RequestFindObjectByPath(UUID baseFolder, UUID inventoryOwner, string[] pathArray, FindObjectByPathCallback callback) - { - // Create the RequestFolderContents callback: - FolderContentsCallback contentsCallback = ConstructFindContentsHandler(String.Join("/", pathArray), pathArray, 0, callback); + // Store this search + InventorySearch search; + search.Folder = baseFolder; + search.Owner = inventoryOwner; + search.Path = path.Split('/'); + search.Level = 0; + lock (_Searches) _Searches.Add(search); + // Start the search - RequestFolderContents(baseFolder, inventoryOwner, true, true, InventorySortOrder.ByName, contentsCallback); + RequestFolderContents(baseFolder, inventoryOwner, true, true, InventorySortOrder.ByName); } /// - /// This constructs the callback that RequestFindObjectByPath needs to call RequestFolderContents. - /// We need to put it in its own method because the callback will need to create another callback for - /// recursing into child folders. - /// Used for display purposes only. - /// - /// - /// + /// Search inventory Store object for an item or folder /// - private FolderContentsCallback ConstructFindContentsHandler(string path, string[] pathArray, int level, FindObjectByPathCallback callback) + /// The folder to begin the search in + /// An array which creates a path to search + /// Number of levels below baseFolder to conduct searches + /// if True, will stop searching after first match is found + /// A list of inventory items found + public List LocalFind(UUID baseFolder, string[] path, int level, bool firstOnly) { - return new FolderContentsCallback( - delegate(UUID folder, List items, List folders) + List objects = new List(); + //List folders = new List(); + List contents = _Store.GetContents(baseFolder); + + foreach (InventoryBase inv in contents) + { + if (inv.Name.CompareTo(path[level]) == 0) { - foreach (FolderData folderData in folders) + if (level == path.Length - 1) { - if (folderData.Name == pathArray[level]) - { - if (level == pathArray.Length - 1) - { - Logger.DebugLog("Finished path search of " + path, _Client); - - // This is the last node in the path, fire the callback and clean up - callback(path, folderData.UUID); - } - else - { - // Construct the callback that will be called to recurse into the child folder. - FolderContentsCallback contentsCallback = ConstructFindContentsHandler(path, pathArray, level + 1, callback); - RequestFolderContents(folderData.UUID, folderData.OwnerID, true, true, InventorySortOrder.ByName, contentsCallback); - } - } + objects.Add(inv); + if (firstOnly) return objects; } - foreach (ItemData item in items) - { - if (item.Name == pathArray[level]) - { - if (level == pathArray.Length - 1) - { - Logger.DebugLog("Finished path search of " + path, _Client); + else if (inv is InventoryFolder) + objects.AddRange(LocalFind(inv.UUID, path, level + 1, firstOnly)); + } + } - // This is the last node in the path, fire the callback and clean up - callback(path, item.UUID); - } - else - { - Logger.Log("Path search attempted to request the contents of an item.", Helpers.LogLevel.Warning, _Client); - callback(path, UUID.Zero); - } - } - } - }); + return objects; } #endregion Find #region Move/Rename - + + /// + /// Move an inventory item or folder to a new location + /// + /// The item or folder to move + /// The to move item or folder to + public void Move(InventoryBase item, InventoryFolder newParent) + { + if (item is InventoryFolder) + MoveFolder(item.UUID, newParent.UUID); + else + MoveItem(item.UUID, newParent.UUID); + } /// - /// Rename a folder. + /// Move an inventory item or folder to a new location and change its name /// - /// The folder's - /// The folder's parent - /// The new name of the folder. - public void RenameFolder(UUID folderID, UUID parentID, string newName) + /// The item or folder to move + /// The to move item or folder to + /// The name to change the item or folder to + public void Move(InventoryBase item, InventoryFolder newParent, string newName) { + if (item is InventoryFolder) + MoveFolder(item.UUID, newParent.UUID, newName); + else + MoveItem(item.UUID, newParent.UUID, newName); + } + + /// + /// Move and rename a folder + /// + /// The source folders + /// The destination folders + /// The name to change the folder to + public void MoveFolder(UUID folderID, UUID newparentID, string newName) + { + lock (Store) + { + if (_Store.Contains(folderID)) + { + InventoryBase inv = Store[folderID]; + inv.Name = newName; + _Store.UpdateNodeFor(inv); + } + } + UpdateInventoryFolderPacket move = new UpdateInventoryFolderPacket(); - move.AgentData.AgentID = _Agents.AgentID; - move.AgentData.SessionID = _Agents.SessionID; + move.AgentData.AgentID = _Client.Self.AgentID; + move.AgentData.SessionID = _Client.Self.SessionID; move.FolderData = new UpdateInventoryFolderPacket.FolderDataBlock[1]; move.FolderData[0] = new UpdateInventoryFolderPacket.FolderDataBlock(); move.FolderData[0].FolderID = folderID; - move.FolderData[0].ParentID = parentID; + move.FolderData[0].ParentID = newparentID; move.FolderData[0].Name = Utils.StringToBytes(newName); move.FolderData[0].Type = -1; - _Network.SendPacket(move); + _Client.Network.SendPacket(move); } /// /// Move a folder /// - /// The source folder's - /// The destination folder's + /// The source folders + /// The destination folders public void MoveFolder(UUID folderID, UUID newParentID) { - MoveFolders(new UUID[] { folderID }, newParentID); - } - - /// - /// Move multiple folders. - /// - /// The parent to move the folders to. - /// The folders to move. - public void MoveFolders(ICollection folders, UUID newParent) - { - //TODO: Test if this truly supports multiple-folder move - MoveInventoryFolderPacket move = new MoveInventoryFolderPacket(); - move.AgentData.AgentID = _Agents.AgentID; - move.AgentData.SessionID = _Agents.SessionID; - move.AgentData.Stamp = false; //FIXME: ?? - - move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[folders.Count]; - - int i = 0; - foreach (UUID folder in folders) + lock (Store) { - MoveInventoryFolderPacket.InventoryDataBlock block = new MoveInventoryFolderPacket.InventoryDataBlock(); - block.FolderID = folder; - block.ParentID = newParent; - move.InventoryData[i] = block; - ++i; + if (_Store.Contains(folderID)) + { + InventoryBase inv = Store[folderID]; + inv.ParentUUID = newParentID; + _Store.UpdateNodeFor(inv); + } } - _Network.SendPacket(move); - } - - /// - /// Rename an inventory item - /// - /// The of the source item to move - /// The of the item's parent. - /// The name to change the folder to - public void RenameItem(UUID itemID, UUID parentID, string newName) - { - MoveInventoryItemPacket move = new MoveInventoryItemPacket(); - move.AgentData.AgentID = _Agents.AgentID; - move.AgentData.SessionID = _Agents.SessionID; + MoveInventoryFolderPacket move = new MoveInventoryFolderPacket(); + move.AgentData.AgentID = _Client.Self.AgentID; + move.AgentData.SessionID = _Client.Self.SessionID; move.AgentData.Stamp = false; //FIXME: ?? - move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[1]; - move.InventoryData[0] = new MoveInventoryItemPacket.InventoryDataBlock(); - move.InventoryData[0].ItemID = itemID; - move.InventoryData[0].FolderID = parentID; - move.InventoryData[0].NewName = Utils.StringToBytes(newName); - - _Network.SendPacket(move); + move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[1]; + move.InventoryData[0] = new MoveInventoryFolderPacket.InventoryDataBlock(); + move.InventoryData[0].FolderID = folderID; + move.InventoryData[0].ParentID = newParentID; + + _Client.Network.SendPacket(move); } + + /// + /// Move multiple folders, the keys in the Dictionary parameter, + /// to a new parents, the value of that folder's key. + /// + /// A Dictionary containing the + /// of the source as the key, and the + /// of the destination as the value + public void MoveFolders(Dictionary foldersNewParents) + { + // FIXME: Use two List to stay consistent + + lock (Store) + { + foreach (KeyValuePair entry in foldersNewParents) + { + if (_Store.Contains(entry.Key)) + { + InventoryBase inv = _Store[entry.Key]; + inv.ParentUUID = entry.Value; + _Store.UpdateNodeFor(inv); + } + } + } + + //TODO: Test if this truly supports multiple-folder move + MoveInventoryFolderPacket move = new MoveInventoryFolderPacket(); + move.AgentData.AgentID = _Client.Self.AgentID; + move.AgentData.SessionID = _Client.Self.SessionID; + move.AgentData.Stamp = false; //FIXME: ?? + + move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[foldersNewParents.Count]; + + int index = 0; + foreach (KeyValuePair folder in foldersNewParents) + { + MoveInventoryFolderPacket.InventoryDataBlock block = new MoveInventoryFolderPacket.InventoryDataBlock(); + block.FolderID = folder.Key; + block.ParentID = folder.Value; + move.InventoryData[index++] = block; + } + + _Client.Network.SendPacket(move); + } + /// /// Move an inventory item to a new folder @@ -1421,35 +1332,80 @@ namespace OpenMetaverse /// The of the destination folder public void MoveItem(UUID itemID, UUID folderID) { - MoveItems(new UUID[] { itemID }, folderID); + MoveItem(itemID, folderID, String.Empty); } /// - /// Move multiple inventory items. + /// Move and rename an inventory item /// - /// The of the new parent. - /// The s of the items. - public void MoveItems(ICollection items, UUID newParentID) + /// The of the source item to move + /// The of the destination folder + /// The name to change the folder to + public void MoveItem(UUID itemID, UUID folderID, string newName) { - MoveInventoryItemPacket move = new MoveInventoryItemPacket(); - move.AgentData.AgentID = _Agents.AgentID; - move.AgentData.SessionID = _Agents.SessionID; - move.AgentData.Stamp = false; //FIXME: ?? - - move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[items.Count]; - - int i = 0; - foreach (UUID item in items) + lock (_Store) { - MoveInventoryItemPacket.InventoryDataBlock block = new MoveInventoryItemPacket.InventoryDataBlock(); - block.ItemID = item; - block.FolderID = newParentID; - block.NewName = new byte[0]; - move.InventoryData[i] = block; - ++i; + if (_Store.Contains(itemID)) + { + InventoryBase inv = _Store[itemID]; + inv.ParentUUID = folderID; + _Store.UpdateNodeFor(inv); + } } - _Network.SendPacket(move); + MoveInventoryItemPacket move = new MoveInventoryItemPacket(); + move.AgentData.AgentID = _Client.Self.AgentID; + move.AgentData.SessionID = _Client.Self.SessionID; + move.AgentData.Stamp = false; //FIXME: ?? + + move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[1]; + move.InventoryData[0] = new MoveInventoryItemPacket.InventoryDataBlock(); + move.InventoryData[0].ItemID = itemID; + move.InventoryData[0].FolderID = folderID; + move.InventoryData[0].NewName = Utils.StringToBytes(newName); + + _Client.Network.SendPacket(move); + } + + /// + /// Move multiple inventory items to new locations + /// + /// A Dictionary containing the + /// of the source item as the key, and the + /// of the destination folder as the value + public void MoveItems(Dictionary itemsNewParents) + { + lock (_Store) + { + foreach (KeyValuePair entry in itemsNewParents) + { + if (_Store.Contains(entry.Key)) + { + InventoryBase inv = _Store[entry.Key]; + inv.ParentUUID = entry.Value; + _Store.UpdateNodeFor(inv); + } + } + } + + MoveInventoryItemPacket move = new MoveInventoryItemPacket(); + move.AgentData.AgentID = _Client.Self.AgentID; + move.AgentData.SessionID = _Client.Self.SessionID; + move.AgentData.Stamp = false; //FIXME: ?? + + move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[itemsNewParents.Count]; + + int index = 0; + foreach (KeyValuePair entry in itemsNewParents) + { + MoveInventoryItemPacket.InventoryDataBlock block = new MoveInventoryItemPacket.InventoryDataBlock(); + block.ItemID = entry.Key; + block.FolderID = entry.Value; + block.NewName = new byte[0]; + move.InventoryData[index++] = block; + } + + _Client.Network.SendPacket(move); } #endregion Move @@ -1463,10 +1419,23 @@ namespace OpenMetaverse public void RemoveDescendants(UUID folder) { PurgeInventoryDescendentsPacket purge = new PurgeInventoryDescendentsPacket(); - purge.AgentData.AgentID = _Agents.AgentID; - purge.AgentData.SessionID = _Agents.SessionID; + purge.AgentData.AgentID = _Client.Self.AgentID; + purge.AgentData.SessionID = _Client.Self.SessionID; purge.InventoryData.FolderID = folder; - _Network.SendPacket(purge); + _Client.Network.SendPacket(purge); + + // Update our local copy + lock (_Store) + { + if (_Store.Contains(folder)) + { + List contents = _Store.GetContents(folder); + foreach (InventoryBase obj in contents) + { + _Store.RemoveNodeFor(obj); + } + } + } } /// @@ -1498,14 +1467,14 @@ namespace OpenMetaverse /// /// A List containing the s of items to remove /// A List containing the s of the folders to remove - public void Remove(ICollection items, ICollection folders) + public void Remove(List items, List folders) { if ((items == null || items.Count == 0) && (folders == null || folders.Count == 0)) return; RemoveInventoryObjectsPacket rem = new RemoveInventoryObjectsPacket(); - rem.AgentData.AgentID = _Agents.AgentID; - rem.AgentData.SessionID = _Agents.SessionID; + rem.AgentData.AgentID = _Client.Self.AgentID; + rem.AgentData.SessionID = _Client.Self.SessionID; if (items == null || items.Count == 0) { @@ -1516,13 +1485,18 @@ namespace OpenMetaverse } else { - rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[items.Count]; - int i = 0; - foreach (UUID item in items) + lock (_Store) { - rem.ItemData[i] = new RemoveInventoryObjectsPacket.ItemDataBlock(); - rem.ItemData[i].ItemID = item; - ++i; + rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[items.Count]; + for (int i = 0; i < items.Count; i++) + { + rem.ItemData[i] = new RemoveInventoryObjectsPacket.ItemDataBlock(); + rem.ItemData[i].ItemID = items[i]; + + // Update local copy + if (_Store.Contains(items[i])) + _Store.RemoveNodeFor(Store[items[i]]); + } } } @@ -1535,18 +1509,23 @@ namespace OpenMetaverse } else { - rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[folders.Count]; - int i = 0; - foreach (UUID folder in folders) + lock (_Store) { - rem.FolderData[i] = new RemoveInventoryObjectsPacket.FolderDataBlock(); - rem.FolderData[i].FolderID = folder; - ++i; + rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[folders.Count]; + for (int i = 0; i < folders.Count; i++) + { + rem.FolderData[i] = new RemoveInventoryObjectsPacket.FolderDataBlock(); + rem.FolderData[i].FolderID = folders[i]; + + // Update local copy + if (_Store.Contains(folders[i])) + _Store.RemoveNodeFor(Store[folders[i]]); + } } } - _Network.SendPacket(rem); + _Client.Network.SendPacket(rem); } - + /// /// Empty the Lost and Found folder /// @@ -1565,82 +1544,62 @@ namespace OpenMetaverse private void EmptySystemFolder(AssetType folderType) { - RemoveDescendants(FindFolderForType(folderType)); - } + List items = _Store.GetContents(_Store.RootFolder); + + UUID folderKey = UUID.Zero; + foreach (InventoryBase item in items) + { + if ((item as InventoryFolder) != null) + { + InventoryFolder folder = item as InventoryFolder; + if (folder.PreferredType == folderType) + { + folderKey = folder.UUID; + break; + } + } + } + items = _Store.GetContents(folderKey); + List remItems = new List(); + List remFolders = new List(); + foreach (InventoryBase item in items) + { + if ((item as InventoryFolder) != null) + { + remFolders.Add(item.UUID); + } + else + { + remItems.Add(item.UUID); + } + } + Remove(remItems, remFolders); + } #endregion Remove #region Create - - [Obsolete("Wearables must upload an Asset before being created.", false)] - public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, - InventoryType invType, WearableType wearableType, PermissionMask nextOwnerMask, - ItemCreatedCallback callback) - { - RequestCreateItem(parentFolder, name, description, type, UUID.Zero, invType, wearableType, nextOwnerMask, callback); - } - /// - /// Creates an inventory item without needing to upload an asset. - /// In most cases, this means the AssetID of the resulting item is UUID.Zero. - /// For gestures, the server automatically creates an asset and assigns it an ID. - /// This is the method the Second Life Client (as of v1.9) uses to create scripts and notecards + /// /// - /// of folder to put item in. - /// Name of new item. - /// Description of new item. - /// Asset type of item. - /// Inventory type of item. - /// Permissions for the next owner. - /// Callback to trigger when item is created. - public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, - InventoryType invType, PermissionMask nextOwnerMask, ItemCreatedCallback callback) - { - // Even though WearableType 0 is Shape, in this context it is treated as NOT_WEARABLE - RequestCreateItem(parentFolder, name, description, type, UUID.Zero, invType, (WearableType)0, nextOwnerMask, - callback); - } - - /// - /// Creates an inventory item referenceing an asset upload. This associates - /// the item with an asset. The resulting ItemData will have the AssetUUID - /// of the uploaded asset if the upload completed successfully. - /// This is the method that the Second Life Client (as of v1.9) uses to create gestures. - /// - /// - /// - /// - /// - /// - /// - /// /// Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here. public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID, InventoryType invType, PermissionMask nextOwnerMask, ItemCreatedCallback callback) { // Even though WearableType 0 is Shape, in this context it is treated as NOT_WEARABLE - RequestCreateItem(parentFolder, name, description, type, assetTransactionID, invType, (WearableType)0, nextOwnerMask, + RequestCreateItem(parentFolder, name, description, type, assetTransactionID, invType, (WearableType)0, nextOwnerMask, callback); } /// - /// Creates a wearable inventory item referencing an asset upload. - /// Second Life v1.9 uses this method to create wearable inventory items. + /// /// - /// - /// - /// - /// - /// - /// - /// - /// /// Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here. public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID, InventoryType invType, WearableType wearableType, PermissionMask nextOwnerMask, ItemCreatedCallback callback) { CreateInventoryItemPacket create = new CreateInventoryItemPacket(); - create.AgentData.AgentID = _Agents.AgentID; - create.AgentData.SessionID = _Agents.SessionID; + create.AgentData.AgentID = _Client.Self.AgentID; + create.AgentData.SessionID = _Client.Self.SessionID; create.InventoryBlock.CallbackID = RegisterItemCreatedCallback(callback); create.InventoryBlock.FolderID = parentFolder; @@ -1652,7 +1611,7 @@ namespace OpenMetaverse create.InventoryBlock.Name = Utils.StringToBytes(name); create.InventoryBlock.Description = Utils.StringToBytes(description); - _Network.SendPacket(create); + _Client.Network.SendPacket(create); } /// @@ -1696,17 +1655,30 @@ namespace OpenMetaverse } } + // Create the new folder locally + InventoryFolder newFolder = new InventoryFolder(id); + newFolder.Version = 1; + newFolder.DescendentCount = 0; + newFolder.ParentUUID = parentID; + newFolder.PreferredType = preferredType; + newFolder.Name = name; + newFolder.OwnerID = _Client.Self.AgentID; + + // Update the local store + try { _Store[newFolder.UUID] = newFolder; } + catch (InventoryException ie) { Logger.Log(ie.Message, Helpers.LogLevel.Warning, _Client, ie); } + // Create the create folder packet and send it CreateInventoryFolderPacket create = new CreateInventoryFolderPacket(); - create.AgentData.AgentID = _Agents.AgentID; - create.AgentData.SessionID = _Agents.SessionID; + create.AgentData.AgentID = _Client.Self.AgentID; + create.AgentData.SessionID = _Client.Self.SessionID; create.FolderData.FolderID = id; create.FolderData.ParentID = parentID; create.FolderData.Type = (sbyte)preferredType; create.FolderData.Name = Utils.StringToBytes(name); - _Network.SendPacket(create); + _Client.Network.SendPacket(create); return id; } @@ -1714,10 +1686,10 @@ namespace OpenMetaverse public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType, InventoryType invType, UUID folderID, CapsClient.ProgressCallback progCallback, ItemCreatedFromAssetCallback callback) { - if (_Network.CurrentSim == null || _Network.CurrentSim.Caps == null) + if (_Client.Network.CurrentSim == null || _Client.Network.CurrentSim.Caps == null) throw new Exception("NewFileAgentInventory capability is not currently available"); - Uri url = _Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory"); + Uri url = _Client.Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory"); if (url != null) { @@ -1745,29 +1717,6 @@ namespace OpenMetaverse #region Copy - public bool CopyItem(UUID itemUUID, UUID newParent, string newName, TimeSpan timeout, out ItemData copy) - { - ManualResetEvent mre = new ManualResetEvent(false); - ItemData _copy = new ItemData(); - ItemCopiedCallback callback = - delegate(ItemData item) - { - _copy = item; - mre.Set(); - }; - RequestCopyItem(itemUUID, newParent, newName, callback); - if (mre.WaitOne(timeout, false)) - { - copy = _copy; - return true; - } - else - { - copy = _copy; - return false; - } - } - /// /// /// @@ -1777,7 +1726,7 @@ namespace OpenMetaverse /// public void RequestCopyItem(UUID item, UUID newParent, string newName, ItemCopiedCallback callback) { - RequestCopyItem(item, newParent, newName, _Agents.AgentID, callback); + RequestCopyItem(item, newParent, newName, _Client.Self.AgentID, callback); } /// @@ -1811,7 +1760,7 @@ namespace OpenMetaverse /// /// /// - public void RequestCopyItems(IList items, IList targetFolders, IList newNames, + public void RequestCopyItems(List items, List targetFolders, List newNames, UUID oldOwnerID, ItemCopiedCallback callback) { if (items.Count != targetFolders.Count || (newNames != null && items.Count != newNames.Count)) @@ -1820,8 +1769,8 @@ namespace OpenMetaverse uint callbackID = RegisterItemsCopiedCallback(callback); CopyInventoryItemPacket copy = new CopyInventoryItemPacket(); - copy.AgentData.AgentID = _Agents.AgentID; - copy.AgentData.SessionID = _Agents.SessionID; + copy.AgentData.AgentID = _Client.Self.AgentID; + copy.AgentData.SessionID = _Client.Self.SessionID; copy.InventoryData = new CopyInventoryItemPacket.InventoryDataBlock[items.Count]; for (int i = 0; i < items.Count; ++i) @@ -1838,7 +1787,7 @@ namespace OpenMetaverse copy.InventoryData[i].NewName = new byte[0]; } - _Network.SendPacket(copy); + _Client.Network.SendPacket(copy); } /// @@ -1851,8 +1800,8 @@ namespace OpenMetaverse public void RequestCopyItemFromNotecard(UUID objectID, UUID notecardID, UUID folderID, UUID itemID) { CopyInventoryFromNotecardPacket copy = new CopyInventoryFromNotecardPacket(); - copy.AgentData.AgentID = _Agents.AgentID; - copy.AgentData.SessionID = _Agents.SessionID; + copy.AgentData.AgentID = _Client.Self.AgentID; + copy.AgentData.SessionID = _Client.Self.SessionID; copy.NotecardData.ObjectID = objectID; copy.NotecardData.NotecardItemID = notecardID; @@ -1862,7 +1811,7 @@ namespace OpenMetaverse copy.InventoryData[0].FolderID = folderID; copy.InventoryData[0].ItemID = itemID; - _Network.SendPacket(copy); + _Client.Network.SendPacket(copy); } #endregion Copy @@ -1872,17 +1821,20 @@ namespace OpenMetaverse /// /// /// - /// - public void RequestUpdateItem(ItemData parameters) + /// + public void RequestUpdateItem(InventoryItem item) { - RequestUpdateItems(new ItemData[] { parameters }, UUID.Random()); + List items = new List(1); + items.Add(item); + + RequestUpdateItems(items, UUID.Random()); } /// /// /// /// - public void RequestUpdateItems(ICollection items) + public void RequestUpdateItems(List items) { RequestUpdateItems(items, UUID.Random()); } @@ -1892,17 +1844,18 @@ namespace OpenMetaverse /// /// /// - public void RequestUpdateItems(ICollection items, UUID transactionID) + public void RequestUpdateItems(List items, UUID transactionID) { UpdateInventoryItemPacket update = new UpdateInventoryItemPacket(); - update.AgentData.AgentID = _Agents.AgentID; - update.AgentData.SessionID = _Agents.SessionID; + update.AgentData.AgentID = _Client.Self.AgentID; + update.AgentData.SessionID = _Client.Self.SessionID; update.AgentData.TransactionID = transactionID; update.InventoryData = new UpdateInventoryItemPacket.InventoryDataBlock[items.Count]; - int index = 0; - foreach (ItemData item in items) + for (int i = 0; i < items.Count; i++) { + InventoryItem item = items[i]; + UpdateInventoryItemPacket.InventoryDataBlock block = new UpdateInventoryItemPacket.InventoryDataBlock(); block.BaseMask = (uint)item.Permissions.BaseMask; block.CRC = ItemCRC(item); @@ -1926,11 +1879,10 @@ namespace OpenMetaverse block.TransactionID = UUID.Zero; block.Type = (sbyte)item.AssetType; - update.InventoryData[index] = block; - ++index; + update.InventoryData[i] = block; } - _Network.SendPacket(update); + _Client.Network.SendPacket(update); } /// @@ -1941,10 +1893,10 @@ namespace OpenMetaverse /// public void RequestUploadNotecardAsset(byte[] data, UUID notecardID, NotecardUploadedAssetCallback callback) { - if (_Network.CurrentSim == null || _Network.CurrentSim.Caps == null) + if (_Client.Network.CurrentSim == null || _Client.Network.CurrentSim.Caps == null) throw new Exception("UpdateNotecardAgentInventory capability is not currently available"); - Uri url = _Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardAgentInventory"); + Uri url = _Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardAgentInventory"); if (url != null) { @@ -1976,9 +1928,9 @@ namespace OpenMetaverse /// Vector of where to place object /// InventoryObject object containing item details public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, - ItemData item) + InventoryObject item) { - return RequestRezFromInventory(simulator, rotation, position, item, _Agents.ActiveGroup, + return RequestRezFromInventory(simulator, rotation, position, item, _Client.Self.ActiveGroup, UUID.Random(), false); } @@ -1991,7 +1943,7 @@ namespace OpenMetaverse /// InventoryObject object containing item details /// UUID of group to own the object public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, - ItemData item, UUID groupOwner) + InventoryObject item, UUID groupOwner) { return RequestRezFromInventory(simulator, rotation, position, item, groupOwner, UUID.Random(), false); } @@ -2008,12 +1960,12 @@ namespace OpenMetaverse /// if set to true the simulator /// will automatically send object detail packet(s) back to the client public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, - ItemData item, UUID groupOwner, UUID queryID, bool requestObjectDetails) + InventoryObject item, UUID groupOwner, UUID queryID, bool requestObjectDetails) { RezObjectPacket add = new RezObjectPacket(); - add.AgentData.AgentID = _Agents.AgentID; - add.AgentData.SessionID = _Agents.SessionID; + add.AgentData.AgentID = _Client.Self.AgentID; + add.AgentData.SessionID = _Client.Self.SessionID; add.AgentData.GroupID = groupOwner; add.RezData.FromTaskID = UUID.Zero; @@ -2050,7 +2002,7 @@ namespace OpenMetaverse add.InventoryData.Description = Utils.StringToBytes(item.Description); add.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); - _Network.SendPacket(add, simulator); + _Client.Network.SendPacket(add, simulator); return queryID; } @@ -2061,7 +2013,7 @@ namespace OpenMetaverse /// The simulator Local ID of the object public void RequestDeRezToInventory(uint objectLocalID) { - RequestDeRezToInventory(objectLocalID, DeRezDestination.ObjectsFolder, + RequestDeRezToInventory(objectLocalID, DeRezDestination.ObjectsFolder, _Client.Inventory.FindFolderForType(AssetType.Object), UUID.Random()); } @@ -2078,8 +2030,8 @@ namespace OpenMetaverse { DeRezObjectPacket take = new DeRezObjectPacket(); - take.AgentData.AgentID = _Agents.AgentID; - take.AgentData.SessionID = _Agents.SessionID; + take.AgentData.AgentID = _Client.Self.AgentID; + take.AgentData.SessionID = _Client.Self.SessionID; take.AgentBlock = new DeRezObjectPacket.AgentBlockBlock(); take.AgentBlock.GroupID = UUID.Zero; take.AgentBlock.Destination = (byte)destType; @@ -2091,8 +2043,8 @@ namespace OpenMetaverse take.ObjectData = new DeRezObjectPacket.ObjectDataBlock[1]; take.ObjectData[0] = new DeRezObjectPacket.ObjectDataBlock(); take.ObjectData[0].ObjectLocalID = objectLocalID; - - _Network.SendPacket(take); + + _Client.Network.SendPacket(take); } @@ -2110,34 +2062,30 @@ namespace OpenMetaverse byte[] bucket; - bucket = new byte[17]; - bucket[0] = (byte)assetType; - Buffer.BlockCopy(itemID.GetBytes(), 0, bucket, 1, 16); + bucket = new byte[17]; + bucket[0] = (byte)assetType; + Buffer.BlockCopy(itemID.GetBytes(), 0, bucket, 1, 16); - _Agents.InstantMessage( - _Agents.Name, + _Client.Self.InstantMessage( + _Client.Self.Name, recipient, itemName, UUID.Random(), InstantMessageDialog.InventoryOffered, InstantMessageOnline.Online, - _Agents.SimPosition, - _Network.CurrentSim.ID, + _Client.Self.SimPosition, + _Client.Network.CurrentSim.ID, bucket); if (doEffect) { - _Agents.BeamEffect(_Agents.AgentID, recipient, Vector3d.Zero, + _Client.Self.BeamEffect(_Client.Self.AgentID, recipient, Vector3d.Zero, _Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random()); } } /// /// Give an inventory Folder with contents to another avatar - /// This calls the synchronous method which blocks until - /// the folder's contents are retrieved, so it might take a while to return. - /// For an alternative, specify the folder's contents explicitly using the other - /// method. /// /// The of the Folder to give /// The name of the folder @@ -2146,52 +2094,43 @@ namespace OpenMetaverse /// true to generate a beameffect during transfer public void GiveFolder(UUID folderID, string folderName, AssetType assetType, UUID recipient, bool doEffect) - { - List folderContents; - List placeholder; - - FolderContents(folderID, _Agents.AgentID, false, true, InventorySortOrder.ByDate, - TimeSpan.FromMilliseconds(1000 * 15), - out folderContents, out placeholder); - - GiveFolder(folderID, folderName, assetType, recipient, doEffect, folderContents); - } - - - public void GiveFolder(UUID folderID, string folderName, AssetType assetType, UUID recipient, - bool doEffect, ICollection folderContents) { byte[] bucket; + List folderContents = new List(); - bucket = new byte[17 * (folderContents.Count + 1)]; - //Add parent folder (first item in bucket) - bucket[0] = (byte)assetType; - Buffer.BlockCopy(folderID.GetBytes(), 0, bucket, 1, 16); + _Client.Inventory.FolderContents(folderID, _Client.Self.AgentID, false, true, InventorySortOrder.ByDate, 1000 * 15).ForEach( + delegate(InventoryBase ib) + { + folderContents.Add(_Client.Inventory.FetchItem(ib.UUID, _Client.Self.AgentID, 1000 * 10)); + }); + bucket = new byte[17 * (folderContents.Count + 1)]; - //Add contents to bucket after folder - int index = 1; - foreach (ItemData item in folderContents) - { - bucket[index * 17] = (byte)item.AssetType; - Buffer.BlockCopy(item.UUID.GetBytes(), 0, bucket, index * 17 + 1, 16); - ++index; - } + //Add parent folder (first item in bucket) + bucket[0] = (byte)assetType; + Buffer.BlockCopy(folderID.GetBytes(), 0, bucket, 1, 16); - _Agents.InstantMessage( - _Agents.Name, + //Add contents to bucket after folder + for (int i = 1; i <= folderContents.Count; ++i) + { + bucket[i * 17] = (byte)folderContents[i - 1].AssetType; + Buffer.BlockCopy(folderContents[i - 1].UUID.GetBytes(), 0, bucket, i * 17 + 1, 16); + } + + _Client.Self.InstantMessage( + _Client.Self.Name, recipient, folderName, UUID.Random(), InstantMessageDialog.InventoryOffered, InstantMessageOnline.Online, - _Agents.SimPosition, - _Network.CurrentSim.ID, + _Client.Self.SimPosition, + _Client.Network.CurrentSim.ID, bucket); if (doEffect) { - _Agents.BeamEffect(_Agents.AgentID, recipient, Vector3d.Zero, + _Client.Self.BeamEffect(_Client.Self.AgentID, recipient, Vector3d.Zero, _Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random()); } } @@ -2206,13 +2145,13 @@ namespace OpenMetaverse /// /// /// - public UUID UpdateTaskInventory(uint objectLocalID, ItemData item) + public UUID UpdateTaskInventory(uint objectLocalID, InventoryItem item) { UUID transactionID = UUID.Random(); UpdateTaskInventoryPacket update = new UpdateTaskInventoryPacket(); - update.AgentData.AgentID = _Agents.AgentID; - update.AgentData.SessionID = _Agents.SessionID; + update.AgentData.AgentID = _Client.Self.AgentID; + update.AgentData.SessionID = _Client.Self.SessionID; update.UpdateData.Key = 0; update.UpdateData.LocalID = objectLocalID; @@ -2238,7 +2177,7 @@ namespace OpenMetaverse update.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); update.InventoryData.CRC = ItemCRC(item); - _Network.SendPacket(update); + _Client.Network.SendPacket(update); return transactionID; } @@ -2248,11 +2187,9 @@ namespace OpenMetaverse /// /// The tasks /// The tasks simulator local ID - /// Time to wait for reply from simulator - /// - /// + /// milliseconds to wait for reply from simulator /// A List containing the inventory items inside the task - public void GetTaskInventory(UUID objectID, uint objectLocalID, TimeSpan timeout, out List items, out List folders) + public List GetTaskInventory(UUID objectID, uint objectLocalID, int timeoutMS) { string filename = null; AutoResetEvent taskReplyEvent = new AutoResetEvent(false); @@ -2271,7 +2208,7 @@ namespace OpenMetaverse RequestTaskInventory(objectLocalID); - if (taskReplyEvent.WaitOne(timeout, false)) + if (taskReplyEvent.WaitOne(timeoutMS, false)) { OnTaskInventoryReply -= callback; @@ -2296,38 +2233,31 @@ namespace OpenMetaverse // Start the actual asset xfer xferID = _Client.Assets.RequestAssetXfer(filename, true, false, UUID.Zero, AssetType.Unknown); - if (taskDownloadEvent.WaitOne(timeout, false)) + if (taskDownloadEvent.WaitOne(timeoutMS, false)) { _Client.Assets.OnXferReceived -= xferCallback; string taskList = Utils.BytesToString(assetData); - ParseTaskInventory(this, taskList, out items, out folders); - return; + return ParseTaskInventory(taskList); } else { Logger.Log("Timed out waiting for task inventory download for " + filename, Helpers.LogLevel.Warning, _Client); _Client.Assets.OnXferReceived -= xferCallback; - items = null; - folders = null; - return; + return null; } } else { Logger.DebugLog("Task is empty for " + objectLocalID, _Client); - items = null; - folders = null; - return; + return null; } } else { Logger.Log("Timed out waiting for task inventory reply for " + objectLocalID, Helpers.LogLevel.Warning, _Client); OnTaskInventoryReply -= callback; - items = null; - folders = null; - return; + return null; } } @@ -2337,7 +2267,7 @@ namespace OpenMetaverse /// public void RequestTaskInventory(uint objectLocalID) { - RequestTaskInventory(objectLocalID, _Network.CurrentSim); + RequestTaskInventory(objectLocalID, _Client.Network.CurrentSim); } /// @@ -2348,13 +2278,13 @@ namespace OpenMetaverse public void RequestTaskInventory(uint objectLocalID, Simulator simulator) { RequestTaskInventoryPacket request = new RequestTaskInventoryPacket(); - request.AgentData.AgentID = _Agents.AgentID; - request.AgentData.SessionID = _Agents.SessionID; + request.AgentData.AgentID = _Client.Self.AgentID; + request.AgentData.SessionID = _Client.Self.SessionID; request.InventoryData.LocalID = objectLocalID; - _Network.SendPacket(request, simulator); + _Client.Network.SendPacket(request, simulator); } - + /// /// Moves an Item from an objects (Prim) Inventory to the specified folder in the avatars inventory /// @@ -2365,17 +2295,17 @@ namespace OpenMetaverse public void MoveTaskInventory(uint objectLocalID, UUID taskItemID, UUID inventoryFolderID, Simulator simulator) { MoveTaskInventoryPacket request = new MoveTaskInventoryPacket(); - request.AgentData.AgentID = _Agents.AgentID; - request.AgentData.SessionID = _Agents.SessionID; + request.AgentData.AgentID = _Client.Self.AgentID; + request.AgentData.SessionID = _Client.Self.SessionID; request.AgentData.FolderID = inventoryFolderID; request.InventoryData.ItemID = taskItemID; request.InventoryData.LocalID = objectLocalID; - _Network.SendPacket(request, simulator); + _Client.Network.SendPacket(request, simulator); } - + /// /// Remove an item from an objects (Prim) Inventory /// @@ -2385,13 +2315,13 @@ namespace OpenMetaverse public void RemoveTaskInventory(uint objectLocalID, UUID taskItemID, Simulator simulator) { RemoveTaskInventoryPacket remove = new RemoveTaskInventoryPacket(); - remove.AgentData.AgentID = _Agents.AgentID; - remove.AgentData.SessionID = _Agents.SessionID; + remove.AgentData.AgentID = _Client.Self.AgentID; + remove.AgentData.SessionID = _Client.Self.SessionID; remove.InventoryData.ItemID = taskItemID; remove.InventoryData.LocalID = objectLocalID; - _Network.SendPacket(remove, simulator); + _Client.Network.SendPacket(remove, simulator); } #endregion Task @@ -2507,7 +2437,7 @@ namespace OpenMetaverse /// /// The source InventoryItem /// A uint representing the source InventoryItem as a CRC - public static uint ItemCRC(ItemData iitem) + public static uint ItemCRC(InventoryItem iitem) { uint CRC = 0; @@ -2538,6 +2468,46 @@ namespace OpenMetaverse return CRC; } + /// + /// Wrapper for creating a new object + /// + /// The type of item from the enum + /// The of the newly created object + /// An object with the type and id passed + public static InventoryItem CreateInventoryItem(InventoryType type, UUID id) + { + switch (type) + { + case InventoryType.Texture: return new InventoryTexture(id); + case InventoryType.Sound: return new InventorySound(id); + case InventoryType.CallingCard: return new InventoryCallingCard(id); + case InventoryType.Landmark: return new InventoryLandmark(id); + case InventoryType.Object: return new InventoryObject(id); + case InventoryType.Notecard: return new InventoryNotecard(id); + case InventoryType.Category: return new InventoryCategory(id); + case InventoryType.LSL: return new InventoryLSL(id); + case InventoryType.Snapshot: return new InventorySnapshot(id); + case InventoryType.Attachment: return new InventoryAttachment(id); + case InventoryType.Wearable: return new InventoryWearable(id); + case InventoryType.Animation: return new InventoryAnimation(id); + case InventoryType.Gesture: return new InventoryGesture(id); + default: return new InventoryItem(type, id); + } + } + + private InventoryItem SafeCreateInventoryItem(InventoryType InvType, UUID ItemID) + { + InventoryItem ret = null; + + if (_Store.Contains(ItemID)) + ret = _Store[ItemID] as InventoryItem; + + if (ret == null) + ret = CreateInventoryItem(InvType, ItemID); + + return ret; + } + private static bool ParseLine(string line, out string key, out string value) { string origLine = line; @@ -2576,15 +2546,11 @@ namespace OpenMetaverse /// /// Parse the results of a RequestTaskInventory() response /// - /// /// A string which contains the data from the task reply - /// - /// /// A List containing the items contained within the tasks inventory - public static void ParseTaskInventory(InventoryManager manager, string taskData, out List items, out List folders) + public static List ParseTaskInventory(string taskData) { - items = new List(); - folders = new List(); + List items = new List(); int lineNum = 0; string[] lines = taskData.Replace("\r\n", "\n").Split('\n'); @@ -2636,19 +2602,20 @@ namespace OpenMetaverse if (assetType == AssetType.Folder) { - FolderData folderData = new FolderData(itemID); - folderData.Name = name; - folderData.ParentUUID = parentID; + InventoryFolder folder = new InventoryFolder(itemID); + folder.Name = name; + folder.ParentUUID = parentID; - folders.Add(folderData); + items.Add(folder); } else { - ItemData itemParams = new ItemData(itemID); - itemParams.Name = name; - itemParams.ParentUUID = parentID; - itemParams.AssetType = assetType; - items.Add(itemParams); + InventoryItem item = new InventoryItem(itemID); + item.Name = name; + item.ParentUUID = parentID; + item.AssetType = assetType; + + items.Add(item); } #endregion inv_object @@ -2751,6 +2718,7 @@ namespace OpenMetaverse } else if (key == "creator_id") { + UUID.TryParse(value, out creatorID); } else if (key == "owner_id") @@ -2843,7 +2811,8 @@ namespace OpenMetaverse } } } - ItemData item = new ItemData(itemID, inventoryType); + + InventoryItem item = CreateInventoryItem(inventoryType, itemID); item.AssetUUID = assetID; item.AssetType = assetType; item.CreationDate = creationDate; @@ -2858,6 +2827,7 @@ namespace OpenMetaverse item.Permissions = perms; item.SalePrice = salePrice; item.SaleType = saleType; + items.Add(item); #endregion inv_item @@ -2869,8 +2839,10 @@ namespace OpenMetaverse } } } - } + return items; + } + #endregion Helper Functions #region Callbacks @@ -2882,8 +2854,6 @@ namespace OpenMetaverse ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[1]; byte[] itemData = (byte[])args[2]; - LLSDMap contents = (LLSDMap)result; - if (result == null) { try { callback(false, error.Message, UUID.Zero, UUID.Zero); } @@ -2891,6 +2861,8 @@ namespace OpenMetaverse return; } + LLSDMap contents = (LLSDMap)result; + string status = contents["state"].AsString().ToLower(); if (status == "upload") @@ -2909,27 +2881,12 @@ namespace OpenMetaverse } else if (status == "complete") { - Logger.DebugLog("CreateItemFromAsset: completed"); + Logger.DebugLog("CreateItemFromAsset: completed"); if (contents.ContainsKey("new_inventory_item") && contents.ContainsKey("new_asset")) { try { callback(true, String.Empty, contents["new_inventory_item"].AsUUID(), contents["new_asset"].AsUUID()); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } - - // Notify everyone of the creation. - // TODO: Is there a way to avoid fetching the whole thing? - FetchItemsCallback fetchCallback = - delegate(List items) - { - if (items.Count > 0) - { - ItemData item = items[0]; - item.AssetUUID = contents["new_asset"].AsUUID(); - try { OnItemCreated(true, item); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } - } - }; - RequestFetchItems(new UUID[] { contents["new_inventory_item"].AsUUID() }, _Agents.AgentID, fetchCallback); } else { @@ -2948,48 +2905,43 @@ namespace OpenMetaverse private void SaveAssetIntoInventoryHandler(Packet packet, Simulator simulator) { SaveAssetIntoInventoryPacket save = (SaveAssetIntoInventoryPacket)packet; - if (OnAssetUpdate != null) - { - try { OnAssetUpdate(save.InventoryData.ItemID, save.InventoryData.NewAssetID); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } - } + + // FIXME: Find this item in the inventory structure and mark the parent as needing an update + //save.InventoryData.ItemID; + Logger.Log("SaveAssetIntoInventory packet received, someone write this function!", Helpers.LogLevel.Error, _Client); } private void InventoryDescendentsHandler(Packet packet, Simulator simulator) { InventoryDescendentsPacket reply = (InventoryDescendentsPacket)packet; - ItemData[] items = null; - FolderData[] folders = null; + if (reply.AgentData.Descendents > 0) { // InventoryDescendantsReply sends a null folder if the parent doesnt contain any folders if (reply.FolderData[0].FolderID != UUID.Zero) { - folders = new FolderData[reply.FolderData.Length]; // Iterate folders in this packet for (int i = 0; i < reply.FolderData.Length; i++) { - UUID folderID = reply.FolderData[i].FolderID; - FolderData folder = new FolderData(folderID); + InventoryFolder folder = new InventoryFolder(reply.FolderData[i].FolderID); folder.ParentUUID = reply.FolderData[i].ParentID; folder.Name = Utils.BytesToString(reply.FolderData[i].Name); folder.PreferredType = (AssetType)reply.FolderData[i].Type; folder.OwnerID = reply.AgentData.OwnerID; - folders[i] = folder; + + _Store[folder.UUID] = folder; } } // InventoryDescendantsReply sends a null item if the parent doesnt contain any items. if (reply.ItemData[0].ItemID != UUID.Zero) { - items = new ItemData[reply.ItemData.Length]; // Iterate items in this packet for (int i = 0; i < reply.ItemData.Length; i++) { if (reply.ItemData[i].ItemID != UUID.Zero) { - UUID itemID = reply.ItemData[i].ItemID; - ItemData item = new ItemData(itemID); + InventoryItem item; /* * Objects that have been attached in-world prior to being stored on the * asset server are stored with the InventoryType of 0 (Texture) @@ -2998,16 +2950,18 @@ namespace OpenMetaverse * This corrects that behavior by forcing Object Asset types that have an * invalid InventoryType with the proper InventoryType of Attachment. */ - if ((AssetType)reply.ItemData[i].Type == AssetType.Object + if ((AssetType)reply.ItemData[i].Type == AssetType.Object && (InventoryType)reply.ItemData[i].InvType == InventoryType.Texture) { + item = CreateInventoryItem(InventoryType.Attachment, reply.ItemData[i].ItemID); item.InventoryType = InventoryType.Attachment; } else { + item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID); item.InventoryType = (InventoryType)reply.ItemData[i].InvType; } - + item.ParentUUID = reply.ItemData[i].FolderID; item.CreatorID = reply.ItemData[i].CreatorID; item.AssetType = (AssetType)reply.ItemData[i].Type; @@ -3027,61 +2981,102 @@ namespace OpenMetaverse item.SalePrice = reply.ItemData[i].SalePrice; item.SaleType = (SaleType)reply.ItemData[i].SaleType; item.OwnerID = reply.AgentData.OwnerID; - items[i] = item; + + _Store[item.UUID] = item; } } } } - #region FolderContents Handling - if (_DescendentsRequests.Count > 0) + InventoryFolder parentFolder = null; + + if (_Store.Contains(reply.AgentData.FolderID) && + _Store[reply.AgentData.FolderID] is InventoryFolder) { - lock (_DescendentsRequests) + parentFolder = _Store[reply.AgentData.FolderID] as InventoryFolder; + } + else + { + Logger.Log("Don't have a reference to FolderID " + reply.AgentData.FolderID.ToString() + + " or it is not a folder", Helpers.LogLevel.Error, _Client); + return; + } + + if (reply.AgentData.Version < parentFolder.Version) + { + Logger.Log("Got an outdated InventoryDescendents packet for folder " + parentFolder.Name + + ", this version = " + reply.AgentData.Version + ", latest version = " + parentFolder.Version, + Helpers.LogLevel.Warning, _Client); + return; + } + + parentFolder.Version = reply.AgentData.Version; + // FIXME: reply.AgentData.Descendants is not parentFolder.DescendentCount if we didn't + // request items and folders + parentFolder.DescendentCount = reply.AgentData.Descendents; + + #region FindObjectsByPath Handling + + if (_Searches.Count > 0) + { + lock (_Searches) { - // Iterate backwards, ensures safe removal: - for (int i = _DescendentsRequests.Count - 1; i >= 0; --i) + StartSearch: + + // Iterate over all of the outstanding searches + for (int i = 0; i < _Searches.Count; i++) { - DescendentsRequest request = _DescendentsRequests[i]; - if (request.Folder == reply.AgentData.FolderID) + InventorySearch search = _Searches[i]; + List folderContents = _Store.GetContents(search.Folder); + + // Iterate over all of the inventory objects in the base search folder + for (int j = 0; j < folderContents.Count; j++) { - // Store the descendent count if we haven't received a responce yet: - if (!request.ReceivedResponse) + // Check if this inventory object matches the current path node + if (folderContents[j].Name == search.Path[search.Level]) { - request.ReceivedResponse = true; - request.Descendents = reply.AgentData.Descendents; - } - - // Store the items and folders: - if (folders != null) - request.FolderContents.AddRange(folders); - if (items != null) - request.ItemContents.AddRange(items); - - _DescendentsRequests[i] = request; - - int contentsReceived = request.FolderContents.Count + request.ItemContents.Count; - - // Fire the partial callback, if we have one: - if (request.PartialCallback != null) - { - request.PartialCallback(reply.AgentData.FolderID, items, folders, request.Descendents - contentsReceived); - } - - // Check if we're done: - if (contentsReceived >= request.Descendents) - { - // Fire the callback: - if (request.Callback != null) + if (search.Level == search.Path.Length - 1) { - request.Callback(reply.AgentData.FolderID, request.ItemContents, request.FolderContents); + Logger.DebugLog("Finished path search of " + String.Join("/", search.Path), _Client); + + // This is the last node in the path, fire the callback and clean up + if (OnFindObjectByPath != null) + { + try { OnFindObjectByPath(String.Join("/", search.Path), folderContents[j].UUID); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } + } + + // Remove this entry and restart the loop since we are changing the collection size + _Searches.RemoveAt(i); + goto StartSearch; + } + else + { + // We found a match but it is not the end of the path, request the next level + Logger.DebugLog(String.Format("Matched level {0}/{1} in a path search of {2}", + search.Level, search.Path.Length - 1, String.Join("/", search.Path)), _Client); + + search.Folder = folderContents[j].UUID; + search.Level++; + _Searches[i] = search; + + RequestFolderContents(search.Folder, search.Owner, true, true, + InventorySortOrder.ByName); } - _DescendentsRequests.RemoveAt(i); } } } } } - #endregion FolderContents Handling + + #endregion FindObjectsByPath Handling + + // Callback for inventory folder contents being updated + if (OnFolderUpdated != null) + { + try { OnFolderUpdated(parentFolder.UUID); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } + } } /// @@ -3103,7 +3098,7 @@ namespace OpenMetaverse continue; } - ItemData item = new ItemData(dataBlock.ItemID, (InventoryType)dataBlock.InvType); + InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType,dataBlock.ItemID); item.AssetType = (AssetType)dataBlock.Type; item.AssetUUID = dataBlock.AssetID; item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); @@ -3124,8 +3119,10 @@ namespace OpenMetaverse item.SalePrice = dataBlock.SalePrice; item.SaleType = (SaleType)dataBlock.SaleType; + // Update the local copy + _Store[item.UUID] = item; + // Look for an "item created" callback - // Let the requester know that its item was created. ItemCreatedCallback createdCallback; if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out createdCallback)) { @@ -3135,26 +3132,43 @@ namespace OpenMetaverse catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } } - // Let everyone know that the item was created - if (OnItemCreated != null) + // TODO: Is this callback even triggered when items are copied? + // Look for an "item copied" callback + ItemCopiedCallback copyCallback; + if (_ItemCopiedCallbacks.TryGetValue(dataBlock.CallbackID, out copyCallback)) { - try { OnItemCreated(true, item); } + _ItemCopiedCallbacks.Remove(dataBlock.CallbackID); + + try { copyCallback(item); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } } - + //This is triggered when an item is received from a task if (OnTaskItemReceived != null) { - try - { - OnTaskItemReceived(dataBlock.ItemID, dataBlock.FolderID, item.CreatorID, item.AssetUUID, - item.InventoryType); - } + try { OnTaskItemReceived(item.UUID, dataBlock.FolderID, item.CreatorID, item.AssetUUID, + item.InventoryType); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } } } } + private void MoveInventoryItemHandler(Packet packet, Simulator simulator) + { + MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet; + + for (int i = 0; i < move.InventoryData.Length; i++) + { + // FIXME: Do something here + string newName = Utils.BytesToString(move.InventoryData[i].NewName); + + Logger.Log(String.Format( + "MoveInventoryItemHandler: Item {0} is moving to Folder {1} with new name \"{2}\". Someone write this function!", + move.InventoryData[i].ItemID.ToString(), move.InventoryData[i].FolderID.ToString(), + newName), Helpers.LogLevel.Warning, _Client); + } + } + private void BulkUpdateInventoryHandler(Packet packet, Simulator simulator) { BulkUpdateInventoryPacket update = packet as BulkUpdateInventoryPacket; @@ -3163,16 +3177,14 @@ namespace OpenMetaverse { foreach (BulkUpdateInventoryPacket.FolderDataBlock dataBlock in update.FolderData) { - if (OnFolderUpdate != null) - { - FolderData folderParams = new FolderData(); - folderParams.Name = Utils.BytesToString(dataBlock.Name); - folderParams.OwnerID = update.AgentData.AgentID; - folderParams.ParentUUID = dataBlock.ParentID; + if (!_Store.Contains(dataBlock.FolderID)) + Logger.Log("Received BulkUpdate for unknown folder: " + dataBlock.FolderID, Helpers.LogLevel.Warning, _Client); - try { OnFolderUpdate(folderParams); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } - } + InventoryFolder folder = new InventoryFolder(dataBlock.FolderID); + folder.Name = Utils.BytesToString(dataBlock.Name); + folder.OwnerID = update.AgentData.AgentID; + folder.ParentUUID = dataBlock.ParentID; + _Store[folder.UUID] = folder; } } @@ -3182,7 +3194,13 @@ namespace OpenMetaverse { BulkUpdateInventoryPacket.ItemDataBlock dataBlock = update.ItemData[i]; - ItemData item = new ItemData(dataBlock.ItemID, (InventoryType)dataBlock.InvType); + // If we are given a folder of items, the item information might arrive before the folder + // (parent) is in the store + if (!_Store.Contains(dataBlock.ItemID)) + Logger.Log("Received BulkUpdate for unknown item: " + dataBlock.ItemID, Helpers.LogLevel.Warning, _Client); + + InventoryItem item = SafeCreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID); + item.AssetType = (AssetType)dataBlock.Type; if (dataBlock.AssetID != UUID.Zero) item.AssetUUID = dataBlock.AssetID; item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); @@ -3203,6 +3221,8 @@ namespace OpenMetaverse item.SalePrice = dataBlock.SalePrice; item.SaleType = (SaleType)dataBlock.SaleType; + _Store[item.UUID] = item; + // Look for an "item created" callback ItemCreatedCallback callback; if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out callback)) @@ -3222,75 +3242,51 @@ namespace OpenMetaverse try { copyCallback(item); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } } - - if (OnItemUpdate != null) - { - try { OnItemUpdate(item); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } - } } } } private void FetchInventoryReplyHandler(Packet packet, Simulator simulator) { - lock (_FetchRequests) + FetchInventoryReplyPacket reply = packet as FetchInventoryReplyPacket; + + foreach (FetchInventoryReplyPacket.InventoryDataBlock dataBlock in reply.InventoryData) { - FetchInventoryReplyPacket reply = packet as FetchInventoryReplyPacket; - foreach (FetchInventoryReplyPacket.InventoryDataBlock dataBlock in reply.InventoryData) + if (dataBlock.InvType == (sbyte)InventoryType.Folder) { - if (dataBlock.InvType == (sbyte)InventoryType.Folder) - { - Logger.Log("Received FetchInventoryReply for an inventory folder, this should not happen!", - Helpers.LogLevel.Error, _Client); - continue; - } + Logger.Log("Received FetchInventoryReply for an inventory folder, this should not happen!", + Helpers.LogLevel.Error, _Client); + continue; + } - ItemData item = new ItemData(dataBlock.ItemID); - item.InventoryType = (InventoryType)dataBlock.InvType; - item.AssetType = (AssetType)dataBlock.Type; - item.AssetUUID = dataBlock.AssetID; - item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); - item.CreatorID = dataBlock.CreatorID; - item.Description = Utils.BytesToString(dataBlock.Description); - item.Flags = dataBlock.Flags; - item.GroupID = dataBlock.GroupID; - item.GroupOwned = dataBlock.GroupOwned; - item.Name = Utils.BytesToString(dataBlock.Name); - item.OwnerID = dataBlock.OwnerID; - item.ParentUUID = dataBlock.FolderID; - item.Permissions = new Permissions( - dataBlock.BaseMask, - dataBlock.EveryoneMask, - dataBlock.GroupMask, - dataBlock.NextOwnerMask, - dataBlock.OwnerMask); - item.SalePrice = dataBlock.SalePrice; - item.SaleType = (SaleType)dataBlock.SaleType; + InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType,dataBlock.ItemID); + item.AssetType = (AssetType)dataBlock.Type; + item.AssetUUID = dataBlock.AssetID; + item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); + item.CreatorID = dataBlock.CreatorID; + item.Description = Utils.BytesToString(dataBlock.Description); + item.Flags = dataBlock.Flags; + item.GroupID = dataBlock.GroupID; + item.GroupOwned = dataBlock.GroupOwned; + item.Name = Utils.BytesToString(dataBlock.Name); + item.OwnerID = dataBlock.OwnerID; + item.ParentUUID = dataBlock.FolderID; + item.Permissions = new Permissions( + dataBlock.BaseMask, + dataBlock.EveryoneMask, + dataBlock.GroupMask, + dataBlock.NextOwnerMask, + dataBlock.OwnerMask); + item.SalePrice = dataBlock.SalePrice; + item.SaleType = (SaleType)dataBlock.SaleType; - #region FetchItems Handling - // Iterate backwards through fetch requests, ensures safe removal: - for (int i = _FetchRequests.Count - 1; i >= 0; --i) - { - FetchRequest request = _FetchRequests[i]; - if (request.RequestedItems.ContainsKey(item.UUID)) - { - request.StoreFetchedItem(item); - if (request.ItemsFetched == request.RequestedItems.Count) - { - // We're done, create the list that the callback needs: - List items = new List(request.ItemsFetched); - foreach (KeyValuePair pair in request.RequestedItems) - items.Add(pair.Value.Value); + _Store[item.UUID] = item; - // Fire the callback: - request.Callback(items); - _FetchRequests.RemoveAt(i); - } - _FetchRequests[i] = request; - } - } - #endregion FetchItems Handling + // Fire the callback for an item being fetched + if (OnItemReceived != null) + { + try { OnItemReceived(item); } + catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } } } } @@ -3315,8 +3311,8 @@ namespace OpenMetaverse // TODO: MainAvatar.InstantMessageDialog.GroupNotice can also be an inventory offer, should we // handle it here? - if (OnObjectOffered != null && - (im.Dialog == InstantMessageDialog.InventoryOffered + if (OnObjectOffered != null && + (im.Dialog == InstantMessageDialog.InventoryOffered || im.Dialog == InstantMessageDialog.TaskInventoryOffered)) { AssetType type = AssetType.Unknown; @@ -3351,25 +3347,27 @@ namespace OpenMetaverse } } + // Find the folder where this is going to go + UUID destinationFolderID = FindFolderForType(type); + // Fire the callback try { ImprovedInstantMessagePacket imp = new ImprovedInstantMessagePacket(); - imp.AgentData.AgentID = _Agents.AgentID; - imp.AgentData.SessionID = _Agents.SessionID; + imp.AgentData.AgentID = _Client.Self.AgentID; + imp.AgentData.SessionID = _Client.Self.SessionID; imp.MessageBlock.FromGroup = false; imp.MessageBlock.ToAgentID = im.FromAgentID; imp.MessageBlock.Offline = 0; imp.MessageBlock.ID = im.IMSessionID; imp.MessageBlock.Timestamp = 0; - imp.MessageBlock.FromAgentName = Utils.StringToBytes(_Agents.Name); + imp.MessageBlock.FromAgentName = Utils.StringToBytes(_Client.Self.Name); imp.MessageBlock.Message = new byte[0]; imp.MessageBlock.ParentEstateID = 0; imp.MessageBlock.RegionID = UUID.Zero; - imp.MessageBlock.Position = _Agents.SimPosition; + imp.MessageBlock.Position = _Client.Self.SimPosition; - UUID destinationFolderID = OnObjectOffered(im, type, objectID, fromTask); - if (destinationFolderID != UUID.Zero) + if (OnObjectOffered(im, type, objectID, fromTask)) { // Accept the inventory offer switch (im.Dialog) @@ -3406,7 +3404,7 @@ namespace OpenMetaverse imp.MessageBlock.BinaryBucket = new byte[0]; } - _Network.SendPacket(imp, simulator); + _Client.Network.SendPacket(imp, simulator); } catch (Exception e) { @@ -3414,27 +3412,29 @@ namespace OpenMetaverse } } } - + private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason, LoginResponseData replyData) { if (loginSuccess) { - if (Settings.ENABLE_INVENTORY_STORE) - { - InventorySkeleton = new InventorySkeleton(replyData.InventoryRoot, replyData.AgentID); - InventorySkeleton.Folders = replyData.InventoryFolders; - } - if (Settings.ENABLE_LIBRARY_STORE) - { - LibrarySkeleton = new InventorySkeleton(replyData.LibraryRoot, replyData.LibraryOwner); - LibrarySkeleton.Folders = replyData.LibraryFolders; - } - if (OnSkeletonsReceived != null) - { - try { OnSkeletonsReceived(this); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, _Client, e); } + // Initialize the store here so we know who owns it: + _Store = new Inventory(_Client, this, _Client.Self.AgentID); + Logger.DebugLog("Setting InventoryRoot to " + replyData.InventoryRoot.ToString(), _Client); + InventoryFolder rootFolder = new InventoryFolder(replyData.InventoryRoot); + rootFolder.Name = String.Empty; + rootFolder.ParentUUID = UUID.Zero; + _Store.RootFolder = rootFolder; - } + for (int i = 0; i < replyData.InventorySkeleton.Length; i++) + _Store.UpdateNodeFor(replyData.InventorySkeleton[i]); + + InventoryFolder libraryRootFolder = new InventoryFolder(replyData.LibraryRoot); + libraryRootFolder.Name = String.Empty; + libraryRootFolder.ParentUUID = UUID.Zero; + _Store.LibraryFolder = libraryRootFolder; + + for(int i = 0; i < replyData.LibrarySkeleton.Length; i++) + _Store.UpdateNodeFor(replyData.LibrarySkeleton[i]); } } diff --git a/OpenMetaverse/InventoryNode.cs b/OpenMetaverse/InventoryNode.cs new file mode 100644 index 00000000..3eff410f --- /dev/null +++ b/OpenMetaverse/InventoryNode.cs @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2007-2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenMetaverse +{ + public class InventoryNode + { + private InventoryBase data; + private InventoryNode parent; + private InventoryNodeDictionary nodes; + + /// + public InventoryBase Data + { + get { return data; } + set { data = value; } + } + + /// + public InventoryNode Parent + { + get { return parent; } + set { parent = value; } + } + + /// + public InventoryNodeDictionary Nodes + { + get + { + if (nodes == null) + nodes = new InventoryNodeDictionary(this); + + return nodes; + } + set { nodes = value; } + } + + /// + /// + /// + public InventoryNode() + { + } + + /// + /// + /// + /// + public InventoryNode(InventoryBase data) + { + this.data = data; + } + + public InventoryNode(InventoryBase data, InventoryNode parent) + { + this.data = data; + this.parent = parent; + + if (parent != null) + { + // Add this node to the collection of parent nodes + lock (parent.Nodes.SyncRoot) parent.Nodes.Add(data.UUID, this); + } + } + + /// + /// + /// + /// + public override string ToString() + { + if (this.Data == null) return "[Empty Node]"; + return this.Data.ToString(); + } + } +} diff --git a/OpenMetaverse/InventoryNodeDictionary.cs b/OpenMetaverse/InventoryNodeDictionary.cs new file mode 100644 index 00000000..c7d4f9a8 --- /dev/null +++ b/OpenMetaverse/InventoryNodeDictionary.cs @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2007-2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenMetaverse +{ + public class InventoryNodeDictionary + { + protected Dictionary Dictionary = new Dictionary(); + protected InventoryNode parent; + protected object syncRoot = new object(); + + public InventoryNode Parent + { + get { return parent; } + set { parent = value; } + } + + public object SyncRoot { get { return syncRoot; } } + + public int Count { get { return Dictionary.Count; } } + + public InventoryNodeDictionary(InventoryNode parentNode) + { + parent = parentNode; + } + + public InventoryNode this[UUID key] + { + get { return (InventoryNode)this.Dictionary[key]; } + set + { + value.Parent = parent; + lock (syncRoot) this.Dictionary[key] = value; + } + } + + public ICollection Keys { get { return this.Dictionary.Keys; } } + public ICollection Values { get { return this.Dictionary.Values; } } + + public void Add(UUID key, InventoryNode value) + { + value.Parent = parent; + lock (syncRoot) this.Dictionary.Add(key, value); + } + + public void Remove(UUID key) + { + lock (syncRoot) this.Dictionary.Remove(key); + } + + public bool Contains(UUID key) + { + return this.Dictionary.ContainsKey(key); + } + } +} diff --git a/OpenMetaverse/Login.cs b/OpenMetaverse/Login.cs index 29b95bb6..5b653095 100644 --- a/OpenMetaverse/Login.cs +++ b/OpenMetaverse/Login.cs @@ -125,8 +125,8 @@ namespace OpenMetaverse public DateTime SecondsSinceEpoch; public UUID InventoryRoot; public UUID LibraryRoot; - public FolderData[] InventoryFolders; - public FolderData[] LibraryFolders; + public InventoryFolder[] InventorySkeleton; + public InventoryFolder[] LibrarySkeleton; public UUID LibraryOwner; public void Parse(LLSDMap reply) @@ -205,10 +205,10 @@ namespace OpenMetaverse SecondsSinceEpoch = Utils.UnixTimeToDateTime(ParseUInt("seconds_since_epoch", reply)); InventoryRoot = ParseMappedUUID("inventory-root", "folder_id", reply); - InventoryFolders = ParseInventoryFolders("inventory-skeleton", AgentID, reply); + InventorySkeleton = ParseInventoryFolders("inventory-skeleton", AgentID, reply); LibraryRoot = ParseMappedUUID("inventory-lib-root", "folder_id", reply); LibraryOwner = ParseMappedUUID("inventory-lib-owner", "agent_id", reply); - LibraryFolders = ParseInventoryFolders("inventory-skel-lib", LibraryOwner, reply); + LibrarySkeleton = ParseInventoryFolders("inventory-skel-lib", LibraryOwner, reply); } public void ToXmlRpc(XmlWriter writer) @@ -464,9 +464,9 @@ namespace OpenMetaverse return UUID.Zero; } - public static FolderData[] ParseInventoryFolders(string key, UUID owner, LLSDMap reply) + public static InventoryFolder[] ParseInventoryFolders(string key, UUID owner, LLSDMap reply) { - List folders = new List(); + List folders = new List(); LLSD skeleton; if (reply.TryGetValue(key, out skeleton) && skeleton.Type == LLSDType.Array) @@ -478,7 +478,7 @@ namespace OpenMetaverse if (array[i].Type == LLSDType.Map) { LLSDMap map = (LLSDMap)array[i]; - FolderData folder = new FolderData(map["folder_id"].AsUUID()); + InventoryFolder folder = new InventoryFolder(map["folder_id"].AsUUID()); folder.PreferredType = (AssetType)map["type_default"].AsInteger(); folder.Version = map["version"].AsInteger(); folder.OwnerID = owner; diff --git a/OpenMetaverse/ReversableDictionary.cs b/OpenMetaverse/ReversableDictionary.cs deleted file mode 100644 index 808a7da4..00000000 --- a/OpenMetaverse/ReversableDictionary.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenMetaverse -{ - public class ReversableDictionary : IDictionary - { - private Dictionary dict; - private Dictionary reverseDict; - - public ReversableDictionary() - : this(10) { } - - public ReversableDictionary(int initialCapacity) - { - dict = new Dictionary(initialCapacity); - reverseDict = new Dictionary(initialCapacity); - } - - #region IDictionary Members - - public void Add(K key, V value) - { - dict.Add(key, value); - reverseDict.Add(value, key); - } - - public bool ContainsKey(K key) - { - return dict.ContainsKey(key); - } - - public ICollection Keys - { - get { return dict.Keys; } - } - - public bool Remove(K key) - { - V value = dict[key]; - bool success = dict.Remove(key); - reverseDict.Remove(value); - return success; - } - - public bool TryGetValue(K key, out V value) - { - return dict.TryGetValue(key, out value); - } - - public bool TryGetKey(V value, out K key) - { - return reverseDict.TryGetValue(value, out key); - } - - public ICollection Values - { - get { return dict.Values; } - } - - public V this[K key] - { - get - { - return dict[key]; - } - set - { - dict[key] = value; - } - } - - public K this[V val] - { - get - { - return reverseDict[val]; - } - set - { - reverseDict[val] = value; - } - } - - #endregion - - #region ICollection> Members - - public void Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } - - public void Clear() - { - dict.Clear(); - reverseDict.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return dict.ContainsKey(item.Key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - foreach (KeyValuePair kvp in dict) - array[arrayIndex++] = kvp; - } - - public int Count - { - get { return dict.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public bool Remove(KeyValuePair item) - { - return Remove(item.Key); - } - - #endregion - - #region IEnumerable> Members - - public IEnumerator> GetEnumerator() - { - foreach (KeyValuePair kvp in dict) - { - yield return kvp; - } - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } -} diff --git a/OpenMetaverse/TextHierarchyParser.cs b/OpenMetaverse/TextHierarchyParser.cs deleted file mode 100644 index ed9457dd..00000000 --- a/OpenMetaverse/TextHierarchyParser.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace OpenMetaverse -{ - public class IndentWriter : TextWriter - { - private string Prefix = String.Empty; - public string Indent = String.Empty; - private bool AddPrefix = false; - - private StringBuilder builder = new StringBuilder(); - - private void TryAddPrefix() - { - if (AddPrefix) - { - builder.Append(Prefix); - AddPrefix = false; - } - } - - public override void Write(string str) - { - TryAddPrefix(); - builder.Append(str); - } - - public override void Write(char c) - { - if (c == '}') - Prefix.Remove(0, Indent.Length); - - TryAddPrefix(); - - builder.Append(c); - - if (c == '{') - Prefix += Indent; - - } - - public override void Write(object o) - { - TryAddPrefix(); - builder.Append(o); - } - - public override void WriteLine(string line) - { - TryAddPrefix(); - builder.AppendLine(line); - AddPrefix = true; - } - - public override void WriteLine(char c) - { - if (c == '}') - Prefix = Prefix.Remove(0, Indent.Length); - - TryAddPrefix(); - builder.Append(c); - builder.AppendLine(); - - if (c == '{') - Prefix += Indent; - - AddPrefix = true; - } - - public override void WriteLine(object o) - { - TryAddPrefix(); - builder.Append(o); - builder.AppendLine(); - AddPrefix = true; - } - - public override Encoding Encoding - { - get { return Encoding.UTF8; } - } - - public override string ToString() - { - return builder.ToString(); - } - } - - public class TextData - { - public string Name; - public string Value; - public Dictionary Nested = new Dictionary(); - public void ParseLine(string line) - { - int firstSpace = line.IndexOfAny(TextHierarchyParser.WordSeperators); - Name = line.Substring(0, firstSpace); - Value = line.Substring(firstSpace + 1).Trim(); - } - - public override string ToString() - { - IndentWriter writer = new IndentWriter(); - writer.Indent = "\t"; - ToString(writer); - return writer.ToString(); - } - - public void ToString(TextWriter sink) - { - sink.Write(Name); - sink.Write('\t'); - sink.WriteLine(Value); - if (Nested.Count > 0) - { - sink.WriteLine('{'); - foreach (TextData child in Nested.Values) - { - child.ToString(sink); - } - sink.WriteLine('}'); - } - } - } - - public class TextHierarchyParser - { - protected internal static readonly char[] WordSeperators = new char[] { ' ', '\t' }; - - public static TextData Parse(TextReader source) - { - string prevLine = null; - string startLine = source.ReadLine().Trim(); - while (startLine[0] != '{') - { - prevLine = startLine; - startLine = source.ReadLine().Trim(); - } - - TextData startParent = new TextData(); - if (prevLine != null) - startParent.ParseLine(prevLine); - ParseNested(startParent, source); - return startParent; - } - - private static void ParseNested(TextData parent, TextReader source) - { - string line = null; - TextData current = null; - do - { - line = source.ReadLine().Trim(); - if (line.Length > 0) - { - if (line[0] == '{') - { - if (current == null) - current = new TextData(); - ParseNested(current, source); - } - else if (line[0] == '}') - { - return; - } - else - { - current = new TextData(); - current.ParseLine(line); - } - } - - if (current != null) - parent.Nested[current.Name] = current; - } while (line != null); - } - } -} diff --git a/Programs/GridImageUpload/frmGridImageUpload.cs b/Programs/GridImageUpload/frmGridImageUpload.cs index 29e2d3b2..2d2a5f61 100644 --- a/Programs/GridImageUpload/frmGridImageUpload.cs +++ b/Programs/GridImageUpload/frmGridImageUpload.cs @@ -78,9 +78,9 @@ namespace GridImageUpload private void LoadImage() { - if (FileName == null || FileName == "") - return; - + if (FileName == null || FileName == "") + return; + string lowfilename = FileName.ToLower(); Bitmap bitmap = null; @@ -175,7 +175,8 @@ namespace GridImageUpload private void cmdConnect_Click(object sender, EventArgs e) { - if (cmdConnect.Text == "Connect") { + if (cmdConnect.Text == "Connect") + { cmdConnect.Text = "Disconnect"; txtFirstName.Enabled = txtLastName.Enabled = txtPassword.Enabled = false; LoginParams lp = new LoginParams(); @@ -187,7 +188,9 @@ namespace GridImageUpload cmdConnect.Enabled = false; Client.Network.BeginLogin(lp); return; - } else { + } + else + { Client.Network.Logout(); cmdConnect.Text = "Connect"; txtFirstName.Enabled = txtLastName.Enabled = txtPassword.Enabled = true; @@ -198,7 +201,8 @@ namespace GridImageUpload void Network_OnLogin(LoginStatus login, string message) { - if (InvokeRequired) { + if (InvokeRequired) + { BeginInvoke(new MethodInvoker( delegate() { @@ -207,10 +211,13 @@ namespace GridImageUpload )); return; } - if (login == LoginStatus.Success) { + if (login == LoginStatus.Success) + { MessageBox.Show("Connected: " + message); cmdConnect.Enabled = true; - } else if (login == LoginStatus.Failed) { + } + else if (login == LoginStatus.Failed) + { MessageBox.Show(this, String.Format("Error logging in ({0}): {1}", Client.Network.LoginErrorKey, Client.Network.LoginMessage)); cmdConnect.Text = "Connect"; @@ -224,8 +231,8 @@ namespace GridImageUpload private void cmdLoad_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); - dialog.Filter = - "Image Files (*.jp2,*.j2c,*.jpg,*.jpeg,*.gif,*.png,*.bmp,*.tga,*.tif,*.tiff,*.ico,*.wmf,*.emf)|" + + dialog.Filter = + "Image Files (*.jp2,*.j2c,*.jpg,*.jpeg,*.gif,*.png,*.bmp,*.tga,*.tif,*.tiff,*.ico,*.wmf,*.emf)|" + "*.jp2;*.j2c;*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.tga;*.tif;*.tiff;*.ico;*.wmf;*.emf;"; if (dialog.ShowDialog() == DialogResult.OK) @@ -312,11 +319,12 @@ namespace GridImageUpload UpdateAssetID(); // Fix the permissions on the new upload since they are fscked by default + InventoryItem item = Client.Inventory.FetchItem(itemID, Client.Self.AgentID, 1000 * 15); Transferred = UploadData.Length; BeginInvoke((MethodInvoker)delegate() { SetProgress(); }); - ItemData item; - if (Client.Inventory.FetchItem(itemID, Client.Self.AgentID, TimeSpan.FromSeconds(15), out item)) + + if (item != null) { item.Permissions.EveryoneMask = PermissionMask.All; item.Permissions.NextOwnerMask = PermissionMask.All; diff --git a/Programs/GridProxy/Plugins/ClientAO.cs b/Programs/GridProxy/Plugins/ClientAO.cs index d940fbd1..406fad18 100644 --- a/Programs/GridProxy/Plugins/ClientAO.cs +++ b/Programs/GridProxy/Plugins/ClientAO.cs @@ -182,7 +182,7 @@ public class ClientAO : ProxyPlugin // Number of directory descendents received int nbdescendantsreceived; //List of items in the current folder - Dictionary currentFolderItems; + Dictionary currentFolderItems; //Asset download request ID UUID assetdownloadID; //Downloaded bytes so far @@ -295,7 +295,7 @@ public class ClientAO : ProxyPlugin InventorySortOrder order) { //empty the dictionnary containing current folder items by name - currentFolderItems = new Dictionary(); + currentFolderItems = new Dictionary(); //reset the number of descendants received nbdescendantsreceived = 0; //build a packet to request the content @@ -388,7 +388,7 @@ public class ClientAO : ProxyPlugin //configuration notecard if (reply.ItemData[i].ItemID != UUID.Zero) { - ItemData item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID); + InventoryItem item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID); item.ParentUUID = reply.ItemData[i].FolderID; item.CreatorID = reply.ItemData[i].CreatorID; item.AssetType = (AssetType)reply.ItemData[i].Type; @@ -465,13 +465,29 @@ public class ClientAO : ProxyPlugin } } - public static ItemData CreateInventoryItem(InventoryType type, UUID id) + public static InventoryItem CreateInventoryItem(InventoryType type, UUID id) { - return new ItemData(id, type); + switch (type) + { + case InventoryType.Texture: return new InventoryTexture(id); + case InventoryType.Sound: return new InventorySound(id); + case InventoryType.CallingCard: return new InventoryCallingCard(id); + case InventoryType.Landmark: return new InventoryLandmark(id); + case InventoryType.Object: return new InventoryObject(id); + case InventoryType.Notecard: return new InventoryNotecard(id); + case InventoryType.Category: return new InventoryCategory(id); + case InventoryType.LSL: return new InventoryLSL(id); + case InventoryType.Snapshot: return new InventorySnapshot(id); + case InventoryType.Attachment: return new InventoryAttachment(id); + case InventoryType.Wearable: return new InventoryWearable(id); + case InventoryType.Animation: return new InventoryAnimation(id); + case InventoryType.Gesture: return new InventoryGesture(id); + default: return new InventoryItem(type, id); + } } //Ask for download of an item - public UUID RequestInventoryAsset(ItemData item) + public UUID RequestInventoryAsset(InventoryItem item) { // Build the request packet and send it TransferRequestPacket request = new TransferRequestPacket(); diff --git a/Programs/Simian/Simian.cs b/Programs/Simian/Simian.cs index 118cc2c8..d3bb57e7 100644 --- a/Programs/Simian/Simian.cs +++ b/Programs/Simian/Simian.cs @@ -271,10 +271,10 @@ namespace Simian response.HomeLookAt = Vector3.UnitX; response.HomePosition = new Vector3(128f, 128f, 25f); response.HomeRegion = Helpers.UIntsToLong(regionX, regionY); - response.InventoryFolders = null; + response.InventorySkeleton = null; response.InventoryRoot = UUID.Random(); response.LastName = agent.LastName; - response.LibraryFolders = null; + response.LibrarySkeleton = null; response.LibraryOwner = response.AgentID; response.LibraryRoot = UUID.Random(); response.LookAt = Vector3.UnitX; diff --git a/Programs/examples/TestClient/Commands/Appearance/CloneCommand.cs b/Programs/examples/TestClient/Commands/Appearance/CloneCommand.cs index 0262a03b..1b9acbfe 100644 --- a/Programs/examples/TestClient/Commands/Appearance/CloneCommand.cs +++ b/Programs/examples/TestClient/Commands/Appearance/CloneCommand.cs @@ -61,7 +61,7 @@ namespace OpenMetaverse.TestClient #endregion AvatarAppearance to AgentSetAppearance // Detach everything we are currently wearing - Client.Appearance.AddAttachments(new List(0), true); + Client.Appearance.AddAttachments(new List(), true); // Send the new appearance packet Client.Network.SendPacket(set); diff --git a/Programs/examples/TestClient/Commands/Appearance/WearCommand.cs b/Programs/examples/TestClient/Commands/Appearance/WearCommand.cs index cdf5e76e..1fbd4f4c 100644 --- a/Programs/examples/TestClient/Commands/Appearance/WearCommand.cs +++ b/Programs/examples/TestClient/Commands/Appearance/WearCommand.cs @@ -1,12 +1,11 @@ using System; using OpenMetaverse; -using System.Collections.Generic; namespace OpenMetaverse.TestClient { public class WearCommand : Command { - public WearCommand(TestClient testClient) + public WearCommand(TestClient testClient) { Client = testClient; Name = "wear"; @@ -17,7 +16,7 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { if (args.Length < 1) - return "Usage: wear [outfit name] [nobake] eg: 'wear \"/Clothing/My Outfit\" [nobake]'"; + return "Usage: wear [outfit name] eg: 'wear /My Outfit/Dance Party"; string target = String.Empty; bool bake = true; @@ -30,13 +29,11 @@ namespace OpenMetaverse.TestClient target = target + args[ct] + " "; } - List results = Client.InventoryStore.InventoryFromPath(target, Client.CurrentDirectory, true); - if (results.Count == 0 || !(results[0] is InventoryFolder)) - return "Unable to find folder at " + target; + target = target.TrimEnd(); try { - Client.Appearance.WearOutfit(results[0] as InventoryFolder, bake); + Client.Appearance.WearOutfit(target.Split('/'), bake); } catch (InvalidOutfitException ex) { diff --git a/Programs/examples/TestClient/Commands/Inventory/BackupCommand.cs b/Programs/examples/TestClient/Commands/Inventory/BackupCommand.cs index 7a9683da..c2dad1d4 100644 --- a/Programs/examples/TestClient/Commands/Inventory/BackupCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/BackupCommand.cs @@ -155,7 +155,7 @@ namespace OpenMetaverse.TestClient BackupWorker = new BackgroundWorker(); BackupWorker.WorkerSupportsCancellation = true; - BackupWorker.DoWork +=new DoWorkEventHandler(bwBackup_DoWork); + BackupWorker.DoWork += new DoWorkEventHandler(bwBackup_DoWork); BackupWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwBackup_RunWorkerCompleted); BackupWorker.RunWorkerAsync(args); @@ -202,7 +202,7 @@ namespace OpenMetaverse.TestClient qdi.IsRequested = true; qdi.TransferID = Client.Assets.RequestInventoryAsset( qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true); - + lock (CurrentDownloads) CurrentDownloads.Add(qdi); } } @@ -229,7 +229,7 @@ namespace OpenMetaverse.TestClient TextItemsFound = 0; - args = (string[]) e.Argument; + args = (string[])e.Argument; lock (CurrentDownloads) CurrentDownloads.Clear(); @@ -240,7 +240,7 @@ namespace OpenMetaverse.TestClient DirectoryInfo di = new DirectoryInfo(args[1]); // recurse on the root folder into the entire inventory - BackupFolder(Client.InventoryStore.RootFolder, di.FullName); + BackupFolder(Client.Inventory.Store.RootNode, di.FullName); } /// @@ -248,43 +248,42 @@ namespace OpenMetaverse.TestClient /// /// The current leaf in the inventory tree /// path so far, in the form @"c:\here" -- this needs to be "clean" for the current filesystem - private void BackupFolder(InventoryFolder folder, string sPathSoFar) + private void BackupFolder(InventoryNode folder, string sPathSoFar) { StringBuilder sbRequests = new StringBuilder(); - - if (folder.IsStale) - folder.DownloadContents(TimeSpan.FromSeconds(10)); + // FIXME: + //Client.Inventory.RequestFolderContents(folder.Data.UUID, Client.Self.AgentID, true, true, false, + // InventorySortOrder.ByName); // first scan this folder for text - - foreach (InventoryBase ib in folder) + foreach (InventoryNode iNode in folder.Nodes.Values) { if (BackupWorker.CancellationPending) return; - if (ib is InventoryItem) + if (iNode.Data is OpenMetaverse.InventoryItem) { - InventoryItem ii = ib as InventoryItem; - if (ii.Data.AssetType == AssetType.LSLText || ii.Data.AssetType == AssetType.Notecard) + InventoryItem ii = iNode.Data as InventoryItem; + if (ii.AssetType == AssetType.LSLText || ii.AssetType == AssetType.Notecard) { // check permissions on scripts - if (ii.Data.AssetType == AssetType.LSLText) + if (ii.AssetType == AssetType.LSLText) { - if ((ii.Data.Permissions.OwnerMask & PermissionMask.Modify) == PermissionMask.None) + if ((ii.Permissions.OwnerMask & PermissionMask.Modify) == PermissionMask.None) { // skip this one continue; } } - string sExtension = (ii.Data.AssetType == AssetType.LSLText) ? ".lsl" : ".txt"; + string sExtension = (ii.AssetType == AssetType.LSLText) ? ".lsl" : ".txt"; // make the output file string sPath = sPathSoFar + @"\" + MakeValid(ii.Name.Trim()) + sExtension; // create the new qdi - QueuedDownloadInfo qdi = new QueuedDownloadInfo(sPath, ii.Data.AssetUUID, ii.UUID, UUID.Zero, - Client.Self.AgentID, ii.Data.AssetType); - + QueuedDownloadInfo qdi = new QueuedDownloadInfo(sPath, ii.AssetUUID, iNode.Data.UUID, UUID.Zero, + Client.Self.AgentID, ii.AssetType); + // add it to the queue lock (PendingDownloads) { @@ -296,12 +295,12 @@ namespace OpenMetaverse.TestClient } // now run any subfolders - foreach (InventoryBase ib in folder) + foreach (InventoryNode i in folder.Nodes.Values) { if (BackupWorker.CancellationPending) return; - else if (ib is InventoryFolder) - BackupFolder(ib as InventoryFolder, sPathSoFar + @"\" + MakeValid(ib.Name.Trim())); + else if (i.Data is OpenMetaverse.InventoryFolder) + BackupFolder(i, sPathSoFar + @"\" + MakeValid(i.Data.Name.Trim())); } } @@ -336,7 +335,7 @@ namespace OpenMetaverse.TestClient else { TextItemErrors++; - Console.WriteLine("{0}: Download of asset {1} ({2}) failed with status {3}", Name, r.FileName, + Console.WriteLine("{0}: Download of asset {1} ({2}) failed with status {3}", Name, r.FileName, r.AssetID.ToString(), asset.Status.ToString()); } diff --git a/Programs/examples/TestClient/Commands/Inventory/ChangeDirectoryCommand.cs b/Programs/examples/TestClient/Commands/Inventory/ChangeDirectoryCommand.cs index 1a9dd785..e5e3f7b6 100644 --- a/Programs/examples/TestClient/Commands/Inventory/ChangeDirectoryCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/ChangeDirectoryCommand.cs @@ -19,34 +19,68 @@ namespace OpenMetaverse.TestClient.Commands.Inventory.Shell public override string Execute(string[] args, UUID fromAgentID) { Manager = Client.Inventory; - Inventory = Client.InventoryStore; + Inventory = Client.Inventory.Store; + if (args.Length > 1) + return "Usage: cd [path-to-folder]"; + string pathStr = ""; + string[] path = null; if (args.Length == 0) - return "Current folder: " + Client.CurrentDirectory.Name; - - string path = args[0]; - for(int i = 1; i < args.Length; ++i) { - path += " " + args[i]; + path = new string[] { "" }; + // cd without any arguments doesn't do anything. } - - List results = Inventory.InventoryFromPath(path, Client.CurrentDirectory, true); - if (results.Count == 0) - return "Can not find inventory at: " + path; - InventoryFolder destFolder = null; - foreach (InventoryBase ib in results) + else if (args.Length == 1) { - if (ib is InventoryFolder) + pathStr = args[0]; + path = pathStr.Split(new char[] { '/' }); + // Use '/' as a path seperator. + } + InventoryFolder currentFolder = Client.CurrentDirectory; + if (pathStr.StartsWith("/")) + currentFolder = Inventory.RootFolder; + + if (currentFolder == null) // We need this to be set to something. + return "Error: Client not logged in."; + + // Traverse the path, looking for the + for (int i = 0; i < path.Length; ++i) + { + string nextName = path[i]; + if (string.IsNullOrEmpty(nextName) || nextName == ".") + continue; // Ignore '.' and blanks, stay in the current directory. + if (nextName == ".." && currentFolder != Inventory.RootFolder) { - destFolder = ib as InventoryFolder; - break; + // If we encounter .., move to the parent folder. + currentFolder = Inventory[currentFolder.ParentUUID] as InventoryFolder; + } + else + { + List currentContents = Inventory.GetContents(currentFolder); + // Try and find an InventoryBase with the corresponding name. + bool found = false; + foreach (InventoryBase item in currentContents) + { + // Allow lookup by UUID as well as name: + if (item.Name == nextName || item.UUID.ToString() == nextName) + { + found = true; + if (item is InventoryFolder) + { + currentFolder = item as InventoryFolder; + } + else + { + return item.Name + " is not a folder."; + } + } + } + if (!found) + return nextName + " not found in " + currentFolder.Name; } } - if (destFolder == null) - return path + " is not a folder."; - - Client.CurrentDirectory = destFolder; - return "Current folder: " + Client.CurrentDirectory.Name; + Client.CurrentDirectory = currentFolder; + return "Current folder: " + currentFolder.Name; } } -} +} \ No newline at end of file diff --git a/Programs/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs b/Programs/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs index 06a5694c..2508a487 100644 --- a/Programs/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs @@ -16,19 +16,19 @@ namespace OpenMetaverse.TestClient void OnNoteUpdate(bool success, string status, UUID itemID, UUID assetID) { - if (success) - Console.WriteLine("Notecard successfully uploaded, ItemID {0} AssetID {1}", itemID, assetID); + if (success) + Console.WriteLine("Notecard successfully uploaded, ItemID {0} AssetID {1}", itemID, assetID); } public override string Execute(string[] args, UUID fromAgentID) { - if(args.Length < 1) + if (args.Length < 1) return "Usage: createnotecard filename.txt"; string file = String.Empty; - for (int ct = 0; ct < args.Length; ct++) - file = file + args[ct] + " "; - file = file.TrimEnd(); + for (int ct = 0; ct < args.Length; ct++) + file = file + args[ct] + " "; + file = file.TrimEnd(); Console.WriteLine("Filename: {0}", file); if (!File.Exists(file)) @@ -45,9 +45,10 @@ namespace OpenMetaverse.TestClient Client.Inventory.RequestCreateItem(Client.Inventory.FindFolderForType(AssetType.Notecard), file, desc, AssetType.Notecard, UUID.Random(), InventoryType.Notecard, PermissionMask.All, - delegate(bool success, ItemData item) { - if(success) // upload the asset - Client.Inventory.RequestUploadNotecardAsset(CreateNotecardAsset(body), item.UUID, new InventoryManager.NotecardUploadedAssetCallback(OnNoteUpdate)); + delegate(bool success, InventoryItem item) + { + if (success) // upload the asset + Client.Inventory.RequestUploadNotecardAsset(CreateNotecardAsset(body), item.UUID, new InventoryManager.NotecardUploadedAssetCallback(OnNoteUpdate)); } ); return "Done"; @@ -83,4 +84,4 @@ namespace OpenMetaverse.TestClient return assetData; } } -} +} \ No newline at end of file diff --git a/Programs/examples/TestClient/Commands/Inventory/DeleteFolderCommand.cs b/Programs/examples/TestClient/Commands/Inventory/DeleteFolderCommand.cs index 4df06234..0d225d07 100644 --- a/Programs/examples/TestClient/Commands/Inventory/DeleteFolderCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/DeleteFolderCommand.cs @@ -14,7 +14,7 @@ namespace OpenMetaverse.TestClient /// public class DeleteFolderCommand : Command { - public DeleteFolderCommand(TestClient testClient) + public DeleteFolderCommand(TestClient testClient) { Name = "deleteFolder"; Description = "Moves a folder to the Trash Folder"; @@ -27,27 +27,19 @@ namespace OpenMetaverse.TestClient string target = String.Empty; for (int ct = 0; ct < args.Length; ct++) target = target + args[ct] + " "; + target = target.TrimEnd(); // initialize results list List found = new List(); try { // find the folder - - found = Client.InventoryStore.InventoryFromPath(target, Client.CurrentDirectory, true); - if (found.Count > 0) + found = Client.Inventory.LocalFind(Client.Inventory.Store.RootFolder.UUID, target.Split('/'), 0, true); + if (found.Count.Equals(1)) { - InventoryBase item = found[0]; - InventoryFolder trash = Client.InventoryStore[Client.Inventory.FindFolderForType(AssetType.TrashFolder)] as InventoryFolder; - if (trash != null) - { - item.Move(trash); - return String.Format("Moved folder {0} ({1}) to Trash", item.Name, item.UUID); - } - } - else - { - return String.Format("Unable to locate {0}", target); + // move the folder to the trash folder + Client.Inventory.MoveFolder(found[0].UUID, Client.Inventory.FindFolderForType(AssetType.TrashFolder)); + return String.Format("Moved folder {0} to Trash", found[0].Name); } } catch (InvalidOutfitException ex) @@ -55,6 +47,6 @@ namespace OpenMetaverse.TestClient return "Folder Not Found: (" + ex.Message + ")"; } return string.Empty; - } - } -} + } + } +} \ No newline at end of file diff --git a/Programs/examples/TestClient/Commands/Inventory/GiveItemCommand.cs b/Programs/examples/TestClient/Commands/Inventory/GiveItemCommand.cs index 1ed5f6bc..32ce044d 100644 --- a/Programs/examples/TestClient/Commands/Inventory/GiveItemCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/GiveItemCommand.cs @@ -26,24 +26,34 @@ namespace OpenMetaverse.TestClient.Commands.Inventory.Shell return "First argument expected agent UUID."; } Manager = Client.Inventory; - Inventory = Client.InventoryStore; + Inventory = Manager.Store; string ret = ""; string nl = "\n"; for (int i = 1; i < args.Length; ++i) { - string itemPath = args[i]; - - List results = Inventory.InventoryFromPath(itemPath, Client.CurrentDirectory, true); - - if (results.Count == 0) + string inventoryName = args[i]; + // WARNING: Uses local copy of inventory contents, need to download them first. + List contents = Inventory.GetContents(Client.CurrentDirectory); + bool found = false; + foreach (InventoryBase b in contents) { - ret += "No inventory item at " + itemPath + " found." + nl; - } - else - { - results[0].Give(dest, true); - ret += "Gave " + results[0].Name + nl; + if (inventoryName == b.Name || inventoryName == b.UUID.ToString()) + { + found = true; + if (b is InventoryItem) + { + InventoryItem item = b as InventoryItem; + Manager.GiveItem(item.UUID, item.Name, item.AssetType, dest, true); + ret += "Gave " + item.Name + nl; + } + else + { + ret += "Unable to give folder " + b.Name + nl; + } + } } + if (!found) + ret += "No inventory item named " + inventoryName + " found." + nl; } return ret; } diff --git a/Programs/examples/TestClient/Commands/Inventory/InventoryCommand.cs b/Programs/examples/TestClient/Commands/Inventory/InventoryCommand.cs index f46d5992..701cda91 100644 --- a/Programs/examples/TestClient/Commands/Inventory/InventoryCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/InventoryCommand.cs @@ -14,8 +14,8 @@ namespace OpenMetaverse.TestClient { private Inventory Inventory; private InventoryManager Manager; - - public InventoryCommand(TestClient testClient) + + public InventoryCommand(TestClient testClient) { Name = "i"; Description = "Prints out inventory."; @@ -25,7 +25,7 @@ namespace OpenMetaverse.TestClient public override string Execute(string[] args, UUID fromAgentID) { Manager = Client.Inventory; - Inventory = Client.InventoryStore; + Inventory = Manager.Store; StringBuilder result = new StringBuilder(); @@ -35,29 +35,17 @@ namespace OpenMetaverse.TestClient return result.ToString(); } - void PrintFolder(InventoryFolder folder, StringBuilder result, int indent) + void PrintFolder(InventoryFolder f, StringBuilder result, int indent) { - folder.DownloadContents(TimeSpan.FromSeconds(10)); - foreach (InventoryBase b in folder) + foreach (InventoryBase i in Manager.FolderContents(f.UUID, Client.Self.AgentID, true, true, InventorySortOrder.ByName, 3000)) { - if (b is InventoryFolder) + result.AppendFormat("{0}{1} ({2})\n", new String(' ', indent * 2), i.Name, i.UUID); + if (i is InventoryFolder) { - result.Append(Print(b as InventoryFolder, indent)); - PrintFolder(b as InventoryFolder, result, indent + 1); - } - else if (b is InventoryItem) - { - result.Append(Print(b as InventoryItem, indent)); + InventoryFolder folder = (InventoryFolder)i; + PrintFolder(folder, result, indent + 1); } } } - string Print(InventoryItem item, int indent) - { - return string.Format("{0}{1} ({2})\n", new String(' ', indent * 2), item.Data.Name, item.UUID); - } - string Print(InventoryFolder folder, int indent) - { - return string.Format("{0}{1} ({2})\n", new String(' ', indent * 2), folder.Data.Name, folder.UUID); - } - } -} + } +} \ No newline at end of file diff --git a/Programs/examples/TestClient/Commands/Inventory/ListContentsCommand.cs b/Programs/examples/TestClient/Commands/Inventory/ListContentsCommand.cs index be90ba3b..1fb2391e 100644 --- a/Programs/examples/TestClient/Commands/Inventory/ListContentsCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/ListContentsCommand.cs @@ -17,49 +17,20 @@ namespace OpenMetaverse.TestClient.Commands.Inventory.Shell } public override string Execute(string[] args, UUID fromAgentID) { - Manager = Client.Inventory; - Inventory = Client.InventoryStore; if (args.Length > 1) - return "Usage: ls [-l] [directory path]"; + return "Usage: ls [-l]"; bool longDisplay = false; - InventoryFolder directory = Client.CurrentDirectory; - if (args.Length > 0) - { - int start = 0; - if (args[0] == "-l") - { - longDisplay = true; - start = 1; - } - if (start < args.Length) - { - string path = args[start]; - for (int i = start + 1; i < args.Length; ++i) - { - path += " " + args[i]; - } - bool found = false; - List results = Inventory.InventoryFromPath(path, Client.CurrentDirectory, true); - foreach (InventoryBase ib in results) - { - if (ib is InventoryFolder) - { - directory = ib as InventoryFolder; - found = true; - } - } - if (!found) - return "Unable to find directory at path: " + path; - } - } - - if (directory.IsStale) - directory.DownloadContents(TimeSpan.FromSeconds(30)); + if (args.Length > 0 && args[0] == "-l") + longDisplay = true; + Manager = Client.Inventory; + Inventory = Manager.Store; + // WARNING: Uses local copy of inventory contents, need to download them first. + List contents = Inventory.GetContents(Client.CurrentDirectory); string displayString = ""; string nl = "\n"; // New line character // Pretty simple, just print out the contents. - foreach (InventoryBase b in directory) + foreach (InventoryBase b in contents) { if (longDisplay) { @@ -80,17 +51,16 @@ namespace OpenMetaverse.TestClient.Commands.Inventory.Shell { InventoryItem item = b as InventoryItem; displayString += "-"; - displayString += PermMaskString(item.Data.Permissions.OwnerMask); - displayString += PermMaskString(item.Data.Permissions.GroupMask); - displayString += PermMaskString(item.Data.Permissions.EveryoneMask); + displayString += PermMaskString(item.Permissions.OwnerMask); + displayString += PermMaskString(item.Permissions.GroupMask); + displayString += PermMaskString(item.Permissions.EveryoneMask); displayString += " " + item.UUID; displayString += " " + item.Name; } } else { - string name = b.Name; - displayString += name; + displayString += b.Name; } displayString += nl; } @@ -106,7 +76,8 @@ namespace OpenMetaverse.TestClient.Commands.Inventory.Shell /// /// /// - private static string PermMaskString(PermissionMask mask) { + private static string PermMaskString(PermissionMask mask) + { string str = ""; if (((uint)mask | (uint)PermissionMask.Copy) == (uint)PermissionMask.Copy) str += "C"; diff --git a/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs b/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs index d8094990..8df7543a 100644 --- a/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/ObjectInventoryCommand.cs @@ -29,22 +29,24 @@ namespace OpenMetaverse.TestClient else return "Couldn't find prim " + objectID.ToString(); - List items; - List folders; - Client.Inventory.GetTaskInventory(objectID, objectLocalID, TimeSpan.FromMilliseconds(1000 * 30), out items, out folders); + List items = Client.Inventory.GetTaskInventory(objectID, objectLocalID, 1000 * 30); if (items != null) { string result = String.Empty; - foreach (ItemData item in items) + for (int i = 0; i < items.Count; i++) { - result += String.Format("[Item] Name: {0} Desc: {1} Type: {2}", item.Name, item.Description, + if (items[i] is InventoryFolder) + { + result += String.Format("[Folder] Name: {0}", items[i].Name) + Environment.NewLine; + } + else + { + InventoryItem item = (InventoryItem)items[i]; + result += String.Format("[Item] Name: {0} Desc: {1} Type: {2}", item.Name, item.Description, item.AssetType) + Environment.NewLine; - } - foreach (FolderData folder in folders) - { - result += String.Format("[Folder] Name: {0}", folder.Name) + Environment.NewLine; + } } return result; diff --git a/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs b/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs index 6735d6bd..756914a1 100644 --- a/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs +++ b/Programs/examples/TestClient/Commands/Prims/ChangePermsCommand.cs @@ -131,18 +131,20 @@ namespace OpenMetaverse.TestClient { if ((prim.Flags & LLObject.ObjectFlags.InventoryEmpty) == 0) { - List items; - List folders; - Client.Inventory.GetTaskInventory(prim.ID, prim.LocalID, TimeSpan.FromSeconds(30), out items, out folders); + List items = Client.Inventory.GetTaskInventory(prim.ID, prim.LocalID, 1000 * 30); if (items != null) { - foreach (ItemData item in items) + for (int i = 0; i < items.Count; i++) { - ItemData iitem = item; - iitem.Permissions.NextOwnerMask = Perms; - Client.Inventory.UpdateTaskInventory(prim.LocalID, iitem); - ++taskItems; + if (!(items[i] is InventoryFolder)) + { + InventoryItem item = (InventoryItem)items[i]; + item.Permissions.NextOwnerMask = Perms; + + Client.Inventory.UpdateTaskInventory(prim.LocalID, item); + ++taskItems; + } } } } diff --git a/Programs/examples/TestClient/TestClient.cs b/Programs/examples/TestClient/TestClient.cs index fdeb3b52..4aaf37c7 100644 --- a/Programs/examples/TestClient/TestClient.cs +++ b/Programs/examples/TestClient/TestClient.cs @@ -12,14 +12,14 @@ namespace OpenMetaverse.TestClient { public UUID GroupID = UUID.Zero; public Dictionary GroupMembers; - public Dictionary Appearances = new Dictionary(); - public Dictionary Commands = new Dictionary(); - public bool Running = true; + public Dictionary Appearances = new Dictionary(); + public Dictionary Commands = new Dictionary(); + public bool Running = true; public bool GroupCommands = false; public string MasterName = String.Empty; public UUID MasterKey = UUID.Zero; public bool AllowObjectMaster = false; - public ClientManager ClientManager; + public ClientManager ClientManager; public VoiceManager VoiceManager; // Shell-like inventory commands need to be aware of the 'current' inventory folder. public InventoryFolder CurrentDirectory = null; @@ -35,7 +35,7 @@ namespace OpenMetaverse.TestClient /// public TestClient(ClientManager manager) { - ClientManager = manager; + ClientManager = manager; updateTimer = new System.Timers.Timer(500); updateTimer.Elapsed += new System.Timers.ElapsedEventHandler(updateTimer_Elapsed); @@ -60,6 +60,7 @@ namespace OpenMetaverse.TestClient Network.RegisterCallback(PacketType.AlertMessage, new NetworkManager.PacketCallback(AlertMessageHandler)); VoiceManager = new VoiceManager(this); + updateTimer.Start(); } @@ -68,16 +69,12 @@ namespace OpenMetaverse.TestClient /// /// The status of the login /// Error message on failure, MOTD on success. - public void LoginHandler(LoginStatus login, string message) + public void LoginHandler(LoginStatus login, string message) { if (login == LoginStatus.Success) { - // Create the stores: - InventoryStore = new Inventory(Inventory, Inventory.InventorySkeleton); - LibraryStore = new Inventory(Inventory, Inventory.LibrarySkeleton); - - // Start in the inventory root folder: - CurrentDirectory = InventoryStore.RootFolder; + // Start in the inventory root folder. + CurrentDirectory = Inventory.Store.RootFolder; } } @@ -103,17 +100,18 @@ namespace OpenMetaverse.TestClient public void RegisterCommand(Command command) { - command.Client = this; - if (!Commands.ContainsKey(command.Name.ToLower())) - { + command.Client = this; + if (!Commands.ContainsKey(command.Name.ToLower())) + { Commands.Add(command.Name.ToLower(), command); - } + } } //breaks up large responses to deal with the max IM size private void SendResponseIM(GridClient client, UUID fromAgentID, string data) { - for ( int i = 0 ; i < data.Length ; i += 1024 ) { + for (int i = 0; i < data.Length; i += 1024) + { int y; if ((i + 1023) > data.Length) { @@ -128,33 +126,33 @@ namespace OpenMetaverse.TestClient } } - public void DoCommand(string cmd, UUID fromAgentID) + public void DoCommand(string cmd, UUID fromAgentID) { - string[] tokens; + string[] tokens; try { tokens = Parsing.ParseArguments(cmd); } catch (FormatException ex) { Console.WriteLine(ex.Message); return; } if (tokens.Length == 0) return; - - string firstToken = tokens[0].ToLower(); + + string firstToken = tokens[0].ToLower(); // "all balance" will send the balance command to all currently logged in bots - if (firstToken == "all" && tokens.Length > 1) - { - cmd = String.Empty; + if (firstToken == "all" && tokens.Length > 1) + { + cmd = String.Empty; - // Reserialize all of the arguments except for "all" - for (int i = 1; i < tokens.Length; i++) - { - cmd += tokens[i] + " "; - } + // Reserialize all of the arguments except for "all" + for (int i = 1; i < tokens.Length; i++) + { + cmd += tokens[i] + " "; + } - ClientManager.DoCommandAll(cmd, fromAgentID); + ClientManager.DoCommandAll(cmd, fromAgentID); - return; - } + return; + } if (Commands.ContainsKey(firstToken)) { @@ -220,10 +218,7 @@ namespace OpenMetaverse.TestClient bool groupIM = im.GroupIM && GroupMembers != null && GroupMembers.ContainsKey(im.FromAgentID) ? true : false; - if ((im.Dialog == InstantMessageDialog.MessageFromObject) && !AllowObjectMaster) - return; - - if (im.FromAgentID == MasterKey || im.FromAgentName == MasterName || (GroupCommands && groupIM)) + if (im.FromAgentID == MasterKey || (GroupCommands && groupIM)) { // Received an IM from someone that is authenticated Console.WriteLine("<{0} ({1})> {2}: {3} (@{4}:{5})", im.GroupIM ? "GroupIM" : "IM", im.Dialog, im.FromAgentName, im.Message, im.RegionID, im.Position); @@ -248,31 +243,23 @@ namespace OpenMetaverse.TestClient im.RegionID, im.Position); return; } + } - private UUID Inventory_OnInventoryObjectReceived(InstantMessage offer, AssetType type, + private bool Inventory_OnInventoryObjectReceived(InstantMessage offer, AssetType type, UUID objectID, bool fromTask) { if (MasterKey != UUID.Zero) { if (offer.FromAgentID != MasterKey) - return UUID.Zero; + return false; } else if (GroupMembers != null && !GroupMembers.ContainsKey(offer.FromAgentID)) { - return UUID.Zero; - } - else if (MasterName != String.Empty) - { - if (offer.FromAgentName != MasterName) - return UUID.Zero; - } - else if (fromTask && !AllowObjectMaster) - { - return UUID.Zero; + return false; } - return Inventory.FindFolderForType(type); + return true; } - } + } } diff --git a/Programs/importprimscript/importprimscript.cs b/Programs/importprimscript/importprimscript.cs index 49946794..94840d60 100644 --- a/Programs/importprimscript/importprimscript.cs +++ b/Programs/importprimscript/importprimscript.cs @@ -131,7 +131,7 @@ namespace importprimscript // // Create a folder to hold all of our texture uploads - UploadFolderID = Client.Inventory.CreateFolder(Client.Inventory.InventorySkeleton.RootUUID, scriptfilename); + UploadFolderID = Client.Inventory.CreateFolder(Client.Inventory.Store.RootFolder.UUID, scriptfilename); // Loop through each sculpty and do what we need to do for (int i = 0; i < sculpties.Count; i++) diff --git a/prebuild.xml b/prebuild.xml index 086e37f5..d3d9dcb8 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -204,7 +204,7 @@ - + @@ -331,7 +331,7 @@ - +