Skip to content

Multiplayer and Networking

Dominik Burger edited this page Jun 10, 2026 · 5 revisions

Multiplayer and Networking [CURRENT]

Multiplayer Model

Online multiplayer is live at tamersquest.com — one combined Node service serves the client and runs the authoritative WebSocket game on the same origin.

  • Up to 16 players share one round on one map instance; multiple rounds run concurrently.
  • Authoritative server owns all state (positions, monsters, combat, zone). Clients send inputs & render snapshots — never trusted (see Anti-cheat).
  • Map by seed: the server picks a seed and sends only that; clients regenerate the identical map deterministically (seeded RNG).
  • Sessions: a logged-in account (Google / Discord / email) owns its characters server-side — cloud saves that follow it across devices, authorized by an account session token. A guest (nickname, isGuest: true) plays session-only and keeps no saved characters. Each character is a server profile keyed by an opaque token the client uses to resume that character's run on reconnect. See Onboarding and Title.
  • Visibility & monster behaviour: the server filters each snapshot to an area of interest — some monsters spawn hidden and only reveal at close range (ambush). About 30% of visible monsters are hunters that slowly walk toward a player within 700px (~35% of base speed, deterministic subset); the rest idle, and hidden monsters always stay put as ambushers — so a monster can come to you.

The client never owns truth — it sends intent, predicts its own move locally, and reconciles to what the server reports:

sequenceDiagram
    participant C as Client
    participant S as Authoritative Server
    C->>S: input (move direction, action)
    Note over S: integrate at 15 Hz<br/>collision, combat, zone
    S-->>C: filtered snapshot (~7.5 Hz)
    Note over C: predict self + extrapolate rivals,<br/>then reconcile to the snapshot
Loading

Networking & Anti-cheat

Aspect Value / rule
Tick rate 15 Hz authoritative simulation
Snapshot rate ~7.5 Hz (every other tick), per-player
Area of interest visible monsters within 900px; hidden monsters only within 220px
Hidden monster split ~35% of a round's monsters start hidden (deterministic from id)
Client rendering predicts your own movement locally (same BASE_SPEED×sprint + shared isWalkable collision the server uses) and reconciles toward the authoritative snapshot (error-proportional; hard-snaps on a big divergence); extrapolates rivals by their estimated velocity between snapshots, then corrects — so your moves feel instant and rivals glide instead of stutter, with the server still authoritative (#10/#80)
Movement authority client sends a direction only; server integrates at BASE_SPEED (200px/s), clamps the axis to [-1,1] (NaN/∞-safe), normalizes diagonals
Tile collision server per-axis: a move applies only if the destination tile is walkable → slide along walls
Map bounds positions clamped to the map; no walking off into the void
Combat actions server honors only the active monster's own attacks (off-roster names → a skipped turn); a player can only act on their own combat

Reconnect (Q12 resolved): a dropped in-round player keeps their slot for a 120s grace window — reconnecting with the token resumes the round at the current position; no return within 120s counts as a death (loses the active team, per Q10). The client auto-reconnects. See Open Questions.

Clone this wiki locally