Files
game2/README.md
T
martijn.diersmann@pm.me 4bdac1e6e8 laatste versie
2026-09-26 23:25:04 +02:00

5.6 KiB
Raw Blame History

🏎️ 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)

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.

npm test

Docker

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.

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

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.