* 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
0.6.1-post-fixes
Justin Clarke Casey 2008-12-04 19:57:36 +00:00
parent 1bd92a6d34
commit 38ca31b37a
9 changed files with 381 additions and 42 deletions

View File

@ -556,8 +556,10 @@ namespace OpenSim.Framework.Communications.Cache
} }
/// <summary> /// <summary>
/// Add an item to the user's inventory /// Add an item to the user's inventory.
/// </summary> /// </summary>
/// If the item has no folder set (i.e. it is UUID.Zero), then it is placed in the most appropriate folder
/// for that type.
/// <param name="itemInfo"></param> /// <param name="itemInfo"></param>
public void AddItem(InventoryItemBase item) public void AddItem(InventoryItemBase item)
{ {
@ -572,6 +574,7 @@ namespace OpenSim.Framework.Communications.Cache
item.Folder = RootFolder.ID; item.Folder = RootFolder.ID;
} }
ItemReceive(item, null); ItemReceive(item, null);
if (m_commsManager.SecureInventoryService != null) if (m_commsManager.SecureInventoryService != null)
{ {
m_commsManager.SecureInventoryService.AddItem(item, m_session_id); m_commsManager.SecureInventoryService.AddItem(item, m_session_id);

View File

@ -358,6 +358,10 @@ namespace OpenSim.Framework.Communications.Cache
return folderList; return folderList;
} }
/// <value>
/// The total number of items in this folder and in the immediate child folders (though not from other
/// descendants).
/// </value>
public int TotalCount public int TotalCount
{ {
get get

View File

@ -828,6 +828,13 @@ namespace OpenSim.Framework
void SendTaskInventory(UUID taskID, short serial, byte[] fileName); void SendTaskInventory(UUID taskID, short serial, byte[] fileName);
/// <summary>
/// Used by the server to inform the client of new inventory items. Will transfer the contents of the folder
/// (including all descendent folders) as well as the folder itself.
/// </summary>
/// <param name="folder"></param>
void SendBulkUpdateInventory(InventoryFolderBase folder);
/// <summary> /// <summary>
/// Used by the server to inform the client of a new inventory item. Used when transferring items /// Used by the server to inform the client of a new inventory item. Used when transferring items
/// between avatars, possibly among other things. /// between avatars, possibly among other things.

View File

@ -1144,6 +1144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
msg.MessageBlock.Message = Utils.StringToBytes(message); msg.MessageBlock.Message = Utils.StringToBytes(message);
msg.MessageBlock.BinaryBucket = binaryBucket; msg.MessageBlock.BinaryBucket = binaryBucket;
System.Console.WriteLine("SendInstantMessage: " + msg);
OutPacket(msg, ThrottleOutPacketType.Task); OutPacket(msg, ThrottleOutPacketType.Task);
} }
} }
@ -1764,6 +1765,153 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(inventoryReply, ThrottleOutPacketType.Asset); OutPacket(inventoryReply, ThrottleOutPacketType.Asset);
} }
/// <see>IClientAPI.SendBulkUpdateInventory(InventoryFolderBase)</see>
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<BulkUpdateInventoryPacket.FolderDataBlock> folderDataBlocks
= new List<BulkUpdateInventoryPacket.FolderDataBlock>();
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);
}
}
/// <summary>
/// Recursively construct bulk update packets to send folders and items
/// </summary>
/// <param name="folder"></param>
/// <param name="folderDataBlocks"></param>
/// <param name="transactionId"></param>
private void SendBulkUpdateInventoryRecursive(
InventoryFolderImpl folder, ref List<BulkUpdateInventoryPacket.FolderDataBlock> 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<InventoryItemBase> 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<BulkUpdateInventoryPacket.FolderDataBlock>();
// 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<InventoryFolderImpl> subFolders = folder.RequestListOfFolderImpls();
foreach (InventoryFolderImpl subFolder in subFolders)
{
SendBulkUpdateInventoryRecursive(subFolder, ref folderDataBlocks, transactionId);
}
}
/// <summary>
/// Generate a bulk update inventory data block for the given folder
/// </summary>
/// <param name="folder"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Generate a bulk update inventory data block for the given item
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
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;
}
/// <see>IClientAPI.SendBulkUpdateInventory(InventoryItemBase)</see> /// <see>IClientAPI.SendBulkUpdateInventory(InventoryItemBase)</see>
public void SendBulkUpdateInventory(InventoryItemBase item) public void SendBulkUpdateInventory(InventoryItemBase item)
{ {

View File

@ -136,9 +136,51 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
if (im.dialog == (byte) InstantMessageDialog.InventoryOffered) if (im.dialog == (byte) InstantMessageDialog.InventoryOffered)
{ {
ScenePresence user = m_log.DebugFormat("Asset type {0}", ((AssetType)im.binaryBucket[0]));
scene.GetScenePresence(new UUID(im.toAgentID));
ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID));
UUID copyID;
// 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 // First byte of the array is probably the item type
// Next 16 bytes are the UUID // Next 16 bytes are the UUID
@ -158,9 +200,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
return; return;
} }
byte[] itemCopyID = itemCopy.ID.GetBytes(); copyID = itemCopy.ID;
Array.Copy(copyID.GetBytes(), 0, im.binaryBucket, 1, 16);
Array.Copy(itemCopyID, 0, im.binaryBucket, 1, 16); if (user != null && !user.IsChildAgent)
{
user.ControllingClient.SendBulkUpdateInventory(itemCopy);
}
}
// Send the IM to the recipient. The item is already // Send the IM to the recipient. The item is already
// in their inventory, so it will not be lost if // in their inventory, so it will not be lost if
@ -168,10 +215,6 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
// //
if (user != null && !user.IsChildAgent) 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 // And notify. Transaction ID is the item ID. We get that
// same ID back on the reply so we know what to act on // 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.fromAgentID), im.message,
new UUID(im.toAgentID), new UUID(im.toAgentID),
im.fromAgentName, im.dialog, im.timestamp, im.fromAgentName, im.dialog, im.timestamp,
itemCopy.ID, false, im.binaryBucket); copyID, false, im.binaryBucket);
return; return;
} }
@ -209,8 +252,6 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
} }
else if (im.dialog == (byte) InstantMessageDialog.InventoryDeclined) 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 // Here, the recipient is local and we can assume that the
// inventory is loaded. Courtesy of the above bulk update, // inventory is loaded. Courtesy of the above bulk update,
// It will have been pushed to the client, too // It will have been pushed to the client, too
@ -225,26 +266,42 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Inventory.Transfer
InventoryFolderImpl trashFolder = InventoryFolderImpl trashFolder =
userInfo.FindFolderForType((int)AssetType.TrashFolder); userInfo.FindFolderForType((int)AssetType.TrashFolder);
InventoryItemBase item = UUID inventoryEntityID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
userInfo.RootFolder.FindItem(itemID);
if (trashFolder != null && item != null) InventoryItemBase item = userInfo.RootFolder.FindItem(inventoryEntityID);
InventoryFolderBase folder = null;
if (item != null && trashFolder != null)
{ {
item.Folder = trashFolder.ID; item.Folder = trashFolder.ID;
userInfo.DeleteItem(itemID); userInfo.DeleteItem(inventoryEntityID);
scene.AddInventoryItem(client, item); scene.AddInventoryItem(client, item);
} }
else 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) if (trashFolder == null)
reason += " Trash folder not found."; reason += " Trash folder not found.";
if (item == null) if (item == null)
reason += " Item not found."; reason += " Item not found.";
if (folder == null)
reason += " Folder not found.";
client.SendAgentAlertMessage("Unable to delete "+ client.SendAgentAlertMessage("Unable to delete "+
"received item" + reason, false); "received inventory" + reason, false);
} }
} }

