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