From c9030fc150c19d428586febc5a911dcf473f81cc Mon Sep 17 00:00:00 2001 From: leviathan Date: Wed, 11 Mar 2026 15:22:33 -0700 Subject: [PATCH 1/3] more aggressive retry strategy for crucial connection Messages --- indra/newview/app_settings/settings.xml | 14 +-- indra/newview/llstartup.cpp | 157 +++++++++++++++++++----- indra/newview/llviewermessage.cpp | 21 +++- indra/newview/llviewerregion.cpp | 21 +++- 4 files changed, 172 insertions(+), 41 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0671aadee58..03f3ed204b2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2623,7 +2623,7 @@ Value 0 - DoubleClickTeleport + DoubleClickTeleport Comment Enable double-click to teleport where allowed (afects minimap and people panel) @@ -9285,7 +9285,7 @@ Value 1.0 - + RenderReflectionProbeDrawDistance Comment @@ -9484,7 +9484,7 @@ Value 1.0 - + RenderReflectionProbeMaxLocalLightAmbiance Comment @@ -11019,7 +11019,7 @@ Boolean Value 0 - + NearbyListShowMap Comment @@ -13140,7 +13140,7 @@ Type S32 Value - 3 + 20 UseCircuitCodeTimeout @@ -13151,7 +13151,7 @@ Type F32 Value - 5.0 + 0.5 UseDebugLogin @@ -16319,7 +16319,7 @@ Type Boolean Value - 1 + 1 UpdateAppWindowTitleBar diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 018c23963cb..5ae4e1afb23 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -24,6 +24,72 @@ * $/LicenseInfo$ */ +// +// LOGIN AND CONNECTION SEQUENCE OVERVIEW +// ====================================== +// The Viewer connects to the SL service in two phases: HTTP authentication +// followed by UDP "Circuit" establishment to the first Simulator. +// +// PHASE 1: HTTP LOGIN (see lllogin.cpp, process_login_success_response()) +// ----------------------------------------------------------------------- +// Viewer sends an XMLRPC HTTP POST to LoginServer containing: +// - Credentials (first name, last name, password) +// - Client version, channel, MAC address, machine ID +// - Start location preferences +// +// Login-server responds with critical connection data: +// - agent_id, session_id, secure_session_id (authentication tokens) +// - Circuit_code (used to establish UDP Circuit with Simulator) +// - sim_ip, sim_port (Simulator address to connect to) +// - seed_capability (base URL for HTTP capability requests) +// - region_x, region_y (region grid coordinates) +// +// PHASE 2: UDP CIRCUIT ESTABLISHMENT (see idle_startup() state machine below) +// --------------------------------------------------------------------------- +// After HTTP login succeeds, Viewer establishes a UDP Circuit with the +// simulator. This also happens whenever the Viewer connects to new Simulators +// in the same session. The following UDP messages are exchanged: +// +// 1. UseCircuitCode (Viewer -> Simulator) +// - Sent in STATE_WORLD_INIT +// - Contains: Circuit_code, session_id, agent_id +// - Establishes the UDP Circuit with the Simulator +// +// 2. RegionHandshake (Simulator -> Viewer) +// - Handled by process_region_handshake() in llworld.cpp +// - Contains: region name, terrain textures, water height, region flags +// - Viewer responds with RegionHandshakeReply +// +// 3. CompleteAgentMovement (Viewer -> Simulator) +// - Sent in STATE_AGENT_SEND via send_complete_agent_movement() +// - Signals the Viewer is ready to enter the world +// +// 4. AgentMovementComplete (Simulator -> Viewer) +// - Handled by process_agent_movement_complete() in llviewermessage.cpp +// - Contains: final agent position, look_at direction, region handle +// - Sets gAgentMovementCompleted = true +// - Agent is now fully connected to the region +// +// STARTUP STATE MACHINE +// --------------------- +// The connection sequence is managed by idle_startup() which progresses +// through these key states: +// +// STATE_LOGIN_WAIT - Waiting for HTTP login response +// STATE_LOGIN_PROCESS_RESPONSE - Processing login response data +// STATE_WORLD_INIT - Send UseCircuitCode, enable UDP Circuit +// STATE_WORLD_WAIT - Wait for Circuit acknowledgment +// STATE_AGENT_SEND - Send CompleteAgentMovement +// STATE_AGENT_WAIT - Wait for AgentMovementComplete +// STATE_INVENTORY_SEND - Agent connected, begin loading inventory +// +// HTTP CAPABILITIES +// ----------------- +// After UDP connection, Viewer fetches "capability" URLs from the +// seed_capability endpoint. These provide HTTP endpoints for various +// services (inventory, textures, etc.) that supplement the UDP protocol. +// + #include "llviewerprecompiledheaders.h" #include "llappviewer.h" @@ -271,7 +337,6 @@ void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); bool login_alert_status(const LLSD& notification, const LLSD& response); -void use_circuit_callback(void**, S32 result); void register_viewer_callbacks(LLMessageSystem* msg); void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32); bool callback_choose_gender(const LLSD& notification, const LLSD& response); @@ -1707,13 +1772,48 @@ bool idle_startup() gUseCircuitCallbackCalled = false; msg->enableCircuit(gFirstSim, true); - // now, use the circuit info to tell simulator about us! + + // UDP CONNECTION STEP 1: Send UseCircuitCode + // This is the first UDP message sent to Simulator after HTTP login. + // It establishes the UDP circuit using the circuit_code received from + // LoginServer. Simulator will respond with an ACK, then send + // RegionHandshake message with region details. LL_INFOS("AppInit") << "viewer: UserLoginLocationReply() Enabling " << gFirstSim << " with code " << msg->mOurCircuitCode << LL_ENDL; msg->newMessageFast(_PREHASH_UseCircuitCode); msg->nextBlockFast(_PREHASH_CircuitCode); msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); + + // build a lambda to be used as callback on ACK or timeout + void (*use_circuit_callback)(void**, S32) = [](void**, S32 result) + { + // bail if we're quitting. + if(LLApp::isExiting()) return; + if( !gUseCircuitCallbackCalled ) + { + gUseCircuitCallbackCalled = true; + if (result != LL_ERR_NOERR) + { + // Make sure user knows something bad happened. JC + LL_WARNS("AppInit") << "Backing up to login screen!" << LL_ENDL; + if (gRememberPassword) + { + LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); + } + else + { + LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); + } + reset_login(); + } + else + { + gGotUseCircuitCodeAck = true; + } + } + }; + msg->sendReliable( gFirstSim, gSavedSettings.getS32("UseCircuitCodeMaxRetries"), @@ -1731,6 +1831,9 @@ bool idle_startup() //--------------------------------------------------------------------- // World Wait //--------------------------------------------------------------------- + // UDP CONNECTION STEP 2: Wait for UseCircuitCode acknowledgment + // While waiting, Simulator also sends RegionHandshake (handled by + // process_region_handshake() in llworld.cpp) containing region info. if(STATE_WORLD_WAIT == LLStartUp::getStartupState()) { LL_DEBUGS("AppInit") << "Waiting for simulator ack...." << LL_ENDL; @@ -1746,13 +1849,16 @@ bool idle_startup() //--------------------------------------------------------------------- // Agent Send //--------------------------------------------------------------------- + // UDP CONNECTION STEP 3: Send CompleteAgentMovement + // After the circuit is established and RegionHandshake received, we signal + // to Simulator the Viewer is ready to enter the world. if (STATE_AGENT_SEND == LLStartUp::getStartupState()) { LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL; set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD); do_startup_frame(); - // register with the message system so it knows we're - // expecting this message + // Register handler process_agent_movement_complete for AgentMovementComplete - + // the final UDP message confirming the agent is connected. LLMessageSystem* msg = gMessageSystem; msg->setHandlerFuncFast( _PREHASH_AgentMovementComplete, @@ -1795,12 +1901,17 @@ bool idle_startup() //--------------------------------------------------------------------- // Agent Wait //--------------------------------------------------------------------- + // UDP CONNECTION STEP 4: Wait for AgentMovementComplete + // Simulator responds with the agent's confirmed position and look_at + // direction. Once received, gAgentMovementCompleted is set true and the + // agent is fully connected to the region. if (STATE_AGENT_WAIT == LLStartUp::getStartupState()) { do_startup_frame(); if (gAgentMovementCompleted) { + // Connection complete - agent is now in-world LLStartUp::setStartupState( STATE_INVENTORY_SEND ); } do_startup_frame(); @@ -2728,34 +2839,6 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) } -void use_circuit_callback(void**, S32 result) -{ - // bail if we're quitting. - if(LLApp::isExiting()) return; - if( !gUseCircuitCallbackCalled ) - { - gUseCircuitCallbackCalled = true; - if (result) - { - // Make sure user knows something bad happened. JC - LL_WARNS("AppInit") << "Backing up to login screen!" << LL_ENDL; - if (gRememberPassword) - { - LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); - } - else - { - LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); - } - reset_login(); - } - else - { - gGotUseCircuitCodeAck = true; - } - } -} - void register_viewer_callbacks(LLMessageSystem* msg) { msg->setHandlerFuncFast(_PREHASH_LayerData, process_layer_data ); @@ -3648,6 +3731,14 @@ bool init_benefits(LLSD& response) return succ; } +// HTTP LOGIN RESPONSE PROCESSING +// Called after successful HTTP XMLRPC authentication. Extracts critical data +// from LoginServer response needed to establish the UDP connection: +// - agent_id, session_id, secure_session_id (authentication tokens) +// - circuit_code (used in UseCircuitCode UDP message) +// - sim_ip, sim_port (simulator address for UDP circuit) +// - seed_capability (URL for fetching HTTP capability endpoints) +// bool process_login_success_response() { LLSD response = LLLoginInstance::getInstance()->getResponse(); @@ -3770,6 +3861,8 @@ bool process_login_success_response() gAgentStartLocation.assign(text); } + // Extract UDP circuit parameters from login response. + // These are used in STATE_WORLD_INIT to establish the UDP circuit. text = response["circuit_code"].asString(); if(!text.empty()) { diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5d8bd452186..7a9651ed53e 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -418,7 +418,26 @@ void send_complete_agent_movement(const LLHost& sim_host) msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode); - msg->sendReliable(sim_host); + + // build a lambda to be used as callback on ACK or timeout + void (*complete_agent_movement_callback)(void**, S32) = [](void**, S32 result) + { + if(LLApp::isExiting()) return; + if (result != LL_ERR_NOERR) + { + LL_WARNS("Messaging") << "CompleteAgentMovement failed with err=" << result << LL_ENDL; + } + }; + + // We use same retry strategy as UseCircuitCode because this is a crucial message + // that MUST arrive else we'll suffer a failed login/teleport/region-cross + msg->sendReliable( + sim_host, + gSavedSettings.getS32("UseCircuitCodeMaxRetries"), + false, + (F32Seconds)gSavedSettings.getF32("UseCircuitCodeTimeout"), + complete_agent_movement_callback, + NULL); } void process_logout_reply(LLMessageSystem* msg, void**) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 42a587f3764..1d55ab19576 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3202,7 +3202,26 @@ void LLViewerRegion::unpackRegionHandshake() flags |= 0x00000002; //set the bit 1 to be 1 to tell sim the cache file is empty, no need to send cache probes. } msg->addU32("Flags", flags ); - msg->sendReliable(host); + + // build a lambda to be used as callback on ACK or timeout + void (*region_handshake_reply_callback)(void**, S32) = [](void**, S32 result) + { + if(LLApp::isExiting()) return; + if (result != LL_ERR_NOERR) + { + LL_WARNS("Messaging") << "RegionHandshakeReply failed with err=" << result << LL_ENDL; + } + }; + + // We use same retry strategy as UseCircuitCode because this is a crucial message + // that MUST arrive else we'll suffer a failed login/teleport/region-cross + msg->sendReliable( + host, + gSavedSettings.getS32("UseCircuitCodeMaxRetries"), + false, + (F32Seconds)gSavedSettings.getF32("UseCircuitCodeTimeout"), + region_handshake_reply_callback, + NULL); mRegionTimer.reset(); //reset region timer. } From 6732af2cc4cb2b6eb75a96815a276630a3087ac5 Mon Sep 17 00:00:00 2001 From: AndrewMeadows Date: Mon, 23 Mar 2026 10:15:55 -0700 Subject: [PATCH 2/3] revert default settings changes --- indra/newview/app_settings/settings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 03f3ed204b2..0671aadee58 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2623,7 +2623,7 @@ Value 0 - DoubleClickTeleport + DoubleClickTeleport Comment Enable double-click to teleport where allowed (afects minimap and people panel) @@ -9285,7 +9285,7 @@ Value 1.0 - + RenderReflectionProbeDrawDistance Comment @@ -9484,7 +9484,7 @@ Value 1.0 - + RenderReflectionProbeMaxLocalLightAmbiance Comment @@ -11019,7 +11019,7 @@ Boolean Value 0 - + NearbyListShowMap Comment @@ -13140,7 +13140,7 @@ Type S32 Value - 20 + 3 UseCircuitCodeTimeout @@ -13151,7 +13151,7 @@ Type F32 Value - 0.5 + 5.0 UseDebugLogin @@ -16319,7 +16319,7 @@ Type Boolean Value - 1 + 1 UpdateAppWindowTitleBar From ff85cbc7a5def2470f84a65d6ca11ad561ec16dd Mon Sep 17 00:00:00 2001 From: AndrewMeadows Date: Mon, 23 Mar 2026 10:46:46 -0700 Subject: [PATCH 3/3] more correct comment --- indra/newview/llviewerregion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 1d55ab19576..8c6a5d80961 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3213,8 +3213,8 @@ void LLViewerRegion::unpackRegionHandshake() } }; - // We use same retry strategy as UseCircuitCode because this is a crucial message - // that MUST arrive else we'll suffer a failed login/teleport/region-cross + // This is a crucial message for establishing a connection to a region + // (either the main region or a visible neighbor). msg->sendReliable( host, gSavedSettings.getS32("UseCircuitCodeMaxRetries"),