From db3e5afbfbbce73e8f8339e8b4f938f11023ca64 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 2 May 2024 23:10:51 +0300 Subject: Viewer#1301 Implement Inventory Favorites Tab --- indra/newview/llviewerinventory.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/llviewerinventory.cpp') diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b9a7c9448f..3a4ac96826 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1436,7 +1436,8 @@ void update_inventory_category( if(obj) { if (LLFolderType::lookupIsProtectedType(obj->getPreferredType()) - && (updates.size() != 1 || !updates.has("thumbnail"))) + && (updates.size() != 1 + || !(updates.has("thumbnail") || updates.has("favorite")))) { LLNotificationsUtil::add("CannotModifyProtectedCategories"); return; -- cgit v1.3 From fece367b9b6b351d112f9036ce445c52397c9b03 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 14 Jun 2024 18:27:20 +0300 Subject: jira-archive-internal#63268 Several objects are worn on the same part of the body by double-clicking on the object from the "Library" folder SL-13411 --- indra/newview/llinventorybridge.cpp | 2 +- indra/newview/llinventorybridge.h | 2 +- indra/newview/lltooldraganddrop.cpp | 4 ++-- indra/newview/llviewerattachmenu.cpp | 4 ++-- indra/newview/llviewerinventory.cpp | 4 ++-- indra/newview/llviewerinventory.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) (limited to 'indra/newview/llviewerinventory.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b4a3054d07..e4828b14cf 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -6880,7 +6880,7 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) else if(item && item->isFinished()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, true)); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index b0139e225e..460d501256 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -778,7 +778,7 @@ private: void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment, - bool replace = false); + bool replace); // Move items from an in-world object's "Contents" folder to a specified // folder in agent inventory. diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index abf9748db3..001530895d 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -2132,7 +2132,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( { if(mSource == SOURCE_LIBRARY) { - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, (LLViewerJointAttachment*)0, false)); copy_inventory_item( gAgent.getID(), item->getPermissions().getOwner(), @@ -2143,7 +2143,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( } else { - rez_attachment(item, 0); + rez_attachment(item, 0, false); } } return ACCEPT_YES_SINGLE; diff --git a/indra/newview/llviewerattachmenu.cpp b/indra/newview/llviewerattachmenu.cpp index f2557e299c..9828ab1fdf 100644 --- a/indra/newview/llviewerattachmenu.cpp +++ b/indra/newview/llviewerattachmenu.cpp @@ -116,12 +116,12 @@ void LLViewerAttachMenu::attachObjects(const uuid_vec_t& items, const std::strin LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getLinkedItem(id); if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) { - rez_attachment(item, attachmentp); // don't replace if called from an "Attach To..." menu + rez_attachment(item, attachmentp, false); // don't replace if called from an "Attach To..." menu } else if(item && item->isFinished()) { // must be in library. copy it to our inventory and put it on. - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, attachmentp)); + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(rez_attachment_cb, _1, attachmentp, false)); copy_inventory_item(gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 16810efa01..2091a92d5d 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -966,7 +966,7 @@ void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id) } } -void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp) +void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace) { if (inv_item.isNull()) return; @@ -974,7 +974,7 @@ void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachme LLViewerInventoryItem *item = gInventory.getItem(inv_item); if (item) { - rez_attachment(item, attachmentp); + rez_attachment(item, attachmentp, replace); } } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 158965fc88..7138bf2a95 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -264,7 +264,7 @@ public: class LLViewerJointAttachment; -void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp); +void rez_attachment_cb(const LLUUID& inv_item, LLViewerJointAttachment *attachmentp, bool replace); void activate_gesture_cb(const LLUUID& inv_item); -- cgit v1.3 From f73e0d09aabd17f778ce802353dea5c5aae007be Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 30 Apr 2025 23:45:22 +0300 Subject: #3905 Crashes in LLInventoryModel::saveToFile I don't expect it to fix the problem. Just making things more explicit in places of most frequent crashes. --- indra/llinventory/llinventory.cpp | 24 ++++++++++++------------ indra/llinventory/llinventory.h | 3 ++- indra/llinventory/llpermissions.cpp | 16 ++++++++++------ indra/llinventory/llpermissions.h | 1 + indra/llinventory/llsaleinfo.cpp | 9 +++++++-- indra/llinventory/llsaleinfo.h | 1 + indra/llinventory/tests/inventorymisc_test.cpp | 8 ++++++-- indra/newview/llinventorymodel.cpp | 8 ++++++-- indra/newview/llviewerinventory.cpp | 6 ++---- indra/newview/llviewerinventory.h | 4 ++-- 10 files changed, 49 insertions(+), 31 deletions(-) (limited to 'indra/newview/llviewerinventory.cpp') diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 5bf8020a68..fe60800700 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -928,7 +928,7 @@ bool LLInventoryItem::exportLegacyStream(std::ostream& output_stream, bool inclu LLSD LLInventoryItem::asLLSD() const { - LLSD sd = LLSD(); + LLSD sd; asLLSD(sd); return sd; } @@ -937,7 +937,7 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const { sd[INV_ITEM_ID_LABEL] = mUUID; sd[INV_PARENT_ID_LABEL] = mParentUUID; - sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions); + ll_fill_sd_from_permissions(sd[INV_PERMISSIONS_LABEL], mPermissions); if (mThumbnailUUID.notNull()) { @@ -963,19 +963,22 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const cipher.encrypt(shadow_id.mData, UUID_BYTES); sd[INV_SHADOW_ID_LABEL] = shadow_id; } - sd[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); - sd[INV_INVENTORY_TYPE_LABEL] = mInventoryType; + sd[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType)); const std::string inv_type_str = LLInventoryType::lookup(mInventoryType); if(!inv_type_str.empty()) { sd[INV_INVENTORY_TYPE_LABEL] = inv_type_str; } + else + { + sd[INV_INVENTORY_TYPE_LABEL] = (LLSD::Integer)mInventoryType; + } //sd[INV_FLAGS_LABEL] = (S32)mFlags; sd[INV_FLAGS_LABEL] = ll_sd_from_U32(mFlags); - sd[INV_SALE_INFO_LABEL] = mSaleInfo.asLLSD(); + mSaleInfo.asLLSD(sd[INV_SALE_INFO_LABEL]); sd[INV_NAME_LABEL] = mName; sd[INV_DESC_LABEL] = mDescription; - sd[INV_CREATION_DATE_LABEL] = (S32) mCreationDate; + sd[INV_CREATION_DATE_LABEL] = (LLSD::Integer)mCreationDate; } bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) @@ -1501,12 +1504,11 @@ bool LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, bool) return true; } -LLSD LLInventoryCategory::exportLLSD() const +void LLInventoryCategory::exportLLSD(LLSD& cat_data) const { - LLSD cat_data; cat_data[INV_FOLDER_ID_LABEL] = mUUID; cat_data[INV_PARENT_ID_LABEL] = mParentUUID; - cat_data[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType); + cat_data[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType)); cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType); cat_data[INV_NAME_LABEL] = mName; @@ -1518,8 +1520,6 @@ LLSD LLInventoryCategory::exportLLSD() const { cat_data[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite); } - - return cat_data; } bool LLInventoryCategory::importLLSD(const LLSD& cat_data) @@ -1570,7 +1570,7 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data) return true; } ///---------------------------------------------------------------------------- -/// Local function definitions +/// Local function definitions for testing purposes ///---------------------------------------------------------------------------- LLSD ll_create_sd_from_inventory_item(LLPointer item) diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 2044b0102c..17670d2ea1 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -273,7 +273,7 @@ public: virtual bool importLegacyStream(std::istream& input_stream); virtual bool exportLegacyStream(std::ostream& output_stream, bool include_asset_key = true) const; - LLSD exportLLSD() const; + virtual void exportLLSD(LLSD& sd) const; bool importLLSD(const LLSD& cat_data); //-------------------------------------------------------------------- // Member Variables @@ -288,6 +288,7 @@ protected: // // These functions convert between structured data and an inventory // item, appropriate for serialization. +// Not up to date (no favorites, nor thumbnails), for testing purposes //----------------------------------------------------------------------------- LLSD ll_create_sd_from_inventory_item(LLPointer item); LLSD ll_create_sd_from_inventory_category(LLPointer cat); diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index c8963881df..d800ca02c9 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -1012,17 +1012,21 @@ static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); LLSD ll_create_sd_from_permissions(const LLPermissions& perm) { LLSD rv; + ll_fill_sd_from_permissions(rv, perm); + return rv; +} +void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm) +{ rv[PERM_CREATOR_ID_LABEL] = perm.getCreator(); rv[PERM_OWNER_ID_LABEL] = perm.getOwner(); rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner(); rv[PERM_GROUP_ID_LABEL] = perm.getGroup(); rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned(); - rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase(); - rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner(); - rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup(); - rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone(); - rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner(); - return rv; + rv[PERM_BASE_MASK_LABEL] = (LLSD::Integer)perm.getMaskBase(); + rv[PERM_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskOwner(); + rv[PERM_GROUP_MASK_LABEL] = (LLSD::Integer)perm.getMaskGroup(); + rv[PERM_EVERYONE_MASK_LABEL] = (LLSD::Integer)perm.getMaskEveryone(); + rv[PERM_NEXT_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskNextOwner(); } LLPermissions ll_permissions_from_sd(const LLSD& sd_perm) diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index a68abcfa34..f3e10af25c 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -435,6 +435,7 @@ protected: // like 'creator_id', 'owner_id', etc, with the value copied from the // permission object. LLSD ll_create_sd_from_permissions(const LLPermissions& perm); +void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm); LLPermissions ll_permissions_from_sd(const LLSD& sd_perm); #endif diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 35bbc1dbb1..b4d64bb4fb 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -90,15 +90,20 @@ bool LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const LLSD LLSaleInfo::asLLSD() const { LLSD sd; + asLLSD(sd); + return sd; +} + +void LLSaleInfo::asLLSD(LLSD& sd) const +{ const char* type = lookup(mSaleType); if (!type) { LL_WARNS_ONCE() << "Unknown sale type: " << mSaleType << LL_ENDL; type = lookup(LLSaleInfo::FS_NOT); } - sd["sale_type"] = type; + sd["sale_type"] = std::string(type); sd["sale_price"] = mSalePrice; - return sd; } bool LLSaleInfo::fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask) diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h index 44eb841641..7186e8ab49 100644 --- a/indra/llinventory/llsaleinfo.h +++ b/indra/llinventory/llsaleinfo.h @@ -86,6 +86,7 @@ public: bool exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; + void asLLSD(LLSD &sd) const; operator LLSD() const { return asLLSD(); } bool fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask); bool importLegacyStream(std::istream& input_stream, bool& has_perm_mask, U32& perm_mask); diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index 9779cb8fbc..ee01f0e428 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -329,7 +329,9 @@ namespace tut } LLPointer src1 = create_random_inventory_item(); - fileXML << LLSDOStreamer(src1->asLLSD()) << std::endl; + LLSD sd; + src1->asLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; fileXML.close(); @@ -458,7 +460,9 @@ namespace tut } LLPointer src1 = create_random_inventory_cat(); - fileXML << LLSDOStreamer(src1->exportLLSD()) << std::endl; + LLSD sd; + src1->exportLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; fileXML.close(); llifstream file(filename.c_str()); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 0b2615c706..c7cc8f3032 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -3500,7 +3500,9 @@ bool LLInventoryModel::saveToFile(const std::string& filename, { if (cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { - fileXML << LLSDOStreamer(cat->exportLLSD()) << std::endl; + LLSD sd = LLSD::emptyMap(); + cat->exportLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; cat_count++; } @@ -3514,7 +3516,9 @@ bool LLInventoryModel::saveToFile(const std::string& filename, auto it_count = items.size(); for (auto& item : items) { - fileXML << LLSDOStreamer(item->asLLSD()) << std::endl; + LLSD sd = LLSD::emptyMap(); + item->asLLSD(sd); + fileXML << LLSDOStreamer(sd) << std::endl; if (fileXML.fail()) { diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 36ec6f0d78..8caa0144c3 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -751,13 +751,11 @@ S32 LLViewerInventoryCategory::getViewerDescendentCount() const return descendents_actual; } -LLSD LLViewerInventoryCategory::exportLLSD() const +void LLViewerInventoryCategory::exportLLSD(LLSD & cat_data) const { - LLSD cat_data = LLInventoryCategory::exportLLSD(); + LLInventoryCategory::exportLLSD(cat_data); cat_data[INV_OWNER_ID] = mOwnerID; cat_data[INV_VERSION] = mVersion; - - return cat_data; } bool LLViewerInventoryCategory::importLLSD(const LLSD& cat_data) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 18daa368d9..5cd31353f8 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -232,8 +232,8 @@ public: // How many descendents do we currently have information for in the InventoryModel? S32 getViewerDescendentCount() const; - LLSD exportLLSD() const; - bool importLLSD(const LLSD& cat_data); + virtual void exportLLSD(LLSD &sd) const; + virtual bool importLLSD(const LLSD& cat_data); void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); -- cgit v1.3 From 2cbff073edad0bb31d2a025b888a9fb2e90fe2cf Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 20 May 2025 20:40:04 +0300 Subject: #4000 Cherry pick inventory LEAP functions from develop branch --- indra/llcommon/llassettype.cpp | 17 ++ indra/llcommon/llassettype.h | 2 + indra/llcommon/llsdutil.h | 38 +++++ indra/llinventory/llfoldertype.cpp | 19 +++ indra/llinventory/llfoldertype.h | 2 + indra/newview/CMakeLists.txt | 2 + indra/newview/llinventoryfunctions.h | 4 +- indra/newview/llinventorylistener.cpp | 309 ++++++++++++++++++++++++++++++++++ indra/newview/llinventorylistener.h | 48 ++++++ indra/newview/llinventorymodel.cpp | 8 + indra/newview/llviewerinventory.cpp | 3 + 11 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 indra/newview/llinventorylistener.cpp create mode 100644 indra/newview/llinventorylistener.h (limited to 'indra/newview/llviewerinventory.cpp') diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index c09cf7abd2..9672a3262b 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -29,6 +29,7 @@ #include "llassettype.h" #include "lldictionary.h" #include "llmemory.h" +#include "llsd.h" #include "llsingleton.h" ///---------------------------------------------------------------------------- @@ -246,3 +247,19 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) } return false; } + +LLSD LLAssetType::getTypeNames() +{ + LLSD type_names; + const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type) + { + const AssetEntry *entry = dict->lookup((LLAssetType::EType) type); + // skip llassettype_bad_lookup + if (entry) + { + type_names.append(entry->mTypeName); + } + } + return type_names; +} diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 547c3f4329..17177d81c3 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -165,6 +165,8 @@ public: static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer + static LLSD getTypeNames(); + static const std::string BADLOOKUP; protected: diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 38bbe19ddd..4d65059fe6 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -553,6 +553,44 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter } // namespace llsd +/***************************************************************************** +* LLSDParam> +*****************************************************************************/ +// Given an LLSD array, return a const std::vector&, where T is a type +// supported by LLSDParam. Bonus: if the LLSD value is actually a scalar, +// return a single-element vector containing the converted value. +template +class LLSDParam>: public LLSDParamBase +{ +public: + LLSDParam(const LLSD& array) + { + // treat undefined "array" as empty vector + if (array.isDefined()) + { + // what if it's a scalar? + if (! array.isArray()) + { + v.push_back(LLSDParam(array)); + } + else // really is an array + { + // reserve space for the array entries + v.reserve(array.size()); + for (const auto& item : llsd::inArray(array)) + { + v.push_back(LLSDParam(item)); + } + } + } + } + + operator const std::vector&() const { return v; } + +private: + std::vector v; +}; + // Specialization for generating a hash value from an LLSD block. namespace boost { diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index 7e1be17ecc..670405e9b5 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -29,6 +29,7 @@ #include "llfoldertype.h" #include "lldictionary.h" #include "llmemory.h" +#include "llsd.h" #include "llsingleton.h" ///---------------------------------------------------------------------------- @@ -220,3 +221,21 @@ const std::string &LLFolderType::badLookup() static const std::string sBadLookup = "llfoldertype_bad_lookup"; return sBadLookup; } + +LLSD LLFolderType::getTypeNames() +{ + LLSD type_names; + for (S32 type = FT_TEXTURE; type < FT_COUNT; ++type) + { + if (lookupIsEnsembleType((LLFolderType::EType)type)) + continue; + + const FolderEntry* entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type); + // skip llfoldertype_bad_lookup + if (entry) + { + type_names.append(entry->mName); + } + } + return type_names; +} diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h index 46a1b92a96..dd12693f66 100644 --- a/indra/llinventory/llfoldertype.h +++ b/indra/llinventory/llfoldertype.h @@ -115,6 +115,8 @@ public: static const std::string& badLookup(); // error string when a lookup fails + static LLSD getTypeNames(); + protected: LLFolderType() {} ~LLFolderType() {} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 98a1d7f0fd..9290bc0ffa 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -362,6 +362,7 @@ set(viewer_SOURCE_FILES llinventorygallerymenu.cpp llinventoryicon.cpp llinventoryitemslist.cpp + llinventorylistener.cpp llinventorylistitem.cpp llinventorymodel.cpp llinventorymodelbackgroundfetch.cpp @@ -1033,6 +1034,7 @@ set(viewer_HEADER_FILES llinventorygallerymenu.h llinventoryicon.h llinventoryitemslist.h + llinventorylistener.h llinventorylistitem.h llinventorymodel.h llinventorymodelbackgroundfetch.h diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index a668cc31d8..b40fc051a0 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -192,7 +192,9 @@ class LLInventoryCollectFunctor { public: virtual ~LLInventoryCollectFunctor(){}; - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; + + virtual bool exceedsLimit() { return false; } static bool itemTransferCommonlyAllowed(const LLInventoryItem* item); }; diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp new file mode 100644 index 0000000000..028483e134 --- /dev/null +++ b/indra/newview/llinventorylistener.cpp @@ -0,0 +1,309 @@ +/** + * @file llinventorylistener.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventorylistener.h" + +#include "llappearancemgr.h" +#include "llinventoryfunctions.h" +#include "lltransutil.h" +#include "llwearableitemslist.h" +#include "stringize.h" + +LLInventoryListener::LLInventoryListener() + : LLEventAPI("LLInventory", + "API for interactions with viewer Inventory items") +{ + add("getItemsInfo", + "Return information about items or folders defined in [\"item_ids\"]:\n" + "reply will contain [\"items\"] and [\"categories\"] result set keys", + &LLInventoryListener::getItemsInfo, + llsd::map("item_ids", LLSD(), "reply", LLSD())); + + add("getFolderTypeNames", + "Return the table of folder type names, contained in [\"names\"]\n", + &LLInventoryListener::getFolderTypeNames, + llsd::map("reply", LLSD())); + + add("getAssetTypeNames", + "Return the table of asset type names, contained in [\"names\"]\n", + &LLInventoryListener::getAssetTypeNames, + llsd::map("reply", LLSD())); + + add("getBasicFolderID", + "Return the UUID of the folder by specified folder type name, for example:\n" + "\"Textures\", \"My outfits\", \"Sounds\" and other basic folders which have associated type", + &LLInventoryListener::getBasicFolderID, + llsd::map("ft_name", LLSD(), "reply", LLSD())); + + add("getDirectDescendants", + "Return result set keys [\"categories\"] and [\"items\"] for the direct\n" + "descendants of the [\"folder_id\"]", + &LLInventoryListener::getDirectDescendants, + llsd::map("folder_id", LLSD(), "reply", LLSD())); + + add("collectDescendantsIf", + "Return result set keys [\"categories\"] and [\"items\"] for the descendants\n" + "of the [\"folder_id\"], if it passes specified filters:\n" + "[\"name\"] is a substring of object's name,\n" + "[\"desc\"] is a substring of object's description,\n" + "asset [\"type\"] corresponds to the string name of the object's asset type\n" + "[\"limit\"] sets item count limit in result set (default unlimited)\n" + "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", + &LLInventoryListener::collectDescendantsIf, + llsd::map("folder_id", LLSD(), "reply", LLSD())); +} + +void add_cat_info(LLEventAPI::Response& response, LLViewerInventoryCategory* cat) +{ + response["categories"].insert(cat->getUUID().asString(), + llsd::map("id", cat->getUUID(), + "name", cat->getName(), + "parent_id", cat->getParentUUID(), + "type", LLFolderType::lookup(cat->getPreferredType()))); + +}; + +void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) +{ + response["items"].insert(item->getUUID().asString(), + llsd::map("id", item->getUUID(), + "name", item->getName(), + "parent_id", item->getParentUUID(), + "desc", item->getDescription(), + "inv_type", LLInventoryType::lookup(item->getInventoryType()), + "asset_type", LLAssetType::lookup(item->getType()), + "creation_date", LLSD::Integer(item->getCreationDate()), + "asset_id", item->getAssetUUID(), + "is_link", item->getIsLinkType(), + "linked_id", item->getLinkedUUID())); +} + +void add_objects_info(LLEventAPI::Response& response, LLInventoryModel::cat_array_t cat_array, LLInventoryModel::item_array_t item_array) +{ + for (auto& p : item_array) + { + add_item_info(response, p); + } + for (auto& p : cat_array) + { + add_cat_info(response, p); + } +} + +void LLInventoryListener::getItemsInfo(LLSD const &data) +{ + Response response(LLSD(), data); + uuid_vec_t ids = LLSDParam(data["item_ids"]); + for (auto &it : ids) + { + LLViewerInventoryItem* item = gInventory.getItem(it); + if (item) + { + add_item_info(response, item); + } + else + { + LLViewerInventoryCategory *cat = gInventory.getCategory(it); + if (cat) + { + add_cat_info(response, cat); + } + } + } +} + +void LLInventoryListener::getFolderTypeNames(LLSD const &data) +{ + Response response(llsd::map("names", LLFolderType::getTypeNames()), data); +} + +void LLInventoryListener::getAssetTypeNames(LLSD const &data) +{ + Response response(llsd::map("names", LLAssetType::getTypeNames()), data); +} + +void LLInventoryListener::getBasicFolderID(LLSD const &data) +{ + Response response(llsd::map("id", gInventory.findCategoryUUIDForType(LLFolderType::lookup(data["ft_name"].asString()))), data); +} + + +void LLInventoryListener::getDirectDescendants(LLSD const &data) +{ + Response response(LLSD(), data); + LLUUID folder_id(data["folder_id"].asUUID()); + LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (!cat) + { + return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); + } + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(folder_id, cats, items); + + add_objects_info(response, *cats, *items); +} + +struct LLFilteredCollector : public LLInventoryCollectFunctor +{ + enum EFilterLink + { + INCLUDE_LINKS, // show links too + EXCLUDE_LINKS, // don't show links + ONLY_LINKS // only show links + }; + + LLFilteredCollector(LLSD const &data); + virtual ~LLFilteredCollector() {} + virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item) override; + virtual bool exceedsLimit() override + { + // mItemLimit == 0 means unlimited + return (mItemLimit && mItemLimit <= mItemCount); + } + + protected: + bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); + bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); + + LLAssetType::EType mType; + std::string mName; + std::string mDesc; + EFilterLink mLinkFilter; + + S32 mItemLimit; + S32 mItemCount; +}; + +void LLInventoryListener::collectDescendantsIf(LLSD const &data) +{ + Response response(LLSD(), data); + LLUUID folder_id(data["folder_id"].asUUID()); + LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); + if (!cat) + { + return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); + } + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + LLFilteredCollector collector = LLFilteredCollector(data); + + gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); + + add_objects_info(response, cat_array, item_array); +} + +LLFilteredCollector::LLFilteredCollector(LLSD const &data) : + mType(LLAssetType::EType::AT_UNKNOWN), + mLinkFilter(INCLUDE_LINKS), + mItemLimit(0), + mItemCount(0) +{ + + mName = data["name"].asString(); + mDesc = data["desc"].asString(); + + if (data.has("type")) + { + mType = LLAssetType::lookup(data["type"]); + } + if (data.has("filter_links")) + { + if (data["filter_links"] == "EXCLUDE_LINKS") + { + mLinkFilter = EXCLUDE_LINKS; + } + else if (data["filter_links"] == "ONLY_LINKS") + { + mLinkFilter = ONLY_LINKS; + } + } + if (data["limit"].isInteger()) + { + mItemLimit = std::max(data["limit"].asInteger(), 1); + } +} + +bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool passed = checkagainstType(cat, item); + passed = passed && checkagainstNameDesc(cat, item); + passed = passed && checkagainstLinks(cat, item); + + if (passed) + { + ++mItemCount; + } + return passed; +} + +bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) +{ + std::string name, desc; + bool passed(true); + if (cat) + { + if (!mDesc.empty()) return false; + name = cat->getName(); + } + if (item) + { + name = item->getName(); + passed = (mDesc.empty() || (item->getDescription().find(mDesc) != std::string::npos)); + } + + return passed && (mName.empty() || name.find(mName) != std::string::npos); +} + +bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item) +{ + if (mType == LLAssetType::AT_UNKNOWN) + { + return true; + } + if (cat && (mType == LLAssetType::AT_CATEGORY)) + { + return true; + } + if (item && item->getType() == mType) + { + return true; + } + return false; +} + +bool LLFilteredCollector::checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item) +{ + bool is_link = cat ? cat->getIsLinkType() : item->getIsLinkType(); + if (is_link && (mLinkFilter == EXCLUDE_LINKS)) + return false; + if (!is_link && (mLinkFilter == ONLY_LINKS)) + return false; + return true; +} diff --git a/indra/newview/llinventorylistener.h b/indra/newview/llinventorylistener.h new file mode 100644 index 0000000000..d50397730c --- /dev/null +++ b/indra/newview/llinventorylistener.h @@ -0,0 +1,48 @@ +/** + * @file llinventorylistener.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLINVENTORYLISTENER_H +#define LL_LLINVENTORYLISTENER_H + +#include "lleventapi.h" +#include "llinventoryfunctions.h" + +class LLInventoryListener : public LLEventAPI +{ +public: + LLInventoryListener(); + +private: + void getItemsInfo(LLSD const &data); + void getFolderTypeNames(LLSD const &data); + void getAssetTypeNames(LLSD const &data); + void getBasicFolderID(LLSD const &data); + void getDirectDescendants(LLSD const &data); + void collectDescendantsIf(LLSD const &data); +}; + +#endif // LL_LLINVENTORYLISTENER_H + diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c7cc8f3032..110957dfa7 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1282,6 +1282,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, { for (auto& cat : *cat_array) { + if (add.exceedsLimit()) + { + break; + } if(add(cat,NULL)) { cats.push_back(cat); @@ -1297,6 +1301,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, { for (auto& item : *item_array) { + if (add.exceedsLimit()) + { + break; + } if(add(NULL, item)) { items.push_back(item); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 8caa0144c3..5f61aeaf13 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -71,6 +71,9 @@ #include "llclipboard.h" #include "llhttpretrypolicy.h" #include "llsettingsvo.h" +#include "llinventorylistener.h" + +LLInventoryListener sInventoryListener; // do-nothing ops for use in callbacks. void no_op_inventory_func(const LLUUID&) {} -- cgit v1.3 From 3b8b408b9031f295a936eb5e3342fbb7eb671c7c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 25 Jul 2025 19:06:06 +0300 Subject: #3969 Make inventory creation from cache faster Still has a lot of space for improvements, but should be 2.5 times faster --- indra/llinventory/llinventory.cpp | 60 +++++---- indra/llinventory/llinventory.h | 3 +- indra/llinventory/llpermissions.cpp | 104 ++++++++++----- indra/llinventory/llpermissions.h | 2 + indra/llinventory/tests/inventorymisc_test.cpp | 2 +- indra/newview/llinventorymodel.cpp | 172 +++++++++++++------------ indra/newview/llviewerinventory.cpp | 19 ++- indra/newview/llviewerinventory.h | 2 +- 8 files changed, 220 insertions(+), 144 deletions(-) (limited to 'indra/newview/llviewerinventory.cpp') diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index fe60800700..3defad8f3b 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -995,6 +995,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) // TODO - figure out if this should be moved into the noclobber fields above mThumbnailUUID.setNull(); mFavorite = false; + mPermissions.init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); // iterate as map to avoid making unnecessary temp copies of everything LLSD::map_const_iterator i, end; @@ -1053,7 +1054,7 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) if (i->first == INV_PERMISSIONS_LABEL) { - mPermissions = ll_permissions_from_sd(i->second); + mPermissions.importLLSD(i->second); continue; } @@ -1522,53 +1523,68 @@ void LLInventoryCategory::exportLLSD(LLSD& cat_data) const } } -bool LLInventoryCategory::importLLSD(const LLSD& cat_data) +bool LLInventoryCategory::importLLSDMap(const LLSD& cat_data) { - if (cat_data.has(INV_FOLDER_ID_LABEL)) + LLSD::map_const_iterator i, end; + end = cat_data.endMap(); + for ( i = cat_data.beginMap(); i != end; ++i) + { + importLLSD(i->first, i->second); + } + return true; +} + +bool LLInventoryCategory::importLLSD(const std::string& label, const LLSD& value) +{ + if (label == INV_FOLDER_ID_LABEL) { - setUUID(cat_data[INV_FOLDER_ID_LABEL].asUUID()); + setUUID(value.asUUID()); + return true; } - if (cat_data.has(INV_PARENT_ID_LABEL)) + else if (label == INV_PARENT_ID_LABEL) { - setParent(cat_data[INV_PARENT_ID_LABEL].asUUID()); + setParent(value.asUUID()); + return true; } - if (cat_data.has(INV_ASSET_TYPE_LABEL)) + else if (label == INV_ASSET_TYPE_LABEL) { - setType(LLAssetType::lookup(cat_data[INV_ASSET_TYPE_LABEL].asString())); + setType(LLAssetType::lookup(value.asString())); + return true; } - if (cat_data.has(INV_PREFERRED_TYPE_LABEL)) + else if (label == INV_PREFERRED_TYPE_LABEL) { - setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString())); + setPreferredType(LLFolderType::lookup(value.asString())); + return true; } - if (cat_data.has(INV_THUMBNAIL_LABEL)) + else if (label == INV_THUMBNAIL_LABEL) { LLUUID thumbnail_uuid; - const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL]; - if (thumbnail_data.has(INV_ASSET_ID_LABEL)) + if (value.has(INV_ASSET_ID_LABEL)) { - thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID(); + thumbnail_uuid = value[INV_ASSET_ID_LABEL].asUUID(); } setThumbnailUUID(thumbnail_uuid); + return true; } - if (cat_data.has(INV_FAVORITE_LABEL)) + if (label == INV_FAVORITE_LABEL) { bool favorite = false; - const LLSD& favorite_data = cat_data[INV_FAVORITE_LABEL]; - if (favorite_data.has(INV_TOGGLED_LABEL)) + if (value.has(INV_TOGGLED_LABEL)) { - favorite = favorite_data[INV_TOGGLED_LABEL].asBoolean(); + favorite = value[INV_TOGGLED_LABEL].asBoolean(); } setFavorite(favorite); } - if (cat_data.has(INV_NAME_LABEL)) + else if (label == INV_NAME_LABEL) { - mName = cat_data[INV_NAME_LABEL].asString(); + mName = value.asString(); LLStringUtil::replaceNonstandardASCII(mName, ' '); LLStringUtil::replaceChar(mName, '|', ' '); + return true; } - - return true; + return false; } + ///---------------------------------------------------------------------------- /// Local function definitions for testing purposes ///---------------------------------------------------------------------------- diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 17670d2ea1..181c46226c 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -274,7 +274,8 @@ public: virtual bool exportLegacyStream(std::ostream& output_stream, bool include_asset_key = true) const; virtual void exportLLSD(LLSD& sd) const; - bool importLLSD(const LLSD& cat_data); + bool importLLSDMap(const LLSD& cat_data); + virtual bool importLLSD(const std::string& label, const LLSD& value); //-------------------------------------------------------------------- // Member Variables //-------------------------------------------------------------------- diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index d800ca02c9..ebf7445c65 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -704,6 +704,79 @@ bool LLPermissions::exportLegacyStream(std::ostream& output_stream) const return true; } +static const std::string PERM_CREATOR_ID_LABEL("creator_id"); +static const std::string PERM_OWNER_ID_LABEL("owner_id"); +static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id"); +static const std::string PERM_GROUP_ID_LABEL("group_id"); +static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group"); +static const std::string PERM_BASE_MASK_LABEL("base_mask"); +static const std::string PERM_OWNER_MASK_LABEL("owner_mask"); +static const std::string PERM_GROUP_MASK_LABEL("group_mask"); +static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask"); +static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); + +void LLPermissions::importLLSD(const LLSD& sd_perm) +{ + LLSD::map_const_iterator i, end; + end = sd_perm.endMap(); + for (i = sd_perm.beginMap(); i != end; ++i) + { + const std::string& label = i->first; + if (label == PERM_CREATOR_ID_LABEL) + { + mCreator = i->second.asUUID(); + continue; + } + if (label == PERM_OWNER_ID_LABEL) + { + mOwner = i->second.asUUID(); + continue; + } + if (label == PERM_LAST_OWNER_ID_LABEL) + { + mLastOwner = i->second.asUUID(); + continue; + } + if (label == PERM_GROUP_ID_LABEL) + { + mGroup = i->second.asUUID(); + continue; + } + if (label == PERM_BASE_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskBase = mask; + continue; + } + if (label == PERM_OWNER_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskOwner = mask; + continue; + } + if (label == PERM_EVERYONE_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskEveryone = mask; + continue; + } + if (label == PERM_GROUP_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskGroup = mask; + continue; + } + if (label == PERM_NEXT_OWNER_MASK_LABEL) + { + PermissionMask mask = i->second.asInteger(); + mMaskNextOwner = mask; + continue; + } + } + + fix(); +} + bool LLPermissions::operator==(const LLPermissions &rhs) const { return @@ -998,16 +1071,6 @@ std::string mask_to_string(U32 mask) ///---------------------------------------------------------------------------- /// exported functions ///---------------------------------------------------------------------------- -static const std::string PERM_CREATOR_ID_LABEL("creator_id"); -static const std::string PERM_OWNER_ID_LABEL("owner_id"); -static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id"); -static const std::string PERM_GROUP_ID_LABEL("group_id"); -static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group"); -static const std::string PERM_BASE_MASK_LABEL("base_mask"); -static const std::string PERM_OWNER_MASK_LABEL("owner_mask"); -static const std::string PERM_GROUP_MASK_LABEL("group_mask"); -static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask"); -static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); LLSD ll_create_sd_from_permissions(const LLPermissions& perm) { @@ -1032,25 +1095,6 @@ void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm) LLPermissions ll_permissions_from_sd(const LLSD& sd_perm) { LLPermissions rv; - rv.init( - sd_perm[PERM_CREATOR_ID_LABEL].asUUID(), - sd_perm[PERM_OWNER_ID_LABEL].asUUID(), - sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(), - sd_perm[PERM_GROUP_ID_LABEL].asUUID()); - - // We do a cast to U32 here since LLSD does not attempt to - // represent unsigned ints. - PermissionMask mask; - mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger()); - rv.setMaskBase(mask); - mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger()); - rv.setMaskOwner(mask); - mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger()); - rv.setMaskEveryone(mask); - mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger()); - rv.setMaskGroup(mask); - mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger()); - rv.setMaskNext(mask); - rv.fix(); + rv.importLLSD(sd_perm); return rv; } diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index f3e10af25c..82cdc03727 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -299,6 +299,8 @@ public: bool importLegacyStream(std::istream& input_stream); bool exportLegacyStream(std::ostream& output_stream) const; + void importLLSD(const LLSD& sd_perm); + bool operator==(const LLPermissions &rhs) const; bool operator!=(const LLPermissions &rhs) const; diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index e41500b4c5..f11a4c3bf7 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -518,7 +518,7 @@ namespace tut file.close(); LLPointer src2 = new LLInventoryCategory(); - src2->importLLSD(s_item); + src2->importLLSDMap(s_item); ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID()); ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID()); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 117f2d1adb..2dfc3b014f 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -78,7 +78,7 @@ // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect. -const S32 LLInventoryModel::sCurrentInvCacheVersion = 4; +const S32 LLInventoryModel::sCurrentInvCacheVersion = 5; bool LLInventoryModel::sFirstTimeInViewer2 = true; S32 LLInventoryModel::sPendingSystemFolders = 0; @@ -3384,7 +3384,7 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } LL_INFOS(LOG_INV) << "loading inventory from: (" << filename << ")" << LL_ENDL; - llifstream file(filename.c_str()); + llifstream file(filename.c_str(), std::ifstream::in | std::ifstream::binary); if (!file.is_open()) { @@ -3393,80 +3393,92 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, } is_cache_obsolete = true; // Obsolete until proven current - - //U64 lines_count = 0U; - std::string line; - LLPointer parser = new LLSDNotationParser(); - while (std::getline(file, line)) + U32 value_nbo = 0; + file.read((char*)&value_nbo, sizeof(U32)); + if (file.fail()) + { + LL_WARNS(LOG_INV) << "Failed to read cache version. Unable to load inventory from: " << filename << LL_ENDL; + } + else { - LLSD s_item; - std::istringstream iss(line); - if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + S32 version = (S32)ntohl(value_nbo); + if (version == sCurrentInvCacheVersion) { - LL_WARNS(LOG_INV)<< "Parsing inventory cache failed" << LL_ENDL; - break; + // Cache is up to date + is_cache_obsolete = false; } - - if (s_item.has("inv_cache_version")) + else { - S32 version = s_item["inv_cache_version"].asInteger(); - if (version == sCurrentInvCacheVersion) - { - // Cache is up to date - is_cache_obsolete = false; - continue; - } - else - { - LL_WARNS(LOG_INV)<< "Inventory cache is out of date" << LL_ENDL; - break; - } + LL_WARNS(LOG_INV) << "Inventory cache is out of date" << LL_ENDL; } - else if (s_item.has("cat_id")) + } + + LLSD inventory; + if (!is_cache_obsolete) + { + LLPointer parser = new LLSDBinaryParser(); + + if (parser->parse(file, inventory, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) { - if (is_cache_obsolete) - break; + is_cache_obsolete = true; + LL_WARNS(LOG_INV) << "Parsing inventory cache failed" << LL_ENDL; + } + } - LLPointer inv_cat = new LLViewerInventoryCategory(LLUUID::null); - if(inv_cat->importLLSD(s_item)) + if (!is_cache_obsolete) + { + const LLSD& llsd_cats = inventory["categories"]; + if (llsd_cats.isArray()) + { + LLSD::array_const_iterator iter = llsd_cats.beginArray(); + LLSD::array_const_iterator end = llsd_cats.endArray(); + for (; iter != end; ++iter) { - categories.push_back(inv_cat); + LLPointer inv_cat = new LLViewerInventoryCategory(LLUUID::null); + if (inv_cat->importLLSDMap(*iter)) + { + categories.push_back(inv_cat); + } } } - else if (s_item.has("item_id")) - { - if (is_cache_obsolete) - break; - LLPointer inv_item = new LLViewerInventoryItem; - if( inv_item->fromLLSD(s_item) ) + const LLSD& llsd_items = inventory["items"]; + if (llsd_items.isArray()) + { + LLSD::array_const_iterator iter = llsd_items.beginArray(); + LLSD::array_const_iterator end = llsd_items.endArray(); + for (; iter != end; ++iter) { - if(inv_item->getUUID().isNull()) - { - LL_DEBUGS(LOG_INV) << "Ignoring inventory with null item id: " - << inv_item->getName() << LL_ENDL; - } - else + LLPointer inv_item = new LLViewerInventoryItem; + if (inv_item->fromLLSD(*iter)) { - if (inv_item->getType() == LLAssetType::AT_UNKNOWN) + if (inv_item->getUUID().isNull()) { - cats_to_update.insert(inv_item->getParentUUID()); + LL_DEBUGS(LOG_INV) << "Ignoring inventory with null item id: " + << inv_item->getName() << LL_ENDL; } else { - items.push_back(inv_item); + if (inv_item->getType() == LLAssetType::AT_UNKNOWN) + { + cats_to_update.insert(inv_item->getParentUUID()); + } + else + { + items.push_back(inv_item); + } } } + + // TODO(brad) - figure out how to reenable this without breaking everything else + // static constexpr U64 BATCH_SIZE = 512U; + // if ((++lines_count % BATCH_SIZE) == 0) + // { + // // SL-19968 - make sure message system code gets a chance to run every so often + // pump_idle_startup_network(); + // } } } - -// TODO(brad) - figure out how to reenable this without breaking everything else -// static constexpr U64 BATCH_SIZE = 512U; -// if ((++lines_count % BATCH_SIZE) == 0) -// { -// // SL-19968 - make sure message system code gets a chance to run every so often -// pump_idle_startup_network(); -// } } file.close(); @@ -3489,58 +3501,54 @@ bool LLInventoryModel::saveToFile(const std::string& filename, try { - llofstream fileXML(filename.c_str()); - if (!fileXML.is_open()) + llofstream fileSD(filename.c_str(), std::ios_base::out | std::ios_base::binary); + if (!fileSD.is_open()) { LL_WARNS(LOG_INV) << "Failed to open file. Unable to save inventory to: " << filename << LL_ENDL; return false; } - - LLSD cache_ver; - cache_ver["inv_cache_version"] = sCurrentInvCacheVersion; - - if (fileXML.fail()) + U32 value_nbo = htonl(sCurrentInvCacheVersion); + fileSD.write((const char*)(&value_nbo), sizeof(U32)); + if (fileSD.fail()) { - LL_WARNS(LOG_INV) << "Failed to write cache version to file. Unable to save inventory to: " << filename << LL_ENDL; + LL_WARNS(LOG_INV) << "Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL; return false; } - fileXML << LLSDOStreamer(cache_ver) << std::endl; + LLSD inventory; + inventory["categories"] = LLSD::emptyArray(); + LLSD& cat_array = inventory["categories"]; S32 cat_count = 0; for (auto& cat : categories) { if (cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { - LLSD sd = LLSD::emptyMap(); + LLSD sd; cat->exportLLSD(sd); - fileXML << LLSDOStreamer(sd) << std::endl; + cat_array.append(sd); cat_count++; } - - if (fileXML.fail()) - { - LL_WARNS(LOG_INV) << "Failed to write a folder to file. Unable to save inventory to: " << filename << LL_ENDL; - return false; - } } + inventory["items"] = LLSD::emptyArray(); + LLSD& item_array = inventory["items"]; auto it_count = items.size(); for (auto& item : items) { - LLSD sd = LLSD::emptyMap(); + LLSD sd; item->asLLSD(sd); - fileXML << LLSDOStreamer(sd) << std::endl; - - if (fileXML.fail()) - { - LL_WARNS(LOG_INV) << "Failed to write an item to file. Unable to save inventory to: " << filename << LL_ENDL; - return false; - } + item_array.append(sd); + } + fileSD << LLSDOStreamer(inventory) << std::endl; + if (fileSD.fail()) + { + LL_WARNS(LOG_INV) << "Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL; + return false; } - fileXML.flush(); + fileSD.flush(); - fileXML.close(); + fileSD.close(); LL_INFOS(LOG_INV) << "Inventory saved: " << (S32)cat_count << " categories, " << (S32)it_count << " items." << LL_ENDL; } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 5f61aeaf13..6155058f14 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -761,18 +761,23 @@ void LLViewerInventoryCategory::exportLLSD(LLSD & cat_data) const cat_data[INV_VERSION] = mVersion; } -bool LLViewerInventoryCategory::importLLSD(const LLSD& cat_data) +bool LLViewerInventoryCategory::importLLSD(const std::string& label, const LLSD& value) { - LLInventoryCategory::importLLSD(cat_data); - if (cat_data.has(INV_OWNER_ID)) + if (LLInventoryCategory::importLLSD(label, value)) { - mOwnerID = cat_data[INV_OWNER_ID].asUUID(); + return true; } - if (cat_data.has(INV_VERSION)) + else if (label == INV_OWNER_ID) { - setVersion(cat_data[INV_VERSION].asInteger()); + mOwnerID = value.asUUID(); + return true; } - return true; + else if (label == INV_VERSION) + { + setVersion(value.asInteger()); + return true; + } + return false; } bool LLViewerInventoryCategory::acceptItem(LLInventoryItem* inv_item) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 5cd31353f8..0dfbf0cced 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -233,7 +233,7 @@ public: S32 getViewerDescendentCount() const; virtual void exportLLSD(LLSD &sd) const; - virtual bool importLLSD(const LLSD& cat_data); + virtual bool importLLSD(const std::string& label, const LLSD& value); void determineFolderType(); void changeType(LLFolderType::EType new_folder_type); -- cgit v1.3 From 7748898b25202b5c90f676bd8e3e8a258a307723 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:44:12 +0300 Subject: #4739 Wearables sort order reliability --- indra/newview/llagentwearables.cpp | 23 +++++++++++++- indra/newview/llagentwearables.h | 1 + indra/newview/llappearancemgr.cpp | 63 +++++++++++++++++++++++-------------- indra/newview/llviewerinventory.cpp | 4 ++- 4 files changed, 66 insertions(+), 25 deletions(-) (limited to 'indra/newview/llviewerinventory.cpp') 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 " -- cgit v1.3