148 lines
5.6 KiB
Markdown
148 lines
5.6 KiB
Markdown
# 🏎️ Turbo Race
|
||
|
||
Multiplayer browser-racegame voor **tot 20 spelers**, speelbaar op de telefoon,
|
||
met een neon/synthwave-look. Server-authoritatief (Node.js + WebSocket),
|
||
deploybaar op je eigen Kubernetes-node.
|
||
|
||
## Features
|
||
|
||
- Verticale scrollende weg, portretmodus, **eenhandig besturen**
|
||
(zet je vinger op de baan; de auto rijdt direct naar die plek en blijft staan).
|
||
- Keyboard-up / pijltjes op desktop.
|
||
- **Host** opent de kamer met `?host=<token>` en start heimelijk met de `S`-toets
|
||
(30-seconden countdown, nieuwe aanmelders blijven welkom).
|
||
De host **rijdt niet mee**, maar volgt als toeschouwer de **koploper (#1)**
|
||
en ziet live de top 5 in het koploperspaneel.
|
||
- Obstakels: **pylonen** (vertragen) en **olievlekken** (wegslijden).
|
||
- Power-ups: **⚡ boost** en **🛡 schild** (respawnen na 4,5s).
|
||
- Live positie, snelheid, afstand, leaderboard en minimap.
|
||
- **1:30 minuten** race over een baan van 23.000 m; wie over de finish is wint,
|
||
anders wint bij aflopen van de tijd wie het verst is. Daarna **uitslag + podium**
|
||
en direct een nieuwe ronde.
|
||
|
||
## Snel starten (lokaal)
|
||
|
||
```bash
|
||
npm install
|
||
npm start # → http://localhost:8080
|
||
```
|
||
|
||
Open `http://localhost:8080` op je laptop én op je telefoon (zelfde wifi).
|
||
Vul een naam in, klik aanmelden, en stuur met je vingers.
|
||
|
||
> 🎮 **De host is toeschouwer en rijdt niet zelf mee.** Open als host:
|
||
> `http://localhost:8080/?host=dev-host`
|
||
> en druk op de **S**-toets om de start-telling in gang te zetten
|
||
> (pas als de kamer vol is of iedereen klaar is 😉). Tijdens de race volgt de
|
||
> **camera de nummer 1** (ook bij wisselende koppositie) en zie je live de
|
||
> **top 5 koplopers**; na afloop de volledige uitslag.
|
||
|
||
### Besturing
|
||
|
||
| Apparaat | Actie |
|
||
| --- | --- |
|
||
| Telefoon | Zet je vinger op de baan; de auto rijdt direct naar die plek en blijft staan tot je beweegt |
|
||
| Toetsenbord | `←` / `A` en `→` / `D` |
|
||
| Host | `S` in lobby of uitslag = race (laten) starten · camera volgt de koploper (#1), rijdt niet mee |
|
||
|
||
## Configuratie (omgevingsvariabelen)
|
||
|
||
| Variabele | Standaard | Betekenis |
|
||
| --- | --- | --- |
|
||
| `PORT` | `8080` | HTTP-poort (ook WebSocket op `/ws`) |
|
||
| `MAX_PLAYERS` | `20` | Maximaal aantal coureurs per kamer |
|
||
| `ADMIN_TOKEN` | `dev-host` | Geheim token waarmee je host wordt |
|
||
| `COUNTDOWN_S` | `30` | Aftellen nadat de host op `S` drukt |
|
||
| `RACE_DURATION_S` | `90` | Maximale racetijd in seconden |
|
||
| `TRACK_LENGTH` | `23000` | Baanglengte in meter (finish = einde van de baan) |
|
||
|
||
> ⚠️ Gebruik in productie een **sterk `ADMIN_TOKEN`** (random string), anders kan
|
||
> iedereen met de URL host worden.
|
||
|
||
## Tests
|
||
|
||
Headless simulatie: engine-unit-test **plus** live integratietest met een echte
|
||
server, 20 websocket-clients en een host die de race start en de uitslag leest.
|
||
|
||
```bash
|
||
npm test
|
||
```
|
||
|
||
## Docker
|
||
|
||
```bash
|
||
docker build -t turbo-race:latest .
|
||
docker run --rm -p 8080:8080 \
|
||
-e ADMIN_TOKEN='moeilijk-te-raden-token' \
|
||
turbo-race:latest
|
||
```
|
||
|
||
## Deployen op Kubernetes
|
||
|
||
Manifests staan in `k8s/` (Kustomize). Bouw de image op je node (of push naar
|
||
een registry) en pas de image-tag in `k8s/base/deployment.yaml` aan.
|
||
|
||
```bash
|
||
# 1) image bouwen (op de node zelf)
|
||
docker build -t turbo-race:latest .
|
||
|
||
# 2) token instellen (kustomization generatiert een Secret)
|
||
# Bewerk de literal 'admin-token=...' in k8s/base/kustomization.yaml
|
||
|
||
# 3) deployen
|
||
kubectl apply -k k8s/base
|
||
kubectl rollout status deploy/turbo-race
|
||
kubectl get svc turbo-race # -> NodePort 30090
|
||
```
|
||
|
||
Speel nu via: `http://<IP-van-je-node>:30090`
|
||
|
||
- Healthcheck: `curl http://<node-ip>:30090/healthz` → `{"ok":true,...}`
|
||
- Met een domein + ingress-controller voeg je `k8s/ingress.yaml` toe
|
||
(pas de `host` aan, WebSocket-tunnels worden lang opengehouden).
|
||
|
||
### Lokaal testen met Kind (macOS/Linux)
|
||
|
||
Op een **echte node** is NodePort 30090 direct bereikbaar op het node-IP.
|
||
Met **Kind** moet de poort wél expliciet naar de host gemapt worden:
|
||
dienst `k8s/kind-config.yaml` mapt `30090 → localhost`.
|
||
|
||
```bash
|
||
kind create cluster --config k8s/kind-config.yaml # alleen bij (her)aanmaken
|
||
kind load docker-image turbo-race:latest
|
||
kubectl apply -k k8s/base
|
||
# speel op: http://localhost:30090
|
||
```
|
||
|
||
> ℹ️ Kind hergebruikt een bestaand cluster; als NodePort niet naar `localhost`
|
||
> werkt, verwijder het cluster eerst (`kind delete cluster`) en maak het opnieuw
|
||
> aan met bovenstaande `--config` (port-mappings staan vast bij creatie).
|
||
|
||
|
||
> ⚠️ **Belangrijk:** de game staat **in-memory** (1 kamer). Houd
|
||
> `replicas: 1` — meerdere replica's zouden spelers over losse kamers verspreiden.
|
||
|
||
## Architectuur
|
||
|
||
```
|
||
public/ frontend (geen bundler): index.html, style.css, client.js, render.js
|
||
server/engine.js server-side spelengine: fysica, obstakels, power-ups, fases, ranking
|
||
server/index.js HTTP/static + WebSocket /ws + events + broadcasts
|
||
test/simulate.js headless test (fluweel: 20 clients ipv 1)
|
||
```
|
||
|
||
- Engine loop 40 Hz; **snapshots op ~16 Hz**; clients **interpoleren** de rendering.
|
||
- Baan is **deterministisch** opgebouwd uit één random seed → eerlijk & identiek
|
||
voor elke speler.
|
||
- De host is een **toeschouwer** (neemt geen spelerslot in); de `host_start`-melding
|
||
wordt alleen geaccepteerd van sockets die met het juiste token zijn aangemeld.
|
||
|
||
## Flow & regels
|
||
|
||
1. **Lobby** — namen aanmelden tot max 20 (fases live zichtbaar).
|
||
2. Host drukt `S` → **30s countdown** (heen-en-weer banners, nieuwe spelers welkom).
|
||
3. **Race** — rechte weg met obstakels en power-ups; finish is het einde van de
|
||
baan. Wie als eerste over de line is, wint; anders wint bij het aflopen van
|
||
de tijd wie het verst is.
|
||
4. **Uitslag** — podium + volledige lijst; de host start opnieuw met `S`.
|