View File

@ -625,6 +625,9 @@ namespace OpenSim.Region.Environment.Modules.World.NPC
{ {
} }
public virtual void SendBulkUpdateInventory(InventoryFolderBase folderBase)
{}
public void SendTakeControls(int controls, bool passToAgent, bool TakeControls) public void SendTakeControls(int controls, bool passToAgent, bool TakeControls)
{ {
} }

View File

@ -400,7 +400,7 @@ namespace OpenSim.Region.Environment.Scenes
} }
/// <summary> /// <summary>
/// Give an inventory item from one avatar to another /// Give an inventory item from one user to another
/// </summary> /// </summary>
/// <param name="recipientClient"></param> /// <param name="recipientClient"></param>
/// <param name="senderId">ID of the sender of the item</param> /// <param name="senderId">ID of the sender of the item</param>
@ -413,7 +413,33 @@ namespace OpenSim.Region.Environment.Scenes
recipientClient.SendBulkUpdateInventory(itemCopy); recipientClient.SendBulkUpdateInventory(itemCopy);
} }
/// <summary>
/// Give an inventory item from one user to another
/// </summary>
/// <param name="recipient"></param>
/// <param name="senderId">ID of the sender of the item</param>
/// <param name="itemId"></param>
/// <returns>The inventory item copy given, null if the give was unsuccessful</returns>
public virtual InventoryItemBase GiveInventoryItem(UUID recipient, UUID senderId, UUID itemId) public virtual InventoryItemBase GiveInventoryItem(UUID recipient, UUID senderId, UUID itemId)
{
return GiveInventoryItem(recipient, senderId, itemId, UUID.Zero);
}
/// <summary>
/// Give an inventory item from one user to another
/// </summary>
/// <param name="recipient"></param>
/// <param name="senderId">ID of the sender of the item</param>
/// <param name="itemId"></param>
/// <param name="recipientFolderId">
/// 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.
/// </param>
/// <returns>
/// The inventory item copy given, null if the give was unsuccessful
/// </returns>
public virtual InventoryItemBase GiveInventoryItem(
UUID recipient, UUID senderId, UUID itemId, UUID recipientFolderId)
{ {
// Retrieve the item from the sender // Retrieve the item from the sender
CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId); CachedUserInfo senderUserInfo = CommsManager.UserProfileCacheService.GetUserDetails(senderId);
@ -438,7 +464,6 @@ namespace OpenSim.Region.Environment.Scenes
return null; return null;
} }
// TODO get recipient's root folder
CachedUserInfo recipientUserInfo CachedUserInfo recipientUserInfo
= CommsManager.UserProfileCacheService.GetUserDetails(recipient); = CommsManager.UserProfileCacheService.GetUserDetails(recipient);
@ -457,7 +482,8 @@ namespace OpenSim.Region.Environment.Scenes
itemCopy.Name = item.Name; itemCopy.Name = item.Name;
itemCopy.AssetType = item.AssetType; itemCopy.AssetType = item.AssetType;
itemCopy.InvType = item.InvType; itemCopy.InvType = item.InvType;
itemCopy.Folder = UUID.Zero; itemCopy.Folder = recipientFolderId;
if (Permissions.PropagatePermissions()) if (Permissions.PropagatePermissions())
{ {
if (item.InvType == 6) if (item.InvType == 6)
@ -529,9 +555,94 @@ namespace OpenSim.Region.Environment.Scenes
m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemId.ToString() + ", no root folder"); m_log.Error("[AGENT INVENTORY]: Failed to find item " + itemId.ToString() + ", no root folder");
return null; return null;
} }
return null; return null;
} }
/// <summary>
/// Give an entire inventory folder from one user to another. The entire contents (including all descendent
/// folders) is given.
/// </summary>
/// <param name="recipientId"></param>
/// <param name="senderId">ID of the sender of the item</param>
/// <param name="folderId"></param>
/// <param name="recipientParentFolderId">
/// 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
/// </param>
/// <returns>
/// The inventory folder copy given, null if the copy was unsuccessful
/// </returns>
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<InventoryFolderImpl> subFolders = folder.RequestListOfFolderImpls();
foreach (InventoryFolderImpl childFolder in subFolders)
{
GiveInventoryFolder(recipientId, senderId, childFolder.ID, copiedFolder.ID);
}
// Give all the items
List<InventoryItemBase> 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, public void CopyInventoryItem(IClientAPI remoteClient, uint callbackID, UUID oldAgentID, UUID oldItemID,
UUID newFolderID, string newName) UUID newFolderID, string newName)
{ {

View File

@ -539,6 +539,9 @@ namespace OpenSim.Region.Environment.Scenes.Tests
{ {
} }
public void SendBulkUpdateInventory(InventoryFolderBase folderBase)
{}
public UUID GetDefaultAnimation(string name) public UUID GetDefaultAnimation(string name)
{ {
return UUID.Zero; return UUID.Zero;
@ -561,8 +564,8 @@ namespace OpenSim.Region.Environment.Scenes.Tests
int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, int PricePublicObjectDecay, int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, int PricePublicObjectDecay,
int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, float TeleportPriceExponent) int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, float TeleportPriceExponent)
{ {
} }
public virtual void SendNameReply(UUID profileId, string firstname, string lastname) public virtual void SendNameReply(UUID profileId, string firstname, string lastname)
{ {
} }

View File

@ -538,6 +538,9 @@ namespace OpenSim.Region.Examples.SimpleModule
{ {
} }
public void SendBulkUpdateInventory(InventoryFolderBase folderBase)
{}
public UUID GetDefaultAnimation(string name) public UUID GetDefaultAnimation(string name)
{ {
return UUID.Zero; return UUID.Zero;