7b44b2f2dc
Server browsing & matchmaking: - HL2-style main menu (QUICK PLAY/SERVER SELECT/OPTIONS/PROFILE/QUIT) replaces the old CASUAL/RANKED tiles; RANKED removed end-to-end (menu, matchmaking-api's Mode.ranked queue path, MMR matching) until ranked is real. - New menu/server_select.gd lists real servers from the matchmaking API's GET /servers and connects directly, retiring the old unreachable menu/server_browser.gd. - Real game-server pool: NetworkManager.host_server() self-registers on boot and heartbeats every 8s with live player counts; API computes available/full status from player_count, and a background sweep marks any server offline once its heartbeat goes stale (catches a crashed server that never deregistered). - Server-select rows get a live UDP ping probe (query port = game port + 10000) instead of trusting stale DB numbers; post-connect HUD shows live RTT off ENet's own peer stats. Race roster overhaul: - Swapped Terran/Mechanos/Vorg for the pivoted roster — Apex Dynamics, Inner Sphere Navy, Outer Rim Collective — each with a 3-ship Fighter/Gunner/Tank lineup, art cropped from concept sheets with background removal + orientation fixes per sheet. - Live headcount + roster + "TEAM FULL" lock on the race-select screen, shared between the initial pre-spawn pick and the pause menu's live SELECT TEAM swap. - Bot personalities (bots/bot_personality.gd): aggression/caution/ accuracy/reaction/awareness traits rolled per bot instead of one fixed AI profile. HUD additions: - Player list (roster, teammates white/enemies yellow, bots flagged), kill feed, minimap, and explosion VFX on death. - Health and energy now render as bars (hud/stat_bar.gd) instead of text in the top-left HUD. Ship energy system: - Per-role max energy (Fighter 100 / Gunner 150 / Tank 250), 75 energy per shot, flat regen (100 per 2.5s), fully server-authoritative and piggybacked on the existing per-tick state broadcast alongside health. - New blue "mirrored" bar top-middle of the screen (hud/energy_bar.gd) whose fill drains from both edges toward the center instead of left-to-right. Ship handling tuning: - Turn rate reduced (4.0 -> 1.0 rad/s) so a quick tap no longer over-rotates; holding past 0.15s ramps to double speed (2.0 rad/s) for fast full turns, gated the same way damage already is so replay during reconciliation can't double-count the hold timer. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
101 lines
3.9 KiB
Markdown
101 lines
3.9 KiB
Markdown
# Tech Stack
|
||
|
||
## Engine & Language
|
||
|
||
- **Engine:** Godot 4.7 (Forward Plus renderer)
|
||
- **Language:** GDScript
|
||
- **Project path:** `/mnt/code/spacewar/spacewar/` (inner folder is the Godot project root)
|
||
- **Platform target:** Linux (Steam Deck native, 1280×800), Windows PC
|
||
- **Art style:** Pixel art (placeholder sprites in use currently)
|
||
|
||
See `structure.md` for the full file tree and scene graph.
|
||
|
||
---
|
||
|
||
## Multiplayer Architecture (Planned)
|
||
|
||
```
|
||
[Client] ──── [Matchmaking Server] ──── [Dedicated Game Server]
|
||
│
|
||
[Backend API]
|
||
(queue, ranks, accounts)
|
||
```
|
||
|
||
### Authoritative Server Model
|
||
|
||
- Dedicated game servers run the authoritative simulation (not player-hosted)
|
||
- Clients send **inputs** (movement, fire, ability) — server validates and broadcasts results
|
||
- No client-side cheating possible on critical state (position, HP, kills)
|
||
- Player position simulated server-side; clients predict locally and reconcile
|
||
|
||
### Godot Networking Layer
|
||
|
||
```gdscript
|
||
# Server: move a player
|
||
@rpc("authority", "call_local", "reliable")
|
||
func set_position(new_pos: Vector2) -> void:
|
||
position = new_pos
|
||
|
||
# Client: send input to server
|
||
@rpc("any_peer", "call_remote", "unreliable")
|
||
func send_input(dir: Vector2, firing: bool) -> void:
|
||
pass
|
||
```
|
||
|
||
- **Transport:** ENet (UDP, built into Godot) for low-latency game data
|
||
- **Reliable channel:** critical state (HP, death, respawn)
|
||
- **Unreliable channel:** high-frequency position/velocity updates
|
||
|
||
### Client-Side Prediction & Lag Compensation
|
||
|
||
1. Client predicts own movement locally (feels instant)
|
||
2. Server confirms or corrects (reconciliation)
|
||
3. Other players interpolated between last two known positions
|
||
4. Lag compensation: server rewinds state slightly to validate hitscan shots
|
||
|
||
### Matchmaking Backend
|
||
|
||
Built — see `matchmaking-api/README.md` for how to run it. Actual stack
|
||
diverged from the original plan below:
|
||
|
||
| 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
|
||
|
||
- **GodotSteam** plugin (open source, wraps Steamworks SDK)
|
||
- Handles: Steam auth, VAC anti-cheat, Steam lobbies, achievements
|
||
- No separate account system needed at launch
|
||
|
||
---
|
||
|
||
## Networking Checklist
|
||
|
||
- [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 for casual; ranked mode implementation removed until it's real (see `CLAUDE.md`); real server pool still open, see Current Tasks in `CLAUDE.md`
|
||
- [ ] GodotSteam auth + VAC
|
||
- [ ] Lag compensation (basic rewind)
|