Add ENet multiplayer (movement/combat sync) and matchmaking API
Networked play, end to end: connect, get matched, spawn, fight, respawn. Godot side: - Replace the single hardcoded Player with per-peer networked ships (MultiplayerSpawner + custom spawn_function), driven by client-side prediction with server reconciliation for movement. - Server-authoritative combat: bullets, health, death/respawn all decided by the server and broadcast to every peer over RPC. - Fix a Camera2D bug where Godot auto-promotes the first camera to ever enter the scene tree as the active one regardless of its own `current` value — with MultiplayerSpawner catch-up replication that was almost never the local player's own ship. `make_current()` on the owner's camera fixes it; property assignment doesn't reliably override the auto-claim. - Fix two "late joiner never learns already-established state" gaps (health, visibility) by folding both into the existing per-tick position broadcast instead of relying on one-shot RPCs that only reach peers already connected when they fire. - Make the 2 offered factions in team select match for every player in a match: the server decides once and clients either read it directly (server's own instance) or request it over RPC, rather than each client rolling its own random pair. - New MatchmakingClient autoload wires the main menu's CASUAL/RANKED buttons to the matchmaking API instead of connecting directly. New matchmaking-api/ (FastAPI + Postgres, Docker Compose): - Queue/match/stats/ranks endpoints. In-memory matchmaking queue in a single API process — no Redis until there's an actual reason to shard the queue across instances. - Background loop forms a match once enough players are queued for a mode, assigns the first available registered game server. - No real server pool yet: one dev server is auto-seeded from DEV_SEED_SERVER_IP/PORT; POST /servers/register exists for real servers to self-register later but nothing calls it yet. Docs: CLAUDE.md and overview/tech.md updated to match — networking checklist mostly checked off, matchmaking backend section rewritten to describe what was actually built vs. the original Go/Redis plan, and Current Tasks reordered around what's actually left (bot fill, real server pool, ranked/MMR refinement, lag compensation). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+29
-18
@@ -55,19 +55,30 @@ func send_input(dir: Vector2, firing: bool) -> void:
|
||||
|
||||
### Matchmaking Backend
|
||||
|
||||
| Component | Tech |
|
||||
|-----------|------|
|
||||
| API Server | Go or Node.js |
|
||||
| Queue / State | Redis |
|
||||
| Database (accounts, ranks) | PostgreSQL |
|
||||
| Hosting | VPS (Hetzner / DigitalOcean) or self-hosted |
|
||||
Built — see `matchmaking-api/README.md` for how to run it. Actual stack
|
||||
diverged from the original plan below:
|
||||
|
||||
**Flow:**
|
||||
1. Client sends "find match" with MMR + mode
|
||||
2. Backend queues player, finds lobby within MMR range
|
||||
3. Lobby full → backend assigns a game server
|
||||
4. Backend sends client the server IP + session token
|
||||
5. Client connects directly to game server
|
||||
| Component | Planned | Actual |
|
||||
|-----------|---------|--------|
|
||||
| API Server | Go or Node.js | **FastAPI (Python)** — I/O-bound queue/CRUD logic, not a latency-critical hot path, so dev velocity won over raw throughput |
|
||||
| Queue / State | Redis | **In-memory in the API process** — no Redis; revisit only if the API needs to run as more than one instance |
|
||||
| Database (accounts, ranks) | PostgreSQL | PostgreSQL, as planned |
|
||||
| Hosting | VPS (Hetzner / DigitalOcean) or self-hosted | Not deployed — local Docker Compose only so far |
|
||||
|
||||
**Flow (as built):**
|
||||
1. Client sends `POST /matchmaking/queue/join` with callsign + mode
|
||||
2. Backend queues the ticket; a background loop forms a match once enough
|
||||
players are queued for that mode
|
||||
3. Match forms → backend assigns the first available registered `GameServer`
|
||||
for that mode
|
||||
4. Client polls `GET /matchmaking/queue/status/{ticket}` until `matched`,
|
||||
then connects directly to the returned server IP/port
|
||||
|
||||
Not yet built: session tokens (server IP/port are handed back unauthenticated
|
||||
— fine for local dev, not for a real deployment), and a real server pool —
|
||||
only one dev server is registered right now (auto-seeded on API startup);
|
||||
`POST /servers/register` exists for real servers to self-register/heartbeat
|
||||
but nothing calls it yet.
|
||||
|
||||
### Steam Integration
|
||||
|
||||
@@ -79,11 +90,11 @@ func send_input(dir: Vector2, firing: bool) -> void:
|
||||
|
||||
## Networking Checklist
|
||||
|
||||
- [ ] Godot ENet server/client setup
|
||||
- [ ] Player input RPC structure
|
||||
- [ ] Position sync with interpolation
|
||||
- [ ] Ship class registration per peer
|
||||
- [ ] Server-authoritative health/death
|
||||
- [ ] Matchmaking API (queue + lobby assignment)
|
||||
- [x] Godot ENet server/client setup
|
||||
- [x] Player input RPC structure
|
||||
- [x] Position sync with interpolation
|
||||
- [x] Ship class registration per peer
|
||||
- [x] Server-authoritative health/death
|
||||
- [x] Matchmaking API (queue + lobby assignment) — client wired end-to-end; real server pool and ranked MMR-window widening still open, see Current Tasks in `CLAUDE.md`
|
||||
- [ ] GodotSteam auth + VAC
|
||||
- [ ] Lag compensation (basic rewind)
|
||||
|
||||
Reference in New Issue
Block a user