Files
MetaCrate/docs/network-manager.md
Chili Palmer d298b4f4c4
All checks were successful
Native code generation / deterministic (push) Successful in 8m55s
Imaging and meshing gate / native (push) Successful in 2m45s
JPEG 2000 feature / linux (push) Successful in 1m41s
Native Rust workspace compile / compile (push) Successful in 1m57s
Skia feature / linux (push) Successful in 31m12s
Implement network manager simulator lifecycle (#53)
2026-08-09 14:46:34 +00:00

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

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

cargo test -p libremetaverse --test network_manager

They cover 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.