diff options
| -rwxr-xr-x | BuildParams | 1 | ||||
| -rw-r--r-- | indra/llinventory/llfoldertype.cpp | 117 | ||||
| -rw-r--r-- | indra/llinventory/llfoldertype.h | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 13 | ||||
| -rw-r--r-- | indra/newview/llattachmentsmgr.cpp | 112 | ||||
| -rw-r--r-- | indra/newview/llfavoritesbar.cpp | 10 | ||||
| -rw-r--r-- | indra/newview/llinventorymodel.cpp | 408 | ||||
| -rw-r--r-- | indra/newview/llinventorymodel.h | 27 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 8 | ||||
| -rw-r--r-- | indra/newview/llviewerstats.cpp | 6 | 
10 files changed, 516 insertions, 188 deletions
diff --git a/BuildParams b/BuildParams index c5f96d5ee3..e55c0c7bda 100755 --- a/BuildParams +++ b/BuildParams @@ -36,6 +36,7 @@ buildscripts_shared_more_NAMEs="build_secrets build_variables"  ################################################################  viewer_channel = "Second Life Test" +  ################################################################  # Special packaging parameters.  # These parameters can be used to create additional packages diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index 7241b3c0c2..675da65af2 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -37,15 +37,22 @@  struct FolderEntry : public LLDictionaryEntry  {  	FolderEntry(const std::string &type_name, // 8 character limit! -				bool is_protected) // can the viewer change categories of this type? +				bool is_protected, // can the viewer change categories of this type? +				bool is_automatic, // always made before first login?  +				bool is_singleton  // should exist as a unique copy under root +		)   		:  	LLDictionaryEntry(type_name), -	mIsProtected(is_protected) +	mIsProtected(is_protected), +	mIsAutomatic(is_automatic), +	mIsSingleton(is_singleton)  	{  		llassert(type_name.length() <= 8);  	}  	const bool mIsProtected; +	const bool mIsAutomatic; +	const bool mIsSingleton;  };  class LLFolderDictionary : public LLSingleton<LLFolderDictionary>, @@ -59,50 +66,64 @@ protected:  	}  }; +// Folder types +//  +// PROTECTED means that folders of this type can't be moved, deleted +// or otherwise modified by the viewer. +//  +// SINGLETON means that there should always be exactly one folder of +// this type, and it should be the root or a child of the root. This +// is true for most types of folders. +// +// AUTOMATIC means that a copy of this folder should be created under +// the root before the user ever logs in, and should never be created +// from the viewer. A missing AUTOMATIC folder should be treated as a +// fatal error by the viewer, since it indicates either corrupted +// inventory or a failure in the inventory services. +//  LLFolderDictionary::LLFolderDictionary()  { -	//       													    TYPE NAME	PROTECTED -	//      													   |-----------|---------| -	addEntry(LLFolderType::FT_TEXTURE, 				new FolderEntry("texture",	TRUE)); -	addEntry(LLFolderType::FT_SOUND, 				new FolderEntry("sound",	TRUE)); -	addEntry(LLFolderType::FT_CALLINGCARD, 			new FolderEntry("callcard",	TRUE)); -	addEntry(LLFolderType::FT_LANDMARK, 			new FolderEntry("landmark",	TRUE)); -	addEntry(LLFolderType::FT_CLOTHING, 			new FolderEntry("clothing",	TRUE)); -	addEntry(LLFolderType::FT_OBJECT, 				new FolderEntry("object",	TRUE)); -	addEntry(LLFolderType::FT_NOTECARD, 			new FolderEntry("notecard",	TRUE)); -	addEntry(LLFolderType::FT_ROOT_INVENTORY, 		new FolderEntry("root_inv",	TRUE)); -	addEntry(LLFolderType::FT_LSL_TEXT, 			new FolderEntry("lsltext",	TRUE)); -	addEntry(LLFolderType::FT_BODYPART, 			new FolderEntry("bodypart",	TRUE)); -	addEntry(LLFolderType::FT_TRASH, 				new FolderEntry("trash",	TRUE)); -	addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, 	new FolderEntry("snapshot", TRUE)); -	addEntry(LLFolderType::FT_LOST_AND_FOUND, 		new FolderEntry("lstndfnd",	TRUE)); -	addEntry(LLFolderType::FT_ANIMATION, 			new FolderEntry("animatn",	TRUE)); -	addEntry(LLFolderType::FT_GESTURE, 				new FolderEntry("gesture",	TRUE)); -	addEntry(LLFolderType::FT_FAVORITE, 			new FolderEntry("favorite",	TRUE)); +	//       													    TYPE NAME, PROTECTED, AUTOMATIC, SINGLETON +	addEntry(LLFolderType::FT_TEXTURE, 				new FolderEntry("texture",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_SOUND, 				new FolderEntry("sound",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_CALLINGCARD, 			new FolderEntry("callcard",	TRUE, TRUE, FALSE)); +	addEntry(LLFolderType::FT_LANDMARK, 			new FolderEntry("landmark",	TRUE, FALSE, FALSE)); +	addEntry(LLFolderType::FT_CLOTHING, 			new FolderEntry("clothing",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_OBJECT, 				new FolderEntry("object",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_NOTECARD, 			new FolderEntry("notecard",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_ROOT_INVENTORY, 		new FolderEntry("root_inv",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_LSL_TEXT, 			new FolderEntry("lsltext",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_BODYPART, 			new FolderEntry("bodypart",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_TRASH, 				new FolderEntry("trash",	TRUE, FALSE, TRUE)); +	addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, 	new FolderEntry("snapshot", TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_LOST_AND_FOUND, 		new FolderEntry("lstndfnd",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_ANIMATION, 			new FolderEntry("animatn",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_GESTURE, 				new FolderEntry("gesture",	TRUE, TRUE, TRUE)); +	addEntry(LLFolderType::FT_FAVORITE, 			new FolderEntry("favorite",	TRUE, FALSE, TRUE));  	for (S32 ensemble_num = S32(LLFolderType::FT_ENSEMBLE_START); ensemble_num <= S32(LLFolderType::FT_ENSEMBLE_END); ensemble_num++)  	{ -		addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE));  +		addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE, FALSE, FALSE)); // Not used  	} -	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new FolderEntry("current",	TRUE)); -	addEntry(LLFolderType::FT_OUTFIT, 				new FolderEntry("outfit",	FALSE)); -	addEntry(LLFolderType::FT_MY_OUTFITS, 			new FolderEntry("my_otfts",	TRUE)); +	addEntry(LLFolderType::FT_CURRENT_OUTFIT, 		new FolderEntry("current",	TRUE, FALSE, TRUE)); +	addEntry(LLFolderType::FT_OUTFIT, 				new FolderEntry("outfit",	FALSE, FALSE, FALSE)); +	addEntry(LLFolderType::FT_MY_OUTFITS, 			new FolderEntry("my_otfts",	TRUE, FALSE, TRUE)); -	addEntry(LLFolderType::FT_MESH, 				new FolderEntry("mesh",	TRUE)); +	addEntry(LLFolderType::FT_MESH, 				new FolderEntry("mesh",		TRUE, FALSE, FALSE)); // Not used? -	addEntry(LLFolderType::FT_INBOX, 				new FolderEntry("inbox",	TRUE)); -	addEntry(LLFolderType::FT_OUTBOX, 				new FolderEntry("outbox",	TRUE)); +	addEntry(LLFolderType::FT_INBOX, 				new FolderEntry("inbox",	TRUE, FALSE, TRUE)); +	addEntry(LLFolderType::FT_OUTBOX, 				new FolderEntry("outbox",	TRUE, FALSE, FALSE)); -	addEntry(LLFolderType::FT_BASIC_ROOT,			new FolderEntry("basic_rt", TRUE)); +	addEntry(LLFolderType::FT_BASIC_ROOT,			new FolderEntry("basic_rt", TRUE, FALSE, FALSE));  -	addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE)); -	addEntry(LLFolderType::FT_MARKETPLACE_STOCK,    new FolderEntry("stock",    FALSE)); -	addEntry(LLFolderType::FT_MARKETPLACE_VERSION,  new FolderEntry("version",    FALSE)); +	addEntry(LLFolderType::FT_MARKETPLACE_LISTINGS, new FolderEntry("merchant", FALSE, FALSE, FALSE)); +	addEntry(LLFolderType::FT_MARKETPLACE_STOCK,    new FolderEntry("stock",    FALSE, FALSE, FALSE)); +	addEntry(LLFolderType::FT_MARKETPLACE_VERSION,  new FolderEntry("version",  FALSE, FALSE, FALSE)); -    addEntry(LLFolderType::FT_SETTINGS,             new FolderEntry("settings", TRUE)); +    addEntry(LLFolderType::FT_SETTINGS,             new FolderEntry("settings", TRUE, FALSE, TRUE)); -	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE)); +	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE, FALSE, FALSE));  };  // static @@ -126,8 +147,8 @@ const std::string &LLFolderType::lookup(LLFolderType::EType folder_type)  }  // static -// Only ensembles and plain folders aren't protected.  "Protected" means -// you can't change certain properties such as their type. +// Only plain folders and a few other types aren't protected.  "Protected" means +// you can't move, deleted, or change certain properties such as their type.  bool LLFolderType::lookupIsProtectedType(EType folder_type)  {  	const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); @@ -138,6 +159,32 @@ bool LLFolderType::lookupIsProtectedType(EType folder_type)  	}  	return true;  } +  +// static +// Is this folder type automatically created outside the viewer?  +bool LLFolderType::lookupIsAutomaticType(EType folder_type) +{ +	const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); +	const FolderEntry *entry = dict->lookup(folder_type); +	if (entry) +	{ +		return entry->mIsAutomatic; +	} +	return true; +} + +// static +// Should this folder always exist as a single copy under (or as) the root? +bool LLFolderType::lookupIsSingletonType(EType folder_type) +{ +	const LLFolderDictionary *dict = LLFolderDictionary::getInstance(); +	const FolderEntry *entry = dict->lookup(folder_type); +	if (entry) +	{ +		return entry->mIsSingleton; +	} +	return true; +}  // static  bool LLFolderType::lookupIsEnsembleType(EType folder_type) diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 85b86f9ce5..1f174520da 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -102,6 +102,8 @@ public:  	static const std::string&	lookup(EType folder_type);  	static bool 				lookupIsProtectedType(EType folder_type); +	static bool 				lookupIsAutomaticType(EType folder_type); +	static bool 				lookupIsSingletonType(EType folder_type);  	static bool 				lookupIsEnsembleType(EType folder_type);  	static LLAssetType::EType	folderTypeToAssetType(LLFolderType::EType folder_type); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c0166f158e..856b60084f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8337,7 +8337,18 @@      <key>QAModeMetrics</key>      <map>        <key>Comment</key> -      <string>"Enables QA features (logging, faster cycling) for metrics collector"</string> +      <string>Enables QA features (logging, faster cycling) for metrics collector</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map> +    <key>QAModeFakeSystemFolderIssues</key> +    <map> +      <key>Comment</key> +      <string>Simulates system folder issues in inventory</string>        <key>Persist</key>        <integer>1</integer>        <key>Type</key> diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index d3e66289d1..443c5ae02f 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -99,22 +99,22 @@ void LLAttachmentsMgr::onIdle()  		return;  	} -    if (LLApp::isExiting()) -    { -        return; -    } +	if (LLApp::isExiting()) +	{ +		return; +	}  	requestPendingAttachments(); -    linkRecentlyArrivedAttachments(); +	linkRecentlyArrivedAttachments(); -    expireOldAttachmentRequests(); +	expireOldAttachmentRequests(); -    expireOldDetachRequests(); +	expireOldDetachRequests(); -    checkInvalidCOFLinks(); -     -    spamStatusInfo(); +	checkInvalidCOFLinks(); +	 +	spamStatusInfo();  }  void LLAttachmentsMgr::requestPendingAttachments() @@ -452,51 +452,55 @@ bool LLAttachmentsMgr::isAttachmentStateComplete() const  //  void LLAttachmentsMgr::checkInvalidCOFLinks()  { -        LLInventoryModel::cat_array_t cat_array; -        LLInventoryModel::item_array_t item_array; -        gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), -                                      cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); -        for (S32 i=0; i<item_array.size(); i++) -        { -            const LLViewerInventoryItem* inv_item = item_array.at(i).get(); -            const LLUUID& item_id = inv_item->getLinkedUUID(); -            if (inv_item->getType() == LLAssetType::AT_OBJECT) -            { -                LLTimer timer; -                bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer); -                bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id); -                if (is_wearing_attachment && is_flagged_questionable) -                { -                    LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now "  -                                        << (is_wearing_attachment ? "attached " : "")  -                                        <<"removing flag after " -                                        << timer.getElapsedTimeF32() << " item " -                                        << inv_item->getName() << " id " << item_id << LL_ENDL; -                    mQuestionableCOFLinks.removeTime(item_id); -                } -            } -        } +	if (!gInventory.isInventoryUsable()) +	{ +		return; +	} +	LLInventoryModel::cat_array_t cat_array; +	LLInventoryModel::item_array_t item_array; +	gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(), +								  cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH); +	for (S32 i=0; i<item_array.size(); i++) +	{ +		const LLViewerInventoryItem* inv_item = item_array.at(i).get(); +		const LLUUID& item_id = inv_item->getLinkedUUID(); +		if (inv_item->getType() == LLAssetType::AT_OBJECT) +		{ +			LLTimer timer; +			bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer); +			bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id); +			if (is_wearing_attachment && is_flagged_questionable) +			{ +				LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now "  +									<< (is_wearing_attachment ? "attached " : "")  +									<<"removing flag after " +									<< timer.getElapsedTimeF32() << " item " +									<< inv_item->getName() << " id " << item_id << LL_ENDL; +				mQuestionableCOFLinks.removeTime(item_id); +			} +		} +	} -        for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin(); -            it != mQuestionableCOFLinks.end(); ) -        { -            LLItemRequestTimes::iterator curr_it = it; -            ++it; -            const LLUUID& item_id = curr_it->first; -            LLViewerInventoryItem *inv_item = gInventory.getItem(item_id); -            if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME) -            { -                if (LLAppearanceMgr::instance().isLinkedInCOF(item_id)) -                { -                    LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after " -                                        << curr_it->second.getElapsedTimeF32() << " seconds for "  -                                        << (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL; -                    LLAppearanceMgr::instance().removeCOFItemLinks(item_id); -                } -				mQuestionableCOFLinks.erase(curr_it); -                continue; -            } -        } +	for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin(); +		it != mQuestionableCOFLinks.end(); ) +	{ +		LLItemRequestTimes::iterator curr_it = it; +		++it; +		const LLUUID& item_id = curr_it->first; +		LLViewerInventoryItem *inv_item = gInventory.getItem(item_id); +		if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME) +		{ +			if (LLAppearanceMgr::instance().isLinkedInCOF(item_id)) +			{ +				LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after " +									<< curr_it->second.getElapsedTimeF32() << " seconds for "  +									<< (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL; +				LLAppearanceMgr::instance().removeCOFItemLinks(item_id); +			} +			mQuestionableCOFLinks.erase(curr_it); +			continue; +		} +	}  }  void LLAttachmentsMgr::spamStatusInfo() diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 347997a69a..d258bea519 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -1859,9 +1859,17 @@ BOOL LLFavoritesOrderStorage::saveFavoritesRecord(bool pref_changed)  	pref_changed |= mRecreateFavoriteStorage;  	mRecreateFavoriteStorage = false; +	// Can get called before inventory is done initializing. +	if (!gInventory.isInventoryUsable()) +	{ +		return FALSE; +	} +	  	LLUUID favorite_folder= gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);  	if (favorite_folder.isNull()) -			return FALSE; +	{ +		return FALSE; +	}  	LLInventoryModel::item_array_t items;  	LLInventoryModel::cat_array_t cats; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 28db6a5808..6e30a31e6d 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -27,6 +27,7 @@  #include "llviewerprecompiledheaders.h"  #include <typeinfo> +#include <random>  #include "llinventorymodel.h" @@ -67,6 +68,9 @@  #include "process.h"  #endif +#include <algorithm> +#include <boost/algorithm/string/join.hpp> +  // 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; @@ -129,6 +133,54 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)  }  ///---------------------------------------------------------------------------- +/// Class LLInventoryValidationInfo +///---------------------------------------------------------------------------- +LLInventoryValidationInfo::LLInventoryValidationInfo(): +	mFatalErrorCount(0), +	mWarningCount(0), +	mInitialized(false) +{ +} + +void LLInventoryValidationInfo::toOstream(std::ostream& os) const +{ +	os << "mFatalErrorCount " << mFatalErrorCount << " mWarningCount " << mWarningCount; +} + + +std::ostream& operator<<(std::ostream& os, const LLInventoryValidationInfo& v) +{ +	v.toOstream(os); +	return os; +} + +void LLInventoryValidationInfo::asLLSD(LLSD& sd) const +{ +	sd["fatal_error_count"] = mFatalErrorCount; +	sd["warning_count"] = mWarningCount; +	sd["initialized"] = mInitialized; +	sd["missing_system_folders_count"] = LLSD::Integer(mMissingRequiredSystemFolders.size()); +	if (mMissingRequiredSystemFolders.size()>0) +	{ +		sd["missing_system_folders"] = LLSD::emptyArray(); +		for(auto ft: mMissingRequiredSystemFolders) +		{ +			sd["missing_system_folders"].append(LLFolderType::lookup(ft));  +		} +	} +	sd["duplicate_system_folders_count"] = LLSD::Integer(mDuplicateRequiredSystemFolders.size()); +	if (mDuplicateRequiredSystemFolders.size()>0) +	{ +		sd["duplicate_system_folders"] = LLSD::emptyArray(); +		for(auto ft: mDuplicateRequiredSystemFolders) +		{ +			sd["duplicate_system_folders"].append(LLFolderType::lookup(ft)); +		} +	} +	 +} + +///----------------------------------------------------------------------------  /// Class LLInventoryModel  ///---------------------------------------------------------------------------- @@ -160,7 +212,8 @@ LLInventoryModel::LLInventoryModel()  	mHttpPriorityFG(0),  	mHttpPriorityBG(0),  	mCategoryLock(), -	mItemLock() +	mItemLock(), +	mValidationInfo(new LLInventoryValidationInfo)  {} @@ -499,12 +552,18 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(  		}  	} -	if(rv.isNull() && isInventoryUsable() && create_folder) +	if(rv.isNull() && create_folder && root_id.notNull())  	{ -		if(root_id.notNull()) + +		if (isInventoryUsable())  		{  			return createNewCategory(root_id, preferred_type, LLStringUtil::null);  		} +		else +		{ +			LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type +								  << " because inventory is not usable" << LL_ENDL; +		}  	}  	return rv;  } @@ -568,20 +627,30 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  										   const std::string& pname,  										   inventory_func_type callback)  { -	  	LLUUID id; -	if(!isInventoryUsable()) +	if (!isInventoryUsable())  	{ -		LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; +		LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type " +						  << preferred_type << LL_ENDL; +		// FIXME failing but still returning an id?  		return id;  	}  	if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())  	{  		LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; +		// FIXME failing but still returning an id?  		return id;  	} +	if (preferred_type != LLFolderType::FT_NONE) +	{ +		// Ultimately this should only be done for non-singleton +		// types. Requires back-end changes to guarantee that others +		// already exist. +		LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL; +	} +  	id.generate();  	std::string name = pname;  	if(!pname.empty()) @@ -611,7 +680,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  		request["message"] = "CreateInventoryCategory";  		request["payload"] = body; -		LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; +		LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL;          LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro",              boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback)); @@ -623,6 +692,10 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  		return LLUUID::null;  	} +	// FIXME this UDP code path needs to be removed. Requires +	// reworking many of the callers to use callbacks rather than +	// assuming instant success. +  	// Add the category to the internal representation  	LLPointer<LLViewerInventoryCategory> cat =  		new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); @@ -632,6 +705,8 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  	accountForUpdate(update);  	updateCategory(cat); +	LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL; +  	// Create the category on the server. We do this to prevent people  	// from munging their protected folders.  	LLMessageSystem* msg = gMessageSystem; @@ -2247,11 +2322,11 @@ bool LLInventoryModel::loadSkeleton(  					}  				} - 				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; + 				LL_DEBUGS(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;  			}  		} @@ -2277,7 +2352,10 @@ bool LLInventoryModel::loadSkeleton(  			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; +		if (invalid_categories.size() > 0) +		{ +			LL_DEBUGS(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 @@ -2565,10 +2643,22 @@ void LLInventoryModel::buildParentChildMap()  				}  			} -			// 'My Inventory', -			// root of the agent's inv found. -			// The inv tree is built. -			mIsAgentInvUsable = true; +			LLPointer<LLInventoryValidationInfo> validation_info = validate(); +			if (validation_info->mFatalErrorCount > 0) +			{ +				// Fatal inventory error. Will not be able to engage in many inventory operations. +				// This should be followed by an error dialog leading to logout. +				LL_WARNS("Inventory") << "Fatal errors were found in validate(): unable to initialize inventory! " +									  << "Will not be able to do normal inventory operations in this session." +									  << LL_ENDL; +				mIsAgentInvUsable = false; +			} +			else +			{ +				mIsAgentInvUsable = true; +			} +			validation_info->mInitialized = true; +			mValidationInfo = validation_info;  			// notifyObservers() has been moved to  			// llstartup/idle_startup() after this func completes. @@ -2576,11 +2666,6 @@ void LLInventoryModel::buildParentChildMap()  			// 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 @@ -3702,56 +3787,68 @@ void LLInventoryModel::dumpInventory() const  }  // Do various integrity checks on model, logging issues found and -// returning an overall good/bad flag. -bool LLInventoryModel::validate() const +// returning an overall good/bad flag.  +LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const  { -	bool valid = true; +	LLPointer<LLInventoryValidationInfo> validation_info = new LLInventoryValidationInfo; +	S32 fatalities = 0; +	S32 warnings = 0;  	if (getRootFolderID().isNull())  	{ -		LL_WARNS() << "no root folder id" << LL_ENDL; -		valid = false; +		LL_WARNS("Inventory") << "Fatal inventory corruption: no root folder id" << LL_ENDL; +		fatalities++;  	}  	if (getLibraryRootFolderID().isNull())  	{ -		LL_WARNS() << "no root folder id" << LL_ENDL; -		valid = false; +		LL_WARNS("Inventory") << "Fatal inventory corruption: no library root folder id" << LL_ENDL; +		fatalities++;  	}  	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; +		LL_INFOS("Inventory") << "unexpected sizes: cat map size " << mCategoryMap.size() +							  << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; +		warnings++;  	}  	S32 cat_lock = 0;  	S32 item_lock = 0;  	S32 desc_unknown_count = 0;  	S32 version_unknown_count = 0; + +	typedef std::map<LLFolderType::EType, S32> ft_count_map; +	ft_count_map ft_counts_under_root; +	ft_count_map ft_counts_elsewhere; +	 +	// Loop over all categories and check.  	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)  		{ -			LL_WARNS() << "invalid cat" << LL_ENDL; -			valid = false; +			LL_WARNS("Inventory") << "null cat" << LL_ENDL; +			warnings++;  			continue;  		} +		LLUUID topmost_ancestor_id; +		// Will leave as null uuid on failure +		getObjectTopmostAncestor(cat_id, topmost_ancestor_id);  		if (cat_id != cat->getUUID())  		{ -			LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; -			valid = false; +			LL_WARNS("Inventory") << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; +			warnings++;  		}  		if (cat->getParentUUID().isNull())  		{  			if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID())  			{ -				LL_WARNS() << "cat " << cat_id << " has no parent, but is not root (" -						<< getRootFolderID() << ") or library root (" -						<< getLibraryRootFolderID() << ")" << LL_ENDL; +				LL_WARNS("Inventory") << "cat " << cat_id << " has no parent, but is not root (" +									  << getRootFolderID() << ") or library root (" +									  << getLibraryRootFolderID() << ")" << LL_ENDL; +				warnings++;  			}  		}  		cat_array_t* cats; @@ -3759,8 +3856,8 @@ bool LLInventoryModel::validate() const  		getDirectDescendentsOf(cat_id,cats,items);  		if (!cats || !items)  		{ -			LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL; -			valid = false; +			LL_WARNS("Inventory") << "invalid direct descendents for " << cat_id << LL_ENDL; +			warnings++;  			continue;  		}  		if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) @@ -3769,22 +3866,29 @@ bool LLInventoryModel::validate() const  		}  		else if (cats->size() + items->size() != cat->getDescendentCount())  		{ -			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; +			// In the case of library this is not unexpected, since +			// different user accounts may be getting the library +			// contents from different inventory hosts. +			if (topmost_ancestor_id.isNull() || topmost_ancestor_id != getLibraryRootFolderID()) +			{ +				LL_WARNS("Inventory") << "invalid desc count for " << cat_id << " [" << getFullPath(cat) << "]" +									  << " cached " << cat->getDescendentCount() +									  << " expected " << cats->size() << "+" << items->size() +									  << "=" << cats->size() +items->size() << LL_ENDL; +				warnings++; +			}  		}  		if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)  		{  			version_unknown_count++;  		} -		if (mCategoryLock.count(cat_id)) +		auto cat_lock_it = mCategoryLock.find(cat_id); +		if (cat_lock_it != mCategoryLock.end() && cat_lock_it->second)  		{  			cat_lock++;  		} -		if (mItemLock.count(cat_id)) +		auto item_lock_it = mItemLock.find(cat_id); +		if (item_lock_it != mItemLock.end() && item_lock_it->second)  		{  			item_lock++;  		} @@ -3794,8 +3898,8 @@ bool LLInventoryModel::validate() const  			if (!item)  			{ -				LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL; -				valid = false; +				LL_WARNS("Inventory") << "null item at index " << i << " for cat " << cat_id << LL_ENDL; +				warnings++;  				continue;  			} @@ -3803,10 +3907,10 @@ bool LLInventoryModel::validate() const  			if (item->getParentUUID() != cat_id)  			{ -				LL_WARNS() << "wrong parent for " << item_id << " found " -						<< item->getParentUUID() << " expected " << cat_id -						<< LL_ENDL; -				valid = false; +				LL_WARNS("Inventory") << "wrong parent for " << item_id << " found " +									  << item->getParentUUID() << " expected " << cat_id +									  << LL_ENDL; +				warnings++;  			} @@ -3814,17 +3918,17 @@ bool LLInventoryModel::validate() const  			item_map_t::const_iterator it = mItemMap.find(item_id);  			if (it == mItemMap.end())  			{ -				LL_WARNS() << "item " << item_id << " found as child of " -						<< cat_id << " but not in top level mItemMap" << LL_ENDL; -				valid = false; +				LL_WARNS("Inventory") << "item " << item_id << " found as child of " +									  << cat_id << " but not in top level mItemMap" << LL_ENDL; +				warnings++;  			}  			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; +					LL_WARNS("Inventory") << "item mismatch, item_id " << item_id +										  << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL;  				}  			} @@ -3833,19 +3937,19 @@ bool LLInventoryModel::validate() const  			bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id);  			if (!found)  			{ -				LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL; -				valid = false; +				LL_WARNS("Inventory") << "unable to find topmost ancestor for " << item_id << LL_ENDL; +				warnings++;  			}  			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; +					LL_WARNS("Inventory") << "unrecognized top level ancestor for " << item_id +										  << " got " << topmost_ancestor_id +										  << " expected " << getRootFolderID() +										  << " or " << getLibraryRootFolderID() << LL_ENDL; +					warnings++;  				}  			}  		} @@ -3859,9 +3963,9 @@ bool LLInventoryModel::validate() const  			getDirectDescendentsOf(parent_id,cats,items);  			if (!cats)  			{ -				LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() -						<< "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; -				valid = false; +				LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() +									  << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; +				warnings++;  			}  			else  			{ @@ -3877,27 +3981,56 @@ bool LLInventoryModel::validate() const  				}  				if (!found)  				{ -					LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() -							<< "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; +					LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() +										  << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; +				} +			} +		} + +		// Update count of preferred types +		LLFolderType::EType folder_type = cat->getPreferredType(); +		bool cat_is_in_library = false; +		LLUUID topmost_id; +		if (getObjectTopmostAncestor(cat->getUUID(),topmost_id) && topmost_id == getLibraryRootFolderID()) +		{ +			cat_is_in_library = true; +		} +		if (!cat_is_in_library) +		{ +			if (getRootFolderID().notNull() && (cat->getUUID()==getRootFolderID() || cat->getParentUUID()==getRootFolderID())) +			{ +				ft_counts_under_root[folder_type]++; +				if (folder_type != LLFolderType::FT_NONE) +				{ +					LL_DEBUGS("Inventory") << "Under root cat: " << getFullPath(cat) << " folder_type " << folder_type << LL_ENDL; +				} +			} +			else +			{ +				ft_counts_elsewhere[folder_type]++; +				if (folder_type != LLFolderType::FT_NONE) +				{ +					LL_DEBUGS("Inventory") << "Elsewhere cat: " << getFullPath(cat) << " folder_type " << folder_type << LL_ENDL;  				}  			}  		}  	} +	// Loop over all items and check  	for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)  	{  		const LLUUID& item_id = iit->first;  		LLViewerInventoryItem *item = iit->second;  		if (item->getUUID() != item_id)  		{ -			LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; -			valid = false; +			LL_WARNS("Inventory") << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; +			warnings++;  		}  		const LLUUID& parent_id = item->getParentUUID();  		if (parent_id.isNull())  		{ -			LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; +			LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL;  		}  		else  		{ @@ -3906,8 +4039,8 @@ bool LLInventoryModel::validate() const  			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; +				LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() +									  << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL;  			}  			else  			{ @@ -3922,8 +4055,8 @@ bool LLInventoryModel::validate() const  				}  				if (!found)  				{ -					LL_WARNS() << "item " << item_id << " name [" << item->getName() -							<< "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; +					LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() +										  << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL;  				}  			} @@ -3938,30 +4071,30 @@ bool LLInventoryModel::validate() const  			// 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; +				LL_WARNS("Inventory") << "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; +				LL_WARNS("Inventory") << "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; +				LL_WARNS("Inventory") << "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; +				LL_WARNS("Inventory") << "link " << item->getName() << " references a link item " +									  << target_item->getName() << " " << target_item->getUUID() << LL_ENDL;  			}  			// 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)  			{ -				LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL; +				LL_WARNS("Inventory") << "Link item " << item->getName() << " has backlinks!" << LL_ENDL;  			}  		}  		else @@ -3975,29 +4108,116 @@ bool LLInventoryModel::validate() const  				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; +					LL_WARNS("Inventory") << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL;  				}  			}  		}  	} -	 + +	// Check system folders +	for (auto fit=ft_counts_under_root.begin(); fit != ft_counts_under_root.end(); ++fit) +	{ +		LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " under root" << LL_ENDL; +	} +	for (auto fit=ft_counts_elsewhere.begin(); fit != ft_counts_elsewhere.end(); ++fit) +	{ +		LL_DEBUGS("Inventory") << "Folder type " << fit->first << " count " << fit->second << " elsewhere" << LL_ENDL; +	} + +	static LLCachedControl<bool> fake_system_folder_issues(gSavedSettings, "QAModeFakeSystemFolderIssues", false); +	static std::default_random_engine e{}; +    static std::uniform_int_distribution<> distrib(0, 1); +	for (S32 ft=LLFolderType::FT_TEXTURE; ft<LLFolderType::FT_COUNT; ft++) +	{ +		LLFolderType::EType folder_type = static_cast<LLFolderType::EType>(ft); +		if (LLFolderType::lookup(folder_type)==LLFolderType::badLookup()) +		{ +			continue; +		} +		bool is_automatic = LLFolderType::lookupIsAutomaticType(folder_type); +		bool is_singleton = LLFolderType::lookupIsSingletonType(folder_type); +		S32 count_under_root = ft_counts_under_root[folder_type]; +		S32 count_elsewhere = ft_counts_elsewhere[folder_type]; +		if (fake_system_folder_issues) +		{ +			// Force all counts to be either 0 or 2, thus flagged as an error. +			count_under_root = 2*distrib(e);  +			count_elsewhere = 2*distrib(e); +		} +		if (is_singleton) +		{ +			if (count_under_root==0) +			{ +				LL_WARNS("Inventory") << "Expected system folder type " << ft << " was not found under root" << LL_ENDL; +				// Need to create, if allowed. +				if (is_automatic) +				{ +					LL_WARNS("Inventory") << "Fatal inventory corruption: cannot create system folder of type " << ft << LL_ENDL; +					fatalities++; +					validation_info->mMissingRequiredSystemFolders.insert(LLFolderType::EType(ft)); +				} +				else +				{ +					// Can create, and will when needed. +					warnings++; +				} +			} +			else if (count_under_root > 1) +			{ +				LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; +				validation_info->mDuplicateRequiredSystemFolders.insert(LLFolderType::EType(ft)); +				fatalities++; +			} +			if (count_elsewhere > 0) +			{ +				LL_WARNS("Inventory") << "Found " << count_elsewhere << " extra folders of type " << ft << " outside of root" << LL_ENDL; +				warnings++; +			} +		} +	} + +  	if (cat_lock > 0 || item_lock > 0)  	{ -		LL_INFOS() << "Found locks on some categories: sub-cat arrays " +		LL_INFOS("Inventory") << "Found locks on some categories: sub-cat arrays "  				<< cat_lock << ", item arrays " << item_lock << LL_ENDL;  	}  	if (desc_unknown_count != 0)  	{ -		LL_INFOS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL;  +		LL_DEBUGS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL;   	}  	if (version_unknown_count != 0)  	{ -		LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL; +		LL_DEBUGS("Inventory") << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL;  	} -	LL_INFOS() << "Validate done, valid = " << (U32) valid << LL_ENDL; +	// FIXME need to fail login and tell user to retry, contact support if problem persists. +	bool valid = (fatalities == 0); +	LL_INFOS("Inventory") << "Validate done, fatal errors: " << fatalities << ", warnings: " << warnings << ", valid: " << valid << LL_ENDL; + +	validation_info->mFatalErrorCount = fatalities; +	validation_info->mWarningCount = warnings; -	return valid; +	return validation_info;  +} + +// Provides a unix-style path from root, like "/My Inventory/Clothing/.../myshirt" +std::string LLInventoryModel::getFullPath(const LLInventoryObject *obj) const +{ +	std::vector<std::string> path_elts; +	std::map<LLUUID,bool> visited; +	while (obj != NULL && !visited[obj->getUUID()]) +	{ +		path_elts.push_back(obj->getName()); +		// avoid infinite loop in the unlikely event of a cycle +		visited[obj->getUUID()] = true; +		obj = getObject(obj->getParentUUID()); +	} +	std::stringstream s; +	std::string delim("/"); +	std::reverse(path_elts.begin(), path_elts.end()); +	std::string result = "/" + boost::algorithm::join(path_elts, delim); +	return result;  }  ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index a4326aaeed..bddaf3a147 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -55,7 +55,26 @@ class LLInventoryCategory;  class LLMessageSystem;  class LLInventoryCollectFunctor; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +///---------------------------------------------------------------------------- +/// LLInventoryValidationInfo  +///---------------------------------------------------------------------------- +class LLInventoryValidationInfo: public LLRefCount +{ +public: +	LLInventoryValidationInfo(); +	void toOstream(std::ostream& os) const; +	void asLLSD(LLSD& sd) const; +	 + +	S32 mFatalErrorCount; +	S32 mWarningCount; +	bool mInitialized; +	std::set<LLFolderType::EType> mMissingRequiredSystemFolders; +	std::set<LLFolderType::EType> mDuplicateRequiredSystemFolders; +}; +std::ostream& operator<<(std::ostream& s, const LLInventoryValidationInfo& v); + +///----------------------------------------------------------------------------  // LLInventoryModel  //  // Represents a collection of inventory, and provides efficient ways to access  @@ -63,7 +82,7 @@ class LLInventoryCollectFunctor;  //   NOTE: This class could in theory be used for any place where you need   //   inventory, though it optimizes for time efficiency - not space efficiency,   //   probably making it inappropriate for use on tasks. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +///----------------------------------------------------------------------------  class LLInventoryModel  {  	LOG_CLASS(LLInventoryModel); @@ -656,7 +675,9 @@ private:  	//--------------------------------------------------------------------  public:  	void dumpInventory() const; -	bool validate() const; +	LLPointer<LLInventoryValidationInfo> validate() const; +	LLPointer<LLInventoryValidationInfo> mValidationInfo; +	std::string getFullPath(const LLInventoryObject *obj) const;  /**                    Miscellaneous   **                                                                            ** diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 17777c3ceb..51a96b2b10 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1807,6 +1807,13 @@ bool idle_startup()  		// This method MUST be called before gInventory.findCategoryUUIDForType because of   		// gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap.  		gInventory.buildParentChildMap(); + +		// If buildParentChildMap succeeded, inventory will now be in +		// a usable state and gInventory.isInventoryUsable() will be +		// true. + +		// FIXME if inventory is unusable, we need to bail out. +		  		gInventory.createCommonSystemCategories();  		// It's debatable whether this flag is a good idea - sets all @@ -1850,6 +1857,7 @@ bool idle_startup()  		display_startup();  		LLStartUp::setStartupState( STATE_MISC );  		display_startup(); +  		return FALSE;  	} diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 05f88b0a75..b6acdddca8 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -63,6 +63,7 @@  #include "llsdutil.h"  #include "llcorehttputil.h"  #include "llvoicevivox.h" +#include "llinventorymodel.h"  namespace LLStatViewer  { @@ -577,6 +578,11 @@ void send_viewer_stats(bool include_preferences)  	fail["invalid"] = (S32) gMessageSystem->mInvalidOnCircuitPackets;  	fail["missing_updater"] = (S32) LLAppViewer::instance()->isUpdaterMissing(); +	LLSD &inventory = body["inventory"]; +	inventory["usable"] = gInventory.isInventoryUsable(); +	LLSD& validation_info = inventory["validation_info"]; +	gInventory.mValidationInfo->asLLSD(validation_info); +  	body["stats"]["voice"] = LLVoiceVivoxStats::getInstance()->read();  	// Misc stats, two strings and two ints  | 
