diff options
| author | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2025-09-26 23:44:12 +0300 | 
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2025-09-29 19:28:04 +0300 | 
| commit | 7748898b25202b5c90f676bd8e3e8a258a307723 (patch) | |
| tree | 1ecb7cbd7a710f9895620cbc6116f4fcab969648 | |
| parent | bdf942b94a9a9d054b4158b8bdd14fe0234b59b6 (diff) | |
#4739 Wearables sort order reliability
| -rw-r--r-- | indra/newview/llagentwearables.cpp | 23 | ||||
| -rw-r--r-- | indra/newview/llagentwearables.h | 1 | ||||
| -rw-r--r-- | indra/newview/llappearancemgr.cpp | 63 | ||||
| -rw-r--r-- | indra/newview/llviewerinventory.cpp | 4 | 
4 files changed, 66 insertions, 25 deletions
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 25f5cbd78f..a075b6f004 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -538,6 +538,27 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp      return item;  } +const S32 LLAgentWearables::getWearableIdxFromItem(const LLViewerInventoryItem* item) const +{ +    if (!item) return -1; +    if (!item->isWearableType()) return -1; + +    LLWearableType::EType type = item->getWearableType(); +    U32 wearable_count = getWearableCount(type); +    if (0 == wearable_count) return -1; + +    const LLUUID& asset_id = item->getAssetUUID(); + +    for (U32 i = 0; i < wearable_count; ++i) +    { +        const LLViewerWearable* wearable = getViewerWearable(type, i); +        if (!wearable) continue; +        if (wearable->getAssetID() != asset_id) continue; +        return i; +    } + +    return -1; +}  const LLViewerWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const  {      const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); @@ -1471,7 +1492,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos      LLWearableType::EType type = item->getWearableType();      U32 wearable_count = getWearableCount(type); -    if (0 == wearable_count) return false; +    if (wearable_count < 2) return false;      const LLUUID& asset_id = item->getAssetUUID(); diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 3b8ff93c76..1e118ffa98 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -87,6 +87,7 @@ public:  public:      const LLUUID        getWearableItemID(LLWearableType::EType type, U32 index /*= 0*/) const;      const LLUUID        getWearableAssetID(LLWearableType::EType type, U32 index /*= 0*/) const; +    const S32 getWearableIdxFromItem(const LLViewerInventoryItem* item) const;      const LLViewerWearable* getWearableFromItemID(const LLUUID& item_id) const;      LLViewerWearable*   getWearableFromItemID(const LLUUID& item_id);      LLViewerWearable*   getWearableFromAssetID(const LLUUID& asset_id); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6fa23727b1..caf3990801 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -4218,37 +4218,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); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 6155058f14..ec5381ddfc 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -419,7 +419,9 @@ void LLViewerInventoryItem::updateServer(bool is_new) const                           << LL_ENDL;          return;      } -    if(gAgent.getID() != mPermissions.getOwner()) +    LLUUID owner = mPermissions.getOwner(); +    if(gAgent.getID() != owner +        && owner.notNull()) // incomplete?      {          // *FIX: deal with this better.          LL_WARNS(LOG_INV) << "LLViewerInventoryItem::updateServer() - for unowned item "  | 
