Commit Graph

125 Commits

Author SHA1 Message Date
Josh Creek 6ac2d0fbb1 fix(multiplayer): project accepted queue state 2026-09-01 21:54:10 +01:00
Josh Creek fa2c93ec06 fix(multiplayer): defer reconnect recovery during mutations 2026-09-01 21:53:07 +01:00
Josh Creek 51f6e19339 feat(multiplayer): expose ranked season countdown 2026-09-01 21:49:48 +01:00
Josh Creek 9240cd4b27 feat(multiplayer): preserve authoritative queue wait 2026-09-01 21:45:07 +01:00
Josh Creek 84372204fd feat(multiplayer): persist ranked arena allocations 2026-09-01 21:04:55 +01:00
Josh Creek e934bbfe44 test(training): cover wall rebound geometry 2026-09-01 17:39:53 +01:00
Josh Creek 7b2f9c26f4 feat(training): add opt-in teamplay evaluation 2026-09-01 17:30:40 +01:00
Josh Creek 6ef55affcb feat(multiplayer): add idempotent client mutation retries 2026-09-01 17:02:55 +01:00
Josh Creek 4e88ea80f3 feat(multiplayer): handle planned shutdowns on clients 2026-09-01 16:43:47 +01:00
Josh Creek b24f9fc448 feat(multiplayer): notify clients before server shutdown 2026-09-01 16:42:15 +01:00
Josh Creek 334d8a26ac feat(multiplayer): enforce allocated initial connect policy 2026-09-01 16:36:26 +01:00
Josh Creek 80d6ee8cf5 fix(multiplayer): validate allocated roster shape 2026-09-01 16:18:31 +01:00
Josh Creek 69a8402f11 fix(multiplayer): honor assigned team and slot 2026-09-01 16:17:10 +01:00
Josh Creek b4ea50d76a fix(multiplayer): reclaim slots by signed identity 2026-09-01 16:08:01 +01:00
Josh Creek bdb3c8f4a7 fix(multiplayer): gate allocated matches on full roster 2026-09-01 16:03:43 +01:00
Josh Creek dad26a164c fix(multiplayer): recover stale proposal mutations 2026-09-01 15:31:01 +01:00
Josh Creek d081a72b9a feat(multiplayer): wire ranked profile runtime policy 2026-09-01 15:28:37 +01:00
Josh Creek f096e8ff0b test(multiplayer): verify live assignment delivery 2026-09-01 15:25:36 +01:00
Josh Creek aa93aeec95 test(multiplayer): verify two-player proposal round trip 2026-09-01 15:17:53 +01:00
Josh Creek 57fe6f4acc test(multiplayer): extend the real integration test to cover ranked profile fetch
Adds fetch_ranked_profile() right after login, asserting the expected
404 for a brand-new identity round-trips correctly through the real
RankedProfileProvider (previous commit) before proceeding to the
existing queue_create -> heartbeat -> cancel sequence. Verified stable
across 3 consecutive full runs.
2026-09-01 14:20:09 +01:00
Josh Creek f7657ad9ad test(multiplayer): extend the real integration test to cover queue cancel
Adds a fourth real round trip to control_plane_smoke.gd: heartbeat ->
cancel_queue -> CANCELLED, using the same idle-wait pattern the
heartbeat step already needed. Verified stable across 3 consecutive
full runs (real Postgres, real testkit-api, real headless Godot
client), plus the full Go and Godot unit suites clean.
2026-09-01 14:16:06 +01:00
Josh Creek 521b8122ac test(multiplayer): add a real Go+Postgres+Godot end-to-end integration test
Every existing test of the client/control-plane boundary is either a
Go unit test with a mocked HTTP layer or a GDScript unit test with no
network at all (multiplayer-next.md 8.40's own evidence names "live
multi-process control-plane/game verification" as remaining). Nothing
before this actually ran the real compiled Go binary, a real
PostgreSQL instance, and a real headless Godot process talking real
HTTP to each other -- and it immediately found a real bug (previous
commit).

server/cmd/testkit-api is a new, deliberately separate, clearly-marked
test-only binary wired identically to cmd/control-plane except for
SteamLogin: cmd/control-plane has no way to authenticate against a
real Steam Web API from this sandbox (task 8.7's own documented
blocker), so testkit-api accepts any non-empty ticket string and
derives a deterministic identity instead. This bypass is confined to
its own binary -- never a flag on cmd/control-plane, never referenced
by any Dockerfile stage or Kubernetes manifest -- specifically so it
can't become a footgun on the real one.

Game/tests/control_plane_smoke.gd drives the real ControlPlaneClient
autoload through login -> queue_create -> heartbeat against a real
server and prints SMOKE PASS/FAIL, matching the existing net_smoke.gd
convention. scripts/verify_control_plane_integration.sh orchestrates
both sides (real postgres:17-alpine, the built testkit-api binary, the
Godot client) end to end.

Two real bugs surfaced building this, both fixed and re-verified, not
just the target bug: the smoke script's own use of `go run` left a
zombie process that survived cleanup and squatting on its port
corrupted the NEXT run with a misleading "http=401 unauthorized" (now
builds and runs a real binary directly, plus a belt-and-suspenders
port-kill in cleanup); and calling heartbeat() synchronously from
within a request_succeeded handler produced a spurious "Busy" because
ControlPlaneClient's own internal resync (see previous commit) was
still in flight -- the test now waits for ControlPlaneClient to go
idle via a real Timer (call_deferred alone floods the message queue
without ever yielding a frame for the in-flight request to complete).

Verified stable across 3 consecutive full runs: real PostgreSQL
container up, migrations applied, testkit-api built and started, real
headless Godot client round-tripping login/queue/heartbeat, clean
teardown with no leftover processes, containers, or bound ports each
time.
2026-09-01 14:13:52 +01:00
Josh Creek 4fb7ddfecf docs(multiplayer): consolidate tracking into one document
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.
2026-09-01 12:32:43 +01:00
Josh Creek e7c835af52 feat: add Godot Agones REST bridge 2026-09-01 09:16:41 +01:00
Josh Creek 6caf719158 feat: add allocated server readiness control 2026-09-01 09:12:10 +01:00
Josh Creek 3aad68a6e6 feat: fence expired allocated reconnects 2026-09-01 08:52:12 +01:00
Josh Creek e25d61d80e feat: verify allocated join authorisations with hmac 2026-09-01 08:48:48 +01:00
Josh Creek 4b01b7fc88 fix: prevent concurrent join token reuse 2026-09-01 08:41:38 +01:00
Josh Creek 68d832f7bc feat: enforce allocated join roster admission 2026-09-01 08:39:56 +01:00
Josh Creek d8ea2f4ac7 feat: connect clients from validated assignments 2026-09-01 08:37:00 +01:00
Josh Creek aeb37a4c6e fix: ignore pre-history prediction acknowledgements 2026-09-01 08:32:44 +01:00
Josh Creek c5678ce877 fix: pass Godot 4.7 multiplayer test suite 2026-09-01 08:24:33 +01:00
Josh Creek 467c25b20c feat: expose validated assignment endpoints 2026-09-01 08:17:39 +01:00
Josh Creek 3533e8ae6c feat: consume assignment matchmaking events 2026-08-31 23:15:42 +01:00
Josh Creek a16db39884 feat: connect Godot matchmaking event stream 2026-08-31 23:06:44 +01:00
Josh Creek 6b3db98327 fix: bind assignments to authenticated player 2026-08-31 22:55:22 +01:00
Josh Creek 69f1ef6be1 feat: add player-scoped assignment recovery 2026-08-31 22:54:16 +01:00
Josh Creek 585056ccc3 feat: add Godot Steam session login 2026-08-31 22:49:05 +01:00
Josh Creek 222bbb5b84 feat: classify matchmaking recovery failures 2026-08-31 22:45:20 +01:00
Josh Creek fddbebb33b feat: display backend ranked profile 2026-08-31 22:44:21 +01:00
Josh Creek 62c1478913 feat: persist matchmaking recovery state 2026-08-31 22:42:43 +01:00
Josh Creek 47550aefae feat: add matchmaking queue UI 2026-08-31 22:39:29 +01:00
Josh Creek 5106ac64da feat: add authenticated matchmaking control client 2026-08-31 22:36:05 +01:00
Josh Creek 39ebfce1bb feat: add revisioned matchmaking client state 2026-08-31 22:33:33 +01:00
Josh Creek 698413cd91 feat: validate allocated assignment manifest 2026-08-31 20:38:56 +01:00
Josh Creek d864ce2475 feat: add allocated server compatibility config 2026-08-31 20:13:22 +01:00
Josh Creek 964094f65a docs: correct stale multiplayer/C#-backend claims, add TECH_STACK doc
CLAUDE.md and README.md described the pre-multiplayer state (local-only
MVP, planned C# backend) even though server-authoritative multiplayer,
the dedicated server, Docker/CI verification, and Steam transport have
since shipped (Phases 1-6). Update both to reflect reality and add a
docs index in CLAUDE.md pointing at multiplayer-next.md as the current
checklist.

- Add docs/TECH_STACK.md, linked from README, explaining the stack and
  why it's a single GDScript project with no separate backend.
- Add one TODO item for the video settings menu (missing presets/vsync/
  resolution scaling), blocked on the same profiling gate as the
  multiplayer 0.17 tasks.
- Pick up editor-generated .gd.uid sidecars and minor project.godot
  formatting noise from opening the project in Godot 4.7.
2026-08-31 18:20:52 +01:00
Josh Creek 6320b982a8 fix(project): keep comments out of project.godot and guard the settings
Godot's ConfigFile writer does not round-trip comments in project.godot. An
observed rewrite deleted both `;` blocks outright and spliced the three-line
`#` block above run/main_scene.dedicated_server onto the setting's own line,
leaving it commented out — which would send dedicated builds to the
interactive main menu instead of server_boot.tscn, with nothing failing until
someone noticed a server process rendering a menu.

Move the explanations into the code that owns the settings (server_boot.gd for
the dedicated-server override, video_settings.gd for stretch mode and vsync)
so they cannot be destroyed by a rewrite, and leave project.godot holding only
assignments plus Godot's own regenerated header.

Add tests/cases/test_project_settings.gd as the backstop: the feature-override
assertions read project.godot as text and reject a line that has been folded
into a comment, since ProjectSettings resolves `key.<feature>` overrides at
load time and never exposes the suffixed key. Verified by reproducing the
exact corruption, which fails the test, and it also covers the Jolt physics
engine, the required autoloads, and that no test-hook autoload is ever shipped
registered.
2026-08-24 08:40:16 +01:00
Josh Creek 46fe696a58 fix(tests): measure cumulative travel, not displacement, in the ENet host check
run_ci_host_check asserted input reached the server by comparing each bot
ship's position against one recorded before the check forced a goal. But a
goal's kickoff teleports every ship back to spawn (_begin_kickoff ->
reset_ships), so that comparison measured only the distance covered since the
last reset — a window whose length depends on when the sample lands relative
to the kickoff rather than on whether input was flowing at all.

It failed on master with peers at 0.51m and 0.23m against a 0.5m threshold:
one passed by a centimetre, the other failed, with both connected, neither
stalled, and every other assertion in the run green. The commit it failed on
touches only training JSON, and the push two minutes earlier passed on
identical game code.

Accumulate per-tick path length in _await_recording_score instead, discarding
any single-frame step over 2.0m as a teleport — Ship.max_speed (35 m/s) is
hard-clamped each tick in _integrate_forces, so 60Hz caps legitimate travel at
~0.58m. Same 0.5m threshold now reads 28-75m across runs, and it is strictly
stronger than before: it asserts input kept arriving for the whole wait rather
than that the ship merely ended up somewhere else.
2026-08-24 08:40:06 +01:00
Josh Creek 24d6a547d6 fix(ci): isolate imported client test image 2026-08-21 20:16:01 +01:00