5.4 KiB
Native network manager and simulator lifecycle
NetworkManager, Simulator, PacketEventDictionary, and
CapsEventDictionary implement the native Rust boundary corresponding to the
LibreMetaverse connection and dispatch layer. They use the generated packet
codecs and the bounded UDPBase transport; they do not invoke .NET code or
start a helper process.
Ownership and execution
A manager owns bounded incoming and outgoing channels plus one keepalive
worker. Each simulator owns a UDPBase transport running on a dedicated
current-thread Tokio executor so the mapped synchronous connection API does not
depend on the caller already having entered a runtime. Async connection methods
move blocking compatibility work to short-lived named standard threads and
return their result through a runtime-neutral one-shot future. Every worker
holds only a weak manager reference between operations. Dropping the last
manager and subscription guard therefore releases all manager-owned
GridClient handles, cancels the workers, joins their threads, and closes
simulator transports.
The public SimulatorCollection is a shared snapshot collection. Lookup,
addition, removal, current-simulator selection, and disconnect transitions are
synchronized. User callbacks receive cloned handles only after internal locks
have been released. Subscription is an RAII guard: dropping or closing it
removes exactly its registration through a weak registry reference.
Dispatch semantics
Packet callbacks preserve registration order. PacketType::Default handlers
are selected before handlers for the concrete packet type. Once any callback
registered for a packet type requests asynchronous dispatch, that packet type
remains asynchronous after later removals, matching the conservative C#
delegate state. A bounded worker executes each asynchronous default/specific
batch in order. Callback panics are isolated and cannot terminate a processor
or poison a callback registry.
CAPS dispatch snapshots the default ("") and named callback chains, then
invokes them in that order without holding the registry lock. The built-in
EnableSimulator handler honors Agent.MultipleSims, ignores duplicate
endpoints, validates ports, and connects each newly advertised region with its
reported handle and dimensions. Incoming DisableSimulator, KickUser,
RegionHandshake, StartPingCheck, and generic-streaming UDP packets perform
their reference lifecycle actions before user dispatch. Ping replies preserve
the incoming ping identifier and immediately flush pending acknowledgements
when the simulator reports a nonzero OldestUnacked sequence.
Connection and shutdown behavior
Connecting reuses an existing endpoint or inserts one simulator atomically,
starts the bounded manager workers, raises cancellable SimConnecting, sends a
reliable UseCircuitCode, and waits up to Timing.LoginTimeout for its actual
protocol ACK. Async compatibility methods use a runtime-neutral blocking
bridge, so they can be polled without an ambient Tokio runtime. Circuit-code
changes propagate to every tracked simulator. A default connection stores its
seed capability, updates CurrentSim, raises SimChanged, and then raises
SimConnected. A received region handshake is decoded and answered with the
reference RegionHandshakeReply flags before the simulator is marked complete.
The keepalive uses the same two-interval disconnect-candidate transition as the
C# timer: traffic clears the candidate, the first silent interval marks it and
sends a ping, and the second shuts the manager down with NetworkTimeout.
Concurrent disconnect attempts are serialized so one simulator and one global
transition are emitted. Per-simulator removal reports NetworkTimeout; losing
the last simulator reports SimShutdown. Explicit shutdown preserves the
caller-provided reason/message and sends CloseCircuit only for client or
network-timeout shutdowns.
Login behavior
The native login path constructs the reference LLSD request, including the C#
MD5 password form, token/MFA fields, viewer identity, platform metadata,
requested options, and normalized home/last/region start locations. It uses the
client's injectable capability HTTP transport, limits redirects and server
delays, observes linked caller/client cancellation, and reports the mapped
LoginStatus transitions. Only a successful response invokes registered login
callbacks and proceeds to an ACK-gated initial simulator connection.
LoginResponseData parses the session and circuit identifiers, home and look-at
vectors, simulator endpoint and dimensions, seed capability, inventory roots and
skeletons, buddy rights, account benefits, service URLs, category/config arrays,
premium packages, and initial outfit. A rejected, malformed, timed-out, or
canceled login leaves no simulator installed. Passwords, login tokens, MFA
hashes, session identifiers, seed capabilities, and estate access tokens are
excluded from Debug output and diagnostic messages.
The deterministic tests use loopback fake simulators and require no live grid:
cargo test -p libremetaverse --test network_manager
They cover login hashing, redirects, cancellation and initial handoff alongside ACK-gated circuit setup, typed handshake reply/state, ping response, CAPS enable, UDP disable, current/seed selection, callback ordering and filtering, RAII unregistration, runtime-neutral async calls, concurrent registration/removal, reentrant/concurrent disconnect, and keepalive timeout reasons.