From 61417e2ba3ba77fe79b9d1d477524cba130106c6 Mon Sep 17 00:00:00 2001 From: Darl Date: Wed, 1 Apr 2026 11:36:51 -0500 Subject: Allow mute list to fallback to cached copy when simulator response somehow isn't received Signed-off-by: Darl --- indra/newview/llmutelist.cpp | 130 ++++++++++++++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 31 deletions(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index b7bba02b9d..d9e674a423 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -92,7 +92,7 @@ public: const LLUUID& invoice, const sparam_t& strings) { - LLMuteList::getInstance()->setLoaded(); + LLMuteList::getInstance()->setLoaded(LLMuteList::MLS_SERVER_EMPTY); return true; } }; @@ -155,7 +155,9 @@ std::string LLMute::getDisplayType() const //----------------------------------------------------------------------------- LLMuteList::LLMuteList() : mLoadState(ML_INITIAL), - mRequestStartTime(0.f) + mLoadSource(MLS_NONE), + mRequestStartTime(0.f), + mTriedCacheFallback(false) { gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); @@ -210,7 +212,7 @@ bool LLMuteList::isLinden(const std::string& name) return last_name == "linden"; } -bool LLMuteList::getLoadFailed() const +bool LLMuteList::getLoadFailed() { if (mLoadState == ML_FAILED) { @@ -221,12 +223,78 @@ bool LLMuteList::getLoadFailed() const constexpr F64 WAIT_SECONDS = 30; if (mRequestStartTime + WAIT_SECONDS < LLTimer::getTotalSeconds()) { - return true; + LL_WARNS() << "Mute list request timed out; trying cache fallback once" << LL_ENDL; + tryLoadCacheFallback(gAgent.getID(), "request timeout"); + return mLoadState == ML_FAILED; } } return false; } +const char* LLMuteList::sourceToString(EMuteListSource source) +{ + switch (source) + { + case MLS_NONE: + return "none"; + case MLS_SERVER: + return "server"; + case MLS_SERVER_EMPTY: + return "server-empty"; + case MLS_SERVER_CACHE: + return "server-cached"; + case MLS_FALLBACK_CACHE: + return "fallback-cache"; + default: + return "unknown"; + } +} + +std::string LLMuteList::getCacheFilename(const LLUUID& agent_id) const +{ + std::string agent_id_string; + agent_id.toString(agent_id_string); + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".cached_mute"; +} + +void LLMuteList::setFailed(const std::string& reason) +{ + mLoadState = ML_FAILED; + if (mLoadSource == MLS_NONE) + { + LL_WARNS() << "Mute list unavailable: " << reason << LL_ENDL; + } + else + { + LL_WARNS() << "Mute list unavailable: " << reason << " (last source=" << sourceToString(mLoadSource) << ")" << LL_ENDL; + } +} + +bool LLMuteList::tryLoadCacheFallback(const LLUUID& agent_id, const std::string& reason) +{ + if (mTriedCacheFallback) + { + if (!isLoaded()) + { + setFailed("cache fallback already attempted before " + reason); + } + return isLoaded(); + } + + mTriedCacheFallback = true; + const std::string filename = getCacheFilename(agent_id); + LL_INFOS() << "Trying mute list cache fallback due to " << reason << ": " << filename << LL_ENDL; + + if (loadFromFile(filename, MLS_FALLBACK_CACHE)) + { + LL_WARNS() << "Loaded mute list from cache fallback due to " << reason << LL_ENDL; + return true; + } + + setFailed("cache fallback failed after " + reason); + return false; +} + static LLVOAvatar* find_avatar(const LLUUID& id) { LLViewerObject *obj = gObjectList.findObject(id); @@ -580,14 +648,14 @@ std::vector LLMuteList::getMutes() const //----------------------------------------------------------------------------- // loadFromFile() //----------------------------------------------------------------------------- -bool LLMuteList::loadFromFile(const std::string& filename) +bool LLMuteList::loadFromFile(const std::string& filename, EMuteListSource source) { LL_PROFILE_ZONE_SCOPED; if(!filename.size()) { LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL; - mLoadState = ML_FAILED; + setFailed("empty filename"); return false; } @@ -595,10 +663,14 @@ bool LLMuteList::loadFromFile(const std::string& filename) if (!fp) { LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL; - mLoadState = ML_FAILED; + setFailed("cannot open " + filename); return false; } + // Replace previous server-backed state so fallback can be superseded by authoritative data. + mMutes.clear(); + mLegacyMutes.clear(); + // *NOTE: Changing the size of these buffers will require changes // in the scanf below. char id_buffer[MAX_STRING]; /*Flawfinder: ignore*/ @@ -627,7 +699,7 @@ bool LLMuteList::loadFromFile(const std::string& filename) } } fclose(fp); - setLoaded(); + setLoaded(source); // server does not maintain up-to date account names (not display names!) // in this list, so it falls to viewer. @@ -737,12 +809,11 @@ bool LLMuteList::isMuted(const std::string& username, U32 flags) const //----------------------------------------------------------------------------- void LLMuteList::requestFromServer(const LLUUID& agent_id) { - std::string agent_id_string; - std::string filename; - agent_id.toString(agent_id_string); - filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; + const std::string filename = getCacheFilename(agent_id); LLCRC crc; crc.update(filename); + mTriedCacheFallback = false; + mLoadSource = MLS_NONE; LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MuteListRequest); @@ -755,17 +826,17 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id) if (gDisconnected) { LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL; - mLoadState = ML_FAILED; + tryLoadCacheFallback(agent_id, "disconnected before request"); return; } if (!gAgent.getRegion()) { LL_WARNS() << "No region for agent yet, skipping mute list request!" << LL_ENDL; - mLoadState = ML_FAILED; + tryLoadCacheFallback(agent_id, "no region for request"); return; } mLoadState = ML_REQUESTED; - mRequestStartTime = LLTimer::getElapsedSeconds(); + mRequestStartTime = LLTimer::getTotalSeconds(); // 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); @@ -777,15 +848,16 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id) void LLMuteList::cache(const LLUUID& agent_id) { - // Write to disk even if empty. - if(isLoaded()) + // Write to disk even if empty, but never from degraded fallback state. + if (isLoaded() && mLoadSource != MLS_FALLBACK_CACHE) { - std::string agent_id_string; - std::string filename; - agent_id.toString(agent_id_string); - filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; + const std::string filename = getCacheFilename(agent_id); saveToFile(filename); } + else if (isLoaded()) + { + LL_WARNS() << "Skipping mute list cache write from fallback-only state" << LL_ENDL; + } } //----------------------------------------------------------------------------- @@ -812,7 +884,7 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) LLMuteList* mute_list = getInstance(); mute_list->mLoadState = ML_REQUESTED; - mute_list->mRequestStartTime = LLTimer::getElapsedSeconds(); + mute_list->mRequestStartTime = LLTimer::getTotalSeconds(); // Todo: Based of logs and testing, there is no callback // from server if file doesn't exist server side. @@ -831,12 +903,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()->loadFromFile(LLMuteList::getInstance()->getCacheFilename(gAgent.getID()), MLS_SERVER_CACHE); } void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status) @@ -845,13 +912,13 @@ void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_ if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0)) { LL_INFOS() << "Received mute list from server" << LL_ENDL; - LLMuteList::getInstance()->loadFromFile(*local_filename_and_path); + LLMuteList::getInstance()->loadFromFile(*local_filename_and_path, MLS_SERVER); LLFile::remove(*local_filename_and_path); } else { LL_INFOS() << "LLMuteList xfer failed with code " << error_code << LL_ENDL; - LLMuteList::getInstance()->mLoadState = ML_FAILED; + LLMuteList::getInstance()->tryLoadCacheFallback(gAgent.getID(), "xfer failure"); } delete local_filename_and_path; } @@ -908,9 +975,10 @@ void LLMuteList::removeObserver(LLMuteListObserver* observer) mObservers.erase(observer); } -void LLMuteList::setLoaded() +void LLMuteList::setLoaded(EMuteListSource source) { mLoadState = ML_LOADED; + mLoadSource = source; notifyObservers(); } -- cgit v1.3 From 03c13540cb38d2e0ada6bf62ad2d4666487fa090 Mon Sep 17 00:00:00 2001 From: Darl Date: Thu, 2 Apr 2026 10:23:50 -0500 Subject: Treat emptymutelist dispatch from the simulator as authoritative when in a cache fallback status. This yields to the simulator's responsibility as source-of-truth. e.g. Bob has Alice blocked for a while across all his devices 1. Bob unblocked Alice on his laptop 2. Bob logs in on his desktop with a cached mutelist 3. LLDispatchEmptyMuteList fires 4. LLMuteList becomes eventually-correct, reflecting most recent signaled user intent Signed-off-by: Darl --- indra/newview/llmutelist.cpp | 13 ++++++++++--- indra/newview/llmutelist.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index d9e674a423..154e826196 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -92,7 +92,9 @@ public: const LLUUID& invoice, const sparam_t& strings) { - LLMuteList::getInstance()->setLoaded(LLMuteList::MLS_SERVER_EMPTY); + LLMuteList* mute_list = LLMuteList::getInstance(); + mute_list->clearCachedMutes(); + mute_list->setLoaded(LLMuteList::MLS_SERVER_EMPTY); return true; } }; @@ -231,6 +233,12 @@ bool LLMuteList::getLoadFailed() return false; } +void LLMuteList::clearCachedMutes() +{ + mMutes.clear(); + mLegacyMutes.clear(); +} + const char* LLMuteList::sourceToString(EMuteListSource source) { switch (source) @@ -668,8 +676,7 @@ bool LLMuteList::loadFromFile(const std::string& filename, EMuteListSource sourc } // Replace previous server-backed state so fallback can be superseded by authoritative data. - mMutes.clear(); - mLegacyMutes.clear(); + clearCachedMutes(); // *NOTE: Changing the size of these buffers will require changes // in the scanf below. diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index c243b5a2bd..34857e2006 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -136,6 +136,7 @@ public: void cache(const LLUUID& agent_id); private: + void clearCachedMutes(); bool loadFromFile(const std::string& filename, EMuteListSource source); bool saveToFile(const std::string& filename); bool tryLoadCacheFallback(const LLUUID& agent_id, const std::string& reason); -- cgit v1.3 From 0abb59a959d16b1163076ff0bd804d59ccf57bdb Mon Sep 17 00:00:00 2001 From: Darl Date: Thu, 2 Apr 2026 11:27:48 -0500 Subject: Rename LLMuteList state machine touch points for clarity getLoadFailed -> updateLoadState - No longer labeled as a plain getter, but instead as a state machine advancement point - This name reflects its role in advancing the state according to design parameters when called from the idle loop - Call site in LLIMProcessing::requestOfflineMessages simplified by internalizing our readiness checks isFailed - Reintroduced const to match isLoaded for determining state Signed-off-by: Darl --- indra/newview/llimprocessing.cpp | 2 +- indra/newview/llmutelist.cpp | 6 +++--- indra/newview/llmutelist.h | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index b1e42e11fb..3f60dc5d26 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1534,7 +1534,7 @@ void LLIMProcessing::requestOfflineMessages() && isAgentAvatarValid() && gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived() - && (LLMuteList::getInstance()->isLoaded() || LLMuteList::getInstance()->getLoadFailed())) + && LLMuteList::getInstance()->updateLoadState()) { std::string cap_url = gAgent.getRegionCapability("ReadOfflineMsgs"); diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 154e826196..02a5a43c6b 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -214,9 +214,9 @@ bool LLMuteList::isLinden(const std::string& name) return last_name == "linden"; } -bool LLMuteList::getLoadFailed() +bool LLMuteList::updateLoadState() { - if (mLoadState == ML_FAILED) + if (isLoaded() || isFailed()) { return true; } @@ -227,7 +227,7 @@ bool LLMuteList::getLoadFailed() { LL_WARNS() << "Mute list request timed out; trying cache fallback once" << LL_ENDL; tryLoadCacheFallback(gAgent.getID(), "request timeout"); - return mLoadState == ML_FAILED; + return isLoaded() || isFailed(); } } return false; diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 34857e2006..ff010a02e8 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -125,7 +125,11 @@ public: static bool isLinden(const std::string& name); bool isLoaded() const { return mLoadState == ML_LOADED; } - bool getLoadFailed(); + bool isFailed() const { return mLoadState == ML_FAILED; } + + // Advance the load state machine, trying cache fallback if necessary. + // Return value indicates mute list consumption readiness. + bool updateLoadState(); std::vector getMutes() const; -- cgit v1.3 From 840383def31edd7b60f49bbc82303bf642c0d1ec Mon Sep 17 00:00:00 2001 From: Darl Date: Thu, 2 Apr 2026 11:56:42 -0500 Subject: Eliminate pointless mutelist cache fallback attempt, instead manually set state to guard against a possible cache write to disk to be safe Signed-off-by: Darl --- indra/newview/llmutelist.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 02a5a43c6b..39588ba51d 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -833,7 +833,9 @@ void LLMuteList::requestFromServer(const LLUUID& agent_id) if (gDisconnected) { LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL; - tryLoadCacheFallback(agent_id, "disconnected before request"); + // Guard against potentially writing back to disk since we're not recovering our connection + mLoadState = ML_LOADED; + mLoadSource = MLS_FALLBACK_CACHE; return; } if (!gAgent.getRegion()) -- cgit v1.3 From 9978c669b42c1a7ebd96fbdf34abb211590af66a Mon Sep 17 00:00:00 2001 From: Darl Date: Fri, 3 Apr 2026 18:33:00 -0500 Subject: Improve documentation and logging of LLMuteList changes Signed-off-by: Darl --- indra/newview/llmutelist.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 39588ba51d..f8bdc453b2 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -92,8 +92,16 @@ public: const LLUUID& invoice, const sparam_t& strings) { + // We've gotten a message from the server that indicates our mute list is empty there. + + // First we want to make sure that if we had a cached mute list, we clear it assuming it is outdated. LLMuteList* mute_list = LLMuteList::getInstance(); - mute_list->clearCachedMutes(); + if(mute_list->mLoadSource == LLMuteList::MLS_FALLBACK_CACHE && !mute_list->getMutes().empty()) + { + LL_WARNS() << "Our current mute list is not empty, but the server says that it should be. Is our cache outdated, or did our mutes get lost?" << LL_ENDL; + mute_list->clearCachedMutes(); + } + // Lastly we set the load state to loaded with the source of server empty. We are now in a clean and ready state. mute_list->setLoaded(LLMuteList::MLS_SERVER_EMPTY); return true; } @@ -237,6 +245,7 @@ void LLMuteList::clearCachedMutes() { mMutes.clear(); mLegacyMutes.clear(); + LL_WARNS() << "Cached mutes cleared" << LL_ENDL; } const char* LLMuteList::sourceToString(EMuteListSource source) @@ -276,6 +285,7 @@ void LLMuteList::setFailed(const std::string& reason) { LL_WARNS() << "Mute list unavailable: " << reason << " (last source=" << sourceToString(mLoadSource) << ")" << LL_ENDL; } + notifyObservers(); } bool LLMuteList::tryLoadCacheFallback(const LLUUID& agent_id, const std::string& reason) @@ -880,7 +890,7 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - LL_WARNS() << "Got an mute list update for the wrong agent." << LL_ENDL; + LL_WARNS() << "Got a mute list update for the wrong agent." << LL_ENDL; return; } std::string unclean_filename; @@ -986,9 +996,14 @@ void LLMuteList::removeObserver(LLMuteListObserver* observer) void LLMuteList::setLoaded(EMuteListSource source) { + if(isLoaded()) + { + LL_WARNS() << "Mute list was already loaded from " << sourceToString(mLoadSource) << ", switching to " << sourceToString(source) << LL_ENDL; + } mLoadState = ML_LOADED; mLoadSource = source; notifyObservers(); + LL_INFOS() << "Mute list loaded from " << sourceToString(source) << LL_ENDL; } void LLMuteList::notifyObservers() -- cgit v1.3 From 0a42b9f220c320b9cdc43987ff1f0d9871a4958b Mon Sep 17 00:00:00 2001 From: Darl Date: Fri, 3 Apr 2026 22:57:53 -0500 Subject: Attempt one additional mute list request from simulator after region change Signed-off-by: Darl --- indra/newview/llmutelist.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++-- indra/newview/llmutelist.h | 16 +++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmutelist.cpp') diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index f8bdc453b2..b72301c566 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -190,6 +190,8 @@ LLMuteList::LLMuteList() : // but this way is just more convinient onAccountNameChanged(id, av_name.getUserName()); }); + // Register our region change callback handler early, we'll clean it up if/when we don't need it anymore. + mRegionChangedCallback = gAgent.addRegionChangedCallback(boost::bind(&LLMuteList::onRegionChanged, this)); } //----------------------------------------------------------------------------- @@ -203,6 +205,10 @@ LLMuteList::~LLMuteList() void LLMuteList::cleanupSingleton() { LLAvatarNameCache::getInstance()->setAccountNameChangedCallback(nullptr); + if (mRegionChangedCallback.connected()) + { + mRegionChangedCallback.disconnect(); + } } bool LLMuteList::isLinden(const std::string& name) @@ -826,6 +832,11 @@ bool LLMuteList::isMuted(const std::string& username, U32 flags) const //----------------------------------------------------------------------------- void LLMuteList::requestFromServer(const LLUUID& agent_id) { + if(isLoadedFromServer()) + { + LL_WARNS() << "Blocked attempt to request mute list from server when already loaded from server!" << LL_ENDL; + return; + } const std::string filename = getCacheFilename(agent_id); LLCRC crc; crc.update(filename); @@ -886,6 +897,11 @@ void LLMuteList::cache(const LLUUID& agent_id) void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) { LL_INFOS() << "LLMuteList::processMuteListUpdate()" << LL_ENDL; + LLMuteList* mute_list = getInstance(); + if(mute_list->mTriedRegionChangeRetry) + { + LL_WARNS() << "Received mute list update after retrying region change; success!" << LL_ENDL; + } LLUUID agent_id; msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) @@ -901,7 +917,6 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) LL_WARNS() << "Received empty mute list filename." << LL_ENDL; } - LLMuteList* mute_list = getInstance(); mute_list->mLoadState = ML_REQUESTED; mute_list->mRequestStartTime = LLTimer::getTotalSeconds(); @@ -922,7 +937,12 @@ void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**) { LL_INFOS() << "LLMuteList::processUseCachedMuteList()" << LL_ENDL; - LLMuteList::getInstance()->loadFromFile(LLMuteList::getInstance()->getCacheFilename(gAgent.getID()), MLS_SERVER_CACHE); + LLMuteList* mute_list = LLMuteList::getInstance(); + if(mute_list->mTriedRegionChangeRetry) + { + LL_WARNS() << "Received use cached mute list message after retrying region change; success!" << LL_ENDL; + } + mute_list->loadFromFile(mute_list->getCacheFilename(gAgent.getID()), MLS_SERVER_CACHE); } void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status) @@ -1004,6 +1024,11 @@ void LLMuteList::setLoaded(EMuteListSource source) mLoadSource = source; notifyObservers(); LL_INFOS() << "Mute list loaded from " << sourceToString(source) << LL_ENDL; + if(isLoadedFromServer() && mRegionChangedCallback.connected()) + { + LL_INFOS() << "Mute list loaded from server, disconnecting region change callback" << LL_ENDL; + mRegionChangedCallback.disconnect(); + } } void LLMuteList::notifyObservers() @@ -1032,6 +1057,23 @@ void LLMuteList::notifyObserversDetailed(const LLMute& mute) } } +void LLMuteList::onRegionChanged() +{ + // If we are in a degraded state, either some protocol messages got lost between us and our login region, or our login region was having a bad day. + // Since the previous region might've been unable to provide our mute list, we can try to request it again from our new region. + // This is limited to one retry per session. + if(isLoadedDegraded() && !mTriedRegionChangeRetry) + { + if(mRegionChangedCallback.connected()) + { + mRegionChangedCallback.disconnect(); + } + LL_WARNS() << "Region changed while mute list is in degraded state, queueing a retry of the mute list request" << LL_ENDL; + mTriedRegionChangeRetry = true; + requestFromServer(gAgent.getID()); + } +} + LLRenderMuteList::LLRenderMuteList() {} diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index ff010a02e8..2781e9b177 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -31,6 +31,8 @@ #include "lluuid.h" #include "llextendedstatus.h" +#include + class LLViewerObject; class LLMessageSystem; class LLMuteListObserver; @@ -124,8 +126,13 @@ public: static bool isLinden(const std::string& name); - bool isLoaded() const { return mLoadState == ML_LOADED; } - bool isFailed() const { return mLoadState == ML_FAILED; } + // Load state accessors. + bool isLoaded() const { return mLoadState == ML_LOADED; } // Loaded, but not necessarily from server. + bool isFailed() const { return mLoadState == ML_FAILED; } // Unable to load any mute list. Server did not reply. + // Loaded from server, which is the only source we consider authoritative. + bool isLoadedFromServer() const { return isLoaded() && (mLoadSource == MLS_SERVER || mLoadSource == MLS_SERVER_EMPTY); } + // Loaded, but from cache. Would be nice to upgrade to a server load from here if possible. + bool isLoadedDegraded() const { return isLoaded() && !isLoadedFromServer(); } // Advance the load state machine, trying cache fallback if necessary. // Return value indicates mute list consumption readiness. @@ -139,6 +146,9 @@ public: // call this method on logout to save everything. void cache(const LLUUID& agent_id); + // Handler for region change event, used for server request retries if isLoadedDegraded() is true + void onRegionChanged(); + private: void clearCachedMutes(); bool loadFromFile(const std::string& filename, EMuteListSource source); @@ -198,6 +208,8 @@ private: EMuteListSource mLoadSource; F64 mRequestStartTime; bool mTriedCacheFallback; + bool mTriedRegionChangeRetry; + boost::signals2::connection mRegionChangedCallback; friend class LLDispatchEmptyMuteList; }; -- cgit v1.3