# 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.

Promoting an already-connected simulator is a controlled handoff. The manager
first sends a reliable `UseCircuitCode` followed by the agent/session/circuit
identified `CompleteAgentMovement`, installs the new seed capability, swaps
`CurrentSim`, and finally raises `SimChanged` with the previous simulator. The
old simulator remains tracked until an explicit disconnect so multi-simulator
traffic can continue. A completed shutdown leaves the manager reusable: a later
connection creates fresh workers and transports rather than retaining canceled
ones. Connection attempts made while logout or teardown is still active fail
with `InvalidOperation`, preventing a reentrant reconnect from racing resource
cleanup.

`RequestLogout`, blocking `Logout`, runtime-neutral `LogoutAsync`, and
nonblocking `BeginLogout` all send the native `LogoutRequest` packet at most
once per active handshake. A matching `LogoutReply` is accepted only for the
current agent and session, raises `LoggedOut` with the returned inventory IDs,
and then performs a client-initiated shutdown. Blocking calls wait for that
ordered teardown; asynchronous calls additionally observe caller cancellation.
`BeginLogout` raises an empty `LoggedOut` only after a bounded network-timeout
teardown when no reply arrives, matching the reference ordering.

Teardown is idempotent and ordered. It cancels active login and logout waits,
disconnects non-current simulators before the current simulator, stops manager
workers, clears connection/circuit/session/seed state and parsed login secrets,
then raises one `Disconnected` event and marks shutdown complete. Reentrant and
repeated shutdown calls do not repeat events. Nonblocking logout waiters observe
the lifecycle cancellation and terminate rather than retaining the manager or
client; their last detached error remains available through
`NetworkManager::last_logout_error` for diagnostics.

## 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.

## Statistics and milestone gate

`SimulatorSimStats` is the native thread-safe counterpart of the reference
`Simulator.SimStats`. Atomic counters track received/sent packets and bytes,
resends, drops, pings, pongs, lag, and bandwidth samples. Server `SimStats`
blocks update the 25 defined simulator measurements with the reference ID and
rounding rules; unknown IDs are ignored. Debug snapshots contain aggregate
counters only—never packet bytes, endpoints, capability URLs, or credentials.

The offline fake-grid gate composes the completed layers rather than replacing
their focused tests. It authenticates through injected LLSD HTTP, establishes a
real loopback UDP circuit, discovers seed capabilities, consumes an
`EventQueueGet` message that enables a second region, downloads a capability
asset, promotes that region, and completes the logout handshake. It then proves
CAPS polling was canceled, download and network workers were joined, sockets
were closed, state was cleared, and diagnostic formatting contains none of the
fixture tokens. `tools/check_milestone_08.py` also audits owned source files for
failure-only bodies and verifies the explicit queue, payload, event, retry, and
concurrency policy markers remain present.

The deterministic tests use loopback fake simulators and require no live grid:

```sh
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, controlled region handoff, reconnect after
teardown, all three logout wait modes, reply validation and event order, callback
ordering and filtering, RAII unregistration, runtime-neutral async calls,
concurrent registration/removal, reentrant/concurrent disconnect, and keepalive
timeout reasons.
