Merge pull request #110 from nooperation/messagepack_serialization
Replace all file serilization with MessagePack
This commit is contained in:
@@ -127,7 +127,6 @@ namespace OpenMetaverse
|
||||
|
||||
#endregion Enums
|
||||
|
||||
[Serializable]
|
||||
public class AppearanceManagerException : Exception
|
||||
{
|
||||
public AppearanceManagerException(string message)
|
||||
|
||||
31
LibreMetaverse/Formatters/UUIDFormatter.cs
Normal file
31
LibreMetaverse/Formatters/UUIDFormatter.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse.Formatters
|
||||
{
|
||||
public class UUIDFormatter : IMessagePackFormatter<UUID>
|
||||
{
|
||||
public static readonly UUIDFormatter Instance = new UUIDFormatter();
|
||||
|
||||
public void Serialize(ref MessagePackWriter writer, UUID value, MessagePackSerializerOptions options)
|
||||
{
|
||||
writer.Write(value.GetBytes());
|
||||
}
|
||||
|
||||
public UUID Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||
{
|
||||
var sequence = reader.ReadBytes();
|
||||
if (sequence == null)
|
||||
{
|
||||
throw new MessagePackSerializationException("Invalid UUID");
|
||||
}
|
||||
|
||||
var bytes = sequence.Value.ToArray();
|
||||
return new UUID(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,14 +28,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using MemoryPack;
|
||||
#else
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
#endif
|
||||
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
@@ -43,7 +36,6 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// Exception class to identify inventory exceptions
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class InventoryException : Exception
|
||||
{
|
||||
public InventoryException() { }
|
||||
@@ -357,156 +349,24 @@ namespace OpenMetaverse
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current inventory structure to a cache file
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the cache file to save to</param>
|
||||
public void SaveToDisk(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream stream = File.Open(filename, FileMode.Create))
|
||||
{
|
||||
#if !NET7_0_OR_GREATER
|
||||
var bformatter = new BinaryFormatter();
|
||||
#endif
|
||||
lock (Items)
|
||||
{
|
||||
Logger.Log($"Caching {Items.Count} inventory items to {filename}", Helpers.LogLevel.Info);
|
||||
foreach (var kvp in Items)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
MemoryPackSerializer.SerializeAsync(stream, kvp.Value);
|
||||
#else
|
||||
bformatter.Serialize(stream, kvp.Value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Error saving inventory cache to disk", Helpers.LogLevel.Error, e);
|
||||
}
|
||||
InventoryCache.SaveToDisk(filename, Items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads in inventory cache file into the inventory structure. Note only valid to call after login has been successful.
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the cache file to load</param>
|
||||
/// <returns>The number of inventory items successfully reconstructed into the inventory node tree</returns>
|
||||
/// <returns>The number of inventory items successfully reconstructed into the inventory node tree, or -1 on error</returns>
|
||||
public int RestoreFromDisk(string filename)
|
||||
{
|
||||
var cacheNodes = new List<InventoryNode>();
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
using (var stream = File.Open(filename, FileMode.Open))
|
||||
{
|
||||
#if !NET7_0_OR_GREATER
|
||||
var bformatter = new BinaryFormatter();
|
||||
#endif
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
var node = MemoryPackSerializer.DeserializeAsync<InventoryNode>(stream);
|
||||
cacheNodes.Add(node.Result);
|
||||
#else
|
||||
var node = (InventoryNode)bformatter.Deserialize(stream);
|
||||
cacheNodes.Add(node);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Error accessing inventory cache file", Helpers.LogLevel.Error, e);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Logger.Log($"Read {cacheNodes.Count} items from inventory cache file", Helpers.LogLevel.Info);
|
||||
|
||||
var dirtyFolders = new HashSet<UUID>();
|
||||
|
||||
// First pass: process InventoryFolders
|
||||
foreach (var cacheNode in cacheNodes)
|
||||
{
|
||||
if(!(cacheNode.Data is InventoryFolder cacheFolder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cacheNode.ParentID == UUID.Zero)
|
||||
{
|
||||
//We don't need the root nodes "My Inventory" etc as they will already exist for the correct
|
||||
// user of this cache.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryGetValue(cacheNode.Data.UUID, out var serverNode))
|
||||
{
|
||||
// This is an orphaned folder that no longer exists on the server.
|
||||
continue;
|
||||
}
|
||||
|
||||
var serverFolder = (InventoryFolder)serverNode.Data;
|
||||
serverNode.NeedsUpdate = cacheFolder.Version != serverFolder.Version;
|
||||
|
||||
if (serverNode.NeedsUpdate)
|
||||
{
|
||||
Logger.DebugLog($"Inventory Cache/Server version mismatch on {cacheNode.Data.Name} {cacheFolder.Version} vs {serverFolder.Version}");
|
||||
dirtyFolders.Add(cacheNode.Data.UUID);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: process InventoryItems
|
||||
var itemCount = 0;
|
||||
foreach (var cacheNode in cacheNodes)
|
||||
{
|
||||
if (!(cacheNode.Data is InventoryItem cacheItem))
|
||||
{
|
||||
// Only process InventoryItems
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryGetValue(cacheNode.ParentID, out var serverParentNode))
|
||||
{
|
||||
// This item does not have a parent in our known inventory. The folder was probably deleted on the server
|
||||
// and our cache is old
|
||||
continue;
|
||||
}
|
||||
|
||||
if(dirtyFolders.Contains(serverParentNode.Data.UUID))
|
||||
{
|
||||
// This item belongs to a folder that has been marked as dirty, so it too is dirty and must be skipped
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Items.ContainsKey(cacheItem.UUID))
|
||||
{
|
||||
// This item was already added to our Items store, likely added from previous server requests during this session
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryAdd(cacheItem.UUID, cacheNode))
|
||||
{
|
||||
Logger.Log($"Failed to add cache item node {cacheItem.Name} with parent {serverParentNode.Data.Name}", Helpers.LogLevel.Info);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add this cached InventoryItem node to the parent
|
||||
cacheNode.Parent = serverParentNode;
|
||||
serverParentNode.Nodes.Add(cacheItem.UUID, cacheNode);
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
Logger.Log($"Reassembled {itemCount} items from inventory cache file", Helpers.LogLevel.Info);
|
||||
return itemCount;
|
||||
return InventoryCache.RestoreFromDisk(filename, Items);
|
||||
}
|
||||
|
||||
#region Operators
|
||||
|
||||
@@ -26,56 +26,51 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using OpenMetaverse.StructuredData;
|
||||
#if NET7_0_OR_GREATER
|
||||
using MemoryPack;
|
||||
#endif
|
||||
using MessagePack;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Class for Inventory Items
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
[MemoryPackUnion(0, typeof(InventoryFolder))]
|
||||
[MemoryPackUnion(1, typeof(InventoryItem))]
|
||||
[MemoryPackUnion(2, typeof(InventoryAnimation))]
|
||||
[MemoryPackUnion(3, typeof(InventoryAttachment))]
|
||||
[MemoryPackUnion(4, typeof(InventoryCallingCard))]
|
||||
[MemoryPackUnion(5, typeof(InventoryCategory))]
|
||||
[MemoryPackUnion(6, typeof(InventoryGesture))]
|
||||
[MemoryPackUnion(7, typeof(InventoryLSL))]
|
||||
[MemoryPackUnion(8, typeof(InventoryLandmark))]
|
||||
[MemoryPackUnion(9, typeof(InventoryMaterial))]
|
||||
[MemoryPackUnion(10, typeof(InventoryNotecard))]
|
||||
[MemoryPackUnion(11, typeof(InventoryObject))]
|
||||
[MemoryPackUnion(12, typeof(InventorySettings))]
|
||||
[MemoryPackUnion(13, typeof(InventorySnapshot))]
|
||||
[MemoryPackUnion(14, typeof(InventorySound))]
|
||||
[MemoryPackUnion(15, typeof(InventoryTexture))]
|
||||
[MemoryPackUnion(16, typeof(InventoryWearable))]
|
||||
#endif
|
||||
public abstract partial class InventoryBase : ISerializable
|
||||
[MessagePackObject]
|
||||
[Union(0, typeof(InventoryFolder))]
|
||||
[Union(1, typeof(InventoryItem))]
|
||||
[Union(2, typeof(InventoryAnimation))]
|
||||
[Union(3, typeof(InventoryAttachment))]
|
||||
[Union(4, typeof(InventoryCallingCard))]
|
||||
[Union(5, typeof(InventoryCategory))]
|
||||
[Union(6, typeof(InventoryGesture))]
|
||||
[Union(7, typeof(InventoryLSL))]
|
||||
[Union(8, typeof(InventoryLandmark))]
|
||||
[Union(9, typeof(InventoryMaterial))]
|
||||
[Union(10, typeof(InventoryNotecard))]
|
||||
[Union(11, typeof(InventoryObject))]
|
||||
[Union(12, typeof(InventorySettings))]
|
||||
[Union(13, typeof(InventorySnapshot))]
|
||||
[Union(14, typeof(InventorySound))]
|
||||
[Union(15, typeof(InventoryTexture))]
|
||||
[Union(16, typeof(InventoryWearable))]
|
||||
public abstract partial class InventoryBase
|
||||
{
|
||||
/// <summary><see cref="OpenMetaverse.UUID"/> of item/folder</summary>
|
||||
[Key("UUID")]
|
||||
public UUID UUID;
|
||||
/// <summary><see cref="OpenMetaverse.UUID"/> of parent folder</summary>
|
||||
[Key("ParentUUID")]
|
||||
public UUID ParentUUID;
|
||||
/// <summary>Name of item/folder</summary>
|
||||
[Key("Name")]
|
||||
public string Name;
|
||||
/// <summary>Item/Folder Owners <see cref="OpenMetaverse.UUID"/></summary>
|
||||
[Key("OwnerID")]
|
||||
public UUID OwnerID;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor, takes an itemID as a parameter
|
||||
/// </summary>
|
||||
/// <param name="UUID">The <see cref="OpenMetaverse.UUID"/> of the item</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
protected InventoryBase(UUID UUID)
|
||||
{
|
||||
if (UUID == UUID.Zero)
|
||||
@@ -83,32 +78,6 @@ namespace OpenMetaverse
|
||||
this.UUID = UUID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get object data
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="context"></param>
|
||||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("UUID", UUID);
|
||||
info.AddValue("ParentUUID", ParentUUID);
|
||||
info.AddValue("Name", Name);
|
||||
info.AddValue("OwnerID", OwnerID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inventory base ctor
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ctxt"></param>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
@@ -152,10 +121,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// An Item in Inventory
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryItem : InventoryBase
|
||||
{
|
||||
public override string ToString()
|
||||
@@ -163,45 +129,58 @@ namespace OpenMetaverse
|
||||
return $"{AssetType} {AssetUUID} ({InventoryType} {UUID}) '{Name}'/'{Description}' {Permissions}";
|
||||
}
|
||||
/// <summary><see cref="UUID"/> of the underlying asset</summary>
|
||||
[Key("AssetUUID")]
|
||||
public UUID AssetUUID;
|
||||
/// <summary>Combined <see cref="OpenMetaverse.Permissions"/> of the item</summary>
|
||||
[Key("Permissions")]
|
||||
public Permissions Permissions;
|
||||
/// <summary><see cref="OpenMetaverse.AssetType"/> of the underlying asset</summary>
|
||||
[Key("AssetType")]
|
||||
public AssetType AssetType;
|
||||
/// <summary><see cref="OpenMetaverse.InventoryType"/> of the item</summary>
|
||||
[Key("InventoryType")]
|
||||
public InventoryType InventoryType;
|
||||
/// <summary><see cref="UUID"/> of the creator of the item</summary>
|
||||
[Key("CreatorID")]
|
||||
public UUID CreatorID;
|
||||
/// <summary>Description of the item</summary>
|
||||
[Key("Description")]
|
||||
public string Description;
|
||||
/// <summary><see cref="Group"/>s <see cref="UUID"/> the item is owned by</summary>
|
||||
[Key("GroupID")]
|
||||
public UUID GroupID;
|
||||
/// <summary>If true, item is owned by a group</summary>
|
||||
[Key("GroupOwned")]
|
||||
public bool GroupOwned;
|
||||
/// <summary>Price the item can be purchased for</summary>
|
||||
[Key("SalePrice")]
|
||||
public int SalePrice;
|
||||
/// <summary><see cref="OpenMetaverse.SaleType"/> of the item</summary>
|
||||
[Key("SaleType")]
|
||||
public SaleType SaleType;
|
||||
/// <summary>Combined flags from <see cref="InventoryItemFlags"/></summary>
|
||||
[Key("Flags")]
|
||||
public uint Flags;
|
||||
|
||||
/// <summary>Time and date the inventory item was created, stored as
|
||||
/// UTC (Coordinated Universal Time)</summary>
|
||||
[Key("CreationDate")]
|
||||
public DateTime CreationDate;
|
||||
/// <summary>Used to update the AssetID in requests sent to the server</summary>
|
||||
[Key("TransactionID")]
|
||||
public UUID TransactionID;
|
||||
/// <summary><see cref="UUID"/> of the previous owner of the item</summary>
|
||||
[Key("LastOwnerID")]
|
||||
public UUID LastOwnerID;
|
||||
|
||||
/// <summary>inventoryID that this item points to, else this item's inventoryID</summary>
|
||||
[IgnoreMember]
|
||||
public UUID ActualUUID => IsLink() ? AssetUUID : UUID;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new InventoryItem object
|
||||
/// </summary>
|
||||
/// <param name="UUID">The <see cref="UUID"/> of the item</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryItem(UUID UUID)
|
||||
: base(UUID) { }
|
||||
|
||||
@@ -221,53 +200,6 @@ namespace OpenMetaverse
|
||||
return AssetType == AssetType.Link || AssetType == AssetType.LinkFolder;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Get object data
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="context"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inventory item ctor
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ctxt"></param>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
@@ -508,10 +440,7 @@ namespace OpenMetaverse
|
||||
/// InventoryTexture Class representing a graphical image
|
||||
/// </summary>
|
||||
/// <seealso cref="T:OpenMetaverse.Imaging.ManagedImage" />
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryTexture : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -519,33 +448,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryTexture(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryTexture object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryTexture(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Texture;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventorySound Class representing a playable sound
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventorySound : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -553,33 +467,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventorySound(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Sound;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventorySound object from a serialization stream
|
||||
/// </summary>
|
||||
public InventorySound(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Sound;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryCallingCard Class, contains information on another avatar
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryCallingCard : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -587,33 +486,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryCallingCard(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.CallingCard;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryCallingCard object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryCallingCard(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.CallingCard;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryLandmark Class, contains details on a specific location
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryLandmark : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -621,28 +505,16 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryLandmark(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Landmark;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryLandmark object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryLandmark(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Landmark;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Landmarks use the InventoryItemFlags struct and will have a flag of 1 set if they have been visited
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public bool LandmarkVisited
|
||||
{
|
||||
get => (Flags & 1) != 0;
|
||||
@@ -658,10 +530,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// InventoryObject Class contains details on a primitive or coalesced set of primitives
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryObject : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -669,27 +538,16 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryObject(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryObject object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryObject(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the upper byte of the Flags value
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public InventoryItemFlags ItemFlags
|
||||
{
|
||||
get => (InventoryItemFlags)(Flags & ~0xFF);
|
||||
@@ -699,6 +557,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// Gets or sets the object attachment point, the lower byte of the Flags value
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public AttachmentPoint AttachPoint
|
||||
{
|
||||
get => (AttachmentPoint)(Flags & 0xFF);
|
||||
@@ -710,10 +569,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// InventoryNotecard Class, contains details on an encoded text document
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryNotecard : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -721,33 +577,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryNotecard(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Notecard;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryNotecard object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryNotecard(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Notecard;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryCategory Class
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryCategory : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -755,33 +596,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryCategory(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Category;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryCategory object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryCategory(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Category;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryLSL Class, represents a Linden Scripting Language object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryLSL : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -789,33 +615,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryLSL(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.LSL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryLSL object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryLSL(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.LSL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventorySnapshot Class, an image taken with the viewer
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventorySnapshot : InventoryItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -824,32 +635,17 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="T:OpenMetaverse.UUID" /> which becomes the
|
||||
/// <seealso cref="T:OpenMetaverse.InventoryItem" /> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventorySnapshot(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Snapshot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventorySnapshot object from a serialization stream
|
||||
/// </summary>
|
||||
public InventorySnapshot(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// InventoryAttachment Class, contains details on an attachable object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryAttachment : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -857,27 +653,16 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryAttachment(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Attachment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryAttachment object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryAttachment(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Attachment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the last AttachmentPoint this object was attached to
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public AttachmentPoint AttachmentPoint
|
||||
{
|
||||
get => (AttachmentPoint)Flags;
|
||||
@@ -889,10 +674,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// InventoryWearable Class, details on a clothing item or body part
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryWearable : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -900,23 +682,12 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryWearable(UUID UUID) : base(UUID) { InventoryType = InventoryType.Wearable; }
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryWearable object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryWearable(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Wearable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OpenMetaverse.WearableType"/>, Skin, Shape, Skirt, Etc
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public WearableType WearableType
|
||||
{
|
||||
get => (WearableType)Flags;
|
||||
@@ -928,10 +699,7 @@ namespace OpenMetaverse
|
||||
/// <summary>
|
||||
/// InventoryAnimation Class, A bvh encoded object which animates an avatar
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryAnimation : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -939,33 +707,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryAnimation(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Animation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryAnimation object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryAnimation(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Animation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryGesture Class, details on a series of animations, sounds, and actions
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryGesture : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -973,33 +726,18 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryGesture(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Gesture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryGesture object from a serialization stream
|
||||
/// </summary>
|
||||
public InventoryGesture(SerializationInfo info, StreamingContext ctxt)
|
||||
: base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Gesture;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventorySettings, LLSD settings blob as an asset
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventorySettings : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1007,28 +745,17 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventorySettings(UUID UUID) : base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Settings;
|
||||
}
|
||||
|
||||
public InventorySettings(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Settings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// InventoryMaterial, material as an asset
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryMaterial : InventoryItem
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1036,47 +763,38 @@ namespace OpenMetaverse
|
||||
/// </summary>
|
||||
/// <param name="UUID">A <see cref="UUID"/> which becomes the
|
||||
/// <seealso cref="InventoryItem"/> objects UUID</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryMaterial(UUID UUID) : base(UUID)
|
||||
{
|
||||
InventoryType = InventoryType.Settings;
|
||||
}
|
||||
|
||||
public InventoryMaterial(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt)
|
||||
{
|
||||
InventoryType = InventoryType.Settings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// A folder contains <see cref="T:OpenMetaverse.InventoryItem" />s and has certain attributes specific
|
||||
/// to itself
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
[MessagePackObject]
|
||||
public partial class InventoryFolder : InventoryBase
|
||||
{
|
||||
public const int VERSION_UNKNOWN = -1;
|
||||
|
||||
/// <summary>The Preferred <see cref="T:OpenMetaverse.FolderType"/> for a folder.</summary>
|
||||
[Key("PreferredType")]
|
||||
public FolderType PreferredType;
|
||||
|
||||
/// <summary>The Version of this folder</summary>
|
||||
[Key("Version")]
|
||||
public int Version;
|
||||
|
||||
/// <summary>Number of child items this folder contains.</summary>
|
||||
[Key("DescendentCount")]
|
||||
public int DescendentCount;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="UUID">UUID of the folder</param>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryFolder(UUID UUID)
|
||||
: base(UUID)
|
||||
{
|
||||
@@ -1094,30 +812,6 @@ namespace OpenMetaverse
|
||||
return Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Serialization data for this InventoryFolder object
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="context"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct an InventoryFolder object from a serialization stream
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return int hash code
|
||||
/// </summary>
|
||||
|
||||
214
LibreMetaverse/InventoryCache.cs
Normal file
214
LibreMetaverse/InventoryCache.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using MessagePack.Resolvers;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
internal class InventoryCache
|
||||
{
|
||||
private static readonly string InventoryCacheMagic = "INVCACHE";
|
||||
private static readonly int InventoryCacheVersion = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Creates MessagePack serializer options for use in inventory cache serializing and deserializing
|
||||
/// </summary>
|
||||
/// <returns>MessagePack serializer options</returns>
|
||||
private static MessagePackSerializerOptions GetSerializerOptions()
|
||||
{
|
||||
var resolver = MessagePack.Resolvers.CompositeResolver.Create(
|
||||
new List<IMessagePackFormatter>()
|
||||
{
|
||||
Formatters.UUIDFormatter.Instance,
|
||||
},
|
||||
new List<IFormatterResolver>
|
||||
{
|
||||
StandardResolver.Instance,
|
||||
}
|
||||
);
|
||||
|
||||
return MessagePackSerializerOptions
|
||||
.Standard
|
||||
.WithResolver(resolver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current inventory structure to a cache file
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the cache file to save to</param>
|
||||
public static void SaveToDisk(string filename, ConcurrentDictionary<UUID, InventoryNode> Items)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var bw = new BinaryWriter(File.Open(filename, FileMode.Create)))
|
||||
{
|
||||
lock (Items)
|
||||
{
|
||||
Logger.Log($"Caching {Items.Count} inventory items to {filename}", Helpers.LogLevel.Info);
|
||||
|
||||
var options = GetSerializerOptions();
|
||||
var items = Items.Values.ToList();
|
||||
|
||||
bw.Write(Encoding.ASCII.GetBytes(InventoryCacheMagic));
|
||||
bw.Write(InventoryCacheVersion);
|
||||
MessagePackSerializer.Serialize(bw.BaseStream, items, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Error saving inventory cache to disk", Helpers.LogLevel.Error, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads in inventory cache file into the inventory structure. Note only valid to call after login has been successful.
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the cache file to load</param>
|
||||
/// <returns>The number of inventory items successfully reconstructed into the inventory node tree, or -1 on error</returns>
|
||||
public static int RestoreFromDisk(string filename, ConcurrentDictionary<UUID, InventoryNode> Items)
|
||||
{
|
||||
List<InventoryNode> cacheNodes;
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
using (var br = new BinaryReader(File.Open(filename, FileMode.Open)))
|
||||
{
|
||||
var options = GetSerializerOptions();
|
||||
|
||||
if (br.BaseStream.Length < InventoryCacheMagic.Length + sizeof(int))
|
||||
{
|
||||
Logger.Log($"Invalid inventory cache file. Missing header.", Helpers.LogLevel.Warning);
|
||||
return -1;
|
||||
}
|
||||
|
||||
var magic = br.ReadBytes(InventoryCacheMagic.Length);
|
||||
if (Encoding.ASCII.GetString(magic) != InventoryCacheMagic)
|
||||
{
|
||||
Logger.Log($"Invalid inventory cache file. Missing magic header.", Helpers.LogLevel.Warning);
|
||||
return -1;
|
||||
}
|
||||
|
||||
var version = br.ReadInt32();
|
||||
if (version != InventoryCacheVersion)
|
||||
{
|
||||
Logger.Log($"Invalid inventory cache file. Expected version {InventoryCacheVersion}, got {version}.", Helpers.LogLevel.Warning);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cacheNodes = MessagePackSerializer.Deserialize<List<InventoryNode>>(br.BaseStream, options);
|
||||
if (cacheNodes == null)
|
||||
{
|
||||
Logger.Log($"Invalid inventory cache file. Failed to deserialize contents.", Helpers.LogLevel.Warning);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log("Error accessing inventory cache file", Helpers.LogLevel.Error, e);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Logger.Log($"Read {cacheNodes.Count} items from inventory cache file", Helpers.LogLevel.Info);
|
||||
|
||||
var dirtyFolders = new HashSet<UUID>();
|
||||
|
||||
// First pass: process InventoryFolders
|
||||
foreach (var cacheNode in cacheNodes)
|
||||
{
|
||||
if (!(cacheNode.Data is InventoryFolder cacheFolder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cacheNode.Data.ParentUUID == UUID.Zero)
|
||||
{
|
||||
//We don't need the root nodes "My Inventory" etc as they will already exist for the correct
|
||||
// user of this cache.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryGetValue(cacheNode.Data.UUID, out var serverNode))
|
||||
{
|
||||
// This is an orphaned folder that no longer exists on the server.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serverNode.Data is InventoryFolder serverFolder))
|
||||
{
|
||||
Logger.Log($"Cached inventory node folder has a parent that is not an InventoryFolder", Helpers.LogLevel.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
serverNode.NeedsUpdate = cacheFolder.Version != serverFolder.Version;
|
||||
|
||||
if (serverNode.NeedsUpdate)
|
||||
{
|
||||
Logger.DebugLog($"Inventory Cache/Server version mismatch on {cacheNode.Data.Name} {cacheFolder.Version} vs {serverFolder.Version}");
|
||||
dirtyFolders.Add(cacheNode.Data.UUID);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: process InventoryItems
|
||||
var itemCount = 0;
|
||||
foreach (var cacheNode in cacheNodes)
|
||||
{
|
||||
if (!(cacheNode.Data is InventoryItem cacheItem))
|
||||
{
|
||||
// Only process InventoryItems
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryGetValue(cacheNode.Data.ParentUUID, out var serverParentNode))
|
||||
{
|
||||
// This item does not have a parent in our known inventory. The folder was probably deleted on the server
|
||||
// and our cache is old
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(serverParentNode.Data is InventoryFolder serverParentFolder))
|
||||
{
|
||||
Logger.Log($"Cached inventory node item {cacheItem.Name} has a parent {serverParentNode.Data.Name} that is not an InventoryFolder", Helpers.LogLevel.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirtyFolders.Contains(serverParentFolder.UUID))
|
||||
{
|
||||
// This item belongs to a folder that has been marked as dirty, so it too is dirty and must be skipped
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Items.ContainsKey(cacheItem.UUID))
|
||||
{
|
||||
// This item was already added to our Items store, likely added from previous server requests during this session
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Items.TryAdd(cacheItem.UUID, cacheNode))
|
||||
{
|
||||
Logger.Log($"Failed to add cache item node {cacheItem.Name} with parent {serverParentFolder.Name}", Helpers.LogLevel.Info);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add this cached InventoryItem node to the parent
|
||||
cacheNode.Parent = serverParentNode;
|
||||
serverParentNode.Nodes.Add(cacheItem.UUID, cacheNode);
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
Logger.Log($"Reassembled {itemCount} items from inventory cache file", Helpers.LogLevel.Info);
|
||||
return itemCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,30 +26,21 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
#if NET7_0_OR_GREATER
|
||||
using MemoryPack;
|
||||
#endif
|
||||
using MessagePack;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
[Serializable]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
public partial class InventoryNode : ISerializable
|
||||
[MessagePackObject]
|
||||
public partial class InventoryNode
|
||||
{
|
||||
private InventoryBase data;
|
||||
private InventoryNode parent;
|
||||
private UUID parentID; //used for deseralization
|
||||
private InventoryNodeDictionary nodes;
|
||||
private bool needsUpdate = true;
|
||||
[NonSerialized]
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackIgnore]
|
||||
#endif
|
||||
|
||||
private object tag;
|
||||
|
||||
[Key("Data")]
|
||||
public InventoryBase Data
|
||||
{
|
||||
get => data;
|
||||
@@ -57,27 +48,21 @@ namespace OpenMetaverse
|
||||
}
|
||||
|
||||
/// <summary>User data</summary>
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackIgnore]
|
||||
#endif
|
||||
[IgnoreMember]
|
||||
public object Tag
|
||||
{
|
||||
get => tag;
|
||||
set => tag = value;
|
||||
}
|
||||
|
||||
[IgnoreMember]
|
||||
public InventoryNode Parent
|
||||
{
|
||||
get => parent;
|
||||
set => parent = value;
|
||||
}
|
||||
|
||||
public UUID ParentID
|
||||
{
|
||||
get => parentID;
|
||||
private set => parentID = value;
|
||||
}
|
||||
|
||||
[IgnoreMember]
|
||||
public InventoryNodeDictionary Nodes
|
||||
{
|
||||
get => nodes ?? (nodes = new InventoryNodeDictionary(this));
|
||||
@@ -88,12 +73,14 @@ namespace OpenMetaverse
|
||||
/// For inventory folder nodes specifies weather the folder needs to be
|
||||
/// refreshed from the server
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public bool NeedsUpdate
|
||||
{
|
||||
get => needsUpdate;
|
||||
set => needsUpdate = value;
|
||||
}
|
||||
|
||||
[IgnoreMember]
|
||||
public DateTime ModifyTime
|
||||
{
|
||||
get
|
||||
@@ -120,9 +107,6 @@ namespace OpenMetaverse
|
||||
Nodes.Sort();
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackConstructor]
|
||||
#endif
|
||||
public InventoryNode()
|
||||
{
|
||||
}
|
||||
@@ -148,29 +132,6 @@ namespace OpenMetaverse
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialization handler for the InventoryNode Class
|
||||
/// </summary>
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
|
||||
{
|
||||
info.AddValue("Parent", parent?.Data.UUID ?? UUID.Zero, typeof(UUID));
|
||||
info.AddValue("Type", data.GetType(), typeof(Type));
|
||||
data.GetObjectData(info, ctxt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// De-serialization handler for the InventoryNode Class
|
||||
/// </summary>
|
||||
public InventoryNode(SerializationInfo info, StreamingContext ctxt)
|
||||
{
|
||||
parentID = (UUID)info.GetValue("Parent", typeof(UUID));
|
||||
Type type = (Type)info.GetValue("Type", typeof(Type));
|
||||
|
||||
// Construct a new inventory object based on the Type stored in Type
|
||||
System.Reflection.ConstructorInfo ctr = type.GetConstructor(new[] {typeof(SerializationInfo),typeof(StreamingContext)});
|
||||
if (ctr != null) data = (InventoryBase)ctr.Invoke(new object[] { info, ctxt });
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Data == null ? "[Empty Node]" : this.Data.ToString();
|
||||
|
||||
@@ -27,23 +27,15 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if NET7_0_OR_GREATER
|
||||
using MemoryPack;
|
||||
#endif
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackable]
|
||||
#endif
|
||||
public partial class InventoryNodeDictionary: IComparer<UUID>
|
||||
{
|
||||
protected readonly SortedDictionary<UUID, InventoryNode> SDictionary;
|
||||
protected readonly Dictionary<UUID, InventoryNode> Dictionary = new Dictionary<UUID, InventoryNode>();
|
||||
protected InventoryNode parent;
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackIgnore]
|
||||
#endif
|
||||
|
||||
protected readonly object syncRoot = new object();
|
||||
public int Compare(UUID x, UUID y)
|
||||
{
|
||||
@@ -91,9 +83,6 @@ namespace OpenMetaverse
|
||||
set => parent = value;
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[MemoryPackIgnore]
|
||||
#endif
|
||||
public object SyncRoot => syncRoot;
|
||||
|
||||
public int Count => Dictionary.Count;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CoreJ2K.Skia" Version="1.1.1.48" />
|
||||
<PackageReference Include="log4net" Version="3.0.4" />
|
||||
<PackageReference Include="MemoryPack" Version="1.21.4" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.4" />
|
||||
<PackageReference Include="OggVorbisEncoder" Version="1.2.2" />
|
||||
<PackageReference Include="Pfim" Version="0.11.3" />
|
||||
|
||||
@@ -29,7 +29,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
@@ -90,13 +89,6 @@ namespace OpenMetaverse
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.")]
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
lock (Dictionary)
|
||||
Dictionary.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
public void OnDeserialization(object sender)
|
||||
{
|
||||
lock( Dictionary)
|
||||
|
||||
@@ -1061,7 +1061,6 @@ namespace OpenMetaverse
|
||||
|
||||
#endregion Structs
|
||||
|
||||
[Serializable]
|
||||
public class LoginException : Exception
|
||||
{
|
||||
public LoginException(string message)
|
||||
|
||||
@@ -603,7 +603,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
/// <summary>
|
||||
/// The details of a single parcel in a region, also contains some regionwide globals
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ParcelPropertiesMessage : IMessage
|
||||
{
|
||||
/// <summary>Simulator-local ID of this parcel</summary>
|
||||
@@ -1086,7 +1085,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
}
|
||||
|
||||
/// <summary>Base class used for the RemoteParcelRequest message</summary>
|
||||
[Serializable]
|
||||
public abstract class RemoteParcelRequestBlock
|
||||
{
|
||||
public abstract OSDMap Serialize();
|
||||
@@ -1137,7 +1135,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
/// A message sent from the simulator to the viewer in response to a <see cref="RemoteParcelRequestRequest"/>
|
||||
/// which will contain parcel information
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RemoteParcelRequestReply : RemoteParcelRequestBlock
|
||||
{
|
||||
/// <summary>The grid-wide unique parcel ID</summary>
|
||||
@@ -1170,7 +1167,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
/// A message containing a request for a remote parcel from a viewer, or a response
|
||||
/// from the simulator to that request
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RemoteParcelRequestMessage : IMessage
|
||||
{
|
||||
/// <summary>The request or response details block</summary>
|
||||
@@ -4102,13 +4098,11 @@ namespace OpenMetaverse.Messages.Linden
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DirLandReplyMessage : IMessage
|
||||
{
|
||||
public UUID AgentID;
|
||||
public UUID QueryID;
|
||||
|
||||
[Serializable]
|
||||
public class QueryReply
|
||||
{
|
||||
public int ActualArea;
|
||||
@@ -4739,7 +4733,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
|
||||
|
||||
/// <summary>Base class used for the ObjectMedia message</summary>
|
||||
[Serializable]
|
||||
public abstract class ObjectMediaBlock
|
||||
{
|
||||
public abstract OSDMap Serialize();
|
||||
@@ -4941,7 +4934,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
/// <summary>
|
||||
/// Message for setting or getting per face MediaEntry
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ObjectMediaMessage : IMessage
|
||||
{
|
||||
/// <summary>The request or response details block</summary>
|
||||
@@ -5489,7 +5481,6 @@ namespace OpenMetaverse.Messages.Linden
|
||||
/// <summary>
|
||||
/// Message received when requesting AgentProfile for an avatar
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AgentProfileMessage : IMessage
|
||||
{
|
||||
public class GroupData
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using MessagePack;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
@@ -61,13 +62,18 @@ namespace OpenMetaverse
|
||||
All = 0x1F
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[MessagePackObject]
|
||||
public struct Permissions
|
||||
{
|
||||
[Key("BaseMask")]
|
||||
public PermissionMask BaseMask;
|
||||
[Key("EveryoneMask")]
|
||||
public PermissionMask EveryoneMask;
|
||||
[Key("GroupMask")]
|
||||
public PermissionMask GroupMask;
|
||||
[Key("NextOwnerMask")]
|
||||
public PermissionMask NextOwnerMask;
|
||||
[Key("OwnerMask")]
|
||||
public PermissionMask OwnerMask;
|
||||
|
||||
public Permissions(uint baseMask, uint everyoneMask, uint groupMask, uint nextOwnerMask, uint ownerMask)
|
||||
|
||||
@@ -194,7 +194,6 @@ namespace OpenMetaverse.Rendering
|
||||
|
||||
#region Exceptions
|
||||
|
||||
[Serializable]
|
||||
public class RenderingException : Exception
|
||||
{
|
||||
public RenderingException(string message)
|
||||
|
||||
@@ -29,7 +29,6 @@ using OpenMetaverse.Packets;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
[Serializable]
|
||||
public class TerrainPatch
|
||||
{
|
||||
#region Enums and Structs
|
||||
|
||||
@@ -49,7 +49,6 @@ namespace OpenMetaverse.Packets
|
||||
/// <summary>
|
||||
/// Thrown when a packet could not be successfully deserialized
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class MalformedDataException : ApplicationException
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user