Networked Multiplayer — design (Phase 3)
Networked Multiplayer — design (Phase 3)
Section titled “Networked Multiplayer — design (Phase 3)”Status: parked — NOT in the first App Store release (v0.1 Crystals). Post-launch milestone.
Date: 2026-06-30
Supersedes scope of: the local 2-player co-pilot (feat(coop): local 2-player co-pilot mode, BUILD 115) — that shared-screen model is retained as a “couch” mode but is no longer the target architecture.
Multiple players, each on their own device, in a shared world, each flying around anywhere with their own view. An iPhone can join an Apple TV over local WiFi (and later over the internet). Each player is a complete pilot — own ship, own HP, own dash + drone, own separate XP / level / weapon build. Cross-device works for free because every platform (iOS / tvOS / desktop / web) runs the same Godot engine and the same netcode.
This is the project’s long-decided Phase 3. It is enabled by the keystone property the whole sim
was built around: a deterministic, input-driven, RefCounted sim with no engine physics, our own
SpatialHash, and all randomness through SeededRng.
Decisions (locked 2026-06-30, with Chris)
Section titled “Decisions (locked 2026-06-30, with Chris)”- Separate per-player progression — each player has their own XP, level, and weapon build (NOT a shared pool). Each player picks their own level-up upgrades for their own arsenal.
- Every player is a full pilot — own weapons, own Dash + Drone, own colour. (P1 cyan, P2 amber, P3 violet; co-pilots render slightly smaller. Final palette during M-C.)
- Transport order: LAN host-authority first, then online via a dedicated server.
- First playable: one device (e.g. the ATV) hosts the authoritative
Sim; others join over WiFi, send input, receive state, and predict their own player. - Online: promote that same host logic into a headless Godot dedicated server on hetzner.
- First playable: one device (e.g. the ATV) hosts the authoritative
- Authority model: authoritative-host + client prediction — the chess-moba reference
(
server/src/match-room.ts+src/net/prediction.ts), NOT lockstep relay. Because the sim is GDScript it cannot run in a Cloudflare DO, so authority is a Godot ENet host (LAN) or a headless Godot dedicated server (online). Mirror themoba-bakeoff/slice-godotper-tick-trace determinism discipline. - Couch co-op retained — the same-screen 2-player (shared/split camera) mode stays as a no-network option and is reworked + fixed inside this milestone (it currently has the dash/drone phantom-join bug; see below).
- Dash/drone bug fix is folded into this milestone — no standalone fix. (For v0.1, co-op is gated
OFF entirely via
main.V01_LOCK_COOPso the bug cannot surface in the launch build.) - Join trigger: press a button on an unused real gamepad / an explicit lobby join — never the movement-stick auto-join (that is what causes the tvOS Siri-Remote phantom-join).
The dash/drone bug this supersedes (for the record)
Section titled “The dash/drone bug this supersedes (for the record)”When the current local co-op is active, main.gd overwrites P1’s input with
input_router.poll_device(0), which hardcodes dash=false, decoy=false — so P1 permanently loses
dash + drone the moment a P2 exists. On tvOS the auto-join (`Input.get_connected_joypads().size()
= 2
+ slot-1 stick push) fires spuriously because the Siri Remote also counts as a joypad and a single controller can land on slot 1, so a player's own movement can spawn a phantom P2 and leave the real controller as the ability-less co-pilot. The networked rework rebuilds this input path so every pilot carries a full per-deviceInputState` (move + aim + dash + decoy with per-device edge latches), which fixes it by construction.
Decomposition (each its own spec → plan → build cycle)
Section titled “Decomposition (each its own spec → plan → build cycle)”M-A — Multi-player sim foundation (deterministic, transport-agnostic)
Section titled “M-A — Multi-player sim foundation (deterministic, transport-agnostic)”Generalize the sim from one player to N independent full pilots in a shared world. The
load-bearing constraint: keep the single-player determinism baseline byte-identical (current:
both modes 2730172591 / 2950189379). Use a null-object/empty-list seam exactly like the existing
player2 == null seam — pilots.size() == 1 in single-player executes the current code path with
identical RNG draw order. Re-run the determinism + checksum tests after every chunk.
- Per-pilot:
PlayerState, own arsenal (active_weapon_ids+ weapon instances), own XP/level/build, own dash state, own drone charge. - Enemies target the nearest pilot; spawns ring around each pilot; game-over only when all pilots are down.
- Open problems to solve here: a world that spans multiple pilots’ regions (spatial hash + active area + perf with players far apart), per-pilot level-up flow, and whether weapons fire from a per-pilot origin override or per-pilot instances.
M-B — LAN transport (host-authority)
Section titled “M-B — LAN transport (host-authority)”- One device hosts: runs the authoritative
Sim, owns the canonical tick. - Clients connect over WiFi (
ENetMultiplayerPeer, connect-by-IP + optional UDP auto-discovery on the LAN), send theirInputStateeach tick, receive authoritative snapshots/deltas. - Client prediction + reconciliation for the local player (chess-moba
prediction.tspattern): predict locally from input, correct on authoritative state. - First “it works” moment: iPhone joins ATV, both fly anywhere.
M-C — Per-device view + lobby UX
Section titled “M-C — Per-device view + lobby UX”- Each client renders its own camera following its own pilot, sees other pilots + enemies + the world around it. (Drops the shared-midpoint camera entirely for networked mode.)
- Join/lobby screen, player colours + names, per-player level-up + HUD (each device shows only its own pilot’s picks/HUD).
- Couch co-op (same-screen) is reconciled here too: shared or split camera, both controllers fixed to carry full input.
M-D — Online (dedicated server)
Section titled “M-D — Online (dedicated server)”- Promote the M-B host into a headless Godot dedicated server (hetzner) with a public address, so play works over the internet and scales toward the roadmap’s 2–32 target.
- Connect/matchmaking flow; NAT is sidestepped by clients dialing the public server.
- iOS over cellular reaches the dedicated server directly.
Build order: M-A → (M-B + M-C together — the first playable) → M-D.
Risks / watch-items
Section titled “Risks / watch-items”- Determinism is the asset — protect it. Every M-A chunk re-verifies the baseline; a desync in
prediction/reconciliation is the classic failure (use
state_checksum()per the existing position-level desync-hunting note). - World scale + perf when players are far apart (spawns/hash/culling per pilot) — the current
SOFT_ENEMY_CAPand adaptive-quality ladder assume one focal player. - Per-platform input (touch vs controller) already abstracts via
Platform.is_touch()/InputRouter; netcode is platform-agnostic so cross-device is mostly free, but lobby/HUD UX must suit both 10-foot (TV) and handheld (iPhone). - Two co-op models to maintain (couch + networked) — keep the per-pilot sim layer shared so only the transport/camera differs.
Out of scope for v0.1
Section titled “Out of scope for v0.1”All of the above. v0.1 ships single-player Crystals with co-op gated off (main.V01_LOCK_COOP = true).