diff options
Diffstat (limited to 'indra/newview/llappearancemgr.cpp')
| -rw-r--r-- | indra/newview/llappearancemgr.cpp | 453 |
1 files changed, 299 insertions, 154 deletions
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 946d674e8b..9636db21c1 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -31,6 +31,7 @@ #include "llagent.h" #include "llagentcamera.h" #include "llagentwearables.h" +#include "llappearancelistener.h" #include "llappearancemgr.h" #include "llattachmentsmgr.h" #include "llcommandhandler.h" @@ -66,6 +67,8 @@ #include "llavatarpropertiesprocessor.h" +LLAppearanceListener sAppearanceListener; + namespace { const S32 BAKE_RETRY_MAX_COUNT = 5; @@ -537,9 +540,14 @@ LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() selfStopPhase("update_appearance_on_destroy"); - LLAppearanceMgr::instance().updateAppearanceFromCOF(mEnforceItemRestrictions, - mEnforceOrdering, - mPostUpdateFunc); + //avoid calling an update inside coroutine + bool force_restrictions(mEnforceItemRestrictions); + bool enforce_ordering(mEnforceOrdering); + nullary_func_t post_update_func(mPostUpdateFunc); + doOnIdleOneTime([force_restrictions,enforce_ordering,post_update_func]() + { + LLAppearanceMgr::instance().updateAppearanceFromCOF(force_restrictions, enforce_ordering, post_update_func); + }); } } @@ -695,12 +703,12 @@ public: void onFetchCompletion(); bool isFetchCompleted(); bool isTimedOut(); + bool pollStopped(); void checkMissingWearables(); bool pollMissingWearables(); bool isMissingCompleted(); void recoverMissingWearable(LLWearableType::EType type); - void clearCOFLinksForMissingWearables(); void onWearableAssetFetch(LLViewerWearable *wearable); void onAllComplete(); @@ -709,7 +717,6 @@ public: 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(); @@ -719,7 +726,6 @@ public: private: found_list_t mFoundList; - LLInventoryModel::item_array_t mObjItems; LLInventoryModel::item_array_t mGestItems; typedef std::set<S32> type_set_t; type_set_t mTypesToRecover; @@ -796,11 +802,6 @@ 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; @@ -848,7 +849,7 @@ void LLWearableHoldingPattern::checkMissingWearables() // was requested but none was found, create a default asset as a replacement. // In all other cases, don't do anything. // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud - // due to logic in LLVOAvatarSelf::getIsCloud(). + // due to logic in LLVOAvatarSelf::getHasMissingParts(). // For non-critical types (tatoo, socks, etc.) the wearable will just be missing. (requested_by_type[type] > 0) && ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT))) @@ -906,55 +907,10 @@ void LLWearableHoldingPattern::onAllComplete() if (isAgentAvatarValid()) { - 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->clearAttachmentOverrides(); - - if (objects_to_remove.size() || items_to_add.size()) - { - LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size() - << " and add " << items_to_add.size() << " items" << LL_ENDL; - } - - // 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; - if (!objectp->isAnimatedObject()) - { - gAgentAvatarp->addAttachmentOverridesForObject(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()) @@ -992,6 +948,10 @@ bool LLWearableHoldingPattern::pollFetchCompletion() { // runway skip here? LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL; + + // If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves + doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollStopped, this)); + return true; } bool completed = isFetchCompleted(); @@ -1062,6 +1022,9 @@ void recovered_item_cb(const LLUUID& item_id, LLWearableType::EType type, LLView { // runway skip here? LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL; + + // If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves + return; } LL_DEBUGS("Avatar") << self_av_string() << "Recovered item for type " << type << LL_ENDL; @@ -1112,18 +1075,15 @@ bool LLWearableHoldingPattern::isMissingCompleted() return mTypesToLink.size()==0 && mTypesToRecover.size()==0; } -void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() +bool LLWearableHoldingPattern::pollStopped() { - for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) + // We have to keep on polling until we're sure that all callbacks have completed or they'll cause a crash + if (isFetchCompleted() && isMissingCompleted()) { - LLFoundData &data = *it; - if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable)) - { - // Wearable link that was never resolved; remove links to it from COF - LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " removing link for unresolved item " << data.mItemID.asString() << LL_ENDL; - LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID); - } + delete this; + return true; } + return false; } bool LLWearableHoldingPattern::pollMissingWearables() @@ -1132,6 +1092,10 @@ bool LLWearableHoldingPattern::pollMissingWearables() { // runway skip here? LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL; + + // If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves + doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollStopped, this)); + return true; } bool timed_out = isTimedOut(); @@ -1156,14 +1120,6 @@ bool LLWearableHoldingPattern::pollMissingWearables() gAgentAvatarp->debugWearablesLoaded(); - // 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; @@ -1210,13 +1166,6 @@ void LLWearableHoldingPattern::handleLateArrivals() 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)); @@ -1492,6 +1441,27 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false) } } +bool needs_to_replace(LLViewerInventoryItem* item_to_wear, bool & first_for_object, std::vector<bool>& first_for_type, bool replace) +{ + bool res = false; + LLAssetType::EType type = item_to_wear->getType(); + if (type == LLAssetType::AT_OBJECT) + { + res = first_for_object && replace; + first_for_object = false; + } + else if (type == LLAssetType::AT_CLOTHING) + { + LLWearableType::EType wtype = item_to_wear->getWearableType(); + if (wtype >= 0 && wtype < LLWearableType::WT_COUNT) + { + res = first_for_type[wtype] && replace; + first_for_type[wtype] = false; + } + } + return res; +} + void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, bool do_update, bool replace, @@ -1500,7 +1470,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, LL_DEBUGS("UIUsage") << "wearItemsOnAvatar" << LL_ENDL; LLUIUsage::instance().logCommand("Avatar.WearItem"); - bool first = true; + bool first_for_object = true; + std::vector<bool> first_for_type(LLWearableType::WT_COUNT, true); LLInventoryObject::const_object_list_t items_to_link; @@ -1508,9 +1479,6 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, 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()) @@ -1529,8 +1497,9 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) { LL_DEBUGS("Avatar") << "inventory item in library, will copy and wear " - << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL; - LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace)); + << item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL; + bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace); + LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb, _1, replace_item)); copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb); continue; @@ -1568,7 +1537,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, } LLWearableType::EType type = item_to_wear->getWearableType(); S32 wearable_count = gAgentWearables.getWearableCount(type); - if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type)) + bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace); + if ((replace_item && wearable_count != 0) || !gAgentWearables.canAddWearable(type)) { LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1); @@ -1597,7 +1567,13 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, case LLAssetType::AT_OBJECT: { - rez_attachment(item_to_wear, NULL, replace); + // Note that this will replace only first attachment regardless of attachment point, + // so if user is wearing two items over other two on different attachment points, + // only one will be replaced. + // Unfortunately we have no way to determine attachment point from inventory item. + // We might want to forbid wearing multiple objects with replace option in future. + bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace); + rez_attachment(item_to_wear, NULL, replace_item); } break; @@ -2037,7 +2013,7 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) } // Moved from LLWearableList::ContextMenu for wider utility. -bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch) const { // TODO: investigate wearables may not be loaded at this point EXT-8231 @@ -2067,7 +2043,10 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const } else { + if (warn_on_type_mismatch) + { LL_WARNS() << "Unexpected wearable type" << LL_ENDL; + } return false; } } @@ -2105,15 +2084,23 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLIn // 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, S32 max_total) + LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total, bool skip_bodyparts) { // 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++) + LLInventoryModel::item_array_t items_to_keep; size_t non_body_kept = 0; + for (const auto& item : items) { - items_to_keep.push_back(items[i]); + if (skip_bodyparts && item.get() && item.get()->getType() == LLAssetType::AT_BODYPART) + { + items_to_keep.push_back(item); + } + else if (non_body_kept < max_total) + { + items_to_keep.push_back(item); + non_body_kept++; + } } items = items_to_keep; } @@ -2258,7 +2245,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) } if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_slam_request", contents); } slam_inventory_folder(getCOF(), contents, link_waiter); @@ -2598,6 +2585,11 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions, remove_non_link_items(wear_items); remove_non_link_items(obj_items); remove_non_link_items(gest_items); + // Since we're following folder links we might have picked up new duplicates, or exceeded MAX_CLOTHING_LAYERS + removeDuplicateItems(wear_items); + removeDuplicateItems(obj_items); + removeDuplicateItems(gest_items); + filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS, true); dumpItemArray(wear_items,"asset_dump: wear_item"); dumpItemArray(obj_items,"asset_dump: obj_item"); @@ -2609,6 +2601,77 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions, << " descendent_count " << cof->getDescendentCount() << " viewer desc count " << cof->getViewerDescendentCount() << LL_ENDL; } + + // Update attachments to match those requested. + if (isAgentAvatarValid()) + { + // Include attachments which should be in COF but don't have their link created yet + std::set<LLUUID> pendingAttachments; + LLAttachmentsMgr::instance().getPendingAttachments(pendingAttachments); + for (const LLUUID& idAttachItem : pendingAttachments) + { + if ( !gAgentAvatarp->isWearingAttachment(idAttachItem) || isLinkedInCOF(idAttachItem) ) + { + LLAttachmentsMgr::instance().clearPendingAttachmentLink(idAttachItem); + continue; + } + + if (LLViewerInventoryItem* pAttachItem = gInventory.getItem(idAttachItem)) + { + obj_items.push_back(pAttachItem); + } + } + + LL_DEBUGS("Avatar") << self_av_string() << "Updating " << obj_items.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(obj_items, + 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->clearAttachmentOverrides(); + + if (objects_to_remove.size() || items_to_add.size()) + { + LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size() + << " and add " << items_to_add.size() << " items" << LL_ENDL; + } + + // Take off the attachments that will no longer be in the outfit. + // (don't remove attachments until avatar is fully loaded - reduces random attaching/detaching/reattaching at log-on) + if (gAgentAvatarp->isFullyLoaded()) + { + LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove); + } + + // 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; + if (!objectp->isAnimatedObject()) + { + gAgentAvatarp->addAttachmentOverridesForObject(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(!wear_items.size()) { LLNotificationsUtil::add("CouldNotPutOnOutfit"); @@ -2623,7 +2686,6 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions, LLTimer hp_block_timer; LLWearableHoldingPattern* holder = new LLWearableHoldingPattern; - holder->setObjItems(obj_items); holder->setGestItems(gest_items); // Note: can't do normal iteration, because if all the @@ -3870,7 +3932,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LL_DEBUGS("Avatar") << "Will send request for cof_version " << cofVersion << LL_ENDL; bRetry = false; - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + LLCore::HttpRequest::ptr_t httpRequest = std::make_shared<LLCore::HttpRequest>(); if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) { @@ -3951,7 +4013,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL; if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", result); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_ok", result); } } while (bRetry); @@ -3960,7 +4022,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd /*static*/ void LLAppearanceMgr::debugAppearanceUpdateCOF(const LLSD& content) { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content); + dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_error", content); LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger() << " ================================= " << LL_ENDL; @@ -4152,6 +4214,8 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove, nul for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it) { const LLUUID& id_to_remove = *it; + auto attachment = gAgentAvatarp->getWornAttachment(id_to_remove); + if (attachment && attachment->isLocked()) continue; const LLUUID& linked_item_id = gInventory.getLinkedItemID(id_to_remove); LLViewerInventoryItem *item = gInventory.getItem(linked_item_id); if (item && item->getType() == LLAssetType::AT_OBJECT) @@ -4163,6 +4227,7 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove, nul continue; } removeCOFItemLinks(linked_item_id, cb); + LLAttachmentsMgr::instance().clearPendingAttachmentLink(linked_item_id); addDoomedTempAttachment(linked_item_id); } } @@ -4207,37 +4272,54 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b if (item->getType() != LLAssetType::AT_CLOTHING) return false; if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false; + S32 pos = gAgentWearables.getWearableIdxFromItem(item); + if (pos < 0) return false; // Not found + + U32 count = gAgentWearables.getWearableCount(item->getWearableType()); + if (count < 2) return false; // Nothing to swap with + if (closer_to_body) + { + if (pos == 0) return false; // already first + } + else + { + if (pos == count - 1) return false; // already last + } + + U32 old_pos = (U32)pos; + U32 swap_with = closer_to_body ? old_pos - 1 : old_pos + 1; + LLUUID swap_item_id = gAgentWearables.getWearableItemID(item->getWearableType(), swap_with); + + // Find link item from item id. LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; LLFindWearablesOfType filter_wearables_of_type(item->getWearableType()); gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); if (items.empty()) return false; - // We assume that the items have valid descriptions. - std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); - - if (closer_to_body && items.front() == item) return false; - if (!closer_to_body && items.back() == item) return false; - - LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item); - if (items.end() == it) return false; - - - //swapping descriptions - closer_to_body ? --it : ++it; - LLViewerInventoryItem* swap_item = *it; - if (!swap_item) return false; - std::string tmp = swap_item->getActualDescription(); - swap_item->setDescription(item->getActualDescription()); - item->setDescription(tmp); + LLViewerInventoryItem* swap_item = nullptr; + for (auto iter : items) + { + if (iter->getLinkedUUID() == swap_item_id) + { + swap_item = iter.get(); + break; + } + } + if (!swap_item) + { + return false; + } - // LL_DEBUGS("Inventory") << "swap, item " - // << ll_pretty_print_sd(item->asLLSD()) - // << " swap_item " - // << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL; + // Description is supposed to hold sort index, but user could have changed + // order rapidly and there might be a state mismatch between description + // and gAgentWearables, trust gAgentWearables over description. + // Generate new description. + std::string new_desc = build_order_string(item->getWearableType(), old_pos); + swap_item->setDescription(new_desc); + new_desc = build_order_string(item->getWearableType(), swap_with); + item->setDescription(new_desc); - // FIXME switch to use AISv3 where supported. - //items need to be updated on a dataserver item->setComplete(true); item->updateServer(false); gInventory.updateItem(item); @@ -4324,8 +4406,8 @@ LLAppearanceMgr::LLAppearanceMgr(): outfit_observer.addCOFSavedCallback(boost::bind( &LLAppearanceMgr::setOutfitLocked, this, false)); - mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer((F32)gSavedSettings.getS32( - "OutfitOperationsTimeout"))); + mUnlockOutfitTimer = std::make_unique<LLOutfitUnLockTimer>((F32)gSavedSettings.getS32( + "OutfitOperationsTimeout")); gIdleCallbacks.addFunction(&LLAttachmentsMgr::onIdle, NULL); gIdleCallbacks.addFunction(&LLAppearanceMgr::onIdle, NULL); //sheduling appearance update requests @@ -4717,48 +4799,69 @@ void wear_multiple(const uuid_vec_t& ids, bool replace) LLAppearanceMgr::instance().wearItemsOnAvatar(ids, true, replace, cb); } -// SLapp for easy-wearing of a stock (library) avatar -// +bool wear_category(const LLSD& query_map, bool append) +{ + LLUUID folder_uuid; + + if (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()) + LL_WARNS() << "Couldn't find " << std::quoted(outfit_folder_name) << " in the Library" << LL_ENDL; + } + if (folder_uuid.isNull() && query_map.has("folder_id")) + { + folder_uuid = query_map["folder_id"].asUUID(); + } + + if (folder_uuid.notNull()) + { + if (LLViewerInventoryCategory* cat = gInventory.getCategory(folder_uuid)) + { + if (bool is_library = gInventory.isObjectDescendentOf(folder_uuid, gInventory.getRootFolderID())) + { + LLPointer<LLInventoryCategory> new_category = new LLInventoryCategory(folder_uuid, LLUUID::null, LLFolderType::FT_CLOTHING, "Quick Appearance"); + LLAppearanceMgr::getInstance()->wearInventoryCategory(new_category, true, append); + } + else + { + LLAppearanceMgr::getInstance()->wearInventoryCategory(cat, true, append); + } + return true; + } + else + { + LL_WARNS() << "Couldn't find folder id" << folder_uuid << " in Inventory" << LL_ENDL; + } + } + + return false; +} + +bool LLAppearanceMgr::wearOutfit(const LLSD& query_map, bool append) +{ + return wear_category(query_map, append); +} + class LLWearFolderHandler : public LLCommandHandler { public: // not allowed from outside the app - LLWearFolderHandler() : LLCommandHandler("wear_folder", UNTRUSTED_BLOCK) { } + LLWearFolderHandler() : LLCommandHandler("wear_folder", UNTRUSTED_BLOCK) {} bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { - LLSD::UUID folder_uuid; - - 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")) + if (wear_category(query_map, false)) { - 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 ) - { - // Assume this is coming from the predefined avatars web floater - LLUIUsage::instance().logCommand("Avatar.WearPredefinedAppearance"); - LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); + // Assume this is coming from the predefined avatars web floater + LLUIUsage::instance().logCommand("Avatar.WearPredefinedAppearance"); - // *TODOw: This may not be necessary if initial outfit is chosen already -- josh - gAgent.setOutfitChosen(true); - } + // *TODOw: This may not be necessary if initial outfit is chosen already -- josh + gAgent.setOutfitChosen(true); } // release avatar picker keyboard focus @@ -4768,4 +4871,46 @@ public: } }; +class LLAddFolderHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLAddFolderHandler() : LLCommandHandler("add_folder", UNTRUSTED_BLOCK) {} + + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) + { + wear_category(query_map, true); + + return true; + } +}; + +class LLRemoveFolderHandler : public LLCommandHandler +{ +public: + LLRemoveFolderHandler() : LLCommandHandler("remove_folder", UNTRUSTED_BLOCK) {} + + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) + { + if (query_map.has("folder_id")) + { + LLUUID folder_id = query_map["folder_id"].asUUID(); + if (folder_id.notNull()) + { + if (LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id)) + { + LLAppearanceMgr::instance().takeOffOutfit(cat->getLinkedUUID()); + } + else + { + LL_WARNS() << "Couldn't find folder id" << folder_id << " in Inventory" << LL_ENDL; + } + } + } + return true; + } +}; + LLWearFolderHandler gWearFolderHandler; +LLAddFolderHandler gAddFolderHandler; +LLRemoveFolderHandler gRemoveFolderHandler; |
