From c20afbbf800a796f7db4abe8c9569d585cc43e37 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 19 Mar 2009 00:25:03 +0000 Subject: [PATCH] * Added InventoryItemFlags, which is actually only the upper half of the Flags field for inventory items. Stores slam bits, permission override flags, and other things we don't use at all right now [Simian] * Initial task inventory support. Move, remove, and RezScript are not supported yet * SimulationObject Frozen and RotationAxis properties now point to the root prim in the linkset git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2503 52acb1d6-8a22-11de-b505-999d5b087335 --- OpenMetaverse/InventoryManager.cs | 19 ++ OpenMetaverse/Permissions.cs | 13 + OpenMetaverse/Primitives/Primitive.cs | 12 + Programs/Simian/Extensions/SceneManager.cs | 59 ++-- .../Interfaces/ITaskInventoryProvider.cs | 13 +- Programs/Simian/LLUDP/LLAssets.cs | 112 ++++++- Programs/Simian/LLUDP/LLTaskInventory.cs | 163 +++++++++ .../SceneExtensions/TaskInventoryManager.cs | 32 +- Programs/Simian/ScriptApi.cs | 44 ++- Programs/Simian/ScriptTypes.cs | 16 + Programs/Simian/SimulationObject.cs | 68 +++- Programs/Simian/SimulationObjectInventory.cs | 309 ++++++++++++++++++ bin/SimianData/Simian.ini | 3 + 13 files changed, 754 insertions(+), 109 deletions(-) create mode 100644 Programs/Simian/LLUDP/LLTaskInventory.cs create mode 100644 Programs/Simian/SimulationObjectInventory.cs diff --git a/OpenMetaverse/InventoryManager.cs b/OpenMetaverse/InventoryManager.cs index f4f99146..eed0d48c 100644 --- a/OpenMetaverse/InventoryManager.cs +++ b/OpenMetaverse/InventoryManager.cs @@ -154,6 +154,25 @@ namespace OpenMetaverse ReturnToLastOwner = 10 } + /// + /// Upper half of the Flags field for inventory items + /// + [Flags] + public enum InventoryItemFlags + { + None = 0, + LandmarkVisited = 1, + ObjectSlamPerm = 0x100, + ObjectSlamSale = 0x1000, + ObjectPermOverwriteBase = 0x010000, + ObjectPermOverwriteOwner = 0x020000, + ObjectPermOverwriteGroup = 0x040000, + ObjectPermOverwriteEveryone = 0x080000, + ObjectPermOverwriteNextOwner = 0x100000, + ObjectHasMultipleItems = 0x200000, + SharedSingleReference = 0x40000000, + } + #endregion Enums #region Inventory Object Classes diff --git a/OpenMetaverse/Permissions.cs b/OpenMetaverse/Permissions.cs index a71436ac..90d8273c 100644 --- a/OpenMetaverse/Permissions.cs +++ b/OpenMetaverse/Permissions.cs @@ -91,6 +91,19 @@ namespace OpenMetaverse OwnerMask = (PermissionMask)ownerMask; } + public Permissions GetNextPermissions() + { + uint nextMask = (uint)NextOwnerMask; + + return new Permissions( + (uint)BaseMask & nextMask, + (uint)EveryoneMask & nextMask, + (uint)GroupMask & nextMask, + (uint)NextOwnerMask, + (uint)OwnerMask & nextMask + ); + } + public OSD GetOSD() { OSDMap permissions = new OSDMap(5); diff --git a/OpenMetaverse/Primitives/Primitive.cs b/OpenMetaverse/Primitives/Primitive.cs index e8e26f30..ecfa985f 100644 --- a/OpenMetaverse/Primitives/Primitive.cs +++ b/OpenMetaverse/Primitives/Primitive.cs @@ -1008,6 +1008,18 @@ namespace OpenMetaverse Name = props.Name; Description = props.Description; } + + public byte[] GetTextureIDBytes() + { + if (TextureIDs == null || TextureIDs.Length == 0) + return Utils.EmptyBytes; + + byte[] bytes = new byte[16 * TextureIDs.Length]; + for (int i = 0; i < TextureIDs.Length; i++) + TextureIDs[i].ToBytes(bytes, 16 * i); + + return bytes; + } } #endregion Subclasses diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index 0f8e15f7..210fa90f 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -364,24 +364,10 @@ namespace Simian public bool ObjectRemove(object sender, uint localID) { SimulationObject obj; - Agent agent; if (sceneObjects.TryGetValue(localID, out obj)) { - if (sceneAgents.TryGetValue(obj.Prim.ID, out agent)) - AgentRemove(sender, agent); - - if (OnObjectRemove != null) - OnObjectRemove(sender, obj); - - sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); - - KillObjectPacket kill = new KillObjectPacket(); - kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; - kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); - kill.ObjectData[0].ID = obj.Prim.LocalID; - - udp.BroadcastPacket(kill, PacketCategory.State); + RemoveObject(sender, obj); return true; } @@ -391,24 +377,10 @@ namespace Simian public bool ObjectRemove(object sender, UUID id) { SimulationObject obj; - Agent agent; if (sceneObjects.TryGetValue(id, out obj)) { - if (sceneAgents.TryGetValue(id, out agent)) - AgentRemove(sender, agent); - - if (OnObjectRemove != null) - OnObjectRemove(sender, obj); - - sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); - - KillObjectPacket kill = new KillObjectPacket(); - kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; - kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); - kill.ObjectData[0].ID = obj.Prim.LocalID; - - udp.BroadcastPacket(kill, PacketCategory.State); + RemoveObject(sender, obj); return true; } @@ -1377,6 +1349,33 @@ namespace Simian } } + void RemoveObject(object sender, SimulationObject obj) + { + // If this object has an agent associated with it, remove the agent from the scene as well + Agent agent; + if (sceneAgents.TryGetValue(obj.Prim.ID, out agent)) + AgentRemove(sender, agent); + + // Remove the agent from the scene + sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); + + // Fire the callback + if (OnObjectRemove != null) + OnObjectRemove(sender, obj); + + // If this object has a cached task inventory asset, delete it now + if (obj.Inventory.InventorySerial > 0) + taskInventory.RemoveTaskFile(obj.Inventory.GetInventoryFilename()); + + // Broadcast the kill message + KillObjectPacket kill = new KillObjectPacket(); + kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; + kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); + kill.ObjectData[0].ID = obj.Prim.LocalID; + + udp.BroadcastPacket(kill, PacketCategory.State); + } + void SendObjectPacket(SimulationObject obj, bool canUseCompressed, bool canUseImproved, PrimFlags creatorFlags, UpdateFlags updateFlags) { if (!canUseImproved && !canUseCompressed) diff --git a/Programs/Simian/Interfaces/ITaskInventoryProvider.cs b/Programs/Simian/Interfaces/ITaskInventoryProvider.cs index f64abc0a..059d9ca5 100644 --- a/Programs/Simian/Interfaces/ITaskInventoryProvider.cs +++ b/Programs/Simian/Interfaces/ITaskInventoryProvider.cs @@ -6,15 +6,8 @@ namespace Simian { public interface ITaskInventoryProvider { - UUID CreateItem(UUID agentID, UUID containerObjectID, string name, string description, InventoryType invType, - AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, - UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket); - bool RemoveItem(UUID agentID, UUID containerObjectID, UUID itemID); - - bool TryGetAsset(UUID containerObjectID, UUID assetID, out Asset asset); - - void ForEachItem(UUID containerObjectID, Action action); - InventoryTaskItem FindItem(UUID containerObjectID, Predicate match); - List FindAllItems(UUID containerObjectID, Predicate match); + void AddTaskFile(string filename, byte[] assetData); + bool RemoveTaskFile(string filename); + bool TryGetTaskFile(string filename, out byte[] assetData); } } diff --git a/Programs/Simian/LLUDP/LLAssets.cs b/Programs/Simian/LLUDP/LLAssets.cs index 1c6704dc..361a3eb3 100644 --- a/Programs/Simian/LLUDP/LLAssets.cs +++ b/Programs/Simian/LLUDP/LLAssets.cs @@ -9,7 +9,8 @@ namespace Simian public class LLAssets : IExtension { ISceneProvider scene; - Dictionary CurrentUploads = new Dictionary(); + Dictionary currentDownloads = new Dictionary(); + Dictionary currentUploads = new Dictionary(); public LLAssets() { @@ -19,10 +20,12 @@ namespace Simian { this.scene = scene; - scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler)); - scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler)); - scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler)); - scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler)); + scene.UDP.RegisterPacketCallback(PacketType.RequestXfer, RequestXferHandler); + scene.UDP.RegisterPacketCallback(PacketType.ConfirmXferPacket, ConfirmXferPacketHandler); + scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, AssetUploadRequestHandler); + scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, SendXferPacketHandler); + scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, AbortXferHandler); + scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, TransferRequestHandler); return true; } @@ -32,6 +35,89 @@ namespace Simian #region Xfer System + void RequestXferHandler(Packet packet, Agent agent) + { + RequestXferPacket request = (RequestXferPacket)packet; + + string filename = Utils.BytesToString(request.XferID.Filename); + + byte[] assetData; + if (scene.TaskInventory.TryGetTaskFile(filename, out assetData)) + { + SendXferPacketPacket xfer = new SendXferPacketPacket(); + xfer.XferID.ID = request.XferID.ID; + + if (assetData.Length < 1000) + { + xfer.XferID.Packet = 0x80000000; + xfer.DataPacket.Data = new byte[assetData.Length + 4]; + Utils.IntToBytes(assetData.Length, xfer.DataPacket.Data, 0); + Buffer.BlockCopy(assetData, 0, xfer.DataPacket.Data, 4, assetData.Length); + + scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset); + Logger.DebugLog("Completed single packet xfer download of " + filename); + } + else + { + xfer.XferID.Packet = 0; + xfer.DataPacket.Data = new byte[1000 + 4]; + Utils.IntToBytes(assetData.Length, xfer.DataPacket.Data, 0); + Buffer.BlockCopy(assetData, 0, xfer.DataPacket.Data, 4, 1000); + + // We don't need the entire XferDownload class, just the asset data and the current packet number + XferDownload download = new XferDownload(); + download.AssetData = assetData; + download.PacketNum = 1; + download.Filename = filename; + lock (currentDownloads) + currentDownloads[request.XferID.ID] = download; + + scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset); + } + } + else + { + Logger.Log("Got a RequestXfer for an unknown file: " + filename, Helpers.LogLevel.Warning); + } + + // lock (currentDownloads) + } + + void ConfirmXferPacketHandler(Packet packet, Agent agent) + { + ConfirmXferPacketPacket confirm = (ConfirmXferPacketPacket)packet; + + XferDownload download; + if (currentDownloads.TryGetValue(confirm.XferID.ID, out download)) + { + // Send the next packet + SendXferPacketPacket xfer = new SendXferPacketPacket(); + xfer.XferID.ID = confirm.XferID.ID; + + int bytesRemaining = (int)(download.AssetData.Length - (download.PacketNum * 1000)); + + if (bytesRemaining > 1000) + { + xfer.DataPacket.Data = new byte[1000]; + Buffer.BlockCopy(download.AssetData, (int)download.PacketNum * 1000, xfer.DataPacket.Data, 0, 1000); + xfer.XferID.Packet = download.PacketNum++; + } + else + { + // Last packet + xfer.DataPacket.Data = new byte[bytesRemaining]; + Buffer.BlockCopy(download.AssetData, (int)download.PacketNum * 1000, xfer.DataPacket.Data, 0, bytesRemaining); + xfer.XferID.Packet = download.PacketNum++ | 0x80000000; + + lock (currentDownloads) + currentDownloads.Remove(confirm.XferID.ID); + Logger.DebugLog("Completing xfer download for: " + download.Filename); + } + + scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset); + } + } + void AssetUploadRequestHandler(Packet packet, Agent agent) { AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet; @@ -86,8 +172,8 @@ namespace Simian xfer.XferID.VFileType = request.AssetBlock.Type; // Add this asset to the current upload list - lock (CurrentUploads) - CurrentUploads[xfer.XferID.ID] = asset; + lock (currentUploads) + currentUploads[xfer.XferID.ID] = asset; scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Inventory); } @@ -98,7 +184,7 @@ namespace Simian SendXferPacketPacket xfer = (SendXferPacketPacket)packet; Asset asset; - if (CurrentUploads.TryGetValue(xfer.XferID.ID, out asset)) + if (currentUploads.TryGetValue(xfer.XferID.ID, out asset)) { if (asset.AssetData == null) { @@ -136,8 +222,8 @@ namespace Simian // Asset upload finished Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType)); - lock (CurrentUploads) - CurrentUploads.Remove(xfer.XferID.ID); + lock (currentUploads) + currentUploads.Remove(xfer.XferID.ID); scene.Server.Assets.StoreAsset(asset); @@ -159,14 +245,14 @@ namespace Simian { AbortXferPacket abort = (AbortXferPacket)packet; - lock (CurrentUploads) + lock (currentUploads) { - if (CurrentUploads.ContainsKey(abort.XferID.ID)) + if (currentUploads.ContainsKey(abort.XferID.ID)) { Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID, (TransferError)abort.XferID.Result)); - CurrentUploads.Remove(abort.XferID.ID); + currentUploads.Remove(abort.XferID.ID); } else { diff --git a/Programs/Simian/LLUDP/LLTaskInventory.cs b/Programs/Simian/LLUDP/LLTaskInventory.cs new file mode 100644 index 00000000..38726fd3 --- /dev/null +++ b/Programs/Simian/LLUDP/LLTaskInventory.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using ExtensionLoader; +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace Simian +{ + public class LLTaskInventory : IExtension + { + ISceneProvider scene; + + public LLTaskInventory() + { + } + + public bool Start(ISceneProvider scene) + { + this.scene = scene; + + scene.UDP.RegisterPacketCallback(PacketType.RequestTaskInventory, RequestTaskInventoryHandler); + scene.UDP.RegisterPacketCallback(PacketType.UpdateTaskInventory, UpdateTaskInventoryHandler); + scene.UDP.RegisterPacketCallback(PacketType.RemoveTaskInventory, RemoveTaskInventoryHandler); + scene.UDP.RegisterPacketCallback(PacketType.MoveTaskInventory, MoveTaskInventoryHandler); + return true; + } + + public void Stop() + { + } + + void RequestTaskInventoryHandler(Packet packet, Agent agent) + { + RequestTaskInventoryPacket request = (RequestTaskInventoryPacket)packet; + + // Try to find this object in the scene + SimulationObject obj; + if (scene.TryGetObject(request.InventoryData.LocalID, out obj)) + { + ReplyTaskInventoryPacket reply = new ReplyTaskInventoryPacket(); + reply.InventoryData.Filename = Utils.StringToBytes(obj.Inventory.GetInventoryFilename()); + reply.InventoryData.Serial = obj.Inventory.InventorySerial; + reply.InventoryData.TaskID = obj.Prim.ID; + + scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction); + } + } + + void UpdateTaskInventoryHandler(Packet packet, Agent agent) + { + UpdateTaskInventoryPacket update = (UpdateTaskInventoryPacket)packet; + + InventoryTaskItem item; + SimulationObject targetObj; + InventoryObject invObj; + + if (update.UpdateData.Key == 0) + { + if (scene.TryGetObject(update.UpdateData.LocalID, out targetObj)) + { + if (targetObj.Inventory.TryGetItem(update.InventoryData.ItemID, out item)) + { + // Updating an existing item in the task inventory + } + else if (scene.Server.Inventory.TryGetInventory(agent.ID, update.InventoryData.ItemID, out invObj)) + { + // Create a new item in the task inventory + if (invObj is InventoryItem) + { + InventoryItem fromItem = (InventoryItem)invObj; + + item = new InventoryTaskItem(); + //item.ID will be assigned in AddOrUpdateItem + item.AssetID = fromItem.AssetID; + item.AssetType = fromItem.AssetType; + item.CreationDate = fromItem.CreationDate; + item.CreatorID = fromItem.CreatorID; + item.Description = fromItem.Description; + item.Flags = fromItem.Flags; + item.GrantedPermissions = 0; + item.GroupID = fromItem.GroupID; + item.GroupOwned = fromItem.GroupOwned; + item.InventoryType = fromItem.InventoryType; + item.Name = fromItem.Name; + item.OwnerID = agent.ID; + item.ParentID = update.InventoryData.FolderID; + item.Parent = null; // TODO: Try to find a parent folder in task inventory? + item.ParentObjectID = targetObj.Prim.ID; + item.PermissionGranter = UUID.Zero; + item.Permissions = fromItem.Permissions.GetNextPermissions(); + item.SalePrice = fromItem.SalePrice; + item.SaleType = fromItem.SaleType; + + bool allowDrop = (targetObj.Prim.Flags & PrimFlags.AllowInventoryDrop) != 0; + + targetObj.Inventory.AddOrUpdateItem(item, false, allowDrop); + Logger.Log("Created new task inventory item: " + item.Name, Helpers.LogLevel.Info); + + // Send an ObjectPropertiesReply to inform the client that inventory has changed + ObjectPropertiesPacket props = new ObjectPropertiesPacket(); + props.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[1]; + props.ObjectData[0] = new ObjectPropertiesPacket.ObjectDataBlock(); + props.ObjectData[0].AggregatePerms = targetObj.Prim.Properties.AggregatePerms; + props.ObjectData[0].AggregatePermTextures = targetObj.Prim.Properties.AggregatePermTextures; + props.ObjectData[0].AggregatePermTexturesOwner = targetObj.Prim.Properties.AggregatePermTexturesOwner; + props.ObjectData[0].BaseMask = (uint)targetObj.Prim.Properties.Permissions.BaseMask; + props.ObjectData[0].Category = (uint)targetObj.Prim.Properties.Category; + props.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(targetObj.Prim.Properties.CreationDate); + props.ObjectData[0].CreatorID = targetObj.Prim.Properties.CreatorID; + props.ObjectData[0].Description = Utils.StringToBytes(targetObj.Prim.Properties.Description); + props.ObjectData[0].EveryoneMask = (uint)targetObj.Prim.Properties.Permissions.EveryoneMask; + props.ObjectData[0].FolderID = targetObj.Prim.Properties.FolderID; + props.ObjectData[0].FromTaskID = targetObj.Prim.Properties.FromTaskID; + props.ObjectData[0].GroupID = targetObj.Prim.Properties.GroupID; + props.ObjectData[0].GroupMask = (uint)targetObj.Prim.Properties.Permissions.GroupMask; + props.ObjectData[0].InventorySerial = targetObj.Prim.Properties.InventorySerial; + props.ObjectData[0].ItemID = targetObj.Prim.Properties.ItemID; + props.ObjectData[0].LastOwnerID = targetObj.Prim.Properties.LastOwnerID; + props.ObjectData[0].Name = Utils.StringToBytes(targetObj.Prim.Properties.Name); + props.ObjectData[0].NextOwnerMask = (uint)targetObj.Prim.Properties.Permissions.NextOwnerMask; + props.ObjectData[0].ObjectID = targetObj.Prim.ID; + props.ObjectData[0].OwnerID = targetObj.Prim.Properties.OwnerID; + props.ObjectData[0].OwnerMask = (uint)targetObj.Prim.Properties.Permissions.OwnerMask; + props.ObjectData[0].OwnershipCost = targetObj.Prim.Properties.OwnershipCost; + props.ObjectData[0].SalePrice = targetObj.Prim.Properties.SalePrice; + props.ObjectData[0].SaleType = (byte)targetObj.Prim.Properties.SaleType; + props.ObjectData[0].SitName = Utils.StringToBytes(targetObj.Prim.Properties.SitName); + props.ObjectData[0].TextureID = targetObj.Prim.Properties.GetTextureIDBytes(); + props.ObjectData[0].TouchName = Utils.StringToBytes(targetObj.Prim.Properties.TouchName); + + scene.UDP.SendPacket(agent.ID, props, PacketCategory.Transaction); + } + else + { + Logger.Log("[TODO] Handle dropping folders in task inventory", Helpers.LogLevel.Warning); + } + } + else + { + Logger.Log("Got an UpdateTaskInventory packet referencing an unknown inventory item", Helpers.LogLevel.Warning); + } + } + else + { + Logger.Log("Got an UpdateTaskInventory packet referencing an unknown object", Helpers.LogLevel.Warning); + } + } + else + { + Logger.Log("Got an UpdateTaskInventory packet with a Key of " + update.UpdateData.Key, Helpers.LogLevel.Warning); + } + } + + void RemoveTaskInventoryHandler(Packet packet, Agent agent) + { + } + + void MoveTaskInventoryHandler(Packet packet, Agent agent) + { + } + } +} diff --git a/Programs/Simian/SceneExtensions/TaskInventoryManager.cs b/Programs/Simian/SceneExtensions/TaskInventoryManager.cs index c34cf818..26d6457d 100644 --- a/Programs/Simian/SceneExtensions/TaskInventoryManager.cs +++ b/Programs/Simian/SceneExtensions/TaskInventoryManager.cs @@ -10,6 +10,7 @@ namespace Simian class TaskInventoryManager : IExtension, ITaskInventoryProvider { ISceneProvider scene; + Dictionary assets = new Dictionary(); public TaskInventoryManager() { @@ -25,36 +26,21 @@ namespace Simian { } - public UUID CreateItem(UUID agentID, UUID containerObjectID, string name, string description, InventoryType invType, - AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask, - UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket) + public void AddTaskFile(string filename, byte[] assetData) { - return UUID.Zero; + lock (assets) + assets[filename] = assetData; } - public bool RemoveItem(UUID agentID, UUID containerObjectID, UUID itemID) + public bool RemoveTaskFile(string filename) { - return false; + lock (assets) + return assets.Remove(filename); } - public bool TryGetAsset(UUID containerObjectID, UUID assetID, out Asset asset) + public bool TryGetTaskFile(string filename, out byte[] assetData) { - asset = null; - return false; - } - - public void ForEachItem(UUID containerObjectID, Action action) - { - } - - public InventoryTaskItem FindItem(UUID containerObjectID, Predicate match) - { - return null; - } - - public List FindAllItems(UUID containerObjectID, Predicate match) - { - return new List(); + return assets.TryGetValue(filename, out assetData); } } } diff --git a/Programs/Simian/ScriptApi.cs b/Programs/Simian/ScriptApi.cs index 61ec817d..e56ddd72 100644 --- a/Programs/Simian/ScriptApi.cs +++ b/Programs/Simian/ScriptApi.cs @@ -1857,7 +1857,7 @@ namespace Simian } if ((item != null && - scene.TaskInventory.TryGetAsset(hostObject.Prim.ID, item.AssetID, out asset) && + scene.Server.Assets.TryGetAsset(item.AssetID, out asset) && asset is AssetPrim && scene.Server.Assets.TryDecodePrimAsset(((AssetPrim)asset).AssetData, out newLinkset)) || newLinkset != null) @@ -2362,10 +2362,8 @@ namespace Simian { hostObject.AddScriptLPS(1); - InventoryTaskItem scriptItem = scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.ID == scriptID; }); - - if (scriptItem != null) + InventoryTaskItem scriptItem; + if (hostObject.Inventory.TryGetItem(scriptID, out scriptItem)) return scriptItem.PermissionGranter.ToString(); else return LSL_Key.Zero; @@ -2375,12 +2373,10 @@ namespace Simian { hostObject.AddScriptLPS(1); - InventoryTaskItem scriptItem = scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.ID == scriptID; }); - uint perms = 0; - if (scriptItem != null) + InventoryTaskItem scriptItem; + if (hostObject.Inventory.TryGetItem(scriptID, out scriptItem)) { perms = scriptItem.GrantedPermissions; if (automaticLinkPermission) @@ -2499,7 +2495,7 @@ namespace Simian hostObject.AddScriptLPS(1); int count = 0; - scene.TaskInventory.ForEachItem(hostObject.Prim.ID, + hostObject.Inventory.ForEachItem( delegate(InventoryTaskItem item) { if (type == -1 || (int)item.AssetType == type) @@ -4520,10 +4516,8 @@ namespace Simian { hostObject.AddScriptLPS(1); - InventoryTaskItem findItem = scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.Name == itemName; }); - - if (findItem != null) + InventoryTaskItem findItem; + if (hostObject.Inventory.TryGetItem(itemName, out findItem)) { switch (mask) { @@ -5103,15 +5097,17 @@ namespace Simian private InventoryTaskItem InventorySelf() { hostObject.AddScriptLPS(1); - return scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.AssetID == scriptID; }); + + InventoryTaskItem self; + hostObject.Inventory.TryGetItem(scriptID, out self); + return self; } private InventoryTaskItem InventoryKey(string name, AssetType type) { hostObject.AddScriptLPS(1); - return scene.TaskInventory.FindItem(hostObject.Prim.ID, + return hostObject.Inventory.FindItem( delegate(InventoryTaskItem item) { return item.AssetType == type && item.Name == name; }); } @@ -5119,8 +5115,9 @@ namespace Simian { hostObject.AddScriptLPS(1); - return scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.Name == name; }); + InventoryTaskItem item; + hostObject.Inventory.TryGetItem(name, out item); + return item; } /// @@ -5151,10 +5148,11 @@ namespace Simian UUID ScriptByName(string name) { - InventoryTaskItem scriptItem = scene.TaskInventory.FindItem(hostObject.Prim.ID, - delegate(InventoryTaskItem item) { return item.AssetType == AssetType.LSLText && item.Name == name; }); - - return (scriptItem != null) ? scriptItem.ID : UUID.Zero; + InventoryTaskItem scriptItem; + if (hostObject.Inventory.TryGetItem(name, out scriptItem) && scriptItem.AssetType == AssetType.LSLText) + return scriptItem.ID; + else + return UUID.Zero; } void ShoutError(string msg) diff --git a/Programs/Simian/ScriptTypes.cs b/Programs/Simian/ScriptTypes.cs index 83d358df..565ee08e 100644 --- a/Programs/Simian/ScriptTypes.cs +++ b/Programs/Simian/ScriptTypes.cs @@ -4,6 +4,22 @@ using System.Text.RegularExpressions; namespace Simian { + [Flags] + public enum Changed : uint + { + INVENTORY = 1, + COLOR = 2, + SHAPE = 4, + SCALE = 8, + TEXTURE = 16, + LINK = 32, + ALLOWED_DROP = 64, + OWNER = 128, + REGION_RESTART = 256, + REGION = 512, + TELEPORT = 1024 + } + public static class ScriptTypes { #region LSL Types diff --git a/Programs/Simian/SimulationObject.cs b/Programs/Simian/SimulationObject.cs index 8a02272d..4e25cc30 100644 --- a/Programs/Simian/SimulationObject.cs +++ b/Programs/Simian/SimulationObject.cs @@ -8,21 +8,16 @@ namespace Simian { public class SimulationObject { - // TODO: Frozen and RotationAxis might want to become properties that access the parent values - + /// Reference to the scene this object exists in + public ISceneProvider Scene; /// Reference to the primitive object this class wraps public Primitive Prim; /// Link number, if this object is part of a linkset public int LinkNumber; - /// True when an avatar grabs this object. Stops movement and - /// rotation - public bool Frozen; /// Holds the state of the object after each edit to enable undo public CircularQueue UndoSteps = new CircularQueue(10); /// Holds the state of the object after each undo to enable redo public CircularQueue RedoSteps = new CircularQueue(10); - /// Axis of rotation for the object in the physics engine - public Vector3 RotationAxis = Vector3.UnitY; /// A continual rotational impulse public Vector3 Torque; /// Last point the object was attached to (right hand by default) @@ -41,13 +36,61 @@ namespace Simian /// Will be applied to the object when it is dropped. This is always the world /// rotation, since it is only applicable to parent objects public Quaternion BeforeAttachmentRotation = Quaternion.Identity; + /// Task inventory + public SimulationObjectInventory Inventory; - protected ISceneProvider Scene; protected SimpleMesh[] Meshes; protected SimpleMesh[] WorldTransformedMeshes; + bool frozen; + Vector3 rotationAxis = Vector3.UnitY; uint? crc; + #region Properties + + /// True when an avatar grabs this object. Stops movement and + /// rotation + public bool Frozen + { + get + { + SimulationObject parent; + if (Prim.ParentID != 0 && Scene.TryGetObject(Prim.ID, out parent)) + return parent.Frozen; + else + return frozen; + } + set + { + SimulationObject parent; + if (Prim.ParentID != 0 && Scene.TryGetObject(Prim.ID, out parent)) + parent.Frozen = value; + else + frozen = value; + } + } + + /// Axis of rotation for the object in the physics engine + public Vector3 RotationAxis + { + get + { + SimulationObject parent; + if (Prim.ParentID != 0 && Scene.TryGetObject(Prim.ID, out parent)) + return parent.RotationAxis; + else + return rotationAxis; + } + set + { + SimulationObject parent; + if (Prim.ParentID != 0 && Scene.TryGetObject(Prim.ID, out parent)) + parent.RotationAxis = value; + else + rotationAxis = value; + } + } + public uint CRC { get @@ -76,12 +119,16 @@ namespace Simian } } + #endregion Properties + public SimulationObject(SimulationObject obj) { - Prim = new Primitive(obj.Prim); Scene = obj.Scene; + Prim = new Primitive(obj.Prim); + Inventory = new SimulationObjectInventory(this); LinkNumber = obj.LinkNumber; - Frozen = obj.Frozen; + frozen = obj.frozen; + rotationAxis = obj.rotationAxis; // Skip everything else because it can be lazily reconstructed } @@ -89,6 +136,7 @@ namespace Simian { Prim = prim; Scene = scene; + Inventory = new SimulationObjectInventory(this); } public SimulationObject GetLinksetParent() diff --git a/Programs/Simian/SimulationObjectInventory.cs b/Programs/Simian/SimulationObjectInventory.cs new file mode 100644 index 00000000..bce81305 --- /dev/null +++ b/Programs/Simian/SimulationObjectInventory.cs @@ -0,0 +1,309 @@ +using System; +using System.Text; +using System.Collections.Generic; +using OpenMetaverse; + +namespace Simian +{ + public class TaskInventoryStringBuilder + { + StringBuilder builder = new StringBuilder(); + + public TaskInventoryStringBuilder(UUID folderID, UUID parentID) + { + builder.Append("\tinv_object\t0\n\t{\n"); + AddNameValueLine("obj_id", folderID.ToString()); + AddNameValueLine("parent_id", parentID.ToString()); + AddNameValueLine("type", "category"); + AddNameValueLine("name", "Contents|"); + AddSectionEnd(); + } + + public void AddItemStart() + { + builder.Append("\tinv_item\t0\n"); + AddSectionStart(); + } + + public void AddPermissionsStart() + { + builder.Append("\tpermissions 0\n"); + AddSectionStart(); + } + + public void AddSaleStart() + { + builder.Append("\tsale_info\t0\n"); + AddSectionStart(); + } + + protected void AddSectionStart() + { + builder.Append("\t{\n"); + } + + public void AddSectionEnd() + { + builder.Append("\t}\n"); + } + + public void AddLine(string addLine) + { + builder.Append(addLine); + } + + public void AddNameValueLine(string name, string value) + { + builder.Append("\t\t"); + builder.Append(name); + builder.Append("\t"); + builder.Append(value); + builder.Append("\n"); + } + + public override string ToString() + { + return builder.ToString(); + } + } + + public class SimulationObjectInventory + { + SimulationObject hostObject; + string inventoryFilename; + short inventoryFilenameSerial; + DoubleDictionary items; + + public short InventorySerial + { + get { return hostObject.Prim.Properties.InventorySerial; } + set { hostObject.Prim.Properties.InventorySerial = value; } + } + + public SimulationObjectInventory(SimulationObject hostObject) + { + this.hostObject = hostObject; + } + + public void ChangeInventoryOwner(UUID newOwnerID) + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void ChangeInventoryGroup(UUID newGroupID) + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void StartScripts(int startParam, bool triggerOnRezEvent) + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void StopScripts() + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void StartScript(InventoryTaskItem script, int startParam, bool triggerOnRezEvent) + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void StopScript(UUID itemID) + { + LazyInitialize(); + throw new NotImplementedException(); + } + + public void AddOrUpdateItem(InventoryTaskItem item, bool replace, bool allowedDrop) + { + LazyInitialize(); + + item.ParentObjectID = hostObject.Prim.ID; + + if (replace) + { + InventoryTaskItem oldItem; + if (items.TryGetValue(item.Name, out oldItem)) + { + item.ID = oldItem.ID; + items.Remove(item.ID, item.Name); + } + } + else + { + item.Name = NextAvailableFilename(item.Name); + } + + if (item.ID == UUID.Zero) + item.ID = UUID.Random(); + + items.Add(item.ID, item.Name, item); + + UpdateTaskInventoryAsset(); + + // Post a script event + Changed change = allowedDrop ? Changed.ALLOWED_DROP : Changed.INVENTORY; + hostObject.Scene.ScriptEngine.PostObjectEvent(hostObject.Prim.ID, new EventParams( + "changed", new object[] { new ScriptTypes.LSL_Integer((uint)change) }, new DetectParams[0])); + } + + public InventoryType RemoveItem(UUID itemID) + { + LazyInitialize(); + InventoryTaskItem item; + if (items.TryGetValue(itemID, out item)) + { + items.Remove(itemID, item.Name); + + UpdateTaskInventoryAsset(); + + // Post a script event + hostObject.Scene.ScriptEngine.PostObjectEvent(hostObject.Prim.ID, new EventParams( + "changed", new object[] { new ScriptTypes.LSL_Integer((uint)Changed.INVENTORY) }, new DetectParams[0])); + + // FIXME: Check if this prim still classifies as "scripted" + + return item.InventoryType; + } + else + { + return InventoryType.Unknown; + } + } + + public bool TryGetItem(UUID itemID, out InventoryTaskItem item) + { + LazyInitialize(); + return items.TryGetValue(itemID, out item); + } + + public bool TryGetItem(string name, out InventoryTaskItem item) + { + LazyInitialize(); + return items.TryGetValue(name, out item); + } + + public string GetInventoryFilename() + { + if (InventorySerial > 0) + { + if (String.IsNullOrEmpty(inventoryFilename) || inventoryFilenameSerial < InventorySerial) + inventoryFilename = "inventory_" + UUID.Random() + ".tmp"; + + inventoryFilenameSerial = InventorySerial; + + return inventoryFilename; + } + else + { + return String.Empty; + } + } + + public void ForEachItem(Action action) + { + LazyInitialize(); + items.ForEach(action); + } + + public InventoryTaskItem FindItem(Predicate match) + { + LazyInitialize(); + return items.FindValue(match); + } + + public IList FindAllItems(Predicate match) + { + LazyInitialize(); + return items.FindAll(match); + } + + void UpdateTaskInventoryAsset() + { + // Remove the previous task inventory asset + string filename = GetInventoryFilename(); + if (!String.IsNullOrEmpty(filename)) + hostObject.Scene.TaskInventory.RemoveTaskFile(filename); + + // Update the inventory serial number + ++InventorySerial; + + // Create the new asset + filename = GetInventoryFilename(); + byte[] assetData = GetTaskInventoryAsset(); + hostObject.Scene.TaskInventory.AddTaskFile(filename, assetData); + } + + byte[] GetTaskInventoryAsset() + { + TaskInventoryStringBuilder invString = new TaskInventoryStringBuilder(hostObject.Prim.ID, UUID.Zero); + + items.ForEach( + delegate(InventoryTaskItem item) + { + invString.AddItemStart(); + invString.AddNameValueLine("item_id", item.ID.ToString()); + invString.AddNameValueLine("parent_id", hostObject.Prim.ID.ToString()); + + invString.AddPermissionsStart(); + + invString.AddNameValueLine("base_mask", Utils.UIntToHexString((uint)item.Permissions.BaseMask)); + invString.AddNameValueLine("owner_mask", Utils.UIntToHexString((uint)item.Permissions.OwnerMask)); + invString.AddNameValueLine("group_mask", Utils.UIntToHexString((uint)item.Permissions.GroupMask)); + invString.AddNameValueLine("everyone_mask", Utils.UIntToHexString((uint)item.Permissions.EveryoneMask)); + invString.AddNameValueLine("next_owner_mask", Utils.UIntToHexString((uint)item.Permissions.NextOwnerMask)); + + invString.AddNameValueLine("creator_id", item.CreatorID.ToString()); + invString.AddNameValueLine("owner_id", item.OwnerID.ToString()); + + invString.AddNameValueLine("last_owner_id", item.CreatorID.ToString()); // FIXME: Do we need InventoryItem.LastOwnerID? + + invString.AddNameValueLine("group_id", item.GroupID.ToString()); + invString.AddSectionEnd(); + + invString.AddNameValueLine("asset_id", item.AssetID.ToString()); + invString.AddNameValueLine("type", OpenMetaverse.InventoryManager.AssetTypeToString(item.AssetType)); + invString.AddNameValueLine("inv_type", OpenMetaverse.InventoryManager.InventoryTypeToString(item.InventoryType)); + invString.AddNameValueLine("flags", Utils.UIntToHexString(item.Flags)); + + invString.AddSaleStart(); + invString.AddNameValueLine("sale_type", OpenMetaverse.InventoryManager.SaleTypeToString(item.SaleType)); + invString.AddNameValueLine("sale_price", item.SalePrice.ToString()); + invString.AddSectionEnd(); + + invString.AddNameValueLine("name", item.Name + "|"); + invString.AddNameValueLine("desc", item.Description + "|"); + + invString.AddNameValueLine("creation_date", Utils.DateTimeToUnixTime(item.CreationDate).ToString()); + invString.AddSectionEnd(); + } + ); + + return Utils.StringToBytes(invString.ToString()); + } + + string NextAvailableFilename(string name) + { + string tryName = name; + int suffix = 1; + + while (items.ContainsKey(tryName) && suffix < 256) + tryName = String.Format("{0} {1}", name, suffix++); + + return tryName; + } + + void LazyInitialize() + { + if (items == null) + items = new DoubleDictionary(); + } + } +} diff --git a/bin/SimianData/Simian.ini b/bin/SimianData/Simian.ini index cee3de43..010ca345 100644 --- a/bin/SimianData/Simian.ini +++ b/bin/SimianData/Simian.ini @@ -145,6 +145,9 @@ LLObjects ; Packet handling for parcel information LLParcels +; Packet handling for object inventory +LLTaskInventory + ; Packet handling for texture downloads LLTextures