mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
docs: explain why the matchmaking control plane is Go, fix stale status
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.
This commit is contained in:
+5
-1
@@ -29,7 +29,11 @@ Locked constraints:
|
||||
SDR. Direct ENet remains first-class for local development, CI, LAN,
|
||||
self-hosting, and community servers.
|
||||
- The control plane is Go, PostgreSQL, and Redis, deployed on Kubernetes.
|
||||
Agones owns game-server allocation and lifecycle.
|
||||
Agones owns game-server allocation and lifecycle. Go is chosen for the
|
||||
Agones/Kubernetes-native client ecosystem and its concurrency model, not
|
||||
for raw speed — the control plane never touches a simulation packet. See
|
||||
"Matchmaking control plane" in [`TECH_STACK.md`](TECH_STACK.md) for the
|
||||
full rationale and the alternatives weighed.
|
||||
- Infrastructure is provider-portable. Provider-specific cluster, network,
|
||||
DNS, and secret-store configuration lives behind isolated deployment
|
||||
overlays; application code never calls a provider allocation API.
|
||||
|
||||
+49
-3
@@ -133,6 +133,50 @@ most familiar with. That matches what's independently visible in the repo —
|
||||
(`make verify-phase6`), while the systemd unit is native-deployment
|
||||
documentation only, with no automated verification of its own.
|
||||
|
||||
## Matchmaking control plane: Go, PostgreSQL, Redis, Agones
|
||||
|
||||
The one part of the project that is *not* the Godot project. `server/` is a Go
|
||||
module (~13k lines of non-test code across `matcher`, `allocator`, `api`,
|
||||
`store`, `security`, `supervisor`, `agones`, `migrations`, `observability`)
|
||||
implementing the casual/ranked queue design in
|
||||
[`MATCHMAKING.md`](MATCHMAKING.md), plus a small PID-1 supervisor that exists
|
||||
because Godot/GDScript cannot intercept `SIGTERM` and Agones needs a graceful
|
||||
drain signal to land somewhere.
|
||||
|
||||
**Why Go, and why "performance" is the wrong reason to give:** the control
|
||||
plane is not in the simulation hot path. Physics, snapshots and 60 Hz input
|
||||
all live in the Godot dedicated server over ENet/SDR (see the transport
|
||||
sections above); Go never touches a game packet. Its actual workload is many
|
||||
mostly-idle WebSocket connections, a matcher loop that runs on a sub-second
|
||||
tick, and I/O against PostgreSQL, Redis and the Kubernetes API. That is
|
||||
I/O- and concurrency-bound, not CPU-bound, so the raw single-thread speed a
|
||||
systems language would buy is spent on work this service doesn't do. What
|
||||
actually drove the choice:
|
||||
|
||||
- **Agones and Kubernetes are Go-native.** Allocation, the GameServer SDK and
|
||||
the k8s client are all first-party Go. Any other language means hand-rolling
|
||||
REST against the Agones allocation service — see `server/agones/`, which uses
|
||||
those clients directly.
|
||||
- **Goroutines plus `context` are the right shape for the problem** — many
|
||||
concurrent idle connections, a few periodic loops, and cancel-everything-on-
|
||||
shutdown semantics that the PID-1 supervisor depends on.
|
||||
- **The surrounding operational ecosystem is Go** — Prometheus instrumentation
|
||||
(`server/observability/`), structured logging, migrations, and the
|
||||
provider-portable deployment tooling.
|
||||
- **Static binaries and slim containers**, which matters for the supervisor and
|
||||
for keeping the allocated game-server image close to the existing one.
|
||||
|
||||
**Alternatives, honestly weighed:** Rust or C++ would be the correct answer for
|
||||
a custom UDP relay or the simulation server itself, and buy nothing measurable
|
||||
for a queue-and-allocate service — while costing significantly in iteration
|
||||
speed. C# is the only serious contender (ASP.NET Core is fast, its async model
|
||||
is excellent, and Postgres/Redis/WebSocket support is mature); it loses on the
|
||||
Agones/Kubernetes side, where the clients are community-maintained rather than
|
||||
first-party, and on container weight. TypeScript or Python would prototype
|
||||
faster but fit poorly for a service whose failure modes are almost entirely
|
||||
races and timeouts. None of those gaps is large enough to justify rewriting the
|
||||
Go that already exists.
|
||||
|
||||
## AI opponents: reinforcement learning, trained out-of-process, run in pure GDScript
|
||||
|
||||
Two entirely separate pieces, deliberately joined only at a JSON file:
|
||||
@@ -177,9 +221,11 @@ Python, no .NET, no network." Keeping the shipped game GDScript-only (no
|
||||
## What's deliberately absent
|
||||
|
||||
- **No C# or .NET runtime anywhere in the shipped game or server.** The
|
||||
"C# backend" in `README.md`'s early framing was never built. A backend
|
||||
service *is* now planned for matchmaking (see below), but nothing has
|
||||
chosen C# for it — that framing predates every real decision here.
|
||||
"C# backend" an early version of `README.md` described was never built —
|
||||
that wording is long gone from the README itself. A backend
|
||||
service *does* now exist for matchmaking, but it is Go, not C# — that
|
||||
framing predates every real decision here. See "Matchmaking control plane"
|
||||
above for why Go was chosen over C# and over Rust/C++.
|
||||
- **No HTTP/WebSocket/gRPC layer for simulation traffic** — the live game uses
|
||||
ENet/Steam SDR over UDP via Godot's own `MultiplayerAPI`. The matchmaking
|
||||
control plane now has an authenticated Go REST/WebSocket boundary for queue,
|
||||
|
||||
Reference in New Issue
Block a user