summaryrefslogtreecommitdiff
path: root/indra/newview/llstartup.cpp
diff options
context:
space:
mode:
authorAndrew Meadows <leviathan@lindenlab.com>2026-05-27 09:33:09 -0700
committerGitHub <noreply@github.com>2026-05-27 09:33:09 -0700
commitf99e2233a40e3a7b88e6c54aa89a1bc0bcd76456 (patch)
treed9853e2f709615082770f2ea8ece4801345b866b /indra/newview/llstartup.cpp
parente63651672fe66fe2c60bfb6c823fe78ed6f1b28d (diff)
Log failed delivery of crucial vewer-->simulator connection UDP messages (#5568)
Diffstat (limited to 'indra/newview/llstartup.cpp')
-rw-r--r--indra/newview/llstartup.cpp157
1 files changed, 125 insertions, 32 deletions
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index b3ed65be82..6a85ca069a 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"
@@ -277,7 +343,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);
@@ -1719,13 +1784,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"),
@@ -1743,6 +1843,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;
@@ -1758,13 +1861,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,
@@ -1807,12 +1913,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();
@@ -2811,34 +2922,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 );
@@ -3731,6 +3814,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();
@@ -3853,6 +3944,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())
{