/*
* Copyright (c) 2006-2016, openmetaverse.co
* Copyright (c) 2021-2022, Sjofn LLC.
* 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.co 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.Runtime.Serialization;
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;
}
}
}