2828
2929#include < typeinfo>
3030#include < random>
31+ #include < set>
3132
3233#include " llinventorymodel.h"
3334
@@ -125,16 +126,27 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
125126 {
126127 // HACK: downcast
127128 LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
128- if (c->getVersion () != LLViewerInventoryCategory::VERSION_UNKNOWN)
129+
130+ bool descendents_match = true ;
131+ if (c->getVersion () != LLViewerInventoryCategory::VERSION_UNKNOWN)
129132 {
130- S32 descendents_server = c->getDescendentCount ();
131- S32 descendents_actual = c->getViewerDescendentCount ();
132- if (descendents_server == descendents_actual)
133+ const S32 descendents_server = c->getDescendentCount ();
134+ const S32 descendents_actual = c->getViewerDescendentCount ();
135+ descendents_match = (descendents_server == descendents_actual);
136+
137+ if (!descendents_match)
133138 {
134- mCachedCatIDs .insert (c->getUUID ());
135- rv = true ;
139+ LL_DEBUGS (" AsyncInventory" ) << " Caching category with mismatched descendents"
140+ << " cat_id=" << c->getUUID ()
141+ << " name=\" " << c->getName () << " \" "
142+ << " server_descendents=" << descendents_server
143+ << " viewer_descendents=" << descendents_actual
144+ << LL_ENDL;
136145 }
137146 }
147+
148+ mCachedCatIDs .insert (c->getUUID ());
149+ rv = true ;
138150 }
139151 return rv;
140152}
@@ -2411,6 +2423,68 @@ void LLInventoryModel::cache(
24112423 items,
24122424 INCLUDE_TRASH,
24132425 can_cache);
2426+
2427+ std::set<LLUUID> cached_category_ids;
2428+ std::set<LLUUID> cached_item_ids;
2429+ for (auto & cat_ptr : categories)
2430+ {
2431+ if (cat_ptr.notNull ())
2432+ {
2433+ cached_category_ids.insert (cat_ptr->getUUID ());
2434+ }
2435+ }
2436+ for (auto & item_ptr : items)
2437+ {
2438+ if (item_ptr.notNull ())
2439+ {
2440+ cached_item_ids.insert (item_ptr->getUUID ());
2441+ }
2442+ }
2443+
2444+ // Fallback pass: ensure every known category/item under the root is persisted
2445+ // even if the parent/child map failed to enumerate it.
2446+ for (auto & entry : mCategoryMap )
2447+ {
2448+ LLViewerInventoryCategory* cat = entry.second ;
2449+ if (!cat)
2450+ {
2451+ continue ;
2452+ }
2453+ const LLUUID& cat_id = cat->getUUID ();
2454+ if (cached_category_ids.count (cat_id) != 0 )
2455+ {
2456+ continue ;
2457+ }
2458+ if (!isObjectDescendentOf (cat_id, parent_folder_id))
2459+ {
2460+ continue ;
2461+ }
2462+ if (can_cache (cat, NULL ))
2463+ {
2464+ categories.push_back (cat);
2465+ cached_category_ids.insert (cat_id);
2466+ }
2467+ }
2468+
2469+ for (auto & entry : mItemMap )
2470+ {
2471+ LLViewerInventoryItem* item = entry.second ;
2472+ if (!item)
2473+ {
2474+ continue ;
2475+ }
2476+ const LLUUID& item_id = item->getUUID ();
2477+ if (cached_item_ids.count (item_id) != 0 )
2478+ {
2479+ continue ;
2480+ }
2481+ if (!isObjectDescendentOf (item_id, parent_folder_id))
2482+ {
2483+ continue ;
2484+ }
2485+ items.push_back (item);
2486+ cached_item_ids.insert (item_id);
2487+ }
24142488 // Use temporary file to avoid potential conflicts with other
24152489 // instances (even a 'read only' instance unzips into a file)
24162490 std::string temp_file = gDirUtilp ->getTempFilename ();
@@ -3097,6 +3171,8 @@ bool LLInventoryModel::loadSkeletonFromCacheOnly(const LLUUID& owner_id)
30973171 const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
30983172
30993173 size_t cached_category_count = 0 ;
3174+ size_t cached_category_unknown_version = 0 ;
3175+ size_t cached_category_marked_refresh = 0 ;
31003176 for (auto & cat : categories)
31013177 {
31023178 if (!cat)
@@ -3110,13 +3186,22 @@ bool LLInventoryModel::loadSkeletonFromCacheOnly(const LLUUID& owner_id)
31103186 const bool requires_refresh = (cached_version == NO_VERSION)
31113187 || (categories_to_update.find (cat->getUUID ()) != categories_to_update.end ());
31123188 cat->setVersion (requires_refresh ? NO_VERSION : cached_version);
3189+ if (requires_refresh)
3190+ {
3191+ ++cached_category_marked_refresh;
3192+ }
3193+ if (cached_version == NO_VERSION)
3194+ {
3195+ ++cached_category_unknown_version;
3196+ }
31133197 addCategory (cat);
31143198 ++child_counts[cat->getParentUUID ()];
31153199 ++cached_category_count;
31163200 }
31173201
31183202 cat_map_t ::iterator unparented = mCategoryMap .end ();
31193203 size_t cached_item_count = 0 ;
3204+ size_t items_missing_parent = 0 ;
31203205 for (auto & item_ptr : items)
31213206 {
31223207 LLViewerInventoryItem* item = item_ptr.get ();
@@ -3132,8 +3217,14 @@ bool LLInventoryModel::loadSkeletonFromCacheOnly(const LLUUID& owner_id)
31323217 ++child_counts[item->getParentUUID ()];
31333218 ++cached_item_count;
31343219 }
3220+ else
3221+ {
3222+ ++items_missing_parent;
3223+ }
31353224 }
31363225
3226+ const size_t category_map_size_post_load = mCategoryMap .size ();
3227+
31373228 for (auto & entry : child_counts)
31383229 {
31393230 const cat_map_t ::iterator cit = mCategoryMap .find (entry.first );
@@ -3159,9 +3250,14 @@ bool LLInventoryModel::loadSkeletonFromCacheOnly(const LLUUID& owner_id)
31593250
31603251 categories.clear ();
31613252
3162- LL_INFOS (LOG_INV) << " Loaded cache-only skeleton for " << owner_id
3163- << " with " << cached_category_count << " categories and "
3164- << cached_item_count << " items." << LL_ENDL;
3253+ LL_INFOS (" AsyncInventory" ) << " Loaded cache-only skeleton for " << owner_id
3254+ << " categories_from_cache=" << cached_category_count
3255+ << " categories_unknown_version=" << cached_category_unknown_version
3256+ << " categories_marked_refresh=" << cached_category_marked_refresh
3257+ << " items_from_cache=" << cached_item_count
3258+ << " items_missing_parent=" << items_missing_parent
3259+ << " category_map_size_post_load=" << category_map_size_post_load
3260+ << LL_ENDL;
31653261
31663262 return cached_category_count > 0 ;
31673263}
@@ -3236,6 +3332,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
32363332 S32 i;
32373333 S32 lost = 0 ;
32383334 cat_array_t lost_cats;
3335+ size_t tree_links_inserted = 0 ;
32393336 for (auto & cat : cats)
32403337 {
32413338 catsp = getUnlockedCatArray (cat->getParentUUID ());
@@ -3246,6 +3343,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
32463343 cat->getPreferredType () == LLFolderType::FT_ROOT_INVENTORY ))
32473344 {
32483345 catsp->push_back (cat);
3346+ ++tree_links_inserted;
32493347 }
32503348 else
32513349 {
@@ -3261,6 +3359,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
32613359 lost_cats.push_back (cat);
32623360 }
32633361 }
3362+ const S32 lost_categories = lost;
32643363 if (lost)
32653364 {
32663365 LL_WARNS (LOG_INV) << " Found " << lost << " lost categories." << LL_ENDL;
@@ -3299,6 +3398,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
32993398 if (catsp)
33003399 {
33013400 catsp->push_back (cat);
3401+ ++tree_links_inserted;
33023402 }
33033403 else
33043404 {
@@ -3314,6 +3414,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
33143414 // have to do is iterate over the items and put them in the right
33153415 // place.
33163416 item_array_t items;
3417+ size_t item_links_inserted = 0 ;
33173418 if (!mItemMap .empty ())
33183419 {
33193420 LLPointer<LLViewerInventoryItem> item;
@@ -3331,6 +3432,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
33313432 if (itemsp)
33323433 {
33333434 itemsp->push_back (item);
3435+ ++item_links_inserted;
33343436 }
33353437 else
33363438 {
@@ -3348,6 +3450,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
33483450 if (itemsp)
33493451 {
33503452 itemsp->push_back (item);
3453+ ++item_links_inserted;
33513454 }
33523455 else
33533456 {
@@ -3388,6 +3491,21 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
33883491 }
33893492 }
33903493
3494+ const size_t category_tree_nodes = mParentChildCategoryTree .size ();
3495+ const size_t item_tree_nodes = mParentChildItemTree .size ();
3496+ const S32 lost_items = lost;
3497+
3498+ LL_INFOS (" AsyncInventory" ) << " ParentChildMap summary"
3499+ << " category_map_size=" << mCategoryMap .size ()
3500+ << " category_tree_nodes=" << category_tree_nodes
3501+ << " category_links=" << tree_links_inserted
3502+ << " item_map_size=" << mItemMap .size ()
3503+ << " item_tree_nodes=" << item_tree_nodes
3504+ << " item_links=" << item_links_inserted
3505+ << " lost_categories=" << lost_categories
3506+ << " lost_items=" << lost_items
3507+ << LL_ENDL;
3508+
33913509 if (run_validation)
33923510 {
33933511 const LLUUID& agent_inv_root_id = gInventory .getRootFolderID ();
@@ -3427,7 +3545,7 @@ void LLInventoryModel::buildParentChildMap(bool run_validation)
34273545 {
34283546 // Fatal inventory error. Will not be able to engage in many inventory operations.
34293547 // This should be followed by an error dialog leading to logout.
3430- LL_WARNS (" Inventory " ) << " Fatal errors were found in validate(): unable to initialize inventory! "
3548+ LL_WARNS (" AsyncInventory " ) << " Fatal errors were found in validate(): unable to initialize inventory! "
34313549 << " Will not be able to do normal inventory operations in this session."
34323550 << LL_ENDL;
34333551 mIsAgentInvUsable = false ;
@@ -3783,14 +3901,24 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
37833901 LLSD& cat_array = inventory[" categories" ];
37843902
37853903 S32 cat_count = 0 ;
3904+ S32 cat_unknown_version_count = 0 ;
37863905 for (auto & cat : categories)
37873906 {
3788- if (cat-> getVersion () != LLViewerInventoryCategory::VERSION_UNKNOWN )
3907+ if (!cat )
37893908 {
3790- LLSD sd;
3791- cat->exportLLSD (sd);
3792- cat_array.append (sd);
3793- cat_count++;
3909+ continue ;
3910+ }
3911+
3912+ // Persist folders even when their version is unknown so warm-cache
3913+ // hydrations retain the complete hierarchy; those folders will be
3914+ // marked for refresh on the next login.
3915+ LLSD sd;
3916+ cat->exportLLSD (sd);
3917+ cat_array.append (sd);
3918+ ++cat_count;
3919+ if (cat->getVersion () == LLViewerInventoryCategory::VERSION_UNKNOWN)
3920+ {
3921+ ++cat_unknown_version_count;
37943922 }
37953923 }
37963924
@@ -3806,14 +3934,17 @@ bool LLInventoryModel::saveToFile(const std::string& filename,
38063934 fileSD << LLSDOStreamer<LLSDBinaryFormatter>(inventory) << std::endl;
38073935 if (fileSD.fail ())
38083936 {
3809- LL_WARNS (LOG_INV ) << " Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL;
3937+ LL_WARNS (" AsyncInventory " ) << " Failed to write cache. Unable to save inventory to: " << filename << LL_ENDL;
38103938 return false ;
38113939 }
38123940 fileSD.flush ();
38133941
38143942 fileSD.close ();
38153943
3816- LL_INFOS (LOG_INV) << " Inventory saved: " << (S32)cat_count << " categories, " << (S32)it_count << " items." << LL_ENDL;
3944+ LL_INFOS (" AsyncInventory" ) << " Inventory saved: categories=" << cat_count
3945+ << " categories_unknown_version=" << cat_unknown_version_count
3946+ << " items=" << (S32)it_count
3947+ << LL_ENDL;
38173948 }
38183949 catch (...)
38193950 {
0 commit comments