Files
CosmicClash/multiplayer-next.md
T
Josh Creek 4ffa1543cc docs: design per-match server autoscaling, with measured boot time
Casual/ranked queues need servers allocated per match and shut down
afterwards, so cost is incurred only while a match runs - while the
existing Docker and CI gates keep passing unchanged.

Measured against the repo's own cosmicclash-server image rather than
estimated: the runtime image is ~148 MB of content, and boot to the
server_started line is ~870 ms on the container's own clock. That was
taken under x86_64 emulation on an arm64 host, so it is a pessimistic
bound and is recorded as one - it needs re-measuring on native Linux
before it sets any timeout.

Two findings that would each break a naive implementation, both hit
while taking that measurement:

- Godot's stdout is block-buffered off a TTY. A detached container logs
  nothing at all - server_started does not appear even after 35s - so an
  orchestrator readiness probe that greps the log hangs forever. Probe
  the UDP socket or flush explicitly.
- --port defaults to 7777 and the Dockerfile hardcodes EXPOSE 7777/udp,
  so several matches cannot share a host without a port range or an
  address per match. Being UDP, L7 ingress routing does not apply.

Also records the honest tension in 'only pay during a match': a server
must listen before players connect, and image pull plus scheduling can
dwarf 870 ms, so the recommendation is match-level scale-to-zero over a
small warm node pool rather than node-level scale-to-zero.

The rule for keeping verify-phase6 and verify-enet-integration green:
every allocation feature is opt-in via a ServerConfig flag defaulting to
current behaviour, with a second Compose file rather than mutating
compose.phase6-smoke.yml.
2026-08-31 18:45:05 +01:00

94 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Multiplayer — next work
Short, current checklist for online multiplayer. Historical design decisions,
implementation evidence, and completed work stay in
[`multiplayer-todo.md`](multiplayer-todo.md).
## Release blockers
- [ ] **Phase 4 playtest:** a human playtest at roughly 100 ms RTT. Confirm
that ship and ball interaction feel local and contact corrections feel like
bumps rather than glitches.
- [ ] **Phase 5 session:** complete a real 3v3 match with a mid-match
disconnect and late joiner.
- [ ] **Phase 6 external check:** run the exported Docker server and clients
from separate machines over the internet, then play a full match. Keep this
controlled-only until Steam identity is complete.
## Phase 7 — Steam, identity, discovery
- [ ] Obtain the pinned GodotSteam client/server builds and Steamworks SDK
access described in [`STEAM.md`](STEAM.md).
- [ ] Run `make verify-steam-templates` with the custom executables and fix
any custom-template failures.
- [ ] Validate a two-account Steam SDR host/join using the existing explicit
`NetworkManager` Steam transport. ENet direct-IP must keep passing its smoke
test.
- [ ] Build the Steam server browser: internet, LAN, favourites, and history.
- [ ] Add Steam auth tickets, verified Steam identity in the roster, and a
persistent ban list. This fixes the slot-reclaim security issue below.
## Phase 8 — casual and ranked matchmaking (1.0 launch blocker)
Design and reasoning: [`docs/MATCHMAKING.md`](docs/MATCHMAKING.md). This is a
different server model from the community-server one that exists today —
players queue, a matchmaker groups them, and a server is allocated per match.
Phase 7's Steam auth tickets are a hard prerequisite: a rating attached to a
spoofable identity is worse than no rating.
- [ ] Decide the rating algorithm (Glicko-2 recommended over Elo for a small
launch population) and how a team result distributes across individuals.
- [ ] Choose the backend language and hosting, and cost out allocated servers
per match at expected population.
- [ ] Stand up the backend: Steam auth ticket validation via the Steamworks
Web API, queue, rating store, server allocator.
- [ ] Add an assigned-roster server mode so only matched SteamIDs may claim a
slot, replacing the first-come model.
- [ ] Add server-authoritative match result reporting to the backend over a
channel a client cannot forge.
- [ ] Client queue UI: playlist select, estimated wait, accept/decline,
connect-on-assignment, post-match rating delta.
- [ ] Casual and ranked playlist rulesets (backfill, bots, abandon penalties,
arena restriction — see the comparison table in the design doc).
Server orchestration (same phase — the servers must autoscale and bill only
for the duration of a match):
- [ ] Choose the orchestrator (Agones on Kubernetes is the recommended
default; it models allocation, readiness and per-match lifetime natively).
- [ ] Fix readiness detection. Godot's stdout is block-buffered off a TTY, so
`server_started` never appears in `docker logs` for a detached container —
a log-grep readiness probe hangs forever. Probe the UDP socket, or flush.
- [ ] Support more than one match per host: a per-container port from a range,
or one address per match. `--port` defaults to 7777 and the Dockerfile
hardcodes `EXPOSE 7777/udp`.
- [ ] Add a no-show timeout so an allocated server that never fills abandons
and exits instead of idling at cost.
- [ ] Re-measure boot-to-listening on native x86_64 Linux. The repo's current
figure is ~870 ms, measured under emulation on arm64 — a pessimistic bound.
- [ ] Re-measure the SERVER.md sizing estimate (610 processes/core,
150250 MB RSS) under real concurrency before it sizes a bill.
- [ ] Keep `make verify-phase6` and `make verify-enet-integration` green:
every allocation feature is opt-in via a `ServerConfig` flag defaulting to
today's behaviour, with a second Compose file for the allocated path rather
than mutating `compose.phase6-smoke.yml`.
## Known issues to resolve before public hosting
- [ ] Slot reclaim is currently keyed by display name, so someone can take a
disconnected player's reserved slot. Do not expose public servers before
verified Steam identity lands.
- [ ] Investigate occasional input loss during a long server stall; the
existing sequence resync recovers it, but transport delivery is variable.
- [ ] Fix the remaining `_broadcast_snapshot` packet-send stderr race.
## Decide after the latency playtest
- [ ] Decide whether client-only, contact-cohort shadow physics is worthwhile
for the remaining prediction weakness.
## Explicitly deferred
120 Hz simulation, latency-gap measurement, audio hooks, and split-screen are
not part of the current multiplayer release path.