bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb) { S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN; S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN; bool can_be_added = true; LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); // If category could not be retrieved it might mean that // inventory is unusable at the moment so the category is // stored with VERSION_UNKNOWN and DESCENDENT_COUNT_UNKNOWN, // it may be updated later. if (category) { // Inventory category version is used to find out if some changes // to a category have been made. version = category->getVersion(); LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(cat_id, cats, items); if (!cats || !items) { LL_WARNS() << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << LL_ENDL; // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean // that the cat just doesn't have any items or subfolders). // Unrecoverable, so just return "false" meaning that the category can't be observed. can_be_added = false; llassert(cats != NULL && items != NULL); } else { current_num_known_descendents = cats->size() + items->size(); } } if (can_be_added) { mCategoryMap.insert(category_map_value_t( cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents))); } return can_be_added; }
void LLInventoryCategoriesObserver::changed(U32 mask) { if (!mCategoryMap.size()) return; std::vector<LLUUID> deleted_categories_ids; for (category_map_t::iterator iter = mCategoryMap.begin(); iter != mCategoryMap.end(); ++iter) { const LLUUID& cat_id = (*iter).first; LLCategoryData& cat_data = (*iter).second; LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); if (!category) { LL_WARNS() << "Category : Category id = " << cat_id << " disappeared" << LL_ENDL; cat_data.mCallback(); // Keep track of those deleted categories so we can remove them deleted_categories_ids.push_back(cat_id); continue; } const S32 version = category->getVersion(); const S32 expected_num_descendents = category->getDescendentCount(); if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) || (expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)) { continue; } // Check number of known descendents to find out whether it has changed. LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(cat_id, cats, items); if (!cats || !items) { LL_WARNS() << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << LL_ENDL; // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean // that the cat just doesn't have any items or subfolders). // Unrecoverable, so just skip this category. llassert(cats != NULL && items != NULL); continue; } const S32 current_num_known_descendents = cats->size() + items->size(); bool cat_changed = false; // If category version or descendents count has changed // update category data in mCategoryMap if (version != cat_data.mVersion || current_num_known_descendents != cat_data.mDescendentsCount) { cat_data.mVersion = version; cat_data.mDescendentsCount = current_num_known_descendents; cat_changed = true; } // If any item names have changed, update the name hash // Only need to check if (a) name hash has not previously been // computed, or (b) a name has changed. if (!cat_data.mIsNameHashInitialized || (mask & LLInventoryObserver::LABEL)) { LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id); if (cat_data.mItemNameHash != item_name_hash) { cat_data.mIsNameHashInitialized = true; cat_data.mItemNameHash = item_name_hash; cat_changed = true; } } // If anything has changed above, fire the callback. if (cat_changed) cat_data.mCallback(); } // Remove deleted categories from the list for (std::vector<LLUUID>::iterator deleted_id = deleted_categories_ids.begin(); deleted_id != deleted_categories_ids.end(); ++deleted_id) { removeCategory(*deleted_id); } }
void LLInventoryModelBackgroundFetch::backgroundFetch() { if (mBackgroundFetchActive && gAgent.getRegion()) { // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents2"); if (gSavedSettings.getBOOL("UseHTTPInventory") && !url.empty()) { bulkFetch(url); return; } #if 1 //-------------------------------------------------------------------------------- // DEPRECATED OLD CODE // // No more categories to fetch, stop fetch process. if (mFetchQueue.empty()) { llinfos << "Inventory fetch completed" << llendl; setAllFoldersFetched(); return; } F32 fast_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.1f); F32 slow_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.5f); if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() > slow_fetch_time) { // Double timeouts on failure. mMinTimeBetweenFetches = llmin(mMinTimeBetweenFetches * 2.f, 10.f); mMaxTimeBetweenFetches = llmin(mMaxTimeBetweenFetches * 2.f, 120.f); llinfos << "Inventory fetch times grown to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl; // fetch is no longer considered "timely" although we will wait for full time-out. mTimelyFetchPending = FALSE; } while(1) { if (mFetchQueue.empty()) { break; } if(gDisconnected) { // Just bail if we are disconnected. break; } const FetchQueueInfo info = mFetchQueue.front(); LLViewerInventoryCategory* cat = gInventory.getCategory(info.mCatUUID); // Category has been deleted, remove from queue. if (!cat) { mFetchQueue.pop_front(); continue; } if (mFetchTimer.getElapsedTimeF32() > mMinTimeBetweenFetches && LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) { // Category exists but has no children yet, fetch the descendants // for now, just request every time and rely on retry timer to throttle. if (cat->fetch()) { mFetchTimer.reset(); mTimelyFetchPending = TRUE; } else { // The catagory also tracks if it has expired and here it says it hasn't // yet. Get out of here because nothing is going to happen until we // update the timers. break; } } // Do I have all my children? else if (gInventory.isCategoryComplete(info.mCatUUID)) { // Finished with this category, remove from queue. mFetchQueue.pop_front(); // Add all children to queue. LLInventoryModel::cat_array_t* categories; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items); for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it) { mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(),info.mRecursive)); } // We received a response in less than the fast time. if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() < fast_fetch_time) { // Shrink timeouts based on success. mMinTimeBetweenFetches = llmax(mMinTimeBetweenFetches * 0.8f, 0.3f); mMaxTimeBetweenFetches = llmax(mMaxTimeBetweenFetches * 0.8f, 10.f); //llinfos << "Inventory fetch times shrunk to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl; } mTimelyFetchPending = FALSE; continue; } else if (mFetchTimer.getElapsedTimeF32() > mMaxTimeBetweenFetches) { // Received first packet, but our num descendants does not match db's num descendants // so try again later. mFetchQueue.pop_front(); if (mNumFetchRetries++ < MAX_FETCH_RETRIES) { // push on back of queue mFetchQueue.push_back(info); } mTimelyFetchPending = FALSE; mFetchTimer.reset(); break; } // Not enough time has elapsed to do a new fetch break; } // // DEPRECATED OLD CODE //-------------------------------------------------------------------------------- #endif } }
void LLCOFWearables::refresh() { const LLUUID cof_id = LLAppearanceMgr::instance().getCOF(); if (cof_id.isNull()) { llwarns << "COF ID cannot be NULL" << llendl; return; } LLViewerInventoryCategory* catp = gInventory.getCategory(cof_id); if (!catp) { llwarns << "COF category cannot be NULL" << llendl; return; } // BAP - this check has to be removed because an item name change does not // change cat version - ie, checking version is not a complete way // of finding out whether anything has changed in this category. //if (mCOFVersion == catp->getVersion()) return; mCOFVersion = catp->getVersion(); // Save current scrollbar position. typedef std::map<LLFlatListView*, LLRect> scroll_pos_map_t; scroll_pos_map_t saved_scroll_pos; saved_scroll_pos[mAttachments] = mAttachments->getVisibleContentRect(); saved_scroll_pos[mClothing] = mClothing->getVisibleContentRect(); saved_scroll_pos[mBodyParts] = mBodyParts->getVisibleContentRect(); // Save current selection. typedef std::vector<LLSD> values_vector_t; typedef std::map<LLFlatListView*, values_vector_t> selection_map_t; selection_map_t preserve_selection; mAttachments->getSelectedValues(preserve_selection[mAttachments]); mClothing->getSelectedValues(preserve_selection[mClothing]); mBodyParts->getSelectedValues(preserve_selection[mBodyParts]); clear(); LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t cof_items; gInventory.collectDescendents(cof_id, cats, cof_items, LLInventoryModel::EXCLUDE_TRASH); populateAttachmentsAndBodypartsLists(cof_items); LLAppearanceMgr::wearables_by_type_t clothing_by_type(LLWearableType::WT_COUNT); LLAppearanceMgr::getInstance()->divvyWearablesByType(cof_items, clothing_by_type); populateClothingList(clothing_by_type); // Restore previous selection for (selection_map_t::iterator iter = preserve_selection.begin(), iter_end = preserve_selection.end(); iter != iter_end; ++iter) { LLFlatListView* list = iter->first; if (!list) continue; //restoring selection should not fire commit callbacks list->setCommitOnSelectionChange(false); const values_vector_t& values = iter->second; for (values_vector_t::const_iterator value_it = values.begin(), value_it_end = values.end(); value_it != value_it_end; ++value_it) { // value_it may be null because of dummy items // Dummy items have no ID if(value_it->asUUID().notNull()) { list->selectItemByValue(*value_it); } } list->setCommitOnSelectionChange(true); } // Restore previous scrollbar position. for (scroll_pos_map_t::const_iterator it = saved_scroll_pos.begin(); it != saved_scroll_pos.end(); ++it) { LLFlatListView* list = it->first; LLRect scroll_pos = it->second; list->scrollToShowRect(scroll_pos); } }
void AISUpdate::doUpdate() { // Do version/descendent accounting. for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin(); catit != mCatDescendentDeltas.end(); ++catit) { LL_DEBUGS("Inventory") << "descendent accounting for " << catit->first << LL_ENDL; const LLUUID cat_id(catit->first); // Don't account for update if we just created this category. if (mCategoriesCreated.find(cat_id) != mCategoriesCreated.end()) { LL_DEBUGS("Inventory") << "Skipping version increment for new category " << cat_id << LL_ENDL; continue; } // Don't account for update unless AIS told us it updated that category. if (mCatVersionsUpdated.find(cat_id) == mCatVersionsUpdated.end()) { LL_DEBUGS("Inventory") << "Skipping version increment for non-updated category " << cat_id << LL_ENDL; continue; } // If we have a known descendent count, set that now. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); if (cat) { S32 descendent_delta = catit->second; S32 old_count = cat->getDescendentCount(); LL_DEBUGS("Inventory") << "Updating descendent count for " << cat->getName() << " " << cat_id << " with delta " << descendent_delta << " from " << old_count << " to " << (old_count+descendent_delta) << LL_ENDL; LLInventoryModel::LLCategoryUpdate up(cat_id, descendent_delta); gInventory.accountForUpdate(up); } else { LL_DEBUGS("Inventory") << "Skipping version accounting for unknown category " << cat_id << LL_ENDL; } } // CREATE CATEGORIES for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin(); create_it != mCategoriesCreated.end(); ++create_it) { LLUUID category_id(create_it->first); LLPointer<LLViewerInventoryCategory> new_category = create_it->second; gInventory.updateCategory(new_category, LLInventoryObserver::CREATE); LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL; } // UPDATE CATEGORIES for (deferred_category_map_t::const_iterator update_it = mCategoriesUpdated.begin(); update_it != mCategoriesUpdated.end(); ++update_it) { LLUUID category_id(update_it->first); LLPointer<LLViewerInventoryCategory> new_category = update_it->second; // Since this is a copy of the category *before* the accounting update, above, // we need to transfer back the updated version/descendent count. LLViewerInventoryCategory* curr_cat = gInventory.getCategory(new_category->getUUID()); if (!curr_cat) { LL_WARNS("Inventory") << "Failed to update unknown category " << new_category->getUUID() << LL_ENDL; } else { new_category->setVersion(curr_cat->getVersion()); new_category->setDescendentCount(curr_cat->getDescendentCount()); gInventory.updateCategory(new_category); LL_DEBUGS("Inventory") << "updated category " << new_category->getName() << " " << category_id << LL_ENDL; } } // CREATE ITEMS for (deferred_item_map_t::const_iterator create_it = mItemsCreated.begin(); create_it != mItemsCreated.end(); ++create_it) { LLUUID item_id(create_it->first); LLPointer<LLViewerInventoryItem> new_item = create_it->second; // FIXME risky function since it calls updateServer() in some // cases. Maybe break out the update/create cases, in which // case this is create. LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL; gInventory.updateItem(new_item, LLInventoryObserver::CREATE); } // UPDATE ITEMS for (deferred_item_map_t::const_iterator update_it = mItemsUpdated.begin(); update_it != mItemsUpdated.end(); ++update_it) { LLUUID item_id(update_it->first); LLPointer<LLViewerInventoryItem> new_item = update_it->second; // FIXME risky function since it calls updateServer() in some // cases. Maybe break out the update/create cases, in which // case this is update. LL_DEBUGS("Inventory") << "updated item " << item_id << LL_ENDL; //LL_DEBUGS("Inventory") << ll_pretty_print_sd(new_item->asLLSD()) << LL_ENDL; gInventory.updateItem(new_item); } // DELETE OBJECTS for (uuid_list_t::const_iterator del_it = mObjectsDeletedIds.begin(); del_it != mObjectsDeletedIds.end(); ++del_it) { LL_DEBUGS("Inventory") << "deleted item " << *del_it << LL_ENDL; gInventory.onObjectDeletedFromServer(*del_it, false, false, false); } // TODO - how can we use this version info? Need to be sure all // changes are going through AIS first, or at least through // something with a reliable responder. for (uuid_int_map_t::iterator ucv_it = mCatVersionsUpdated.begin(); ucv_it != mCatVersionsUpdated.end(); ++ucv_it) { const LLUUID id = ucv_it->first; S32 version = ucv_it->second; LLViewerInventoryCategory *cat = gInventory.getCategory(id); LL_DEBUGS("Inventory") << "cat version update " << cat->getName() << " to version " << cat->getVersion() << LL_ENDL; if (cat->getVersion() != version) { LL_WARNS() << "Possible version mismatch for category " << cat->getName() << ", viewer version " << cat->getVersion() << " server version " << version << LL_ENDL; } } gInventory.notifyObservers(); }