Files
client/overview/tech.md
T
anekdotin 7b44b2f2dc Add real server pool, race roster overhaul, and ship energy system
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>
2026-07-15 17:56:33 -04:00

101 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)