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.cpp437
1 files changed, 365 insertions, 72 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 06a9892c7e..79106318a4 100644..100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -155,6 +155,121 @@ LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string
}
}
+// Shim between inventory callback and boost function
+typedef boost::function<void(const LLUUID&)> inventory_func_type;
+
+class LLBoostFuncInventoryCallback: public LLInventoryCallback
+{
+public:
+
+ LLBoostFuncInventoryCallback(const inventory_func_type& func):
+ mFunc(func)
+ {
+ }
+
+ void fire(const LLUUID& item_id)
+ {
+ mFunc(item_id);
+ }
+
+private:
+ inventory_func_type mFunc;
+};
+
+LLPointer<LLInventoryCallback> make_inventory_func_callback(const inventory_func_type& func)
+{
+ return new LLBoostFuncInventoryCallback(func);
+}
+
+void report_fire(const LLUUID& item_id)
+{
+ llinfos << item_id << llendl;
+}
+
+class LLInventoryCopyMgr: public LLEventTimer
+{
+public:
+ LLInventoryCopyMgr(LLInventoryModel::item_array_t& src_items, const LLUUID& dst_cat_id,
+ bool append, const std::string& phase):
+ mDstCatID(dst_cat_id),
+ mAppend(append),
+ mTrackingPhase(phase),
+ LLEventTimer(5.0)
+ {
+ for (LLInventoryModel::item_array_t::const_iterator it = src_items.begin();
+ it != src_items.end();
+ ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+ mSrcTimes[item->getUUID()] = LLTimer();
+ requestCopy(item->getUUID());
+ }
+ if (!mTrackingPhase.empty())
+ {
+ selfStartPhase(mTrackingPhase);
+ }
+ }
+
+ void requestCopy(const LLUUID& item_id)
+ {
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
+ if (!item)
+ {
+ llwarns << "requestCopy item not found " << item_id << llendl;
+ return;
+ }
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ mDstCatID,
+ std::string(),
+ make_inventory_func_callback(boost::bind(&LLInventoryCopyMgr::onCopy,this,item->getUUID(),_1))
+ );
+ }
+
+ void onCopy(const LLUUID& src_id, const LLUUID& dst_id)
+ {
+ LL_DEBUGS("Avatar") << "copied, src_id " << src_id << " to dst_id " << dst_id << " after " << mSrcTimes[src_id].getElapsedTimeF32() << " seconds" << llendl;
+ mSrcTimes.erase(src_id);
+ if (mSrcTimes.empty())
+ {
+ onCompletion();
+ }
+ }
+
+ void onCompletion()
+ {
+ llinfos << "done" << llendl;
+ if (!mTrackingPhase.empty())
+ {
+ selfStopPhase(mTrackingPhase);
+ }
+ LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mDstCatID), mAppend);
+ }
+
+ // virtual
+ // Will be deleted after returning true - only safe to do this if all callbacks have fired.
+ BOOL tick()
+ {
+ bool all_done = mSrcTimes.empty();
+
+ if (!all_done)
+ {
+ llwarns << "possible hang in copy, waiting on " << mSrcTimes.size() << " items" << llendl;
+ // TODO possibly add retry logic here.
+
+ }
+ return all_done;
+ }
+
+private:
+ std::string mTrackingPhase;
+ std::map<LLUUID,LLTimer> mSrcTimes;
+ LLUUID mDstCatID;
+ bool mAppend;
+};
+
class LLWearInventoryCategoryCallback : public LLInventoryCallback
{
public:
@@ -278,7 +393,7 @@ struct LLFoundData
std::string mName;
LLAssetType::EType mAssetType;
LLWearableType::EType mWearableType;
- LLWearable* mWearable;
+ LLViewerWearable* mWearable;
bool mIsReplacement;
};
@@ -302,7 +417,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;
@@ -328,7 +443,7 @@ private:
typedef std::set<LLWearableHoldingPattern*> type_set_hp;
static type_set_hp sActiveHoldingPatterns;
bool mIsMostRecent;
- std::set<LLWearable*> mLateArrivals;
+ std::set<LLViewerWearable*> mLateArrivals;
bool mIsAllComplete;
};
@@ -562,7 +677,7 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
class RecoveredItemLinkCB: public LLInventoryCallback
{
public:
- RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+ RecoveredItemLinkCB(LLWearableType::EType type, LLViewerWearable *wearable, LLWearableHoldingPattern* holder):
mHolder(holder),
mWearable(wearable),
mType(type)
@@ -610,14 +725,14 @@ public:
}
private:
LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
+ LLViewerWearable *mWearable;
LLWearableType::EType mType;
};
class RecoveredItemCB: public LLInventoryCallback
{
public:
- RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
+ RecoveredItemCB(LLWearableType::EType type, LLViewerWearable *wearable, LLWearableHoldingPattern* holder):
mHolder(holder),
mWearable(wearable),
mType(type)
@@ -650,7 +765,7 @@ public:
}
private:
LLWearableHoldingPattern* mHolder;
- LLWearable *mWearable;
+ LLViewerWearable *mWearable;
LLWearableType::EType mType;
};
@@ -666,7 +781,7 @@ void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type
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);
+ 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);
@@ -699,7 +814,7 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
{
// Wearable link that was never resolved; remove links to it from COF
LL_INFOS("Avatar") << self_av_string() << "removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
- LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
+ LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID);
}
}
}
@@ -773,11 +888,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)
{
@@ -813,7 +928,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);
@@ -837,7 +952,7 @@ void LLWearableHoldingPattern::resetTime(F32 timeout)
mWaitTime.setTimerExpirySec(timeout);
}
-void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
+void LLWearableHoldingPattern::onWearableAssetFetch(LLViewerWearable *wearable)
{
if (!isMostRecent())
{
@@ -888,7 +1003,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);
@@ -927,6 +1042,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()
{
@@ -1041,7 +1168,7 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
if ((replace && wearable_count != 0) ||
(wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
{
- removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false);
+ removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1));
}
addCOFItemLink(item_to_wear, do_update, cb);
}
@@ -1051,7 +1178,7 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
// 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);
+ removeCOFLinksOfType(item_to_wear->getWearableType());
addCOFItemLink(item_to_wear, do_update, cb);
break;
@@ -1149,11 +1276,13 @@ 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);
}
// Create a copy of src_id + contents as a subfolder of dst_id.
@@ -1577,7 +1706,7 @@ void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, boo
{
lldebugs << "updateAgentWearables()" << llendl;
LLInventoryItem::item_array_t items;
- LLDynamicArray< LLWearable* > wearables;
+ LLDynamicArray< LLViewerWearable* > wearables;
// For each wearable type, find the wearables of that type.
for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ )
@@ -1586,7 +1715,7 @@ 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);
@@ -1732,6 +1861,15 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
// the saved outfit stored as a folder link
updateIsDirty();
+ // Send server request for appearance update
+ if (gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion())
+ {
+ requestServerAppearanceUpdate();
+ }
+ // DRANO really should wait for the appearance message to set this.
+ // verify that deleting this line doesn't break anything.
+ gAgentAvatarp->setIsUsingServerBakes(gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion());
+
//dumpCat(getCOF(),"COF, start");
bool follow_folder_links = true;
@@ -1822,6 +1960,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
// Fetch the wearables about to be worn.
LLWearableList::instance().getAsset(found.mAssetID,
found.mName,
+ gAgentAvatarp,
found.mAssetType,
onWearableAssetFetch,
(void*)holder);
@@ -1956,22 +2095,10 @@ 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 LLInventoryCopyMgr(*items, new_cat_id, append, std::string("wear_inventory_category_callback"));
+
// BAP fixes a lag in display of created dir.
gInventory.notifyObservers();
}
@@ -1988,7 +2115,12 @@ 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() )
+ {
+ return;
+ }
LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName()
<< "'" << LL_ENDL;
@@ -2196,7 +2328,62 @@ void LLAppearanceMgr::addEnsembleLink( LLInventoryCategory* cat, bool do_update
#endif
}
-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,
+ false);
+ 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)
{
gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
@@ -2214,13 +2401,9 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
gInventory.purgeObject(item->getUUID());
}
}
- if (do_update)
- {
- LLAppearanceMgr::updateAppearanceFromCOF();
- }
}
-void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update)
+void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type)
{
LLFindWearablesOfType filter_wearables_of_type(type);
LLInventoryModel::cat_array_t cats;
@@ -2236,11 +2419,6 @@ void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_u
gInventory.purgeObject(item->getUUID());
}
}
-
- if (do_update)
- {
- updateAppearanceFromCOF();
- }
}
bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2)
@@ -2583,8 +2761,129 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base
if (inventory_changed) gInventory.notifyObservers();
}
+// This is intended for use with HTTP Clients/Responders, but is not
+// specifically coupled with those classes.
+class LLHTTPRetryPolicy: public LLThreadSafeRefCount
+{
+public:
+ LLHTTPRetryPolicy() {}
+ virtual ~LLHTTPRetryPolicy() {}
+ virtual bool shouldRetry(U32 status, F32& seconds_to_wait) = 0;
+};
+// Example of simplest possible policy, not necessarily recommended.
+class LLAlwaysRetryImmediatelyPolicy: public LLHTTPRetryPolicy
+{
+public:
+ LLAlwaysRetryImmediatelyPolicy() {}
+ bool shouldRetry(U32 status, F32& seconds_to_wait)
+ {
+ seconds_to_wait = 0.0;
+ return true;
+ }
+};
+// Very general policy with geometric back-off after failures,
+// up to a maximum delay, and maximum number of retries.
+class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy
+{
+public:
+ LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries):
+ mMinDelay(min_delay),
+ mMaxDelay(max_delay),
+ mBackoffFactor(backoff_factor),
+ mMaxRetries(max_retries),
+ mDelay(min_delay),
+ mRetryCount(0)
+ {
+ }
+
+ bool shouldRetry(U32 status, F32& seconds_to_wait)
+ {
+ seconds_to_wait = mDelay;
+ mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay);
+ mRetryCount++;
+ return (mRetryCount<=mMaxRetries);
+ }
+
+private:
+ F32 mMinDelay; // delay never less than this value
+ F32 mMaxDelay; // delay never exceeds this value
+ F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay.
+ U32 mMaxRetries; // maximum number of times shouldRetry will return true.
+ F32 mDelay; // current delay.
+ U32 mRetryCount; // number of times shouldRetry has been called.
+};
+
+class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
+{
+public:
+ RequestAgentUpdateAppearanceResponder()
+ {
+ mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5);
+ }
+
+ virtual ~RequestAgentUpdateAppearanceResponder()
+ {
+ }
+
+ // Successful completion.
+ /* virtual */ void result(const LLSD& content)
+ {
+ llinfos << "request OK" << llendl;
+ }
+
+ // Error
+ /*virtual*/ void error(U32 status, const std::string& reason)
+ {
+ llwarns << "appearance update request failed, status: " << status << " reason: " << reason << llendl;
+ F32 seconds_to_wait;
+ if (mRetryPolicy->shouldRetry(status,seconds_to_wait))
+ {
+ llinfos << "retrying" << llendl;
+ doAfterInterval(boost::bind(&LLAppearanceMgr::requestServerAppearanceUpdate,
+ LLAppearanceMgr::getInstance(),
+ LLCurl::ResponderPtr(this)),
+ seconds_to_wait);
+ }
+ else
+ {
+ llwarns << "giving up after too many retries" << llendl;
+ }
+ }
+
+ LLPointer<LLHTTPRetryPolicy> mRetryPolicy;
+};
+
+void LLAppearanceMgr::requestServerAppearanceUpdate(LLCurl::ResponderPtr responder_ptr)
+{
+ if (!gAgent.getRegion())
+ {
+ llwarns << "Region not set, cannot request server appearance update" << llendl;
+ }
+ if (gAgent.getRegion()->getCentralBakeVersion()==0)
+ {
+ llwarns << "Region does not support baking" << llendl;
+ }
+ std::string url = gAgent.getRegion()->getCapability("UpdateAvatarAppearance");
+ if (url.empty())
+ {
+ llwarns << "No cap for UpdateAvatarAppearance." << llendl;
+ return;
+ }
+
+ LLSD body;
+ S32 cof_version = getCOFVersion();
+ body["cof_version"] = cof_version;
+ //LLCurl::ResponderPtr responder_ptr;
+ if (!responder_ptr.get())
+ {
+ responder_ptr = new RequestAgentUpdateAppearanceResponder;
+ }
+ LLHTTPClient::post(url, body, responder_ptr);
+ llassert(cof_version >= mLastUpdateRequestCOFVersion);
+ mLastUpdateRequestCOFVersion = cof_version;
+}
class LLShowCreatedOutfit: public LLInventoryCallback
{
@@ -2661,33 +2960,26 @@ void LLAppearanceMgr::wearBaseOutfit()
updateCOF(base_outfit_id);
}
-void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
+void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove)
{
- LLViewerInventoryItem * item_to_remove = gInventory.getItem(id_to_remove);
- if (!item_to_remove) return;
-
- switch (item_to_remove->getType())
+ if (ids_to_remove.empty())
{
- 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;
+ llwarns << "called with empty list, nothing to do" << llendl;
}
+ 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);
+ }
+ updateAppearanceFromCOF();
+}
- // *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);
+void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
+{
+ LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove);
+ removeCOFItemLinks(linked_item_id);
+ updateAppearanceFromCOF();
}
bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
@@ -2796,7 +3088,8 @@ LLAppearanceMgr::LLAppearanceMgr():
mAttachmentInvLinkEnabled(false),
mOutfitIsDirty(false),
mOutfitLocked(false),
- mIsInUpdateAppearanceFromCOF(false)
+ mIsInUpdateAppearanceFromCOF(false),
+ mLastUpdateRequestCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN)
{
LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();
@@ -2860,7 +3153,7 @@ void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id)
if (mAttachmentInvLinkEnabled)
{
- LLAppearanceMgr::removeCOFItemLinks(item_id, false);
+ LLAppearanceMgr::removeCOFItemLinks(item_id);
}
else
{