diff options
Diffstat (limited to 'indra/newview/llappearancemgr.cpp')
-rw-r--r-- | indra/newview/llappearancemgr.cpp | 672 |
1 files changed, 548 insertions, 124 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3691d731ed..a7d90ab8d3 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -45,7 +45,7 @@ #include "llinventoryobserver.h" #include "llnotificationsutil.h" #include "lloutfitobserver.h" -#include "llpaneloutfitsinventory.h" +#include "lloutfitslist.h" #include "llselectmgr.h" #include "llsidepanelappearance.h" #include "llsidetray.h" @@ -55,6 +55,25 @@ #include "llviewerregion.h" #include "llwearablelist.h" +// RAII thingy to guarantee that a variable gets reset when the Setter +// goes out of scope. More general utility would be handy - TODO: +// check boost. +class BoolSetter +{ +public: + BoolSetter(bool& var): + mVar(var) + { + mVar = true; + } + ~BoolSetter() + { + mVar = false; + } +private: + bool& mVar; +}; + char ORDER_NUMBER_SEPARATOR('@'); class LLOutfitUnLockTimer: public LLEventTimer @@ -177,13 +196,20 @@ class LLUpdateDirtyState: public LLInventoryCallback { public: LLUpdateDirtyState() {} - virtual ~LLUpdateDirtyState(){ LLAppearanceMgr::getInstance()->updateIsDirty(); } + virtual ~LLUpdateDirtyState() + { + if (LLAppearanceMgr::instanceExists()) + { + LLAppearanceMgr::getInstance()->updateIsDirty(); + } + } virtual void fire(const LLUUID&) {} }; -LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(): - mFireCount(0) +LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering): + mFireCount(0), + mUpdateBaseOrder(update_base_outfit_ordering) { } @@ -193,7 +219,7 @@ LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() if (!LLApp::isExiting()) { - LLAppearanceMgr::instance().updateAppearanceFromCOF(); + LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder); } } @@ -201,7 +227,9 @@ 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; +#endif mFireCount++; } @@ -216,13 +244,15 @@ struct LLFoundData const LLUUID& asset_id, const std::string& name, const LLAssetType::EType& asset_type, - const LLWearableType::EType& wearable_type + const LLWearableType::EType& wearable_type, + const bool is_replacement = false ) : mItemID(item_id), mAssetID(asset_id), mName(name), mAssetType(asset_type), mWearableType(wearable_type), + mIsReplacement(is_replacement), mWearable( NULL ) {} LLUUID mItemID; @@ -231,6 +261,7 @@ struct LLFoundData LLAssetType::EType mAssetType; LLWearableType::EType mWearableType; LLWearable* mWearable; + bool mIsReplacement; }; @@ -253,8 +284,18 @@ public: void onWearableAssetFetch(LLWearable *wearable); void onAllComplete(); - + typedef std::list<LLFoundData> found_list_t; + found_list_t& getFoundList(); + void eraseTypeToLink(LLWearableType::EType type); + void eraseTypeToRecover(LLWearableType::EType type); + void setObjItems(const LLInventoryModel::item_array_t& items); + void setGestItems(const LLInventoryModel::item_array_t& items); + bool isMostRecent(); + void handleLateArrivals(); + void resetTime(F32 timeout); + +private: found_list_t mFoundList; LLInventoryModel::item_array_t mObjItems; LLInventoryModel::item_array_t mGestItems; @@ -264,34 +305,94 @@ public: S32 mResolved; LLTimer mWaitTime; bool mFired; + typedef std::set<LLWearableHoldingPattern*> type_set_hp; + static type_set_hp sActiveHoldingPatterns; + bool mIsMostRecent; + std::set<LLWearable*> mLateArrivals; + bool mIsAllComplete; }; +LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns; + LLWearableHoldingPattern::LLWearableHoldingPattern(): mResolved(0), - mFired(false) + mFired(false), + mIsMostRecent(true), + mIsAllComplete(false) { + if (sActiveHoldingPatterns.size()>0) + { + llinfos << "Creating LLWearableHoldingPattern when " + << sActiveHoldingPatterns.size() + << " other attempts are active." + << " Flagging others as invalid." + << llendl; + for (type_set_hp::iterator it = sActiveHoldingPatterns.begin(); + it != sActiveHoldingPatterns.end(); + ++it) + { + (*it)->mIsMostRecent = false; + } + + } + sActiveHoldingPatterns.insert(this); } LLWearableHoldingPattern::~LLWearableHoldingPattern() { + sActiveHoldingPatterns.erase(this); +} + +bool LLWearableHoldingPattern::isMostRecent() +{ + return mIsMostRecent; +} + +LLWearableHoldingPattern::found_list_t& LLWearableHoldingPattern::getFoundList() +{ + return mFoundList; +} + +void LLWearableHoldingPattern::eraseTypeToLink(LLWearableType::EType type) +{ + mTypesToLink.erase(type); +} + +void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type) +{ + mTypesToRecover.erase(type); +} + +void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items) +{ + mObjItems = items; +} + +void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items) +{ + mGestItems = items; } bool LLWearableHoldingPattern::isFetchCompleted() { - return (mResolved >= (S32)mFoundList.size()); // have everything we were waiting for? + return (mResolved >= (S32)getFoundList().size()); // have everything we were waiting for? } bool LLWearableHoldingPattern::isTimedOut() { - static F32 max_wait_time = 60.0; // give up if wearable fetches haven't completed in max_wait_time seconds. - return mWaitTime.getElapsedTimeF32() > max_wait_time; + return mWaitTime.hasExpired(); } void LLWearableHoldingPattern::checkMissingWearables() { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + 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 = mFoundList.begin(); it != mFoundList.end(); ++it) + for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) { LLFoundData &data = *it; if (data.mWearableType < LLWearableType::WT_COUNT) @@ -302,7 +403,10 @@ void LLWearableHoldingPattern::checkMissingWearables() for (S32 type = 0; type < LLWearableType::WT_COUNT; ++type) { - llinfos << "type " << type << " requested " << requested_by_type[type] << " found " << found_by_type[type] << llendl; + 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; + } if (found_by_type[type] > 0) continue; if ( @@ -322,7 +426,7 @@ void LLWearableHoldingPattern::checkMissingWearables() } } - mWaitTime.reset(); + resetTime(60.0F); if (!pollMissingWearables()) { doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this)); @@ -331,6 +435,11 @@ void LLWearableHoldingPattern::checkMissingWearables() void LLWearableHoldingPattern::onAllComplete() { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + // Activate all gestures in this folder if (mGestItems.count() > 0) { @@ -366,16 +475,31 @@ void LLWearableHoldingPattern::onAllComplete() // Only safe to delete if all wearable callbacks and all missing wearables completed. delete this; } + else + { + mIsAllComplete = true; + handleLateArrivals(); + } } void LLWearableHoldingPattern::onFetchCompletion() { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + checkMissingWearables(); } // Runs as an idle callback until all wearables are fetched (or we time out). bool LLWearableHoldingPattern::pollFetchCompletion() { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + bool completed = isFetchCompleted(); bool timed_out = isTimedOut(); bool done = completed || timed_out; @@ -408,8 +532,13 @@ public: } 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->mTypesToLink.erase(mType); + mHolder->eraseTypeToLink(mType); // Add wearable to FoundData for actual wearing LLViewerInventoryItem *item = gInventory.getItem(item_id); LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; @@ -421,13 +550,14 @@ public: 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 - ); + 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->mFoundList.push_front(found); + mHolder->getFoundList().push_front(found); } else { @@ -456,11 +586,16 @@ public: } 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 for type " << mType << llendl; LLViewerInventoryItem *itemp = gInventory.getItem(item_id); mWearable->setItemID(item_id); LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder); - mHolder->mTypesToRecover.erase(mType); + mHolder->eraseTypeToRecover(mType); llassert(itemp); if (itemp) { @@ -481,6 +616,11 @@ private: void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type) { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + // Try to recover by replacing missing wearable with a new one. LLNotificationsUtil::add("ReplacedMissingWearable"); lldebugs << "Wearable " << LLWearableType::getTypeLabel(type) @@ -511,7 +651,7 @@ bool LLWearableHoldingPattern::isMissingCompleted() void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() { - for (found_list_t::iterator it = mFoundList.begin(); it != mFoundList.end(); ++it) + for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) { LLFoundData &data = *it; if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable)) @@ -525,34 +665,144 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() bool LLWearableHoldingPattern::pollMissingWearables() { + if (!isMostRecent()) + { + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; + } + bool timed_out = isTimedOut(); bool missing_completed = isMissingCompleted(); bool done = timed_out || missing_completed; - - llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size() - << " links " << mTypesToLink.size() - << " wearables, timed out " << timed_out - << " elapsed " << mWaitTime.getElapsedTimeF32() - << " done " << done << llendl; + + if (!done) + { + llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size() + << " links " << mTypesToLink.size() + << " wearables, timed out " << timed_out + << " elapsed " << mWaitTime.getElapsedTimeF32() + << " done " << done << llendl; + } if (done) { gAgentAvatarp->debugWearablesLoaded(); - clearCOFLinksForMissingWearables(); + + // BAP - if we don't call clearCOFLinksForMissingWearables() + // here, we won't have to add the link back in later if the + // wearable arrives late. This is to avoid corruption of + // wearable ordering info. Also has the effect of making + // unworn item links visible in the COF under some + // circumstances. + + //clearCOFLinksForMissingWearables(); onAllComplete(); } return done; } +// Handle wearables that arrived after the timeout period expired. +void LLWearableHoldingPattern::handleLateArrivals() +{ + // Only safe to run if we have previously finished the missing + // wearables and other processing - otherwise we could be in some + // intermediate state - but have not been superceded by a later + // outfit change request. + if (mLateArrivals.size() == 0) + { + // Nothing to process. + return; + } + if (!isMostRecent()) + { + llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl; + } + if (!mIsAllComplete) + { + llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl; + } + + llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl; + + // Update mFoundList using late-arriving wearables. + std::set<LLWearableType::EType> replaced_types; + for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + iter != getFoundList().end(); ++iter) + { + LLFoundData& data = *iter; + for (std::set<LLWearable*>::iterator wear_it = mLateArrivals.begin(); + wear_it != mLateArrivals.end(); + ++wear_it) + { + LLWearable *wearable = *wear_it; + + if(wearable->getAssetID() == data.mAssetID) + { + data.mWearable = wearable; + + replaced_types.insert(data.mWearableType); + + // BAP - if we didn't call + // clearCOFLinksForMissingWearables() earlier, we + // don't need to restore the link here. Fixes + // wearable ordering problems. + + // LLAppearanceMgr::instance().addCOFItemLink(data.mItemID,false); + + // BAP failing this means inventory or asset server + // are corrupted in a way we don't handle. + llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType)); + break; + } + } + } + + // Remove COF links for any default wearables previously used to replace the late arrivals. + // All this pussyfooting around with a while loop and explicit + // iterator incrementing is to allow removing items from the list + // without clobbering the iterator we're using to navigate. + LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + while (iter != getFoundList().end()) + { + LLFoundData& data = *iter; + + // If an item of this type has recently shown up, removed the corresponding replacement wearable from COF. + if (data.mWearable && data.mIsReplacement && + replaced_types.find(data.mWearableType) != replaced_types.end()) + { + LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false); + std::list<LLFoundData>::iterator clobber_ator = iter; + ++iter; + getFoundList().erase(clobber_ator); + } + else + { + ++iter; + } + } + + // Clear contents of late arrivals. + mLateArrivals.clear(); + + // Update appearance based on mFoundList + LLAppearanceMgr::instance().updateAgentWearables(this, false); +} + +void LLWearableHoldingPattern::resetTime(F32 timeout) +{ + mWaitTime.reset(); + mWaitTime.setTimerExpirySec(timeout); +} + void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable) { - mResolved += 1; // just counting callbacks, not successes. - llinfos << "onWearableAssetFetch, resolved count " << mResolved << " of requested " << mFoundList.size() << llendl; - if (wearable) + if (!isMostRecent()) { - llinfos << "wearable found, type " << wearable->getType() << " asset " << wearable->getAssetID() << llendl; + llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; } - else + + mResolved += 1; // just counting callbacks, not successes. + llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl; + if (!wearable) { llwarns << "no wearable found" << llendl; } @@ -560,6 +810,14 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable) if (mFired) { llwarns << "called after holder fired" << llendl; + if (wearable) + { + mLateArrivals.insert(wearable); + if (mIsAllComplete) + { + handleLateArrivals(); + } + } return; } @@ -568,16 +826,20 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable) return; } - for (LLWearableHoldingPattern::found_list_t::iterator iter = mFoundList.begin(); - iter != mFoundList.end(); ++iter) + for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); + iter != getFoundList().end(); ++iter) { LLFoundData& data = *iter; if(wearable->getAssetID() == data.mAssetID) { - data.mWearable = wearable; // Failing this means inventory or asset server are corrupted in a way we don't handle. - llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType)); - break; + 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; + break; + } + + data.mWearable = wearable; } } } @@ -644,6 +906,13 @@ const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() const LLViewerInventoryCategory *cat = item->getLinkedCategory(); if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) { + const LLUUID parent_id = cat->getParentUUID(); + LLViewerInventoryCategory* parent_cat = gInventory.getCategory(parent_id); + // if base outfit moved to trash it means that we don't have base outfit + if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH) + { + return NULL; + } return item; } } @@ -682,10 +951,20 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID() return outfit_cat->getUUID(); } -bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace) +bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb) { 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; @@ -716,24 +995,17 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up { removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false); } + addCOFItemLink(item_to_wear, do_update, cb); } + break; case LLAssetType::AT_BODYPART: - // Don't wear anything until initial wearables are loaded, can - // destroy clothing items. - if (!gAgentWearables.areWearablesLoaded()) - { - LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded"); - return false; - } - + // 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. - if (item_to_wear->getType() == LLAssetType::AT_BODYPART) - { - removeCOFLinksOfType(item_to_wear->getWearableType(), false); - } + removeCOFLinksOfType(item_to_wear->getWearableType(), false); - addCOFItemLink(item_to_wear, do_update); + addCOFItemLink(item_to_wear, do_update, cb); break; case LLAssetType::AT_OBJECT: rez_attachment(item_to_wear, NULL); @@ -823,7 +1095,7 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindWorn collector; + LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false); gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); @@ -846,6 +1118,7 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds llwarns << "folder not found for src " << src_id.asString() << llendl; return; } + llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl; LLUUID parent_id = dst_id; if(parent_id.isNull()) { @@ -866,6 +1139,7 @@ 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; for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); iter != items->end(); ++iter) @@ -905,6 +1179,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; copy_inventory_item(gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), @@ -979,6 +1254,34 @@ bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) return true; } +// static +bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + 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; +} + +// static +bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(outfit_cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + return items.size() > 0; +} + void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) { LLInventoryModel::cat_array_t cats; @@ -1061,8 +1364,9 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid, const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; - - llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; // Seraph remove for 2.1 +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; +#endif } } @@ -1092,9 +1396,12 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) // - Body parts: always include COF contents as a fallback in case any // 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); + 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); @@ -1129,15 +1436,26 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) // Create links to new COF contents. llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl; - LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy; + LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append); - llinfos << "Linking body items" << llendl; // Seraph remove for 2.1 +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking body items" << llendl; +#endif linkAll(cof, body_items, link_waiter); - llinfos << "Linking wear items" << llendl; // Seraph remove for 2.1 + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking wear items" << llendl; +#endif linkAll(cof, wear_items, link_waiter); - llinfos << "Linking obj items" << llendl; // Seraph remove for 2.1 + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking obj items" << llendl; +#endif linkAll(cof, obj_items, link_waiter); - llinfos << "Linking gesture items" << llendl; // Seraph remove for 2.1 + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Linking gesture items" << llendl; +#endif linkAll(cof, gest_items, link_waiter); // Add link to outfit if category is an outfit. @@ -1182,12 +1500,11 @@ void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, boo LLInventoryItem::item_array_t items; LLDynamicArray< LLWearable* > wearables; - // For each wearable type, find the first instance in the category - // that we recursed through. + // For each wearable type, find the wearables of that type. for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) { - for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin(); - iter != holder->mFoundList.end(); ++iter) + for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->getFoundList().begin(); + iter != holder->getFoundList().end(); ++iter) { LLFoundData& data = *iter; LLWearable* wearable = data.mWearable; @@ -1239,19 +1556,92 @@ bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* it return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription(); } -void LLAppearanceMgr::updateAppearanceFromCOF() +void item_array_diff(LLInventoryModel::item_array_t& full_list, + LLInventoryModel::item_array_t& keep_list, + LLInventoryModel::item_array_t& kill_list) + { + for (LLInventoryModel::item_array_t::iterator it = full_list.begin(); + it != full_list.end(); + ++it) + { + LLViewerInventoryItem *item = *it; + if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()? + { + kill_list.push_back(item); + } + } +} + +void LLAppearanceMgr::enforceItemCountLimits() +{ + S32 purge_count = 0; + + LLInventoryModel::item_array_t body_items; + getDescendentsOfAssetType(getCOF(), body_items, LLAssetType::AT_BODYPART, false); + LLInventoryModel::item_array_t curr_body_items = body_items; + removeDuplicateItems(body_items); + filterWearableItems(body_items, 1); + LLInventoryModel::item_array_t kill_body_items; + item_array_diff(curr_body_items,body_items,kill_body_items); + for (LLInventoryModel::item_array_t::iterator it = kill_body_items.begin(); + it != kill_body_items.end(); + ++it) + { + LLViewerInventoryItem *item = *it; + llinfos << "purging dup body part " << item->getName() << llendl; + gInventory.purgeObject(item->getUUID()); + purge_count++; + } + + LLInventoryModel::item_array_t wear_items; + getDescendentsOfAssetType(getCOF(), wear_items, LLAssetType::AT_CLOTHING, false); + LLInventoryModel::item_array_t curr_wear_items = wear_items; + removeDuplicateItems(wear_items); + filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); + LLInventoryModel::item_array_t kill_wear_items; + item_array_diff(curr_wear_items,wear_items,kill_wear_items); + for (LLInventoryModel::item_array_t::iterator it = kill_wear_items.begin(); + it != kill_wear_items.end(); + ++it) + { + LLViewerInventoryItem *item = *it; + llinfos << "purging excess clothing item " << item->getName() << llendl; + gInventory.purgeObject(item->getUUID()); + purge_count++; + } + + if (purge_count>0) + { + gInventory.notifyObservers(); + } +} + +void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) +{ + if (mIsInUpdateAppearanceFromCOF) + { + llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl; + return; + } + + BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF); + + llinfos << "starting" << llendl; + //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(); + updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering); + // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but + // this should catch anything that gets through. + enforceItemCountLimits(); + // update dirty flag to see if the state of the COF matches // the saved outfit stored as a folder link - llinfos << "starting" << llendl; - updateIsDirty(); - dumpCat(getCOF(),"COF, start"); + //dumpCat(getCOF(),"COF, start"); bool follow_folder_links = true; LLUUID current_outfit_id = getCOF(); @@ -1267,6 +1657,9 @@ void LLAppearanceMgr::updateAppearanceFromCOF() remove_non_link_items(obj_items); remove_non_link_items(gest_items); + dumpItemArray(wear_items,"asset_dump: wear_item"); + dumpItemArray(obj_items,"asset_dump: obj_item"); + if(!wear_items.count()) { LLNotificationsUtil::add("CouldNotPutOnOutfit"); @@ -1279,8 +1672,8 @@ void LLAppearanceMgr::updateAppearanceFromCOF() LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; - holder->mObjItems = obj_items; - holder->mGestItems = gest_items; + holder->setObjItems(obj_items); + holder->setGestItems(gest_items); // Note: can't do normal iteration, because if all the // wearables can be resolved immediately, then the @@ -1291,6 +1684,12 @@ void LLAppearanceMgr::updateAppearanceFromCOF() { LLViewerInventoryItem *item = wear_items.get(i); LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; + + // Fault injection: use debug setting to test asset + // fetch failures (should be replaced by new defaults in + // lost&found). + U32 skip_type = gSavedSettings.getU32("ForceAssetFail"); + if (item && item->getIsLinkType() && linked_item) { LLFoundData found(linked_item->getUUID(), @@ -1300,18 +1699,12 @@ void LLAppearanceMgr::updateAppearanceFromCOF() linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID ); -#if 0 - // Fault injection: uncomment this block to test asset - // fetch failures (should be replaced by new defaults in - // lost&found). - if (found.mWearableType == LLWearableType::WT_SHAPE || found.mWearableType == LLWearableType::WT_JACKET) + if (skip_type != LLWearableType::WT_INVALID && skip_type == found.mWearableType) { found.mAssetID.generate(); // Replace with new UUID, guaranteed not to exist in DB - } -#endif //pushing back, not front, to preserve order of wearables for LLAgentWearables - holder->mFoundList.push_back(found); + holder->getFoundList().push_back(found); } else { @@ -1326,12 +1719,12 @@ void LLAppearanceMgr::updateAppearanceFromCOF() } } - for (LLWearableHoldingPattern::found_list_t::iterator it = holder->mFoundList.begin(); - it != holder->mFoundList.end(); ++it) + for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin(); + it != holder->getFoundList().end(); ++it) { LLFoundData& found = *it; - llinfos << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl; + lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl; // Fetch the wearables about to be worn. LLWearableList::instance().getAsset(found.mAssetID, @@ -1342,6 +1735,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF() } + holder->resetTime(gSavedSettings.getF32("MaxWearableWaitTime")); if (!holder->pollFetchCompletion()) { doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollFetchCompletion,holder)); @@ -1402,6 +1796,8 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool { if(!category) return; + gAgentWearables.notifyLoadingStarted(); + llinfos << "wearInventoryCategory( " << category->getName() << " )" << llendl; @@ -1563,9 +1959,10 @@ bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventor class LLDeferredCOFLinkObserver: public LLInventoryObserver { public: - LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update): + LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL): mItemID(item_id), - mDoUpdate(do_update) + mDoUpdate(do_update), + mCallback(cb) { } @@ -1579,7 +1976,7 @@ public: if (item) { gInventory.removeObserver(this); - LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate); + LLAppearanceMgr::instance().addCOFItemLink(item,mDoUpdate,mCallback); delete this; } } @@ -1587,26 +1984,27 @@ public: private: const LLUUID mItemID; bool mDoUpdate; + 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 ) +void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb) { const LLInventoryItem *item = gInventory.getItem(item_id); if (!item) { - LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update); + LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb); gInventory.addObserver(observer); } else { - addCOFItemLink(item, do_update); + addCOFItemLink(item, do_update, cb); } } -void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update ) +void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb) { const LLViewerInventoryItem *vitem = dynamic_cast<const LLViewerInventoryItem*>(item); if (!vitem) @@ -1667,7 +2065,10 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update } else { - LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0; + if(do_update && cb.isNull()) + { + cb = new ModifiedCOFCallback; + } const std::string description = vitem->getIsLinkType() ? vitem->getDescription() : ""; link_inventory_item( gAgent.getID(), vitem->getLinkedUUID(), @@ -1778,17 +2179,19 @@ void LLAppearanceMgr::updateIsDirty() } else { + LLIsOfAssetType collector = LLIsOfAssetType(LLAssetType::AT_LINK); + LLInventoryModel::cat_array_t cof_cats; LLInventoryModel::item_array_t cof_items; - gInventory.collectDescendents(cof, cof_cats, cof_items, - LLInventoryModel::EXCLUDE_TRASH); + gInventory.collectDescendentsIf(cof, cof_cats, cof_items, + LLInventoryModel::EXCLUDE_TRASH, collector); LLInventoryModel::cat_array_t outfit_cats; LLInventoryModel::item_array_t outfit_items; - gInventory.collectDescendents(base_outfit, outfit_cats, outfit_items, - LLInventoryModel::EXCLUDE_TRASH); + gInventory.collectDescendentsIf(base_outfit, outfit_cats, outfit_items, + LLInventoryModel::EXCLUDE_TRASH, collector); - if(outfit_items.count() != cof_items.count() -1) + if(outfit_items.count() != cof_items.count()) { // Current outfit folder should have one more item than the outfit folder. // this one item is the link back to the outfit folder itself. @@ -1796,16 +2199,6 @@ void LLAppearanceMgr::updateIsDirty() return; } - //getting rid of base outfit folder link to simplify comparison - for (LLInventoryModel::item_array_t::iterator it = cof_items.begin(); it != cof_items.end(); ++it) - { - if (*it == base_outfit_item) - { - cof_items.erase(it); - break; - } - } - //"dirty" - also means a difference in linked UUIDs and/or a difference in wearables order (links' descriptions) std::sort(cof_items.begin(), cof_items.end(), sort_by_linked_uuid); std::sort(outfit_items.begin(), outfit_items.end(), sort_by_linked_uuid); @@ -1861,6 +2254,8 @@ bool LLAppearanceMgr::updateBaseOutfit() } setOutfitLocked(true); + gAgentWearables.notifyLoadingStarted(); + const LLUUID base_outfit_id = getBaseOutfitUUID(); if (base_outfit_id.isNull()) return false; @@ -1941,11 +2336,19 @@ struct WearablesOrderComparator U32 mControlSize; }; -void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id) +void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base_outfit_ordering) { 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 @@ -1978,6 +2381,7 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id) item->setComplete(TRUE); item->updateServer(FALSE); gInventory.updateItem(item); + inventory_changed = true; } } @@ -1992,28 +2396,28 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id) class LLShowCreatedOutfit: public LLInventoryCallback { public: - LLShowCreatedOutfit(LLUUID& folder_id): mFolderID(folder_id) + LLShowCreatedOutfit(LLUUID& folder_id, bool show_panel = true): mFolderID(folder_id), mShowPanel(show_panel) {} virtual ~LLShowCreatedOutfit() { LLSD key; - LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); - LLPanelOutfitsInventory *outfit_panel = - dynamic_cast<LLPanelOutfitsInventory*>(LLSideTray::getInstance()->getPanel("panel_outfits_inventory")); - if (outfit_panel) + + //EXT-7727. For new accounts LLShowCreatedOutfit is created during login process + // add may be processed after login process is finished + if (mShowPanel) { - outfit_panel->getRootFolder()->clearSelection(); - outfit_panel->getRootFolder()->setSelectionByID(mFolderID, TRUE); + LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); } - - LLAccordionCtrlTab* tab_outfits = outfit_panel ? outfit_panel->findChild<LLAccordionCtrlTab>("tab_outfits") : 0; - if (tab_outfits && !tab_outfits->getDisplayChildren()) + LLOutfitsList *outfits_list = + dynamic_cast<LLOutfitsList*>(LLSideTray::getInstance()->getPanel("outfitslist_tab")); + if (outfits_list) { - tab_outfits->changeOpenClose(tab_outfits->getDisplayChildren()); + outfits_list->setSelectedOutfitByUUID(mFolderID); } LLAppearanceMgr::getInstance()->updateIsDirty(); + gAgentWearables.notifyLoadingFinished(); // New outfit is saved. LLAppearanceMgr::getInstance()->updatePanelOutfitName(""); } @@ -2022,12 +2426,15 @@ public: private: LLUUID mFolderID; + bool mShowPanel; }; -LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name) +LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, bool show_panel) { if (!isAgentAvatarValid()) return LLUUID::null; + gAgentWearables.notifyLoadingStarted(); + // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); LLUUID folder_id = gInventory.createNewCategory( @@ -2037,7 +2444,7 @@ LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name) updateClothingOrderingInfo(); - LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id); + LLPointer<LLInventoryCallback> cb = new LLShowCreatedOutfit(folder_id,show_panel); shallowCopyCategoryContents(getCOF(),folder_id, cb); createBaseOutfitLink(folder_id, cb); @@ -2085,6 +2492,14 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) } default: break; } + + // *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); } bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) @@ -2176,18 +2591,24 @@ void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg) void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items, const std::string& msg) { - llinfos << msg << llendl; for (S32 i=0; i<items.count(); i++) { LLViewerInventoryItem *item = items.get(i); - llinfos << i <<" " << item->getName() << llendl; + 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; } llinfos << llendl; } LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), - mOutfitIsDirty(false) + mOutfitIsDirty(false), + mIsInUpdateAppearanceFromCOF(false) { LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); @@ -2234,6 +2655,9 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id) if (mAttachmentInvLinkEnabled) { + // 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. } else |