mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
docs: add a version inventory to TECH_STACK.md, refresh stale claims
TECH_STACK.md explained why each choice was made but never listed what is actually pinned, so there was no single place to answer "what version of X do we use". Adds a Version inventory section covering the shipped game, the Go control plane's four direct dependencies, the datastore/platform versions and the exactly-pinned training stack, plus a Verification toolchain subsection for the Make/Docker/kind/Kustomize/Actions harness. Also corrects two things the doc had outgrown: the allocation pipeline is now wired end to end and gated in CI, so only the provider deployment remains; and the Steam section covered only the GodotSteam client transport, omitting the server-side Web API ticket verifier in server/steam.
This commit is contained in:
+113
-3
@@ -105,6 +105,15 @@ port-forwarding requirement and to supply verified player identity —
|
|||||||
direct-IP ENet's slot-reclaim logic is keyed by display name today, which is
|
direct-IP ENet's slot-reclaim logic is keyed by display name today, which is
|
||||||
insecure against a public server (`multiplayer-next.md` §0, known defect C).
|
insecure against a public server (`multiplayer-next.md` §0, known defect C).
|
||||||
|
|
||||||
|
There is a **second, independent** use of Steam that does not involve
|
||||||
|
GodotSteam at all: `server/steam/` verifies session tickets server-side against
|
||||||
|
Valve's `ISteamUserAuth/AuthenticateUserTicket` Web API over plain HTTP, which
|
||||||
|
is what turns a claimed identity into a trusted one for matchmaking and for
|
||||||
|
slot reclaim. It distinguishes "Valve rejected this ticket" (401) from "Valve
|
||||||
|
is unreachable" (503) so an outage cannot be mistaken for an authentication
|
||||||
|
failure, and refuses family-shared and banned accounts. It needs a **publisher
|
||||||
|
Web API key**, which is a server-side secret and must never reach a client.
|
||||||
|
|
||||||
## Dedicated server hosting: Docker (primary) or native systemd
|
## Dedicated server hosting: Docker (primary) or native systemd
|
||||||
|
|
||||||
The dedicated server is not a separately-written service — it's the same
|
The dedicated server is not a separately-written service — it's the same
|
||||||
@@ -206,6 +215,81 @@ file and runs **inside the game** in pure GDScript — shipped bots need no
|
|||||||
Python, no .NET, no network." Keeping the shipped game GDScript-only (no
|
Python, no .NET, no network." Keeping the shipped game GDScript-only (no
|
||||||
.NET Godot build) is consistent with the rest of the stack.
|
.NET Godot build) is consistent with the rest of the stack.
|
||||||
|
|
||||||
|
## Version inventory
|
||||||
|
|
||||||
|
Everything the project actually pins, in one place. The rest of this document
|
||||||
|
explains *why* these were chosen; this is *what* is in use. Versions here are
|
||||||
|
the source of truth's values at the time of writing — when they disagree with
|
||||||
|
the files named, the files win.
|
||||||
|
|
||||||
|
### Shipped game and dedicated server
|
||||||
|
|
||||||
|
| Thing | Version | Pinned in |
|
||||||
|
|---|---|---|
|
||||||
|
| Godot | 4.7.1 | `Dockerfile` (digest-pinned `barichello/godot-ci`) |
|
||||||
|
| Physics | Jolt | `Game/project.godot` — `3d/physics_engine="Jolt Physics"` |
|
||||||
|
| Runtime dependencies | none | pure GDScript; no .NET, no ONNX, no native extensions in the default build |
|
||||||
|
| GodotSteam | custom build, opt-in | `steam-dependencies.lock.json` |
|
||||||
|
|
||||||
|
The shipped client and server carry **no third-party runtime dependency at
|
||||||
|
all** in the default ENet build. That is a deliberate constraint, not an
|
||||||
|
accident of scope — see "What's deliberately absent".
|
||||||
|
|
||||||
|
### Matchmaking control plane (Go)
|
||||||
|
|
||||||
|
| Thing | Version | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Go | 1.23 | `server/go.mod` |
|
||||||
|
| `jackc/pgx/v5` | 5.7.4 | PostgreSQL driver; used through `database/sql` for pooling, and directly for `LISTEN`/`NOTIFY`, which needs a dedicated session |
|
||||||
|
| `redis/go-redis/v9` | 9.7.0 | transient candidate index only; the durable queue is PostgreSQL |
|
||||||
|
| `alicebob/miniredis/v2` | 2.38.0 | test-only in-process Redis |
|
||||||
|
|
||||||
|
Four direct dependencies, three of them drivers. There is no web framework, no
|
||||||
|
ORM, no DI container and no code generation: HTTP is `net/http` with a hand-
|
||||||
|
written mux (`server/api/service.go`), SQL is hand-written, and migrations are
|
||||||
|
numbered `.sql` files under `server/migrations/` — each with a `down/`
|
||||||
|
counterpart — applied by the `cmd/migrate` binary. That is a deliberate choice
|
||||||
|
about a service whose whole job is a small number of carefully-fenced
|
||||||
|
transactions.
|
||||||
|
|
||||||
|
Rating maths is Glicko-2, implemented in `server/domain/rating.go` rather than
|
||||||
|
taken from a library.
|
||||||
|
|
||||||
|
### Datastores and platform
|
||||||
|
|
||||||
|
| Thing | Version | Pinned in |
|
||||||
|
|---|---|---|
|
||||||
|
| PostgreSQL | 17 (alpine) | `compose.*.yml`, `scripts/run_*_integration.sh` |
|
||||||
|
| Redis | 7 (alpine) | `compose.*.yml`, `scripts/run_redis_integration.sh` |
|
||||||
|
| Agones | 1.49.0 | `scripts/verify_kind_agones.sh` (`AGONES_VERSION`) |
|
||||||
|
| Kubernetes | 1.33 in CI | `kindest/node:v1.33.1` |
|
||||||
|
| Manifests | Kustomize | `deploy/k8s/base` + `overlays/{eu,na}` |
|
||||||
|
| Metrics | Prometheus | `deploy/observability/` — ServiceMonitors and PrometheusRules |
|
||||||
|
|
||||||
|
Container images are referenced by digest, never by tag; `scripts/verify_supply_chain.py`
|
||||||
|
fails the build on any mutable reference. All six digests under `deploy/` are
|
||||||
|
currently all-zero placeholders, and the `ghcr.io/cosmic-clash/*` registry
|
||||||
|
namespace does not exist yet — publishing the images is the open work tracked
|
||||||
|
in issue #31, and is the last thing standing between the manifests and a real
|
||||||
|
deployment.
|
||||||
|
|
||||||
|
### Training (out-of-process, not shipped)
|
||||||
|
|
||||||
|
| Thing | Version |
|
||||||
|
|---|---|
|
||||||
|
| Python | 3.12 |
|
||||||
|
| `godot-rl` | 0.8.2 |
|
||||||
|
| `stable-baselines3` | 2.4.0 |
|
||||||
|
| `torch` | 2.13.0 |
|
||||||
|
| `gymnasium` | 1.0.0 |
|
||||||
|
| `tensorboard` | 2.21.0 |
|
||||||
|
|
||||||
|
Pinned exactly, and `training/requirements.txt` explains why in unusual detail:
|
||||||
|
the curriculum depends on specific library *internals* rather than documented
|
||||||
|
public APIs, so an unpinned reinstall could silently change behaviour partway
|
||||||
|
through a 12-hour training stage. None of this ships — the game runs exported
|
||||||
|
policies through a pure-GDScript MLP.
|
||||||
|
|
||||||
## Tooling (not shipped with the game)
|
## Tooling (not shipped with the game)
|
||||||
|
|
||||||
- **`mcp/godot-mcp`** (git submodule, Node/TypeScript) — drives a live
|
- **`mcp/godot-mcp`** (git submodule, Node/TypeScript) — drives a live
|
||||||
@@ -218,6 +302,29 @@ Python, no .NET, no network." Keeping the shipped game GDScript-only (no
|
|||||||
(Blender's embedded Python, plus texture generators) used to produce the
|
(Blender's embedded Python, plus texture generators) used to produce the
|
||||||
project's original meshes and textures.
|
project's original meshes and textures.
|
||||||
|
|
||||||
|
### Verification toolchain
|
||||||
|
|
||||||
|
Everything is driven from `Makefile` targets so that CI and a local run are the
|
||||||
|
same command:
|
||||||
|
|
||||||
|
- **GNU Make** — the single entry point (`verify-phase6`,
|
||||||
|
`verify-enet-integration`, `verify-kind-agones`, `verify-supply-chain`, …).
|
||||||
|
- **Docker and Docker Compose** — the multi-process gates. The game gates use
|
||||||
|
a staged `Dockerfile`; the control-plane gates use `compose.*.yml` fixtures.
|
||||||
|
- **kind** (`kindest/node:v1.33.1`) **and Helm** — a throwaway Kubernetes
|
||||||
|
cluster with Agones installed, for the allocation gate.
|
||||||
|
- **Kustomize** — `deploy/k8s/base` plus `overlays/{eu,na}`, validated by
|
||||||
|
`kubectl kustomize` in CI rather than only at deploy time.
|
||||||
|
- **GitHub Actions** — eight workflows under `.github/workflows/`, each one a
|
||||||
|
thin wrapper around a Make target, with path filters so a docs-only change
|
||||||
|
doesn't spin up a Kubernetes cluster.
|
||||||
|
- **`scripts/verify_supply_chain.py`** — fails the build on any mutable image
|
||||||
|
reference, which is why every manifest pins by digest.
|
||||||
|
|
||||||
|
Godot itself has **no build step and no linter** — the project runs from
|
||||||
|
source, so "the tests pass" is the only mechanical check that exists on the
|
||||||
|
GDScript side.
|
||||||
|
|
||||||
## What's deliberately absent
|
## What's deliberately absent
|
||||||
|
|
||||||
- **No C# or .NET runtime anywhere in the shipped game or server.** The
|
- **No C# or .NET runtime anywhere in the shipped game or server.** The
|
||||||
@@ -238,8 +345,11 @@ Python, no .NET, no network." Keeping the shipped game GDScript-only (no
|
|||||||
- **The remaining Go matchmaking control-plane deployment** — independently
|
- **The remaining Go matchmaking control-plane deployment** — independently
|
||||||
runnable matcher, allocator and maintenance roles backed by PostgreSQL and
|
runnable matcher, allocator and maintenance roles backed by PostgreSQL and
|
||||||
Redis, deployed on provider-portable Kubernetes with Agones-managed game
|
Redis, deployed on provider-portable Kubernetes with Agones-managed game
|
||||||
fleets. The authenticated API boundary exists; durable production wiring and
|
fleets. The durable wiring now exists end to end — queue, latency probes,
|
||||||
provider deployment remain. The cloud provider remains deliberately
|
proposal, allocation, signed assignment rosters and result submission — and
|
||||||
replaceable; the application stack is locked.
|
is exercised by Compose and kind/Agones gates in CI. What remains is the
|
||||||
|
provider deployment itself: a registry to publish the images to, and a live
|
||||||
|
cluster. The cloud provider remains deliberately replaceable; the application
|
||||||
|
stack is locked.
|
||||||
This is a 1.0 launch blocker and the single largest departure from "one
|
This is a 1.0 launch blocker and the single largest departure from "one
|
||||||
Godot project, no backend". See [`MATCHMAKING.md`](MATCHMAKING.md).
|
Godot project, no backend". See [`MATCHMAKING.md`](MATCHMAKING.md).
|
||||||
|
|||||||
Reference in New Issue
Block a user