From d2a3a157a023f8608c44dfd3b07e8986df88c416 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 8 May 2026 00:26:24 +0300 Subject: #5772 'Control not found' crash --- indra/newview/llagent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llagent.cpp') diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 3ab87cac13..3f5c0ad843 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2676,7 +2676,7 @@ void LLAgent::onAnimStop(const LLUUID& id) const bool up_pos = (mControlFlags & AGENT_CONTROL_UP_POS) != 0; const F64 now = LLTimer::getTotalSeconds(); const F64 elapsed = now - mLastJumpInputTime; - static LLCachedControl recent_jump_threshold_secs(gSavedSettings, "RecentJumpThresholdSecs"); + static LLCachedControl recent_jump_threshold_secs(gSavedSettings, "RecentJumpThresholdSecs", 1.0); const bool recent_jump = (mLastJumpInputTime > 0.0) && (elapsed < recent_jump_threshold_secs); if (!up_pos && !recent_jump) -- cgit v1.3 From a4e8f716a2cd682e1fd89eafbee45845648cdf72 Mon Sep 17 00:00:00 2001 From: Trish Date: Tue, 28 Apr 2026 19:34:37 -0400 Subject: FIX - Prejump regressions --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llagent.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'indra/newview/llagent.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7c98572d07..dcae9b9e12 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -492,7 +492,7 @@ RecentJumpThresholdSecs Comment - Seconds after a jump input during which finish-anim is suppressed to avoid interrupting rapid successive jumps. + Seconds after jump input during which landing finish-anim is suppressed to avoid interrupting rapid successive jumps. Persist 1 Type diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 3f5c0ad843..123ffa676e 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2669,17 +2669,20 @@ void LLAgent::onAnimStop(const LLUUID& id) else if (id == ANIM_AGENT_PRE_JUMP || id == ANIM_AGENT_LAND || id == ANIM_AGENT_MEDIUM_LAND) { // FIRE-34049/FIRE-34273/https://github.com/secondlife/viewer/issues/4218 - // Avoid forcing AGENT_CONTROL_FINISH_ANIM, which can short-circuit the next pre-jump - // during rapid successive jumps. + // Avoid forcing AGENT_CONTROL_FINISH_ANIM on landing, which can short-circuit the + // next pre-jump during rapid successive jumps. + // Do not suppress pre-jump finish, otherwise a quick tap from standing can stall. // TODO: a more robust fix would require knowing which specific animation finished, // information that is not currently provided by the simulator. + const bool is_landing_anim = (id == ANIM_AGENT_LAND || id == ANIM_AGENT_MEDIUM_LAND); const bool up_pos = (mControlFlags & AGENT_CONTROL_UP_POS) != 0; const F64 now = LLTimer::getTotalSeconds(); const F64 elapsed = now - mLastJumpInputTime; static LLCachedControl recent_jump_threshold_secs(gSavedSettings, "RecentJumpThresholdSecs", 1.0); const bool recent_jump = (mLastJumpInputTime > 0.0) && (elapsed < recent_jump_threshold_secs); + const bool suppress_finish = is_landing_anim && recent_jump; - if (!up_pos && !recent_jump) + if (!up_pos && !suppress_finish) { setControlFlags(AGENT_CONTROL_FINISH_ANIM); } -- cgit v1.3 From 9cd5eb8cac12b50e1590c1a472886c37aae58b7b Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Wed, 13 May 2026 22:53:45 +0300 Subject: #5806 add option to reset environment after teleport --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llagent.cpp | 6 ++++++ indra/newview/skins/default/xui/en/menu_viewer.xml | 11 +++++++++++ 3 files changed, 28 insertions(+) (limited to 'indra/newview/llagent.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index dcae9b9e12..5327e5b5eb 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13229,6 +13229,17 @@ Value 0 + SwitchToSharedEnvAfterTeleport + + Comment + Switch to Shared Environment after teleport + Persist + 1 + Type + Boolean + Value + 1 + PreferredBrowserBehavior Comment diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 123ffa676e..31814d8f4f 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -44,6 +44,7 @@ #include "llchicletbar.h" #include "llconsole.h" #include "lldonotdisturbnotificationstorage.h" +#include "llenvironment.h" #include "llfirstuse.h" #include "llfloatercamera.h" #include "llfloaterimcontainer.h" @@ -4172,6 +4173,11 @@ void LLAgent::handleTeleportFinished() mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::onCapabilitiesReceivedAfterTeleport)); } } + static LLCachedControl shared_env_on_teleport(gSavedSettings, "SwitchToSharedEnvAfterTeleport", true); + if (shared_env_on_teleport) + { + LLEnvironment::instance().setSharedEnvironment(); + } LLPerfStats::tunables.autoTuneTimeout = true; } diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 591bf6c712..953398c7bd 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1003,6 +1003,17 @@ parameter="region" /> + + + + + -- cgit v1.3 From 1832026b5896968bac100e67235ad242a40f2157 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Sat, 30 May 2026 01:35:57 +0300 Subject: #5856 An option to prevent hibernation --- indra/newview/app_settings/settings.xml | 11 + indra/newview/llagent.cpp | 9 + indra/newview/llappviewer.cpp | 48 +++- indra/newview/llappviewer.h | 15 +- indra/newview/llappviewerlinux.cpp | 302 +++++++++++++++++++++ indra/newview/llappviewerlinux.h | 10 + indra/newview/llappviewermacosx-objc.h | 1 + indra/newview/llappviewermacosx-objc.mm | 66 +++++ indra/newview/llappviewermacosx.cpp | 7 + indra/newview/llappviewermacosx.h | 1 + indra/newview/llappviewerwin32.cpp | 50 ++++ indra/newview/llappviewerwin32.h | 1 + indra/newview/llviewerwindow.cpp | 30 +- .../default/xui/en/panel_preferences_setup.xml | 36 +++ 14 files changed, 578 insertions(+), 9 deletions(-) (limited to 'indra/newview/llagent.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8086afb865..b1e161182e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -14429,6 +14429,17 @@ Value 180 + OSHibernationMode + + Comment + Whether to prevent OS from hibernating. 0 - can hibernate; 1 - can't hibernate, can turn screen off; 2 - can't hibernate, can't turn screen off + Persist + 1 + Type + S32 + Value + 0 + HeightUnits Comment diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 31814d8f4f..35d60e6595 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -1552,6 +1552,8 @@ void LLAgent::setAFK() setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP); gAwayTimer.start(); } + + LLAppViewer::instance()->setPermitOSHibernation(true); } //----------------------------------------------------------------------------- @@ -1570,6 +1572,13 @@ void LLAgent::clearAFK() sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP); clearControlFlags(AGENT_CONTROL_AWAY); } + + if (isAgentAvatarValid()) + { + // Only set this if agent is inworld, login screen + // shouldn't prevent hibernation. + LLAppViewer::instance()->setPermitOSHibernation(false); + } } //----------------------------------------------------------------------------- diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6b5c0ea6b0..4a6739bb40 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -633,6 +633,12 @@ bool LLAppViewer::sendURLToOtherInstance(const std::string& url) return false; } +//virtual +void LLAppViewer::setOSHibernationMode(eHibernationMode mode) +{ + // See OS specific files +} + //---------------------------------------------------------------------------- // LLAppViewer definition @@ -4474,9 +4480,12 @@ void LLAppViewer::abortQuit() mClosingFloaters = false; } -void LLAppViewer::sendViewerStatistics() +void LLAppViewer::sendViewerStatistics(bool include_preferences) { - send_viewer_stats(false); + if (!gDisconnected) + { + send_viewer_stats(include_preferences); + } } void LLAppViewer::migrateCacheDirectory() @@ -5857,6 +5866,29 @@ void LLAppViewer::outOfMemorySoftQuit() } } +void LLAppViewer::setPermitOSHibernation(bool permit) +{ + if (permit) + { + if (mCurrentHibernationMode != LL_HIBERNATE_MODE_DEFAULT) + { + // Will call OS specific code to let OS hibernate when idle + setOSHibernationMode(LL_HIBERNATE_MODE_DEFAULT); + mCurrentHibernationMode = LL_HIBERNATE_MODE_DEFAULT; + } + } + else + { + static LLCachedControl os_hibernation_mode(gSavedSettings, "OSHibernationMode", 0); + eHibernationMode mode = static_cast(os_hibernation_mode()); + if (mode != LL_HIBERNATE_MODE_DEFAULT && mCurrentHibernationMode != mode) + { + setOSHibernationMode(mode); + mCurrentHibernationMode = mode; + } + } +} + void LLAppViewer::idleNameCache() { // Neither old nor new name cache can function before agent has a region @@ -6064,6 +6096,9 @@ void LLAppViewer::disconnectViewer() // Pass the connection state to LLUrlEntryParcel not to attempt // parcel info requests while disconnected. LLUrlEntryParcel::setDisconnected(gDisconnected); + + // Restore default OS hibernation mode + setPermitOSHibernation(true); } void LLAppViewer::forceErrorLLError() @@ -6329,6 +6364,15 @@ void LLAppViewer::handleLoginComplete() // we logged in successfully, so save settings on logout LL_INFOS() << "Login successful, per account settings will be saved on log out." << LL_ENDL; mSavePerAccountSettings=true; + + // Don't allow hibernation while we're running + setPermitOSHibernation(false); + // Track 'hibernation' mode changes + mOSHibernationModeChangeConnection = gSavedSettings.getControl("OSHibernationMode")->getSignal()->connect([](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val) + { + // setPermitOSHibernation will sort itself out based on new mode. + LLAppViewer::instance()->setPermitOSHibernation(false); + }); } //virtual diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index cde58b0850..6ecafae036 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -114,7 +114,7 @@ public: const LLSD& substitutions = LLSD()); // Display an error dialog and forcibly quit. void earlyExitNoNotify(); // Do not display error dialog then forcibly quit. void abortQuit(); // Called to abort a quit request. - void sendViewerStatistics(); + void sendViewerStatistics(bool include_preferences); bool quitRequested() { return mQuitRequested; } bool logoutRequestSent() { return mLogoutRequestSent; } @@ -269,6 +269,8 @@ public: // Note: mQuitRequested can be aborted by user. void outOfMemorySoftQuit(); + virtual void setPermitOSHibernation(bool permit); + #ifdef LL_DISCORD static void initDiscordSocial(); static void updateDiscordActivity(); @@ -285,6 +287,14 @@ protected: virtual bool initSLURLHandler(); virtual bool sendURLToOtherInstance(const std::string& url); + typedef enum + { + LL_HIBERNATE_MODE_DEFAULT = 0, // Use the platform's default behavior. + LL_HIBERNATE_MODE_PREVENT = 1, + LL_HIBERNATE_MODE_PREVENT_SCREEN = 2, + } eHibernationMode; + virtual void setOSHibernationMode(eHibernationMode mode); + virtual bool initParseCommandLine(LLCommandLineParser& clp) { return true; } // Allow platforms to specify the command line args. @@ -391,6 +401,9 @@ private: LLAppCoreHttp mAppCoreHttp; bool mIsFirstRun; + + eHibernationMode mCurrentHibernationMode = LL_HIBERNATE_MODE_DEFAULT; + boost::signals2::scoped_connection mOSHibernationModeChangeConnection; }; // Globals with external linkage. From viewer.h diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 89d19d180b..fe0d6f6e7d 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -58,6 +58,10 @@ namespace void (*gOldTerminateHandler)() = NULL; } +// Initialize static members +guint32 LLAppViewerLinux::sPowerInhibitCookie = 0; +bool LLAppViewerLinux::sPowerInhibitActive = false; + static void exceptionTerminateHandler() { @@ -117,6 +121,11 @@ LLAppViewerLinux::LLAppViewerLinux() LLAppViewerLinux::~LLAppViewerLinux() { + // Clean up any power management inhibition on exit + if (sPowerInhibitActive) + { + uninhibitPowerManagement(); + } } bool LLAppViewerLinux::init() @@ -329,6 +338,299 @@ bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) } #endif // LL_DBUS_ENABLED + +void LLAppViewerLinux::setOSHibernationMode(eHibernationMode mode) +{ + if (mode == LL_HIBERNATE_MODE_DEFAULT) + { + // Allow OS to sleep/hibernate - remove any inhibition + if (sPowerInhibitActive) + { + uninhibitPowerManagement(); + LL_INFOS("OS") << "Permitted OS hibernation/sleep" << LL_ENDL; + } + } + else if (mode == LL_HIBERNATE_MODE_PREVENT) + { + // Prevent system sleep, but allow display to turn off + // Release any existing inhibition first to allow mode switching + if (sPowerInhibitActive) + { + uninhibitPowerManagement(); + } + + if (inhibitPowerManagement(false)) + { + LL_INFOS("OS") << "Prevented OS hibernation/sleep, display sleep allowed" << LL_ENDL; + } + else + { + LL_WARNS("OS") << "Failed to prevent OS hibernation/sleep" << LL_ENDL; + } + } + else if (mode == LL_HIBERNATE_MODE_PREVENT_SCREEN) + { + // Prevent both system and display sleep + // Release any existing inhibition first to allow mode switching + if (sPowerInhibitActive) + { + uninhibitPowerManagement(); + } + + if (inhibitPowerManagement(true)) + { + LL_INFOS("OS") << "Prevented OS hibernation/sleep and display sleep" << LL_ENDL; + } + else + { + LL_WARNS("OS") << "Failed to prevent OS hibernation/sleep and display sleep" << LL_ENDL; + } + } +} + +// TODO: This is AI Generated!!!, needs review and testing. +bool LLAppViewerLinux::inhibitPowerManagement(bool inhibit_display) +{ +#if LL_DBUS_ENABLED + // Try to use D-Bus to inhibit power management via various desktop environment APIs + // This works with GNOME, KDE, XFCE, and most modern Linux desktop environments + + if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) + { + LL_WARNS("OS") << "Failed to load D-Bus symbols for power management" << LL_ENDL; + return false; + } + + GError* error = nullptr; + DBusGConnection* bus = lldbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (!bus) + { + LL_WARNS("OS") << "Failed to connect to D-Bus session bus: " + << (error ? error->message : "unknown error") << LL_ENDL; + if (error) + g_error_free(error); + return false; + } + + // Try multiple power management services in order of preference + // 1. org.freedesktop.PowerManagement (older standard) + // 2. org.gnome.SessionManager (GNOME) + // 3. org.kde.Solid.PowerManagement (KDE) + + const char* services[] = { + "org.freedesktop.PowerManagement", + "org.gnome.SessionManager", + "org.kde.Solid.PowerManagement" + }; + + const char* paths[] = { + "/org/freedesktop/PowerManagement/Inhibit", + "/org/gnome/SessionManager", + "/org/kde/Solid/PowerManagement" + }; + + const char* interfaces[] = { + "org.freedesktop.PowerManagement.Inhibit", + "org.gnome.SessionManager", + "org.kde.Solid.PowerManagement" + }; + + const char* methods[] = { + "Inhibit", + "Inhibit", + "inhibit" + }; + + bool success = false; + + for (int i = 0; i < 3 && !success; ++i) + { + DBusGProxy* proxy = lldbus_g_proxy_new_for_name( + bus, + services[i], + paths[i], + interfaces[i] + ); + + if (!proxy) + continue; + + error = nullptr; + guint32 cookie = 0; + + if (i == 0) // freedesktop.PowerManagement + { + // Inhibit(application_name: s, reason: s) -> cookie: u + success = lldbus_g_proxy_call( + proxy, + methods[i], + &error, + G_TYPE_STRING, "Second Life Viewer", + G_TYPE_STRING, inhibit_display ? + "Viewer active - preventing system and display sleep" : + "Viewer active - preventing system sleep", + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID + ); + } + else if (i == 1) // GNOME SessionManager + { + // Inhibit(app_id: s, toplevel_xid: u, reason: s, flags: u) -> cookie: u + // flags: 4 = suspend, 8 = idle (display), 12 = both + guint32 flags = inhibit_display ? 12 : 4; + success = lldbus_g_proxy_call( + proxy, + methods[i], + &error, + G_TYPE_STRING, "SecondLifeViewer", + G_TYPE_UINT, 0, // toplevel_xid (0 = none) + G_TYPE_STRING, inhibit_display ? + "Viewer active - preventing system and display sleep" : + "Viewer active - preventing system sleep", + G_TYPE_UINT, flags, + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID + ); + } + else if (i == 2) // KDE Solid + { + // Different method signature for KDE + success = lldbus_g_proxy_call( + proxy, + methods[i], + &error, + G_TYPE_INVALID, + G_TYPE_INT, &cookie, + G_TYPE_INVALID + ); + } + + if (success) + { + sPowerInhibitCookie = cookie; + sPowerInhibitActive = true; + LL_INFOS("OS") << "Successfully inhibited power management using " + << services[i] << LL_ENDL; + } + else if (error) + { + LL_DEBUGS("OS") << "Failed to inhibit via " << services[i] + << ": " << error->message << LL_ENDL; + g_error_free(error); + error = nullptr; + } + + g_object_unref(proxy); + } + + return success; + +#else // !LL_DBUS_ENABLED + LL_WARNS("OS") << "Power management control not available - D-Bus support not enabled" << LL_ENDL; + return false; +#endif +} + +void LLAppViewerLinux::uninhibitPowerManagement() +{ +#if LL_DBUS_ENABLED + if (!sPowerInhibitActive || sPowerInhibitCookie == 0) + { + return; + } + + if (!grab_dbus_syms(DBUSGLIB_DYLIB_DEFAULT_NAME)) + { + LL_WARNS("OS") << "Failed to load D-Bus symbols for power management uninhibit" << LL_ENDL; + return; + } + + GError* error = nullptr; + DBusGConnection* bus = lldbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (!bus) + { + if (error) + g_error_free(error); + return; + } + + // Try to uninhibit using all services that might have been used + const char* services[] = { + "org.freedesktop.PowerManagement", + "org.gnome.SessionManager", + "org.kde.Solid.PowerManagement" + }; + + const char* paths[] = { + "/org/freedesktop/PowerManagement/Inhibit", + "/org/gnome/SessionManager", + "/org/kde/Solid/PowerManagement" + }; + + const char* interfaces[] = { + "org.freedesktop.PowerManagement.Inhibit", + "org.gnome.SessionManager", + "org.kde.Solid.PowerManagement" + }; + + const char* methods[] = { + "UnInhibit", + "Uninhibit", + "uninhibit" + }; + + bool success = false; + + for (int i = 0; i < 3; ++i) + { + DBusGProxy* proxy = lldbus_g_proxy_new_for_name( + bus, + services[i], + paths[i], + interfaces[i] + ); + + if (!proxy) + continue; + + error = nullptr; + + if (lldbus_g_proxy_call( + proxy, + methods[i], + &error, + G_TYPE_UINT, sPowerInhibitCookie, + G_TYPE_INVALID, + G_TYPE_INVALID)) + { + success = true; + LL_INFOS("OS") << "Successfully uninhibited power management using " + << services[i] << LL_ENDL; + } + else if (error) + { + LL_DEBUGS("OS") << "Failed to uninhibit via " << services[i] + << ": " << error->message << LL_ENDL; + g_error_free(error); + error = nullptr; + } + + g_object_unref(proxy); + + if (success) + break; + } + + sPowerInhibitCookie = 0; + sPowerInhibitActive = false; + +#endif // LL_DBUS_ENABLED +} + void LLAppViewerLinux::initCrashReporting(bool reportFreeze) { std::string cmd =gDirUtilp->getExecutableDir(); diff --git a/indra/newview/llappviewerlinux.h b/indra/newview/llappviewerlinux.h index dde223878d..6ab3682515 100644 --- a/indra/newview/llappviewerlinux.h +++ b/indra/newview/llappviewerlinux.h @@ -68,6 +68,16 @@ protected: virtual bool initSLURLHandler(); virtual bool sendURLToOtherInstance(const std::string& url); + virtual void setOSHibernationMode(eHibernationMode mode); + +private: + // Power management state tracking + static guint32 sPowerInhibitCookie; + static bool sPowerInhibitActive; + + // Helper methods for power management + bool inhibitPowerManagement(bool inhibit_display); + void uninhibitPowerManagement(); }; #if LL_DBUS_ENABLED diff --git a/indra/newview/llappviewermacosx-objc.h b/indra/newview/llappviewermacosx-objc.h index bfbb48dadb..13b8056193 100644 --- a/indra/newview/llappviewermacosx-objc.h +++ b/indra/newview/llappviewermacosx-objc.h @@ -32,5 +32,6 @@ void force_ns_sxeption(); void register_url_schemes(); +void set_os_hibernation_mode(int mode); #endif // LL_LLAPPVIEWERMACOSX_OBJC_H diff --git a/indra/newview/llappviewermacosx-objc.mm b/indra/newview/llappviewermacosx-objc.mm index 75d6b56e3e..ab4ae428b9 100644 --- a/indra/newview/llappviewermacosx-objc.mm +++ b/indra/newview/llappviewermacosx-objc.mm @@ -29,6 +29,7 @@ #endif #import +#import #include #include "llappviewermacosx-objc.h" @@ -63,3 +64,68 @@ void register_url_schemes() } } } + +// Add these as static variables at file scope +static IOPMAssertionID gPowerAssertionID = kIOPMNullAssertionID; + +void set_os_hibernation_mode(int mode) +{ + // Release existing assertion + if (gPowerAssertionID != kIOPMNullAssertionID) + { + IOReturn result = IOPMAssertionRelease(gPowerAssertionID); + if (result == kIOReturnSuccess) + { + gPowerAssertionID = kIOPMNullAssertionID; + NSLog(@"Permitted OS hibernation/sleep"); + } + else + { + NSLog(@"Failed to release power assertion: %d", result); + } + } + + if (mode == 1) + { + // Prevent OS from sleeping/hibernating + CFStringRef assertionName = CFSTR("Second Life Viewer"); + // kIOPMAssertionTypeNoIdleSleep prevents idle sleep + IOReturn result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoIdleSleep, + kIOPMAssertionLevelOn, + assertionName, + &gPowerAssertionID + ); + + if (result == kIOReturnSuccess) + { + NSLog(@"Prevented OS hibernation/sleep, allow display sleep"); + } + else + { + NSLog(@"Failed to create power assertion: %d", result); + } + } + else if (mode == 2) + { + // Prevent OS from sleeping/hibernating, prevent screen from going off + CFStringRef assertionName = CFSTR("Second Life Viewer"); + // kIOPMAssertionTypeNoIdleSleep prevents idle sleep + // kIOPMAssertionTypeNoDisplaySleep prevents display sleep + IOReturn result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, + assertionName, + &gPowerAssertionID + ); + + if (result == kIOReturnSuccess) + { + NSLog(@"Prevented OS hibernation/sleep or screen from turning off"); + } + else + { + NSLog(@"Failed to create power assertion: %d", result); + } + } +} diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 830e2d4473..1c01d06852 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -125,6 +125,7 @@ bool pumpMainLoop() void cleanupViewer() { + set_os_hibernation_mode(0); // restore default OS hibernation behavior if(!LLApp::isError()) { if (gViewerAppPtr) @@ -427,6 +428,12 @@ bool LLAppViewerMacOSX::initSLURLHandler() return true; } +void LLAppViewerMacOSX::setOSHibernationMode(eHibernationMode mode) +{ + // pass to objective-c++ + set_os_hibernation_mode((int)mode); +} + std::string LLAppViewerMacOSX::generateSerialNumber() { char serial_md5[MD5HEX_STR_SIZE]; // Flawfinder: ignore diff --git a/indra/newview/llappviewermacosx.h b/indra/newview/llappviewermacosx.h index 35fc99dbd9..1153bd4191 100644 --- a/indra/newview/llappviewermacosx.h +++ b/indra/newview/llappviewermacosx.h @@ -47,6 +47,7 @@ public: protected: virtual bool restoreErrorTrap(); virtual bool initSLURLHandler(); + virtual void setOSHibernationMode(eHibernationMode mode); std::string generateSerialNumber(); virtual bool initParseCommandLine(LLCommandLineParser& clp); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 5184b0f025..25aeb2a26e 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -1020,6 +1020,56 @@ bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url) return false; } +void LLAppViewerWin32::setOSHibernationMode(eHibernationMode mode) +{ + // ES_CONTINUOUS tells Windows to reset the idle timer + // and restore normal operation + // ES_SYSTEM_REQUIRED prevents system sleep/hibernation + // ES_DISPLAY_REQUIRED prevents display sleep + + if (mode == LL_HIBERNATE_MODE_DEFAULT) + { + // Allow OS to hibernate - clear the previous execution state flags + // ES_CONTINUOUS without other flags allows the system to idle normally + SetThreadExecutionState(ES_CONTINUOUS); + LL_INFOS("OS") << "Permitted OS hibernation/sleep" << LL_ENDL; + } + else if (mode == LL_HIBERNATE_MODE_PREVENT) + { + // Prevent OS from hibernating while viewer is running + // ES_CONTINUOUS | ES_SYSTEM_REQUIRED keeps the system awake + EXECUTION_STATE result = SetThreadExecutionState( + ES_CONTINUOUS | ES_SYSTEM_REQUIRED + ); + if (result == NULL) + { + LL_WARNS("OS") << "Failed to prevent OS hibernation, error: " << GetLastError() << LL_ENDL; + } + else + { + LL_INFOS("OS") << "Prevented OS hibernation, but allowed display sleep" << LL_ENDL; + } + } + else if (mode == LL_HIBERNATE_MODE_PREVENT_SCREEN) + { + // Prevent OS from hibernating or turning screen off while viewer is running + // ES_CONTINUOUS | ES_SYSTEM_REQUIRED keeps the system awake + // ES_DISPLAY_REQUIRED keeps the display on + EXECUTION_STATE result = SetThreadExecutionState( + ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED + ); + + if (result == NULL) + { + LL_WARNS("OS") << "Failed to prevent OS hibernation and display sleep, error: " << GetLastError() << LL_ENDL; + } + else + { + LL_INFOS("OS") << "Prevented OS hibernation/sleep" << LL_ENDL; + } + } +} + bool LLAppViewerWin32::sendShutdownToOtherInstances(const std::wstring& install_dir) { // Velopack installs viewer like this: diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 971907694a..7c3c7a475a 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -62,6 +62,7 @@ protected: bool restoreErrorTrap() override; bool sendURLToOtherInstance(const std::string& url) override; + void setOSHibernationMode(eHibernationMode mode) override; std::string generateSerialNumber(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index abd7096f50..4229ffcfb5 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1487,12 +1487,30 @@ void LLViewerWindow::handleCloseRequestCanceled() void LLViewerWindow::handleSuspendRequest() { - LLAppViewer::instance()->sendViewerStatistics(); - // Todo: this should send a disconnect request as viewer - // can't keep heartbeat up while suspended and will get - // disconnected within a minute. - // Add a disconnect here once 'prevent OS from sleeping' - // feature is ready. + static LLCachedControl os_hibernation_mode(gSavedSettings, "OSHibernationMode", 0); + if (os_hibernation_mode == 0) + { + LL_INFOS() << "Got a 'suspend' event from OS" << LL_ENDL; + // Viewer doesn't handle hibernation. + // Just send statistics. + LLAppViewer::instance()->sendViewerStatistics(false); + } + else + { + LL_INFOS() << "Got a 'suspend' event from OS, disconnecting" << LL_ENDL; + // Viewer is set to prevent hibernation if agent isn't away. + // If we got here, likely Agent 'went' away then viewer got + // a hibernation message. + // We have a limited timeframe. Sends stats then disconnect. + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + LLAppViewer::instance()->sendViewerStatistics(true); + LLAppViewer::instance()->metricsSend(!gDisconnected); + // Make sure to show a message. + LLAppViewer::instance()->forceDisconnect(LLTrans::getString("YouHaveBeenDisconnected")); + } + } } bool LLViewerWindow::handleCloseRequest(LLWindow *window, bool from_user) diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 2036ed75ca..d5f2f69df7 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -246,5 +246,41 @@ + + Prevent OS from hibernating if not Away: + + + + + + -- cgit v1.3 From b906284977b120c4c554fd08b78fac62759b160a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 22 Jul 2026 09:01:47 -0700 Subject: Defer processing AvatarAppearance until after first ObjectUpdate (#6017) * make AvatarAppearance a low-priority message * remember agent_id of unexpected AvatarAppearance message for later --- indra/llmessage/llpacketbuffer.h | 7 ++++ indra/llmessage/message.cpp | 80 +++++++++++++++++++++++++++++++-------- indra/llmessage/message.h | 6 ++- indra/llprimitive/llprimitive.cpp | 8 +++- indra/llprimitive/llprimitive.h | 3 ++ indra/newview/llagent.cpp | 1 + indra/newview/llviewermessage.cpp | 9 +++-- indra/newview/llvoavatar.cpp | 18 ++++++++- indra/newview/llvoavatar.h | 21 +++++++++- 9 files changed, 128 insertions(+), 25 deletions(-) (limited to 'indra/newview/llagent.cpp') diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h index b0505e7afd..a08e6c22e5 100644 --- a/indra/llmessage/llpacketbuffer.h +++ b/indra/llmessage/llpacketbuffer.h @@ -49,11 +49,18 @@ public: void init(S32 hSocket); void init(const char* buffer, S32 data_size, const LLHost& host); + // Whether LLCircuitData::checkPacketInID() has already been run for this + // packet (done at socket-read time, before this packet was sorted into + // the high/low priority inbound queue). + bool getPacketIDChecked() const { return mPacketIDChecked; } + void setPacketIDChecked(bool checked) { mPacketIDChecked = checked; } + protected: char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */ S32 mSize; // size of buffer in bytes LLHost mHost; // source/dest IP and port LLHost mReceivingIF; // source/dest IP and port + bool mPacketIDChecked = false; }; #endif diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 52180b20f1..8d86856706 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -519,10 +519,11 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) bool recv_resent = false; S32 num_acks = 0; S32 true_rcv_size = 0; + bool recv_packet_id_checked = false; U8* buffer = mTrueReceiveBuffer; - mTrueReceiveSize = receivePacketOrDrop((char *)mTrueReceiveBuffer); + mTrueReceiveSize = receivePacketOrDrop((char *)mTrueReceiveBuffer, recv_packet_id_checked); // If you want to dump all received packets into SecondLife.log, uncomment this //dumpPacketToLog(); @@ -686,7 +687,7 @@ bool LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) if ( valid_packet ) { - logValidMsg(cdp, host, recv_reliable, recv_resent, num_acks>0 ); + logValidMsg(cdp, host, recv_reliable, recv_resent, num_acks>0, recv_packet_id_checked ); valid_packet = mTemplateMessageReader->readMessage(buffer, host); } @@ -866,19 +867,37 @@ bool LLMessageSystem::computeDrop() bool LLMessageSystem::isHighPriorityMessage(const LLPacketBuffer& pkt) const { S32 size = pkt.getSize(); - if (size < LL_PACKET_ID_SIZE + 1) + + // avoid stepping out of bounds + const S32 MIN_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 4; + if (size < MIN_VALID_PACKET_SIZE) { return false; } - // We want to prioritize crucial messages use to establish viewer <--> simulator connection, + // We want to prioritize crucial messages used to establish viewer <--> simulator connection, // which are all low-frequency. A simple approximation is to just prioritize all non high- // frequency messages. // - // High frequency messages use a single byte for message_id whereas all low- and medium- - // frequency messages have 255 at the first byte of the message_id (which is after the - // LL_PACKET_ID_SIZE bytes of packet_id). - return *((const U8*)pkt.getData() + LL_PACKET_ID_SIZE) == 255; + // High frequency messages use a single byte for message_id whereas medium- and low- + // frequency messages have 255 at the first byte (which is after the LL_PACKET_ID_SIZE + // bytes of packet_id). + const U8* header = (const U8*)pkt.getData() + LL_PACKET_ID_SIZE; + if (header[0] != 255) + { + // high-frequency message + return false; + } + + // BUG: The low-frequency AvatarAppearance message will be ignored if its agent is unknown + // and the agent is created upon receipt of its first high-frequency ObjectUpdate message. + // This race condition will be exacerbated by our default prioritization strategy. + // + // WORKAROUND: All middle- and low-frequency messages are high-priority except AvatarAppearance + // + // AvatarAppearance is "Low 158" which means it is stored in four bytes: 0xff 0xff 0x00 0x9E + // the last two bytes represent 158 in a BigEndian U16 + return header[1] != 255 && header[2] != 0 && header[3] != 158; } void LLMessageSystem::dropPackets(U32 num_to_drop) @@ -891,8 +910,10 @@ void LLMessageSystem::setDropPercentage(F32 percent_to_drop) mDropPercentage = percent_to_drop; } -S32 LLMessageSystem::receivePacketOrDrop(char* datap) +S32 LLMessageSystem::receivePacketOrDrop(char* datap, bool& packet_id_already_checked) { + packet_id_already_checked = false; + if (getNumBufferedPackets() > 0) { LLHost invalid_host; @@ -905,6 +926,7 @@ S32 LLMessageSystem::receivePacketOrDrop(char* datap) S32 packet_size = pkt.getSize(); mLastSender = pkt.getHost(); mLastReceivingIF = pkt.getReceivingInterface(); + packet_id_already_checked = pkt.getPacketIDChecked(); if (packet_size > 0) { @@ -913,7 +935,8 @@ S32 LLMessageSystem::receivePacketOrDrop(char* datap) return packet_size; } - // Read directly from the socket. + // Read directly from the socket. checkPacketInID() has not run yet for + // this packet. bool drop = computeDrop(); S32 packet_size = 0; if (LLProxy::isSOCKSProxyEnabled()) @@ -1034,10 +1057,24 @@ S32 LLMessageSystem::bufferInboundPacket() } } - // ACK inbound reliable packet ASAP - if (cdp && (data[0] & LL_RELIABLE_FLAG)) + if (cdp) { - cdp->collectRAck(recv_packet_id); + // ACK inbound reliable packet ASAP + if ((data[0] & LL_RELIABLE_FLAG)) + { + cdp->collectRAck(recv_packet_id); + } + + // Check packet sequencing here, in true socket-arrival order, before + // this packet is sorted into the high/low priority inbound queue. + // Skip genuine duplicate resends, same as checkMessages()/logValidMsg() + // would do further downstream. + bool recv_resent = (data[0] & LL_RESENT_FLAG) != 0; + if (!recv_resent || !cdp->isDuplicateResend(recv_packet_id)) + { + cdp->checkPacketInID(recv_packet_id, recv_resent); + pkt.setPacketIDChecked(true); + } } if (isHighPriorityMessage(pkt)) @@ -1668,7 +1705,13 @@ void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host ) } } -void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, bool recv_reliable, bool recv_resent, bool recv_acks ) +void LLMessageSystem::logValidMsg( + LLCircuitData *cdp, + const LLHost& host, + bool recv_reliable, + bool recv_resent, + bool recv_acks, + bool skip_packet_id_check ) { if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM) { @@ -1685,8 +1728,13 @@ void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, bool r if (cdp) { - // update circuit packet ID tracking (missing/out of order packets) - cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); + if (!skip_packet_id_check) + { + // update circuit packet ID tracking (missing/out of order packets) + // Already done in bufferInboundPacket(), in true socket-arrival + // order, if this packet came off the high/low priority queues. + cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent ); + } cdp->addBytesIn( (S32Bytes)mTrueReceiveSize ); } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index e81cfe16a9..e23a67c04a 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -910,7 +910,7 @@ private: void logMsgFromInvalidCircuit( const LLHost& sender, bool recv_reliable ); void logTrustedMsgFromUntrustedCircuit( const LLHost& sender ); - void logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks ); + void logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks, bool skip_packet_id_check ); class LLMessageCountInfo { @@ -967,8 +967,10 @@ private: // Receive one packet: pop from ring if buffered, else read from mSocket. // Sets mLastSender and mLastReceivingIF. + // Sets packet_id_already_checked to whether checkPacketInID() was already + // run for this packet back when it was buffered (see bufferInboundPacket()). // Returns packet_size, or 0 if no packet or packet was dropped. - S32 receivePacketOrDrop(char* datap); + S32 receivePacketOrDrop(char* datap, bool& packet_id_already_checked); // Read one raw packet from mSocket into inbound message queues // Returns packet_size (0 if no packet was available). diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 7a638dd625..fd755e35dd 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1369,6 +1369,12 @@ bool LLPrimitive::packTEMessage(LLDataPacker &dp) const } S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec) +{ + return parseTEMessage(mesgsys, block_name, block_num, tec, getNumTEs()); +} + +// static +S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec, U8 face_count) { S32 retval = 0; // temp buffer for material ID processing @@ -1403,7 +1409,7 @@ S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name tec.packed_buffer[tec.size] = 0x00; ++tec.size; - tec.face_count = llmin((U32)getNumTEs(),(U32)LLTEContents::MAX_TES); + tec.face_count = llmin((U32)face_count,(U32)LLTEContents::MAX_TES); U8 *cur_ptr = tec.packed_buffer; LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffere sized: " << tec.size << LL_ENDL; diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index c3e3e19ee9..21bf52610e 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -502,6 +502,9 @@ public: S32 unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num); // Variable num of blocks S32 unpackTEMessage(LLDataPacker &dp); S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec); + // Same as above, but usable before an instance exists to derive the face count from + // (e.g. decoding a message for an object that hasn't been created yet). + static S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec, U8 face_count); S32 applyParsedTEMessage(LLTEContents& tec); #ifdef CHECK_FOR_FINITE diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 35d60e6595..2f0ad15d18 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4151,6 +4151,7 @@ void LLAgent::handleTeleportFinished() } clearTeleportRequest(); mTeleportCanceled.reset(); + LLVOAvatar::resetEarlyAppearanceList(); if (mIsMaturityRatingChangingDuringTeleport) { // notify user that the maturity preference has been changed diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1f1ff30b8f..e435ce08e3 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4181,11 +4181,12 @@ void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) if (avatarp) { avatarp->processAvatarAppearance( mesgsys ); + return; } - else - { - LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; - } + // The avatar object doesn't exist yet. + // We will re-request its appearance data after it is created. + LLVOAvatar::registerEarlyAppearance(uuid); + LL_WARNS("Messaging") << "AvatarAppearance received for avatar " << uuid << " before object created" << LL_ENDL; } void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index cd71a2c660..89ee5ede2a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -615,6 +615,8 @@ const LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] = SND_RUBBER_RUBBER }; +uuid_list_t LLVOAvatar::sEarlyAppearanceList; + S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS; S32 LLVOAvatar::sRenderGroupTitles = RENDER_GROUP_TITLE_ALWAYS; S32 LLVOAvatar::sNumVisibleChatBubbles = 0; @@ -779,6 +781,20 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mVisuallyMuteSetting = LLVOAvatar::VisualMuteSettings(LLRenderMuteList::getInstance()->getSavedVisualMuteSetting(getID())); sInstances.push_back(this); + + uuid_list_t::iterator it = sEarlyAppearanceList.find(id); + if (it != sEarlyAppearanceList.end()) + { + // Note: aside from LLVOAvatar::resetEarlyAppearanceList() (called on + // teleport), this is the only place where we remove from + // sEarlyAppearanceList, which means any agent who receives an + // AvatarAppearance message but is never actually instantiated will + // remain on the list until the next teleport. This is a resource leak + // but we expect it to be small enough per-session to not cause problems. + sEarlyAppearanceList.erase(it); + LL_INFOS("Avatar") << "Re-requesting AvatarAppearance for new avatar " << id << LL_ENDL; + LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); + } } std::string LLVOAvatar::avString() const @@ -9937,7 +9953,7 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte if (visualParamWeightsAreDefault() && mRuthTimer.getElapsedTimeF32() > LOADING_TIMEOUT_SECONDS) { // re-request appearance, hoping that it comes back with a shape next time - LL_INFOS() << "Re-requesting AvatarAppearance for object: " << getID() << LL_ENDL; + LL_INFOS() << "Re-requesting AvatarAppearance for agent: " << getID() << LL_ENDL; LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); mRuthTimer.reset(); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 3f33dc54b8..3457e7bb6a 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -949,9 +949,26 @@ protected: ** APPEARANCE **/ +public: + // Used when an AvatarAppearance UDP message is received before the + // corresponding avatar could be created. + static void registerEarlyAppearance(const LLUUID& av_id) + { + sEarlyAppearanceList.emplace(av_id); + } + + // Entries left behind by agents who never get instantiated (e.g. an + // AvatarAppearance message arrives for an avatar we never rez) are a + // small resource leak. Teleporting to another region invalidates the + // whole list, since it was only ever relevant to avatars in the region + // we're leaving, so clear it out at that point to bound the leak. + static void resetEarlyAppearanceList() + { + sEarlyAppearanceList.clear(); + } + LLPointer mLastProcessedAppearance; -public: void parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMessageContents& msg); void processAvatarAppearance(LLMessageSystem* mesgsys); void applyParsedAppearanceMessage(LLAppearanceMessageContents& contents, bool slam_params); @@ -983,6 +1000,8 @@ private: bool mIsEditingAppearance; // flag for if we're actively in appearance editing mode bool mUseLocalAppearance; // flag for if we're using a local composite + static uuid_list_t sEarlyAppearanceList; + //-------------------------------------------------------------------- // Visibility //-------------------------------------------------------------------- -- cgit v1.3