summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xBuildParams1
-rw-r--r--indra/llaudio/llaudioengine_fmodstudio.cpp2
-rw-r--r--indra/llinventory/llfoldertype.cpp117
-rw-r--r--indra/llinventory/llfoldertype.h2
-rw-r--r--indra/llui/CMakeLists.txt2
-rw-r--r--indra/llui/llbutton.cpp8
-rw-r--r--indra/llui/llfloater.cpp2
-rw-r--r--indra/llui/llfloaterreg.cpp2
-rw-r--r--indra/llui/lltabcontainer.cpp3
-rw-r--r--indra/llui/lltoolbar.cpp4
-rw-r--r--indra/llui/lluictrl.cpp22
-rw-r--r--indra/llui/lluictrl.h4
-rw-r--r--indra/llui/lluiusage.cpp146
-rw-r--r--indra/llui/lluiusage.h57
-rw-r--r--indra/newview/app_settings/settings.xml13
-rw-r--r--indra/newview/llattachmentsmgr.cpp112
-rw-r--r--indra/newview/llchatbar.cpp3
-rw-r--r--indra/newview/llfavoritesbar.cpp10
-rw-r--r--indra/newview/llfloaterimnearbychat.cpp2
-rw-r--r--indra/newview/llgroupactions.cpp5
-rw-r--r--indra/newview/llinventorymodel.cpp408
-rw-r--r--indra/newview/llinventorymodel.h27
-rw-r--r--indra/newview/llpanellandmarkinfo.cpp5
-rw-r--r--indra/newview/llpanellandmarkinfo.h1
-rw-r--r--indra/newview/llpanelplaces.cpp7
-rw-r--r--indra/newview/llstartup.cpp11
-rw-r--r--indra/newview/llviewermessage.cpp8
-rw-r--r--indra/newview/llviewerstats.cpp9
-rw-r--r--indra/newview/llworldmipmap.cpp2
29 files changed, 801 insertions, 194 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/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp
index 70b3a08473..b0c87b0208 100644
--- a/indra/llaudio/llaudioengine_fmodstudio.cpp
+++ b/indra/llaudio/llaudioengine_fmodstudio.cpp
@@ -661,7 +661,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename)
return false;
}
- if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB))
+ if (!gDirUtilp->fileExists(filename))
{
// File not found, abort.
return false;
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/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index cce618487b..f781ff4110 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -122,6 +122,7 @@ set(llui_SOURCE_FILES
lluictrl.cpp
lluictrlfactory.cpp
lluistring.cpp
+ lluiusage.cpp
llundo.cpp
llurlaction.cpp
llurlentry.cpp
@@ -241,6 +242,7 @@ set(llui_HEADER_FILES
llui.h
lluicolor.h
lluistring.h
+ lluiusage.h
llundo.h
llurlaction.h
llurlentry.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 9682c3bc10..0e59fdf519 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -47,6 +47,7 @@
#include "llnotificationsutil.h"
#include "llrender.h"
#include "lluictrlfactory.h"
+#include "lluiusage.h"
#include "llhelp.h"
#include "lldockablefloater.h"
#include "llviewereventrecorder.h"
@@ -437,6 +438,13 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
setFocus(TRUE);
}
+ if (!mFunctionName.empty())
+ {
+ LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL;
+ LLUIUsage::instance().logCommand(mFunctionName);
+ LLUIUsage::instance().logControl(getPathname());
+ }
+
/*
* ATTENTION! This call fires another mouse down callback.
* If you wish to remove this call emit that signal directly
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index e9c980ad9a..c03b024dd5 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -58,6 +58,7 @@
#include "llhelp.h"
#include "llmultifloater.h"
#include "llsdutil.h"
+#include "lluiusage.h"
#include <boost/foreach.hpp>
@@ -1631,6 +1632,7 @@ void LLFloater::bringToFront( S32 x, S32 y )
// virtual
void LLFloater::setVisibleAndFrontmost(BOOL take_focus,const LLSD& key)
{
+ LLUIUsage::instance().logFloater(getInstanceName());
LLMultiFloater* hostp = getHost();
if (hostp)
{
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index 85e07fc6a6..7c0933f554 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -32,6 +32,7 @@
#include "llfloater.h"
#include "llmultifloater.h"
#include "llfloaterreglistener.h"
+#include "lluiusage.h"
//*******************************************************
@@ -472,7 +473,6 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD&
std::string name = sdname.asString();
LLFloater* instance = getInstance(name, key);
-
if (!instance)
{
LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index e6b43da8e5..459fdcf2ae 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -38,6 +38,7 @@
#include "llrender.h"
#include "llfloater.h"
#include "lltrans.h"
+#include "lluiusage.h"
//----------------------------------------------------------------------------
@@ -1556,6 +1557,8 @@ BOOL LLTabContainer::setTab(S32 which)
if (is_selected)
{
+ LLUIUsage::instance().logPanel(tuple->mTabPanel->getName());
+
// Make sure selected tab is within scroll region
if (mIsVertical)
{
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index e6f466ec78..4868c66d1b 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -967,6 +967,8 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
executeStopParam.function_name = executeStopFunction;
executeStopParam.parameter = commandp->executeStopParameters();
LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
+ button->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL;
LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
@@ -974,6 +976,8 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
}
else
{
+ button->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL;
button->setCommitCallback(executeParam);
}
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 544a76e8d5..5924542a19 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -35,6 +35,7 @@
#include "lluictrlfactory.h"
#include "lltabcontainer.h"
#include "llaccordionctrltab.h"
+#include "lluiusage.h"
static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
@@ -282,6 +283,7 @@ LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCa
else
{
std::string function_name = cb.function_name;
+ setFunctionName(function_name);
commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name));
if (func)
{
@@ -422,7 +424,19 @@ BOOL LLUICtrl::canFocusChildren() const
void LLUICtrl::onCommit()
{
if (mCommitSignal)
- (*mCommitSignal)(this, getValue());
+ {
+ if (!mFunctionName.empty())
+ {
+ LL_DEBUGS("UIUsage") << "calling commit function " << mFunctionName << LL_ENDL;
+ LLUIUsage::instance().logCommand(mFunctionName);
+ LLUIUsage::instance().logControl(getPathname());
+ }
+ else
+ {
+ //LL_DEBUGS("UIUsage") << "calling commit function " << "UNKNOWN" << LL_ENDL;
+ }
+ (*mCommitSignal)(this, getValue());
+ }
}
//virtual
@@ -597,6 +611,12 @@ void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control)
setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean()));
}
}
+
+void LLUICtrl::setFunctionName(const std::string& function_name)
+{
+ mFunctionName = function_name;
+}
+
// static
bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type)
{
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 7360bd7659..67dd24341c 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -183,6 +183,8 @@ public:
void setMakeVisibleControlVariable(LLControlVariable* control);
void setMakeInvisibleControlVariable(LLControlVariable* control);
+ void setFunctionName(const std::string& function_name);
+
virtual void setTentative(BOOL b);
virtual BOOL getTentative() const;
virtual void setValue(const LLSD& value);
@@ -310,6 +312,8 @@ protected:
LLControlVariable* mMakeInvisibleControlVariable;
boost::signals2::connection mMakeInvisibleControlConnection;
+ std::string mFunctionName;
+
static F32 sActiveControlTransparency;
static F32 sInactiveControlTransparency;
diff --git a/indra/llui/lluiusage.cpp b/indra/llui/lluiusage.cpp
new file mode 100644
index 0000000000..ccae6643b9
--- /dev/null
+++ b/indra/llui/lluiusage.cpp
@@ -0,0 +1,146 @@
+/**
+* @file lluiuisage.cpp
+* @brief Source file for LLUIUsage
+*
+* $LicenseInfo:firstyear=2021&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2021, 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.
+*
+* 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.
+*
+* 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
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "linden_common.h"
+#include "lluiusage.h"
+#include <boost/algorithm/string.hpp>
+
+LLUIUsage::LLUIUsage()
+{
+}
+
+LLUIUsage::~LLUIUsage()
+{
+}
+
+// static
+std::string LLUIUsage::sanitized(const std::string& s)
+{
+ // Remove characters that make the ViewerStats db unhappy
+ std::string result(s);
+ std::replace(result.begin(), result.end(), '.', '_');
+ std::replace(result.begin(), result.end(), ' ', '_');
+ return result;
+}
+
+// static
+void LLUIUsage::setLLSDPath(LLSD& sd, const std::string& path, S32 max_elts, const LLSD& val)
+{
+ // Keep the last max_elts components of the specified path
+ std::vector<std::string> fields;
+ boost::split(fields, path, boost::is_any_of("/"));
+ auto first_pos = std::max(fields.begin(), fields.end() - max_elts);
+ auto end_pos = fields.end();
+ std::vector<std::string> last_fields(first_pos,end_pos);
+
+ setLLSDNested(sd, last_fields, val);
+}
+
+// setLLSDNested()
+// Accomplish the equivalent of
+// sd[fields[0]][fields[1]]... = val;
+// for an arbitrary number of fields.
+// This might be useful as an LLSD utility function; is not specific to LLUIUsage
+//
+// static
+void LLUIUsage::setLLSDNested(LLSD& sd, const std::vector<std::string>& fields, const LLSD& val)
+{
+ LLSD* fsd = &sd;
+ for (auto it=fields.begin(); it!=fields.end(); ++it)
+ {
+ if (it == fields.end()-1)
+ {
+ (*fsd)[*it] = val;
+ }
+ else
+ {
+ if (!(*fsd)[*it].isMap())
+ {
+ (*fsd)[*it] = LLSD::emptyMap();
+ }
+ fsd = &(*fsd)[*it];
+ }
+ }
+}
+
+void LLUIUsage::logCommand(const std::string& command)
+{
+ mCommandCounts[sanitized(command)]++;
+ LL_DEBUGS("UIUsage") << "command " << command << LL_ENDL;
+}
+
+void LLUIUsage::logControl(const std::string& control)
+{
+ mControlCounts[sanitized(control)]++;
+ LL_DEBUGS("UIUsage") << "control " << control << LL_ENDL;
+}
+
+
+void LLUIUsage::logFloater(const std::string& floater)
+{
+ mFloaterCounts[sanitized(floater)]++;
+ LL_DEBUGS("UIUsage") << "floater " << floater << LL_ENDL;
+}
+
+void LLUIUsage::logPanel(const std::string& p)
+{
+ mPanelCounts[sanitized(p)]++;
+ LL_DEBUGS("UIUsage") << "panel " << p << LL_ENDL;
+}
+
+LLSD LLUIUsage::asLLSD() const
+{
+ LLSD result;
+ for (auto const& it : mCommandCounts)
+ {
+ result["commands"][it.first] = LLSD::Integer(it.second);
+ }
+ for (auto const& it : mControlCounts)
+ {
+ setLLSDPath(result["controls"], it.first, 2, LLSD::Integer(it.second));
+ }
+ for (auto const& it : mFloaterCounts)
+ {
+ result["floaters"][it.first] = LLSD::Integer(it.second);
+ }
+ for (auto const& it : mPanelCounts)
+ {
+ result["panels"][it.first] = LLSD::Integer(it.second);
+ }
+ return result;
+}
+
+// Clear up some junk content generated during initial login/UI initialization
+void LLUIUsage::clear()
+{
+
+ LL_DEBUGS("UIUsage") << "clear" << LL_ENDL;
+ mCommandCounts.clear();
+ mControlCounts.clear();
+ mFloaterCounts.clear();
+ mPanelCounts.clear();
+}
+
diff --git a/indra/llui/lluiusage.h b/indra/llui/lluiusage.h
new file mode 100644
index 0000000000..a30cd80db3
--- /dev/null
+++ b/indra/llui/lluiusage.h
@@ -0,0 +1,57 @@
+/**
+* @file lluiuisage.h
+* @brief Header file for LLUIUsage
+*
+* $LicenseInfo:firstyear=2021&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2021, 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.
+*
+* 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.
+*
+* 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
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_LLUIUSAGE_H
+#define LL_LLUIUSAGE_H
+
+#include <map>
+#include "llsd.h"
+#include "llsingleton.h"
+
+// UIUsage tracking to see which operations and UI elements are most popular in a session
+class LLUIUsage : public LLSingleton<LLUIUsage>
+{
+public:
+ LLSINGLETON(LLUIUsage);
+ ~LLUIUsage();
+public:
+ static std::string sanitized(const std::string& s);
+ static void setLLSDPath(LLSD& sd, const std::string& path, S32 max_elts, const LLSD& val);
+ static void setLLSDNested(LLSD& sd, const std::vector<std::string>& fields, const LLSD& val);
+ void logCommand(const std::string& command);
+ void logControl(const std::string& control);
+ void logFloater(const std::string& floater);
+ void logPanel(const std::string& p);
+ LLSD asLLSD() const;
+ void clear();
+private:
+ std::map<std::string,U32> mCommandCounts;
+ std::map<std::string,U32> mControlCounts;
+ std::map<std::string,U32> mFloaterCounts;
+ std::map<std::string,U32> mPanelCounts;
+};
+
+#endif // LLUIUIUSAGE.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 626a3d1ff3..f1c5fc0b14 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/llchatbar.cpp b/indra/newview/llchatbar.cpp
index 3ab5c669c4..e400609a74 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -58,6 +58,7 @@
#include "llmultigesture.h"
#include "llui.h"
#include "lluictrlfactory.h"
+#include "lluiusage.h"
//
// Globals
@@ -566,6 +567,8 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
// as soon as we say something, we no longer care about teaching the user
// how to chat
gWarningSettings.setBOOL("FirstOtherChatBeforeUser", FALSE);
+
+ LLUIUsage::instance().logCommand("Chat.Send"); // Pseudo-command
// Look for "/20 foo" channel chats.
S32 channel = 0;
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/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index a6531ed7e1..3a850d4b68 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -67,6 +67,7 @@
#include "llviewerchat.h"
#include "lltranslate.h"
#include "llautoreplace.h"
+#include "lluiusage.h"
S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0;
@@ -697,6 +698,7 @@ void LLFloaterIMNearbyChat::sendChatFromViewer(const std::string &utf8text, ECha
void LLFloaterIMNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
{
+ LLUIUsage::instance().logCommand("Chat.Send"); // pseuo-command
// Look for "/20 foo" channel chats.
S32 channel = 0;
LLWString out_text = stripChannelNumber(wtext, &channel);
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index d2bd716f55..65c91b54b7 100644
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -366,6 +366,11 @@ void LLGroupActions::show(const LLUUID& group_id)
params["open_tab_name"] = "panel_group_info_sidetray";
LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params);
+ LLFloater *floater = LLFloaterReg::getTypedInstance<LLFloaterSidePanelContainer>("people");
+ if (!floater->isFrontmost())
+ {
+ floater->setVisibleAndFrontmost(TRUE, params);
+ }
}
void LLGroupActions::refresh_notices()
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/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp
index 6751c25fb9..5b24800038 100644
--- a/indra/newview/llpanellandmarkinfo.cpp
+++ b/indra/newview/llpanellandmarkinfo.cpp
@@ -360,6 +360,11 @@ void LLPanelLandmarkInfo::toggleLandmarkEditMode(BOOL enabled)
setFocus(TRUE);
}
+void LLPanelLandmarkInfo::setCanEdit(BOOL enabled)
+{
+ getChild<LLButton>("edit_btn")->setEnabled(enabled);
+}
+
const std::string& LLPanelLandmarkInfo::getLandmarkTitle() const
{
return mLandmarkTitleEditor->getText();
diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h
index 9712736182..f303d87ccf 100644
--- a/indra/newview/llpanellandmarkinfo.h
+++ b/indra/newview/llpanellandmarkinfo.h
@@ -51,6 +51,7 @@ public:
void displayItemInfo(const LLInventoryItem* pItem);
void toggleLandmarkEditMode(BOOL enabled);
+ void setCanEdit(BOOL enabled);
const std::string& getLandmarkTitle() const;
const std::string getLandmarkNotes() const;
diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp
index 53870fb5c7..5dfeb7f619 100644
--- a/indra/newview/llpanelplaces.cpp
+++ b/indra/newview/llpanelplaces.cpp
@@ -430,10 +430,15 @@ void LLPanelPlaces::onOpen(const LLSD& key)
{
mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK);
- LLInventoryItem* item = gInventory.getItem(key["id"].asUUID());
+ LLUUID id = key["id"].asUUID();
+ LLInventoryItem* item = gInventory.getItem(id);
if (!item)
return;
+ BOOL is_editable = gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())
+ && item->getPermissions().allowModifyBy(gAgent.getID());
+ mLandmarkInfo->setCanEdit(is_editable);
+
setItem(item);
}
else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index e2a39bdf86..3f22a26474 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -142,6 +142,7 @@
#include "lltoolmgr.h"
#include "lltrans.h"
#include "llui.h"
+#include "lluiusage.h"
#include "llurldispatcher.h"
#include "llurlentry.h"
#include "llslurl.h"
@@ -1810,6 +1811,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
@@ -1853,6 +1861,7 @@ bool idle_startup()
display_startup();
LLStartUp::setStartupState( STATE_MISC );
display_startup();
+
return FALSE;
}
@@ -2260,6 +2269,8 @@ bool idle_startup()
gAgentAvatarp->sendHoverHeight();
+ LLUIUsage::instance().clear();
+
return TRUE;
}
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 458fc3b13d..1385eb0609 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -116,7 +116,6 @@
#include "llviewerregion.h"
#include "llfloaterregionrestarting.h"
-#include <boost/algorithm/string/split.hpp> //
#include <boost/foreach.hpp>
#include "llnotificationmanager.h" //
@@ -3852,7 +3851,12 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
}
// Don't play sounds from gestures if they are not enabled.
- if (object_id == owner_id && !gSavedSettings.getBOOL("EnableGestureSounds"))
+ // Do play sounds triggered by avatar, since muting your own
+ // gesture sounds and your own sounds played inworld from
+ // Inventory can cause confusion.
+ if (object_id == owner_id
+ && owner_id != gAgentID
+ && !gSavedSettings.getBOOL("EnableGestureSounds"))
{
return;
}
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 05f88b0a75..606e679ab1 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -63,6 +63,8 @@
#include "llsdutil.h"
#include "llcorehttputil.h"
#include "llvoicevivox.h"
+#include "llinventorymodel.h"
+#include "lluiusage.h"
namespace LLStatViewer
{
@@ -577,6 +579,13 @@ 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["ui"] = LLUIUsage::instance().asLLSD();
+
body["stats"]["voice"] = LLVoiceVivoxStats::getInstance()->read();
// Misc stats, two strings and two ints
diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp
index a2e519a61a..040d0deaf3 100644
--- a/indra/newview/llworldmipmap.cpp
+++ b/indra/newview/llworldmipmap.cpp
@@ -190,6 +190,8 @@ LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32
//LL_INFOS("WorldMap") << "LLWorldMipmap::loadObjectsTile(), URL = " << imageurl << LL_ENDL;
LLPointer<LLViewerFetchedTexture> img = LLViewerTextureManager::getFetchedTextureFromUrl(imageurl, FTT_MAP_TILE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ LL_INFOS("MAPURL") << "fetching map tile from " << imageurl << LL_ENDL;
+
img->setBoostLevel(LLGLTexture::BOOST_MAP);
// Return the smart pointer