A backfilled player's join authorisation is issued after their server
started, but the supervisor fetches the roster once before launching the
game child and the game process has no reload path, so backfill cannot
work end to end regardless of how good the selection rule is.
Decided: the control plane marks the roster changed, the supervisor
re-fetches and rewrites the roster file, then signals the game process to
reload. Chosen because it reuses the authenticated channel and roster
endpoint that already exist -- no inbound path into the game pod, no new
trust boundary -- and keeps the roster an allowlist the server was told
to expect rather than admitting anyone holding a valid signature.
Signature verification is untouched and already binds match, server, slot
and generation.
Recorded in docs/MATCHMAKING.md, which the repo treats as the design
source of truth, so the decision is not re-litigated from a task row.
Remaining implementation is tracked in #32 and summarised in §7 8.19.
docs/MATCHMAKING.md is the stated source of truth for this design, so it
changes first: the probe challenge endpoint and why probing gates
matching rather than merely improving it, and the key-ID rotation
procedure that makes overlapping-key rotation concrete.
multiplayer-next.md §0's root blocker is closed rather than deleted --
what it was, why it blocked everything, and how it was resolved, since
the reasoning is what a future reader needs. Tasks 7.6, 8.15 and 8.31
updated to what actually remains, which in every case is now external
rather than unbuilt.
TODO.md #14 asked for a join-signing design decision; that decision is
recorded with its rationale. CLAUDE.md no longer says a real deployment
cannot complete a match end to end.
Add a Go-vs-C#/Rust/C++ rationale for the matchmaking control plane to
TECH_STACK.md, and point at it from MATCHMAKING.md and README.md.
Also correct CLAUDE.md and README.md, which still described the backend
as unstarted/not built even though server/ has ~13k lines of Go across
matcher, allocator, api, store, security, supervisor and agones.
multiplayer-todo.md and multiplayer-next.md tracked overlapping
information in two places. Fold everything into multiplayer-next.md
(architecture decisions, wire format, task breakdown with checkboxes,
gotchas list, testing notes) and delete multiplayer-todo.md. Section
numbers are unchanged, so existing code comments citing them by
section/task number still resolve; update every such reference to
point at the new filename.
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.
Queued matchmaking had never been considered anywhere in the planning
docs - not as planned work, and not even on the explicitly-deferred
list. It is a launch requirement, so record the design before code.
Add docs/MATCHMAKING.md covering the model change (community-server ->
per-match allocation), the decision to use Steam for identity and a
project-owned backend for queue/rating/allocation, what the existing
server already provides (--max-matches=1 is the allocation primitive,
ServerConfig, the roster, MatchState), the casual/ranked ruleset split,
and the open questions - rating algorithm, team-to-individual rating,
and the server cost that allocated matches reintroduce.
Ranked is hard-blocked on Phase 7 Steam auth tickets: slot reclaim is
keyed by display name today, and a rating on a spoofable identity is
worse than no rating.
Add Phase 8 to multiplayer-next.md, and correct README/CLAUDE.md/
TECH_STACK.md, which asserted no backend exists or is planned - true
before this was scoped, wrong now.