diff --git a/OpenSim/Framework/Communications/Cache/CachedUserInfo.cs b/OpenSim/Framework/Communications/Cache/CachedUserInfo.cs index 59e5b6e573..f2dd2bf5b2 100644 --- a/OpenSim/Framework/Communications/Cache/CachedUserInfo.cs +++ b/OpenSim/Framework/Communications/Cache/CachedUserInfo.cs @@ -25,6 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.Collections.Generic; + using libsecondlife; namespace OpenSim.Framework.Communications.Cache @@ -35,50 +38,135 @@ namespace OpenSim.Framework.Communications.Cache = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private readonly CommunicationsManager m_parentCommsManager; - // Fields + + // FIXME: These need to be hidden behind accessors public InventoryFolderImpl RootFolder = null; public UserProfileData UserProfile = null; + + /// + /// Stores received folders for which we have not yet received the parents. + /// + private IDictionary> pendingCategorizationFolders + = new Dictionary>(); public CachedUserInfo(CommunicationsManager commsManager) { m_parentCommsManager = commsManager; } - - // Methods - public void FolderReceive(LLUUID userID, InventoryFolderImpl folderInfo) + + /// + /// Store a folder pending categorization when its parent is received. + /// + /// + private void AddPendingFolder(InventoryFolderImpl folder) { - //m_log.DebugFormat("[INVENTORY CACHE]: Received folder {0} {1} for user {2}", folderInfo.name, folderInfo.folderID, userID); + LLUUID parentFolderId = folder.parentID; - if (userID == UserProfile.UUID) + if (pendingCategorizationFolders.ContainsKey(parentFolderId)) { - if (RootFolder == null) + pendingCategorizationFolders[parentFolderId].Add(folder); + } + else + { + IList folders = new List(); + folders.Add(folder); + + pendingCategorizationFolders[parentFolderId] = folders; + } + } + + /// + /// Add any pending folders which are children of parent + /// + /// + /// A + /// + private void ResolvePendingFolders(InventoryFolderImpl parent) + { + if (pendingCategorizationFolders.ContainsKey(parent.folderID)) + { + foreach (InventoryFolderImpl folder in pendingCategorizationFolders[parent.folderID]) { - if (folderInfo.parentID == LLUUID.Zero) +// m_log.DebugFormat( +// "[INVENTORY CACHE]: Resolving pending received folder {0} {1} into {2} {3}", +// folder.name, folder.folderID, parent.name, parent.folderID); + + if (!parent.SubFolders.ContainsKey(folder.folderID)) { - RootFolder = folderInfo; - } - } - else if (RootFolder.folderID == folderInfo.parentID) - { - if (!RootFolder.SubFolders.ContainsKey(folderInfo.folderID)) - { - RootFolder.SubFolders.Add(folderInfo.folderID, folderInfo); - } - } - else - { - InventoryFolderImpl folder = RootFolder.HasSubFolder(folderInfo.parentID); - if (folder != null) - { - if (!folder.SubFolders.ContainsKey(folderInfo.folderID)) - { - folder.SubFolders.Add(folderInfo.folderID, folderInfo); - } - } + parent.SubFolders.Add(folder.folderID, folder); + } } } } + /// + /// Callback invoked when a folder is received from an async request to the inventory service. + /// + /// + /// + public void FolderReceive(LLUUID userID, InventoryFolderImpl folderInfo) + { + // FIXME: Exceptions thrown upwards never appear on the console. Could fix further up if these + // are simply being swallowed + try + { +// m_log.DebugFormat( +// "[INVENTORY CACHE]: Received folder {0} {1} for user {2}", +// folderInfo.name, folderInfo.folderID, userID); + + if (userID == UserProfile.UUID) + { + if (RootFolder == null) + { + if (folderInfo.parentID == LLUUID.Zero) + { + RootFolder = folderInfo; + } + } + else if (RootFolder.folderID == folderInfo.parentID) + { + if (!RootFolder.SubFolders.ContainsKey(folderInfo.folderID)) + { + RootFolder.SubFolders.Add(folderInfo.folderID, folderInfo); + } + else + { + AddPendingFolder(folderInfo); + } + } + else + { + InventoryFolderImpl folder = RootFolder.HasSubFolder(folderInfo.parentID); + if (folder != null) + { + if (!folder.SubFolders.ContainsKey(folderInfo.folderID)) + { + folder.SubFolders.Add(folderInfo.folderID, folderInfo); + } + } + else + { + AddPendingFolder(folderInfo); + } + } + + ResolvePendingFolders(folderInfo); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY CACHE] {0}", e); + } + } + + /// + /// Callback invoked when an item is received from an async request to the inventory service. + /// + /// FIXME: We're assuming here that items are always received after all the folders have been + /// received. + /// + /// + /// public void ItemReceive(LLUUID userID, InventoryItemBase itemInfo) { if ((userID == UserProfile.UUID) && (RootFolder != null)) diff --git a/OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs b/OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs index 67022c7931..c3f51da44d 100644 --- a/OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs +++ b/OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs @@ -27,7 +27,10 @@ using System; using System.Collections.Generic; +using System.Threading; + using libsecondlife; + using OpenSim.Framework.Console; namespace OpenSim.Framework.Communications.Cache @@ -65,9 +68,7 @@ namespace OpenSim.Framework.Communications.Cache if (userInfo.UserProfile != null) { - // The request itself will occur when the agent finishes logging on to the region - // so there's no need to do it here. - //RequestInventoryForUser(userID, userInfo); + // The inventory will be populated when the user actually enters the scene m_userProfiles.Add(userID, userInfo); } else @@ -219,10 +220,34 @@ namespace OpenSim.Framework.Communications.Cache CachedUserInfo userProfile; if (m_userProfiles.TryGetValue(remoteClient.AgentId, out userProfile)) { + // XXX: When a client crosses into a scene, their entire inventory is fetched + // asynchronously. However, if the client is logging on and does not have a cached root + // folder, then the root folder request usually comes in *before* the async completes, leading to + // inventory failure. + // + // This is a crude way of dealing with that by retrying the lookup. + if (userProfile.RootFolder == null) + { + int attempts = 5; + while (attempts-- > 0) + { + Thread.Sleep(3000); + + if (userProfile.RootFolder != null) + { + break; + } + } + } + if (userProfile.RootFolder != null) { if (userProfile.RootFolder.folderID == folderID) { +// m_log.DebugFormat( +// "[AGENT INVENTORY]: Found root folder {0} for client {1}", +// folderID, remoteClient.AgentId); + remoteClient.SendInventoryFolderDetails( remoteClient.AgentId, folderID, userProfile.RootFolder.RequestListOfItems(), userProfile.RootFolder.RequestListOfFolders(), @@ -234,6 +259,10 @@ namespace OpenSim.Framework.Communications.Cache { if ((fold = userProfile.RootFolder.HasSubFolder(folderID)) != null) { +// m_log.DebugFormat( +// "[AGENT INVENTORY]: Found folder {0} for client {1}", +// folderID, remoteClient.AgentId); + remoteClient.SendInventoryFolderDetails( remoteClient.AgentId, folderID, fold.RequestListOfItems(), fold.RequestListOfFolders(), fetchFolders, fetchItems); diff --git a/OpenSim/Framework/Communications/InventoryServiceBase.cs b/OpenSim/Framework/Communications/InventoryServiceBase.cs index 6e909dae18..5515c77063 100644 --- a/OpenSim/Framework/Communications/InventoryServiceBase.cs +++ b/OpenSim/Framework/Communications/InventoryServiceBase.cs @@ -76,12 +76,27 @@ namespace OpenSim.Framework.Communications #region IInventoryServices methods - // See IInventoryServices + /// + /// Guid to UUID wrapper for same name IInventoryServices method + /// + /// + /// public List RequestFirstLevelFolders(Guid rawUserID) { LLUUID userID = new LLUUID(rawUserID); return RequestFirstLevelFolders(userID); } + + /// + /// Guid to UUID wrapper for same name IInventoryServices method + /// + /// + /// + public List GetInventorySkeleton(Guid rawUserID) + { + LLUUID userID = new LLUUID(rawUserID); + return GetInventorySkeleton(userID); + } // See IInventoryServices public List RequestFirstLevelFolders(LLUUID userID) @@ -112,7 +127,7 @@ namespace OpenSim.Framework.Communications // See IInventoryServices public List GetInventorySkeleton(LLUUID userId) { -// m_log.DebugFormat("[AGENT INVENTORY]: Getting inventory skeleton for {0}", userId); + m_log.DebugFormat("[AGENT INVENTORY]: Getting inventory skeleton for {0}", userId); List userFolders = new List(); @@ -173,7 +188,7 @@ namespace OpenSim.Framework.Communications if (null != existingRootFolder) { - m_log.ErrorFormat("[AGENTINVENTORY]: " + + m_log.ErrorFormat("[AGENT INVENTORY]: " + "Did not create a new inventory for user {0} since they already have " + "a root inventory folder with id {1}", user, existingRootFolder); } diff --git a/OpenSim/Framework/Communications/LoginService.cs b/OpenSim/Framework/Communications/LoginService.cs index e738d0b54e..cbe8783ae9 100644 --- a/OpenSim/Framework/Communications/LoginService.cs +++ b/OpenSim/Framework/Communications/LoginService.cs @@ -272,7 +272,7 @@ namespace OpenSim.Framework.UserManagement } catch (Exception e) { - m_log.Info("[LOGIN]: Login failed, exception" + e.ToString()); + m_log.Info("[LOGIN]: Login failed, " + e.ToString()); } } diff --git a/OpenSim/Grid/InventoryServer/GridInventoryService.cs b/OpenSim/Grid/InventoryServer/GridInventoryService.cs index ea85d5f570..cfe06e8d84 100644 --- a/OpenSim/Grid/InventoryServer/GridInventoryService.cs +++ b/OpenSim/Grid/InventoryServer/GridInventoryService.cs @@ -108,8 +108,7 @@ namespace OpenSim.Grid.InventoryServer { LLUUID userID = new LLUUID(rawUserID); - // We get enough verbose messages later on for diagnostics - //m_log.Info("[INVENTORY]: Request for inventory for " + userID.ToString()); + m_log.Info("[GRID INVENTORY]: Request for inventory of " + userID.ToString()); InventoryCollection invCollection = new InventoryCollection(); List folders; @@ -120,6 +119,21 @@ namespace OpenSim.Grid.InventoryServer invCollection.Folders = folders; invCollection.UserID = userID; } + +// foreach (InventoryFolderBase folder in folders) +// { +// m_log.DebugFormat( +// "[GRID INVENTORY]: Sending back folder {0}, {1}", +// folder.name, folder.folderID); +// } +// +// foreach (InventoryItemBase item in allItems) +// { +// m_log.DebugFormat( +// "[GRID INVENTORY]: Sending back item {0}, {1}, folder {2}", +// item.inventoryName, item.inventoryID, item.parentFolderID); +// } + return invCollection; } diff --git a/OpenSim/Grid/InventoryServer/Main.cs b/OpenSim/Grid/InventoryServer/Main.cs index ea056a0591..009f3f1a24 100644 --- a/OpenSim/Grid/InventoryServer/Main.cs +++ b/OpenSim/Grid/InventoryServer/Main.cs @@ -83,29 +83,32 @@ namespace OpenSim.Grid.InventoryServer protected void AddHttpHandlers() { m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/GetInventory/", - m_inventoryService.GetUserInventory)); + new RestDeserialisehandler( + "POST", "/GetInventory/", m_inventoryService.GetUserInventory)); + m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/CreateInventory/", - m_inventoryService.CreateUsersInventory)); + new RestDeserialisehandler( + "POST", "/CreateInventory/", m_inventoryService.CreateUsersInventory)); + m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/NewFolder/", - m_inventoryService.AddInventoryFolder)); + new RestDeserialisehandler( + "POST", "/NewFolder/", m_inventoryService.AddInventoryFolder)); m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/MoveFolder/", - m_inventoryService.MoveInventoryFolder)); + new RestDeserialisehandler( + "POST", "/MoveFolder/", m_inventoryService.MoveInventoryFolder)); m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/NewItem/", - m_inventoryService.AddInventoryItem)); + new RestDeserialisehandler( + "POST", "/NewItem/", m_inventoryService.AddInventoryItem)); + m_httpServer.AddStreamHandler( - new RestDeserialisehandler("POST", "/DeleteItem/", - m_inventoryService.DeleteInvItem)); + new RestDeserialisehandler( + "POST", "/DeleteItem/", m_inventoryService.DeleteInvItem)); m_httpServer.AddStreamHandler( - new RestDeserialisehandler>("POST", "/RootFolders/", - m_inventoryService.RequestFirstLevelFolders)); + new RestDeserialisehandler> + ("POST", "/RootFolders/", m_inventoryService.GetInventorySkeleton)); // httpServer.AddStreamHandler(new InventoryManager.GetInventory(m_inventoryManager)); } diff --git a/OpenSim/Grid/UserServer/UserLoginService.cs b/OpenSim/Grid/UserServer/UserLoginService.cs index e95acac22c..6d3a081fc3 100644 --- a/OpenSim/Grid/UserServer/UserLoginService.cs +++ b/OpenSim/Grid/UserServer/UserLoginService.cs @@ -323,6 +323,8 @@ namespace OpenSim.Grid.UserServer Hashtable TempHash; foreach (InventoryFolderBase InvFolder in folders) { +// m_log.DebugFormat("[LOGIN]: Received agent inventory folder {0}", InvFolder.name); + if (InvFolder.parentID == LLUUID.Zero) { rootID = InvFolder.folderID; diff --git a/OpenSim/Region/ClientStack/ClientView.cs b/OpenSim/Region/ClientStack/ClientView.cs index 971c2951a6..a8762e4b08 100644 --- a/OpenSim/Region/ClientStack/ClientView.cs +++ b/OpenSim/Region/ClientStack/ClientView.cs @@ -1148,10 +1148,7 @@ namespace OpenSim.Region.ClientStack public void SendInventoryFolderDetails(LLUUID ownerID, LLUUID folderID, List items, List folders, bool fetchFolders, bool fetchItems) - { - // XXX Very temporarily, always fetch the folders - fetchFolders = true; - + { // An inventory descendents packet consists of a single agent section and an inventory details // section for each inventory item. The size of each inventory item is approximately 550 bytes. // In theory, UDP has a maximum packet size of 64k, so it should be possible to send descendent