From 1f18378e604e361988bd4b28c7bb4aea05b1f998 Mon Sep 17 00:00:00 2001 From: Darl Date: Tue, 13 Jan 2026 22:00:05 -0600 Subject: [PATCH] Mute list fallback to cache + retry on region change if still pending - fall back to the cached mute list on request timeout or transfer failure so the list never stays empty if we can help it - retry mute list requests on region change with a short cooldwn to avoid being too noisy, if still in timeout window - cancel pending timeout once the list loads to stop unnecessary potential fallback behaviors - refesh the blocked list UI when a cached or deferred load populates it - notify the user of a forced cache fallback because recent changes may be missing Signed-off-by: Darl --- indra/newview/llblocklist.cpp | 15 +++ indra/newview/llblocklist.h | 8 +- indra/newview/llmutelist.cpp | 117 ++++++++++++++++-- indra/newview/llmutelist.h | 8 ++ .../skins/default/xui/en/notifications.xml | 9 ++ 5 files changed, 145 insertions(+), 12 deletions(-) diff --git a/indra/newview/llblocklist.cpp b/indra/newview/llblocklist.cpp index 89516a8a843..2c64b1f5120 100644 --- a/indra/newview/llblocklist.cpp +++ b/indra/newview/llblocklist.cpp @@ -78,6 +78,21 @@ LLBlockList::~LLBlockList() LLMuteList::getInstance()->removeObserver(this); } +void LLBlockList::onChange() +{ + const U32 current_size = static_cast(LLMuteList::getInstance()->getMutes().size()); + if (current_size == mMuteListSize) + { + return; + } + if (mMuteListSize == 0 && current_size > 0) + { + mShouldAddAll = true; + mActionType = NONE; + setDirty(true); + } +} + void LLBlockList::createList() { std::vector mutes = LLMuteList::instance().getMutes(); diff --git a/indra/newview/llblocklist.h b/indra/newview/llblocklist.h index 64e8246f439..66d289a15c7 100644 --- a/indra/newview/llblocklist.h +++ b/indra/newview/llblocklist.h @@ -54,13 +54,13 @@ class LLBlockList: public LLFlatListViewEx, public LLMuteListObserver LLBlockList(const Params& p); virtual ~LLBlockList(); - virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); + bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } LLBlockedListItem* getBlockedItem() const; - virtual void onChange() { } - virtual void onChangeDetailed(const LLMute& ); - virtual void draw(); + void onChange() override; + void onChangeDetailed(const LLMute& ) override; + void draw() override; void setNameFilter(const std::string& filter); void sortByName(); diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 9157e348332..1b8a07be6c9 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -59,12 +59,28 @@ #include "llworld.h" //for particle system banning #include "llimview.h" #include "llnotifications.h" +#include "llnotificationsutil.h" #include "llviewercontrol.h" #include "llviewerobjectlist.h" #include "lltrans.h" +#include "lleventtimer.h" namespace { + constexpr F32 MUTE_LIST_REQUEST_TIMEOUT_SECONDS = 30.f; + constexpr F32 MUTE_LIST_REQUEST_COOLDOWN_SECONDS = 5.f; + + void notify_mute_list_cache_used() + { + static bool s_notified = false; + if (s_notified) + { + return; + } + s_notified = true; + LLNotificationsUtil::add("MuteListFallbackCache"); + } + // This method is used to return an object to mute given an object id. // Its used by the LLMute constructor and LLMuteList::isMuted. LLViewerObject* get_object_to_mute_from_id(LLUUID object_id) @@ -155,7 +171,8 @@ std::string LLMute::getDisplayType() const //----------------------------------------------------------------------------- LLMuteList::LLMuteList() : mLoadState(ML_INITIAL), - mRequestStartTime(0.f) + mRequestStartTime(0.f), + mRequestTimeout(nullptr) { gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); @@ -178,6 +195,8 @@ LLMuteList::LLMuteList() : // but this way is just more convinient onAccountNameChanged(id, av_name.getUserName()); }); + + mRegionChangedSlot = gAgent.addRegionChangedCallback([this]() { onRegionChanged(); }); } //----------------------------------------------------------------------------- @@ -191,6 +210,11 @@ LLMuteList::~LLMuteList() void LLMuteList::cleanupSingleton() { LLAvatarNameCache::getInstance()->setAccountNameChangedCallback(nullptr); + if (mRegionChangedSlot.connected()) + { + mRegionChangedSlot.disconnect(); + } + cancelRequestTimeout(); } bool LLMuteList::isLinden(const std::string& name) @@ -735,6 +759,17 @@ bool LLMuteList::isMuted(const std::string& username, U32 flags) const //----------------------------------------------------------------------------- void LLMuteList::requestFromServer(const LLUUID& agent_id) { + if (mLoadState == ML_REQUESTED) + { + const F64 now = LLTimer::getElapsedSeconds(); + if (now - mRequestStartTime < MUTE_LIST_REQUEST_COOLDOWN_SECONDS) + { + return; + } + } + + cancelRequestTimeout(); + std::string agent_id_string; std::string filename; agent_id.toString(agent_id_string); @@ -764,11 +799,64 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id) } mLoadState = ML_REQUESTED; mRequestStartTime = LLTimer::getElapsedSeconds(); + const F64 request_start_time = mRequestStartTime; + mRequestTimeout = LLEventTimer::run_after(MUTE_LIST_REQUEST_TIMEOUT_SECONDS, [this, request_start_time]() + { + mRequestTimeout = nullptr; + if (mLoadState != ML_REQUESTED) + { + return; + } + if (mRequestStartTime != request_start_time) + { + return; + } + LL_WARNS() << "Mute list request timed out; loading cached mute list" << LL_ENDL; + if (!loadFromCache()) + { + LL_WARNS() << "Failed to load cached mute list after timeout" << LL_ENDL; + mLoadState = ML_FAILED; + } + else + { + notify_mute_list_cache_used(); + } + }); // Double amount of retries due to this request happening during busy stage // Ideally this should be turned into a capability gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL); } +void LLMuteList::onRegionChanged() +{ + if (isLoaded()) + { + return; + } + if (mLoadState == ML_REQUESTED) + { + const F64 now = LLTimer::getElapsedSeconds(); + if (now - mRequestStartTime < MUTE_LIST_REQUEST_COOLDOWN_SECONDS) + { + return; + } + } + if (gDisconnected || !gAgent.getRegion() || gAgent.getID().isNull()) + { + return; + } + requestFromServer(gAgent.getID()); +} + +void LLMuteList::cancelRequestTimeout() +{ + if (mRequestTimeout) + { + delete mRequestTimeout; + } + mRequestTimeout = nullptr; +} + //----------------------------------------------------------------------------- // cache() //----------------------------------------------------------------------------- @@ -786,6 +874,14 @@ void LLMuteList::cache(const LLUUID& agent_id) } } +bool LLMuteList::loadFromCache() +{ + std::string agent_id_string; + gAgent.getID().toString(agent_id_string); + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".cached_mute"; + return loadFromFile(filename); +} + //----------------------------------------------------------------------------- // Static message handlers //----------------------------------------------------------------------------- @@ -825,12 +921,7 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**) { LL_INFOS() << "LLMuteList::processUseCachedMuteList()" << LL_ENDL; - - std::string agent_id_string; - gAgent.getID().toString(agent_id_string); - std::string filename; - filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; - LLMuteList::getInstance()->loadFromFile(filename); + LLMuteList::getInstance()->loadFromCache(); } void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status) @@ -845,7 +936,16 @@ void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_ } else { - LLMuteList::getInstance()->mLoadState = ML_FAILED; + LL_WARNS() << "Mute list transfer failed; falling back to cached mute list" << LL_ENDL; + LLMuteList* mute_list = LLMuteList::getInstance(); + if (!mute_list->loadFromCache()) + { + mute_list->mLoadState = ML_FAILED; + } + else + { + notify_mute_list_cache_used(); + } } delete local_filename_and_path; } @@ -905,6 +1005,7 @@ void LLMuteList::removeObserver(LLMuteListObserver* observer) void LLMuteList::setLoaded() { mLoadState = ML_LOADED; + cancelRequestTimeout(); notifyObservers(); } diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index b65fd61fccf..0735ee84f1d 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -31,7 +31,10 @@ #include "lluuid.h" #include "llextendedstatus.h" +#include + class LLViewerObject; +class LLEventTimer; class LLMessageSystem; class LLMuteListObserver; @@ -127,6 +130,9 @@ class LLMuteList : public LLSingleton void cache(const LLUUID& agent_id); private: + void onRegionChanged(); + void cancelRequestTimeout(); + bool loadFromCache(); bool loadFromFile(const std::string& filename); bool saveToFile(const std::string& filename); @@ -178,6 +184,8 @@ class LLMuteList : public LLSingleton EMuteListState mLoadState; F64 mRequestStartTime; + LLEventTimer* mRequestTimeout; + boost::signals2::connection mRegionChangedSlot; friend class LLDispatchEmptyMuteList; }; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index e8da8a6322c..5b846c0a66b 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2153,6 +2153,15 @@ Unable to add new entry to block list because you reached the limit of [MUTE_LIM fail + +Could not update the block list from the region. Using the local cached copy. Log out and back in to refresh the block list if recent changes are missing. + fail + +