summaryrefslogtreecommitdiff
path: root/indra/newview/llappearancemgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llappearancemgr.cpp')
-rwxr-xr-x[-rw-r--r--]indra/newview/llappearancemgr.cpp2889
1 files changed, 2053 insertions, 836 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index f40fed5ad3..b3317e937e 100644..100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -26,6 +26,7 @@
#include "llviewerprecompiledheaders.h"
+#include <boost/lexical_cast.hpp>
#include "llaccordionctrltab.h"
#include "llagent.h"
#include "llagentcamera.h"
@@ -34,6 +35,7 @@
#include "llattachmentsmgr.h"
#include "llcommandhandler.h"
#include "lleventtimer.h"
+#include "llfloatersidepanelcontainer.h"
#include "llgesturemgr.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
@@ -43,12 +45,26 @@
#include "lloutfitslist.h"
#include "llselectmgr.h"
#include "llsidepanelappearance.h"
-#include "llsidetray.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
#include "llvoavatarself.h"
#include "llviewerregion.h"
#include "llwearablelist.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llhttpretrypolicy.h"
+#include "llaisapi.h"
+
+#if LL_MSVC
+// disable boost::lexical_cast warning
+#pragma warning (disable:4702)
+#endif
+
+std::string self_av_string()
+{
+ // On logout gAgentAvatarp can already be invalid
+ return isAgentAvatarValid() ? gAgentAvatarp->avString() : "";
+}
// RAII thingy to guarantee that a variable gets reset when the Setter
// goes out of scope. More general utility would be handy - TODO:
@@ -110,7 +126,13 @@ public:
{
// support secondlife:///app/appearance/show, but for now we just
// make all secondlife:///app/appearance SLapps behave this way
- LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD());
+ if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAppearance"))
+ {
+ LLNotificationsUtil::add("NoAppearance", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
+ return true;
+ }
+
+ LLFloaterSidePanelContainer::showPanel("appearance", LLSD());
return true;
}
};
@@ -128,94 +150,349 @@ LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string
item_array,
LLInventoryModel::EXCLUDE_TRASH,
has_name);
- if (0 == cat_array.count())
+ if (0 == cat_array.size())
return LLUUID();
else
{
- LLViewerInventoryCategory *cat = cat_array.get(0);
+ LLViewerInventoryCategory *cat = cat_array.at(0);
if (cat)
return cat->getUUID();
else
{
- llwarns << "null cat" << llendl;
+ LL_WARNS() << "null cat" << LL_ENDL;
return LLUUID();
}
}
}
-class LLWearInventoryCategoryCallback : public LLInventoryCallback
+// We want this to be much lower (e.g. 15.0 is usually fine), bumping
+// up for now until we can diagnose some cases of very slow response
+// to requests.
+const F32 DEFAULT_RETRY_AFTER_INTERVAL = 300.0;
+
+// Given the current back-end problems, retrying is causing too many
+// duplicate items. Bump this back to 2 once they are resolved (or can
+// leave at 0 if the operations become actually reliable).
+const S32 DEFAULT_MAX_RETRIES = 0;
+
+class LLCallAfterInventoryBatchMgr: public LLEventTimer
{
public:
- LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
+ LLCallAfterInventoryBatchMgr(const LLUUID& dst_cat_id,
+ const std::string& phase_name,
+ nullary_func_t on_completion_func,
+ nullary_func_t on_failure_func = no_op,
+ F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
+ S32 max_retries = DEFAULT_MAX_RETRIES
+ ):
+ mDstCatID(dst_cat_id),
+ mTrackingPhase(phase_name),
+ mOnCompletionFunc(on_completion_func),
+ mOnFailureFunc(on_failure_func),
+ mRetryAfter(retry_after),
+ mMaxRetries(max_retries),
+ mPendingRequests(0),
+ mFailCount(0),
+ mCompletionOrFailureCalled(false),
+ mRetryCount(0),
+ LLEventTimer(5.0)
+ {
+ if (!mTrackingPhase.empty())
+ {
+ selfStartPhase(mTrackingPhase);
+ }
+ }
+
+ void addItems(LLInventoryModel::item_array_t& src_items)
{
- mCatID = cat_id;
- mAppend = append;
+ for (LLInventoryModel::item_array_t::const_iterator it = src_items.begin();
+ it != src_items.end();
+ ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+ llassert(item);
+ addItem(item->getUUID());
+ }
}
- void fire(const LLUUID& item_id)
+
+ // Request or re-request operation for specified item.
+ void addItem(const LLUUID& item_id)
{
- /*
- * Do nothing. We only care about the destructor
- *
- * The reason for this is that this callback is used in a hack where the
- * same callback is given to dozens of items, and the destructor is called
- * after the last item has fired the event and dereferenced it -- if all
- * the events actually fire!
- */
+ LL_DEBUGS("Avatar") << "item_id " << item_id << LL_ENDL;
+ if (!requestOperation(item_id))
+ {
+ LL_DEBUGS("Avatar") << "item_id " << item_id << " requestOperation false, skipping" << LL_ENDL;
+ return;
+ }
+
+ mPendingRequests++;
+ // On a re-request, this will reset the timer.
+ mWaitTimes[item_id] = LLTimer();
+ if (mRetryCounts.find(item_id) == mRetryCounts.end())
+ {
+ mRetryCounts[item_id] = 0;
+ }
+ else
+ {
+ mRetryCounts[item_id]++;
+ }
}
-protected:
- ~LLWearInventoryCategoryCallback()
+ virtual bool requestOperation(const LLUUID& item_id) = 0;
+
+ void onOp(const LLUUID& src_id, const LLUUID& dst_id, LLTimer timestamp)
{
- llinfos << "done all inventory callbacks" << llendl;
+ if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateLateOpRate"))
+ {
+ LL_WARNS() << "Simulating late operation by punting handling to later" << LL_ENDL;
+ doAfterInterval(boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,src_id,dst_id,timestamp),
+ mRetryAfter);
+ return;
+ }
+ mPendingRequests--;
+ F32 elapsed = timestamp.getElapsedTimeF32();
+ LL_DEBUGS("Avatar") << "op done, src_id " << src_id << " dst_id " << dst_id << " after " << elapsed << " seconds" << LL_ENDL;
+ if (mWaitTimes.find(src_id) == mWaitTimes.end())
+ {
+ // No longer waiting for this item - either serviced
+ // already or gave up after too many retries.
+ LL_WARNS() << "duplicate or late operation, src_id " << src_id << "dst_id " << dst_id
+ << " elapsed " << elapsed << " after end " << (S32) mCompletionOrFailureCalled << LL_ENDL;
+ }
+ mTimeStats.push(elapsed);
+ mWaitTimes.erase(src_id);
+ if (mWaitTimes.empty() && !mCompletionOrFailureCalled)
+ {
+ onCompletionOrFailure();
+ }
+ }
+
+ void onCompletionOrFailure()
+ {
+ assert (!mCompletionOrFailureCalled);
+ mCompletionOrFailureCalled = true;
- // Is the destructor called by ordinary dereference, or because the app's shutting down?
- // If the inventory callback manager goes away, we're shutting down, no longer want the callback.
- if( LLInventoryCallbackManager::is_instantiated() )
+ // Will never call onCompletion() if any item has been flagged as
+ // a failure - otherwise could wind up with corrupted
+ // outfit, involuntary nudity, etc.
+ reportStats();
+ if (!mTrackingPhase.empty())
+ {
+ selfStopPhase(mTrackingPhase);
+ }
+ if (!mFailCount)
{
- LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
+ onCompletion();
}
else
{
- llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
+ onFailure();
}
}
-private:
- LLUUID mCatID;
- bool mAppend;
-};
+ void onFailure()
+ {
+ LL_INFOS() << "failed" << LL_ENDL;
+ mOnFailureFunc();
+ }
+
+ void onCompletion()
+ {
+ LL_INFOS() << "done" << LL_ENDL;
+ mOnCompletionFunc();
+ }
+
+ // virtual
+ // Will be deleted after returning true - only safe to do this if all callbacks have fired.
+ BOOL tick()
+ {
+ // mPendingRequests will be zero if all requests have been
+ // responded to. mWaitTimes.empty() will be true if we have
+ // received at least one reply for each UUID. If requests
+ // have been dropped and retried, these will not necessarily
+ // be the same. Only safe to return true if all requests have
+ // been serviced, since it will result in this object being
+ // deleted.
+ bool all_done = (mPendingRequests==0);
+
+ if (!mWaitTimes.empty())
+ {
+ LL_WARNS() << "still waiting on " << mWaitTimes.size() << " items" << LL_ENDL;
+ for (std::map<LLUUID,LLTimer>::iterator it = mWaitTimes.begin();
+ it != mWaitTimes.end();)
+ {
+ // Use a copy of iterator because it may be erased/invalidated.
+ std::map<LLUUID,LLTimer>::iterator curr_it = it;
+ ++it;
+
+ F32 time_waited = curr_it->second.getElapsedTimeF32();
+ S32 retries = mRetryCounts[curr_it->first];
+ if (time_waited > mRetryAfter)
+ {
+ if (retries < mMaxRetries)
+ {
+ LL_DEBUGS("Avatar") << "Waited " << time_waited <<
+ " for " << curr_it->first << ", retrying" << LL_ENDL;
+ mRetryCount++;
+ addItem(curr_it->first);
+ }
+ else
+ {
+ LL_WARNS() << "Giving up on " << curr_it->first << " after too many retries" << LL_ENDL;
+ mWaitTimes.erase(curr_it);
+ mFailCount++;
+ }
+ }
+ if (mWaitTimes.empty())
+ {
+ onCompletionOrFailure();
+ }
+ }
+ }
+ return all_done;
+ }
-//Inventory callback updating "dirty" state when destroyed
-class LLUpdateDirtyState: public LLInventoryCallback
+ void reportStats()
+ {
+ LL_DEBUGS("Avatar") << "Phase: " << mTrackingPhase << LL_ENDL;
+ LL_DEBUGS("Avatar") << "mFailCount: " << mFailCount << LL_ENDL;
+ LL_DEBUGS("Avatar") << "mRetryCount: " << mRetryCount << LL_ENDL;
+ LL_DEBUGS("Avatar") << "Times: n " << mTimeStats.getCount() << " min " << mTimeStats.getMinValue() << " max " << mTimeStats.getMaxValue() << LL_ENDL;
+ LL_DEBUGS("Avatar") << "Mean " << mTimeStats.getMean() << " stddev " << mTimeStats.getStdDev() << LL_ENDL;
+ }
+
+ virtual ~LLCallAfterInventoryBatchMgr()
+ {
+ LL_DEBUGS("Avatar") << "deleting" << LL_ENDL;
+ }
+
+protected:
+ std::string mTrackingPhase;
+ std::map<LLUUID,LLTimer> mWaitTimes;
+ std::map<LLUUID,S32> mRetryCounts;
+ LLUUID mDstCatID;
+ nullary_func_t mOnCompletionFunc;
+ nullary_func_t mOnFailureFunc;
+ F32 mRetryAfter;
+ S32 mMaxRetries;
+ S32 mPendingRequests;
+ S32 mFailCount;
+ S32 mRetryCount;
+ bool mCompletionOrFailureCalled;
+ LLViewerStats::StatsAccumulator mTimeStats;
+};
+
+class LLCallAfterInventoryCopyMgr: public LLCallAfterInventoryBatchMgr
{
public:
- LLUpdateDirtyState() {}
- virtual ~LLUpdateDirtyState()
+ LLCallAfterInventoryCopyMgr(LLInventoryModel::item_array_t& src_items,
+ const LLUUID& dst_cat_id,
+ const std::string& phase_name,
+ nullary_func_t on_completion_func,
+ nullary_func_t on_failure_func = no_op,
+ F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
+ S32 max_retries = DEFAULT_MAX_RETRIES
+ ):
+ LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries)
+ {
+ addItems(src_items);
+ sInstanceCount++;
+ }
+
+ ~LLCallAfterInventoryCopyMgr()
{
- if (LLAppearanceMgr::instanceExists())
+ sInstanceCount--;
+ }
+
+ virtual bool requestOperation(const LLUUID& item_id)
+ {
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ llassert(item);
+ LL_DEBUGS("Avatar") << "copying item " << item_id << LL_ENDL;
+ if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
{
- LLAppearanceMgr::getInstance()->updateIsDirty();
+ LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << LL_ENDL;
+ return true;
}
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ mDstCatID,
+ std::string(),
+ new LLBoostFuncInventoryCallback(boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer()))
+ );
+ return true;
}
- virtual void fire(const LLUUID&) {}
+
+ static S32 getInstanceCount() { return sInstanceCount; }
+
+private:
+ static S32 sInstanceCount;
};
+S32 LLCallAfterInventoryCopyMgr::sInstanceCount = 0;
-LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering):
- mFireCount(0),
- mUpdateBaseOrder(update_base_outfit_ordering)
+class LLWearCategoryAfterCopy: public LLInventoryCallback
{
-}
+public:
+ LLWearCategoryAfterCopy(bool append):
+ mAppend(append)
+ {}
-LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
+ // virtual
+ void fire(const LLUUID& id)
+ {
+ // Wear the inventory category.
+ LLInventoryCategory* cat = gInventory.getCategory(id);
+ LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(cat, mAppend);
+ }
+
+private:
+ bool mAppend;
+};
+
+class LLTrackPhaseWrapper : public LLInventoryCallback
{
- llinfos << "done update appearance on destroy" << llendl;
-
- if (!LLApp::isExiting())
+public:
+ LLTrackPhaseWrapper(const std::string& phase_name, LLPointer<LLInventoryCallback> cb = NULL):
+ mTrackingPhase(phase_name),
+ mCB(cb)
{
- LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
+ selfStartPhase(mTrackingPhase);
}
+
+ // virtual
+ void fire(const LLUUID& id)
+ {
+ if (mCB)
+ {
+ mCB->fire(id);
+ }
+ }
+
+ // virtual
+ ~LLTrackPhaseWrapper()
+ {
+ selfStopPhase(mTrackingPhase);
+ }
+
+protected:
+ std::string mTrackingPhase;
+ LLPointer<LLInventoryCallback> mCB;
+};
+
+LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool enforce_item_restrictions,
+ bool enforce_ordering,
+ nullary_func_t post_update_func
+ ):
+ mFireCount(0),
+ mEnforceItemRestrictions(enforce_item_restrictions),
+ mEnforceOrdering(enforce_ordering),
+ mPostUpdateFunc(post_update_func)
+{
+ selfStartPhase("update_appearance_on_destroy");
}
void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
@@ -223,11 +500,60 @@ void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl;
+ LL_DEBUGS("Avatar") << self_av_string() << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << LL_ENDL;
#endif
mFireCount++;
}
+LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
+{
+ if (!LLApp::isExiting())
+ {
+ // speculative fix for MAINT-1150
+ LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
+
+ selfStopPhase("update_appearance_on_destroy");
+
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(mEnforceItemRestrictions,
+ mEnforceOrdering,
+ mPostUpdateFunc);
+ }
+}
+
+LLUpdateAppearanceAndEditWearableOnDestroy::LLUpdateAppearanceAndEditWearableOnDestroy(const LLUUID& item_id):
+ mItemID(item_id)
+{
+}
+
+void edit_wearable_and_customize_avatar(LLUUID item_id)
+{
+ // Start editing the item if previously requested.
+ gAgentWearables.editWearableIfRequested(item_id);
+
+ // TODO: camera mode may not be changed if a debug setting is tweaked
+ if( gAgentCamera.cameraCustomizeAvatar() )
+ {
+ // If we're in appearance editing mode, the current tab may need to be refreshed
+ LLSidepanelAppearance *panel = dynamic_cast<LLSidepanelAppearance*>(
+ LLFloaterSidePanelContainer::getPanel("appearance"));
+ if (panel)
+ {
+ panel->showDefaultSubpart();
+ }
+ }
+}
+
+LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOnDestroy()
+{
+ if (!LLApp::isExiting())
+ {
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(
+ true,true,
+ boost::bind(edit_wearable_and_customize_avatar, mItemID));
+ }
+}
+
+
struct LLFoundData
{
LLFoundData() :
@@ -255,13 +581,15 @@ struct LLFoundData
std::string mName;
LLAssetType::EType mAssetType;
LLWearableType::EType mWearableType;
- LLWearable* mWearable;
+ LLViewerWearable* mWearable;
bool mIsReplacement;
};
class LLWearableHoldingPattern
{
+ LOG_CLASS(LLWearableHoldingPattern);
+
public:
LLWearableHoldingPattern();
~LLWearableHoldingPattern();
@@ -277,7 +605,7 @@ public:
void recoverMissingWearable(LLWearableType::EType type);
void clearCOFLinksForMissingWearables();
- void onWearableAssetFetch(LLWearable *wearable);
+ void onWearableAssetFetch(LLViewerWearable *wearable);
void onAllComplete();
typedef std::list<LLFoundData> found_list_t;
@@ -289,6 +617,8 @@ public:
bool isMostRecent();
void handleLateArrivals();
void resetTime(F32 timeout);
+ static S32 countActive() { return sActiveHoldingPatterns.size(); }
+ S32 index() { return mIndex; }
private:
found_list_t mFoundList;
@@ -302,12 +632,15 @@ private:
bool mFired;
typedef std::set<LLWearableHoldingPattern*> type_set_hp;
static type_set_hp sActiveHoldingPatterns;
+ static S32 sNextIndex;
+ S32 mIndex;
bool mIsMostRecent;
- std::set<LLWearable*> mLateArrivals;
+ std::set<LLViewerWearable*> mLateArrivals;
bool mIsAllComplete;
};
LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns;
+S32 LLWearableHoldingPattern::sNextIndex = 0;
LLWearableHoldingPattern::LLWearableHoldingPattern():
mResolved(0),
@@ -315,13 +648,13 @@ LLWearableHoldingPattern::LLWearableHoldingPattern():
mIsMostRecent(true),
mIsAllComplete(false)
{
- if (sActiveHoldingPatterns.size()>0)
+ if (countActive()>0)
{
- llinfos << "Creating LLWearableHoldingPattern when "
- << sActiveHoldingPatterns.size()
- << " other attempts are active."
- << " Flagging others as invalid."
- << llendl;
+ LL_INFOS() << "Creating LLWearableHoldingPattern when "
+ << countActive()
+ << " other attempts are active."
+ << " Flagging others as invalid."
+ << LL_ENDL;
for (type_set_hp::iterator it = sActiveHoldingPatterns.begin();
it != sActiveHoldingPatterns.end();
++it)
@@ -330,12 +663,20 @@ LLWearableHoldingPattern::LLWearableHoldingPattern():
}
}
+ mIndex = sNextIndex++;
sActiveHoldingPatterns.insert(this);
+ LL_DEBUGS("Avatar") << "HP " << index() << " created" << LL_ENDL;
+ selfStartPhase("holding_pattern");
}
LLWearableHoldingPattern::~LLWearableHoldingPattern()
{
sActiveHoldingPatterns.erase(this);
+ if (isMostRecent())
+ {
+ selfStopPhase("holding_pattern");
+ }
+ LL_DEBUGS("Avatar") << "HP " << index() << " deleted" << LL_ENDL;
}
bool LLWearableHoldingPattern::isMostRecent()
@@ -382,9 +723,10 @@ void LLWearableHoldingPattern::checkMissingWearables()
{
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway why don't we actually skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
-
+
std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0);
std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0);
for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
@@ -400,7 +742,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
{
if (requested_by_type[type] > found_by_type[type])
{
- llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
+ LL_WARNS() << self_av_string() << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << LL_ENDL;
}
if (found_by_type[type] > 0)
continue;
@@ -417,11 +759,16 @@ void LLWearableHoldingPattern::checkMissingWearables()
mTypesToRecover.insert(type);
mTypesToLink.insert(type);
recoverMissingWearable((LLWearableType::EType)type);
- llwarns << "need to replace " << type << llendl;
+ LL_WARNS() << self_av_string() << "need to replace " << type << LL_ENDL;
}
}
resetTime(60.0F);
+
+ if (isMostRecent())
+ {
+ selfStartPhase("get_missing_wearables_2");
+ }
if (!pollMissingWearables())
{
doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
@@ -430,15 +777,21 @@ void LLWearableHoldingPattern::checkMissingWearables()
void LLWearableHoldingPattern::onAllComplete()
{
+ if (isAgentAvatarValid())
+ {
+ gAgentAvatarp->outputRezTiming("Agent wearables fetch complete");
+ }
+
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway need to skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
// Activate all gestures in this folder
- if (mGestItems.count() > 0)
+ if (mGestItems.size() > 0)
{
- llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
+ LL_DEBUGS("Avatar") << self_av_string() << "Activating " << mGestItems.size() << " gestures" << LL_ENDL;
LLGestureMgr::instance().activateGestures(mGestItems);
@@ -454,15 +807,48 @@ void LLWearableHoldingPattern::onAllComplete()
}
}
- // Update wearables.
- llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
- LLAppearanceMgr::instance().updateAgentWearables(this, false);
-
- // Update attachments to match those requested.
if (isAgentAvatarValid())
{
- llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
- LLAgentWearables::userUpdateAttachments(mObjItems);
+ LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL;
+ LLAgentWearables::llvo_vec_t objects_to_remove;
+ LLAgentWearables::llvo_vec_t objects_to_retain;
+ LLInventoryModel::item_array_t items_to_add;
+
+ LLAgentWearables::findAttachmentsAddRemoveInfo(mObjItems,
+ objects_to_remove,
+ objects_to_retain,
+ items_to_add);
+
+ LL_DEBUGS("Avatar") << self_av_string() << "Removing " << objects_to_remove.size()
+ << " attachments" << LL_ENDL;
+
+ // Here we remove the attachment pos overrides for *all*
+ // attachments, even those that are not being removed. This is
+ // needed to get joint positions all slammed down to their
+ // pre-attachment states.
+ gAgentAvatarp->clearAttachmentPosOverrides();
+
+ // Take off the attachments that will no longer be in the outfit.
+ LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
+
+ // Update wearables.
+ LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " updating agent wearables with "
+ << mResolved << " wearable items " << LL_ENDL;
+ LLAppearanceMgr::instance().updateAgentWearables(this);
+
+ // Restore attachment pos overrides for the attachments that
+ // are remaining in the outfit.
+ for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin();
+ it != objects_to_retain.end();
+ ++it)
+ {
+ LLViewerObject *objectp = *it;
+ gAgentAvatarp->addAttachmentPosOverridesForObject(objectp);
+ }
+
+ // Add new attachments to match those requested.
+ LL_DEBUGS("Avatar") << self_av_string() << "Adding " << items_to_add.size() << " attachments" << LL_ENDL;
+ LLAgentWearables::userAttachMultipleAttachments(items_to_add);
}
if (isFetchCompleted() && isMissingCompleted())
@@ -479,9 +865,15 @@ void LLWearableHoldingPattern::onAllComplete()
void LLWearableHoldingPattern::onFetchCompletion()
{
+ if (isMostRecent())
+ {
+ selfStopPhase("get_wearables_2");
+ }
+
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
checkMissingWearables();
@@ -492,7 +884,8 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
{
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
bool completed = isFetchCompleted();
@@ -501,14 +894,14 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
if (done)
{
- llinfos << "polling, done status: " << completed << " timed out " << timed_out
- << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
+ LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " polling, done status: " << completed << " timed out " << timed_out
+ << " elapsed " << mWaitTime.getElapsedTimeF32() << LL_ENDL;
mFired = true;
if (timed_out)
{
- llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
+ LL_WARNS() << self_av_string() << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << LL_ENDL;
}
onFetchCompletion();
@@ -516,115 +909,85 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
return done;
}
-class RecoveredItemLinkCB: public LLInventoryCallback
+void recovered_item_link_cb(const LLUUID& item_id, LLWearableType::EType type, LLViewerWearable *wearable, LLWearableHoldingPattern* holder)
{
-public:
- RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
- mHolder(holder),
- mWearable(wearable),
- mType(type)
+ if (!holder->isMostRecent())
{
+ LL_WARNS() << "HP " << holder->index() << " skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
+ // runway skip here?
}
- void fire(const LLUUID& item_id)
- {
- if (!mHolder->isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
- llinfos << "Recovered item link for type " << mType << llendl;
- mHolder->eraseTypeToLink(mType);
- // Add wearable to FoundData for actual wearing
- LLViewerInventoryItem *item = gInventory.getItem(item_id);
- LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
+ LL_INFOS() << "HP " << holder->index() << " recovered item link for type " << type << LL_ENDL;
+ holder->eraseTypeToLink(type);
+ // Add wearable to FoundData for actual wearing
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
- if (linked_item)
- {
- gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+ if (linked_item)
+ {
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
- if (item)
- {
- LLFoundData found(linked_item->getUUID(),
- linked_item->getAssetUUID(),
- linked_item->getName(),
- linked_item->getType(),
- linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID,
- true // is replacement
- );
- found.mWearable = mWearable;
- mHolder->getFoundList().push_front(found);
- }
- else
- {
- llwarns << "inventory item not found for recovered wearable" << llendl;
- }
+ if (item)
+ {
+ LLFoundData found(linked_item->getUUID(),
+ linked_item->getAssetUUID(),
+ linked_item->getName(),
+ linked_item->getType(),
+ linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID,
+ true // is replacement
+ );
+ found.mWearable = wearable;
+ holder->getFoundList().push_front(found);
}
else
{
- llwarns << "inventory link not found for recovered wearable" << llendl;
+ LL_WARNS() << self_av_string() << "inventory link not found for recovered wearable" << LL_ENDL;
}
}
-private:
- LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
- LLWearableType::EType mType;
-};
+ else
+ {
+ LL_WARNS() << self_av_string() << "HP " << holder->index() << " inventory link not found for recovered wearable" << LL_ENDL;
+ }
+}
-class RecoveredItemCB: public LLInventoryCallback
+void recovered_item_cb(const LLUUID& item_id, LLWearableType::EType type, LLViewerWearable *wearable, LLWearableHoldingPattern* holder)
{
-public:
- RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
- mHolder(holder),
- mWearable(wearable),
- mType(type)
+ if (!holder->isMostRecent())
{
+ // runway skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
- void fire(const LLUUID& item_id)
+
+ LL_DEBUGS("Avatar") << self_av_string() << "Recovered item for type " << type << LL_ENDL;
+ LLConstPointer<LLInventoryObject> itemp = gInventory.getItem(item_id);
+ wearable->setItemID(item_id);
+ holder->eraseTypeToRecover(type);
+ llassert(itemp);
+ if (itemp)
{
- if (!mHolder->isMostRecent())
- {
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
- }
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(recovered_item_link_cb,_1,type,wearable,holder));
- llinfos << "Recovered item for type " << mType << llendl;
- LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
- mWearable->setItemID(item_id);
- LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
- mHolder->eraseTypeToRecover(mType);
- llassert(itemp);
- if (itemp)
- {
- link_inventory_item( gAgent.getID(),
- item_id,
- LLAppearanceMgr::instance().getCOF(),
- itemp->getName(),
- itemp->getDescription(),
- LLAssetType::AT_LINK,
- cb);
- }
+ link_inventory_object(LLAppearanceMgr::instance().getCOF(), itemp, cb);
}
-private:
- LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
- LLWearableType::EType mType;
-};
+}
void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type)
{
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
// Try to recover by replacing missing wearable with a new one.
LLNotificationsUtil::add("ReplacedMissingWearable");
- lldebugs << "Wearable " << LLWearableType::getTypeLabel(type)
- << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
- LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
+ LL_DEBUGS() << "Wearable " << LLWearableType::getTypeLabel(type)
+ << " could not be downloaded. Replaced inventory item with default wearable." << LL_ENDL;
+ LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp);
// Add a new one in the lost and found folder.
const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
- LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this);
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(recovered_item_cb,_1,type,wearable,this));
create_inventory_item(gAgent.getID(),
gAgent.getSessionID(),
@@ -652,8 +1015,8 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
{
// Wearable link that was never resolved; remove links to it from COF
- llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
- LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
+ LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
+ LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID);
}
}
}
@@ -662,7 +1025,8 @@ bool LLWearableHoldingPattern::pollMissingWearables()
{
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ // runway skip here?
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
bool timed_out = isTimedOut();
@@ -671,15 +1035,20 @@ bool LLWearableHoldingPattern::pollMissingWearables()
if (!done)
{
- llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
+ LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " polling missing wearables, waiting for items " << mTypesToRecover.size()
<< " links " << mTypesToLink.size()
<< " wearables, timed out " << timed_out
<< " elapsed " << mWaitTime.getElapsedTimeF32()
- << " done " << done << llendl;
+ << " done " << done << LL_ENDL;
}
if (done)
{
+ if (isMostRecent())
+ {
+ selfStopPhase("get_missing_wearables_2");
+ }
+
gAgentAvatarp->debugWearablesLoaded();
// BAP - if we don't call clearCOFLinksForMissingWearables()
@@ -709,14 +1078,14 @@ void LLWearableHoldingPattern::handleLateArrivals()
}
if (!isMostRecent())
{
- llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl;
+ LL_WARNS() << self_av_string() << "Late arrivals not handled - outfit change no longer valid" << LL_ENDL;
}
if (!mIsAllComplete)
{
- llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
+ LL_WARNS() << self_av_string() << "Late arrivals not handled - in middle of missing wearables processing" << LL_ENDL;
}
- llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl;
+ LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " need to handle " << mLateArrivals.size() << " late arriving wearables" << LL_ENDL;
// Update mFoundList using late-arriving wearables.
std::set<LLWearableType::EType> replaced_types;
@@ -724,11 +1093,11 @@ void LLWearableHoldingPattern::handleLateArrivals()
iter != getFoundList().end(); ++iter)
{
LLFoundData& data = *iter;
- for (std::set<LLWearable*>::iterator wear_it = mLateArrivals.begin();
+ for (std::set<LLViewerWearable*>::iterator wear_it = mLateArrivals.begin();
wear_it != mLateArrivals.end();
++wear_it)
{
- LLWearable *wearable = *wear_it;
+ LLViewerWearable *wearable = *wear_it;
if(wearable->getAssetID() == data.mAssetID)
{
@@ -764,7 +1133,7 @@ void LLWearableHoldingPattern::handleLateArrivals()
if (data.mWearable && data.mIsReplacement &&
replaced_types.find(data.mWearableType) != replaced_types.end())
{
- LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
+ LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID);
std::list<LLFoundData>::iterator clobber_ator = iter;
++iter;
getFoundList().erase(clobber_ator);
@@ -779,7 +1148,7 @@ void LLWearableHoldingPattern::handleLateArrivals()
mLateArrivals.clear();
// Update appearance based on mFoundList
- LLAppearanceMgr::instance().updateAgentWearables(this, false);
+ LLAppearanceMgr::instance().updateAgentWearables(this);
}
void LLWearableHoldingPattern::resetTime(F32 timeout)
@@ -788,23 +1157,23 @@ void LLWearableHoldingPattern::resetTime(F32 timeout)
mWaitTime.setTimerExpirySec(timeout);
}
-void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
+void LLWearableHoldingPattern::onWearableAssetFetch(LLViewerWearable *wearable)
{
if (!isMostRecent())
{
- llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+ LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
}
mResolved += 1; // just counting callbacks, not successes.
- llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl;
+ LL_DEBUGS("Avatar") << self_av_string() << "HP " << index() << " resolved " << mResolved << "/" << getFoundList().size() << LL_ENDL;
if (!wearable)
{
- llwarns << "no wearable found" << llendl;
+ LL_WARNS() << self_av_string() << "no wearable found" << LL_ENDL;
}
if (mFired)
{
- llwarns << "called after holder fired" << llendl;
+ LL_WARNS() << self_av_string() << "called after holder fired" << LL_ENDL;
if (wearable)
{
mLateArrivals.insert(wearable);
@@ -830,7 +1199,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
// Failing this means inventory or asset server are corrupted in a way we don't handle.
if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType))
{
- llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
+ LL_WARNS() << self_av_string() << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << LL_ENDL;
break;
}
@@ -839,7 +1208,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
}
}
-static void onWearableAssetFetch(LLWearable* wearable, void* data)
+static void onWearableAssetFetch(LLViewerWearable* wearable, void* data)
{
LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
holder->onWearableAssetFetch(wearable);
@@ -855,9 +1224,9 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
// encountered, so we actually keep the *last* of each duplicate
// item. This is needed to give the right priority when adding
// duplicate items to an existing outfit.
- for (S32 i=items.count()-1; i>=0; i--)
+ for (S32 i=items.size()-1; i>=0; i--)
{
- LLViewerInventoryItem *item = items.get(i);
+ LLViewerInventoryItem *item = items.at(i);
LLUUID item_id = item->getLinkedUUID();
if (items_seen.find(item_id)!=items_seen.end())
continue;
@@ -868,7 +1237,7 @@ static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
it != tmp_list.end();
++it)
{
- new_items.put(*it);
+ new_items.push_back(*it);
}
items = new_items;
}
@@ -878,6 +1247,18 @@ const LLUUID LLAppearanceMgr::getCOF() const
return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
}
+S32 LLAppearanceMgr::getCOFVersion() const
+{
+ LLViewerInventoryCategory *cof = gInventory.getCategory(getCOF());
+ if (cof)
+ {
+ return cof->getVersion();
+ }
+ else
+ {
+ return LLViewerInventoryCategory::VERSION_UNKNOWN;
+ }
+}
const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
{
@@ -891,8 +1272,7 @@ const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
cat_array,
item_array,
false,
- is_category,
- false);
+ is_category);
for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
iter != item_array.end();
iter++)
@@ -939,80 +1319,132 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID()
if (outfit_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
{
- llwarns << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << llendl;
+ LL_WARNS() << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << LL_ENDL;
return LLUUID::null;
}
return outfit_cat->getUUID();
}
-bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb)
+void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)
{
- if (item_id_to_wear.isNull()) return false;
-
- // *TODO: issue with multi-wearable should be fixed:
- // in this case this method will be called N times - loading started for each item
- // and than N times will be called - loading completed for each item.
- // That means subscribers will be notified that loading is done after first item in a batch is worn.
- // (loading indicator disappears for example before all selected items are worn)
- // Have not fix this issue for 2.1 because of stability reason. EXT-7777.
-
- // Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times
-// gAgentWearables.notifyLoadingStarted();
-
- LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
- if (!item_to_wear) return false;
-
- if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
- {
- LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace);
- copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb);
- return false;
- }
- else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
- {
- return false; // not in library and not in agent's inventory
- }
- else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
- {
- LLNotificationsUtil::add("CannotWearTrash");
- return false;
- }
- else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911
+ if (inv_item.isNull())
+ return;
+
+ LLViewerInventoryItem *item = gInventory.getItem(inv_item);
+ if (item)
{
- return false;
+ LLAppearanceMgr::instance().wearItemOnAvatar(inv_item, true, do_replace);
}
+}
- switch (item_to_wear->getType())
- {
- case LLAssetType::AT_CLOTHING:
- if (gAgentWearables.areWearablesLoaded())
- {
- S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType());
- if ((replace && wearable_count != 0) ||
- (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
- {
- removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false);
- }
- addCOFItemLink(item_to_wear, do_update, cb);
- }
- break;
- case LLAssetType::AT_BODYPART:
- // TODO: investigate wearables may not be loaded at this point EXT-8231
-
- // Remove the existing wearables of the same type.
- // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
- removeCOFLinksOfType(item_to_wear->getWearableType(), false);
-
- addCOFItemLink(item_to_wear, do_update, cb);
- break;
- case LLAssetType::AT_OBJECT:
- rez_attachment(item_to_wear, NULL, replace);
- break;
- default: return false;;
- }
+void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
+ bool do_update,
+ bool replace,
+ LLPointer<LLInventoryCallback> cb)
+{
+ bool first = true;
+
+ LLInventoryObject::const_object_list_t items_to_link;
+
+ for (uuid_vec_t::const_iterator it = item_ids_to_wear.begin();
+ it != item_ids_to_wear.end();
+ ++it)
+ {
+ replace = first && replace;
+ first = false;
+
+ const LLUUID& item_id_to_wear = *it;
+
+ if (item_id_to_wear.isNull()) continue;
+
+ LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
+ if (!item_to_wear) continue;
+
+ if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
+ {
+ LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
+ copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
+ continue;
+ }
+ else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
+ {
+ continue; // not in library and not in agent's inventory
+ }
+ else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
+ {
+ LLNotificationsUtil::add("CannotWearTrash");
+ continue;
+ }
+ else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911
+ {
+ continue;
+ }
+
+ switch (item_to_wear->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ {
+ if (gAgentWearables.areWearablesLoaded())
+ {
+ if (!cb && do_update)
+ {
+ cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ }
+ LLWearableType::EType type = item_to_wear->getWearableType();
+ S32 wearable_count = gAgentWearables.getWearableCount(type);
+ if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
+ {
+ LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
+ wearable_count-1);
+ removeCOFItemLinks(item_id, cb);
+ }
+
+ items_to_link.push_back(item_to_wear);
+ }
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ {
+ // TODO: investigate wearables may not be loaded at this point EXT-8231
+
+ // Remove the existing wearables of the same type.
+ // Remove existing body parts anyway because we must not be able to wear e.g. two skins.
+ removeCOFLinksOfType(item_to_wear->getWearableType());
+ if (!cb && do_update)
+ {
+ cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear);
+ }
+ items_to_link.push_back(item_to_wear);
+ }
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ {
+ rez_attachment(item_to_wear, NULL, replace);
+ }
+ break;
+
+ default: continue;
+ }
+ }
+
+ // Batch up COF link creation - more efficient if using AIS.
+ if (items_to_link.size())
+ {
+ link_inventory_array(getCOF(), items_to_link, cb);
+ }
+}
- return true;
+void LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,
+ bool do_update,
+ bool replace,
+ LLPointer<LLInventoryCallback> cb)
+{
+ uuid_vec_t ids;
+ ids.push_back(item_id_to_wear);
+ wearItemsOnAvatar(ids, do_update, replace, cb);
}
// Update appearance from outfit folder.
@@ -1100,10 +1532,24 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
LLInventoryModel::item_array_t::const_iterator it = items.begin();
const LLInventoryModel::item_array_t::const_iterator it_end = items.end();
+ uuid_vec_t uuids_to_remove;
for( ; it_end != it; ++it)
{
LLViewerInventoryItem* item = *it;
- removeItemFromAvatar(item->getUUID());
+ uuids_to_remove.push_back(item->getUUID());
+ }
+ removeItemsFromAvatar(uuids_to_remove);
+
+ // deactivate all gestures in the outfit folder
+ LLInventoryModel::item_array_t gest_items;
+ getDescendentsOfAssetType(cat_id, gest_items, LLAssetType::AT_GESTURE);
+ for(S32 i = 0; i < gest_items.size(); ++i)
+ {
+ LLViewerInventoryItem *gest_item = gest_items[i];
+ if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) )
+ {
+ LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() );
+ }
}
}
@@ -1114,10 +1560,10 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds
LLInventoryCategory *src_cat = gInventory.getCategory(src_id);
if (!src_cat)
{
- llwarns << "folder not found for src " << src_id.asString() << llendl;
+ LL_WARNS() << "folder not found for src " << src_id.asString() << LL_ENDL;
return;
}
- llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl;
+ LL_INFOS() << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << LL_ENDL;
LLUUID parent_id = dst_id;
if(parent_id.isNull())
{
@@ -1131,6 +1577,58 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds
gInventory.notifyObservers();
}
+void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id,
+ bool include_folder_links, LLPointer<LLInventoryCallback> cb)
+{
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ LLSD contents = LLSD::emptyArray();
+ gInventory.getDirectDescendentsOf(src_id, cats, items);
+ LL_INFOS() << "copying " << items->size() << " items" << LL_ENDL;
+ for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
+ iter != items->end();
+ ++iter)
+ {
+ const LLViewerInventoryItem* item = (*iter);
+ switch (item->getActualType())
+ {
+ case LLAssetType::AT_LINK:
+ {
+ LL_DEBUGS("Avatar") << "linking inventory item " << item->getName() << LL_ENDL;
+ //getActualDescription() is used for a new description
+ //to propagate ordering information saved in descriptions of links
+ LLSD item_contents;
+ item_contents["name"] = item->getName();
+ item_contents["desc"] = item->getActualDescription();
+ item_contents["linked_id"] = item->getLinkedUUID();
+ item_contents["type"] = LLAssetType::AT_LINK;
+ contents.append(item_contents);
+ break;
+ }
+ case LLAssetType::AT_LINK_FOLDER:
+ {
+ LLViewerInventoryCategory *catp = item->getLinkedCategory();
+ if (catp && include_folder_links)
+ {
+ LL_DEBUGS("Avatar") << "linking inventory folder " << item->getName() << LL_ENDL;
+ LLSD base_contents;
+ base_contents["name"] = catp->getName();
+ base_contents["desc"] = ""; // categories don't have descriptions.
+ base_contents["linked_id"] = catp->getLinkedUUID();
+ base_contents["type"] = LLAssetType::AT_LINK_FOLDER;
+ contents.append(base_contents);
+ }
+ break;
+ }
+ default:
+ {
+ // Linux refuses to compile unless all possible enums are handled. Really, Linux?
+ break;
+ }
+ }
+ }
+ slam_inventory_folder(dst_id, contents, cb);
+}
// Copy contents of src_id to dst_id.
void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
LLPointer<LLInventoryCallback> cb)
@@ -1138,7 +1636,8 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(src_id, cats, items);
- llinfos << "copying " << items->count() << " items" << llendl;
+ LL_INFOS() << "copying " << items->size() << " items" << LL_ENDL;
+ LLInventoryObject::const_object_list_t link_array;
for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
iter != items->end();
++iter)
@@ -1148,14 +1647,8 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
{
case LLAssetType::AT_LINK:
{
- //LLInventoryItem::getDescription() is used for a new description
- //to propagate ordering information saved in descriptions of links
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- dst_id,
- item->getName(),
- item->LLInventoryItem::getDescription(),
- LLAssetType::AT_LINK, cb);
+ LL_DEBUGS("Avatar") << "linking inventory item " << item->getName() << LL_ENDL;
+ link_array.push_back(LLConstPointer<LLInventoryObject>(item));
break;
}
case LLAssetType::AT_LINK_FOLDER:
@@ -1164,12 +1657,8 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
// Skip copying outfit links.
if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT)
{
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- dst_id,
- item->getName(),
- item->getDescription(),
- LLAssetType::AT_LINK_FOLDER, cb);
+ LL_DEBUGS("Avatar") << "linking inventory folder " << item->getName() << LL_ENDL;
+ link_array.push_back(LLConstPointer<LLInventoryObject>(item));
}
break;
}
@@ -1178,7 +1667,7 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
case LLAssetType::AT_BODYPART:
case LLAssetType::AT_GESTURE:
{
- llinfos << "copying inventory item " << item->getName() << llendl;
+ LL_DEBUGS("Avatar") << "copying inventory item " << item->getName() << LL_ENDL;
copy_inventory_item(gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
@@ -1192,6 +1681,10 @@ void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LL
break;
}
}
+ if (!link_array.empty())
+ {
+ link_inventory_array(dst_id, link_array, cb);
+ }
}
BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id)
@@ -1256,15 +1749,13 @@ bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id)
// static
bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id)
{
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
+ if (gAgentWearables.isCOFChangeInProgress())
+ {
+ return false;
+ }
+
LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false);
- gInventory.collectDescendentsIf(outfit_cat_id,
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- is_worn);
- return items.size() > 0;
+ return gInventory.hasMatchingDirectDescendent(outfit_cat_id, is_worn);
}
// static
@@ -1283,6 +1774,7 @@ bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id)
items,
LLInventoryModel::EXCLUDE_TRASH,
not_worn);
+
return items.size() > 0;
}
@@ -1309,45 +1801,68 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
items,
LLInventoryModel::EXCLUDE_TRASH,
is_worn);
+
return items.size() > 0;
}
-void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
+// Moved from LLWearableList::ContextMenu for wider utility.
+bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids)
{
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(category, cats, items,
- LLInventoryModel::EXCLUDE_TRASH);
- for (S32 i = 0; i < items.count(); ++i)
+ // TODO: investigate wearables may not be loaded at this point EXT-8231
+
+ U32 n_objects = 0;
+ U32 n_clothes = 0;
+
+ // Count given clothes (by wearable type) and objects.
+ for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
{
- LLViewerInventoryItem *item = items.get(i);
- if (item->getActualType() != LLAssetType::AT_LINK_FOLDER)
- continue;
- if (item->getIsLinkType())
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if (!item)
{
- LLViewerInventoryCategory* catp = item->getLinkedCategory();
- if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
- {
- gInventory.purgeObject(item->getUUID());
- }
+ return false;
+ }
+
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ ++n_objects;
+ }
+ else if (item->getType() == LLAssetType::AT_CLOTHING)
+ {
+ ++n_clothes;
+ }
+ else
+ {
+ LL_WARNS() << "Unexpected wearable type" << LL_ENDL;
+ return false;
}
}
+
+ // Check whether we can add all the objects.
+ if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects))
+ {
+ return false;
+ }
+
+ // Check whether we can add all the clothes.
+ U32 sum_clothes = n_clothes + gAgentWearables.getClothingLayerCount();
+ return sum_clothes <= LLAgentWearables::MAX_CLOTHING_LAYERS;
}
-void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links)
+void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t cats;
LLInventoryModel::item_array_t items;
gInventory.collectDescendents(category, cats, items,
LLInventoryModel::EXCLUDE_TRASH);
- for (S32 i = 0; i < items.count(); ++i)
+ for (S32 i = 0; i < items.size(); ++i)
{
- LLViewerInventoryItem *item = items.get(i);
- if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER))
+ LLViewerInventoryItem *item = items.at(i);
+ if (item->getActualType() != LLAssetType::AT_LINK_FOLDER)
continue;
- if (item->getIsLinkType())
+ LLViewerInventoryCategory* catp = item->getLinkedCategory();
+ if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
{
- gInventory.purgeObject(item->getUUID());
+ remove_inventory_item(item->getUUID(), cb);
}
}
}
@@ -1355,55 +1870,50 @@ void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_lin
// Keep the last N wearables of each type. For viewer 2.0, N is 1 for
// both body parts and clothing items.
void LLAppearanceMgr::filterWearableItems(
- LLInventoryModel::item_array_t& items, S32 max_per_type)
+ LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total)
{
- // Divvy items into arrays by wearable type.
- std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
- divvyWearablesByType(items, items_by_type);
-
- // rebuild items list, retaining the last max_per_type of each array
- items.clear();
- for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
- {
- S32 size = items_by_type[i].size();
- if (size <= 0)
- continue;
- S32 start_index = llmax(0,size-max_per_type);
- for (S32 j = start_index; j<size; j++)
- {
- items.push_back(items_by_type[i][j]);
- }
- }
-}
-
-// Create links to all listed items.
-void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
- LLInventoryModel::item_array_t& items,
- LLPointer<LLInventoryCallback> cb)
-{
- for (S32 i=0; i<items.count(); i++)
- {
- const LLInventoryItem* item = items.get(i).get();
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- cat_uuid,
- item->getName(),
- item->LLInventoryItem::getDescription(),
- LLAssetType::AT_LINK,
- cb);
-
- const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
- const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND";
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl;
-#endif
- }
+ // Restrict by max total items first.
+ if ((max_total > 0) && (items.size() > max_total))
+ {
+ LLInventoryModel::item_array_t items_to_keep;
+ for (S32 i=0; i<max_total; i++)
+ {
+ items_to_keep.push_back(items[i]);
+ }
+ items = items_to_keep;
+ }
+
+ if (max_per_type > 0)
+ {
+ // Divvy items into arrays by wearable type.
+ std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
+ divvyWearablesByType(items, items_by_type);
+
+ // rebuild items list, retaining the last max_per_type of each array
+ items.clear();
+ for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
+ {
+ S32 size = items_by_type[i].size();
+ if (size <= 0)
+ continue;
+ S32 start_index = llmax(0,size-max_per_type);
+ for (S32 j = start_index; j<size; j++)
+ {
+ items.push_back(items_by_type[i][j]);
+ }
+ }
+ }
}
void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
{
LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
- llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
+ if (!pcat)
+ {
+ LL_WARNS() << "no category found for id " << category << LL_ENDL;
+ return;
+ }
+ LL_INFOS("Avatar") << self_av_string() << "starting, cat '" << (pcat ? pcat->getName() : "[UNKNOWN]") << "'" << LL_ENDL;
const LLUUID cof = getCOF();
@@ -1411,10 +1921,10 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
if (!append)
{
LLInventoryModel::item_array_t gest_items;
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
- for(S32 i = 0; i < gest_items.count(); ++i)
+ getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE);
+ for(S32 i = 0; i < gest_items.size(); ++i)
{
- LLViewerInventoryItem *gest_item = gest_items.get(i);
+ LLViewerInventoryItem *gest_item = gest_items.at(i);
if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) )
{
LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() );
@@ -1428,78 +1938,102 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
// required parts are missing.
// Preserve body parts from COF if appending.
LLInventoryModel::item_array_t body_items;
- getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false);
- getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false);
+ getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART);
+ getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART);
if (append)
reverse(body_items.begin(), body_items.end());
// Reduce body items to max of one per type.
removeDuplicateItems(body_items);
- filterWearableItems(body_items, 1);
+ filterWearableItems(body_items, 1, 0);
// - Wearables: include COF contents only if appending.
LLInventoryModel::item_array_t wear_items;
if (append)
- getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false);
- getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false);
+ getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING);
+ getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING);
// Reduce wearables to max of one per type.
removeDuplicateItems(wear_items);
- filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
+ filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS);
// - Attachments: include COF contents only if appending.
LLInventoryModel::item_array_t obj_items;
if (append)
- getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false);
- getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false);
+ getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT);
+ getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT);
removeDuplicateItems(obj_items);
// - Gestures: include COF contents only if appending.
LLInventoryModel::item_array_t gest_items;
if (append)
- getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
- getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false);
+ getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE);
+ getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE);
removeDuplicateItems(gest_items);
- // Remove current COF contents.
- bool keep_outfit_links = append;
- purgeCategory(cof, keep_outfit_links);
- gInventory.notifyObservers();
-
// Create links to new COF contents.
- llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl;
- LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append);
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking body items" << llendl;
-#endif
- linkAll(cof, body_items, link_waiter);
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking wear items" << llendl;
-#endif
- linkAll(cof, wear_items, link_waiter);
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking obj items" << llendl;
-#endif
- linkAll(cof, obj_items, link_waiter);
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- llinfos << "Linking gesture items" << llendl;
-#endif
- linkAll(cof, gest_items, link_waiter);
+ LLInventoryModel::item_array_t all_items;
+ std::copy(body_items.begin(), body_items.end(), std::back_inserter(all_items));
+ std::copy(wear_items.begin(), wear_items.end(), std::back_inserter(all_items));
+ std::copy(obj_items.begin(), obj_items.end(), std::back_inserter(all_items));
+ std::copy(gest_items.begin(), gest_items.end(), std::back_inserter(all_items));
+
+ // Find any wearables that need description set to enforce ordering.
+ desc_map_t desc_map;
+ getWearableOrderingDescUpdates(wear_items, desc_map);
+
+ // Will link all the above items.
+ // link_waiter enforce flags are false because we've already fixed everything up in updateCOF().
+ LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(false,false);
+ LLSD contents = LLSD::emptyArray();
+
+ for (LLInventoryModel::item_array_t::const_iterator it = all_items.begin();
+ it != all_items.end(); ++it)
+ {
+ LLSD item_contents;
+ LLInventoryItem *item = *it;
+
+ std::string desc;
+ desc_map_t::const_iterator desc_iter = desc_map.find(item->getUUID());
+ if (desc_iter != desc_map.end())
+ {
+ desc = desc_iter->second;
+ LL_DEBUGS("Avatar") << item->getName() << " overriding desc to: " << desc
+ << " (was: " << item->getActualDescription() << ")" << LL_ENDL;
+ }
+ else
+ {
+ desc = item->getActualDescription();
+ }
- // Add link to outfit if category is an outfit.
- if (!append)
+ item_contents["name"] = item->getName();
+ item_contents["desc"] = desc;
+ item_contents["linked_id"] = item->getLinkedUUID();
+ item_contents["type"] = LLAssetType::AT_LINK;
+ contents.append(item_contents);
+ }
+ const LLUUID& base_id = append ? getBaseOutfitUUID() : category;
+ LLViewerInventoryCategory *base_cat = gInventory.getCategory(base_id);
+ if (base_cat)
+ {
+ LLSD base_contents;
+ base_contents["name"] = base_cat->getName();
+ base_contents["desc"] = "";
+ base_contents["linked_id"] = base_cat->getLinkedUUID();
+ base_contents["type"] = LLAssetType::AT_LINK_FOLDER;
+ contents.append(base_contents);
+ }
+ if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
{
- createBaseOutfitLink(category, link_waiter);
+ dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents);
}
- llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl;
+ slam_inventory_folder(getCOF(), contents, link_waiter);
+
+ LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL;
}
void LLAppearanceMgr::updatePanelOutfitName(const std::string& name)
{
LLSidepanelAppearance* panel_appearance =
- dynamic_cast<LLSidepanelAppearance *>(LLSideTray::getInstance()->getPanel("sidepanel_appearance"));
+ dynamic_cast<LLSidepanelAppearance *>(LLFloaterSidePanelContainer::getPanel("appearance"));
if (panel_appearance)
{
panel_appearance->refreshCurrentOutfitName(name);
@@ -1512,23 +2046,23 @@ void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLI
LLViewerInventoryCategory* catp = gInventory.getCategory(category);
std::string new_outfit_name = "";
- purgeBaseOutfitLink(cof);
+ purgeBaseOutfitLink(cof, link_waiter);
if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
{
- link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "",
- LLAssetType::AT_LINK_FOLDER, link_waiter);
+ link_inventory_object(cof, catp, link_waiter);
new_outfit_name = catp->getName();
}
updatePanelOutfitName(new_outfit_name);
}
-void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append)
+void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder)
{
- lldebugs << "updateAgentWearables()" << llendl;
+ LL_DEBUGS("Avatar") << "updateAgentWearables()" << LL_ENDL;
LLInventoryItem::item_array_t items;
- LLDynamicArray< LLWearable* > wearables;
+ std::vector< LLViewerWearable* > wearables;
+ wearables.reserve(32);
// For each wearable type, find the wearables of that type.
for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ )
@@ -1537,25 +2071,28 @@ void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, boo
iter != holder->getFoundList().end(); ++iter)
{
LLFoundData& data = *iter;
- LLWearable* wearable = data.mWearable;
+ LLViewerWearable* wearable = data.mWearable;
if( wearable && ((S32)wearable->getType() == i) )
{
LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID);
if( item && (item->getAssetUUID() == wearable->getAssetID()) )
{
- items.put(item);
- wearables.put(wearable);
+ items.push_back(item);
+ wearables.push_back(wearable);
}
}
}
}
- if(wearables.count() > 0)
+ if(wearables.size() > 0)
{
- gAgentWearables.setWearableOutfit(items, wearables, !append);
+ gAgentWearables.setWearableOutfit(items, wearables);
}
+}
-// dec_busy_count();
+S32 LLAppearanceMgr::countActiveHoldingPatterns()
+{
+ return LLWearableHoldingPattern::countActive();
}
static void remove_non_link_items(LLInventoryModel::item_array_t &items)
@@ -1575,15 +2112,15 @@ static void remove_non_link_items(LLInventoryModel::item_array_t &items)
}
//a predicate for sorting inventory items by actual descriptions
-bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
+bool sort_by_actual_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
{
if (!item1 || !item2)
{
- llwarning("either item1 or item2 is NULL", 0);
+ LL_WARNS() << "either item1 or item2 is NULL" << LL_ENDL;
return true;
}
- return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription();
+ return item1->getActualDescription() < item2->getActualDescription();
}
void item_array_diff(LLInventoryModel::item_array_t& full_list,
@@ -1596,7 +2133,7 @@ void item_array_diff(LLInventoryModel::item_array_t& full_list,
++it)
{
LLViewerInventoryItem *item = *it;
- if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()?
+ if (std::find(keep_list.begin(), keep_list.end(), item) == keep_list.end())
{
kill_list.push_back(item);
}
@@ -1605,18 +2142,19 @@ void item_array_diff(LLInventoryModel::item_array_t& full_list,
S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
LLAssetType::EType type,
- S32 max_items,
- LLInventoryModel::item_array_t& items_to_kill)
+ S32 max_items_per_type,
+ S32 max_items_total,
+ LLInventoryObject::object_list_t& items_to_kill)
{
S32 to_kill_count = 0;
LLInventoryModel::item_array_t items;
- getDescendentsOfAssetType(cat_id, items, type, false);
+ getDescendentsOfAssetType(cat_id, items, type);
LLInventoryModel::item_array_t curr_items = items;
removeDuplicateItems(items);
- if (max_items > 0)
+ if (max_items_per_type > 0 || max_items_total > 0)
{
- filterWearableItems(items, max_items);
+ filterWearableItems(items, max_items_per_type, max_items_total);
}
LLInventoryModel::item_array_t kill_items;
item_array_diff(curr_items,items,kill_items);
@@ -1624,74 +2162,101 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
it != kill_items.end();
++it)
{
- items_to_kill.push_back(*it);
+ items_to_kill.push_back(LLPointer<LLInventoryObject>(*it));
to_kill_count++;
}
return to_kill_count;
}
-
-void LLAppearanceMgr::enforceItemRestrictions()
-{
- S32 purge_count = 0;
- LLInventoryModel::item_array_t items_to_kill;
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART,
- 1, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING,
- LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
- purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT,
- -1, items_to_kill);
+void LLAppearanceMgr::findAllExcessOrDuplicateItems(const LLUUID& cat_id,
+ LLInventoryObject::object_list_t& items_to_kill)
+{
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_BODYPART,
+ 1, 0, items_to_kill);
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_CLOTHING,
+ 0, LLAgentWearables::MAX_CLOTHING_LAYERS, items_to_kill);
+ findExcessOrDuplicateItems(cat_id,LLAssetType::AT_OBJECT,
+ 0, 0, items_to_kill);
+}
+void LLAppearanceMgr::enforceCOFItemRestrictions(LLPointer<LLInventoryCallback> cb)
+{
+ LLInventoryObject::object_list_t items_to_kill;
+ findAllExcessOrDuplicateItems(getCOF(), items_to_kill);
if (items_to_kill.size()>0)
{
- for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
- it != items_to_kill.end();
- ++it)
- {
- LLViewerInventoryItem *item = *it;
- llinfos << "purging duplicate or excess item " << item->getName() << llendl;
- gInventory.purgeObject(item->getUUID());
- }
- gInventory.notifyObservers();
+ // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
+ // this should catch anything that gets through.
+ remove_inventory_items(items_to_kill, cb);
}
}
-void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
+void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
+ bool enforce_ordering,
+ nullary_func_t post_update_func)
{
if (mIsInUpdateAppearanceFromCOF)
{
- llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl;
+ LL_WARNS() << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << LL_ENDL;
return;
}
- BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
+ LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL;
- llinfos << "starting" << llendl;
+ if (enforce_item_restrictions)
+ {
+ // The point here is just to call
+ // updateAppearanceFromCOF() again after excess items
+ // have been removed. That time we will set
+ // enforce_item_restrictions to false so we don't get
+ // caught in a perpetual loop.
+ LLPointer<LLInventoryCallback> cb(
+ new LLUpdateAppearanceOnDestroy(false, enforce_ordering, post_update_func));
+ enforceCOFItemRestrictions(cb);
+ return;
+ }
- //checking integrity of the COF in terms of ordering of wearables,
- //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
- updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering);
+ if (enforce_ordering)
+ {
+ //checking integrity of the COF in terms of ordering of wearables,
+ //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
+
+ // As with enforce_item_restrictions handling above, we want
+ // to wait for the update callbacks, then (finally!) call
+ // updateAppearanceFromCOF() with no additional COF munging needed.
+ LLPointer<LLInventoryCallback> cb(
+ new LLUpdateAppearanceOnDestroy(false, false, post_update_func));
+ updateClothingOrderingInfo(LLUUID::null, cb);
+ return;
+ }
+
+ if (!validateClothingOrderingInfo())
+ {
+ LL_WARNS() << "Clothing ordering error" << LL_ENDL;
+ }
+
+ BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
+ selfStartPhase("update_appearance_from_cof");
- // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
- // this should catch anything that gets through.
- enforceItemRestrictions();
-
// update dirty flag to see if the state of the COF matches
// the saved outfit stored as a folder link
updateIsDirty();
- //dumpCat(getCOF(),"COF, start");
+ // Send server request for appearance update
+ if (gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion())
+ {
+ requestServerAppearanceUpdate();
+ }
- bool follow_folder_links = true;
LLUUID current_outfit_id = getCOF();
// Find all the wearables that are in the COF's subtree.
- lldebugs << "LLAppearanceMgr::updateFromCOF()" << llendl;
+ LL_DEBUGS() << "LLAppearanceMgr::updateFromCOF()" << LL_ENDL;
LLInventoryModel::item_array_t wear_items;
LLInventoryModel::item_array_t obj_items;
LLInventoryModel::item_array_t gest_items;
- getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links);
+ getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items);
// Get rid of non-links in case somehow the COF was corrupted.
remove_non_link_items(wear_items);
remove_non_link_items(obj_items);
@@ -1700,7 +2265,14 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
dumpItemArray(wear_items,"asset_dump: wear_item");
dumpItemArray(obj_items,"asset_dump: obj_item");
- if(!wear_items.count())
+ LLViewerInventoryCategory *cof = gInventory.getCategory(current_outfit_id);
+ if (!gInventory.isCategoryComplete(current_outfit_id))
+ {
+ LL_WARNS() << "COF info is not complete. Version " << cof->getVersion()
+ << " descendent_count " << cof->getDescendentCount()
+ << " viewer desc count " << cof->getViewerDescendentCount() << LL_ENDL;
+ }
+ if(!wear_items.size())
{
LLNotificationsUtil::add("CouldNotPutOnOutfit");
return;
@@ -1710,6 +2282,8 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
sortItemsByActualDescription(wear_items);
+ LL_DEBUGS("Avatar") << "HP block starts" << LL_ENDL;
+ LLTimer hp_block_timer;
LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
holder->setObjItems(obj_items);
@@ -1720,9 +2294,9 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
// callback will be called (and this object deleted)
// before the final getNextData().
- for(S32 i = 0; i < wear_items.count(); ++i)
+ for(S32 i = 0; i < wear_items.size(); ++i)
{
- LLViewerInventoryItem *item = wear_items.get(i);
+ LLViewerInventoryItem *item = wear_items.at(i);
LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
// Fault injection: use debug setting to test asset
@@ -1750,25 +2324,28 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
{
if (!item)
{
- llwarns << "Attempt to wear a null item " << llendl;
+ LL_WARNS() << "Attempt to wear a null item " << LL_ENDL;
}
else if (!linked_item)
{
- llwarns << "Attempt to wear a broken link [ name:" << item->getName() << " ] " << llendl;
+ LL_WARNS() << "Attempt to wear a broken link [ name:" << item->getName() << " ] " << LL_ENDL;
}
}
}
+ selfStartPhase("get_wearables_2");
+
for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin();
it != holder->getFoundList().end(); ++it)
{
LLFoundData& found = *it;
- lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
+ LL_DEBUGS() << self_av_string() << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << LL_ENDL;
// Fetch the wearables about to be worn.
LLWearableList::instance().getAsset(found.mAssetID,
found.mName,
+ gAgentAvatarp,
found.mAssetType,
onWearableAssetFetch,
(void*)holder);
@@ -1780,12 +2357,14 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
{
doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder));
}
+ post_update_func();
+
+ LL_DEBUGS("Avatar") << "HP block ends, elapsed " << hp_block_timer.getElapsedTimeF32() << LL_ENDL;
}
void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category,
LLInventoryModel::item_array_t& items,
- LLAssetType::EType type,
- bool follow_folder_links)
+ LLAssetType::EType type)
{
LLInventoryModel::cat_array_t cats;
LLIsType is_of_type(type);
@@ -1793,15 +2372,13 @@ void LLAppearanceMgr::getDescendentsOfAssetType(const LLUUID& category,
cats,
items,
LLInventoryModel::EXCLUDE_TRASH,
- is_of_type,
- follow_folder_links);
+ is_of_type);
}
void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
LLInventoryModel::item_array_t& wear_items,
LLInventoryModel::item_array_t& obj_items,
- LLInventoryModel::item_array_t& gest_items,
- bool follow_folder_links)
+ LLInventoryModel::item_array_t& gest_items)
{
LLInventoryModel::cat_array_t wear_cats;
LLFindWearables is_wearable;
@@ -1809,8 +2386,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
wear_cats,
wear_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_wearable,
- follow_folder_links);
+ is_wearable);
LLInventoryModel::cat_array_t obj_cats;
LLIsType is_object( LLAssetType::AT_OBJECT );
@@ -1818,8 +2394,7 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
obj_cats,
obj_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_object,
- follow_folder_links);
+ is_object);
// Find all gestures in this folder
LLInventoryModel::cat_array_t gest_cats;
@@ -1828,27 +2403,58 @@ void LLAppearanceMgr::getUserDescendents(const LLUUID& category,
gest_cats,
gest_items,
LLInventoryModel::EXCLUDE_TRASH,
- is_gesture,
- follow_folder_links);
+ is_gesture);
}
void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append)
{
if(!category) return;
+ selfClearPhases();
+ selfStartPhase("wear_inventory_category");
+
gAgentWearables.notifyLoadingStarted();
- llinfos << "wearInventoryCategory( " << category->getName()
- << " )" << llendl;
+ LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
+ << " )" << LL_ENDL;
- callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
- &LLAppearanceMgr::instance(),
- category->getUUID(), copy, append));
+ // If we are copying from library, attempt to use AIS to copy the category.
+ bool ais_ran=false;
+ if (copy && AISCommand::isAPIAvailable())
+ {
+ LLUUID parent_id;
+ parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
+ if (parent_id.isNull())
+ {
+ parent_id = gInventory.getRootFolderID();
+ }
+
+ LLPointer<LLInventoryCallback> copy_cb = new LLWearCategoryAfterCopy(append);
+ LLPointer<LLInventoryCallback> track_cb = new LLTrackPhaseWrapper(
+ std::string("wear_inventory_category_callback"), copy_cb);
+ LLPointer<AISCommand> cmd_ptr = new CopyLibraryCategoryCommand(category->getUUID(), parent_id, track_cb);
+ ais_ran=cmd_ptr->run_command();
+ }
+
+ if (!ais_ran)
+ {
+ selfStartPhase("wear_inventory_category_fetch");
+ callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
+ &LLAppearanceMgr::instance(),
+ category->getUUID(), copy, append));
+ }
+}
+
+S32 LLAppearanceMgr::getActiveCopyOperations() const
+{
+ return LLCallAfterInventoryCopyMgr::getInstanceCount();
}
void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
{
- llinfos << "starting" << llendl;
+ LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+
+ selfStopPhase("wear_inventory_category_fetch");
// We now have an outfit ready to be copied to agent inventory. Do
// it, and wear that outfit normally.
@@ -1897,22 +2503,15 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap
pid,
LLFolderType::FT_NONE,
name);
- LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(new_cat_id, append);
- it = items->begin();
- for(; it < end; ++it)
- {
- item = *it;
- if(item)
- {
- copy_inventory_item(
- gAgent.getID(),
- item->getPermissions().getOwner(),
- item->getUUID(),
- new_cat_id,
- std::string(),
- cb);
- }
- }
+
+ // Create a CopyMgr that will copy items, manage its own destruction
+ new LLCallAfterInventoryCopyMgr(
+ *items, new_cat_id, std::string("wear_inventory_category_callback"),
+ boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar,
+ LLAppearanceMgr::getInstance(),
+ gInventory.getCategory(new_cat_id),
+ append));
+
// BAP fixes a lag in display of created dir.
gInventory.notifyObservers();
}
@@ -1929,24 +2528,30 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
// Avoid unintentionally overwriting old wearables. We have to do
// this up front to avoid having to deal with the case of multiple
// wearables being dirty.
- if(!category) return;
+ if (!category) return;
+
+ if ( !LLInventoryCallbackManager::is_instantiated() )
+ {
+ // shutting down, ignore.
+ return;
+ }
- llinfos << "wearInventoryCategoryOnAvatar( " << category->getName()
- << " )" << llendl;
+ LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName()
+ << "'" << LL_ENDL;
if (gAgentCamera.cameraCustomizeAvatar())
{
// switching to outfit editor should automagically save any currently edited wearable
- LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit"));
+ LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit"));
}
LLAppearanceMgr::changeOutfit(TRUE, category->getUUID(), append);
}
+// FIXME do we really want to search entire inventory for matching name?
void LLAppearanceMgr::wearOutfitByName(const std::string& name)
{
- llinfos << "Wearing category " << name << llendl;
- //inc_busy_count();
+ LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
@@ -1958,10 +2563,10 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name)
has_name);
bool copy_items = false;
LLInventoryCategory* cat = NULL;
- if (cat_array.count() > 0)
+ if (cat_array.size() > 0)
{
// Just wear the first one that matches
- cat = cat_array.get(0);
+ cat = cat_array.at(0);
}
else
{
@@ -1970,9 +2575,9 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name)
item_array,
LLInventoryModel::EXCLUDE_TRASH,
has_name);
- if(cat_array.count() > 0)
+ if(cat_array.size() > 0)
{
- cat = cat_array.get(0);
+ cat = cat_array.at(0);
copy_items = true;
}
}
@@ -1983,11 +2588,9 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name)
}
else
{
- llwarns << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
- << llendl;
+ LL_WARNS() << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
+ << LL_ENDL;
}
-
- //dec_busy_count();
}
bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventoryItem *b)
@@ -1999,10 +2602,10 @@ bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventor
class LLDeferredCOFLinkObserver: public LLInventoryObserver
{
public:
- LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL):
+ LLDeferredCOFLinkObserver(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb, const std::string& description):
mItemID(item_id),
- mDoUpdate(do_update),
- mCallback(cb)
+ mCallback(cb),
+ mDescription(description)
{
}
@@ -2016,40 +2619,44 @@ public:
if (item)
{
gInventory.removeObserver(this);
- LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback);
+ LLAppearanceMgr::instance().addCOFItemLink(item, mCallback, mDescription);
delete this;
}
}
private:
const LLUUID mItemID;
- bool mDoUpdate;
+ std::string mDescription;
LLPointer<LLInventoryCallback> mCallback;
};
// BAP - note that this runs asynchronously if the item is not already loaded from inventory.
// Dangerous if caller assumes link will exist after calling the function.
-void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb)
+void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id,
+ LLPointer<LLInventoryCallback> cb,
+ const std::string description)
{
const LLInventoryItem *item = gInventory.getItem(item_id);
if (!item)
{
- LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb);
+ LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, cb, description);
gInventory.addObserver(observer);
}
else
{
- addCOFItemLink(item, do_update, cb);
+ addCOFItemLink(item, cb, description);
}
}
-void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb)
-{
+void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,
+ LLPointer<LLInventoryCallback> cb,
+ const std::string description)
+{
const LLViewerInventoryItem *vitem = dynamic_cast<const LLViewerInventoryItem*>(item);
if (!vitem)
{
- llwarns << "not an llviewerinventoryitem, failed" << llendl;
+ LL_WARNS() << "not an llviewerinventoryitem, failed" << LL_ENDL;
return;
}
@@ -2062,11 +2669,10 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
item_array,
LLInventoryModel::EXCLUDE_TRASH);
bool linked_already = false;
- U32 count = 0;
- for (S32 i=0; i<item_array.count(); i++)
+ for (S32 i=0; i<item_array.size(); i++)
{
// Are these links to the same object?
- const LLViewerInventoryItem* inv_item = item_array.get(i).get();
+ const LLViewerInventoryItem* inv_item = item_array.at(i).get();
const LLWearableType::EType wearable_type = inv_item->getWearableType();
const bool is_body_part = (wearable_type == LLWearableType::WT_SHAPE)
@@ -2082,62 +2688,115 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
// type? If so, new item will replace old.
else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type))
{
- ++count;
- if (is_body_part && inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type))
+ if (is_body_part && inv_item->getIsLinkType())
{
- gInventory.purgeObject(inv_item->getUUID());
+ remove_inventory_item(inv_item->getUUID(), cb);
}
- else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE)
+ else if (!gAgentWearables.canAddWearable(wearable_type))
{
- // MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE
- gInventory.purgeObject(inv_item->getUUID());
+ // MULTI-WEARABLES: make sure we don't go over clothing limits
+ remove_inventory_item(inv_item->getUUID(), cb);
}
}
}
- if (linked_already)
+ if (!linked_already)
{
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
- return;
+ LLViewerInventoryItem *copy_item = new LLViewerInventoryItem;
+ copy_item->copyViewerItem(vitem);
+ copy_item->setDescription(description);
+ link_inventory_object(getCOF(), copy_item, cb);
}
- else
+}
+
+LLInventoryModel::item_array_t LLAppearanceMgr::findCOFItemLinks(const LLUUID& item_id)
+{
+
+ LLInventoryModel::item_array_t result;
+ const LLViewerInventoryItem *vitem =
+ dynamic_cast<const LLViewerInventoryItem*>(gInventory.getItem(item_id));
+
+ if (vitem)
{
- if(do_update && cb.isNull())
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(LLAppearanceMgr::getCOF(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ for (S32 i=0; i<item_array.size(); i++)
{
- cb = new ModifiedCOFCallback;
+ const LLViewerInventoryItem* inv_item = item_array.at(i).get();
+ if (inv_item->getLinkedUUID() == vitem->getLinkedUUID())
+ {
+ result.push_back(item_array.at(i));
+ }
}
- const std::string description = vitem->getIsLinkType() ? vitem->getDescription() : "";
- link_inventory_item( gAgent.getID(),
- vitem->getLinkedUUID(),
- getCOF(),
- vitem->getName(),
- description,
- LLAssetType::AT_LINK,
- cb);
}
- return;
+ return result;
}
-// BAP remove ensemble code for 2.1?
-void LLAppearanceMgr::addEnsembleLink( LLInventoryCategory* cat, bool do_update )
+bool LLAppearanceMgr::isLinkedInCOF(const LLUUID& item_id)
{
-#if SUPPORT_ENSEMBLES
- // BAP add check for already in COF.
- LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0;
- link_inventory_item( gAgent.getID(),
- cat->getLinkedUUID(),
- getCOF(),
- cat->getName(),
- cat->getDescription(),
- LLAssetType::AT_LINK_FOLDER,
- cb);
-#endif
+ LLInventoryModel::item_array_t links = LLAppearanceMgr::instance().findCOFItemLinks(item_id);
+ return links.size() > 0;
}
-void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
+void LLAppearanceMgr::removeAllClothesFromAvatar()
+{
+ // Fetch worn clothes (i.e. the ones in COF).
+ LLInventoryModel::item_array_t clothing_items;
+ LLInventoryModel::cat_array_t dummy;
+ LLIsType is_clothing(LLAssetType::AT_CLOTHING);
+ gInventory.collectDescendentsIf(getCOF(),
+ dummy,
+ clothing_items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_clothing);
+ uuid_vec_t item_ids;
+ for (LLInventoryModel::item_array_t::iterator it = clothing_items.begin();
+ it != clothing_items.end(); ++it)
+ {
+ item_ids.push_back((*it).get()->getLinkedUUID());
+ }
+
+ // Take them off by removing from COF.
+ removeItemsFromAvatar(item_ids);
+}
+
+void LLAppearanceMgr::removeAllAttachmentsFromAvatar()
+{
+ if (!isAgentAvatarValid()) return;
+
+ LLAgentWearables::llvo_vec_t objects_to_remove;
+
+ for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
+ iter != gAgentAvatarp->mAttachmentPoints.end();)
+ {
+ LLVOAvatar::attachment_map_t::iterator curiter = iter++;
+ LLViewerJointAttachment* attachment = curiter->second;
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ LLViewerObject *attached_object = (*attachment_iter);
+ if (attached_object)
+ {
+ objects_to_remove.push_back(attached_object);
+ }
+ }
+ }
+ uuid_vec_t ids_to_remove;
+ for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_remove.begin();
+ it != objects_to_remove.end();
+ ++it)
+ {
+ ids_to_remove.push_back((*it)->getAttachmentItemID());
+ }
+ removeItemsFromAvatar(ids_to_remove);
+}
+
+void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb)
{
gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
@@ -2147,21 +2806,22 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
- for (S32 i=0; i<item_array.count(); i++)
+ for (S32 i=0; i<item_array.size(); i++)
{
- const LLInventoryItem* item = item_array.get(i).get();
+ const LLInventoryItem* item = item_array.at(i).get();
if (item->getIsLinkType() && item->getLinkedUUID() == item_id)
{
- gInventory.purgeObject(item->getUUID());
+ bool immediate_delete = false;
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ immediate_delete = true;
+ }
+ remove_inventory_item(item->getUUID(), cb, immediate_delete);
}
}
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
}
-void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update)
+void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, LLPointer<LLInventoryCallback> cb)
{
LLFindWearablesOfType filter_wearables_of_type(type);
LLInventoryModel::cat_array_t cats;
@@ -2174,21 +2834,16 @@ void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_u
const LLViewerInventoryItem* item = *it;
if (item->getIsLinkType()) // we must operate on links only
{
- gInventory.purgeObject(item->getUUID());
+ remove_inventory_item(item->getUUID(), cb);
}
}
-
- if (do_update)
- {
- updateAppearanceFromCOF();
- }
}
bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2)
{
if (!item1 || !item2)
{
- llwarning("item1, item2 cannot be null, something is very wrong", 0);
+ LL_WARNS() << "item1, item2 cannot be null, something is very wrong" << LL_ENDL;
return true;
}
@@ -2218,7 +2873,7 @@ void LLAppearanceMgr::updateIsDirty()
if (base_outfit.notNull())
{
- LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK);
+ LLIsValidItemLink collector;
LLInventoryModel::cat_array_t cof_cats;
LLInventoryModel::item_array_t cof_items;
@@ -2230,8 +2885,9 @@ void LLAppearanceMgr::updateIsDirty()
gInventory.collectDescendentsIf(base_outfit, outfit_cats, outfit_items,
LLInventoryModel::EXCLUDE_TRASH, collector);
- if(outfit_items.count() != cof_items.count())
+ if(outfit_items.size() != cof_items.size())
{
+ LL_DEBUGS("Avatar") << "item count different - base " << outfit_items.size() << " cof " << cof_items.size() << LL_ENDL;
// Current outfit folder should have one more item than the outfit folder.
// this one item is the link back to the outfit folder itself.
mOutfitIsDirty = true;
@@ -2244,44 +2900,160 @@ void LLAppearanceMgr::updateIsDirty()
for (U32 i = 0; i < cof_items.size(); ++i)
{
- LLViewerInventoryItem *item1 = cof_items.get(i);
- LLViewerInventoryItem *item2 = outfit_items.get(i);
+ LLViewerInventoryItem *item1 = cof_items.at(i);
+ LLViewerInventoryItem *item2 = outfit_items.at(i);
if (item1->getLinkedUUID() != item2->getLinkedUUID() ||
item1->getName() != item2->getName() ||
- item1->LLInventoryItem::getDescription() != item2->LLInventoryItem::getDescription())
+ item1->getActualDescription() != item2->getActualDescription())
{
+ if (item1->getLinkedUUID() != item2->getLinkedUUID())
+ {
+ LL_DEBUGS("Avatar") << "link id different " << LL_ENDL;
+ }
+ else
+ {
+ if (item1->getName() != item2->getName())
+ {
+ LL_DEBUGS("Avatar") << "name different " << item1->getName() << " " << item2->getName() << LL_ENDL;
+ }
+ if (item1->getActualDescription() != item2->getActualDescription())
+ {
+ LL_DEBUGS("Avatar") << "desc different " << item1->getActualDescription()
+ << " " << item2->getActualDescription()
+ << " names " << item1->getName() << " " << item2->getName() << LL_ENDL;
+ }
+ }
mOutfitIsDirty = true;
return;
}
}
}
+ llassert(!mOutfitIsDirty);
+ LL_DEBUGS("Avatar") << "clean" << LL_ENDL;
}
-void LLAppearanceMgr::autopopulateOutfits()
+// *HACK: Must match name in Library or agent inventory
+const std::string ROOT_GESTURES_FOLDER = "Gestures";
+const std::string COMMON_GESTURES_FOLDER = "Common Gestures";
+const std::string MALE_GESTURES_FOLDER = "Male Gestures";
+const std::string FEMALE_GESTURES_FOLDER = "Female Gestures";
+const std::string SPEECH_GESTURES_FOLDER = "Speech Gestures";
+const std::string OTHER_GESTURES_FOLDER = "Other Gestures";
+
+void LLAppearanceMgr::copyLibraryGestures()
{
- // If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account)
- // then auto-populate outfits from the library into the My Outfits folder.
+ LL_INFOS("Avatar") << self_av_string() << "Copying library gestures" << LL_ENDL;
+
+ // Copy gestures
+ LLUUID lib_gesture_cat_id =
+ gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false);
+ if (lib_gesture_cat_id.isNull())
+ {
+ LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL;
+ }
+ LLUUID dst_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE);
- llinfos << "avatar fully visible" << llendl;
+ std::vector<std::string> gesture_folders_to_copy;
+ gesture_folders_to_copy.push_back(MALE_GESTURES_FOLDER);
+ gesture_folders_to_copy.push_back(FEMALE_GESTURES_FOLDER);
+ gesture_folders_to_copy.push_back(COMMON_GESTURES_FOLDER);
+ gesture_folders_to_copy.push_back(SPEECH_GESTURES_FOLDER);
+ gesture_folders_to_copy.push_back(OTHER_GESTURES_FOLDER);
- static bool check_populate_my_outfits = true;
- if (check_populate_my_outfits &&
- (LLInventoryModel::getIsFirstTimeInViewer2()
- || gSavedSettings.getBOOL("MyOutfitsAutofill")))
+ for(std::vector<std::string>::iterator it = gesture_folders_to_copy.begin();
+ it != gesture_folders_to_copy.end();
+ ++it)
{
- gAgentWearables.populateMyOutfitsFolder();
+ std::string& folder_name = *it;
+
+ LLPointer<LLInventoryCallback> cb(NULL);
+
+ // After copying gestures, activate Common, Other, plus
+ // Male and/or Female, depending upon the initial outfit gender.
+ ESex gender = gAgentAvatarp->getSex();
+
+ std::string activate_male_gestures;
+ std::string activate_female_gestures;
+ switch (gender) {
+ case SEX_MALE:
+ activate_male_gestures = MALE_GESTURES_FOLDER;
+ break;
+ case SEX_FEMALE:
+ activate_female_gestures = FEMALE_GESTURES_FOLDER;
+ break;
+ case SEX_BOTH:
+ activate_male_gestures = MALE_GESTURES_FOLDER;
+ activate_female_gestures = FEMALE_GESTURES_FOLDER;
+ break;
+ }
+
+ if (folder_name == activate_male_gestures ||
+ folder_name == activate_female_gestures ||
+ folder_name == COMMON_GESTURES_FOLDER ||
+ folder_name == OTHER_GESTURES_FOLDER)
+ {
+ cb = new LLBoostFuncInventoryCallback(activate_gesture_cb);
+ }
+
+ LLUUID cat_id = findDescendentCategoryIDByName(lib_gesture_cat_id,folder_name);
+ if (cat_id.isNull())
+ {
+ LL_WARNS() << self_av_string() << "failed to find gesture folder for " << folder_name << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("Avatar") << self_av_string() << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << LL_ENDL;
+ callAfterCategoryFetch(cat_id,
+ boost::bind(&LLAppearanceMgr::shallowCopyCategory,
+ &LLAppearanceMgr::instance(),
+ cat_id, dst_id, cb));
+ }
}
- check_populate_my_outfits = false;
}
// Handler for anything that's deferred until avatar de-clouds.
void LLAppearanceMgr::onFirstFullyVisible()
{
+ gAgentAvatarp->outputRezTiming("Avatar fully loaded");
+ gAgentAvatarp->reportAvatarRezTime();
gAgentAvatarp->debugAvatarVisible();
- autopopulateOutfits();
+
+ // If this is the first time we've ever logged in,
+ // then copy default gestures from the library.
+ if (gAgent.isFirstLogin()) {
+ copyLibraryGestures();
+ }
}
+// update "dirty" state - defined outside class to allow for calling
+// after appearance mgr instance has been destroyed.
+void appearance_mgr_update_dirty_state()
+{
+ if (LLAppearanceMgr::instanceExists())
+ {
+ LLAppearanceMgr::getInstance()->updateIsDirty();
+ LLAppearanceMgr::getInstance()->setOutfitLocked(false);
+ gAgentWearables.notifyLoadingFinished();
+ }
+}
+
+void update_base_outfit_after_ordering()
+{
+ LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance();
+
+ LLPointer<LLInventoryCallback> dirty_state_updater =
+ new LLBoostFuncInventoryCallback(no_op_inventory_func, appearance_mgr_update_dirty_state);
+
+ //COF contains only links so we copy to the Base Outfit only links
+ const LLUUID base_outfit_id = app_mgr.getBaseOutfitUUID();
+ bool copy_folder_links = false;
+ app_mgr.slamCategoryLinks(app_mgr.getCOF(), base_outfit_id, copy_folder_links, dirty_state_updater);
+
+}
+
+// Save COF changes - update the contents of the current base outfit
+// to match the current COF. Fails if no current base outfit is set.
bool LLAppearanceMgr::updateBaseOutfit()
{
if (isOutfitLocked())
@@ -2290,23 +3062,20 @@ bool LLAppearanceMgr::updateBaseOutfit()
llassert(!isOutfitLocked());
return false;
}
+
setOutfitLocked(true);
gAgentWearables.notifyLoadingStarted();
const LLUUID base_outfit_id = getBaseOutfitUUID();
if (base_outfit_id.isNull()) return false;
+ LL_DEBUGS("Avatar") << "saving cof to base outfit " << base_outfit_id << LL_ENDL;
- updateClothingOrderingInfo();
-
- // in a Base Outfit we do not remove items, only links
- purgeCategory(base_outfit_id, false);
-
-
- LLPointer<LLInventoryCallback> dirty_state_updater = new LLUpdateDirtyState();
-
- //COF contains only links so we copy to the Base Outfit only links
- shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater);
+ LLPointer<LLInventoryCallback> cb =
+ new LLBoostFuncInventoryCallback(no_op_inventory_func, update_base_outfit_after_ordering);
+ // Really shouldn't be needed unless there's a race condition -
+ // updateAppearanceFromCOF() already calls updateClothingOrderingInfo.
+ updateClothingOrderingInfo(LLUUID::null, cb);
return true;
}
@@ -2316,12 +3085,12 @@ void LLAppearanceMgr::divvyWearablesByType(const LLInventoryModel::item_array_t&
items_by_type.resize(LLWearableType::WT_COUNT);
if (items.empty()) return;
- for (S32 i=0; i<items.count(); i++)
+ for (S32 i=0; i<items.size(); i++)
{
- LLViewerInventoryItem *item = items.get(i);
+ LLViewerInventoryItem *item = items.at(i);
if (!item)
{
- LL_WARNS("Appearance") << "NULL item found" << llendl;
+ LL_WARNS("Appearance") << "NULL item found" << LL_ENDL;
continue;
}
// Ignore non-wearables.
@@ -2354,14 +3123,8 @@ struct WearablesOrderComparator
bool operator()(const LLInventoryItem* item1, const LLInventoryItem* item2)
{
- if (!item1 || !item2)
- {
- llwarning("either item1 or item2 is NULL", 0);
- return true;
- }
-
- const std::string& desc1 = item1->LLInventoryItem::getDescription();
- const std::string& desc2 = item2->LLInventoryItem::getDescription();
+ const std::string& desc1 = item1->getActualDescription();
+ const std::string& desc2 = item2->getActualDescription();
bool item1_valid = (desc1.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc1[0]);
bool item2_valid = (desc2.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc2[0]);
@@ -2373,134 +3136,630 @@ struct WearablesOrderComparator
//items with ordering information but not for the associated wearables type
if (!item1_valid && item2_valid)
return false;
+ else if (item1_valid && !item2_valid)
+ return true;
- return true;
+ return item1->getName() < item2->getName();
}
U32 mControlSize;
};
-void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base_outfit_ordering)
+void LLAppearanceMgr::getWearableOrderingDescUpdates(LLInventoryModel::item_array_t& wear_items,
+ desc_map_t& desc_map)
{
- if (cat_id.isNull())
- {
- cat_id = getCOF();
- if (update_base_outfit_ordering)
- {
- const LLUUID base_outfit_id = getBaseOutfitUUID();
- if (base_outfit_id.notNull())
- {
- updateClothingOrderingInfo(base_outfit_id,false);
- }
- }
- }
-
- // COF is processed if cat_id is not specified
- LLInventoryModel::item_array_t wear_items;
- getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING, false);
-
wearables_by_type_t items_by_type(LLWearableType::WT_COUNT);
divvyWearablesByType(wear_items, items_by_type);
- bool inventory_changed = false;
for (U32 type = LLWearableType::WT_SHIRT; type < LLWearableType::WT_COUNT; type++)
{
-
U32 size = items_by_type[type].size();
if (!size) continue;
-
+
//sinking down invalid items which need reordering
std::sort(items_by_type[type].begin(), items_by_type[type].end(), WearablesOrderComparator((LLWearableType::EType) type));
-
+
//requesting updates only for those links which don't have "valid" descriptions
for (U32 i = 0; i < size; i++)
{
LLViewerInventoryItem* item = items_by_type[type][i];
if (!item) continue;
-
+
std::string new_order_str = build_order_string((LLWearableType::EType)type, i);
- if (new_order_str == item->LLInventoryItem::getDescription()) continue;
-
- item->setDescription(new_order_str);
- item->setComplete(TRUE);
- item->updateServer(FALSE);
- gInventory.updateItem(item);
+ if (new_order_str == item->getActualDescription()) continue;
- inventory_changed = true;
+ desc_map[item->getUUID()] = new_order_str;
}
}
+}
- //*TODO do we really need to notify observers?
- if (inventory_changed) gInventory.notifyObservers();
+bool LLAppearanceMgr::validateClothingOrderingInfo(LLUUID cat_id)
+{
+ // COF is processed if cat_id is not specified
+ if (cat_id.isNull())
+ {
+ cat_id = getCOF();
+ }
+
+ LLInventoryModel::item_array_t wear_items;
+ getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING);
+
+ // Identify items for which desc needs to change.
+ desc_map_t desc_map;
+ getWearableOrderingDescUpdates(wear_items, desc_map);
+
+ for (desc_map_t::const_iterator it = desc_map.begin();
+ it != desc_map.end(); ++it)
+ {
+ const LLUUID& item_id = it->first;
+ const std::string& new_order_str = it->second;
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_WARNS() << "Order validation fails: " << item->getName()
+ << " needs to update desc to: " << new_order_str
+ << " (from: " << item->getActualDescription() << ")" << LL_ENDL;
+ }
+
+ return desc_map.size() == 0;
}
+void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id,
+ LLPointer<LLInventoryCallback> cb)
+{
+ // COF is processed if cat_id is not specified
+ if (cat_id.isNull())
+ {
+ cat_id = getCOF();
+ }
+ LLInventoryModel::item_array_t wear_items;
+ getDescendentsOfAssetType(cat_id, wear_items, LLAssetType::AT_CLOTHING);
+
+ // Identify items for which desc needs to change.
+ desc_map_t desc_map;
+ getWearableOrderingDescUpdates(wear_items, desc_map);
+ for (desc_map_t::const_iterator it = desc_map.begin();
+ it != desc_map.end(); ++it)
+ {
+ LLSD updates;
+ const LLUUID& item_id = it->first;
+ const std::string& new_order_str = it->second;
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ LL_DEBUGS("Avatar") << item->getName() << " updating desc to: " << new_order_str
+ << " (was: " << item->getActualDescription() << ")" << LL_ENDL;
+ updates["desc"] = new_order_str;
+ update_inventory_item(item_id,updates,cb);
+ }
+
+}
-class LLShowCreatedOutfit: public LLInventoryCallback
+class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
{
+ LOG_CLASS(RequestAgentUpdateAppearanceResponder);
+
+ friend class LLAppearanceMgr;
+
public:
- LLShowCreatedOutfit(LLUUID& folder_id, bool show_panel = true): mFolderID(folder_id), mShowPanel(show_panel)
- {}
+ RequestAgentUpdateAppearanceResponder();
+
+ virtual ~RequestAgentUpdateAppearanceResponder();
+
+private:
+ // Called when sendServerAppearanceUpdate called. May or may not
+ // trigger a request depending on various bits of state.
+ void onRequestRequested();
+
+ // Post the actual appearance request to cap.
+ void sendRequest();
+
+ void debugCOF(const LLSD& content);
+
+protected:
+ // Successful completion.
+ /* virtual */ void httpSuccess();
+
+ // Error
+ /*virtual*/ void httpFailure();
+
+ void onFailure();
+ void onSuccess();
- virtual ~LLShowCreatedOutfit()
+ S32 mInFlightCounter;
+ LLTimer mInFlightTimer;
+ LLPointer<LLHTTPRetryPolicy> mRetryPolicy;
+};
+
+RequestAgentUpdateAppearanceResponder::RequestAgentUpdateAppearanceResponder()
+{
+ bool retry_on_4xx = true;
+ mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 32.0, 2.0, 10, retry_on_4xx);
+ mInFlightCounter = 0;
+}
+
+RequestAgentUpdateAppearanceResponder::~RequestAgentUpdateAppearanceResponder()
+{
+}
+
+void RequestAgentUpdateAppearanceResponder::onRequestRequested()
+{
+ // If we have already received an update for this or higher cof version, ignore.
+ S32 cof_version = LLAppearanceMgr::instance().getCOFVersion();
+ S32 last_rcv = gAgentAvatarp->mLastUpdateReceivedCOFVersion;
+ S32 last_req = gAgentAvatarp->mLastUpdateRequestCOFVersion;
+ LL_DEBUGS("Avatar") << "cof_version " << cof_version
+ << " last_rcv " << last_rcv
+ << " last_req " << last_req
+ << " in flight " << mInFlightCounter << LL_ENDL;
+ if ((mInFlightCounter>0) && (mInFlightTimer.hasExpired()))
+ {
+ LL_WARNS("Avatar") << "in flight timer expired, resetting " << LL_ENDL;
+ mInFlightCounter = 0;
+ }
+ if (cof_version < last_rcv)
+ {
+ LL_DEBUGS("Avatar") << "Have already received update for cof version " << last_rcv
+ << " will not request for " << cof_version << LL_ENDL;
+ return;
+ }
+ if (mInFlightCounter>0 && last_req >= cof_version)
+ {
+ LL_DEBUGS("Avatar") << "Request already in flight for cof version " << last_req
+ << " will not request for " << cof_version << LL_ENDL;
+ return;
+ }
+
+ // Actually send the request.
+ LL_DEBUGS("Avatar") << "Will send request for cof_version " << cof_version << LL_ENDL;
+ mRetryPolicy->reset();
+ sendRequest();
+}
+
+void RequestAgentUpdateAppearanceResponder::sendRequest()
+{
+ if (gAgentAvatarp->isEditingAppearance())
+ {
+ // don't send out appearance updates if in appearance editing mode
+ return;
+ }
+
+ if (!gAgent.getRegion())
+ {
+ LL_WARNS() << "Region not set, cannot request server appearance update" << LL_ENDL;
+ return;
+ }
+ if (gAgent.getRegion()->getCentralBakeVersion()==0)
+ {
+ LL_WARNS() << "Region does not support baking" << LL_ENDL;
+ }
+ std::string url = gAgent.getRegion()->getCapability("UpdateAvatarAppearance");
+ if (url.empty())
{
- if (!LLApp::isRunning())
+ LL_WARNS() << "No cap for UpdateAvatarAppearance." << LL_ENDL;
+ return;
+ }
+
+ LLSD body;
+ S32 cof_version = LLAppearanceMgr::instance().getCOFVersion();
+ if (gSavedSettings.getBOOL("DebugAvatarExperimentalServerAppearanceUpdate"))
+ {
+ body = LLAppearanceMgr::instance().dumpCOF();
+ }
+ else
+ {
+ body["cof_version"] = cof_version;
+ if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure"))
{
- llwarns << "called during shutdown, skipping" << llendl;
- return;
+ body["cof_version"] = cof_version+999;
}
+ }
+ LL_DEBUGS("Avatar") << "request url " << url << " my_cof_version " << cof_version << LL_ENDL;
- LLSD key;
-
- //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process
- // add may be processed after login process is finished
- if (mShowPanel)
+ mInFlightCounter++;
+ mInFlightTimer.setTimerExpirySec(60.0);
+ LLHTTPClient::post(url, body, this);
+ llassert(cof_version >= gAgentAvatarp->mLastUpdateRequestCOFVersion);
+ gAgentAvatarp->mLastUpdateRequestCOFVersion = cof_version;
+}
+
+void RequestAgentUpdateAppearanceResponder::debugCOF(const LLSD& content)
+{
+ LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger()
+ << " ================================= " << LL_ENDL;
+ std::set<LLUUID> ais_items, local_items;
+ const LLSD& cof_raw = content["cof_raw"];
+ for (LLSD::array_const_iterator it = cof_raw.beginArray();
+ it != cof_raw.endArray(); ++it)
+ {
+ const LLSD& item = *it;
+ if (item["parent_id"].asUUID() == LLAppearanceMgr::instance().getCOF())
+ {
+ ais_items.insert(item["item_id"].asUUID());
+ if (item["type"].asInteger() == 24) // link
+ {
+ LL_INFOS("Avatar") << "AIS Link: item_id: " << item["item_id"].asUUID()
+ << " linked_item_id: " << item["asset_id"].asUUID()
+ << " name: " << item["name"].asString()
+ << LL_ENDL;
+ }
+ else if (item["type"].asInteger() == 25) // folder link
+ {
+ LL_INFOS("Avatar") << "AIS Folder link: item_id: " << item["item_id"].asUUID()
+ << " linked_item_id: " << item["asset_id"].asUUID()
+ << " name: " << item["name"].asString()
+ << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("Avatar") << "AIS Other: item_id: " << item["item_id"].asUUID()
+ << " linked_item_id: " << item["asset_id"].asUUID()
+ << " name: " << item["name"].asString()
+ << " type: " << item["type"].asInteger()
+ << LL_ENDL;
+ }
+ }
+ }
+ LL_INFOS("Avatar") << LL_ENDL;
+ LL_INFOS("Avatar") << "Local COF, version requested: " << content["observed"].asInteger()
+ << " ================================= " << LL_ENDL;
+ 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();
+ local_items.insert(inv_item->getUUID());
+ LL_INFOS("Avatar") << "LOCAL: item_id: " << inv_item->getUUID()
+ << " linked_item_id: " << inv_item->getLinkedUUID()
+ << " name: " << inv_item->getName()
+ << " parent: " << inv_item->getParentUUID()
+ << LL_ENDL;
+ }
+ LL_INFOS("Avatar") << " ================================= " << LL_ENDL;
+ S32 local_only = 0, ais_only = 0;
+ for (std::set<LLUUID>::iterator it = local_items.begin(); it != local_items.end(); ++it)
+ {
+ if (ais_items.find(*it) == ais_items.end())
{
- LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key);
+ LL_INFOS("Avatar") << "LOCAL ONLY: " << *it << LL_ENDL;
+ local_only++;
}
- LLOutfitsList *outfits_list =
- dynamic_cast<LLOutfitsList*>(LLSideTray::getInstance()->getPanel("outfitslist_tab"));
- if (outfits_list)
+ }
+ for (std::set<LLUUID>::iterator it = ais_items.begin(); it != ais_items.end(); ++it)
+ {
+ if (local_items.find(*it) == local_items.end())
{
- outfits_list->setSelectedOutfitByUUID(mFolderID);
+ LL_INFOS("Avatar") << "AIS ONLY: " << *it << LL_ENDL;
+ ais_only++;
}
+ }
+ if (local_only==0 && ais_only==0)
+ {
+ LL_INFOS("Avatar") << "COF contents identical, only version numbers differ (req "
+ << content["observed"].asInteger()
+ << " rcv " << content["expected"].asInteger()
+ << ")" << LL_ENDL;
+ }
+}
- LLAppearanceMgr::getInstance()->updateIsDirty();
- gAgentWearables.notifyLoadingFinished(); // New outfit is saved.
- LLAppearanceMgr::getInstance()->updatePanelOutfitName("");
+/* virtual */ void RequestAgentUpdateAppearanceResponder::httpSuccess()
+{
+ const LLSD& content = getContent();
+ if (!content.isMap())
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
+ return;
}
+ if (content["success"].asBoolean())
+ {
+ LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL;
+ if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+ {
+ dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", content);
+ }
- virtual void fire(const LLUUID&)
- {}
+ onSuccess();
+ }
+ else
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Non-success response", content);
+ }
+}
+
+void RequestAgentUpdateAppearanceResponder::onSuccess()
+{
+ mInFlightCounter = llmax(mInFlightCounter-1,0);
+}
+
+/*virtual*/ void RequestAgentUpdateAppearanceResponder::httpFailure()
+{
+ LL_WARNS("Avatar") << "appearance update request failed, status "
+ << getStatus() << " reason " << getReason() << LL_ENDL;
+
+ if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+ {
+ const LLSD& content = getContent();
+ dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content);
+ debugCOF(content);
+ }
+ onFailure();
+}
+
+void RequestAgentUpdateAppearanceResponder::onFailure()
+{
+ mInFlightCounter = llmax(mInFlightCounter-1,0);
+
+ F32 seconds_to_wait;
+ mRetryPolicy->onFailure(getStatus(), getResponseHeaders());
+ if (mRetryPolicy->shouldRetry(seconds_to_wait))
+ {
+ LL_INFOS() << "retrying" << LL_ENDL;
+ doAfterInterval(boost::bind(&RequestAgentUpdateAppearanceResponder::sendRequest,this),
+ seconds_to_wait);
+ }
+ else
+ {
+ LL_WARNS() << "giving up after too many retries" << LL_ENDL;
+ }
+}
+
+
+LLSD LLAppearanceMgr::dumpCOF() const
+{
+ LLSD links = LLSD::emptyArray();
+ LLMD5 md5;
+
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(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();
+ LLSD item;
+ LLUUID item_id(inv_item->getUUID());
+ md5.update((unsigned char*)item_id.mData, 16);
+ item["description"] = inv_item->getActualDescription();
+ md5.update(inv_item->getActualDescription());
+ item["asset_type"] = inv_item->getActualType();
+ LLUUID linked_id(inv_item->getLinkedUUID());
+ item["linked_id"] = linked_id;
+ md5.update((unsigned char*)linked_id.mData, 16);
+
+ if (LLAssetType::AT_LINK == inv_item->getActualType())
+ {
+ const LLViewerInventoryItem* linked_item = inv_item->getLinkedItem();
+ if (NULL == linked_item)
+ {
+ LL_WARNS() << "Broken link for item '" << inv_item->getName()
+ << "' (" << inv_item->getUUID()
+ << ") during requestServerAppearanceUpdate" << LL_ENDL;
+ continue;
+ }
+ // Some assets may be 'hidden' and show up as null in the viewer.
+ //if (linked_item->getAssetUUID().isNull())
+ //{
+ // LL_WARNS() << "Broken link (null asset) for item '" << inv_item->getName()
+ // << "' (" << inv_item->getUUID()
+ // << ") during requestServerAppearanceUpdate" << LL_ENDL;
+ // continue;
+ //}
+ LLUUID linked_asset_id(linked_item->getAssetUUID());
+ md5.update((unsigned char*)linked_asset_id.mData, 16);
+ U32 flags = linked_item->getFlags();
+ md5.update(boost::lexical_cast<std::string>(flags));
+ }
+ else if (LLAssetType::AT_LINK_FOLDER != inv_item->getActualType())
+ {
+ LL_WARNS() << "Non-link item '" << inv_item->getName()
+ << "' (" << inv_item->getUUID()
+ << ") type " << (S32) inv_item->getActualType()
+ << " during requestServerAppearanceUpdate" << LL_ENDL;
+ continue;
+ }
+ links.append(item);
+ }
+ LLSD result = LLSD::emptyMap();
+ result["cof_contents"] = links;
+ char cof_md5sum[MD5HEX_STR_SIZE];
+ md5.finalize();
+ md5.hex_digest(cof_md5sum);
+ result["cof_md5sum"] = std::string(cof_md5sum);
+ return result;
+}
+
+void LLAppearanceMgr::requestServerAppearanceUpdate()
+{
+ mAppearanceResponder->onRequestRequested();
+}
+
+class LLIncrementCofVersionResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLIncrementCofVersionResponder);
+public:
+ LLIncrementCofVersionResponder() : LLHTTPClient::Responder()
+ {
+ mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5);
+ }
+
+ virtual ~LLIncrementCofVersionResponder()
+ {
+ }
+
+protected:
+ virtual void httpSuccess()
+ {
+ LL_INFOS() << "Successfully incremented agent's COF." << LL_ENDL;
+ const LLSD& content = getContent();
+ if (!content.isMap())
+ {
+ failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
+ return;
+ }
+ S32 new_version = content["category"]["version"].asInteger();
+
+ // cof_version should have increased
+ llassert(new_version > gAgentAvatarp->mLastUpdateRequestCOFVersion);
+
+ gAgentAvatarp->mLastUpdateRequestCOFVersion = new_version;
+ }
+
+ virtual void httpFailure()
+ {
+ LL_WARNS("Avatar") << "While attempting to increment the agent's cof we got an error "
+ << dumpResponse() << LL_ENDL;
+ F32 seconds_to_wait;
+ mRetryPolicy->onFailure(getStatus(), getResponseHeaders());
+ if (mRetryPolicy->shouldRetry(seconds_to_wait))
+ {
+ LL_INFOS() << "retrying" << LL_ENDL;
+ doAfterInterval(boost::bind(&LLAppearanceMgr::incrementCofVersion,
+ LLAppearanceMgr::getInstance(),
+ LLHTTPClient::ResponderPtr(this)),
+ seconds_to_wait);
+ }
+ else
+ {
+ LL_WARNS() << "giving up after too many retries" << LL_ENDL;
+ }
+ }
private:
- LLUUID mFolderID;
- bool mShowPanel;
+ LLPointer<LLHTTPRetryPolicy> mRetryPolicy;
};
-LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel)
+void LLAppearanceMgr::incrementCofVersion(LLHTTPClient::ResponderPtr responder_ptr)
{
- if (!isAgentAvatarValid()) return LLUUID::null;
+ // If we don't have a region, report it as an error
+ if (gAgent.getRegion() == NULL)
+ {
+ LL_WARNS() << "Region not set, cannot request cof_version increment" << LL_ENDL;
+ return;
+ }
- gAgentWearables.notifyLoadingStarted();
+ std::string url = gAgent.getRegion()->getCapability("IncrementCofVersion");
+ if (url.empty())
+ {
+ LL_WARNS() << "No cap for IncrementCofVersion." << LL_ENDL;
+ return;
+ }
- // First, make a folder in the My Outfits directory.
- const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
- LLUUID folder_id = gInventory.createNewCategory(
- parent_id,
- LLFolderType::FT_OUTFIT,
- new_folder_name);
+ LL_INFOS() << "Requesting cof_version be incremented via capability to: "
+ << url << LL_ENDL;
+ LLSD headers;
+ LLSD body = LLSD::emptyMap();
+
+ if (!responder_ptr.get())
+ {
+ responder_ptr = LLHTTPClient::ResponderPtr(new LLIncrementCofVersionResponder());
+ }
- updateClothingOrderingInfo();
+ LLHTTPClient::get(url, body, responder_ptr, headers, 30.0f);
+}
- LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id,show_panel);
- shallowCopyCategoryContents(getCOF(),folder_id, cb);
- createBaseOutfitLink(folder_id, cb);
+U32 LLAppearanceMgr::getNumAttachmentsInCOF()
+{
+ const LLUUID cof = getCOF();
+ LLInventoryModel::item_array_t obj_items;
+ getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT);
+ return obj_items.size();
+}
- dumpCat(folder_id,"COF, new outfit");
- return folder_id;
+std::string LLAppearanceMgr::getAppearanceServiceURL() const
+{
+ if (gSavedSettings.getString("DebugAvatarAppearanceServiceURLOverride").empty())
+ {
+ return mAppearanceServiceURL;
+ }
+ return gSavedSettings.getString("DebugAvatarAppearanceServiceURLOverride");
+}
+
+void show_created_outfit(LLUUID& folder_id, bool show_panel = true)
+{
+ if (!LLApp::isRunning())
+ {
+ LL_WARNS() << "called during shutdown, skipping" << LL_ENDL;
+ return;
+ }
+
+ LL_DEBUGS("Avatar") << "called" << LL_ENDL;
+ LLSD key;
+
+ //EXT-7727. For new accounts inventory callback is created during login process
+ // and may be processed after login process is finished
+ if (show_panel)
+ {
+ LL_DEBUGS("Avatar") << "showing panel" << LL_ENDL;
+ LLFloaterSidePanelContainer::showPanel("appearance", "panel_outfits_inventory", key);
+
+ }
+ LLOutfitsList *outfits_list =
+ dynamic_cast<LLOutfitsList*>(LLFloaterSidePanelContainer::getPanel("appearance", "outfitslist_tab"));
+ if (outfits_list)
+ {
+ outfits_list->setSelectedOutfitByUUID(folder_id);
+ }
+
+ LLAppearanceMgr::getInstance()->updateIsDirty();
+ gAgentWearables.notifyLoadingFinished(); // New outfit is saved.
+ LLAppearanceMgr::getInstance()->updatePanelOutfitName("");
+
+ // For SSB, need to update appearance after we add a base outfit
+ // link, since, the COF version has changed. There is a race
+ // condition in initial outfit setup which can lead to rez
+ // failures - SH-3860.
+ LL_DEBUGS("Avatar") << "requesting appearance update after createBaseOutfitLink" << LL_ENDL;
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
+ LLAppearanceMgr::getInstance()->createBaseOutfitLink(folder_id, cb);
+}
+
+void LLAppearanceMgr::onOutfitFolderCreated(const LLUUID& folder_id, bool show_panel)
+{
+ LLPointer<LLInventoryCallback> cb =
+ new LLBoostFuncInventoryCallback(no_op_inventory_func,
+ boost::bind(&LLAppearanceMgr::onOutfitFolderCreatedAndClothingOrdered,this,folder_id,show_panel));
+ updateClothingOrderingInfo(LLUUID::null, cb);
+}
+
+void LLAppearanceMgr::onOutfitFolderCreatedAndClothingOrdered(const LLUUID& folder_id, bool show_panel)
+{
+ LLPointer<LLInventoryCallback> cb =
+ new LLBoostFuncInventoryCallback(no_op_inventory_func,
+ boost::bind(show_created_outfit,folder_id,show_panel));
+ bool copy_folder_links = false;
+ slamCategoryLinks(getCOF(), folder_id, copy_folder_links, cb);
+}
+
+void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel)
+{
+ if (!isAgentAvatarValid()) return;
+
+ LL_DEBUGS("Avatar") << "creating new outfit" << LL_ENDL;
+
+ gAgentWearables.notifyLoadingStarted();
+
+ // First, make a folder in the My Outfits directory.
+ const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+ if (AISCommand::isAPIAvailable())
+ {
+ // cap-based category creation was buggy until recently. use
+ // existence of AIS as an indicator the fix is present. Does
+ // not actually use AIS to create the category.
+ inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel);
+ LLUUID folder_id = gInventory.createNewCategory(
+ parent_id,
+ LLFolderType::FT_OUTFIT,
+ new_folder_name,
+ func);
+ }
+ else
+ {
+ LLUUID folder_id = gInventory.createNewCategory(
+ parent_id,
+ LLFolderType::FT_OUTFIT,
+ new_folder_name);
+ onOutfitFolderCreated(folder_id, show_panel);
+ }
}
void LLAppearanceMgr::wearBaseOutfit()
@@ -2511,35 +3770,58 @@ void LLAppearanceMgr::wearBaseOutfit()
updateCOF(base_outfit_id);
}
+void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
+{
+ if (ids_to_remove.empty())
+ {
+ LL_WARNS() << "called with empty list, nothing to do" << LL_ENDL;
+ return;
+ }
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
+ for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it)
+ {
+ const LLUUID& id_to_remove = *it;
+ const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove);
+ removeCOFItemLinks(linked_item_id, cb);
+ addDoomedTempAttachment(linked_item_id);
+ }
+}
+
void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
{
- LLViewerInventoryItem * item_to_remove = gInventory.getItem(id_to_remove);
- if (!item_to_remove) return;
+ LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove);
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
+ removeCOFItemLinks(linked_item_id, cb);
+ addDoomedTempAttachment(linked_item_id);
+}
- switch (item_to_remove->getType())
- {
- case LLAssetType::AT_CLOTHING:
- if (get_is_item_worn(id_to_remove))
- {
- //*TODO move here the exact removing code from LLWearableBridge::removeItemFromAvatar in the future
- LLWearableBridge::removeItemFromAvatar(item_to_remove);
- }
- break;
- case LLAssetType::AT_OBJECT:
- LLVOAvatarSelf::detachAttachmentIntoInventory(item_to_remove->getLinkedUUID());
- default:
- break;
+
+// Adds the given item ID to mDoomedTempAttachmentIDs iff it's a temp attachment
+void LLAppearanceMgr::addDoomedTempAttachment(const LLUUID& id_to_remove)
+{
+ LLViewerObject * attachmentp = gAgentAvatarp->findAttachmentByID(id_to_remove);
+ if (attachmentp &&
+ attachmentp->isTempAttachment())
+ { // If this is a temp attachment and we want to remove it, record the ID
+ // so it will be deleted when attachments are synced up with COF
+ mDoomedTempAttachmentIDs.insert(id_to_remove);
+ //LL_INFOS() << "Will remove temp attachment id " << id_to_remove << LL_ENDL;
}
+}
- // *HACK: Force to remove garbage from COF.
- // Unworn links or objects can't be processed by existed removing functionality
- // since it is not designed for such cases. As example attachment object can't be removed
- // since sever don't sends message _PREHASH_KillObject in that case.
- // Also we can't check is link was successfully removed from COF since in case
- // deleting attachment link removing performs asynchronously in process_kill_object callback.
- removeCOFItemLinks(id_to_remove,false);
+// Find AND REMOVES the given UUID from mDoomedTempAttachmentIDs
+bool LLAppearanceMgr::shouldRemoveTempAttachment(const LLUUID& item_id)
+{
+ doomed_temp_attachments_t::iterator iter = mDoomedTempAttachmentIDs.find(item_id);
+ if (iter != mDoomedTempAttachmentIDs.end())
+ {
+ mDoomedTempAttachmentIDs.erase(iter);
+ return true;
+ }
+ return false;
}
+
bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
{
if (!item || !item->isWearableType()) return false;
@@ -2566,11 +3848,16 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
closer_to_body ? --it : ++it;
LLViewerInventoryItem* swap_item = *it;
if (!swap_item) return false;
- std::string tmp = swap_item->LLInventoryItem::getDescription();
- swap_item->setDescription(item->LLInventoryItem::getDescription());
+ std::string tmp = swap_item->getActualDescription();
+ swap_item->setDescription(item->getActualDescription());
item->setDescription(tmp);
+ // LL_DEBUGS("Inventory") << "swap, item "
+ // << ll_pretty_print_sd(item->asLLSD())
+ // << " swap_item "
+ // << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL;
+ // FIXME switch to use AISv3 where supported.
//items need to be updated on a dataserver
item->setComplete(TRUE);
item->updateServer(FALSE);
@@ -2582,9 +3869,9 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
//to cause appearance of the agent to be updated
bool result = false;
- if (result = gAgentWearables.moveWearable(item, closer_to_body))
+ if ((result = gAgentWearables.moveWearable(item, closer_to_body)))
{
- gAgentAvatarp->wearableUpdated(item->getWearableType(), FALSE);
+ gAgentAvatarp->wearableUpdated(item->getWearableType());
}
setOutfitDirty(true);
@@ -2600,7 +3887,7 @@ void LLAppearanceMgr::sortItemsByActualDescription(LLInventoryModel::item_array_
{
if (items.size() < 2) return;
- std::sort(items.begin(), items.end(), sort_by_description);
+ std::sort(items.begin(), items.end(), sort_by_actual_description);
}
//#define DUMP_CAT_VERBOSE
@@ -2612,42 +3899,42 @@ void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg)
gInventory.collectDescendents(cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH);
#ifdef DUMP_CAT_VERBOSE
- llinfos << llendl;
- llinfos << str << llendl;
+ LL_INFOS() << LL_ENDL;
+ LL_INFOS() << str << LL_ENDL;
S32 hitcount = 0;
- for(S32 i=0; i<items.count(); i++)
+ for(S32 i=0; i<items.size(); i++)
{
LLViewerInventoryItem *item = items.get(i);
if (item)
hitcount++;
- llinfos << i <<" "<< item->getName() <<llendl;
+ LL_INFOS() << i <<" "<< item->getName() <<LL_ENDL;
}
#endif
- llinfos << msg << " count " << items.count() << llendl;
+ LL_INFOS() << msg << " count " << items.size() << LL_ENDL;
}
void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items,
- const std::string& msg)
+ const std::string& msg)
{
- for (S32 i=0; i<items.count(); i++)
+ for (S32 i=0; i<items.size(); i++)
{
- LLViewerInventoryItem *item = items.get(i);
+ LLViewerInventoryItem *item = items.at(i);
LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
LLUUID asset_id;
if (linked_item)
{
asset_id = linked_item->getAssetUUID();
}
- llinfos << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << llendl;
+ LL_DEBUGS("Avatar") << self_av_string() << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << LL_ENDL;
}
- llinfos << llendl;
}
LLAppearanceMgr::LLAppearanceMgr():
mAttachmentInvLinkEnabled(false),
mOutfitIsDirty(false),
mOutfitLocked(false),
- mIsInUpdateAppearanceFromCOF(false)
+ mIsInUpdateAppearanceFromCOF(false),
+ mAppearanceResponder(new RequestAgentUpdateAppearanceResponder)
{
LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();
@@ -2667,13 +3954,13 @@ LLAppearanceMgr::~LLAppearanceMgr()
void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val)
{
- llinfos << "setAttachmentInvLinkEnable => " << (int) val << llendl;
+ LL_DEBUGS("Avatar") << "setAttachmentInvLinkEnable => " << (int) val << LL_ENDL;
mAttachmentInvLinkEnabled = val;
}
void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg)
{
- llinfos << msg << llendl;
+ LL_INFOS() << msg << LL_ENDL;
for (std::set<LLUUID>::const_iterator it = atts.begin();
it != atts.end();
++it)
@@ -2681,11 +3968,11 @@ void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg)
LLUUID item_id = *it;
LLViewerInventoryItem *item = gInventory.getItem(item_id);
if (item)
- llinfos << "atts " << item->getName() << llendl;
+ LL_INFOS() << "atts " << item->getName() << LL_ENDL;
else
- llinfos << "atts " << "UNKNOWN[" << item_id.asString() << "]" << llendl;
+ LL_INFOS() << "atts " << "UNKNOWN[" << item_id.asString() << "]" << LL_ENDL;
}
- llinfos << llendl;
+ LL_INFOS() << LL_ENDL;
}
void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
@@ -2697,11 +3984,15 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id)
// we have to pass do_update = true to call LLAppearanceMgr::updateAppearanceFromCOF.
// it will trigger gAgentWariables.notifyLoadingFinished()
// But it is not acceptable solution. See EXT-7777
- LLAppearanceMgr::addCOFItemLink(item_id, false); // Add COF link for item.
+ if (!isLinkedInCOF(item_id))
+ {
+ LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy();
+ LLAppearanceMgr::addCOFItemLink(item_id, cb); // Add COF link for item.
+ }
}
else
{
- //llinfos << "no link changes, inv link not enabled" << llendl;
+ //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
}
}
@@ -2711,32 +4002,31 @@ void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
if (mAttachmentInvLinkEnabled)
{
- LLAppearanceMgr::removeCOFItemLinks(item_id, false);
+ LLAppearanceMgr::removeCOFItemLinks(item_id);
}
else
{
- //llinfos << "no link changes, inv link not enabled" << llendl;
+ //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL;
}
}
BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const
{
- return gInventory.isObjectDescendentOf(obj_id, getCOF());
+ const LLUUID& cof = getCOF();
+ if (obj_id == cof)
+ return TRUE;
+ const LLInventoryObject* obj = gInventory.getObject(obj_id);
+ if (obj && obj->getParentUUID() == cof)
+ return TRUE;
+ return FALSE;
}
// static
bool LLAppearanceMgr::isLinkInCOF(const LLUUID& obj_id)
{
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLLinkedItemIDMatches find_links(gInventory.getLinkedItemID(obj_id));
- gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(),
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- find_links);
-
- return !items.empty();
+ const LLUUID& target_id = gInventory.getLinkedItemID(obj_id);
+ LLLinkedItemIDMatches find_links(target_id);
+ return gInventory.hasMatchingDirectDescendent(LLAppearanceMgr::instance().getCOF(), find_links);
}
BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
@@ -2753,87 +4043,6 @@ BOOL LLAppearanceMgr::getIsProtectedCOFItem(const LLUUID& obj_id) const
// For now, don't allow direct deletion from the COF. Instead, force users
// to choose "Detach" or "Take Off".
return TRUE;
- /*
- const LLInventoryObject *obj = gInventory.getObject(obj_id);
- if (!obj) return FALSE;
-
- // Can't delete bodyparts, since this would be equivalent to removing the item.
- if (obj->getType() == LLAssetType::AT_BODYPART) return TRUE;
-
- // Can't delete the folder link, since this is saved for bookkeeping.
- if (obj->getActualType() == LLAssetType::AT_LINK_FOLDER) return TRUE;
-
- return FALSE;
- */
-}
-
-// Shim class to allow arbitrary boost::bind
-// expressions to be run as one-time idle callbacks.
-//
-// TODO: rework idle function spec to take a boost::function in the first place.
-class OnIdleCallbackOneTime
-{
-public:
- OnIdleCallbackOneTime(nullary_func_t callable):
- mCallable(callable)
- {
- }
- static void onIdle(void *data)
- {
- gIdleCallbacks.deleteFunction(onIdle, data);
- OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data);
- self->call();
- delete self;
- }
- void call()
- {
- mCallable();
- }
-private:
- nullary_func_t mCallable;
-};
-
-void doOnIdleOneTime(nullary_func_t callable)
-{
- OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable);
- gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor);
-}
-
-// Shim class to allow generic boost functions to be run as
-// recurring idle callbacks. Callable should return true when done,
-// false to continue getting called.
-//
-// TODO: rework idle function spec to take a boost::function in the first place.
-class OnIdleCallbackRepeating
-{
-public:
- OnIdleCallbackRepeating(bool_func_t callable):
- mCallable(callable)
- {
- }
- // Will keep getting called until the callable returns true.
- static void onIdle(void *data)
- {
- OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data);
- bool done = self->call();
- if (done)
- {
- gIdleCallbacks.deleteFunction(onIdle, data);
- delete self;
- }
- }
- bool call()
- {
- return mCallable();
- }
-private:
- bool_func_t mCallable;
-};
-
-void doOnIdleRepeating(bool_func_t callable)
-{
- OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable);
- gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
}
class CallAfterCategoryFetchStage2: public LLInventoryFetchItemsObserver
@@ -2850,8 +4059,8 @@ public:
}
virtual void done()
{
- llinfos << this << " done with incomplete " << mIncomplete.size()
- << " complete " << mComplete.size() << " calling callable" << llendl;
+ LL_INFOS() << this << " done with incomplete " << mIncomplete.size()
+ << " complete " << mComplete.size() << " calling callable" << LL_ENDL;
gInventory.removeObserver(this);
doOnIdleOneTime(mCallable);
@@ -2874,34 +4083,32 @@ public:
}
virtual void done()
{
- // What we do here is get the complete information on the items in
- // the library, and set up an observer that will wait for that to
- // happen.
+ // What we do here is get the complete information on the
+ // items in the requested category, and set up an observer
+ // that will wait for that to happen.
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(mComplete.front(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
- S32 count = item_array.count();
+ S32 count = item_array.size();
if(!count)
{
- llwarns << "Nothing fetched in category " << mComplete.front()
- << llendl;
- //dec_busy_count();
+ LL_WARNS() << "Nothing fetched in category " << mComplete.front()
+ << LL_ENDL;
gInventory.removeObserver(this);
+ doOnIdleOneTime(mCallable);
- // lets notify observers that loading is finished.
- gAgentWearables.notifyLoadingFinished();
delete this;
return;
}
- llinfos << "stage1 got " << item_array.count() << " items, passing to stage2 " << llendl;
+ LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL;
uuid_vec_t ids;
for(S32 i = 0; i < count; ++i)
{
- ids.push_back(item_array.get(i)->getUUID());
+ ids.push_back(item_array.at(i)->getUUID());
}
gInventory.removeObserver(this);
@@ -2943,16 +4150,7 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)
void wear_multiple(const uuid_vec_t& ids, bool replace)
{
LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy;
-
- bool first = true;
- uuid_vec_t::const_iterator it;
- for (it = ids.begin(); it != ids.end(); ++it)
- {
- // if replace is requested, the first item worn will replace the current top
- // item, and others will be added.
- LLAppearanceMgr::instance().wearItemOnAvatar(*it,false,first && replace,cb);
- first = false;
- }
+ LLAppearanceMgr::instance().wearItemsOnAvatar(ids, false, replace, cb);
}
// SLapp for easy-wearing of a stock (library) avatar
@@ -2966,18 +4164,37 @@ public:
bool handle(const LLSD& tokens, const LLSD& query_map,
LLMediaCtrl* web)
{
- LLPointer<LLInventoryCategory> category = new LLInventoryCategory(query_map["folder_id"],
- LLUUID::null,
- LLFolderType::FT_CLOTHING,
- "Quick Appearance");
- LLSD::UUID folder_uuid = query_map["folder_id"].asUUID();
- if ( gInventory.getCategory( folder_uuid ) != NULL )
- {
- LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false);
+ LLSD::UUID folder_uuid;
- // *TODOw: This may not be necessary if initial outfit is chosen already -- josh
- gAgent.setGenderChosen(TRUE);
+ if (folder_uuid.isNull() && query_map.has("folder_name"))
+ {
+ std::string outfit_folder_name = query_map["folder_name"];
+ folder_uuid = findDescendentCategoryIDByName(
+ gInventory.getLibraryRootFolderID(),
+ outfit_folder_name);
+ }
+ if (folder_uuid.isNull() && query_map.has("folder_id"))
+ {
+ folder_uuid = query_map["folder_id"].asUUID();
}
+
+ if (folder_uuid.notNull())
+ {
+ LLPointer<LLInventoryCategory> category = new LLInventoryCategory(folder_uuid,
+ LLUUID::null,
+ LLFolderType::FT_CLOTHING,
+ "Quick Appearance");
+ if ( gInventory.getCategory( folder_uuid ) != NULL )
+ {
+ LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false);
+
+ // *TODOw: This may not be necessary if initial outfit is chosen already -- josh
+ gAgent.setOutfitChosen(TRUE);
+ }
+ }
+
+ // release avatar picker keyboard focus
+ gFocusMgr.setKeyboardFocus( NULL );
return true;
}