Netcode
Since 0.7Changed in next
Multiplayer is not available to games yet; these notes are the rules scripts will run under once it is.
Multiplayer is not available to games yet; these notes are the rules scripts will run under once it is.
The networked-play notes that concern scripts and the engine host. Transport, rollback and sessions will be documented here as they are built; this file starts with what the host side has settled.
Mixed sessions
A match may have browser peers and desktop peers together.
- Physics agrees. The browser build steps the same world bit for bit as the shipped desktop engine.
crates/physics-digestcompares the two tick by tick, and both run Rapier's deterministic maths.
- Scripts agree when every peer runs
HostConfig::networked().with_web_peers(). scripts aMaththat is the same bits on every engine and OS (script_math.js;**is refused, useMath.pow).determinism_digest(), the scene (the SHA-256 of its file's canonical JSON), the tick rate and the seed. The first that differs refuses the session with itspeer-*-mismatchcode (docs/RUNTIME-CODES.md, "Peers"). The digest alone is not enough: two peers on one build with different seeds pass it, then draw differentMath.randomnumbers. Every peer loads the scene from its file: a world built from a struct is hashed by its parsed scene, which a file-loaded peer does not match.{"type":"hello","proto":1,"engine":"argus","version":"0.7.0","determinismDigest":"e176215280f181dc","scene":"Arena","sceneSha256":"…","tickRate":60,"seed":"12345"}. The seed and the digest are strings (decimal, 16 hex digits): a u64 does not fit a JS number exactly. On join,PeerHello::acceptreadsprotofirst and refuses another protocol, or a message that is not a whole protocol-1 hello, withpeer-protocol-mismatchbefore it compares any field; then it compares the identity. Each side checks the hello it received, and both refuse with the same code. A hello's fields change only with a new protocol number, so an older peer meets a clean refusal naming the protocol (or, whenprotois not a whole number, saying the message is no hello it can read), never a half-read hello. A hello is read only in the form a peer writes it: lower-case hex, and a seed with no sign or leading zero.- The preset counts calls, limits call depth to 256, refuses promise jobs, removes
SharedArrayBufferand gives with_web_peers()gives desktop peers only thectxmembers the browser serves.- Peers compare their identity in the handshake (
argus::PeerIdentity): the engine and its version, - A peer's first message is its hello (
argus::PeerHello), the identity under a protocol number:
- The preset counts calls, limits call depth to 256, refuses promise jobs, removes
- The cost is the browser's gap. A behaviour that calls a member the browser does not serve throws
WebUnsupportedErroron every peer. (the networked-session section);crates/host/src/web/served.jsonis its source.- The members a desktop peer loses, and when each comes to the browser, are the generated table in
docs/API.md - Every member the web host serves shrinks it.
- The members a desktop peer loses, and when each comes to the browser, are the generated table in
- Sound is presentation.
ctx.audio.playgives the same voice numbers everywhere, and the browser plays it through WebAudio (kira's cpal backend). A browser holds sound until the player's first click, key or touch on the page, so a browser peer is silent until then; the voices and their numbers run all the same. - Where it stands (20 Oct 2026). Every sample (hop-lane, kart-starter, lantern-hollow, pebblewind-heights, twin-trail) and
games/duel-yardruns in the browser with no script errors, on the web host the merge queue holds. navigation, tilemaps, most of the camera's screen maths, the environment's readers, the audio mixer's bus and music controls, part of the animator, players and views, the world's spawn and grab queries, labels, render parameters, input rebinding andentity.setParent.- The browser serves 186 of the 270 handle members. The 84 left are in groups no sample's own code calls: joints,
- A behaviour that calls one of them still throws
WebUnsupportedErroron every peer of a mixed session.