summaryrefslogtreecommitdiff
path: root/indra/newview/llinventorymodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llinventorymodel.cpp')
-rwxr-xr-x[-rw-r--r--]indra/newview/llinventorymodel.cpp4041
1 files changed, 2004 insertions, 2037 deletions
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 9a8647aba4..f92332dea5 100644..100755
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -2,93 +2,78 @@
* @file llinventorymodel.cpp
* @brief Implementation of the inventory model used to track agent inventory.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
-#include "llinventorymodel.h"
+#include <typeinfo>
-#include "llassetstorage.h"
-#include "llcrc.h"
-#include "lldir.h"
-#include "llsys.h"
-#include "llxfermanager.h"
-#include "message.h"
+#include "llinventorymodel.h"
+#include "llaisapi.h"
#include "llagent.h"
#include "llagentwearables.h"
-#include "llfloater.h"
-#include "llfocusmgr.h"
+#include "llappearancemgr.h"
+#include "llclipboard.h"
+#include "llinventorypanel.h"
#include "llinventorybridge.h"
-#include "llfloaterinventory.h"
-#include "llviewerinventory.h"
+#include "llinventoryfunctions.h"
+#include "llinventoryobserver.h"
+#include "llinventorypanel.h"
+#include "llnotificationsutil.h"
+#include "llwindow.h"
+#include "llviewercontrol.h"
+#include "llpreview.h"
#include "llviewermessage.h"
+#include "llviewerfoldertype.h"
#include "llviewerwindow.h"
-#include "llviewerregion.h"
#include "llappviewer.h"
-#include "lldbstrings.h"
-#include "llviewerstats.h"
-#include "llmutelist.h"
-#include "llnotifications.h"
+#include "llviewerregion.h"
#include "llcallbacklist.h"
-#include "llpreview.h"
-#include "llviewercontrol.h"
#include "llvoavatarself.h"
+#include "llgesturemgr.h"
#include "llsdutil.h"
-#include <deque>
+#include "bufferarray.h"
+#include "bufferstream.h"
+#include "llcorehttputil.h"
//#define DIFF_INVENTORY_FILES
#ifdef DIFF_INVENTORY_FILES
#include "process.h"
#endif
-BOOL LLInventoryModel::sBackgroundFetchActive = FALSE;
-BOOL LLInventoryModel::sAllFoldersFetched = FALSE;
-BOOL LLInventoryModel::sFullFetchStarted = FALSE;
-S32 LLInventoryModel::sNumFetchRetries = 0;
-F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f;
-F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f;
-BOOL LLInventoryModel::sTimelyFetchPending = FALSE;
-LLFrameTimer LLInventoryModel::sFetchTimer;
-S16 LLInventoryModel::sBulkFetchCount = 0;
-
-// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue
-static std::deque<LLUUID> sFetchQueue;
+// Increment this if the inventory contents change in a non-backwards-compatible way.
+// For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect.
+const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
+BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE;
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
//BOOL decompress_file(const char* src_filename, const char* dst_filename);
-const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
-const S32 MAX_FETCH_RETRIES = 10;
-const char CACHE_FORMAT_STRING[] = "%s.inv";
+static const char CACHE_FORMAT_STRING[] = "%s.inv";
+static const char * const LOG_INV("Inventory");
struct InventoryIDPtrLess
{
@@ -126,17 +111,7 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
{
S32 descendents_server = c->getDescendentCount();
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- mModel->getDirectDescendentsOf(
- c->getUUID(),
- cats,
- items);
- S32 descendents_actual = 0;
- if(cats && items)
- {
- descendents_actual = cats->count() + items->count();
- }
+ S32 descendents_actual = c->getViewerDescendentCount();
if(descendents_server == descendents_actual)
{
mCachedCatIDs.insert(c->getUUID());
@@ -156,32 +131,67 @@ LLInventoryModel gInventory;
// Default constructor
LLInventoryModel::LLInventoryModel()
-: mModifyMask(LLInventoryObserver::ALL),
- mChangedItemIDs(),
+: // These are now ordered, keep them that way.
+ mBacklinkMMap(),
+ mIsAgentInvUsable(false),
+ mRootFolderID(),
+ mLibraryRootFolderID(),
+ mLibraryOwnerID(),
mCategoryMap(),
mItemMap(),
- mCategoryLock(),
- mItemLock(),
- mLastItem(NULL),
mParentChildCategoryTree(),
mParentChildItemTree(),
+ mLastItem(NULL),
+ mIsNotifyObservers(FALSE),
+ mModifyMask(LLInventoryObserver::ALL),
+ mChangedItemIDs(),
mObservers(),
- mRootFolderID(),
- mLibraryRootFolderID(),
- mLibraryOwnerID(),
- mIsAgentInvUsable(false)
-{
-}
+ mHttpRequestFG(NULL),
+ mHttpRequestBG(NULL),
+ mHttpOptions(NULL),
+ mHttpHeaders(NULL),
+ mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+ mHttpPriorityFG(0),
+ mHttpPriorityBG(0),
+ mCategoryLock(),
+ mItemLock()
+{}
+
// Destroys the object
LLInventoryModel::~LLInventoryModel()
{
+ cleanupInventory();
+}
+
+void LLInventoryModel::cleanupInventory()
+{
empty();
- for (observer_list_t::iterator iter = mObservers.begin();
- iter != mObservers.end(); ++iter)
+ // Deleting one observer might erase others from the list, so always pop off the front
+ while (!mObservers.empty())
+ {
+ observer_list_t::iterator iter = mObservers.begin();
+ LLInventoryObserver* observer = *iter;
+ mObservers.erase(iter);
+ delete observer;
+ }
+ mObservers.clear();
+
+ // Run down HTTP transport
+ if (mHttpHeaders)
+ {
+ mHttpHeaders->release();
+ mHttpHeaders = NULL;
+ }
+ if (mHttpOptions)
{
- delete *iter;
+ mHttpOptions->release();
+ mHttpOptions = NULL;
}
+ delete mHttpRequestFG;
+ mHttpRequestFG = NULL;
+ delete mHttpRequestBG;
+ mHttpRequestBG = NULL;
}
// This is a convenience function to check if one object has a parent
@@ -189,6 +199,8 @@ LLInventoryModel::~LLInventoryModel()
BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
const LLUUID& cat_id) const
{
+ if (obj_id == cat_id) return TRUE;
+
const LLInventoryObject* obj = getObject(obj_id);
while(obj)
{
@@ -208,6 +220,78 @@ BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
return FALSE;
}
+const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(const LLUUID& obj_id) const
+{
+ const LLInventoryObject* obj = getObject(obj_id);
+
+ // Search up the parent chain until we get to root or an acceptable folder.
+ // This assumes there are no cycles in the tree else we'll get a hang.
+ LLUUID parent_id = obj->getParentUUID();
+ while (!parent_id.isNull())
+ {
+ const LLViewerInventoryCategory *cat = getCategory(parent_id);
+ if (!cat) break;
+ const LLFolderType::EType folder_type = cat->getPreferredType();
+ if (folder_type != LLFolderType::FT_NONE &&
+ folder_type != LLFolderType::FT_ROOT_INVENTORY &&
+ !LLFolderType::lookupIsEnsembleType(folder_type))
+ {
+ return cat;
+ }
+ parent_id = cat->getParentUUID();
+ }
+ return NULL;
+}
+
+//
+// Search up the parent chain until we get to the specified parent, then return the first child category under it
+//
+const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const
+{
+ if (master_parent_id == obj_id)
+ {
+ return NULL;
+ }
+
+ const LLViewerInventoryCategory* current_cat = getCategory(obj_id);
+
+ if (current_cat == NULL)
+ {
+ current_cat = getCategory(getObject(obj_id)->getParentUUID());
+ }
+
+ while (current_cat != NULL)
+ {
+ const LLUUID& current_parent_id = current_cat->getParentUUID();
+
+ if (current_parent_id == master_parent_id)
+ {
+ return current_cat;
+ }
+
+ current_cat = getCategory(current_parent_id);
+ }
+
+ return NULL;
+}
+
+bool LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const
+{
+ LLInventoryObject *object = getObject(object_id);
+ while (object && object->getParentUUID().notNull())
+ {
+ LLInventoryObject *parent_object = getObject(object->getParentUUID());
+ if (!parent_object)
+ {
+ LL_WARNS(LOG_INV) << "unable to trace topmost ancestor, missing item for uuid " << object->getParentUUID() << LL_ENDL;
+ return false;
+ }
+ object = parent_object;
+ }
+ result = object->getUUID();
+ return true;
+}
+
// Get the object by id. Returns NULL if not found.
LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
{
@@ -279,6 +363,30 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
items = get_ptr_in_map(mParentChildItemTree, cat_id);
}
+LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
+{
+ LLInventoryModel::cat_array_t* cat_array;
+ LLInventoryModel::item_array_t* item_array;
+ getDirectDescendentsOf(cat_id,cat_array,item_array);
+ LLMD5 item_name_hash;
+ if (!item_array)
+ {
+ item_name_hash.finalize();
+ return item_name_hash;
+ }
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
+ iter != item_array->end();
+ iter++)
+ {
+ const LLViewerInventoryItem *item = (*iter);
+ if (!item)
+ continue;
+ item_name_hash.update(item->getName());
+ }
+ item_name_hash.finalize();
+ return item_name_hash;
+}
+
// SJB: Added version to lock the arrays to catch potential logic bugs
void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id,
cat_array_t*& categories,
@@ -301,70 +409,194 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id)
mItemLock[cat_id] = false;
}
-// findCategoryUUIDForType() returns the uuid of the category that
-// specifies 'type' as what it defaults to containing. The category is
-// not necessarily only for that type. *NOTE: This will create a new
-// inventory category on the fly if one does not exist.
-LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t, bool create_folder)
+void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::EType type)
{
- const LLUUID &rv = findCatUUID(t);
- if(rv.isNull() && isInventoryUsable() && create_folder)
- {
- const LLUUID &root_id = gInventory.getRootFolderID();
- if(root_id.notNull())
- {
- return createNewCategory(root_id, t, LLStringUtil::null);
- }
+ // Make a list of folders that are not "main_id" and are of "type"
+ std::vector<LLUUID> folder_ids;
+ for (cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ if ((cat->getPreferredType() == type) && (cat->getUUID() != main_id))
+ {
+ folder_ids.push_back(cat->getUUID());
+ }
+ }
+
+ // Iterate through those folders
+ for (std::vector<LLUUID>::iterator folder_ids_it = folder_ids.begin(); folder_ids_it != folder_ids.end(); ++folder_ids_it)
+ {
+ LLUUID folder_id = (*folder_ids_it);
+
+ // Get the content of this folder
+ cat_array_t* cats;
+ item_array_t* items;
+ getDirectDescendentsOf(folder_id, cats, items);
+
+ // Move all items to the main folder
+ // Note : we get the list of UUIDs and iterate on them instead of iterating directly on item_array_t
+ // elements. This is because moving elements modify the maps and, consequently, invalidate iterators on them.
+ // This "gather and iterate" method is verbose but resilient.
+ std::vector<LLUUID> list_uuids;
+ for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it)
+ {
+ list_uuids.push_back((*it)->getUUID());
+ }
+ for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it)
+ {
+ LLViewerInventoryItem* item = getItem(*it);
+ changeItemParent(item, main_id, TRUE);
+ }
+
+ // Move all folders to the main folder
+ list_uuids.clear();
+ for (cat_array_t::const_iterator it = cats->begin(); it != cats->end(); ++it)
+ {
+ list_uuids.push_back((*it)->getUUID());
+ }
+ for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = getCategory(*it);
+ changeCategoryParent(cat, main_id, TRUE);
+ }
+
+ // Purge the emptied folder
+ // Note: we'd like to use purgeObject() but it doesn't cleanly eliminate the folder
+ // which leads to issues further down the road when the folder is found again
+ //purgeObject(folder_id);
+ // We remove the folder and empty the trash instead which seems to work
+ removeCategory(folder_id);
+ gInventory.emptyFolderType("", LLFolderType::FT_TRASH);
}
- return rv;
}
-// Internal method which looks for a category with the specified
-// preferred type. Returns LLUUID::null if not found.
-const LLUUID &LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type) const
+const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
+ LLFolderType::EType preferred_type,
+ bool create_folder,
+ const LLUUID& root_id)
{
- const LLUUID &root_id = gInventory.getRootFolderID();
- if(LLAssetType::AT_CATEGORY == preferred_type)
+ LLUUID rv = LLUUID::null;
+ if(LLFolderType::FT_ROOT_INVENTORY == preferred_type)
{
- return root_id;
+ rv = root_id;
}
- if(root_id.notNull())
+ else if (root_id.notNull())
{
cat_array_t* cats = NULL;
cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
if(cats)
{
- S32 count = cats->count();
+ S32 count = cats->size();
for(S32 i = 0; i < count; ++i)
{
- if(cats->get(i)->getPreferredType() == preferred_type)
+ if(cats->at(i)->getPreferredType() == preferred_type)
{
- return cats->get(i)->getUUID();
+ const LLUUID& folder_id = cats->at(i)->getUUID();
+ if (rv.isNull() || folder_id < rv)
+ {
+ rv = folder_id;
+ }
}
}
}
}
- return LLUUID::null;
+
+ if(rv.isNull() && isInventoryUsable() && create_folder)
+ {
+ if(root_id.notNull())
+ {
+ return createNewCategory(root_id, preferred_type, LLStringUtil::null);
+ }
+ }
+ return rv;
+}
+
+// findCategoryUUIDForType() returns the uuid of the category that
+// specifies 'type' as what it defaults to containing. The category is
+// not necessarily only for that type. *NOTE: This will create a new
+// inventory category on the fly if one does not exist.
+const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder)
+{
+ return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID());
+}
+
+const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder)
+{
+ return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID());
}
+class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLCreateInventoryCategoryResponder);
+public:
+ LLCreateInventoryCategoryResponder(LLInventoryModel* model,
+ boost::optional<inventory_func_type> callback):
+ mModel(model),
+ mCallback(callback)
+ {
+ }
+
+protected:
+ virtual void httpFailure()
+ {
+ LL_WARNS(LOG_INV) << dumpResponse() << LL_ENDL;
+ }
+
+ virtual void httpSuccess()
+ {
+ //Server has created folder.
+ const LLSD& content = getContent();
+ if (!content.isMap() || !content.has("folder_id"))
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
+ return;
+ }
+ LLUUID category_id = content["folder_id"].asUUID();
+
+ LL_DEBUGS(LOG_INV) << ll_pretty_print_sd(content) << LL_ENDL;
+ // Add the category to the internal representation
+ LLPointer<LLViewerInventoryCategory> cat =
+ new LLViewerInventoryCategory( category_id,
+ content["parent_id"].asUUID(),
+ (LLFolderType::EType)content["type"].asInteger(),
+ content["name"].asString(),
+ gAgent.getID() );
+ cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
+ cat->setDescendentCount(0);
+ LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1);
+ mModel->accountForUpdate(update);
+ mModel->updateCategory(cat);
+
+ if (mCallback)
+ {
+ mCallback.get()(category_id);
+ }
+ }
+
+private:
+ boost::optional<inventory_func_type> mCallback;
+ LLInventoryModel* mModel;
+};
+
// Convenience function to create a new category. You could call
// updateCategory() with a newly generated UUID category, but this
// version will take care of details like what the name should be
// based on preferred type. Returns the UUID of the new category.
LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
- LLAssetType::EType preferred_type,
- const std::string& pname)
+ LLFolderType::EType preferred_type,
+ const std::string& pname,
+ boost::optional<inventory_func_type> callback)
{
+
LLUUID id;
if(!isInventoryUsable())
{
- llwarns << "Inventory is broken." << llendl;
+ LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;
return id;
}
- if(preferred_type == LLAssetType::AT_SIMSTATE)
+ if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())
{
- lldebugs << "Attempt to create simstate category." << llendl;
+ LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL;
return id;
}
@@ -376,7 +608,35 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
}
else
{
- name.assign(LLAssetType::lookupCategoryName(preferred_type));
+ name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
+ }
+
+ LLViewerRegion* viewer_region = gAgent.getRegion();
+ std::string url;
+ if ( viewer_region )
+ url = viewer_region->getCapability("CreateInventoryCategory");
+
+ if (!url.empty() && callback.get_ptr())
+ {
+ //Let's use the new capability.
+
+ LLSD request, body;
+ body["folder_id"] = id;
+ body["parent_id"] = parent_id;
+ body["type"] = (LLSD::Integer) preferred_type;
+ body["name"] = name;
+
+ request["message"] = "CreateInventoryCategory";
+ request["payload"] = body;
+
+ LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL;
+ // viewer_region->getCapAPI().post(request);
+ LLHTTPClient::post(
+ url,
+ body,
+ new LLCreateInventoryCategoryResponder(this, callback) );
+
+ return LLUUID::null;
}
// Add the category to the internal representation
@@ -403,7 +663,41 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
return id;
}
-// Starting with the object specified, add it's descendents to the
+// This is optimized for the case that we just want to know whether a
+// category has any immediate children meeting a condition, without
+// needing to recurse or build up any lists.
+bool LLInventoryModel::hasMatchingDirectDescendent(const LLUUID& cat_id,
+ LLInventoryCollectFunctor& filter)
+{
+ LLInventoryModel::cat_array_t *cats;
+ LLInventoryModel::item_array_t *items;
+ getDirectDescendentsOf(cat_id, cats, items);
+ if (cats)
+ {
+ for (LLInventoryModel::cat_array_t::const_iterator it = cats->begin();
+ it != cats->end(); ++it)
+ {
+ if (filter(*it,NULL))
+ {
+ return true;
+ }
+ }
+ }
+ if (items)
+ {
+ for (LLInventoryModel::item_array_t::const_iterator it = items->begin();
+ it != items->end(); ++it)
+ {
+ if (filter(NULL,*it))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Starting with the object specified, add its descendents to the
// array provided, but do not add the inventory object specified by
// id. There is no guaranteed order. Neither array will be erased
// before adding objects to it. Do not store a copy of the pointers
@@ -434,26 +728,25 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
cat_array_t& cats,
item_array_t& items,
BOOL include_trash,
- LLInventoryCollectFunctor& add,
- BOOL follow_folder_links)
+ LLInventoryCollectFunctor& add)
{
// Start with categories
if(!include_trash)
{
- const LLUUID trash_id = findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
if(trash_id.notNull() && (trash_id == id))
return;
}
cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
if(cat_array)
{
- S32 count = cat_array->count();
+ S32 count = cat_array->size();
for(S32 i = 0; i < count; ++i)
{
- LLViewerInventoryCategory* cat = cat_array->get(i);
+ LLViewerInventoryCategory* cat = cat_array->at(i);
if(add(cat,NULL))
{
- cats.put(cat);
+ cats.push_back(cat);
}
collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
}
@@ -462,111 +755,77 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
LLViewerInventoryItem* item = NULL;
item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
- // Follow folder links recursively. Currently never goes more
- // than one level deep (for current outfit support)
- // Note: if making it fully recursive, need more checking against infinite loops.
- if (follow_folder_links && item_array)
- {
- S32 count = item_array->count();
- for(S32 i = 0; i < count; ++i)
- {
- item = item_array->get(i);
- if (item->getActualType() == LLAssetType::AT_LINK_FOLDER)
- {
- LLViewerInventoryCategory *linked_cat = item->getLinkedCategory();
- if (linked_cat && linked_cat->getPreferredType() != LLAssetType::AT_OUTFIT)
- // BAP - was
- // LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType()))
- // Change back once ensemble typing is in place.
- {
- if(add(linked_cat,NULL))
- {
- // BAP should this be added here? May not
- // matter if it's only being used in current
- // outfit traversal.
- cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat));
- }
- collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE);
- }
- }
- }
- }
-
// Move onto items
if(item_array)
{
- S32 count = item_array->count();
+ S32 count = item_array->size();
for(S32 i = 0; i < count; ++i)
{
- item = item_array->get(i);
+ item = item_array->at(i);
if(add(NULL, item))
{
- items.put(item);
+ items.push_back(item);
}
}
}
}
-void LLInventoryModel::updateLinkedObjects(const LLUUID& object_id)
+void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask)
{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t item_array;
- LLLinkedItemIDMatches is_linked_item_match(object_id);
- collectDescendentsIf(gInventory.getRootFolderID(),
- cat_array,
- item_array,
- LLInventoryModel::INCLUDE_TRASH,
- is_linked_item_match);
-
- for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin();
- cat_iter != cat_array.end();
- cat_iter++)
- {
- LLViewerInventoryCategory *linked_cat = (*cat_iter);
- addChangedMask(LLInventoryObserver::LABEL, linked_cat->getUUID());
- };
+ const LLInventoryObject *obj = getObject(object_id);
+ if (!obj || obj->getIsLinkType())
+ return;
+ LLInventoryModel::item_array_t item_array = collectLinksTo(object_id);
for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
iter != item_array.end();
iter++)
{
LLViewerInventoryItem *linked_item = (*iter);
- addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+ addChangedMask(mask, linked_item->getUUID());
};
- notifyObservers();
}
-void LLInventoryModel::collectLinkedItems(const LLUUID& id,
- item_array_t& items)
+const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const
{
- LLInventoryModel::cat_array_t cat_array;
- LLLinkedItemIDMatches is_linked_item_match(id);
- collectDescendentsIf(gInventory.getRootFolderID(),
- cat_array,
- items,
- LLInventoryModel::INCLUDE_TRASH,
- is_linked_item_match);
+ const LLInventoryItem *item = gInventory.getItem(object_id);
+ if (!item)
+ {
+ return object_id;
+ }
+
+ // Find the base item in case this a link (if it's not a link,
+ // this will just be inv_item_id)
+ return item->getLinkedUUID();
}
-// Generates a string containing the path to the item specified by
-// item_id.
-void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) const
+LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const
{
- std::string temp;
- const LLInventoryObject* obj = getObject(id);
- LLUUID parent_id;
- if(obj) parent_id = obj->getParentUUID();
- std::string forward_slash("/");
- while(obj)
+ return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL;
+}
+
+LLInventoryModel::item_array_t LLInventoryModel::collectLinksTo(const LLUUID& id)
+{
+ // Get item list via collectDescendents (slow!)
+ item_array_t items;
+ const LLInventoryObject *obj = getObject(id);
+ // FIXME - should be as in next line, but this is causing a
+ // stack-smashing crash of cause TBD... check in the REBUILD code.
+ //if (obj && obj->getIsLinkType())
+ if (!obj || obj->getIsLinkType())
+ return items;
+
+ std::pair<backlink_mmap_t::iterator, backlink_mmap_t::iterator> range = mBacklinkMMap.equal_range(id);
+ for (backlink_mmap_t::iterator it = range.first; it != range.second; ++it)
{
- obj = getCategory(parent_id);
- if(obj)
+ LLViewerInventoryItem *item = getItem(it->second);
+ if (item)
{
- temp.assign(forward_slash + obj->getName() + temp);
- parent_id = obj->getParentUUID();
+ items.push_back(item);
}
}
- path.append(temp);
+
+ return items;
}
bool LLInventoryModel::isInventoryUsable() const
@@ -582,9 +841,8 @@ bool LLInventoryModel::isInventoryUsable() const
// Calling this method with an inventory item will either change an
// existing item with a matching item_id, or will add the item to the
// current inventory.
-U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
+U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)
{
- U32 mask = LLInventoryObserver::NONE;
if(item->getUUID().isNull())
{
return mask;
@@ -592,11 +850,19 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
if(!isInventoryUsable())
{
- llwarns << "Inventory is broken." << llendl;
+ LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;
return mask;
}
- LLViewerInventoryItem* old_item = getItem(item->getUUID());
+ // We're hiding mesh types
+#if 0
+ if (item->getType() == LLAssetType::AT_MESH)
+ {
+ return mask;
+ }
+#endif
+
+ LLPointer<LLViewerInventoryItem> old_item = getItem(item->getUUID());
LLPointer<LLViewerInventoryItem> new_item;
if(old_item)
{
@@ -604,6 +870,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
new_item = old_item;
LLUUID old_parent_id = old_item->getParentUUID();
LLUUID new_parent_id = item->getParentUUID();
+
if(old_parent_id != new_parent_id)
{
// need to update the parent-child tree
@@ -611,12 +878,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
if(item_array)
{
- item_array->removeObj(old_item);
+ vector_replace_with_last(*item_array, old_item);
}
item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
if(item_array)
{
- item_array->put(old_item);
+ item_array->push_back(old_item);
}
mask |= LLInventoryObserver::STRUCTURE;
}
@@ -635,18 +902,18 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
if(item->getParentUUID().isNull())
{
- LLUUID category_id = findCategoryUUIDForType(new_item->getType());
+ const LLUUID category_id = findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(new_item->getType()));
new_item->setParent(category_id);
item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
if( item_array )
{
// *FIX: bit of a hack to call update server from here...
new_item->updateServer(TRUE);
- item_array->put(new_item);
+ item_array->push_back(new_item);
}
else
{
- llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
+ LL_WARNS(LOG_INV) << "Couldn't find parent-child item tree for " << new_item->getName() << LL_ENDL;
}
}
else
@@ -659,20 +926,25 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
LLUUID parent_id = item->getParentUUID();
if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
{
- parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
new_item->setParent(parent_id);
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate new_folder(parent_id, 1);
+ update.push_back(new_folder);
+ accountForUpdate(update);
+
}
item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
if(item_array)
{
- item_array->put(new_item);
+ item_array->push_back(new_item);
}
else
{
// Whoops! No such parent, make one.
- llinfos << "Lost item: " << new_item->getUUID() << " - "
- << new_item->getName() << llendl;
- parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ LL_INFOS(LOG_INV) << "Lost item: " << new_item->getUUID() << " - "
+ << new_item->getName() << LL_ENDL;
+ parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
new_item->setParent(parent_id);
item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
if(item_array)
@@ -680,11 +952,11 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
// *FIX: bit of a hack to call update server from
// here...
new_item->updateServer(TRUE);
- item_array->put(new_item);
+ item_array->push_back(new_item);
}
else
{
- llwarns << "Lost and found Not there!!" << llendl;
+ LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;
}
}
}
@@ -703,10 +975,26 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
// Valid UUID; set the item UUID and rename it
new_item->setCreator(id);
std::string avatar_name;
- // Fetch the currect name
- gCacheName->get(id, FALSE, boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), _1, _2, _3));
+
+ if (gCacheName->getFullName(id, avatar_name))
+ {
+ new_item->rename(avatar_name);
+ mask |= LLInventoryObserver::LABEL;
+ }
+ else
+ {
+ // Fetch the current name
+ gCacheName->get(id, FALSE,
+ boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
+ _1, _2, _3));
+ }
+
}
}
+ else if (new_item->getType() == LLAssetType::AT_GESTURE)
+ {
+ mask |= LLInventoryObserver::GESTURE;
+ }
addChangedMask(mask, new_item->getUUID());
return mask;
}
@@ -733,7 +1021,7 @@ LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLU
// Calling this method with an inventory category will either change
// an existing item with the matching id, or it will add the category.
-void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
+void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 mask)
{
if(cat->getUUID().isNull())
{
@@ -742,15 +1030,14 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
if(!isInventoryUsable())
{
- llwarns << "Inventory is broken." << llendl;
+ LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;
return;
}
- LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
+ LLPointer<LLViewerInventoryCategory> old_cat = getCategory(cat->getUUID());
if(old_cat)
{
// We already have an old category, modify it's values
- U32 mask = LLInventoryObserver::NONE;
LLUUID old_parent_id = old_cat->getParentUUID();
LLUUID new_parent_id = cat->getParentUUID();
if(old_parent_id != new_parent_id)
@@ -760,14 +1047,15 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
cat_array = getUnlockedCatArray(old_parent_id);
if(cat_array)
{
- cat_array->removeObj(old_cat);
+ vector_replace_with_last(*cat_array, old_cat);
}
cat_array = getUnlockedCatArray(new_parent_id);
if(cat_array)
{
- cat_array->put(old_cat);
+ cat_array->push_back(old_cat);
}
mask |= LLInventoryObserver::STRUCTURE;
+ mask |= LLInventoryObserver::INTERNAL;
}
if(old_cat->getName() != cat->getName())
{
@@ -783,12 +1071,12 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
new_cat->copyViewerCategory(cat);
addCategory(new_cat);
- // make sure this category is correctly referenced by it's parent.
+ // make sure this category is correctly referenced by its parent.
cat_array_t* cat_array;
cat_array = getUnlockedCatArray(cat->getParentUUID());
if(cat_array)
{
- cat_array->put(new_cat);
+ cat_array->push_back(new_cat);
}
// make space in the tree for this category's children.
@@ -804,275 +1092,386 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
{
- lldebugs << "LLInventoryModel::moveObject()" << llendl;
+ LL_DEBUGS(LOG_INV) << "LLInventoryModel::moveObject()" << LL_ENDL;
if(!isInventoryUsable())
{
- llwarns << "Inventory is broken." << llendl;
+ LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL;
return;
}
if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
{
- llwarns << "Could not move inventory object " << object_id << " to "
- << cat_id << llendl;
+ LL_WARNS(LOG_INV) << "Could not move inventory object " << object_id << " to "
+ << cat_id << LL_ENDL;
return;
}
- LLViewerInventoryCategory* cat = getCategory(object_id);
+ LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id);
if(cat && (cat->getParentUUID() != cat_id))
{
cat_array_t* cat_array;
cat_array = getUnlockedCatArray(cat->getParentUUID());
- if(cat_array) cat_array->removeObj(cat);
+ if(cat_array) vector_replace_with_last(*cat_array, cat);
cat_array = getUnlockedCatArray(cat_id);
cat->setParent(cat_id);
- if(cat_array) cat_array->put(cat);
+ if(cat_array) cat_array->push_back(cat);
addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
return;
}
- LLViewerInventoryItem* item = getItem(object_id);
+ LLPointer<LLViewerInventoryItem> item = getItem(object_id);
if(item && (item->getParentUUID() != cat_id))
{
item_array_t* item_array;
item_array = getUnlockedItemArray(item->getParentUUID());
- if(item_array) item_array->removeObj(item);
+ if(item_array) vector_replace_with_last(*item_array, item);
item_array = getUnlockedItemArray(cat_id);
item->setParent(cat_id);
- if(item_array) item_array->put(item);
+ if(item_array) item_array->push_back(item);
addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
return;
}
}
-// Delete a particular inventory object by ID.
-void LLInventoryModel::deleteObject(const LLUUID& id)
+// Migrated from llinventoryfunctions
+void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item,
+ const LLUUID& new_parent_id,
+ BOOL restamp)
{
- purgeLinkedObjects(id);
- lldebugs << "LLInventoryModel::deleteObject()" << llendl;
- LLPointer<LLInventoryObject> obj = getObject(id);
- if(obj)
+ if (item->getParentUUID() == new_parent_id)
{
- lldebugs << "Deleting inventory object " << id << llendl;
- mLastItem = NULL;
- LLUUID parent_id = obj->getParentUUID();
- mCategoryMap.erase(id);
- mItemMap.erase(id);
- //mInventory.erase(id);
- item_array_t* item_list = getUnlockedItemArray(parent_id);
- if(item_list)
- {
- LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
- item_list->removeObj(item);
- }
- cat_array_t* cat_list = getUnlockedCatArray(parent_id);
- if(cat_list)
- {
- LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
- cat_list->removeObj(cat);
- }
- item_list = getUnlockedItemArray(id);
- if(item_list)
- {
- delete item_list;
- mParentChildItemTree.erase(id);
- }
- cat_list = getUnlockedCatArray(id);
- if(cat_list)
- {
- delete cat_list;
- mParentChildCategoryTree.erase(id);
- }
- addChangedMask(LLInventoryObserver::REMOVE, id);
- obj = NULL; // delete obj
+ LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID()
+ << ") is already in folder " << new_parent_id << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS(LOG_INV) << "Moving '" << item->getName() << "' (" << item->getUUID()
+ << ") from " << item->getParentUUID() << " to folder "
+ << new_parent_id << LL_ENDL;
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
+ update.push_back(new_folder);
+ accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(new_parent_id);
+ new_item->updateParentOnServer(restamp);
+ updateItem(new_item);
+ notifyObservers();
}
}
-// Delete a particular inventory item by ID, and remove it from the server.
-void LLInventoryModel::purgeObject(const LLUUID &id)
+// Migrated from llinventoryfunctions
+void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat,
+ const LLUUID& new_parent_id,
+ BOOL restamp)
{
- lldebugs << "LLInventoryModel::purgeObject()" << llendl;
- LLPointer<LLInventoryObject> obj = getObject(id);
- if(obj)
+ if (!cat)
{
- obj->removeFromServer();
- LLPreview::hide(id);
- deleteObject(id);
+ return;
}
+
+ // Can't move a folder into a child of itself.
+ if (isObjectDescendentOf(new_parent_id, cat->getUUID()))
+ {
+ return;
+ }
+
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
+ update.push_back(new_folder);
+ accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->setParent(new_parent_id);
+ new_cat->updateParentOnServer(restamp);
+ updateCategory(new_cat);
+ notifyObservers();
}
-void LLInventoryModel::purgeLinkedObjects(const LLUUID &id)
+void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update)
{
- LLInventoryObject* objectp = getObject(id);
- if (!objectp) return;
-
- if (objectp->getIsLinkType())
+ LLTimer timer;
+ if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
{
- return;
+ dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
}
- LLInventoryModel::item_array_t item_array;
- collectLinkedItems(id, item_array);
-
- for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
- iter != item_array.end();
- iter++)
+ AISUpdate ais_update(update); // parse update llsd into stuff to do.
+ ais_update.doUpdate(); // execute the updates in the appropriate order.
+ LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL;
+}
+
+// Does not appear to be used currently.
+void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version)
+{
+ U32 mask = LLInventoryObserver::NONE;
+
+ LLPointer<LLViewerInventoryItem> item = gInventory.getItem(item_id);
+ LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (item ? item->getName() : "(NOT FOUND)") << LL_ENDL;
+ if(item)
{
- LLViewerInventoryItem *linked_item = (*iter);
- if (linked_item->getUUID() == id) continue;
- purgeObject(linked_item->getUUID());
+ for (LLSD::map_const_iterator it = updates.beginMap();
+ it != updates.endMap(); ++it)
+ {
+ if (it->first == "name")
+ {
+ LL_INFOS(LOG_INV) << "Updating name from " << item->getName() << " to " << it->second.asString() << LL_ENDL;
+ item->rename(it->second.asString());
+ mask |= LLInventoryObserver::LABEL;
+ }
+ else if (it->first == "desc")
+ {
+ LL_INFOS(LOG_INV) << "Updating description from " << item->getActualDescription()
+ << " to " << it->second.asString() << LL_ENDL;
+ item->setDescription(it->second.asString());
+ }
+ else
+ {
+ LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL;
+ }
+ }
+ mask |= LLInventoryObserver::INTERNAL;
+ addChangedMask(mask, item->getUUID());
+ if (update_parent_version)
+ {
+ // Descendent count is unchanged, but folder version incremented.
+ LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0);
+ accountForUpdate(up);
+ }
+ notifyObservers(); // do we want to be able to make this optional?
}
}
-// This is a method which collects the descendents of the id
-// provided. If the category is not found, no action is
-// taken. This method goes through the long winded process of
-// cancelling any calling cards, removing server representation of
-// folders, items, etc in a fairly efficient manner.
-void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
+// Not used?
+void LLInventoryModel::onCategoryUpdated(const LLUUID& cat_id, const LLSD& updates)
{
- EHasChildren children = categoryHasChildren(id);
- if(children == CHILDREN_NO)
+ U32 mask = LLInventoryObserver::NONE;
+
+ LLPointer<LLViewerInventoryCategory> cat = gInventory.getCategory(cat_id);
+ LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (cat ? cat->getName() : "(NOT FOUND)") << LL_ENDL;
+ if(cat)
{
- llinfos << "Not purging descendents of " << id << llendl;
- return;
+ for (LLSD::map_const_iterator it = updates.beginMap();
+ it != updates.endMap(); ++it)
+ {
+ if (it->first == "name")
+ {
+ LL_INFOS(LOG_INV) << "Updating name from " << cat->getName() << " to " << it->second.asString() << LL_ENDL;
+ cat->rename(it->second.asString());
+ mask |= LLInventoryObserver::LABEL;
+ }
+ else
+ {
+ LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL;
+ }
+ }
+ mask |= LLInventoryObserver::INTERNAL;
+ addChangedMask(mask, cat->getUUID());
+ notifyObservers(); // do we want to be able to make this optional?
}
- LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
- if(cat.notNull())
+}
+
+// Update model after descendents have been purged.
+void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links)
+{
+ LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id);
+ if (cat.notNull())
{
// do the cache accounting
- llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
- << llendl;
S32 descendents = cat->getDescendentCount();
if(descendents > 0)
{
- LLCategoryUpdate up(id, -descendents);
+ LLInventoryModel::LLCategoryUpdate up(object_id, -descendents);
accountForUpdate(up);
}
- // we know that descendent count is 0, aide since the
+ // we know that descendent count is 0, however since the
// accounting may actually not do an update, we should force
// it here.
cat->setDescendentCount(0);
- // send it upstream
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessage("PurgeInventoryDescendents");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->nextBlock("InventoryData");
- msg->addUUID("FolderID", id);
- gAgent.sendReliableMessage();
-
// unceremoniously remove anything we have locally stored.
- cat_array_t categories;
- item_array_t items;
- collectDescendents(id,
+ LLInventoryModel::cat_array_t categories;
+ LLInventoryModel::item_array_t items;
+ collectDescendents(object_id,
categories,
items,
- INCLUDE_TRASH);
- S32 count = items.count();
- S32 i;
- for(i = 0; i < count; ++i)
+ LLInventoryModel::INCLUDE_TRASH);
+ S32 count = items.size();
+
+ LLUUID uu_id;
+ for(S32 i = 0; i < count; ++i)
+ {
+ uu_id = items.at(i)->getUUID();
+
+ // This check prevents the deletion of a previously deleted item.
+ // This is necessary because deletion is not done in a hierarchical
+ // order. The current item may have been already deleted as a child
+ // of its deleted parent.
+ if (getItem(uu_id))
+ {
+ deleteObject(uu_id, fix_broken_links);
+ }
+ }
+
+ count = categories.size();
+ // Slightly kludgy way to make sure categories are removed
+ // only after their child categories have gone away.
+
+ // FIXME: Would probably make more sense to have this whole
+ // descendent-clearing thing be a post-order recursive
+ // function to get the leaf-up behavior automatically.
+ S32 deleted_count;
+ S32 total_deleted_count = 0;
+ do
{
- deleteObject(items.get(i)->getUUID());
+ deleted_count = 0;
+ for(S32 i = 0; i < count; ++i)
+ {
+ uu_id = categories.at(i)->getUUID();
+ if (getCategory(uu_id))
+ {
+ cat_array_t* cat_list = getUnlockedCatArray(uu_id);
+ if (!cat_list || (cat_list->size() == 0))
+ {
+ deleteObject(uu_id, fix_broken_links);
+ deleted_count++;
+ }
+ }
+ }
+ total_deleted_count += deleted_count;
}
- count = categories.count();
- for(i = 0; i < count; ++i)
+ while (deleted_count > 0);
+ if (total_deleted_count != count)
{
- deleteObject(categories.get(i)->getUUID());
+ LL_WARNS(LOG_INV) << "Unexpected count of categories deleted, got "
+ << total_deleted_count << " expected " << count << LL_ENDL;
}
+ //gInventory.validate();
}
}
-void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
- LLDynamicArray<LLUUID>& item_ids)
+// Update model after an item is confirmed as removed from
+// server. Works for categories or items.
+void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool fix_broken_links, bool update_parent_version, bool do_notify_observers)
{
- // Store off tre UUIDS of parents which are being deleted (thus no
- // need to increment) and the parents which are being modified. We
- // have to increment the version of the parent with each message
- // sent upstream since the dataserver will increment each unique
- // parent per update message.
- std::set<LLUUID> ignore_parents;
- update_map_t inc_parents;
-
- S32 i;
- S32 count = category_ids.count();
- BOOL start_new_message = TRUE;
- LLMessageSystem* msg = gMessageSystem;
- LLPointer<LLViewerInventoryCategory> cat;
- for(i = 0; i < count; i++)
+ LLPointer<LLInventoryObject> obj = getObject(object_id);
+ if(obj)
{
- if(start_new_message)
- {
- start_new_message = FALSE;
- msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- }
- LLUUID cat_id = category_ids.get(i);
-
- msg->nextBlockFast(_PREHASH_FolderData);
- msg->addUUIDFast(_PREHASH_FolderID, cat_id);
- cat = getCategory(cat_id);
- ignore_parents.insert(cat_id);
- addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id);
- if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end()))
+ if (getCategory(object_id))
{
- --inc_parents[cat->getParentUUID()];
+ // For category, need to delete/update all children first.
+ onDescendentsPurgedFromServer(object_id, fix_broken_links);
}
- if(msg->isSendFullFast(_PREHASH_FolderData))
+
+
+ // From item/cat removeFromServer()
+ if (update_parent_version)
{
- start_new_message = TRUE;
- msg->nextBlockFast(_PREHASH_ItemData);
- msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
- gAgent.sendReliableMessage();
- accountForUpdate(inc_parents);
- inc_parents.clear();
+ LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
+ accountForUpdate(up);
}
+
+ // From purgeObject()
+ LLPreview::hide(object_id);
+ deleteObject(object_id, fix_broken_links, do_notify_observers);
}
+}
- count = item_ids.count();
- std::set<LLUUID>::iterator not_ignored = ignore_parents.end();
- LLPointer<LLViewerInventoryItem> item;
- if((0 == count) && (!start_new_message))
+
+// Delete a particular inventory object by ID.
+void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, bool do_notify_observers)
+{
+ LL_DEBUGS(LOG_INV) << "LLInventoryModel::deleteObject()" << LL_ENDL;
+ LLPointer<LLInventoryObject> obj = getObject(id);
+ if (!obj)
{
- msg->nextBlockFast(_PREHASH_ItemData);
- msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+ LL_WARNS(LOG_INV) << "Deleting non-existent object [ id: " << id << " ] " << LL_ENDL;
+ return;
}
- for(i = 0; i < count; i++)
+
+ LL_DEBUGS(LOG_INV) << "Deleting inventory object " << id << LL_ENDL;
+ mLastItem = NULL;
+ LLUUID parent_id = obj->getParentUUID();
+ mCategoryMap.erase(id);
+ mItemMap.erase(id);
+ //mInventory.erase(id);
+ item_array_t* item_list = getUnlockedItemArray(parent_id);
+ if(item_list)
{
- if(start_new_message)
- {
- start_new_message = FALSE;
- msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_FolderData);
- msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null);
- }
- LLUUID item_id = item_ids.get(i);
- msg->nextBlockFast(_PREHASH_ItemData);
- msg->addUUIDFast(_PREHASH_ItemID, item_id);
- item = getItem(item_id);
- addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id);
- if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored))
+ LLPointer<LLViewerInventoryItem> item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
+ vector_replace_with_last(*item_list, item);
+ }
+ cat_array_t* cat_list = getUnlockedCatArray(parent_id);
+ if(cat_list)
+ {
+ LLPointer<LLViewerInventoryCategory> cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
+ vector_replace_with_last(*cat_list, cat);
+ }
+ item_list = getUnlockedItemArray(id);
+ if(item_list)
+ {
+ if (item_list->size())
{
- --inc_parents[item->getParentUUID()];
+ LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child items" << LL_ENDL;
}
- if(msg->isSendFullFast(_PREHASH_ItemData))
+ delete item_list;
+ mParentChildItemTree.erase(id);
+ }
+ cat_list = getUnlockedCatArray(id);
+ if(cat_list)
+ {
+ if (cat_list->size())
{
- start_new_message = TRUE;
- gAgent.sendReliableMessage();
- accountForUpdate(inc_parents);
- inc_parents.clear();
+ LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child cats" << LL_ENDL;
}
+ delete cat_list;
+ mParentChildCategoryTree.erase(id);
+ }
+ addChangedMask(LLInventoryObserver::REMOVE, id);
+
+ bool is_link_type = obj->getIsLinkType();
+ if (is_link_type)
+ {
+ removeBacklinkInfo(obj->getUUID(), obj->getLinkedUUID());
+ }
+
+ // Can't have links to links, so there's no need for this update
+ // if the item removed is a link. Can also skip if source of the
+ // update is getting broken link info separately.
+ obj = NULL; // delete obj
+ if (fix_broken_links && !is_link_type)
+ {
+ updateLinkedObjectsFromPurge(id);
}
- if(!start_new_message)
+ if (do_notify_observers)
{
- gAgent.sendReliableMessage();
- accountForUpdate(inc_parents);
+ notifyObservers();
+ }
+}
+
+void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
+{
+ LLInventoryModel::item_array_t item_array = collectLinksTo(baseobj_id);
+
+ // REBUILD is expensive, so clear the current change list first else
+ // everything else on the changelist will also get rebuilt.
+ if (item_array.size() > 0)
+ {
+ notifyObservers();
+ for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
+ iter != item_array.end();
+ iter++)
+ {
+ const LLViewerInventoryItem *linked_item = (*iter);
+ const LLUUID &item_id = linked_item->getUUID();
+ if (item_id == baseobj_id) continue;
+ addChangedMask(LLInventoryObserver::REBUILD, item_id);
+ }
+ notifyObservers();
}
}
@@ -1093,26 +1492,36 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
return mObservers.find(observer) != mObservers.end();
}
-// Call this method when it's time to update everyone on a new state,
-// by default, the inventory model will not update observers
-// automatically.
-// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328]
-void LLInventoryModel::notifyObservers(const std::string service_name)
+void LLInventoryModel::idleNotifyObservers()
{
+ // *FIX: Think I want this conditional or moved elsewhere...
+ handleResponses(true);
+
+ if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))
+ {
+ return;
+ }
+ notifyObservers();
+}
+
+// Call this method when it's time to update everyone on a new state.
+void LLInventoryModel::notifyObservers()
+{
+ if (mIsNotifyObservers)
+ {
+ // Within notifyObservers, something called notifyObservers
+ // again. This type of recursion is unsafe because it causes items to be
+ // processed twice, and this can easily lead to infinite loops.
+ LL_WARNS(LOG_INV) << "Call was made to notifyObservers within notifyObservers!" << LL_ENDL;
+ return;
+ }
+
+ mIsNotifyObservers = TRUE;
for (observer_list_t::iterator iter = mObservers.begin();
iter != mObservers.end(); )
{
LLInventoryObserver* observer = *iter;
-
- if (service_name.empty())
- {
- observer->changed(mModifyMask);
- }
- else
- {
- observer->mMessageName = service_name;
- observer->changed(mModifyMask);
- }
+ observer->changed(mModifyMask);
// safe way to increment since changed may delete entries! (@!##%@!@&*!)
iter = mObservers.upper_bound(observer);
@@ -1120,121 +1529,66 @@ void LLInventoryModel::notifyObservers(const std::string service_name)
mModifyMask = LLInventoryObserver::NONE;
mChangedItemIDs.clear();
+ mAddedItemIDs.clear();
+ mIsNotifyObservers = FALSE;
}
// store flag for change
// and id of object change applies to
void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
{
- mModifyMask |= mask;
- if (referent.notNull())
+ if (mIsNotifyObservers)
{
- mChangedItemIDs.insert(referent);
- }
-}
-
-// This method to prepares a set of mock inventory which provides
-// minimal functionality before the actual arrival of inventory.
-/*
-void LLInventoryModel::mock(const LLUUID& root_id)
-{
- llinfos << "LLInventoryModel::mock() " << root_id << llendl;
- if(root_id.isNull())
- {
- llwarns << "Not a valid root id" << llendl;
- return;
- }
- LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
- root_id,
- LLUUID::null,
- LLAssetType::AT_CATEGORY,
- LLAssetType::lookupCategoryName(LLAssetType::AT_ROOT_CATEGORY),
- gAgent.getID());
- addCategory(cat);
- gInventory.buildParentChildMap();
-}
-*/
-
-//If we get back a normal response, handle it here
-void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
-{
- start_new_inventory_observer();
-
- /*LLUUID agent_id;
- agent_id = content["agent_id"].asUUID();
- if(agent_id != gAgent.getID())
- {
- llwarns << "Got a inventory update for the wrong agent: " << agent_id
- << llendl;
- return;
- }*/
- item_array_t items;
- update_map_t update;
- S32 count = content["items"].size();
- bool all_one_folder = true;
- LLUUID folder_id;
- // Does this loop ever execute more than once?
- for(S32 i = 0; i < count; ++i)
- {
- LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
- titem->unpackMessage(content["items"][i]);
-
- lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
- << titem->getUUID() << llendl;
- items.push_back(titem);
- // examine update for changes.
- LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
- if(itemp)
+ // Something marked an item for change within a call to notifyObservers
+ // (which is in the process of processing the list of items marked for change).
+ // This means the change may fail to be processed.
+ LL_WARNS(LOG_INV) << "Adding changed mask within notify observers! Change will likely be lost." << LL_ENDL;
+ LLViewerInventoryItem *item = getItem(referent);
+ if (item)
{
- if(titem->getParentUUID() == itemp->getParentUUID())
- {
- update[titem->getParentUUID()];
- }
- else
- {
- ++update[titem->getParentUUID()];
- --update[itemp->getParentUUID()];
- }
+ LL_WARNS(LOG_INV) << "Item " << item->getName() << LL_ENDL;
}
else
{
- ++update[titem->getParentUUID()];
+ LLViewerInventoryCategory *cat = getCategory(referent);
+ if (cat)
+ {
+ LL_WARNS(LOG_INV) << "Category " << cat->getName() << LL_ENDL;
+ }
}
- if (folder_id.isNull())
+ }
+
+ mModifyMask |= mask;
+ if (referent.notNull())
+ {
+ mChangedItemIDs.insert(referent);
+
+ if (mask & LLInventoryObserver::ADD)
{
- folder_id = titem->getParentUUID();
+ mAddedItemIDs.insert(referent);
}
- else
+
+ // Update all linked items. Starting with just LABEL because I'm
+ // not sure what else might need to be accounted for this.
+ if (mask & LLInventoryObserver::LABEL)
{
- all_one_folder = false;
+ addChangedMaskForLinks(referent, LLInventoryObserver::LABEL);
}
}
-
- U32 changes = 0x0;
- //as above, this loop never seems to loop more than once per call
- for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
- {
- changes |= gInventory.updateItem(*it);
- }
- gInventory.notifyObservers("fetchinventory");
- gViewerWindow->getWindow()->decBusyCount();
-}
-
-//If we get back an error (not found, etc...), handle it here
-void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason)
-{
- llinfos << "fetchInventory::error "
- << status << ": " << reason << llendl;
- gInventory.notifyObservers("fetchinventory");
}
-bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
+bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const
{
+ if(folder_id.isNull())
+ {
+ LL_WARNS(LOG_INV) << "Calling fetch descendents on NULL folder id!" << LL_ENDL;
+ return false;
+ }
LLViewerInventoryCategory* cat = getCategory(folder_id);
if(!cat)
{
- llwarns << "Asked to fetch descendents of non-existent folder: "
- << folder_id << llendl;
+ LL_WARNS(LOG_INV) << "Asked to fetch descendents of non-existent folder: "
+ << folder_id << LL_ENDL;
return false;
}
//S32 known_descendents = 0;
@@ -1242,506 +1596,25 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
//item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
//if(categories)
//{
- // known_descendents += categories->count();
+ // known_descendents += categories->size();
//}
//if(items)
//{
- // known_descendents += items->count();
+ // known_descendents += items->size();
//}
- return cat->fetchDescendents();
-}
-
-//Initialize statics.
-bool LLInventoryModel::isBulkFetchProcessingComplete()
-{
- return ( (sFetchQueue.empty()
- && sBulkFetchCount<=0) ? TRUE : FALSE ) ;
-}
-
-class fetchDescendentsResponder: public LLHTTPClient::Responder
-{
- public:
- fetchDescendentsResponder(const LLSD& request_sd) : mRequestSD(request_sd) {};
- //fetchDescendentsResponder() {};
- void result(const LLSD& content);
- void error(U32 status, const std::string& reason);
- public:
- typedef std::vector<LLViewerInventoryCategory*> folder_ref_t;
- protected:
- LLSD mRequestSD;
-};
-
-//If we get back a normal response, handle it here
-void fetchDescendentsResponder::result(const LLSD& content)
-{
- if (content.has("folders"))
- {
-
- for(LLSD::array_const_iterator folder_it = content["folders"].beginArray();
- folder_it != content["folders"].endArray();
- ++folder_it)
- {
- LLSD folder_sd = *folder_it;
-
-
- //LLUUID agent_id = folder_sd["agent_id"];
-
- //if(agent_id != gAgent.getID()) //This should never happen.
- //{
- // llwarns << "Got a UpdateInventoryItem for the wrong agent."
- // << llendl;
- // break;
- //}
-
- LLUUID parent_id = folder_sd["folder_id"];
- LLUUID owner_id = folder_sd["owner_id"];
- S32 version = (S32)folder_sd["version"].asInteger();
- S32 descendents = (S32)folder_sd["descendents"].asInteger();
- LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
-
- if (parent_id.isNull())
- {
- LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
- for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray();
- item_it != folder_sd["items"].endArray();
- ++item_it)
- {
- LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
- if (lost_uuid.notNull())
- {
- LLSD item = *item_it;
- titem->unpackMessage(item);
-
- LLInventoryModel::update_list_t update;
- LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1);
- update.push_back(new_folder);
- gInventory.accountForUpdate(update);
-
- titem->setParent(lost_uuid);
- titem->updateParentOnServer(FALSE);
- gInventory.updateItem(titem);
- gInventory.notifyObservers("fetchDescendents");
-
- }
- }
- }
-
- LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id);
- if (!pcat)
- {
- continue;
- }
-
- for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray();
- category_it != folder_sd["categories"].endArray();
- ++category_it)
- {
- LLSD category = *category_it;
- tcategory->fromLLSD(category);
-
- if (LLInventoryModel::sFullFetchStarted)
- {
- sFetchQueue.push_back(tcategory->getUUID());
- }
- else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) )
- {
- gInventory.updateCategory(tcategory);
- }
-
- }
- LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
- for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray();
- item_it != folder_sd["items"].endArray();
- ++item_it)
- {
- LLSD item = *item_it;
- titem->unpackMessage(item);
-
- gInventory.updateItem(titem);
- }
-
- // set version and descendentcount according to message.
- LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
- if(cat)
- {
- cat->setVersion(version);
- cat->setDescendentCount(descendents);
- cat->determineFolderType();
- }
-
- }
- }
-
- if (content.has("bad_folders"))
- {
- for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray();
- folder_it != content["bad_folders"].endArray();
- ++folder_it)
- {
- LLSD folder_sd = *folder_it;
-
- //These folders failed on the dataserver. We probably don't want to retry them.
- llinfos << "Folder " << folder_sd["folder_id"].asString()
- << "Error: " << folder_sd["error"].asString() << llendl;
- }
- }
-
- LLInventoryModel::incrBulkFetch(-1);
-
- if (LLInventoryModel::isBulkFetchProcessingComplete())
- {
- llinfos << "Inventory fetch completed" << llendl;
- if (LLInventoryModel::sFullFetchStarted)
- {
- LLInventoryModel::sAllFoldersFetched = TRUE;
- }
- LLInventoryModel::stopBackgroundFetch();
- }
-
- gInventory.notifyObservers("fetchDescendents");
-}
-
-//If we get back an error (not found, etc...), handle it here
-void fetchDescendentsResponder::error(U32 status, const std::string& reason)
-{
- llinfos << "fetchDescendentsResponder::error "
- << status << ": " << reason << llendl;
-
- LLInventoryModel::incrBulkFetch(-1);
-
- if (status==499) //timed out. Let's be awesome!
- {
- for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray();
- folder_it != mRequestSD["folders"].endArray();
- ++folder_it)
- {
- LLSD folder_sd = *folder_it;
- LLUUID folder_id = folder_sd["folder_id"];
- sFetchQueue.push_front(folder_id);
- }
- }
- else
- {
- if (LLInventoryModel::isBulkFetchProcessingComplete())
- {
- if (LLInventoryModel::sFullFetchStarted)
- {
- LLInventoryModel::sAllFoldersFetched = TRUE;
- }
- LLInventoryModel::stopBackgroundFetch();
- }
- }
- gInventory.notifyObservers("fetchDescendents");
-}
-
-//static Bundle up a bunch of requests to send all at once.
-void LLInventoryModel::bulkFetch(std::string url)
-{
- //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
- //If there are items in sFetchQueue, we want to check the time since the last bulkFetch was
- //sent. If it exceeds our retry time, go ahead and fire off another batch.
- //Stopbackgroundfetch will be run from the Responder instead of here.
-
- S16 max_concurrent_fetches=8;
- F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely.
- if (sMinTimeBetweenFetches < new_min_time) sMinTimeBetweenFetches=new_min_time; //HACK! See above.
-
- if(gDisconnected
- || sBulkFetchCount > max_concurrent_fetches
- || sFetchTimer.getElapsedTimeF32() < sMinTimeBetweenFetches)
- {
- return; // just bail if we are disconnected.
- }
-
- U32 folder_count=0;
- U32 max_batch_size=5;
-
- U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
-
- LLSD body;
- LLSD body_lib;
- while( !(sFetchQueue.empty() ) && (folder_count < max_batch_size) )
- {
- if (sFetchQueue.front().isNull()) //DEV-17797
- {
- LLSD folder_sd;
- folder_sd["folder_id"] = LLUUID::null.asString();
- folder_sd["owner_id"] = gAgent.getID();
- folder_sd["sort_order"] = (LLSD::Integer)sort_order;
- folder_sd["fetch_folders"] = (LLSD::Boolean)FALSE;
- folder_sd["fetch_items"] = (LLSD::Boolean)TRUE;
- body["folders"].append(folder_sd);
- folder_count++;
- }
- else
- {
-
-
- LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
-
- if (cat)
- {
- if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
- {
- LLSD folder_sd;
- folder_sd["folder_id"] = cat->getUUID();
- folder_sd["owner_id"] = cat->getOwnerID();
- folder_sd["sort_order"] = (LLSD::Integer)sort_order;
- folder_sd["fetch_folders"] = TRUE; //(LLSD::Boolean)sFullFetchStarted;
- folder_sd["fetch_items"] = (LLSD::Boolean)TRUE;
-
- if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
- body_lib["folders"].append(folder_sd);
- else
- body["folders"].append(folder_sd);
- folder_count++;
- }
- if (sFullFetchStarted)
- { //Already have this folder but append child folders to list.
- // add all children to queue
- parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
- if (cat_it != gInventory.mParentChildCategoryTree.end())
- {
- cat_array_t* child_categories = cat_it->second;
-
- for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
- {
- sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
- }
- }
-
- }
- }
- }
- sFetchQueue.pop_front();
- }
-
- if (folder_count > 0)
- {
- sBulkFetchCount++;
- if (body["folders"].size())
- {
- LLHTTPClient::post(url, body, new fetchDescendentsResponder(body),300.0);
- }
- if (body_lib["folders"].size())
- {
- std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents");
- LLHTTPClient::post(url_lib, body_lib, new fetchDescendentsResponder(body_lib),300.0);
- }
- sFetchTimer.reset();
- }
- else if (isBulkFetchProcessingComplete())
- {
- if (sFullFetchStarted)
- {
- sAllFoldersFetched = TRUE;
- }
- stopBackgroundFetch();
- }
-}
-
-// static
-bool LLInventoryModel::isEverythingFetched()
-{
- return (sAllFoldersFetched ? true : false);
-}
-
-//static
-BOOL LLInventoryModel::backgroundFetchActive()
-{
- return sBackgroundFetchActive;
-}
-
-//static
-void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
-{
- if (!sAllFoldersFetched)
- {
- sBackgroundFetchActive = TRUE;
- if (cat_id.isNull())
- {
- if (!sFullFetchStarted)
- {
- sFullFetchStarted = TRUE;
- sFetchQueue.push_back(gInventory.getLibraryRootFolderID());
- sFetchQueue.push_back(gInventory.getRootFolderID());
- gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
- }
- }
- else
- {
- // specific folder requests go to front of queue
- if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
- {
- sFetchQueue.push_front(cat_id);
- gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
- }
- }
- }
-}
-
-//static
-void LLInventoryModel::findLostItems()
-{
- sBackgroundFetchActive = TRUE;
- sFetchQueue.push_back(LLUUID::null);
- gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
-}
-
-//static
-void LLInventoryModel::stopBackgroundFetch()
-{
- if (sBackgroundFetchActive)
- {
- sBackgroundFetchActive = FALSE;
- gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
- sBulkFetchCount=0;
- sMinTimeBetweenFetches=0.0f;
-// sFullFetchStarted=FALSE;
- }
-}
-
-//static
-void LLInventoryModel::backgroundFetch(void*)
-{
- if (sBackgroundFetchActive && gAgent.getRegion())
- {
- //If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
- std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents");
- if (!url.empty())
- {
- bulkFetch(url);
- return;
- }
-
- //DEPRECATED OLD CODE FOLLOWS.
- // no more categories to fetch, stop fetch process
- if (sFetchQueue.empty())
- {
- llinfos << "Inventory fetch completed" << llendl;
- if (sFullFetchStarted)
- {
- sAllFoldersFetched = TRUE;
- }
- stopBackgroundFetch();
- return;
- }
-
- F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f);
- F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f);
- if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time)
- {
- // double timeouts on failure
- sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f);
- sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f);
- llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
- // fetch is no longer considered "timely" although we will wait for full time-out
- sTimelyFetchPending = FALSE;
- }
-
- while(1)
- {
- if (sFetchQueue.empty())
- {
- break;
- }
-
- if(gDisconnected)
- {
- // just bail if we are disconnected.
- break;
- }
-
- LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
-
- // category has been deleted, remove from queue.
- if (!cat)
- {
- sFetchQueue.pop_front();
- continue;
- }
-
- if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches &&
- LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
- {
- // category exists but has no children yet, fetch the descendants
- // for now, just request every time and rely on retry timer to throttle
- if (cat->fetchDescendents())
- {
- sFetchTimer.reset();
- sTimelyFetchPending = TRUE;
- }
- else
- {
- // The catagory also tracks if it has expired and here it says it hasn't
- // yet. Get out of here because nothing is going to happen until we
- // update the timers.
- break;
- }
- }
- // do I have all my children?
- else if (gInventory.isCategoryComplete(sFetchQueue.front()))
- {
- // finished with this category, remove from queue
- sFetchQueue.pop_front();
-
- // add all children to queue
- parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
- if (cat_it != gInventory.mParentChildCategoryTree.end())
- {
- cat_array_t* child_categories = cat_it->second;
-
- for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
- {
- sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
- }
- }
-
- // we received a response in less than the fast time
- if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time)
- {
- // shrink timeouts based on success
- sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f);
- sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f);
- //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
- }
-
- sTimelyFetchPending = FALSE;
- continue;
- }
- else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches)
- {
- // received first packet, but our num descendants does not match db's num descendants
- // so try again later
- LLUUID fetch_id = sFetchQueue.front();
- sFetchQueue.pop_front();
-
- if (sNumFetchRetries++ < MAX_FETCH_RETRIES)
- {
- // push on back of queue
- sFetchQueue.push_back(fetch_id);
- }
- sTimelyFetchPending = FALSE;
- sFetchTimer.reset();
- break;
- }
-
- // not enough time has elapsed to do a new fetch
- break;
- }
- }
+ return cat->fetch();
}
void LLInventoryModel::cache(
const LLUUID& parent_folder_id,
const LLUUID& agent_id)
{
- lldebugs << "Caching " << parent_folder_id << " for " << agent_id
- << llendl;
+ LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id
+ << LL_ENDL;
LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
if(!root_cat) return;
cat_array_t categories;
- categories.put(root_cat);
+ categories.push_back(root_cat);
item_array_t items;
LLCanCache can_cache(this);
@@ -1762,48 +1635,117 @@ void LLInventoryModel::cache(
gzip_filename.append(".gz");
if(gzip_file(inventory_filename, gzip_filename))
{
- lldebugs << "Successfully compressed " << inventory_filename << llendl;
+ LL_DEBUGS(LOG_INV) << "Successfully compressed " << inventory_filename << LL_ENDL;
LLFile::remove(inventory_filename);
}
else
{
- llwarns << "Unable to compress " << inventory_filename << llendl;
+ LL_WARNS(LOG_INV) << "Unable to compress " << inventory_filename << LL_ENDL;
}
}
void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
{
- //llinfos << "LLInventoryModel::addCategory()" << llendl;
+ //LL_INFOS(LOG_INV) << "LLInventoryModel::addCategory()" << LL_ENDL;
if(category)
{
+ // We aren't displaying the Meshes folder
+ if (category->mPreferredType == LLFolderType::FT_MESH)
+ {
+ return;
+ }
+
+ // try to localize default names first. See EXT-8319, EXT-7051.
+ category->localizeName();
+
// Insert category uniquely into the map
mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
//mInventory[category->getUUID()] = category;
}
}
+bool LLInventoryModel::hasBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) const
+{
+ std::pair <backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range;
+ range = mBacklinkMMap.equal_range(target_id);
+ for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it)
+ {
+ if (it->second == link_id)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLInventoryModel::addBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id)
+{
+ if (!hasBacklinkInfo(link_id, target_id))
+ {
+ mBacklinkMMap.insert(std::make_pair(target_id, link_id));
+ }
+}
+
+void LLInventoryModel::removeBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id)
+{
+ std::pair <backlink_mmap_t::iterator, backlink_mmap_t::iterator> range;
+ range = mBacklinkMMap.equal_range(target_id);
+ for (backlink_mmap_t::iterator it = range.first; it != range.second; )
+ {
+ if (it->second == link_id)
+ {
+ backlink_mmap_t::iterator delete_it = it; // iterator will be invalidated by erase.
+ ++it;
+ mBacklinkMMap.erase(delete_it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
void LLInventoryModel::addItem(LLViewerInventoryItem* item)
{
- //llinfos << "LLInventoryModel::addItem()" << llendl;
+ llassert(item);
if(item)
{
+ // This can happen if assettype enums from llassettype.h ever change.
+ // For example, there is a known backwards compatibility issue in some viewer prototypes prior to when
+ // the AT_LINK enum changed from 23 to 24.
+ if ((item->getType() == LLAssetType::AT_NONE)
+ || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup())
+ {
+ LL_WARNS(LOG_INV) << "Got bad asset type for item [ name: " << item->getName()
+ << " type: " << item->getType()
+ << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL;
+ return;
+ }
+
// This condition means that we tried to add a link without the baseobj being in memory.
// The item will show up as a broken link.
if (item->getIsBrokenLink())
{
- llwarns << "Add link item without baseobj present ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl;
-// llassert_always(FALSE); // DO NOT MERGE THIS IN. This is an AVP debugging line. If this line triggers, it means that you just loaded in a broken link. Unless that happens because you actually deleted a baseobj without deleting the link, it's indicative of a serious problem (likely with your inventory) and should be diagnosed.
+ LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName()
+ << " itemID: " << item->getUUID()
+ << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL;
+ }
+ if (item->getIsLinkType())
+ {
+ // Add back-link from linked-to UUID.
+ const LLUUID& link_id = item->getUUID();
+ const LLUUID& target_id = item->getLinkedUUID();
+ addBacklinkInfo(link_id, target_id);
}
mItemMap[item->getUUID()] = item;
- //mInventory[item->getUUID()] = item;
}
}
// Empty the entire contents
void LLInventoryModel::empty()
{
-// llinfos << "LLInventoryModel::empty()" << llendl;
+// LL_INFOS(LOG_INV) << "LLInventoryModel::empty()" << LL_ENDL;
std::for_each(
mParentChildCategoryTree.begin(),
mParentChildCategoryTree.end(),
@@ -1814,6 +1756,7 @@ void LLInventoryModel::empty()
mParentChildItemTree.end(),
DeletePairedPointer());
mParentChildItemTree.clear();
+ mBacklinkMMap.clear(); // forget all backlink information.
mCategoryMap.clear(); // remove all references (should delete entries)
mItemMap.clear(); // remove all references (should delete entries)
mLastItem = NULL;
@@ -1825,40 +1768,39 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
if(cat)
{
- bool accounted = false;
S32 version = cat->getVersion();
if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
{
S32 descendents_server = cat->getDescendentCount();
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- getDirectDescendentsOf(update.mCategoryID, cats, items);
- S32 descendents_actual = 0;
- if(cats && items)
- {
- descendents_actual = cats->count() + items->count();
- }
+ S32 descendents_actual = cat->getViewerDescendentCount();
if(descendents_server == descendents_actual)
{
- accounted = true;
descendents_actual += update.mDescendentDelta;
cat->setDescendentCount(descendents_actual);
cat->setVersion(++version);
- llinfos << "accounted: '" << cat->getName() << "' "
- << version << " with " << descendents_actual
- << " descendents." << llendl;
+ LL_DEBUGS(LOG_INV) << "accounted: '" << cat->getName() << "' "
+ << version << " with " << descendents_actual
+ << " descendents." << LL_ENDL;
+ }
+ else
+ {
+ // Error condition, this means that the category did not register that
+ // it got new descendents (perhaps because it is still being loaded)
+ // which means its descendent count will be wrong.
+ LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version:"
+ << version << " due to mismatched descendent count: server == "
+ << descendents_server << ", viewer == " << descendents_actual << LL_ENDL;
}
}
- if(!accounted)
+ else
{
- lldebugs << "No accounting for: '" << cat->getName() << "' "
- << version << llendl;
+ LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version: unknown ("
+ << version << ")" << LL_ENDL;
}
}
else
{
- llwarns << "No category found for update " << update.mCategoryID
- << llendl;
+ LL_WARNS(LOG_INV) << "No category found for update " << update.mCategoryID << LL_ENDL;
}
}
@@ -1887,47 +1829,6 @@ void LLInventoryModel::accountForUpdate(
}
}
-
-/*
-void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
-{
- LLViewerInventoryCategory* cat = getCategory(category_id);
- if(cat)
- {
- S32 version = cat->getVersion();
- if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
- {
- cat->setVersion(version + 1);
- llinfos << "IncrementVersion: " << cat->getName() << " "
- << cat->getVersion() << llendl;
- }
- else
- {
- llinfos << "Attempt to increment version when unknown: "
- << category_id << llendl;
- }
- }
- else
- {
- llinfos << "Attempt to increment category: " << category_id << llendl;
- }
-}
-void LLInventoryModel::incrementCategorySetVersion(
- const std::set<LLUUID>& categories)
-{
- if(!categories.empty())
- {
- std::set<LLUUID>::const_iterator it = categories.begin();
- std::set<LLUUID>::const_iterator end = categories.end();
- for(; it != end; ++it)
- {
- incrementCategoryVersion(*it);
- }
- }
-}
-*/
-
-
LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
const LLUUID& cat_id) const
{
@@ -1949,12 +1850,12 @@ LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
// Shouldn't have to run this, but who knows.
parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
- if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
+ if (cat_it != mParentChildCategoryTree.end() && cat_it->second->size() > 0)
{
return CHILDREN_YES;
}
parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
- if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
+ if (item_it != mParentChildItemTree.end() && item_it->second->size() > 0)
{
return CHILDREN_YES;
}
@@ -1968,14 +1869,7 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
{
S32 descendents_server = cat->getDescendentCount();
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- getDirectDescendentsOf(cat_id, cats, items);
- S32 descendents_actual = 0;
- if(cats && items)
- {
- descendents_actual = cats->count() + items->count();
- }
+ S32 descendents_actual = cat->getViewerDescendentCount();
if(descendents_server == descendents_actual)
{
return true;
@@ -1988,7 +1882,7 @@ bool LLInventoryModel::loadSkeleton(
const LLSD& options,
const LLUUID& owner_id)
{
- lldebugs << "importing inventory skeleton for " << owner_id << llendl;
+ LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;
typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
cat_set_t temp_cats;
@@ -2013,11 +1907,11 @@ bool LLInventoryModel::loadSkeleton(
cat->setUUID(folder_id.asUUID());
cat->setParent(parent_id.asUUID());
- LLAssetType::EType preferred_type = LLAssetType::AT_NONE;
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
LLSD type_default = (*it)["type_default"];
if(type_default.isDefined())
{
- preferred_type = (LLAssetType::EType)type_default.asInteger();
+ preferred_type = (LLFolderType::EType)type_default.asInteger();
}
cat->setPreferredType(preferred_type);
cat->setVersion(version.asInteger());
@@ -2025,7 +1919,7 @@ bool LLInventoryModel::loadSkeleton(
}
else
{
- llwarns << "Unable to import near " << name.asString() << llendl;
+ LL_WARNS(LOG_INV) << "Unable to import near " << name.asString() << LL_ENDL;
rv = false;
}
}
@@ -2037,6 +1931,7 @@ bool LLInventoryModel::loadSkeleton(
update_map_t child_counts;
cat_array_t categories;
item_array_t items;
+ item_array_t possible_broken_links;
cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded.
std::string owner_id_str;
owner_id.toString(owner_id_str);
@@ -2061,16 +1956,17 @@ bool LLInventoryModel::loadSkeleton(
}
else
{
- llinfos << "Unable to gunzip " << gzip_filename << llendl;
+ LL_INFOS(LOG_INV) << "Unable to gunzip " << gzip_filename << LL_ENDL;
}
}
- if(loadFromFile(inventory_filename, categories, items))
+ bool is_cache_obsolete = false;
+ if(loadFromFile(inventory_filename, categories, items, is_cache_obsolete))
{
// We were able to find a cache of files. So, use what we
// found to generate a set of categories we should add. We
// will go through each category loaded and if the version
// does not match, invalidate the version.
- S32 count = categories.count();
+ S32 count = categories.size();
cat_set_t::iterator not_cached = temp_cats.end();
std::set<LLUUID> cached_ids;
for(S32 i = 0; i < count; ++i)
@@ -2084,7 +1980,7 @@ bool LLInventoryModel::loadSkeleton(
LLViewerInventoryCategory* tcat = *cit;
// we can safely ignore anything loaded from file, but
- // not sent down in the skeleton.
+ // not sent down in the skeleton. Must have been removed from inventory.
if(cit == not_cached)
{
continue;
@@ -2121,7 +2017,9 @@ bool LLInventoryModel::loadSkeleton(
// Add all the items loaded which are parented to a
// category with a correctly cached parent
- count = items.count();
+ S32 bad_link_count = 0;
+ S32 good_link_count = 0;
+ S32 recovered_link_count = 0;
cat_map_t::iterator unparented = mCategoryMap.end();
for(item_array_t::const_iterator item_iter = items.begin();
item_iter != items.end();
@@ -2138,16 +2036,56 @@ bool LLInventoryModel::loadSkeleton(
// This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.
if (item->getIsBrokenLink())
{
- llinfos << "Attempted to cached link item without baseobj present ( itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) " << llendl;
- invalid_categories.insert(cit->second);
+ //bad_link_count++;
+ LL_DEBUGS(LOG_INV) << "Attempted to add cached link item without baseobj present ( name: "
+ << item->getName() << " itemID: " << item->getUUID()
+ << " assetID: " << item->getAssetUUID()
+ << " ). Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL;
+ possible_broken_links.push_back(item);
continue;
}
+ else if (item->getIsLinkType())
+ {
+ good_link_count++;
+ }
+ addItem(item);
+ cached_item_count += 1;
+ ++child_counts[cat->getUUID()];
+ }
+ }
+ }
+ if (possible_broken_links.size() > 0)
+ {
+ for(item_array_t::const_iterator item_iter = possible_broken_links.begin();
+ item_iter != possible_broken_links.end();
+ ++item_iter)
+ {
+ LLViewerInventoryItem *item = (*item_iter).get();
+ const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID());
+ const LLViewerInventoryCategory* cat = cit->second.get();
+ if (item->getIsBrokenLink())
+ {
+ bad_link_count++;
+ invalid_categories.insert(cit->second);
+ //LL_INFOS(LOG_INV) << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL;
+ }
+ else
+ {
+ // was marked as broken because of loading order, its actually fine to load
addItem(item);
cached_item_count += 1;
++child_counts[cat->getUUID()];
+ recovered_link_count++;
}
}
+
+ LL_INFOS(LOG_INV) << "Attempted to add " << bad_link_count
+ << " cached link items without baseobj present. "
+ << good_link_count << " link items were successfully added. "
+ << recovered_link_count << " links added in recovery. "
+ << "The corresponding categories were invalidated." << LL_ENDL;
}
+
}
else
{
@@ -2161,6 +2099,18 @@ bool LLInventoryModel::loadSkeleton(
}
}
+ // Invalidate all categories that failed fetching descendents for whatever
+ // reason (e.g. one of the descendents was a broken link).
+ for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin();
+ invalid_cat_it != invalid_categories.end();
+ invalid_cat_it++)
+ {
+ LLViewerInventoryCategory* cat = (*invalid_cat_it).get();
+ cat->setVersion(NO_VERSION);
+ LL_DEBUGS(LOG_INV) << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL;
+ }
+ LL_INFOS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL;
+
// At this point, we need to set the known descendents for each
// category which successfully cached so that we do not
// needlessly fetch descendents for categories which we have.
@@ -2183,111 +2133,24 @@ bool LLInventoryModel::loadSkeleton(
}
}
- // Invalidate all categories that failed fetching descendents for whatever
- // reason (e.g. one of the descendents was a broken link).
- for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin();
- invalid_cat_it != invalid_categories.end();
- invalid_cat_it++)
- {
- LLViewerInventoryCategory* cat = (*invalid_cat_it).get();
- cat->setVersion(NO_VERSION);
- llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl;
- }
-
if(remove_inventory_file)
{
// clean up the gunzipped file.
LLFile::remove(inventory_filename);
}
+ if(is_cache_obsolete)
+ {
+ // If out of date, remove the gzipped file too.
+ LL_WARNS(LOG_INV) << "Inv cache out of date, removing" << LL_ENDL;
+ LLFile::remove(gzip_filename);
+ }
categories.clear(); // will unref and delete entries
}
- llinfos << "Successfully loaded " << cached_category_count
- << " categories and " << cached_item_count << " items from cache."
- << llendl;
-
- return rv;
-}
-
-bool LLInventoryModel::loadMeat(const LLSD& options, const LLUUID& owner_id)
-{
- llinfos << "importing inventory for " << owner_id << llendl;
- bool rv = true;
- for(LLSD::array_const_iterator it = options.beginArray(),
- end = options.endArray(); it != end; ++it)
- {
- LLSD name = (*it)["name"];
- LLSD item_id = (*it)["item_id"];
- LLSD parent_id = (*it)["parent_id"];
- LLSD asset_type = (*it)["type"];
- LLSD data_id = (*it)["data_id"];
- if(name.isDefined()
- && item_id.isDefined()
- && parent_id.isDefined()
- && asset_type.isDefined()
- && data_id.isDefined())
- {
- LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem;
- item->rename(name.asString());
- item->setUUID(item_id.asUUID());
- item->setParent(parent_id.asUUID());
- LLAssetType::EType type = (LLAssetType::EType)asset_type.asInteger();
- item->setType(type);
-
- LLSD llsd_inv_type = (*it)["inv_type"];
- if(llsd_inv_type.isDefined())
- {
- LLInventoryType::EType inv_type = (LLInventoryType::EType)llsd_inv_type.asInteger();
- item->setInventoryType(inv_type);
- }
-
- if(LLAssetType::AT_CALLINGCARD == type)
- {
- LLPermissions perm;
- perm.init(data_id.asUUID(), owner_id, LLUUID::null, LLUUID::null);
- item->setPermissions(perm);
- }
- else
- {
- LLPermissions default_perm;
- default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null);
- LLSD llsd_perm_mask = (*it)["perm_mask"];
- if(llsd_perm_mask.isDefined())
- {
- PermissionMask perm_mask = llsd_perm_mask.asInteger();
- default_perm.initMasks(
- perm_mask, perm_mask, perm_mask, perm_mask, perm_mask);
- }
- else
- {
- default_perm.initMasks(
- PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
- }
- item->setPermissions(default_perm);
- item->setAssetUUID(data_id.asUUID());
- }
-
- LLSD flags = (*it)["flags"];
- if(flags.isDefined())
- {
- // Not sure how well LLSD.asInteger() maps to
- // unsigned long - using strtoul()
- item->setFlags(strtoul(flags.asString().c_str(), NULL, 0));
- }
+ LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count
+ << " categories and " << cached_item_count << " items from cache."
+ << LL_ENDL;
- LLSD time = (*it)["time"];
- if(time.isDefined())
- {
- item->setCreationDate(time.asInteger());
- }
- addItem(item);
- }
- else
- {
- llwarns << "Unable to import near " << name.asString() << llendl;
- rv = false;
- }
- }
return rv;
}
@@ -2296,7 +2159,7 @@ bool LLInventoryModel::loadMeat(const LLSD& options, const LLUUID& owner_id)
// should be sufficient for our needs.
void LLInventoryModel::buildParentChildMap()
{
- llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
+ LL_INFOS(LOG_INV) << "LLInventoryModel::buildParentChildMap()" << LL_ENDL;
// *NOTE: I am skipping the logic around folder version
// synchronization here because it seems if a folder is lost, we
@@ -2313,7 +2176,7 @@ void LLInventoryModel::buildParentChildMap()
for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
{
LLViewerInventoryCategory* cat = cit->second;
- cats.put(cat);
+ cats.push_back(cat);
if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
{
llassert_always(mCategoryLock[cat->getUUID()] == false);
@@ -2341,16 +2204,21 @@ void LLInventoryModel::buildParentChildMap()
// Now we have a structure with all of the categories that we can
// iterate over and insert into the correct place in the child
// category tree.
- S32 count = cats.count();
+ S32 count = cats.size();
S32 i;
S32 lost = 0;
+ cat_array_t lost_cats;
for(i = 0; i < count; ++i)
{
- LLViewerInventoryCategory* cat = cats.get(i);
+ LLViewerInventoryCategory* cat = cats.at(i);
catsp = getUnlockedCatArray(cat->getParentUUID());
- if(catsp)
+ if(catsp &&
+ // Only the two root folders should be children of null.
+ // Others should go to lost & found.
+ (cat->getParentUUID().notNull() ||
+ cat->getPreferredType() == LLFolderType::FT_ROOT_INVENTORY ))
{
- catsp->put(cat);
+ catsp->push_back(cat);
}
else
{
@@ -2360,42 +2228,57 @@ void LLInventoryModel::buildParentChildMap()
// implement it, we would need a set or map of uuid pairs
// which would be (folder_id, new_parent_id) to be sent up
// to the server.
- llinfos << "Lost categroy: " << cat->getUUID() << " - "
- << cat->getName() << llendl;
+ LL_INFOS(LOG_INV) << "Lost category: " << cat->getUUID() << " - "
+ << cat->getName() << LL_ENDL;
++lost;
- // plop it into the lost & found.
- LLAssetType::EType pref = cat->getPreferredType();
- if(LLAssetType::AT_NONE == pref)
- {
- cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
- }
- else if(LLAssetType::AT_CATEGORY == pref)
- {
- // it's the root
- cat->setParent(LLUUID::null);
- }
- else
- {
- // it's a protected folder.
- cat->setParent(gInventory.getRootFolderID());
- }
- cat->updateServer(TRUE);
- catsp = getUnlockedCatArray(cat->getParentUUID());
- if(catsp)
- {
- catsp->put(cat);
- }
- else
- {
- llwarns << "Lost and found Not there!!" << llendl;
- }
+ lost_cats.push_back(cat);
}
}
if(lost)
{
- llwarns << "Found " << lost << " lost categories." << llendl;
+ LL_WARNS(LOG_INV) << "Found " << lost << " lost categories." << LL_ENDL;
}
+ // Do moves in a separate pass to make sure we've properly filed
+ // the FT_LOST_AND_FOUND category before we try to find its UUID.
+ for(i = 0; i<lost_cats.size(); ++i)
+ {
+ LLViewerInventoryCategory *cat = lost_cats.at(i);
+
+ // plop it into the lost & found.
+ LLFolderType::EType pref = cat->getPreferredType();
+ if(LLFolderType::FT_NONE == pref)
+ {
+ cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
+ }
+ else if(LLFolderType::FT_ROOT_INVENTORY == pref)
+ {
+ // it's the root
+ cat->setParent(LLUUID::null);
+ }
+ else
+ {
+ // it's a protected folder.
+ cat->setParent(gInventory.getRootFolderID());
+ }
+ // FIXME note that updateServer() fails with protected
+ // types, so this will not work as intended in that case.
+ cat->updateServer(TRUE);
+ catsp = getUnlockedCatArray(cat->getParentUUID());
+ if(catsp)
+ {
+ catsp->push_back(cat);
+ }
+ else
+ {
+ LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;
+ }
+ }
+
+ const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null);
+ sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin();
+
+
// Now the items. We allocated in the last step, so now all we
// have to do is iterate over the items and put them in the right
// place.
@@ -2406,29 +2289,29 @@ void LLInventoryModel::buildParentChildMap()
for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
{
item = (*iit).second;
- items.put(item);
+ items.push_back(item);
}
}
- count = items.count();
+ count = items.size();
lost = 0;
- std::vector<LLUUID> lost_item_ids;
+ uuid_vec_t lost_item_ids;
for(i = 0; i < count; ++i)
{
LLPointer<LLViewerInventoryItem> item;
- item = items.get(i);
+ item = items.at(i);
itemsp = getUnlockedItemArray(item->getParentUUID());
if(itemsp)
{
- itemsp->put(item);
+ itemsp->push_back(item);
}
else
{
- llinfos << "Lost item: " << item->getUUID() << " - "
- << item->getName() << llendl;
+ LL_INFOS(LOG_INV) << "Lost item: " << item->getUUID() << " - "
+ << item->getName() << LL_ENDL;
++lost;
// plop it into the lost & found.
//
- item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
+ item->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
// move it later using a special message to move items. If
// we update server here, the client might crash.
//item->updateServer();
@@ -2436,21 +2319,21 @@ void LLInventoryModel::buildParentChildMap()
itemsp = getUnlockedItemArray(item->getParentUUID());
if(itemsp)
{
- itemsp->put(item);
+ itemsp->push_back(item);
}
else
{
- llwarns << "Lost and found Not there!!" << llendl;
+ LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL;
}
}
}
if(lost)
{
- llwarns << "Found " << lost << " lost items." << llendl;
+ LL_WARNS(LOG_INV) << "Found " << lost << " lost items." << LL_ENDL;
LLMessageSystem* msg = gMessageSystem;
BOOL start_new_message = TRUE;
- LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
- for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
+ const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+ for(uuid_vec_t::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
{
if(start_new_message)
{
@@ -2483,16 +2366,129 @@ void LLInventoryModel::buildParentChildMap()
cat_array_t* catsp = get_ptr_in_map(mParentChildCategoryTree, agent_inv_root_id);
if(catsp)
{
+ // *HACK - fix root inventory folder
+ // some accounts has pbroken inventory root folders
+
+ std::string name = "My Inventory";
+ LLUUID prev_root_id = mRootFolderID;
+ for (parent_cat_map_t::const_iterator it = mParentChildCategoryTree.begin(),
+ it_end = mParentChildCategoryTree.end(); it != it_end; ++it)
+ {
+ cat_array_t* cat_array = it->second;
+ for (cat_array_t::const_iterator cat_it = cat_array->begin(),
+ cat_it_end = cat_array->end(); cat_it != cat_it_end; ++cat_it)
+ {
+ LLPointer<LLViewerInventoryCategory> category = *cat_it;
+
+ if(category && category->getPreferredType() != LLFolderType::FT_ROOT_INVENTORY)
+ continue;
+ if ( category && 0 == LLStringUtil::compareInsensitive(name, category->getName()) )
+ {
+ if(category->getUUID()!=mRootFolderID)
+ {
+ LLUUID& new_inv_root_folder_id = const_cast<LLUUID&>(mRootFolderID);
+ new_inv_root_folder_id = category->getUUID();
+ }
+ }
+ }
+ }
+
// 'My Inventory',
// root of the agent's inv found.
// The inv tree is built.
mIsAgentInvUsable = true;
- llinfos << "Inventory initialized, notifying observers" << llendl;
- addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
- notifyObservers();
+ // notifyObservers() has been moved to
+ // llstartup/idle_startup() after this func completes.
+ // Allows some system categories to be created before
+ // observers start firing.
}
}
+
+ if (!gInventory.validate())
+ {
+ LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL;
+ }
+}
+
+// Would normally do this at construction but that's too early
+// in the process for gInventory. Have the first requestPost()
+// call set things up.
+void LLInventoryModel::initHttpRequest()
+{
+ if (! mHttpRequestFG)
+ {
+ // Haven't initialized, get to it
+ LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp());
+
+ mHttpRequestFG = new LLCore::HttpRequest;
+ mHttpRequestBG = new LLCore::HttpRequest;
+ mHttpOptions = new LLCore::HttpOptions;
+ mHttpOptions->setTransferTimeout(300);
+ mHttpOptions->setUseRetryAfter(true);
+ // mHttpOptions->setTrace(2); // Do tracing of requests
+ mHttpHeaders = new LLCore::HttpHeaders;
+ mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
+ mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML);
+ mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY);
+ }
+}
+
+void LLInventoryModel::handleResponses(bool foreground)
+{
+ if (foreground && mHttpRequestFG)
+ {
+ mHttpRequestFG->update(0);
+ }
+ else if (! foreground && mHttpRequestBG)
+ {
+ mHttpRequestBG->update(50000L);
+ }
+}
+
+LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground,
+ const std::string & url,
+ const LLSD & body,
+ LLCore::HttpHandler * handler,
+ const char * const message)
+{
+ if (! mHttpRequestFG)
+ {
+ // We do the initialization late and lazily as this class is
+ // statically-constructed and not all the bits are ready at
+ // that time.
+ initHttpRequest();
+ }
+
+ LLCore::HttpRequest * request(foreground ? mHttpRequestFG : mHttpRequestBG);
+ LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+ handle = LLCoreHttpUtil::requestPostWithLLSD(request,
+ mHttpPolicyClass,
+ (foreground ? mHttpPriorityFG : mHttpPriorityBG),
+ url,
+ body,
+ mHttpOptions,
+ mHttpHeaders,
+ handler);
+ if (LLCORE_HTTP_HANDLE_INVALID == handle)
+ {
+ LLCore::HttpStatus status(request->getStatus());
+ LL_WARNS(LOG_INV) << "HTTP POST request failed for " << message
+ << ", Status: " << status.toTerseString()
+ << " Reason: '" << status.toString() << "'"
+ << LL_ENDL;
+ delete handler;
+ }
+ return handle;
+}
+
+void LLInventoryModel::createCommonSystemCategories()
+{
+ gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true);
+ gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true);
+ gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true);
+ gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true);
}
struct LLUUIDAndName
@@ -2527,111 +2523,69 @@ bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
return (mID > rhs.mID);
}
-// Given the current state of the inventory items, figure out the
-// clone information. *FIX: This is sub-optimal, since we can insert
-// this information snurgically, but this makes sure the implementation
-// works before we worry about optimization.
-//void LLInventoryModel::recalculateCloneInformation()
-//{
-// //dumpInventory();
-//
-// // This implements a 'multi-map' like structure to keep track of
-// // how many clones we find.
-// typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t;
-// typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t;
-// clone_map_t clone_map;
-// LLUUIDAndName id_and_name;
-// viewer_item_array_t* clones = NULL;
-// LLViewerInventoryItem* item = NULL;
-// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData();
-// item != NULL;
-// item = (LLViewerInventoryItem*)mItemMap.getNextData())
-// {
-// if(item->getType() == LLAssetType::AT_CALLINGCARD)
-// {
-// // if it's a calling card, we key off of the creator id, not
-// // the asset id.
-// id_and_name.mID = item->getCreatorUUID();
-// }
-// else
-// {
-// // if it's not a calling card, we key clones from the
-// // asset id.
-// id_and_name.mID = item->getAssetUUID();
-// }
-// if(id_and_name.mID == LLUUID::null)
-// {
-// continue;
-// }
-// id_and_name.mName = item->getName();
-// if(clone_map.checkData(id_and_name))
-// {
-// clones = clone_map.getData(id_and_name);
-// }
-// else
-// {
-// clones = new viewer_item_array_t;
-// clone_map.addData(id_and_name, clones);
-// }
-// clones->put(item);
-// }
-//
-// S32 count = 0;
-// for(clones = clone_map.getFirstData();
-// clones != NULL;
-// clones = clone_map.getNextData())
-// {
-// count = clones->count();
-// for(S32 i = 0; i < count; i++)
-// {
-// item = clones->get(i);
-// item->setCloneCount(count - 1);
-// //clones[i] = NULL;
-// }
-// delete clones;
-// }
-// clone_map.removeAllData();
-// //dumpInventory();
-//}
-
// static
bool LLInventoryModel::loadFromFile(const std::string& filename,
LLInventoryModel::cat_array_t& categories,
- LLInventoryModel::item_array_t& items)
+ LLInventoryModel::item_array_t& items,
+ bool &is_cache_obsolete)
{
if(filename.empty())
{
- llerrs << "Filename is Null!" << llendl;
+ LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL;
return false;
}
- llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
+ LL_INFOS(LOG_INV) << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL;
LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
if(!file)
{
- llinfos << "unable to load inventory from: " << filename << llendl;
+ LL_INFOS(LOG_INV) << "unable to load inventory from: " << filename << LL_ENDL;
return false;
}
// *NOTE: This buffer size is hard coded into scanf() below.
char buffer[MAX_STRING]; /*Flawfinder: ignore*/
char keyword[MAX_STRING]; /*Flawfinder: ignore*/
+ char value[MAX_STRING]; /*Flawfinder: ignore*/
+ is_cache_obsolete = true; // Obsolete until proven current
while(!feof(file) && fgets(buffer, MAX_STRING, file))
{
- sscanf(buffer, " %254s", keyword); /* Flawfinder: ignore */
- if(0 == strcmp("inv_category", keyword))
+ sscanf(buffer, " %126s %126s", keyword, value); /* Flawfinder: ignore */
+ if(0 == strcmp("inv_cache_version", keyword))
+ {
+ S32 version;
+ int succ = sscanf(value,"%d",&version);
+ if ((1 == succ) && (version == sCurrentInvCacheVersion))
+ {
+ // Cache is up to date
+ is_cache_obsolete = false;
+ continue;
+ }
+ else
+ {
+ // Cache is out of date
+ break;
+ }
+ }
+ else if(0 == strcmp("inv_category", keyword))
{
+ if (is_cache_obsolete)
+ break;
+
LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
if(inv_cat->importFileLocal(file))
{
- categories.put(inv_cat);
+ categories.push_back(inv_cat);
}
else
{
- llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
+ LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL;
//delete inv_cat; // automatic when inv_cat is reassigned or destroyed
}
}
else if(0 == strcmp("inv_item", keyword))
{
+ if (is_cache_obsolete)
+ break;
+
LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
if( inv_item->importFileLocal(file) )
{
@@ -2641,28 +2595,30 @@ bool LLInventoryModel::loadFromFile(const std::string& filename,
if(inv_item->getUUID().isNull())
{
//delete inv_item; // automatic when inv_cat is reassigned or destroyed
- llwarns << "Ignoring inventory with null item id: "
- << inv_item->getName() << llendl;
+ LL_WARNS(LOG_INV) << "Ignoring inventory with null item id: "
+ << inv_item->getName() << LL_ENDL;
}
else
{
- items.put(inv_item);
+ items.push_back(inv_item);
}
}
else
{
- llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl;
+ LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL;
//delete inv_item; // automatic when inv_cat is reassigned or destroyed
}
}
else
{
- llwarns << "Unknown token in inventory file '" << keyword << "'"
- << llendl;
+ LL_WARNS(LOG_INV) << "Unknown token in inventory file '" << keyword << "'"
+ << LL_ENDL;
}
}
fclose(file);
+ if (is_cache_obsolete)
+ return false;
return true;
}
@@ -2673,18 +2629,19 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
{
if(filename.empty())
{
- llerrs << "Filename is Null!" << llendl;
+ LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL;
return false;
}
- llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
+ LL_INFOS(LOG_INV) << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL;
LLFILE* file = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
if(!file)
{
- llwarns << "unable to save inventory to: " << filename << llendl;
+ LL_WARNS(LOG_INV) << "unable to save inventory to: " << filename << LL_ENDL;
return false;
}
- S32 count = categories.count();
+ fprintf(file, "\tinv_cache_version\t%d\n",sCurrentInvCacheVersion);
+ S32 count = categories.size();
S32 i;
for(i = 0; i < count; ++i)
{
@@ -2695,7 +2652,7 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
}
}
- count = items.count();
+ count = items.size();
for(i = 0; i < count; ++i)
{
items[i]->exportFile(file);
@@ -2727,6 +2684,9 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
processRemoveInventoryFolder,
NULL);
+ msg->setHandlerFuncFast(_PREHASH_RemoveInventoryObjects,
+ processRemoveInventoryObjects,
+ NULL);
//msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
// processExchangeCallingcard,
// NULL);
@@ -2752,7 +2712,7 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**)
{
// do accounting and highlight new items if they arrive
- if (gInventory.messageUpdateCore(msg, true))
+ if (gInventory.messageUpdateCore(msg, true, LLInventoryObserver::UPDATE_CREATE))
{
U32 callback_id;
LLUUID item_id;
@@ -2772,7 +2732,7 @@ void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**)
}
-bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
+bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32 mask)
{
//make sure our added inventory observer is active
start_new_inventory_observer();
@@ -2781,22 +2741,21 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got a inventory update for the wrong agent: " << agent_id
- << llendl;
+ LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id
+ << LL_ENDL;
return false;
}
item_array_t items;
update_map_t update;
S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
- bool all_one_folder = true;
LLUUID folder_id;
// Does this loop ever execute more than once?
for(S32 i = 0; i < count; ++i)
{
LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
titem->unpackMessage(msg, _PREHASH_InventoryData, i);
- lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
- << titem->getUUID() << llendl;
+ LL_DEBUGS(LOG_INV) << "LLInventoryModel::messageUpdateCore() item id: "
+ << titem->getUUID() << LL_ENDL;
items.push_back(titem);
// examine update for changes.
LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
@@ -2820,10 +2779,6 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
{
folder_id = titem->getParentUUID();
}
- else
- {
- all_one_folder = false;
- }
}
if(account)
{
@@ -2831,10 +2786,14 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
}
U32 changes = 0x0;
+ if (account)
+ {
+ mask |= LLInventoryObserver::CREATE;
+ }
//as above, this loop never seems to loop more than once per call
for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
{
- changes |= gInventory.updateItem(*it);
+ changes |= gInventory.updateItem(*it, mask);
}
gInventory.notifyObservers();
gViewerWindow->getWindow()->decBusyCount();
@@ -2843,26 +2802,21 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
}
// static
-void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
+void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label)
{
- lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl;
- LLUUID agent_id, item_id;
- msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
- if(agent_id != gAgent.getID())
- {
- llwarns << "Got a RemoveInventoryItem for the wrong agent."
- << llendl;
- return;
- }
- S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
- std::vector<LLUUID> item_ids;
+ LLUUID item_id;
+ S32 count = msg->getNumberOfBlocksFast(msg_label);
+ LL_DEBUGS(LOG_INV) << "Message has " << count << " item blocks" << LL_ENDL;
+ uuid_vec_t item_ids;
update_map_t update;
for(S32 i = 0; i < count; ++i)
{
- msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+ msg->getUUIDFast(msg_label, _PREHASH_ItemID, item_id, i);
+ LL_DEBUGS(LOG_INV) << "Checking for item-to-be-removed " << item_id << LL_ENDL;
LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
if(itemp)
{
+ LL_DEBUGS(LOG_INV) << "Item will be removed " << item_id << LL_ENDL;
// we only bother with the delete and account if we found
// the item - this is usually a back-up for permissions,
// so frequently the item will already be gone.
@@ -2871,10 +2825,26 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
}
}
gInventory.accountForUpdate(update);
- for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
+ for(uuid_vec_t::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
{
+ LL_DEBUGS(LOG_INV) << "Calling deleteObject " << *it << LL_ENDL;
gInventory.deleteObject(*it);
}
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
+{
+ LL_DEBUGS(LOG_INV) << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL;
+ LLUUID agent_id, item_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ LL_WARNS(LOG_INV) << "Got a RemoveInventoryItem for the wrong agent."
+ << LL_ENDL;
+ return;
+ }
+ LLInventoryModel::removeInventoryItem(agent_id, msg, _PREHASH_InventoryData);
gInventory.notifyObservers();
}
@@ -2882,14 +2852,14 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
void**)
{
- lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl;
+ LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL;
LLUUID agent_id, folder_id, parent_id;
//char name[DB_INV_ITEM_NAME_BUF_SIZE];
msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got an UpdateInventoryFolder for the wrong agent."
- << llendl;
+ LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent."
+ << LL_ENDL;
return;
}
LLPointer<LLViewerInventoryCategory> lastfolder; // hack
@@ -2902,7 +2872,7 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
lastfolder = tfolder;
tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
// make sure it's not a protected folder
- tfolder->setPreferredType(LLAssetType::AT_NONE);
+ tfolder->setPreferredType(LLFolderType::FT_NONE);
folders.push_back(tfolder);
// examine update for changes.
LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
@@ -2931,27 +2901,19 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
gInventory.notifyObservers();
// *HACK: Do the 'show' logic for a new item in the inventory.
- LLFloaterInventory* view = LLFloaterInventory::getActiveInventory();
- if(view)
+ LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel();
+ if (active_panel)
{
- view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
+ active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
}
}
// static
-void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
- void**)
+void LLInventoryModel::removeInventoryFolder(LLUUID agent_id,
+ LLMessageSystem* msg)
{
- lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl;
- LLUUID agent_id, folder_id;
- msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
- if(agent_id != gAgent.getID())
- {
- llwarns << "Got a RemoveInventoryFolder for the wrong agent."
- << llendl;
- return;
- }
- std::vector<LLUUID> folder_ids;
+ LLUUID folder_id;
+ uuid_vec_t folder_ids;
update_map_t update;
S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
for(S32 i = 0; i < count; ++i)
@@ -2965,10 +2927,46 @@ void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
}
}
gInventory.accountForUpdate(update);
- for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
+ for(uuid_vec_t::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
{
gInventory.deleteObject(*it);
}
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
+ void**)
+{
+ LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryFolder()" << LL_ENDL;
+ LLUUID agent_id, session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if(agent_id != gAgent.getID())
+ {
+ LL_WARNS() << "Got a RemoveInventoryFolder for the wrong agent."
+ << LL_ENDL;
+ return;
+ }
+ LLInventoryModel::removeInventoryFolder( agent_id, msg );
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryObjects(LLMessageSystem* msg,
+ void**)
+{
+ LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryObjects()" << LL_ENDL;
+ LLUUID agent_id, session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if(agent_id != gAgent.getID())
+ {
+ LL_WARNS() << "Got a RemoveInventoryObjects for the wrong agent."
+ << LL_ENDL;
+ return;
+ }
+ LLInventoryModel::removeInventoryFolder( agent_id, msg );
+ LLInventoryModel::removeInventoryItem( agent_id, msg, _PREHASH_ItemData );
gInventory.notifyObservers();
}
@@ -2980,8 +2978,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got a SaveAssetIntoInventory message for the wrong agent."
- << llendl;
+ LL_WARNS() << "Got a SaveAssetIntoInventory message for the wrong agent."
+ << LL_ENDL;
return;
}
@@ -2991,8 +2989,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
// The viewer ignores the asset id because this message is only
// used for attachments/objects, so the asset id is not used in
// the viewer anyway.
- lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID="
- << item_id << llendl;
+ LL_DEBUGS() << "LLInventoryModel::processSaveAssetIntoInventory itemID="
+ << item_id << LL_ENDL;
LLViewerInventoryItem* item = gInventory.getItem( item_id );
if( item )
{
@@ -3003,8 +3001,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
}
else
{
- llinfos << "LLInventoryModel::processSaveAssetIntoInventory item"
- " not found: " << item_id << llendl;
+ LL_INFOS() << "LLInventoryModel::processSaveAssetIntoInventory item"
+ " not found: " << item_id << LL_ENDL;
}
if(gViewerWindow)
{
@@ -3027,12 +3025,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl;
+ LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL;
return;
}
LLUUID tid;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
- llinfos << "Bulk inventory: " << tid << llendl;
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LL_DEBUGS("Inventory") << "Bulk inventory: " << tid << LL_ENDL;
+#endif
update_map_t update;
cat_array_t folders;
@@ -3043,9 +3043,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
{
LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
- //llinfos << "unpaked folder '" << tfolder->getName() << "' ("
- // << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
- // << llendl;
+ LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' ("
+ << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
+ << LL_ENDL;
if(tfolder->getUUID().notNull())
{
folders.push_back(tfolder);
@@ -3078,18 +3078,18 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
- std::vector<LLUUID> wearable_ids;
+ uuid_vec_t wearable_ids;
item_array_t items;
std::list<InventoryCallbackInfo> cblist;
for(i = 0; i < count; ++i)
{
LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
titem->unpackMessage(msg, _PREHASH_ItemData, i);
- //llinfos << "unpaked item '" << titem->getName() << "' in "
- // << titem->getParentUUID() << llendl;
+ LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in "
+ << titem->getParentUUID() << LL_ENDL;
U32 callback_id;
msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
- if(titem->getUUID().notNull())
+ if(titem->getUUID().notNull() ) // && callback_id.notNull() )
{
items.push_back(titem);
cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
@@ -3140,20 +3140,20 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
// The incoming inventory could span more than one BulkInventoryUpdate packet,
// so record the transaction ID for this purchase, then wear all clothing
// that comes in as part of that transaction ID. JC
- if (LLFloaterInventory::sWearNewClothing)
+ if (LLInventoryState::sWearNewClothing)
{
- LLFloaterInventory::sWearNewClothingTransactionID = tid;
- LLFloaterInventory::sWearNewClothing = FALSE;
+ LLInventoryState::sWearNewClothingTransactionID = tid;
+ LLInventoryState::sWearNewClothing = FALSE;
}
- if (tid == LLFloaterInventory::sWearNewClothingTransactionID)
+ if (tid.notNull() && tid == LLInventoryState::sWearNewClothingTransactionID)
{
count = wearable_ids.size();
for (i = 0; i < count; ++i)
{
LLViewerInventoryItem* wearable_item;
wearable_item = gInventory.getItem(wearable_ids[i]);
- wear_inventory_item_on_avatar(wearable_item);
+ LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true);
}
}
@@ -3163,6 +3163,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
InventoryCallbackInfo cbinfo = (*inv_it);
gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
}
+
+ //gInventory.validate();
+
// Don't show the inventory. We used to call showAgentInventory here.
//LLFloaterInventory* view = LLFloaterInventory::getActiveInventory();
//if(view)
@@ -3192,8 +3195,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got a UpdateInventoryItem for the wrong agent."
- << llendl;
+ LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." << LL_ENDL;
return;
}
LLUUID parent_id;
@@ -3204,6 +3206,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
msg->getS32("AgentData", "Version", version);
S32 descendents;
msg->getS32("AgentData", "Descendents", descendents);
+
S32 i;
S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
@@ -3218,6 +3221,13 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
for(i = 0; i < count; ++i)
{
titem->unpackMessage(msg, _PREHASH_ItemData, i);
+ // If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added.
+ if (gInventory.getItem(titem->getUUID()))
+ {
+ LL_DEBUGS("Inventory") << "Skipping prefetched item [ Name: " << titem->getName()
+ << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << LL_ENDL;
+ continue;
+ }
gInventory.updateItem(titem);
}
@@ -3227,6 +3237,9 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
{
cat->setVersion(version);
cat->setDescendentCount(descendents);
+ // Get this UUID on the changed list so that whatever's listening for it
+ // will get triggered.
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat->getUUID());
}
gInventory.notifyObservers();
}
@@ -3234,13 +3247,13 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
// static
void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
{
- lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl;
+ LL_DEBUGS() << "LLInventoryModel::processMoveInventoryItem()" << LL_ENDL;
LLUUID agent_id;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
if(agent_id != gAgent.getID())
{
- llwarns << "Got a MoveInventoryItem message for the wrong agent."
- << llendl;
+ LL_WARNS() << "Got a MoveInventoryItem message for the wrong agent."
+ << LL_ENDL;
return;
}
@@ -3259,8 +3272,8 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i);
msg->getString("InventoryData", "NewName", new_name, i);
- lldebugs << "moving item " << item_id << " to folder "
- << folder_id << llendl;
+ LL_DEBUGS() << "moving item " << item_id << " to folder "
+ << folder_id << LL_ENDL;
update_list_t update;
LLCategoryUpdate old_folder(item->getParentUUID(), -1);
update.push_back(old_folder);
@@ -3278,7 +3291,7 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
}
else
{
- llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl;
+ LL_INFOS() << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << LL_ENDL;
}
}
if(anything_changed)
@@ -3289,33 +3302,31 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
//----------------------------------------------------------------------------
-// Trash: LLAssetType::AT_TRASH, "ConfirmEmptyTrash"
-// Lost&Found: LLAssetType::AT_LOST_AND_FOUND, "ConfirmEmptyLostAndFound"
+// Trash: LLFolderType::FT_TRASH, "ConfirmEmptyTrash"
+// Lost&Found: LLFolderType::FT_LOST_AND_FOUND, "ConfirmEmptyLostAndFound"
-bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLAssetType::EType folder_type)
+bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLFolderType::EType preferred_type)
{
- S32 option = LLNotification::getSelectedOption(notification, response);
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0) // YES
{
- LLUUID folder_id = findCategoryUUIDForType(folder_type);
- purgeDescendentsOf(folder_id);
- notifyObservers();
+ const LLUUID folder_id = findCategoryUUIDForType(preferred_type);
+ purge_descendents_of(folder_id, NULL);
}
return false;
}
-void LLInventoryModel::emptyFolderType(const std::string notification, LLAssetType::EType folder_type)
+void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderType::EType preferred_type)
{
if (!notification.empty())
{
- LLNotifications::instance().add(notification, LLSD(), LLSD(),
- boost::bind(&LLInventoryModel::callbackEmptyFolderType, this, _1, _2, folder_type));
+ LLNotificationsUtil::add(notification, LLSD(), LLSD(),
+ boost::bind(&LLInventoryModel::callbackEmptyFolderType, this, _1, _2, preferred_type));
}
else
{
- LLUUID folder_id = findCategoryUUIDForType(folder_type);
- purgeDescendentsOf(folder_id);
- notifyObservers();
+ const LLUUID folder_id = findCategoryUUIDForType(preferred_type);
+ purge_descendents_of(folder_id, NULL);
}
}
@@ -3324,21 +3335,77 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLAssetTy
void LLInventoryModel::removeItem(const LLUUID& item_id)
{
LLViewerInventoryItem* item = getItem(item_id);
- const LLUUID new_parent = findCategoryUUIDForType(LLAssetType::AT_TRASH);
- if (item && item->getParentUUID() != new_parent)
+ if (! item)
{
- LLInventoryModel::update_list_t update;
- LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
- update.push_back(old_folder);
- LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
- update.push_back(new_folder);
- accountForUpdate(update);
+ LL_WARNS("Inventory") << "couldn't find inventory item " << item_id << LL_ENDL;
+ }
+ else
+ {
+ const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if (new_parent.notNull())
+ {
+ LL_INFOS("Inventory") << "Moving to Trash (" << new_parent << "):" << LL_ENDL;
+ changeItemParent(item, new_parent, TRUE);
+ }
+ }
+}
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
- new_item->setParent(new_parent);
- new_item->updateParentOnServer(TRUE);
- updateItem(new_item);
- notifyObservers();
+void LLInventoryModel::removeCategory(const LLUUID& category_id)
+{
+ if (! get_is_category_removable(this, category_id))
+ {
+ return;
+ }
+
+ // Look for any gestures and deactivate them
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ collectDescendents(category_id, descendent_categories, descendent_items, FALSE);
+
+ for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin();
+ iter != descendent_items.end();
+ ++iter)
+ {
+ const LLViewerInventoryItem* item = (*iter);
+ const LLUUID& item_id = item->getUUID();
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureMgr::instance().isGestureActive(item_id))
+ {
+ LLGestureMgr::instance().deactivateGesture(item_id);
+ }
+ }
+
+ LLViewerInventoryCategory* cat = getCategory(category_id);
+ if (cat)
+ {
+ const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if (trash_id.notNull())
+ {
+ changeCategoryParent(cat, trash_id, TRUE);
+ }
+ }
+}
+
+void LLInventoryModel::removeObject(const LLUUID& object_id)
+{
+ LLInventoryObject* obj = getObject(object_id);
+ if (dynamic_cast<LLViewerInventoryItem*>(obj))
+ {
+ removeItem(object_id);
+ }
+ else if (dynamic_cast<LLViewerInventoryCategory*>(obj))
+ {
+ removeCategory(object_id);
+ }
+ else if (obj)
+ {
+ LL_WARNS("Inventory") << "object ID " << object_id
+ << " is an object of unrecognized class "
+ << typeid(*obj).name() << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Inventory") << "object ID " << object_id << " not found" << LL_ENDL;
}
}
@@ -3372,750 +3439,477 @@ void LLInventoryModel::setLibraryOwnerID(const LLUUID& val)
mLibraryOwnerID = val;
}
-//----------------------------------------------------------------------------
-
-// *NOTE: DEBUG functionality
-void LLInventoryModel::dumpInventory() const
+// static
+BOOL LLInventoryModel::getIsFirstTimeInViewer2()
{
- llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
- llinfos << "mCategory[] contains " << mCategoryMap.size() << " items." << llendl;
- for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
- {
- const LLViewerInventoryCategory* cat = cit->second;
- if(cat)
- {
- llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' "
- << cat->getVersion() << " " << cat->getDescendentCount()
- << llendl;
- }
- else
- {
- llinfos << " NULL!" << llendl;
- }
- }
- llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
- for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+ // Do not call this before parentchild map is built.
+ if (!gInventory.mIsAgentInvUsable)
{
- const LLViewerInventoryItem* item = iit->second;
- if(item)
- {
- llinfos << " " << item->getUUID() << " "
- << item->getName() << llendl;
- }
- else
- {
- llinfos << " NULL!" << llendl;
- }
+ LL_WARNS() << "Parent Child Map not yet built; guessing as first time in viewer2." << LL_ENDL;
+ return TRUE;
}
- llinfos << "\n**********************\nEnd Inventory Dump" << llendl;
-}
-///----------------------------------------------------------------------------
-/// LLInventoryCollectFunctor implementations
-///----------------------------------------------------------------------------
+ return sFirstTimeInViewer2;
+}
-// static
-bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(LLInventoryItem* item)
+LLInventoryModel::item_array_t::iterator LLInventoryModel::findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id)
{
- if (!item)
- return false;
-
- bool allowed = false;
- LLVOAvatarSelf* my_avatar = NULL;
+ LLInventoryModel::item_array_t::iterator curr_item = items.begin();
- switch(item->getType())
+ while (curr_item != items.end())
{
- case LLAssetType::AT_CALLINGCARD:
- // not allowed
- break;
-
- case LLAssetType::AT_OBJECT:
- my_avatar = gAgent.getAvatarObject();
- if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
- {
- allowed = true;
- }
- break;
-
- case LLAssetType::AT_BODYPART:
- case LLAssetType::AT_CLOTHING:
- if(!gAgentWearables.isWearingItem(item->getUUID()))
+ if ((*curr_item)->getUUID() == id)
{
- allowed = true;
+ break;
}
- break;
-
- default:
- allowed = true;
- break;
+ ++curr_item;
}
- return allowed;
+ return curr_item;
}
-bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+// static
+// * @param[in, out] items - vector with items to be updated. It should be sorted in a right way
+// * before calling this method.
+// * @param src_item_id - LLUUID of inventory item to be moved in new position
+// * @param dest_item_id - LLUUID of inventory item before (or after) which source item should
+// * be placed.
+// * @param insert_before - bool indicating if src_item_id should be placed before or after
+// * dest_item_id. Default is true.
+void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id, bool insert_before)
{
- if(mType == LLAssetType::AT_CATEGORY)
- {
- if(cat) return TRUE;
- }
- if(item)
+ LLInventoryModel::item_array_t::iterator it_src = findItemIterByUUID(items, src_item_id);
+ LLInventoryModel::item_array_t::iterator it_dest = findItemIterByUUID(items, dest_item_id);
+
+ // If one of the passed UUID is not in the item list, bail out
+ if ((it_src == items.end()) || (it_dest == items.end()))
+ return;
+
+ // Erase the source element from the list, keep a copy before erasing.
+ LLViewerInventoryItem* src_item = *it_src;
+ items.erase(it_src);
+
+ // Note: Target iterator is not valid anymore because the container was changed, so update it.
+ it_dest = findItemIterByUUID(items, dest_item_id);
+
+ // Go to the next element if one wishes to insert after the dest element
+ if (!insert_before)
{
- if(item->getType() == mType) return TRUE;
+ ++it_dest;
}
- return FALSE;
-}
-
-bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-{
- if(mType == LLAssetType::AT_CATEGORY)
+
+ // Reinsert the source item in the right place
+ if (it_dest != items.end())
{
- if(cat) return FALSE;
+ items.insert(it_dest, src_item);
}
- if(item)
+ else
{
- if(item->getType() == mType) return FALSE;
- else return TRUE;
+ // Append to the list if it_dest reached the end
+ items.push_back(src_item);
}
- return TRUE;
}
-bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+//* @param[in] items vector of items in order to be saved.
+/*
+void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items)
{
- if(mType == LLAssetType::AT_CATEGORY)
- {
- if(cat)
- {
- return TRUE;
- }
- }
- if(item)
+ int sortField = 0;
+
+ // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field
+ for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i)
{
- if(item->getType() == mType)
- {
- LLPermissions perm = item->getPermissions();
- if ((perm.getMaskBase() & mPerm) == mPerm)
- {
- return TRUE;
- }
- }
- }
- return FALSE;
-}
+ LLViewerInventoryItem* item = *i;
+ item->setSortField(++sortField);
+ item->setComplete(TRUE);
+ item->updateServer(FALSE);
-//bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-//{
-// if(cat) return FALSE;
-// if(item)
-// {
-// if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD)
-// {
-// if((item->getType() == LLAssetType::AT_CALLINGCARD)
-// && !(item->getCreatorUUID().isNull())
-// && (item->getCreatorUUID() == mItemMap->getCreatorUUID()))
-// {
-// return TRUE;
-// }
-// }
-// else
-// {
-// if((item->getType() == mItemMap->getType())
-// && !(item->getAssetUUID().isNull())
-// && (item->getAssetUUID() == mItemMap->getAssetUUID())
-// && (item->getName() == mItemMap->getName()))
-// {
-// return TRUE;
-// }
-// }
-// }
-// return FALSE;
-//}
+ updateItem(item);
-bool LLBuddyCollector::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- if(item)
- {
- if((LLAssetType::AT_CALLINGCARD == item->getType())
- && (!item->getCreatorUUID().isNull())
- && (item->getCreatorUUID() != gAgent.getID()))
- {
- return true;
- }
+ // Tell the parent folder to refresh its sort order.
+ addChangedMask(LLInventoryObserver::SORT, item->getParentUUID());
}
- return false;
-}
-
-bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- if(item)
- {
- if((LLAssetType::AT_CALLINGCARD == item->getType())
- && (item->getCreatorUUID().notNull())
- && (item->getCreatorUUID() != gAgent.getID()))
- {
- mSeen.insert(item->getCreatorUUID());
- return true;
- }
- }
- return false;
+ notifyObservers();
}
-
-
-bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
+*/
+// See also LLInventorySort where landmarks in the Favorites folder are sorted.
+class LLViewerInventoryItemSort
{
- if(item)
+public:
+ bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b)
{
- if((LLAssetType::AT_CALLINGCARD == item->getType())
- && (item->getCreatorUUID() == mBuddyID))
- {
- return TRUE;
- }
+ return a->getSortField() < b->getSortField();
}
- return FALSE;
-}
+};
+/**
+ * Sorts passed items by LLViewerInventoryItem sort field.
+ *
+ * @param[in, out] items - array of items, not sorted.
+ */
+//static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items)
+//{
+// static LLViewerInventoryItemSort sort_functor;
+// std::sort(items.begin(), items.end(), sort_functor);
+//}
-bool LLNameCategoryCollector::operator()(
- LLInventoryCategory* cat, LLInventoryItem* item)
+// * @param source_item_id - LLUUID of the source item to be moved into new position
+// * @param target_item_id - LLUUID of the target item before which source item should be placed.
+/*
+void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id)
{
- if(cat)
- {
- if (!LLStringUtil::compareInsensitive(mName, cat->getName()))
- {
- return true;
- }
- }
- return false;
-}
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLIsType is_type(LLAssetType::AT_LANDMARK);
+ LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+ gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type);
+ // ensure items are sorted properly before changing order. EXT-3498
+ rearrange_item_order_by_sort_field(items);
+ // update order
+ updateItemsOrder(items, source_item_id, target_item_id);
-///----------------------------------------------------------------------------
-/// Observers
-///----------------------------------------------------------------------------
+ saveItemsOrder(items);
+}
+*/
+//----------------------------------------------------------------------------
-void LLInventoryCompletionObserver::changed(U32 mask)
+// *NOTE: DEBUG functionality
+void LLInventoryModel::dumpInventory() const
{
- // scan through the incomplete items and move or erase them as
- // appropriate.
- if(!mIncomplete.empty())
+ LL_INFOS() << "\nBegin Inventory Dump\n**********************:" << LL_ENDL;
+ LL_INFOS() << "mCategory[] contains " << mCategoryMap.size() << " items." << LL_ENDL;
+ for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
{
- for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ const LLViewerInventoryCategory* cat = cit->second;
+ if(cat)
{
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if(!item)
- {
- it = mIncomplete.erase(it);
- continue;
- }
- if(item->isComplete())
- {
- mComplete.push_back(*it);
- it = mIncomplete.erase(it);
- continue;
- }
- ++it;
+ LL_INFOS() << " " << cat->getUUID() << " '" << cat->getName() << "' "
+ << cat->getVersion() << " " << cat->getDescendentCount()
+ << LL_ENDL;
}
- if(mIncomplete.empty())
+ else
{
- done();
+ LL_INFOS() << " NULL!" << LL_ENDL;
}
- }
-}
-
-void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
-{
- if(id.notNull())
- {
- mIncomplete.push_back(id);
- }
-}
-
-
-void LLInventoryFetchObserver::changed(U32 mask)
-{
- // scan through the incomplete items and move or erase them as
- // appropriate.
- if(!mIncomplete.empty())
+ }
+ LL_INFOS() << "mItemMap[] contains " << mItemMap.size() << " items." << LL_ENDL;
+ for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
{
- for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ const LLViewerInventoryItem* item = iit->second;
+ if(item)
{
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if(!item)
- {
- // BUG: This can cause done() to get called prematurely below.
- // This happens with the LLGestureInventoryFetchObserver that
- // loads gestures at startup. JC
- it = mIncomplete.erase(it);
- continue;
- }
- if(item->isComplete())
- {
- mComplete.push_back(*it);
- it = mIncomplete.erase(it);
- continue;
- }
- ++it;
+ LL_INFOS() << " " << item->getUUID() << " "
+ << item->getName() << LL_ENDL;
}
- if(mIncomplete.empty())
+ else
{
- done();
+ LL_INFOS() << " NULL!" << LL_ENDL;
}
}
- //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl;
- //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
+ LL_INFOS() << "\n**********************\nEnd Inventory Dump" << LL_ENDL;
}
-bool LLInventoryFetchObserver::isEverythingComplete() const
+// Do various integrity checks on model, logging issues found and
+// returning an overall good/bad flag.
+bool LLInventoryModel::validate() const
{
- return mIncomplete.empty();
-}
+ bool valid = true;
-void fetch_items_from_llsd(const LLSD& items_llsd)
-{
- if (!items_llsd.size()) return;
- LLSD body;
- body[0]["cap_name"] = "FetchInventory";
- body[1]["cap_name"] = "FetchLib";
- for (S32 i=0; i<items_llsd.size();i++)
+ if (getRootFolderID().isNull())
{
- if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString())
- {
- body[0]["items"].append(items_llsd[i]);
- continue;
- }
- if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString())
- {
- body[1]["items"].append(items_llsd[i]);
- continue;
- }
+ LL_WARNS() << "no root folder id" << LL_ENDL;
+ valid = false;
}
-
- for (S32 i=0; i<body.size(); i++)
+ if (getLibraryRootFolderID().isNull())
{
- if (0 >= body[i].size()) continue;
- std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString());
+ LL_WARNS() << "no root folder id" << LL_ENDL;
+ valid = false;
+ }
- if (!url.empty())
+ if (mCategoryMap.size() + 1 != mParentChildCategoryTree.size())
+ {
+ // ParentChild should be one larger because of the special entry for null uuid.
+ LL_INFOS() << "unexpected sizes: cat map size " << mCategoryMap.size()
+ << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL;
+ valid = false;
+ }
+ S32 cat_lock = 0;
+ S32 item_lock = 0;
+ S32 desc_unknown_count = 0;
+ S32 version_unknown_count = 0;
+ for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ const LLUUID& cat_id = cit->first;
+ const LLViewerInventoryCategory *cat = cit->second;
+ if (!cat)
{
- body[i]["agent_id"] = gAgent.getID();
- LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i]));
- break;
+ LL_WARNS() << "invalid cat" << LL_ENDL;
+ valid = false;
+ continue;
+ }
+ if (cat_id != cat->getUUID())
+ {
+ LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL;
+ valid = false;
}
- LLMessageSystem* msg = gMessageSystem;
- BOOL start_new_message = TRUE;
- for (S32 j=0; j<body[i]["items"].size(); j++)
+ if (cat->getParentUUID().isNull())
{
- LLSD item_entry = body[i]["items"][j];
- if(start_new_message)
+ if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID())
{
- start_new_message = FALSE;
- msg->newMessageFast(_PREHASH_FetchInventory);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- }
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID());
- msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID());
- if(msg->isSendFull(NULL))
- {
- start_new_message = TRUE;
- gAgent.sendReliableMessage();
+ LL_WARNS() << "cat " << cat_id << " has no parent, but is not root ("
+ << getRootFolderID() << ") or library root ("
+ << getLibraryRootFolderID() << ")" << LL_ENDL;
}
}
- if(!start_new_message)
+ cat_array_t* cats;
+ item_array_t* items;
+ getDirectDescendentsOf(cat_id,cats,items);
+ if (!cats || !items)
{
- gAgent.sendReliableMessage();
+ LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL;
+ valid = false;
+ continue;
}
- }
-}
-
-void LLInventoryFetchObserver::fetchItems(
- const LLInventoryFetchObserver::item_ref_t& ids)
-{
- LLUUID owner_id;
- LLSD items_llsd;
- for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it)
- {
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if(item)
+ if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
{
- if(item->isComplete())
- {
- // It's complete, so put it on the complete container.
- mComplete.push_back(*it);
- continue;
- }
- else
- {
- owner_id = item->getPermissions().getOwner();
- }
+ desc_unknown_count++;
}
- else
+ else if (cats->size() + items->size() != cat->getDescendentCount())
{
- // assume it's agent inventory.
- owner_id = gAgent.getID();
+ LL_WARNS() << "invalid desc count for " << cat_id << " name [" << cat->getName()
+ << "] parent " << cat->getParentUUID()
+ << " cached " << cat->getDescendentCount()
+ << " expected " << cats->size() << "+" << items->size()
+ << "=" << cats->size() +items->size() << LL_ENDL;
+ valid = false;
}
-
- // It's incomplete, so put it on the incomplete container, and
- // pack this on the message.
- mIncomplete.push_back(*it);
-
- // Prepare the data to fetch
- LLSD item_entry;
- item_entry["owner_id"] = owner_id;
- item_entry["item_id"] = (*it);
- items_llsd.append(item_entry);
- }
- fetch_items_from_llsd(items_llsd);
-}
-
-// virtual
-void LLInventoryFetchDescendentsObserver::changed(U32 mask)
-{
- for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
- if(!cat)
+ if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)
{
- it = mIncompleteFolders.erase(it);
- continue;
+ version_unknown_count++;
}
- if(isComplete(cat))
+ if (mCategoryLock.count(cat_id))
{
- mCompleteFolders.push_back(*it);
- it = mIncompleteFolders.erase(it);
- continue;
+ cat_lock++;
}
- ++it;
- }
- if(mIncompleteFolders.empty())
- {
- done();
- }
-}
-
-void LLInventoryFetchDescendentsObserver::fetchDescendents(
- const folder_ref_t& ids)
-{
- for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
- if(!cat) continue;
- if(!isComplete(cat))
+ if (mItemLock.count(cat_id))
{
- cat->fetchDescendents(); //blindly fetch it without seeing if anything else is fetching it.
- mIncompleteFolders.push_back(*it); //Add to list of things being downloaded for this observer.
+ item_lock++;
}
- else
+ for (S32 i = 0; i<items->size(); i++)
{
- mCompleteFolders.push_back(*it);
- }
- }
-}
+ LLViewerInventoryItem *item = items->at(i);
-bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const
-{
- return mIncompleteFolders.empty();
-}
+ if (!item)
+ {
+ LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL;
+ valid = false;
+ continue;
+ }
-bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat)
-{
- S32 version = cat->getVersion();
- S32 descendents = cat->getDescendentCount();
- if((LLViewerInventoryCategory::VERSION_UNKNOWN == version)
- || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents))
- {
- return false;
- }
- // it might be complete - check known descendents against
- // currently available.
- LLInventoryModel::cat_array_t* cats;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
- if(!cats || !items)
- {
- // bit of a hack - pretend we're done if they are gone or
- // incomplete. should never know, but it would suck if this
- // kept tight looping because of a corrupt memory state.
- return true;
- }
- S32 known = cats->count() + items->count();
- if(descendents == known)
- {
- // hey - we're done.
- return true;
- }
- return false;
-}
+ const LLUUID& item_id = item->getUUID();
+
+ if (item->getParentUUID() != cat_id)
+ {
+ LL_WARNS() << "wrong parent for " << item_id << " found "
+ << item->getParentUUID() << " expected " << cat_id
+ << LL_ENDL;
+ valid = false;
+ }
-void LLInventoryFetchComboObserver::changed(U32 mask)
-{
- if(!mIncompleteItems.empty())
- {
- for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); )
- {
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if(!item)
+
+ // Entries in items and mItemMap should correspond.
+ item_map_t::const_iterator it = mItemMap.find(item_id);
+ if (it == mItemMap.end())
{
- it = mIncompleteItems.erase(it);
- continue;
+ LL_WARNS() << "item " << item_id << " found as child of "
+ << cat_id << " but not in top level mItemMap" << LL_ENDL;
+ valid = false;
+ }
+ else
+ {
+ LLViewerInventoryItem *top_item = it->second;
+ if (top_item != item)
+ {
+ LL_WARNS() << "item mismatch, item_id " << item_id
+ << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL;
+ }
}
- if(item->isComplete())
+
+ // Topmost ancestor should be root or library.
+ LLUUID topmost_ancestor_id;
+ bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id);
+ if (!found)
{
- mCompleteItems.push_back(*it);
- it = mIncompleteItems.erase(it);
- continue;
+ LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL;
+ valid = false;
+ }
+ else
+ {
+ if (topmost_ancestor_id != getRootFolderID() &&
+ topmost_ancestor_id != getLibraryRootFolderID())
+ {
+ LL_WARNS() << "unrecognized top level ancestor for " << item_id
+ << " got " << topmost_ancestor_id
+ << " expected " << getRootFolderID()
+ << " or " << getLibraryRootFolderID() << LL_ENDL;
+ valid = false;
+ }
}
- ++it;
}
- }
- if(!mIncompleteFolders.empty())
- {
- for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
+
+ // Does this category appear as a child of its supposed parent?
+ const LLUUID& parent_id = cat->getParentUUID();
+ if (!parent_id.isNull())
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
- if(!cat)
+ cat_array_t* cats;
+ item_array_t* items;
+ getDirectDescendentsOf(parent_id,cats,items);
+ if (!cats)
{
- it = mIncompleteFolders.erase(it);
- continue;
+ LL_WARNS() << "cat " << cat_id << " name [" << cat->getName()
+ << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL;
+ valid = false;
}
- if(gInventory.isCategoryComplete(*it))
+ else
{
- mCompleteFolders.push_back(*it);
- it = mIncompleteFolders.erase(it);
- continue;
+ bool found = false;
+ for (S32 i = 0; i<cats->size(); i++)
+ {
+ LLViewerInventoryCategory *kid_cat = cats->at(i);
+ if (kid_cat == cat)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ LL_WARNS() << "cat " << cat_id << " name [" << cat->getName()
+ << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL;
+ }
}
- ++it;
}
}
- if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty())
- {
- mDone = true;
- done();
- }
-}
-void LLInventoryFetchComboObserver::fetch(
- const folder_ref_t& folder_ids,
- const item_ref_t& item_ids)
-{
- lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl;
- for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit)
+ for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
{
- LLViewerInventoryCategory* cat = gInventory.getCategory(*fit);
- if(!cat) continue;
- if(!gInventory.isCategoryComplete(*fit))
+ const LLUUID& item_id = iit->first;
+ LLViewerInventoryItem *item = iit->second;
+ if (item->getUUID() != item_id)
{
- cat->fetchDescendents();
- lldebugs << "fetching folder " << *fit <<llendl;
- mIncompleteFolders.push_back(*fit);
+ LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL;
+ valid = false;
}
- else
- {
- mCompleteFolders.push_back(*fit);
- lldebugs << "completing folder " << *fit <<llendl;
- }
- }
- // Now for the items - we fetch everything which is not a direct
- // descendent of an incomplete folder because the item will show
- // up in an inventory descendents message soon enough so we do not
- // have to fetch it individually.
- LLSD items_llsd;
- LLUUID owner_id;
- for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit)
- {
- LLViewerInventoryItem* item = gInventory.getItem(*iit);
- if(!item)
- {
- lldebugs << "uanble to find item " << *iit << llendl;
- continue;
- }
- if(item->isComplete())
+ const LLUUID& parent_id = item->getParentUUID();
+ if (parent_id.isNull())
{
- // It's complete, so put it on the complete container.
- mCompleteItems.push_back(*iit);
- lldebugs << "completing item " << *iit << llendl;
- continue;
+ LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL;
}
else
{
- mIncompleteItems.push_back(*iit);
- owner_id = item->getPermissions().getOwner();
- }
- if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end())
- {
- LLSD item_entry;
- item_entry["owner_id"] = owner_id;
- item_entry["item_id"] = (*iit);
- items_llsd.append(item_entry);
+ cat_array_t* cats;
+ item_array_t* items;
+ getDirectDescendentsOf(parent_id,cats,items);
+ if (!items)
+ {
+ LL_WARNS() << "item " << item_id << " name [" << item->getName()
+ << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL;
+ }
+ else
+ {
+ bool found = false;
+ for (S32 i=0; i<items->size(); ++i)
+ {
+ if (items->at(i) == item)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ LL_WARNS() << "item " << item_id << " name [" << item->getName()
+ << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL;
+ }
+ }
+
}
- else
+ // Link checking
+ if (item->getIsLinkType())
{
- lldebugs << "not worrying about " << *iit << llendl;
- }
- }
- fetch_items_from_llsd(items_llsd);
-}
-
-void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
-{
- if(id.notNull())
- {
- mMIA.push_back(id);
- }
-}
+ const LLUUID& link_id = item->getUUID();
+ const LLUUID& target_id = item->getLinkedUUID();
+ LLViewerInventoryItem *target_item = getItem(target_id);
+ LLViewerInventoryCategory *target_cat = getCategory(target_id);
+ // Linked-to UUID should have back reference to this link.
+ if (!hasBacklinkInfo(link_id, target_id))
+ {
+ LL_WARNS() << "link " << item->getUUID() << " type " << item->getActualType()
+ << " missing backlink info at target_id " << target_id
+ << LL_ENDL;
+ }
+ // Links should have referents.
+ if (item->getActualType() == LLAssetType::AT_LINK && !target_item)
+ {
+ LL_WARNS() << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+ }
+ else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat)
+ {
+ LL_WARNS() << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL;
+ }
+ if (target_item && target_item->getIsLinkType())
+ {
+ LL_WARNS() << "link " << item->getName() << " references a link item "
+ << target_item->getName() << " " << target_item->getUUID() << LL_ENDL;
+ }
-void LLInventoryExistenceObserver::changed(U32 mask)
-{
- // scan through the incomplete items and move or erase them as
- // appropriate.
- if(!mMIA.empty())
- {
- for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); )
- {
- LLViewerInventoryItem* item = gInventory.getItem(*it);
- if(!item)
+ // Links should not have backlinks.
+ std::pair<backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range = mBacklinkMMap.equal_range(link_id);
+ if (range.first != range.second)
{
- ++it;
- continue;
+ LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL;
}
- mExist.push_back(*it);
- it = mMIA.erase(it);
}
- if(mMIA.empty())
+ else
{
- done();
+ // Check the backlinks of a non-link item.
+ const LLUUID& target_id = item->getUUID();
+ std::pair<backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range = mBacklinkMMap.equal_range(target_id);
+ for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it)
+ {
+ const LLUUID& link_id = it->second;
+ LLViewerInventoryItem *link_item = getItem(link_id);
+ if (!link_item || !link_item->getIsLinkType())
+ {
+ LL_WARNS() << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL;
+ }
+ }
}
}
-}
-
-void LLInventoryAddedObserver::changed(U32 mask)
-{
- if(!(mask & LLInventoryObserver::ADD))
- {
- return;
- }
-
- // *HACK: If this was in response to a packet off
- // the network, figure out which item was updated.
- LLMessageSystem* msg = gMessageSystem;
-
- std::string msg_name;
- if (mMessageName.empty())
- {
- msg_name = msg->getMessageName();
- }
- else
- {
- msg_name = mMessageName;
- }
-
- if (msg_name.empty())
- {
- return;
- }
- // We only want newly created inventory items. JC
- if ( msg_name != "UpdateCreateInventoryItem")
+ if (cat_lock > 0 || item_lock > 0)
{
- return;
+ LL_INFOS() << "Found locks on some categories: sub-cat arrays "
+ << cat_lock << ", item arrays " << item_lock << LL_ENDL;
}
-
- LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
- S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
- for(S32 i = 0; i < num_blocks; ++i)
+ if (desc_unknown_count != 0)
{
- titem->unpackMessage(msg, _PREHASH_InventoryData, i);
- if (!(titem->getUUID().isNull()))
- {
- //we don't do anything with null keys
- mAdded.push_back(titem->getUUID());
- }
+ LL_INFOS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL;
}
- if (!mAdded.empty())
+ if (version_unknown_count != 0)
{
- done();
+ LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL;
}
-}
-LLInventoryTransactionObserver::LLInventoryTransactionObserver(
- const LLTransactionID& transaction_id) :
- mTransactionID(transaction_id)
-{
-}
+ LL_INFOS() << "Validate done, valid = " << (U32) valid << LL_ENDL;
-void LLInventoryTransactionObserver::changed(U32 mask)
-{
- if(mask & LLInventoryObserver::ADD)
- {
- // This could be it - see if we are processing a bulk update
- LLMessageSystem* msg = gMessageSystem;
- if(msg->getMessageName()
- && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
- {
- // we have a match for the message - now check the
- // transaction id.
- LLUUID id;
- msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
- if(id == mTransactionID)
- {
- // woo hoo, we found it
- folder_ref_t folders;
- item_ref_t items;
- S32 count;
- count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
- S32 i;
- for(i = 0; i < count; ++i)
- {
- msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
- if(id.notNull())
- {
- folders.push_back(id);
- }
- }
- count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
- for(i = 0; i < count; ++i)
- {
- msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
- if(id.notNull())
- {
- items.push_back(id);
- }
- }
-
- // call the derived class the implements this method.
- done(folders, items);
- }
- }
- }
-}
-
-
-///----------------------------------------------------------------------------
-/// LLAssetIDMatches
-///----------------------------------------------------------------------------
-bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-{
- return (item && item->getAssetUUID() == mAssetID);
-}
-
-///----------------------------------------------------------------------------
-/// LLLinkedItemIDMatches
-///----------------------------------------------------------------------------
-bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-{
- return (item &&
- (item->getIsLinkType()) &&
- (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID.
+ return valid;
}
///----------------------------------------------------------------------------
@@ -4123,7 +3917,7 @@ bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem
///----------------------------------------------------------------------------
-/*
+#if 0
BOOL decompress_file(const char* src_filename, const char* dst_filename)
{
BOOL rv = FALSE;
@@ -4162,4 +3956,177 @@ BOOL decompress_file(const char* src_filename, const char* dst_filename)
if(dst != NULL) fclose(dst);
return rv;
}
-*/
+#endif
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryModel::FetchItemHttpHandler
+///----------------------------------------------------------------------------
+
+LLInventoryModel::FetchItemHttpHandler::FetchItemHttpHandler(const LLSD & request_sd)
+ : LLCore::HttpHandler(),
+ mRequestSD(request_sd)
+{}
+
+LLInventoryModel::FetchItemHttpHandler::~FetchItemHttpHandler()
+{}
+
+void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle handle,
+ LLCore::HttpResponse * response)
+{
+ do // Single-pass do-while used for common exit handling
+ {
+ LLCore::HttpStatus status(response->getStatus());
+ // status = LLCore::HttpStatus(404); // Dev tool to force error handling
+ if (! status)
+ {
+ processFailure(status, response);
+ break; // Goto common exit
+ }
+
+ LLCore::BufferArray * body(response->getBody());
+ // body = NULL; // Dev tool to force error handling
+ if (! body || ! body->size())
+ {
+ LL_WARNS(LOG_INV) << "Missing data in inventory item query." << LL_ENDL;
+ processFailure("HTTP response for inventory item query missing body", response);
+ break; // Goto common exit
+ }
+
+ // body->write(0, "Garbage Response", 16); // Dev tool to force error handling
+ LLSD body_llsd;
+ if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd))
+ {
+ // INFOS-level logging will occur on the parsed failure
+ processFailure("HTTP response for inventory item query has malformed LLSD", response);
+ break; // Goto common exit
+ }
+
+ // Expect top-level structure to be a map
+ // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling
+ if (! body_llsd.isMap())
+ {
+ processFailure("LLSD response for inventory item not a map", response);
+ break; // Goto common exit
+ }
+
+ // Check for 200-with-error failures
+ //
+ // Original Responder-based serivce model didn't check for these errors.
+ // It may be more robust to ignore this condition. With aggregated requests,
+ // an error in one inventory item might take down the entire request.
+ // So if this instead broke up the aggregated items into single requests,
+ // maybe that would make progress. Or perhaps there's structured information
+ // that can tell us what went wrong. Need to dig into this and firm up
+ // the API.
+ //
+ // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling
+ // body_llsd["error"]["identifier"] = "Development";
+ // body_llsd["error"]["message"] = "You left development code in the viewer";
+ if (body_llsd.has("error"))
+ {
+ processFailure("Inventory application error (200-with-error)", response);
+ break; // Goto common exit
+ }
+
+ // Okay, process data if possible
+ processData(body_llsd, response);
+ }
+ while (false);
+
+ // Must delete on completion.
+ delete this;
+}
+
+void LLInventoryModel::FetchItemHttpHandler::processData(LLSD & content, LLCore::HttpResponse * response)
+{
+ start_new_inventory_observer();
+
+#if 0
+ LLUUID agent_id;
+ agent_id = content["agent_id"].asUUID();
+ if (agent_id != gAgent.getID())
+ {
+ LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id
+ << LL_ENDL;
+ return;
+ }
+#endif
+
+ LLInventoryModel::item_array_t items;
+ LLInventoryModel::update_map_t update;
+ LLUUID folder_id;
+ LLSD content_items(content["items"]);
+ const S32 count(content_items.size());
+
+ // Does this loop ever execute more than once?
+ for (S32 i(0); i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ titem->unpackMessage(content_items[i]);
+
+ LL_DEBUGS(LOG_INV) << "ItemHttpHandler::httpSuccess item id: "
+ << titem->getUUID() << LL_ENDL;
+ items.push_back(titem);
+
+ // examine update for changes.
+ LLViewerInventoryItem * itemp(gInventory.getItem(titem->getUUID()));
+
+ if (itemp)
+ {
+ if (titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ }
+
+ if (folder_id.isNull())
+ {
+ folder_id = titem->getParentUUID();
+ }
+ }
+
+ // as above, this loop never seems to loop more than once per call
+ U32 changes(0U);
+ for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ changes |= gInventory.updateItem(*it);
+ }
+ // *HUH: Have computed 'changes', nothing uses it.
+
+ gInventory.notifyObservers();
+ gViewerWindow->getWindow()->decBusyCount();
+}
+
+
+void LLInventoryModel::FetchItemHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response)
+{
+ const std::string & ct(response->getContentType());
+ LL_WARNS(LOG_INV) << "Inventory item fetch failure\n"
+ << "[Status: " << status.toTerseString() << "]\n"
+ << "[Reason: " << status.toString() << "]\n"
+ << "[Content-type: " << ct << "]\n"
+ << "[Content (abridged): "
+ << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL;
+ gInventory.notifyObservers();
+}
+
+void LLInventoryModel::FetchItemHttpHandler::processFailure(const char * const reason, LLCore::HttpResponse * response)
+{
+ LL_WARNS(LOG_INV) << "Inventory item fetch failure\n"
+ << "[Status: internal error]\n"
+ << "[Reason: " << reason << "]\n"
+ << "[Content (abridged): "
+ << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL;
+ gInventory.notifyObservers();
+}
+