Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 125 additions & 32 deletions indra/newview/llstartup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
};
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be a helper function but we're moving into this context as a lambda.


msg->sendReliable(
gFirstSim,
gSavedSettings.getS32("UseCircuitCodeMaxRetries"),
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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())
{
Expand Down
21 changes: 20 additions & 1 deletion indra/newview/llviewermessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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**)
Expand Down
21 changes: 20 additions & 1 deletion indra/newview/llviewerregion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
Expand Down
Loading