# 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)
