using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; using OpenMetaverse.StructuredData; namespace OpenMetaverse { /// /// Base Class for Inventory Items /// [Serializable()] public abstract class InventoryBase : ISerializable { /// of item/folder public 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 protected InventoryBase(UUID itemID) { if (itemID == UUID.Zero) Logger.Log("Initializing an InventoryBase with UUID.Zero", Helpers.LogLevel.Warning); UUID = itemID; } /// /// Get object data /// /// /// public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("UUID", UUID); info.AddValue("ParentUUID", ParentUUID); info.AddValue("Name", Name); info.AddValue("OwnerID", OwnerID); } /// /// Inventory base ctor /// /// /// protected InventoryBase(SerializationInfo info, StreamingContext ctxt) { UUID = (UUID)info.GetValue("UUID", typeof(UUID)); ParentUUID = (UUID)info.GetValue("ParentUUID", typeof(UUID)); Name = (string)info.GetValue("Name", typeof(string)); OwnerID = (UUID)info.GetValue("OwnerID", typeof(UUID)); } /// /// 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 obj) { return obj is InventoryBase inv && 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; } /// /// Convert inventory to OSD /// /// OSD representation public abstract OSD GetOSD(); } /// /// An Item in Inventory /// [Serializable] public class InventoryItem : InventoryBase { public override string ToString() { return AssetType + " " + AssetUUID + " (" + InventoryType + " " + UUID + ") '" + Name + "'/'" + Description + "' " + Permissions; } /// The of this item public UUID AssetUUID; /// The combined of this item public Permissions Permissions; /// The type of item from public AssetType AssetType; /// The type of item from the enum public InventoryType InventoryType; /// The of the creator of this item public UUID CreatorID; /// A Description of this item public string Description; /// The s this item is set to or owned by public UUID GroupID; /// If true, item is owned by a group public bool GroupOwned; /// The price this item can be purchased for public int SalePrice; /// The type of sale from the enum public SaleType SaleType; /// Combined flags from public uint Flags; /// Time and date this inventory item was created, stored as /// UTC (Coordinated Universal Time) public DateTime CreationDate; /// Used to update the AssetID in requests sent to the server public UUID TransactionID; /// The of the previous owner of the item public UUID LastOwnerID; /// /// 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; } /// /// Indicates inventory item is a link /// /// True if inventory item is a link to another inventory item public bool IsLink() { return AssetType == AssetType.Link || AssetType == AssetType.LinkFolder; } /// /// /// Get object data /// /// /// public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("AssetUUID", AssetUUID, typeof(UUID)); info.AddValue("Permissions", Permissions, typeof(Permissions)); info.AddValue("AssetType", AssetType); info.AddValue("InventoryType", InventoryType); info.AddValue("CreatorID", CreatorID); info.AddValue("Description", Description); info.AddValue("GroupID", GroupID); info.AddValue("GroupOwned", GroupOwned); info.AddValue("SalePrice", SalePrice); info.AddValue("SaleType", SaleType); info.AddValue("Flags", Flags); info.AddValue("CreationDate", CreationDate); info.AddValue("LastOwnerID", LastOwnerID); } /// /// Inventory item ctor /// /// /// public InventoryItem(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { AssetUUID = (UUID)info.GetValue("AssetUUID", typeof(UUID)); Permissions = (Permissions)info.GetValue("Permissions", typeof(Permissions)); AssetType = (AssetType)info.GetValue("AssetType", typeof(AssetType)); InventoryType = (InventoryType)info.GetValue("InventoryType", typeof(InventoryType)); CreatorID = (UUID)info.GetValue("CreatorID", typeof(UUID)); Description = (string)info.GetValue("Description", typeof(string)); GroupID = (UUID)info.GetValue("GroupID", typeof(UUID)); GroupOwned = (bool)info.GetValue("GroupOwned", typeof(bool)); SalePrice = (int)info.GetValue("SalePrice", typeof(int)); SaleType = (SaleType)info.GetValue("SaleType", typeof(SaleType)); Flags = (uint)info.GetValue("Flags", typeof(uint)); CreationDate = (DateTime)info.GetValue("CreationDate", typeof(DateTime)); LastOwnerID = (UUID)info.GetValue("LastOwnerID", typeof(UUID)); } /// /// 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 AssetUUID.GetHashCode() ^ Permissions.GetHashCode() ^ AssetType.GetHashCode() ^ InventoryType.GetHashCode() ^ Description.GetHashCode() ^ GroupID.GetHashCode() ^ GroupOwned.GetHashCode() ^ SalePrice.GetHashCode() ^ SaleType.GetHashCode() ^ Flags.GetHashCode() ^ CreationDate.GetHashCode() ^ LastOwnerID.GetHashCode(); } /// /// /// Compares an object /// /// The object to compare /// true if comparison object matches public override bool Equals(object obj) { return obj is InventoryItem item && 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 override bool Equals(InventoryBase o) { return o is InventoryItem item && 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) && o.AssetType == AssetType && o.AssetUUID == AssetUUID && o.CreationDate == CreationDate && o.Description == Description && o.Flags == Flags && o.GroupID == GroupID && o.GroupOwned == GroupOwned && o.InventoryType == InventoryType && o.Permissions.Equals(Permissions) && o.SalePrice == SalePrice && o.SaleType == SaleType && o.LastOwnerID == LastOwnerID; } /// /// Create InventoryItem from OSD /// /// OSD Data that makes up InventoryItem /// Inventory item created public static InventoryItem FromOSD(OSD data) { OSDMap descItem = (OSDMap)data; InventoryType type = (InventoryType)descItem["inv_type"].AsInteger(); if (type == InventoryType.Texture && (AssetType)descItem["type"].AsInteger() == AssetType.Object) { type = InventoryType.Attachment; } InventoryItem item = InventoryManager.CreateInventoryItem(type, descItem["item_id"]); item.ParentUUID = descItem["parent_id"]; item.Name = descItem["name"]; item.Description = descItem["desc"]; item.OwnerID = descItem["agent_id"]; item.ParentUUID = descItem["parent_id"]; item.AssetUUID = descItem["asset_id"]; item.AssetType = (AssetType)descItem["type"].AsInteger(); item.CreationDate = Utils.UnixTimeToDateTime(descItem["created_at"]); item.Flags = descItem["flags"]; OSDMap perms = (OSDMap)descItem["permissions"]; item.CreatorID = perms["creator_id"]; item.LastOwnerID = perms["last_owner_id"]; item.Permissions = new Permissions(perms["base_mask"], perms["everyone_mask"], perms["group_mask"], perms["next_owner_mask"], perms["owner_mask"]); item.GroupOwned = perms["is_owner_group"]; item.GroupID = perms["group_id"]; OSDMap sale = (OSDMap)descItem["sale_info"]; item.SalePrice = sale["sale_price"]; item.SaleType = (SaleType)sale["sale_type"].AsInteger(); return item; } /// /// Convert InventoryItem to OSD /// /// OSD representation of InventoryItem public override OSD GetOSD() { OSDMap map = new OSDMap { ["item_id"] = UUID, ["parent_id"] = ParentUUID, ["type"] = (sbyte)AssetType, ["inv_type"] = (sbyte)InventoryType, ["flags"] = Flags, ["name"] = Name, ["desc"] = Description, ["asset_id"] = AssetUUID, ["created_at"] = CreationDate }; OSDMap perms = (OSDMap)Permissions.GetOSD(); perms["creator_id"] = CreatorID; perms["last_owner_id"] = LastOwnerID; perms["is_owner_group"] = GroupOwned; perms["group_id"] = GroupID; map["permissions"] = perms; OSDMap sale = new OSDMap { ["sale_price"] = SalePrice, ["sale_type"] = (sbyte)SaleType }; map["sale_info"] = sale; return map; } } /// /// /// InventoryTexture Class representing a graphical image /// /// [Serializable()] public class InventoryTexture : InventoryItem { /// /// Construct an InventoryTexture object /// /// A which becomes the /// objects AssetUUID public InventoryTexture(UUID itemID) : base(itemID) { InventoryType = InventoryType.Texture; } /// /// Construct an InventoryTexture object from a serialization stream /// public InventoryTexture(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Texture; } } /// /// /// InventorySound Class representing a playable sound /// [Serializable()] public class InventorySound : InventoryItem { /// /// Construct an InventorySound object /// /// A which becomes the /// objects AssetUUID public InventorySound(UUID itemID) : base(itemID) { InventoryType = InventoryType.Sound; } /// /// Construct an InventorySound object from a serialization stream /// public InventorySound(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Sound; } } /// /// /// InventoryCallingCard Class, contains information on another avatar /// [Serializable()] public class InventoryCallingCard : InventoryItem { /// /// Construct an InventoryCallingCard object /// /// A which becomes the /// objects AssetUUID public InventoryCallingCard(UUID itemID) : base(itemID) { InventoryType = InventoryType.CallingCard; } /// /// Construct an InventoryCallingCard object from a serialization stream /// public InventoryCallingCard(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.CallingCard; } } /// /// /// InventoryLandmark Class, contains details on a specific location /// [Serializable()] public class InventoryLandmark : InventoryItem { /// /// Construct an InventoryLandmark object /// /// A which becomes the /// objects AssetUUID public InventoryLandmark(UUID itemID) : base(itemID) { InventoryType = InventoryType.Landmark; } /// /// Construct an InventoryLandmark object from a serialization stream /// public InventoryLandmark(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Landmark; } /// /// Landmarks use the InventoryItemFlags struct and will have a flag of 1 set if they have been visited /// public bool LandmarkVisited { get => (Flags & 1) != 0; set { if (value) Flags |= 1; else Flags &= ~1u; } } } /// /// /// InventoryObject Class contains details on a primitive or coalesced set of primitives /// [Serializable()] public class InventoryObject : InventoryItem { /// /// Construct an InventoryObject object /// /// A which becomes the /// objects AssetUUID public InventoryObject(UUID itemID) : base(itemID) { InventoryType = InventoryType.Object; } /// /// Construct an InventoryObject object from a serialization stream /// public InventoryObject(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Object; } /// /// Gets or sets the upper byte of the Flags value /// public InventoryItemFlags ItemFlags { get => (InventoryItemFlags)(Flags & ~0xFF); set => Flags = (uint)value | (Flags & 0xFF); } /// /// Gets or sets the object attachment point, the lower byte of the Flags value /// public AttachmentPoint AttachPoint { get => (AttachmentPoint)(Flags & 0xFF); set => Flags = (uint)value | (Flags & 0xFFFFFF00); } } /// /// /// InventoryNotecard Class, contains details on an encoded text document /// [Serializable()] public class InventoryNotecard : InventoryItem { /// /// Construct an InventoryNotecard object /// /// A which becomes the /// objects AssetUUID public InventoryNotecard(UUID itemID) : base(itemID) { InventoryType = InventoryType.Notecard; } /// /// Construct an InventoryNotecard object from a serialization stream /// public InventoryNotecard(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Notecard; } } /// /// /// InventoryCategory Class /// /// TODO: Is this even used for anything? [Serializable()] public class InventoryCategory : InventoryItem { /// /// Construct an InventoryCategory object /// /// A which becomes the /// objects AssetUUID public InventoryCategory(UUID itemID) : base(itemID) { InventoryType = InventoryType.Category; } /// /// Construct an InventoryCategory object from a serialization stream /// public InventoryCategory(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Category; } } /// /// /// InventoryLSL Class, represents a Linden Scripting Language object /// [Serializable()] public class InventoryLSL : InventoryItem { /// /// Construct an InventoryLSL object /// /// A which becomes the /// objects AssetUUID public InventoryLSL(UUID itemID) : base(itemID) { InventoryType = InventoryType.LSL; } /// /// Construct an InventoryLSL object from a serialization stream /// public InventoryLSL(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.LSL; } } /// /// /// InventorySnapshot Class, an image taken with the viewer /// [Serializable()] public class InventorySnapshot : InventoryItem { /// /// /// Construct an InventorySnapshot object /// /// A which becomes the /// objects AssetUUID public InventorySnapshot(UUID itemID) : base(itemID) { InventoryType = InventoryType.Snapshot; } /// /// Construct an InventorySnapshot object from a serialization stream /// public InventorySnapshot(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Snapshot; } } /// /// InventoryAttachment Class, contains details on an attachable object /// [Serializable()] public class InventoryAttachment : InventoryItem { /// /// Construct an InventoryAttachment object /// /// A which becomes the /// objects AssetUUID public InventoryAttachment(UUID itemID) : base(itemID) { InventoryType = InventoryType.Attachment; } /// /// Construct an InventoryAttachment object from a serialization stream /// public InventoryAttachment(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Attachment; } /// /// Get the last AttachmentPoint this object was attached to /// public AttachmentPoint AttachmentPoint { get => (AttachmentPoint)Flags; set => Flags = (uint)value; } } /// /// /// InventoryWearable Class, details on a clothing item or body part /// [Serializable()] public class InventoryWearable : InventoryItem { /// /// Construct an InventoryWearable object /// /// A which becomes the /// objects AssetUUID public InventoryWearable(UUID itemID) : base(itemID) { InventoryType = InventoryType.Wearable; } /// /// Construct an InventoryWearable object from a serialization stream /// public InventoryWearable(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Wearable; } /// /// The , Skin, Shape, Skirt, Etc /// public WearableType WearableType { get => (WearableType)Flags; set => Flags = (uint)value; } } /// /// /// InventoryAnimation Class, A bvh encoded object which animates an avatar /// [Serializable()] public class InventoryAnimation : InventoryItem { /// /// Construct an InventoryAnimation object /// /// A which becomes the /// objects AssetUUID public InventoryAnimation(UUID itemID) : base(itemID) { InventoryType = InventoryType.Animation; } /// /// Construct an InventoryAnimation object from a serialization stream /// public InventoryAnimation(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Animation; } } /// /// /// InventoryGesture Class, details on a series of animations, sounds, and actions /// [Serializable()] public class InventoryGesture : InventoryItem { /// /// Construct an InventoryGesture object /// /// A which becomes the /// objects AssetUUID public InventoryGesture(UUID itemID) : base(itemID) { InventoryType = InventoryType.Gesture; } /// /// Construct an InventoryGesture object from a serialization stream /// public InventoryGesture(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { InventoryType = InventoryType.Gesture; } } /// /// /// A folder contains s and has certain attributes specific /// to itself /// [Serializable()] public class InventoryFolder : InventoryBase { /// The Preferred for a folder. public FolderType PreferredType; /// The Version of this folder public int Version; /// Number of child items this folder contains. public int DescendentCount; /// /// Constructor /// /// UUID of the folder public InventoryFolder(UUID itemID) : base(itemID) { PreferredType = FolderType.None; Version = 1; DescendentCount = 0; } /// /// Returns folder name /// /// Return folder name as string public override string ToString() { return Name; } /// /// Get Serilization data for this InventoryFolder object /// /// /// public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("PreferredType", PreferredType, typeof(FolderType)); info.AddValue("Version", Version); info.AddValue("DescendentCount", DescendentCount); } /// /// Construct an InventoryFolder object from a serialization stream /// public InventoryFolder(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { PreferredType = (FolderType)info.GetValue("PreferredType", typeof(FolderType)); Version = (int)info.GetValue("Version", typeof(int)); DescendentCount = (int)info.GetValue("DescendentCount", typeof(int)); } /// /// Return int hash code /// /// Hash code as integer public override int GetHashCode() { return PreferredType.GetHashCode() ^ Version.GetHashCode() ^ DescendentCount.GetHashCode(); } public override bool Equals(object obj) { return obj is InventoryFolder folder && Equals(folder); } public override bool Equals(InventoryBase o) { return o is InventoryFolder folder && Equals(folder); } public bool Equals(InventoryFolder o) { return base.Equals(o as InventoryBase) && o.DescendentCount == DescendentCount && o.PreferredType == PreferredType && o.Version == Version; } /// /// Create InventoryFolder from OSD /// /// OSD Data that makes up InventoryFolder /// Inventory folder created public static InventoryFolder FromOSD(OSD data) { OSDMap res = (OSDMap)data; InventoryFolder folder = new InventoryFolder(res["item_id"].AsUUID()) { UUID = res["item_id"].AsUUID(), ParentUUID = res["parent_id"].AsUUID(), PreferredType = (FolderType)(sbyte)res["type"].AsUInteger(), Name = res["name"] }; return folder; } /// /// Convert InventoryFolder to OSD /// /// OSD representation of InventoryFolder public override OSD GetOSD() { OSDMap res = new OSDMap(4) { ["item_id"] = UUID, ["parent_id"] = ParentUUID, ["type"] = (sbyte)PreferredType, ["name"] = Name }; return res; } } }