From 38ca31b37a6ac8fe74b77e4488112eb77d612827 Mon Sep 17 00:00:00 2001
From: Justin Clarke Casey
Date: Thu, 4 Dec 2008 19:57:36 +0000
Subject: * Put in the code necessary to allow inventory transfer of whole
folders (and their contents) between agents, not just single items * However,
this is not currently activated since it's not absolutely fully tested and
there's a bug lurking in there to do with the sending of the
BulkInventoryUpdate packets
---
.../Region/ClientStack/LindenUDP/LLClientView.cs | 148 +++++++++++++++++++++
.../Inventory/Transfer/InventoryTransferModule.cs | 127 +++++++++++++-----
.../Environment/Modules/World/NPC/NPCAvatar.cs | 3 +
.../Region/Environment/Scenes/Scene.Inventory.cs | 119 ++++++++++++++++-
.../Region/Environment/Scenes/Tests/TestClient.cs | 5 +-
.../Region/Examples/SimpleModule/MyNpcCharacter.cs | 3 +
6 files changed, 365 insertions(+), 40 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 74bf7e5..42f190f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -1144,6 +1144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
msg.MessageBlock.Message = Utils.StringToBytes(message);
msg.MessageBlock.BinaryBucket = binaryBucket;
+ System.Console.WriteLine("SendInstantMessage: " + msg);
OutPacket(msg, ThrottleOutPacketType.Task);
}
}
@@ -1763,6 +1764,153 @@ namespace OpenSim.Region.ClientStack.LindenUDP
inventoryReply.Header.Zerocoded = true;
OutPacket(inventoryReply, ThrottleOutPacketType.Asset);
}
+
+ /// IClientAPI.SendBulkUpdateInventory(InventoryFolderBase)
+ public void SendBulkUpdateInventory(InventoryFolderBase folderBase)
+ {
+ // XXX: Nasty temporary move that will be resolved shortly
+ InventoryFolderImpl folder = (InventoryFolderImpl)folderBase;
+
+ // We will use the same transaction id for all the separate packets to be sent out in this update.
+ UUID transactionId = UUID.Random();
+
+ List folderDataBlocks
+ = new List();
+
+ SendBulkUpdateInventoryRecursive(folder, ref folderDataBlocks, transactionId);
+
+ if (folderDataBlocks.Count > 0)
+ {
+ // We'll end up with some unsent folder blocks if there were some empty folders at the end of the list
+ // Send these now
+ BulkUpdateInventoryPacket bulkUpdate
+ = (BulkUpdateInventoryPacket)PacketPool.Instance.GetPacket(PacketType.BulkUpdateInventory);
+ bulkUpdate.Header.Zerocoded = true;
+
+ bulkUpdate.AgentData.AgentID = AgentId;
+ bulkUpdate.AgentData.TransactionID = transactionId;
+ bulkUpdate.FolderData = folderDataBlocks.ToArray();
+
+ Console.WriteLine("SendBulkUpdateInventory :" + bulkUpdate);
+ OutPacket(bulkUpdate, ThrottleOutPacketType.Asset);
+ }
+ }
+
+ ///
+ /// Recursively construct bulk update packets to send folders and items
+ ///
+ ///
+ ///
+ ///
+ private void SendBulkUpdateInventoryRecursive(
+ InventoryFolderImpl folder, ref List folderDataBlocks,
+ UUID transactionId)
+ {
+ folderDataBlocks.Add(GenerateBulkUpdateFolderDataBlock(folder));
+
+ const int MAX_ITEMS_PER_PACKET = 5;
+
+ // If there are any items then we have to start sending them off in this packet - the next folder will have
+ // to be in its own bulk update packet. Also, we can only fit 5 items in a packet (at least this was the limit
+ // being used on the Linden grid at 20081203).
+ List items = folder.RequestListOfItems();
+ while (items.Count > 0)
+ {
+ BulkUpdateInventoryPacket bulkUpdate
+ = (BulkUpdateInventoryPacket)PacketPool.Instance.GetPacket(PacketType.BulkUpdateInventory);
+ bulkUpdate.Header.Zerocoded = true;
+
+ bulkUpdate.AgentData.AgentID = AgentId;
+ bulkUpdate.AgentData.TransactionID = transactionId;
+ bulkUpdate.FolderData = folderDataBlocks.ToArray();
+
+ int itemsToSend = (items.Count > MAX_ITEMS_PER_PACKET ? MAX_ITEMS_PER_PACKET : items.Count);
+ bulkUpdate.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[itemsToSend];
+
+ for (int i = 0; i < itemsToSend; i++)
+ {
+ // Remove from the end of the list so that we don't incur a performance penalty
+ bulkUpdate.ItemData[i] = GenerateBulkUpdateItemDataBlock(items[items.Count - 1]);
+ items.RemoveAt(items.Count - 1);
+ }
+
+ Console.WriteLine("SendBulkUpdateInventoryRecursive :" + bulkUpdate);
+ OutPacket(bulkUpdate, ThrottleOutPacketType.Asset);
+
+ folderDataBlocks = new List();
+
+ // If we're going to be sending another items packet then it needs to contain just the folder to which those
+ // items belong.
+ if (items.Count > 0)
+ folderDataBlocks.Add(GenerateBulkUpdateFolderDataBlock(folder));
+ }
+
+ List subFolders = folder.RequestListOfFolderImpls();
+ foreach (InventoryFolderImpl subFolder in subFolders)
+ {
+ SendBulkUpdateInventoryRecursive(subFolder, ref folderDataBlocks, transactionId);
+ }
+ }
+
+ ///
+ /// Generate a bulk update inventory data block for the given folder
+ ///
+ ///
+ ///
+ private BulkUpdateInventoryPacket.FolderDataBlock GenerateBulkUpdateFolderDataBlock(InventoryFolderBase folder)
+ {
+ BulkUpdateInventoryPacket.FolderDataBlock folderBlock = new BulkUpdateInventoryPacket.FolderDataBlock();
+
+ folderBlock.FolderID = folder.ID;
+ folderBlock.ParentID = folder.ParentID;
+ folderBlock.Type = -1;
+ folderBlock.Name = Utils.StringToBytes(folder.Name);
+
+ return folderBlock;
+ }
+
+ ///
+ /// Generate a bulk update inventory data block for the given item
+ ///
+ ///
+ ///
+ private BulkUpdateInventoryPacket.ItemDataBlock GenerateBulkUpdateItemDataBlock(InventoryItemBase item)
+ {
+ BulkUpdateInventoryPacket.ItemDataBlock itemBlock = new BulkUpdateInventoryPacket.ItemDataBlock();
+
+ itemBlock.ItemID = item.ID;
+ itemBlock.AssetID = item.AssetID;
+ itemBlock.CreatorID = item.Creator;
+ itemBlock.BaseMask = item.BasePermissions;
+ itemBlock.Description = Utils.StringToBytes(item.Description);
+ itemBlock.EveryoneMask = item.EveryOnePermissions;
+ itemBlock.FolderID = item.Folder;
+ itemBlock.InvType = (sbyte)item.InvType;
+ itemBlock.Name = Utils.StringToBytes(item.Name);
+ itemBlock.NextOwnerMask = item.NextPermissions;
+ itemBlock.OwnerID = item.Owner;
+ itemBlock.OwnerMask = item.CurrentPermissions;
+ itemBlock.Type = (sbyte)item.AssetType;
+ itemBlock.GroupID = item.GroupID;
+ itemBlock.GroupOwned = item.GroupOwned;
+ itemBlock.GroupMask = item.GroupPermissions;
+ itemBlock.Flags = item.Flags;
+ itemBlock.SalePrice = item.SalePrice;
+ itemBlock.SaleType = item.SaleType;
+ itemBlock.CreationDate = item.CreationDate;
+
+ itemBlock.CRC =
+ Helpers.InventoryCRC(
+ 1000, 0, itemBlock.InvType,
+ itemBlock.Type, itemBlock.AssetID,
+ itemBlock.GroupID, 100,
+ itemBlock.OwnerID, itemBlock.CreatorID,
+ itemBlock.ItemID, itemBlock.FolderID,
+ (uint)PermissionMask.All, 1, (uint)PermissionMask.All, (uint)PermissionMask.All,
+ (uint)PermissionMask.All);
+
+ return itemBlock;
+ }
/// IClientAPI.SendBulkUpdateInventory(InventoryItemBase)
public void SendBulkUpdateInventory(InventoryItemBase item)
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
index 73f1761..b41c36f 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
@@ -136,42 +136,85 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
if (im.dialog == (byte) InstantMessageDialog.InventoryOffered)
{
- ScenePresence user =
- scene.GetScenePresence(new UUID(im.toAgentID));
+ m_log.DebugFormat("Asset type {0}", ((AssetType)im.binaryBucket[0]));
+
+ ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID));
+ UUID copyID;
- // First byte of the array is probably the item type
- // Next 16 bytes are the UUID
+ // First byte is the asset type
+ AssetType assetType = (AssetType)im.binaryBucket[0];
+
+ // Temporarily disabled pending test of complex transfers (folders within folders, lots of items,
+ // empty folders, etc.)
+ if (AssetType.Folder == assetType)
+ return;
+
+ if (AssetType.Folder == assetType)
+ {
+ UUID folderID = new UUID(im.binaryBucket, 1);
+
+ m_log.DebugFormat("[AGENT INVENTORY]: Inserting original folder {0} "+
+ "into agent {1}'s inventory",
+ folderID, new UUID(im.toAgentID));
+
+ InventoryFolderImpl folderCopy
+ = scene.GiveInventoryFolder(new UUID(im.toAgentID), client.AgentId, folderID, UUID.Zero);
+
+ if (folderCopy == null)
+ {
+ client.SendAgentAlertMessage("Can't find folder to give. Nothing given.", false);
+ return;
+ }
+
+ // The outgoing binary bucket should contain only the byte which signals an asset folder is
+ // being copied and the following bytes for the copied folder's UUID
+ copyID = folderCopy.ID;
+ byte[] copyIDBytes = copyID.GetBytes();
+ im.binaryBucket = new byte[1 + copyIDBytes.Length];
+ im.binaryBucket[0] = (byte)AssetType.Folder;
+ Array.Copy(copyIDBytes, 0, im.binaryBucket, 1, copyIDBytes.Length);
+
+ if (user != null && !user.IsChildAgent)
+ {
+ user.ControllingClient.SendBulkUpdateInventory(folderCopy);
+ }
+ }
+ else
+ {
+ // First byte of the array is probably the item type
+ // Next 16 bytes are the UUID
- UUID itemID = new UUID(im.binaryBucket, 1);
+ UUID itemID = new UUID(im.binaryBucket, 1);
- m_log.DebugFormat("[AGENT INVENTORY]: Inserting item {0} "+
- "into agent {1}'s inventory",
- itemID, new UUID(im.toAgentID));
+ m_log.DebugFormat("[AGENT INVENTORY]: Inserting item {0} "+
+ "into agent {1}'s inventory",
+ itemID, new UUID(im.toAgentID));
- InventoryItemBase itemCopy = scene.GiveInventoryItem(
- new UUID(im.toAgentID),
- client.AgentId, itemID);
+ InventoryItemBase itemCopy = scene.GiveInventoryItem(
+ new UUID(im.toAgentID),
+ client.AgentId, itemID);
- if (itemCopy == null)
- {
- client.SendAgentAlertMessage("Can't find item to give. Nothing given.", false);
- return;
+ if (itemCopy == null)
+ {
+ client.SendAgentAlertMessage("Can't find item to give. Nothing given.", false);
+ return;
+ }
+
+ copyID = itemCopy.ID;
+ Array.Copy(copyID.GetBytes(), 0, im.binaryBucket, 1, 16);
+
+ if (user != null && !user.IsChildAgent)
+ {
+ user.ControllingClient.SendBulkUpdateInventory(itemCopy);
+ }
}
- byte[] itemCopyID = itemCopy.ID.GetBytes();
-
- Array.Copy(itemCopyID, 0, im.binaryBucket, 1, 16);
-
// Send the IM to the recipient. The item is already
// in their inventory, so it will not be lost if
// they are offline.
//
if (user != null && !user.IsChildAgent)
{
- // User is online. So, let's make the item visible
- //
- user.ControllingClient.SendBulkUpdateInventory(itemCopy);
-
// And notify. Transaction ID is the item ID. We get that
// same ID back on the reply so we know what to act on
//
@@ -179,7 +222,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
new UUID(im.fromAgentID), im.message,
new UUID(im.toAgentID),
im.fromAgentName, im.dialog, im.timestamp,
- itemCopy.ID, false, im.binaryBucket);
+ copyID, false, im.binaryBucket);
return;
}
@@ -208,9 +251,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
}
}
else if (im.dialog == (byte) InstantMessageDialog.InventoryDeclined)
- {
- UUID itemID = new UUID(im.imSessionID); // The item, back from it's trip
-
+ {
// Here, the recipient is local and we can assume that the
// inventory is loaded. Courtesy of the above bulk update,
// It will have been pushed to the client, too
@@ -224,27 +265,43 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
{
InventoryFolderImpl trashFolder =
userInfo.FindFolderForType((int)AssetType.TrashFolder);
-
- InventoryItemBase item =
- userInfo.RootFolder.FindItem(itemID);
-
- if (trashFolder != null && item != null)
+
+ UUID inventoryEntityID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
+
+ InventoryItemBase item = userInfo.RootFolder.FindItem(inventoryEntityID);
+ InventoryFolderBase folder = null;
+
+ if (item != null && trashFolder != null)
{
item.Folder = trashFolder.ID;
- userInfo.DeleteItem(itemID);
+ userInfo.DeleteItem(inventoryEntityID);
scene.AddInventoryItem(client, item);
}
else
{
- string reason = "";
+ folder = userInfo.RootFolder.FindFolder(inventoryEntityID);
+
+ if (folder != null & trashFolder != null)
+ {
+ userInfo.MoveFolder(inventoryEntityID, trashFolder.ID);
+ }
+ }
+
+ if ((null == item && null == folder) | null == trashFolder)
+ {
+ string reason = String.Empty;
+
if (trashFolder == null)
reason += " Trash folder not found.";
if (item == null)
reason += " Item not found.";
+ if (folder == null)
+ reason += " Folder not found.";
+
client.SendAgentAlertMessage("Unable to delete "+
- "received item" + reason, false);
+ "received inventory" + reason, false);
}
}
diff --git a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
index b5a5123..fb1d1ff 100644
--- a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
+++ b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
@@ -624,6 +624,9 @@ namespace OpenSim.Region.Environment.Modules.World.NPC
public virtual void SendBulkUpdateInventory(InventoryItemBase item)
{
}
+
+ public virtual void SendBulkUpdateInventory(InventoryFolderBase folderBase)
+ {}
public void SendTakeControls(int controls, bool passToAgent, bool TakeControls)
{
diff --git a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
index d1e0c24..3d6a905 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
@@ -400,11 +400,11 @@ namespace OpenSim.Region.Environment.Scenes
}
///
- /// Give an inventory item from one avatar to another
+ /// Give an inventory item from one user to another
///
///
/// ID of the sender of the item
- ///
+ ///
public virtual void GiveInventoryItem(IClientAPI recipientClient, UUID senderId, UUID itemId)
{
InventoryItemBase itemCopy = GiveInventoryItem(recipientClient.AgentId, senderId, itemId);
@@ -413,8 +413,34 @@ namespace OpenSim.Region.Environment.Scenes
recipientClient.SendBulkUpdateInventory(itemCopy);
}
+ ///
+ /// Give an inventory item from one user to another
+ ///
+ ///
+ /// ID of the sender of the item
+ ///
+ /// The inventory item copy given, null if the give was unsuccessful
public virtual InventoryItemBase GiveInventoryItem(UUID recipient, UUID senderId, UUID itemId)
{
+ return GiveInventoryItem(recipient, senderId, itemId, UUID.Zero);
+ }
+
+ ///
+ /// Give an inventory item from one user to another
+ ///
+ ///
+ /// ID of the sender of the item
+ ///
+ ///
+ /// The id of the folder in which the copy item should go. If UUID.Zero then the item is placed in the most
+ /// appropriate default folder.
+ ///
+ ///
+ /// The inventory item copy given, null if the give was unsuccessful
+ ///
+ public virtual InventoryItemBase GiveInventoryItem(
+ UUID recipient, UUID senderId, UUID itemId, UUID recipientFolderId)
+ {
// Retrieve the item from the sender
CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId);
@@ -438,7 +464,6 @@ namespace OpenSim.Region.Environment.Scenes
return null;
}
- // TODO get recipient's root folder
CachedUserInfo recipientUserInfo
= CommsManager.UserProfileCacheService.GetUserDetails(recipient);
@@ -457,7 +482,8 @@ namespace OpenSim.Region.Environment.Scenes
itemCopy.Name = item.Name;
itemCopy.AssetType = item.AssetType;
itemCopy.InvType = item.InvType;
- itemCopy.Folder = UUID.Zero;
+ itemCopy.Folder = recipientFolderId;
+
if (Permissions.PropagatePermissions())
{
if (item.InvType == 6)
@@ -529,8 +555,93 @@ namespace OpenSim.Region.Environment.Scenes
m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemId.ToString() + ", no root folder");
return null;
}
+
return null;
}
+
+ ///
+ /// Give an entire inventory folder from one user to another. The entire contents (including all descendent
+ /// folders) is given.
+ ///
+ ///
+ /// ID of the sender of the item
+ ///
+ ///
+ /// The id of the receipient folder in which the send folder should be placed. If UUID.Zero then the
+ /// recipient folder is the root folder
+ ///
+ ///
+ /// The inventory folder copy given, null if the copy was unsuccessful
+ ///
+ public virtual InventoryFolderImpl GiveInventoryFolder(
+ UUID recipientId, UUID senderId, UUID folderId, UUID recipientParentFolderId)
+ {
+ // Retrieve the folder from the sender
+ CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId);
+
+ if (null == senderUserInfo)
+ {
+ m_log.ErrorFormat(
+ "[AGENT INVENTORY]: Failed to find sending user {0} for folder {1}", senderId, folderId);
+
+ return null;
+ }
+
+ if (!senderUserInfo.HasReceivedInventory)
+ {
+ m_log.DebugFormat(
+ "[AGENT INVENTORY]: Could not give inventory folder - have not yet received inventory for {0}",
+ senderId);
+
+ return null;
+ }
+
+ InventoryFolderImpl folder = senderUserInfo.RootFolder.FindFolder(folderId);
+
+ if (null == folder)
+ {
+ m_log.ErrorFormat(
+ "[AGENT INVENTORY]: Could not find inventory folder {0} to give", folderId);
+
+ return null;
+ }
+
+ CachedUserInfo recipientUserInfo
+ = CommsManager.UserProfileCacheService.GetUserDetails(recipientId);
+
+ if (null == recipientUserInfo)
+ {
+ m_log.ErrorFormat(
+ "[AGENT INVENTORY]: Failed to find receiving user {0} for folder {1}", recipientId, folderId);
+
+ return null;
+ }
+
+ if (recipientParentFolderId == UUID.Zero)
+ recipientParentFolderId = recipientUserInfo.RootFolder.ID;
+
+ UUID newFolderId = UUID.Random();
+ recipientUserInfo.CreateFolder(folder.Name, newFolderId, (ushort)folder.Type, recipientParentFolderId);
+
+ // XXX: Messy - we should really get this back in the CreateFolder call
+ InventoryFolderImpl copiedFolder = recipientUserInfo.RootFolder.FindFolder(newFolderId);
+
+ // Give all the subfolders
+ List subFolders = folder.RequestListOfFolderImpls();
+ foreach (InventoryFolderImpl childFolder in subFolders)
+ {
+ GiveInventoryFolder(recipientId, senderId, childFolder.ID, copiedFolder.ID);
+ }
+
+ // Give all the items
+ List items = folder.RequestListOfItems();
+ foreach (InventoryItemBase item in items)
+ {
+ GiveInventoryItem(recipientId, senderId, item.ID, copiedFolder.ID);
+ }
+
+ return copiedFolder;
+ }
public void CopyInventoryItem(IClientAPI remoteClient, uint callbackID, UUID oldAgentID, UUID oldItemID,
UUID newFolderID, string newName)
diff --git a/OpenSim/Region/Environment/Scenes/Tests/TestClient.cs b/OpenSim/Region/Environment/Scenes/Tests/TestClient.cs
index 69e45bb..cecb115 100644
--- a/OpenSim/Region/Environment/Scenes/Tests/TestClient.cs
+++ b/OpenSim/Region/Environment/Scenes/Tests/TestClient.cs
@@ -538,6 +538,9 @@ namespace OpenSim.Region.Environment.Scenes.Tests
public virtual void SendBulkUpdateInventory(InventoryItemBase item)
{
}
+
+ public void SendBulkUpdateInventory(InventoryFolderBase folderBase)
+ {}
public UUID GetDefaultAnimation(string name)
{
@@ -561,8 +564,8 @@ namespace OpenSim.Region.Environment.Scenes.Tests
int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, int PricePublicObjectDecay,
int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, float TeleportPriceExponent)
{
-
}
+
public virtual void SendNameReply(UUID profileId, string firstname, string lastname)
{
}
diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
index 89b3bf6..f1a61bb 100644
--- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
+++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
@@ -537,6 +537,9 @@ namespace OpenSim.Region.Examples.SimpleModule
public virtual void SendBulkUpdateInventory(InventoryItemBase item)
{
}
+
+ public void SendBulkUpdateInventory(InventoryFolderBase folderBase)
+ {}
public UUID GetDefaultAnimation(string name)
{
--
cgit v1.1