diff --git a/OpenMetaverse/AssetTypes.cs b/OpenMetaverse/AssetTypes.cs index d41c8a0d..1d24f9bd 100644 --- a/OpenMetaverse/AssetTypes.cs +++ b/OpenMetaverse/AssetTypes.cs @@ -99,6 +99,7 @@ namespace OpenMetaverse public abstract class Asset { public byte[] AssetData; + public bool Temporary; private UUID _AssetID; public UUID AssetID @@ -251,6 +252,7 @@ namespace OpenMetaverse public ManagedImage Image; public OpenJPEG.J2KLayerInfo[] LayerInfo; + public int Components; public AssetTexture() { } @@ -259,6 +261,15 @@ namespace OpenMetaverse public AssetTexture(ManagedImage image) { Image = image; + Components = 0; + if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0) + Components += 3; + if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0) + ++Components; } /// @@ -277,7 +288,25 @@ namespace OpenMetaverse /// True if the decoding was successful, otherwise false public override bool Decode() { - return OpenJPEG.DecodeToImage(AssetData, out Image); + Components = 0; + + if (OpenJPEG.DecodeToImage(AssetData, out Image)) + { + if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0) + Components += 3; + if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0) + ++Components; + + return true; + } + else + { + return false; + } } /// @@ -287,7 +316,7 @@ namespace OpenMetaverse /// public bool DecodeLayerBoundaries() { - return OpenJPEG.DecodeLayerBoundaries(AssetData, out LayerInfo); + return OpenJPEG.DecodeLayerBoundaries(AssetData, out LayerInfo, out Components); } } diff --git a/OpenMetaverse/Imaging/OpenJPEG.cs b/OpenMetaverse/Imaging/OpenJPEG.cs index f70592b7..41c183df 100644 --- a/OpenMetaverse/Imaging/OpenJPEG.cs +++ b/OpenMetaverse/Imaging/OpenJPEG.cs @@ -306,11 +306,13 @@ namespace OpenMetaverse.Imaging /// /// /// + /// /// - public static bool DecodeLayerBoundaries(byte[] encoded, out J2KLayerInfo[] layerInfo) + public static bool DecodeLayerBoundaries(byte[] encoded, out J2KLayerInfo[] layerInfo, out int components) { bool success = false; layerInfo = null; + components = 0; MarshalledImage marshalled = new MarshalledImage(); // Allocate and copy to input buffer @@ -321,6 +323,8 @@ namespace OpenMetaverse.Imaging // Run the decode if (DotNetDecodeWithInfo(ref marshalled)) { + components = marshalled.components; + // Sanity check if (marshalled.layers * marshalled.resolutions * marshalled.components == marshalled.packet_count) { diff --git a/Programs/Simian/Agent.cs b/Programs/Simian/Agent.cs index 6734b37d..dc58b292 100644 --- a/Programs/Simian/Agent.cs +++ b/Programs/Simian/Agent.cs @@ -50,31 +50,18 @@ namespace Simian //public byte[] Texture; public float Height; public UUID ShapeItem; - public UUID ShapeAsset; public UUID SkinItem; - public UUID SkinAsset; public UUID HairItem; - public UUID HairAsset; public UUID EyesItem; - public UUID EyesAsset; public UUID ShirtItem; - public UUID ShirtAsset; public UUID PantsItem; - public UUID PantsAsset; public UUID ShoesItem; - public UUID ShoesAsset; public UUID SocksItem; - public UUID SocksAsset; public UUID JacketItem; - public UUID JacketAsset; public UUID GlovesItem; - public UUID GlovesAsset; public UUID UndershirtItem; - public UUID UndershirtAsset; public UUID UnderpantsItem; - public UUID UnderpantsAsset; public UUID SkirtItem; - public UUID SkirtAsset; // Temporary [NonSerialized] diff --git a/Programs/Simian/Extensions/AssetManager.cs b/Programs/Simian/Extensions/AssetManager.cs index a37ac230..5fdeb25e 100644 --- a/Programs/Simian/Extensions/AssetManager.cs +++ b/Programs/Simian/Extensions/AssetManager.cs @@ -10,9 +10,12 @@ namespace Simian.Extensions { public class AssetManager : IExtension, IAssetProvider { + public const string UPLOAD_DIR = "uploadedAssets"; + Simian Server; Dictionary AssetStore = new Dictionary(); Dictionary CurrentUploads = new Dictionary(); + string UploadDir; public AssetManager(Simian server) { @@ -21,7 +24,22 @@ namespace Simian.Extensions public void Start() { - LoadDefaultAssets(Server.DataDir); + UploadDir = Path.Combine(Server.DataDir, UPLOAD_DIR); + + // Try to create the data directories if they don't already exist + if (!Directory.Exists(Server.DataDir)) + { + try { Directory.CreateDirectory(Server.DataDir); } + catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); } + } + if (!Directory.Exists(UploadDir)) + { + try { Directory.CreateDirectory(UploadDir); } + catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); } + } + + LoadAssets(Server.DataDir); + LoadAssets(UploadDir); Server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler)); Server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler)); @@ -43,6 +61,8 @@ namespace Simian.Extensions { lock (AssetStore) AssetStore[asset.AssetID] = texture; + if (!asset.Temporary) + SaveAsset(texture); } else { @@ -56,6 +76,8 @@ namespace Simian.Extensions { lock (AssetStore) AssetStore[asset.AssetID] = asset; + if (!asset.Temporary) + SaveAsset(asset); } else { @@ -77,9 +99,10 @@ namespace Simian.Extensions AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet; UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID); + // Check if the asset is small enough to fit in a single packet if (request.AssetBlock.AssetData.Length != 0) { - // Create a new asset from the upload + // Create a new asset from the completed upload Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData); if (asset == null) { @@ -89,6 +112,8 @@ namespace Simian.Extensions Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType)); + asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal); + // Store the asset StoreAsset(asset); @@ -101,15 +126,18 @@ namespace Simian.Extensions } else { - // Create a new asset for the upload + // Create a new (empty) asset for the upload Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, null); if (asset == null) { Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning); + return; } Logger.DebugLog(String.Format("Starting upload for {0} ({1})", assetID, asset.AssetType)); + asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal); + RequestXferPacket xfer = new RequestXferPacket(); xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile; xfer.XferID.FilePath = 0; @@ -168,7 +196,7 @@ namespace Simian.Extensions if ((xfer.XferID.Packet & (uint)0x80000000) != 0) { // Asset upload finished - Logger.DebugLog("Completed asset upload"); + Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType)); lock (CurrentUploads) CurrentUploads.Remove(xfer.XferID.ID); @@ -348,6 +376,19 @@ namespace Simian.Extensions #endregion Transfer System + void SaveAsset(Asset asset) + { + try + { + File.WriteAllBytes(Path.Combine(UploadDir, String.Format("{0}.{1}", asset.AssetID, + asset.AssetType.ToString().ToLower())), asset.AssetData); + } + catch (Exception ex) + { + Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); + } + } + Asset CreateAsset(AssetType type, UUID assetID, byte[] data) { switch (type) @@ -389,38 +430,51 @@ namespace Simian.Extensions } } - void LoadDefaultAssets(string path) + void LoadAssets(string path) { - string[] textures = Directory.GetFiles(path, "*.jp2", SearchOption.TopDirectoryOnly); - string[] clothing = Directory.GetFiles(path, "*.clothing", SearchOption.TopDirectoryOnly); - string[] bodyparts = Directory.GetFiles(path, "*.bodypart", SearchOption.TopDirectoryOnly); - string[] sounds = Directory.GetFiles(path, "*.ogg", SearchOption.TopDirectoryOnly); - - for (int i = 0; i < textures.Length; i++) + try { - UUID assetID = ParseUUIDFromFilename(textures[i]); - StoreAsset(new AssetTexture(assetID, File.ReadAllBytes(textures[i]))); - } + string[] textures = Directory.GetFiles(path, "*.jp2", SearchOption.TopDirectoryOnly); + string[] clothing = Directory.GetFiles(path, "*.clothing", SearchOption.TopDirectoryOnly); + string[] bodyparts = Directory.GetFiles(path, "*.bodypart", SearchOption.TopDirectoryOnly); + string[] sounds = Directory.GetFiles(path, "*.ogg", SearchOption.TopDirectoryOnly); - for (int i = 0; i < clothing.Length; i++) + for (int i = 0; i < textures.Length; i++) + { + UUID assetID = ParseUUIDFromFilename(textures[i]); + Asset asset = new AssetTexture(assetID, File.ReadAllBytes(textures[i])); + asset.Temporary = true; + StoreAsset(asset); + } + + for (int i = 0; i < clothing.Length; i++) + { + UUID assetID = ParseUUIDFromFilename(clothing[i]); + Asset asset = new AssetClothing(assetID, File.ReadAllBytes(clothing[i])); + asset.Temporary = true; + StoreAsset(asset); + } + + for (int i = 0; i < bodyparts.Length; i++) + { + UUID assetID = ParseUUIDFromFilename(bodyparts[i]); + Asset asset = new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i])); + asset.Temporary = true; + StoreAsset(asset); + } + + for (int i = 0; i < sounds.Length; i++) + { + UUID assetID = ParseUUIDFromFilename(sounds[i]); + Asset asset = new AssetSound(assetID, File.ReadAllBytes(sounds[i])); + asset.Temporary = true; + StoreAsset(asset); + } + } + catch (Exception ex) { - UUID assetID = ParseUUIDFromFilename(clothing[i]); - StoreAsset(new AssetClothing(assetID, File.ReadAllBytes(clothing[i]))); + Logger.Log(ex.Message, Helpers.LogLevel.Warning, ex); } - - for (int i = 0; i < bodyparts.Length; i++) - { - UUID assetID = ParseUUIDFromFilename(bodyparts[i]); - StoreAsset(new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i]))); - } - - for (int i = 0; i < sounds.Length; i++) - { - UUID assetID = ParseUUIDFromFilename(sounds[i]); - StoreAsset(new AssetSound(assetID, File.ReadAllBytes(sounds[i]))); - } - - Logger.DebugLog(String.Format("Loaded {0} assets", AssetStore.Count)); } static UUID ParseUUIDFromFilename(string filename) diff --git a/Programs/Simian/Extensions/AuthFreeForAll.cs b/Programs/Simian/Extensions/AuthFreeForAll.cs index 706e4615..6ee414cf 100644 --- a/Programs/Simian/Extensions/AuthFreeForAll.cs +++ b/Programs/Simian/Extensions/AuthFreeForAll.cs @@ -66,39 +66,33 @@ namespace Simian.Extensions UUID hairAsset = new UUID("dc675529-7ba5-4976-b91d-dcb9e5e36188"); UUID hairItem = server.Inventory.CreateItem(agent.AgentID, "Default Hair", "Default Hair", InventoryType.Wearable, AssetType.Bodypart, hairAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); UUID pantsAsset = new UUID("3e8ee2d6-4f21-4a55-832d-77daa505edff"); UUID pantsItem = server.Inventory.CreateItem(agent.AgentID, "Default Pants", "Default Pants", InventoryType.Wearable, AssetType.Clothing, pantsAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); UUID shapeAsset = new UUID("530a2614-052e-49a2-af0e-534bb3c05af0"); UUID shapeItem = server.Inventory.CreateItem(agent.AgentID, "Default Shape", "Default Shape", InventoryType.Wearable, AssetType.Clothing, shapeAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); UUID shirtAsset = new UUID("6a714f37-fe53-4230-b46f-8db384465981"); UUID shirtItem = server.Inventory.CreateItem(agent.AgentID, "Default Shirt", "Default Shirt", InventoryType.Wearable, AssetType.Clothing, shirtAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); UUID skinAsset = new UUID("5f787f25-f761-4a35-9764-6418ee4774c4"); UUID skinItem = server.Inventory.CreateItem(agent.AgentID, "Default Skin", "Default Skin", InventoryType.Wearable, AssetType.Clothing, skinAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); UUID eyesAsset = new UUID("78d20332-9b07-44a2-bf74-3b368605f4b5"); UUID eyesItem = server.Inventory.CreateItem(agent.AgentID, "Default Eyes", "Default Eyes", InventoryType.Wearable, AssetType.Bodypart, eyesAsset, defaultOutfitFolder, - PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0); + PermissionMask.All, PermissionMask.All, agent.AgentID, agent.AgentID, UUID.Random(), 0, false); - agent.HairAsset = hairAsset; agent.HairItem = hairItem; - agent.PantsAsset = pantsAsset; agent.PantsItem = pantsItem; - agent.ShapeAsset = shapeAsset; agent.ShapeItem = shapeItem; - agent.ShirtAsset = shirtAsset; agent.ShirtItem = shirtItem; - agent.SkinAsset = skinAsset; agent.SkinItem = skinItem; - agent.EyesAsset = eyesAsset; agent.EyesItem = eyesItem; server.Accounts.AddAccount(agent); diff --git a/Programs/Simian/Extensions/AvatarManager.cs b/Programs/Simian/Extensions/AvatarManager.cs index 9a5be626..5be11bb3 100644 --- a/Programs/Simian/Extensions/AvatarManager.cs +++ b/Programs/Simian/Extensions/AvatarManager.cs @@ -152,23 +152,38 @@ namespace Simian.Extensions } } - int CountWearables(Agent agent) + bool TryAddWearable(UUID agentID, Dictionary wearables, WearableType type, UUID itemID) { - int wearables = 0; + InventoryObject obj; + if (itemID != UUID.Zero && Server.Inventory.TryGetInventory(agentID, itemID, out obj) && + obj is InventoryItem) + { + wearables.Add(type, (InventoryItem)obj); + return true; + } + else + { + return false; + } + } - if (agent.ShapeAsset != UUID.Zero) ++wearables; - if (agent.SkinAsset != UUID.Zero) ++wearables; - if (agent.HairAsset != UUID.Zero) ++wearables; - if (agent.EyesAsset != UUID.Zero) ++wearables; - if (agent.ShirtAsset != UUID.Zero) ++wearables; - if (agent.PantsAsset != UUID.Zero) ++wearables; - if (agent.ShoesAsset != UUID.Zero) ++wearables; - if (agent.SocksAsset != UUID.Zero) ++wearables; - if (agent.JacketAsset != UUID.Zero) ++wearables; - if (agent.GlovesAsset != UUID.Zero) ++wearables; - if (agent.UndershirtAsset != UUID.Zero) ++wearables; - if (agent.UnderpantsAsset != UUID.Zero) ++wearables; - if (agent.SkirtAsset != UUID.Zero) ++wearables; + Dictionary GetCurrentWearables(Agent agent) + { + Dictionary wearables = new Dictionary(); + + TryAddWearable(agent.AgentID, wearables, WearableType.Shape, agent.ShapeItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Skin, agent.SkinItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Hair, agent.HairItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Eyes, agent.EyesItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Shirt, agent.ShirtItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Pants, agent.PantsItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Shoes, agent.ShoesItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Socks, agent.SocksItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Jacket, agent.JacketItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Gloves, agent.GlovesItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Undershirt, agent.UndershirtItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Underpants, agent.UnderpantsItem); + TryAddWearable(agent.AgentID, wearables, WearableType.Skirt, agent.SkirtItem); return wearables; } @@ -178,126 +193,24 @@ namespace Simian.Extensions AgentWearablesUpdatePacket update = new AgentWearablesUpdatePacket(); update.AgentData.AgentID = agent.AgentID; - // Count the number of WearableData blocks needed - int wearableCount = CountWearables(agent); + Dictionary wearables = GetCurrentWearables(agent); + update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[wearables.Count]; int i = 0; - update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[wearableCount]; - - #region WearableData Blocks - - if (agent.ShapeAsset != UUID.Zero) + foreach (KeyValuePair kvp in wearables) { update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.ShapeAsset; - update.WearableData[i].ItemID = agent.ShapeItem; - update.WearableData[i].WearableType = (byte)WearableType.Shape; + update.WearableData[i].AssetID = kvp.Value.AssetID; + update.WearableData[i].ItemID = kvp.Value.ID; + update.WearableData[i].WearableType = (byte)kvp.Key; ++i; } - if (agent.SkinAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.SkinAsset; - update.WearableData[i].ItemID = agent.SkinItem; - update.WearableData[i].WearableType = (byte)WearableType.Skin; - ++i; - } - if (agent.HairAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.HairAsset; - update.WearableData[i].ItemID = agent.HairItem; - update.WearableData[i].WearableType = (byte)WearableType.Hair; - ++i; - } - if (agent.EyesAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.EyesAsset; - update.WearableData[i].ItemID = agent.EyesItem; - update.WearableData[i].WearableType = (byte)WearableType.Eyes; - ++i; - } - if (agent.ShirtAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.ShirtAsset; - update.WearableData[i].ItemID = agent.ShirtItem; - update.WearableData[i].WearableType = (byte)WearableType.Shirt; - ++i; - } - if (agent.PantsAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.PantsAsset; - update.WearableData[i].ItemID = agent.PantsItem; - update.WearableData[i].WearableType = (byte)WearableType.Pants; - ++i; - } - if (agent.ShoesAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.ShoesAsset; - update.WearableData[i].ItemID = agent.ShoesItem; - update.WearableData[i].WearableType = (byte)WearableType.Shoes; - ++i; - } - if (agent.SocksAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.SocksAsset; - update.WearableData[i].ItemID = agent.SocksItem; - update.WearableData[i].WearableType = (byte)WearableType.Socks; - ++i; - } - if (agent.JacketAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.JacketAsset; - update.WearableData[i].ItemID = agent.JacketItem; - update.WearableData[i].WearableType = (byte)WearableType.Jacket; - ++i; - } - if (agent.GlovesAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.GlovesAsset; - update.WearableData[i].ItemID = agent.GlovesItem; - update.WearableData[i].WearableType = (byte)WearableType.Gloves; - ++i; - } - if (agent.UndershirtAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.UndershirtAsset; - update.WearableData[i].ItemID = agent.UndershirtItem; - update.WearableData[i].WearableType = (byte)WearableType.Undershirt; - ++i; - } - if (agent.UnderpantsAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.UnderpantsAsset; - update.WearableData[i].ItemID = agent.UnderpantsItem; - update.WearableData[i].WearableType = (byte)WearableType.Underpants; - ++i; - } - if (agent.SkirtAsset != UUID.Zero) - { - update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock(); - update.WearableData[i].AssetID = agent.SkirtAsset; - update.WearableData[i].ItemID = agent.SkirtItem; - update.WearableData[i].WearableType = (byte)WearableType.Skirt; - ++i; - } - - #endregion WearableData Blocks // Technically this should be per-agent, but if the only requirement is that it // increments this is easier update.AgentData.SerialNum = (uint)Interlocked.Increment(ref currentWearablesSerialNum); - Logger.DebugLog(String.Format("Sending info about {0} wearables", wearableCount)); + Logger.DebugLog(String.Format("Sending info about {0} wearables", wearables.Count)); Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Asset); } @@ -313,67 +226,49 @@ namespace Simian.Extensions for (int i = 0; i < wearing.WearableData.Length; i++) { - UUID assetID = UUID.Zero; UUID itemID = wearing.WearableData[i].ItemID; - InventoryObject invObj; - if (Server.Inventory.TryGetInventory(agent.AgentID, itemID, out invObj) && invObj is InventoryItem) - assetID = ((InventoryItem)invObj).AssetID; - #region Update Wearables switch ((WearableType)wearing.WearableData[i].WearableType) { case WearableType.Shape: - agent.ShapeAsset = assetID; agent.ShapeItem = itemID; break; case WearableType.Skin: - agent.SkinAsset = assetID; agent.SkinItem = itemID; break; case WearableType.Hair: - agent.HairAsset = assetID; agent.HairItem = itemID; break; case WearableType.Eyes: - agent.EyesAsset = assetID; agent.EyesItem = itemID; break; case WearableType.Shirt: - agent.ShirtAsset = assetID; agent.ShirtItem = itemID; break; case WearableType.Pants: - agent.PantsAsset = assetID; agent.PantsItem = itemID; break; case WearableType.Shoes: - agent.ShoesAsset = assetID; agent.ShoesItem = itemID; break; case WearableType.Socks: - agent.SocksAsset = assetID; agent.SocksItem = itemID; break; case WearableType.Jacket: - agent.JacketAsset = assetID; agent.JacketItem = itemID; break; case WearableType.Gloves: - agent.GlovesAsset = assetID; agent.GlovesItem = itemID; break; case WearableType.Undershirt: - agent.UndershirtAsset = assetID; agent.UndershirtItem = itemID; break; case WearableType.Underpants: - agent.UnderpantsAsset = assetID; agent.UnderpantsItem = itemID; break; case WearableType.Skirt: - agent.SkirtAsset = assetID; agent.SkirtItem = itemID; break; } @@ -381,7 +276,8 @@ namespace Simian.Extensions #endregion Update Wearables } - Logger.DebugLog("Updated agent wearables, new count: " + CountWearables(agent)); + // FIXME: GetCurrentWearables() is a very expensive call, remove it from this debug line + Logger.DebugLog("Updated agent wearables, new count: " + GetCurrentWearables(agent).Count); } void AgentSetAppearanceHandler(Packet packet, Agent agent) diff --git a/Programs/Simian/Extensions/InventoryManager.cs b/Programs/Simian/Extensions/InventoryManager.cs index 1bf459b7..892078de 100644 --- a/Programs/Simian/Extensions/InventoryManager.cs +++ b/Programs/Simian/Extensions/InventoryManager.cs @@ -81,7 +81,7 @@ namespace Simian.Extensions CreateItem(agent.AgentID, Utils.BytesToString(create.InventoryBlock.Name), "Created in Simian", (InventoryType)create.InventoryBlock.InvType, assetType, assetID, parentID, PermissionMask.All, (PermissionMask)create.InventoryBlock.NextOwnerMask, agent.AgentID, - agent.AgentID, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID); + agent.AgentID, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID, true); } void CreateInventoryFolderHandler(Packet packet, Agent agent) @@ -137,6 +137,7 @@ namespace Simian.Extensions item.SalePrice = block.SalePrice; item.SaleType = (SaleType)block.SaleType; item.AssetType = (AssetType)block.Type; + item.AssetID = UUID.Combine(block.TransactionID, agent.SecureSessionID); Logger.DebugLog(String.Format( "UpdateInventoryItem: CallbackID: {0}, TransactionID: {1}", @@ -228,14 +229,7 @@ namespace Simian.Extensions itemBlocks[i] = new InventoryDescendentsPacket.ItemDataBlock(); itemBlocks[i].AssetID = currentItem.AssetID; itemBlocks[i].BaseMask = (uint)currentItem.Permissions.BaseMask; - itemBlocks[i].CRC = Helpers.InventoryCRC( - (int)Utils.DateTimeToUnixTime(currentItem.CreationDate), - (byte)currentItem.SaleType, (sbyte)currentItem.InventoryType, - (sbyte)currentItem.AssetType, currentItem.AssetID, currentItem.GroupID, - currentItem.SalePrice, currentItem.OwnerID, currentItem.CreatorID, currentItem.ID, - currentItem.ParentID, (uint)currentItem.Permissions.EveryoneMask, currentItem.Flags, - (uint)currentItem.Permissions.NextOwnerMask, (uint)currentItem.Permissions.GroupMask, - (uint)currentItem.Permissions.OwnerMask); + itemBlocks[i].CRC = currentItem.CRC; itemBlocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(currentItem.CreationDate); itemBlocks[i].CreatorID = currentItem.CreatorID; itemBlocks[i].Description = Utils.StringToBytes(currentItem.Description); @@ -366,11 +360,7 @@ namespace Simian.Extensions blocks[i].AssetID = item.AssetID; blocks[i].BaseMask = (uint)item.Permissions.BaseMask; - blocks[i].CRC = Helpers.InventoryCRC((int)Utils.DateTimeToUnixTime(item.CreationDate), - (byte)item.SaleType, (sbyte)item.InventoryType, (sbyte)item.AssetType, item.AssetID, item.GroupID, - item.SalePrice, item.OwnerID, item.CreatorID, item.ID, item.ParentID, - (uint)item.Permissions.EveryoneMask, item.Flags, (uint)item.Permissions.NextOwnerMask, - (uint)item.Permissions.GroupMask, (uint)item.Permissions.OwnerMask); + blocks[i].CRC = item.CRC; blocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); blocks[i].CreatorID = item.CreatorID; blocks[i].Description = Utils.StringToBytes(item.Description); @@ -447,7 +437,7 @@ namespace Simian.Extensions // Create the copy CreateItem(agent.AgentID, newName, item.Description, item.InventoryType, item.AssetType, item.AssetID, folderObj.ID, item.Permissions.OwnerMask, item.Permissions.NextOwnerMask, - agent.AgentID, item.CreatorID, UUID.Zero, block.CallbackID); + agent.AgentID, item.CreatorID, UUID.Zero, block.CallbackID, true); } else { @@ -477,7 +467,8 @@ namespace Simian.Extensions UUID newFolderID = block.FolderID; if (newFolderID == UUID.Zero) newFolderID = agent.InventoryRoot; - MoveInventory(agentInventory, block.ItemID, newFolderID, Utils.BytesToString(block.NewName)); + MoveInventory(agent, agentInventory, block.ItemID, newFolderID, Utils.BytesToString(block.NewName), + UUID.Zero, 0); } } @@ -494,11 +485,64 @@ namespace Simian.Extensions UUID newFolderID = block.ParentID; if (newFolderID == UUID.Zero) newFolderID = agent.InventoryRoot; - MoveInventory(agentInventory, block.FolderID, newFolderID, null); + MoveInventory(agent, agentInventory, block.FolderID, newFolderID, null, UUID.Zero, 0); } } - void MoveInventory(Dictionary agentInventory, UUID objectID, UUID newFolderID, string newName) + void SendBulkUpdate(Agent agent, InventoryObject obj, UUID transactionID, uint callbackID) + { + BulkUpdateInventoryPacket update = new BulkUpdateInventoryPacket(); + update.AgentData.AgentID = agent.AgentID; + update.AgentData.TransactionID = transactionID; + + if (obj is InventoryItem) + { + InventoryItem item = (InventoryItem)obj; + + update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[0]; + update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[1]; + update.ItemData[0] = new BulkUpdateInventoryPacket.ItemDataBlock(); + update.ItemData[0].AssetID = item.AssetID; + update.ItemData[0].BaseMask = (uint)item.Permissions.BaseMask; + update.ItemData[0].CallbackID = callbackID; + update.ItemData[0].CRC = item.CRC; + update.ItemData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); + update.ItemData[0].CreatorID = item.CreatorID; + update.ItemData[0].Description = Utils.StringToBytes(item.Description); + update.ItemData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask; + update.ItemData[0].Flags = item.Flags; + update.ItemData[0].FolderID = item.ParentID; + update.ItemData[0].GroupID = item.GroupID; + update.ItemData[0].GroupMask = (uint)item.Permissions.GroupMask; + update.ItemData[0].GroupOwned = item.GroupOwned; + update.ItemData[0].InvType = (sbyte)item.InventoryType; + update.ItemData[0].ItemID = item.ID; + update.ItemData[0].Name = Utils.StringToBytes(item.Name); + update.ItemData[0].NextOwnerMask = (uint)item.Permissions.NextOwnerMask; + update.ItemData[0].OwnerID = item.OwnerID; + update.ItemData[0].OwnerMask = (uint)item.Permissions.OwnerMask; + update.ItemData[0].SalePrice = item.SalePrice; + update.ItemData[0].SaleType = (byte)item.SaleType; + update.ItemData[0].Type = (sbyte)item.InventoryType; + } + else + { + InventoryFolder folder = (InventoryFolder)obj; + + update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[0]; + update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[1]; + update.FolderData[0] = new BulkUpdateInventoryPacket.FolderDataBlock(); + update.FolderData[0].FolderID = folder.ID; + update.FolderData[0].Name = Utils.StringToBytes(folder.Name); + update.FolderData[0].ParentID = folder.ParentID; + update.FolderData[0].Type = (sbyte)folder.PreferredType; + } + + Server.UDP.SendPacket(agent.AgentID, update, PacketCategory.Inventory); + } + + void MoveInventory(Agent agent, Dictionary agentInventory, UUID objectID, + UUID newFolderID, string newName, UUID transactionID, uint callbackID) { InventoryObject obj; if (agentInventory.TryGetValue(objectID, out obj)) @@ -531,6 +575,8 @@ namespace Simian.Extensions Logger.Log("MoveInventory called with an unknown destination folder " + newFolderID, Helpers.LogLevel.Warning); } + + SendBulkUpdate(agent, obj, transactionID, callbackID); } } else @@ -662,7 +708,7 @@ namespace Simian.Extensions public UUID CreateItem(UUID agentID, string name, string description, InventoryType invType, AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, UUID ownerID, - UUID creatorID, UUID transactionID, uint callbackID) + UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket) { Dictionary agentInventory = GetAgentInventory(agentID); @@ -716,12 +762,7 @@ namespace Simian.Extensions update.InventoryData[0].BaseMask = (uint)PermissionMask.All; update.InventoryData[0].CallbackID = callbackID; update.InventoryData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); - update.InventoryData[0].CRC = - Helpers.InventoryCRC((int)Utils.DateTimeToUnixTime(item.CreationDate), (byte)item.SaleType, - (sbyte)item.InventoryType, (sbyte)item.AssetType, item.AssetID, item.GroupID, item.SalePrice, - item.OwnerID, item.CreatorID, item.ID, item.ParentID, (uint)item.Permissions.EveryoneMask, - item.Flags, (uint)item.Permissions.NextOwnerMask, (uint)item.Permissions.GroupMask, - (uint)item.Permissions.OwnerMask); + update.InventoryData[0].CRC = item.CRC; update.InventoryData[0].CreatorID = item.CreatorID; update.InventoryData[0].Description = Utils.StringToBytes(item.Description); update.InventoryData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask; @@ -740,7 +781,9 @@ namespace Simian.Extensions update.InventoryData[0].SaleType = (byte)item.SaleType; update.InventoryData[0].Type = (sbyte)item.AssetType; - Server.UDP.SendPacket(agentID, update, PacketCategory.Inventory); + if (sendPacket) + Server.UDP.SendPacket(agentID, update, PacketCategory.Inventory); + return item.ID; } else diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs index 2a64a0ed..a3073589 100644 --- a/Programs/Simian/Extensions/ObjectManager.cs +++ b/Programs/Simian/Extensions/ObjectManager.cs @@ -565,7 +565,7 @@ namespace Simian.Extensions InventoryFolder trash = (InventoryFolder)invObj; Server.Inventory.CreateItem(agent.AgentID, obj.Prim.Properties.Name, obj.Prim.Properties.Description, InventoryType.Object, AssetType.Object, obj.Prim.ID, trash.ID, PermissionMask.All, PermissionMask.All, agent.AgentID, - obj.Prim.Properties.CreatorID, derez.AgentBlock.TransactionID, 0); + obj.Prim.Properties.CreatorID, derez.AgentBlock.TransactionID, 0, true); Server.Scene.ObjectRemove(this, obj); Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID)); diff --git a/Programs/Simian/Interfaces/IInventoryProvider.cs b/Programs/Simian/Interfaces/IInventoryProvider.cs index 7d7b7177..209965c0 100644 --- a/Programs/Simian/Interfaces/IInventoryProvider.cs +++ b/Programs/Simian/Interfaces/IInventoryProvider.cs @@ -7,7 +7,7 @@ namespace Simian { UUID CreateItem(UUID agentID, string name, string description, InventoryType invType, AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, UUID ownerID, - UUID creatorID, UUID transactionID, uint callbackID); + UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket); bool CreateFolder(UUID agentID, UUID folderID, string name, AssetType preferredType, UUID parentID, UUID ownerID); bool CreateRootFolder(UUID agentID, UUID folderID, string name, UUID ownerID); diff --git a/Programs/Simian/InventoryDefinitions.cs b/Programs/Simian/InventoryDefinitions.cs index edcb7893..6f6fa5bd 100644 --- a/Programs/Simian/InventoryDefinitions.cs +++ b/Programs/Simian/InventoryDefinitions.cs @@ -74,6 +74,20 @@ namespace Simian /// UTC (Coordinated Universal Time) public DateTime CreationDate; + /// Cyclic redundancy check for this inventory item, calculated by adding most of + /// the fields together + public uint CRC + { + get + { + return Helpers.InventoryCRC((int)Utils.DateTimeToUnixTime(CreationDate), (byte)SaleType, + (sbyte)InventoryType, (sbyte)AssetType, AssetID, GroupID, SalePrice, + OwnerID, CreatorID, ID, ParentID, (uint)Permissions.EveryoneMask, + Flags, (uint)Permissions.NextOwnerMask, (uint)Permissions.GroupMask, + (uint)Permissions.OwnerMask); + } + } + public override int GetHashCode() { return ID.GetHashCode();