10

mazerpc

A deterministic FPV maze racer showcasing reproducible gameplay, server‑authoritative physics, and event‑sourced infra.

Labyrinth Racer (MazeRPC)

Fast‑loop, seed‑deterministic FPV maze racer. Monorepo with a Next.js client, a Node Room Authority (WS + event log), shared types/PRNG, a tiny AI helper, and infra bits (Redis, Postgres, Prometheus).

Project repo: J0YY/mazerpc

The gist

  • Deterministic seed → same maze for everyone. Client renders locally using the shared generator; server is authoritative for physics and state.
  • Clients send high‑rate inputs with sequence numbers. Authority ticks at 20 Hz, applies inputs, handles pickups/finish, broadcasts STATE frames.
  • Event sourcing: append significant events to Redis Streams; periodic snapshots to Postgres for restart/replay.
  • Observability: Prometheus metrics at /metrics (tick duration, WS connects, frame bytes).
  • Optional AI: server can request “rival radio” banter or post‑run recaps via OpenAI Responses API (env‑gated, off by default).

Background / inspo

Speedrunning + distributed systems. The thrill is a live authority that never lies: your client can speculate, but the server keeps you honest. The maze is verifiable from a signed seed, so runs are shareable and replayable.

What’s in the box

  • apps/web: Next.js + React Three Fiber client. FPV/Orbit toggle, warm dungeon palette, start/exit beacons, candles, breadcrumb trail, hot/cold proximity panel, local input sampling.
  • apps/room-authority: Express + ws authority. Deterministic maze from seed, 20 Hz loop, collision, pickups, Redis Streams logging, metrics, Prisma snapshots.
  • packages/shared: zod‑typed contracts, mulberry32 PRNG, seed/hash utils.
  • packages/sim: maze generation (recursive backtracker + loop carving), path metric, simple physics with sub‑stepped per‑axis sweeps (anti‑tunneling).
  • packages/ai: minimal OpenAI Responses wrapper (rival banter / coach recap), env‑gated.
  • infra/compose: docker‑compose for Redis + Postgres.

Controls (client)

  • F: Toggle FPV (pointer‑lock) / Orbit (peek). Esc unlocks in FPV.
  • WASD (view‑relative), Shift to sprint.
  • Left panel: escape proximity meter (hot/cold). Right panel: rival radio.
  • Settings: FOV, brightness, grid toggle, candle density, speed.

How it works (high‑level)

  1. Seed notarization: GET /seed/:mode{ seed, roomId, ws }. Seed is sha256(mode|salt|roomUUID).
  2. WS connect: /ws?room=<roomId>&player=<uuid>; first client instantiates the authority with that seed.
  3. INIT: server sends { kind: "INIT", seed, layoutHash, tickRate, ... }; client builds the same maze locally.
  4. Inputs: client sends InputMsg { t_client, seq, move, action } every frame; server applies inputs at 20 Hz (sub‑steps prevent tunneling).
  5. Broadcast: authority emits STATE frames (~10–15 Hz). Client reconciles and renders HUD/breadcrumbs.
  6. Finish: reaching exit emits a Finish event; client may request a signed receipt (seed, timeMs, path hash).

Determinism and fairness

  • Maze = f(seed). Seed is announced and reused on both client/server → verifiable runs.
  • Physics: authority resolves collisions. Client prediction is allowed but authoritative state wins.

Event log and snapshots

  • Events → Redis Streams match:<roomId>:events (fast path).
  • Snapshots every ~5s → Postgres via Prisma for rapid recovery.
  • Replay plan: load latest snapshot, apply events since offset.

Observability

  • /metrics exposes Prometheus counters/histograms: tick duration, frame bytes, WS connects, etc.

AI hooks (optional)

  • Rival banter and coach recap via OpenAI Responses API; both behind env flags.

Repo layout

apps/
  room-authority/   # Node 20 authority (Express + ws + Redis + Prisma)
  web/              # Next.js + R3F client
packages/
  shared/           # zod contracts, PRNG, seed utils
  sim/              # maze gen, path metric, physics
  ai/               # OpenAI helpers (Responses API)
infra/
  compose/          # docker-compose for Redis + Postgres