Player
A player in Quake 0 is a fighter: a small rectangle that runs, jumps and drops through platforms, and fires automatically at the nearest rival. This folder holds what a player sends to join, the state each one carries, and the scripts that run over a fighter's life — on join and leave, the per-viewer relevance pass, and the client-only render body.
gamehoster-player-join-schema.json
All a player supplies on connect is a display name.
[ { "gamehoster-player-join-schema-name": "name",
"gamehoster-player-join-schema-type": "string" } ]gamehoster-player-schema.json
The state each fighter carries splits in two. The schema holds only the public
fields, streamed to every viewer to be drawn (the bounded ones carry
min/max/decimals range hints). The physics scratch and
timers the per-tick update reads and writes are not in the schema at all: they
live in the fighter's Player.Persistent bag, seeded
on join and never streamed. The table marks where each field lives.
| Field | Type | Where | Meaning |
|---|---|---|---|
name | string | public | Display name. (There is deliberately no colour field — a fighter's colour is chosen by each front end, not stored on the server.) |
x, y | number | public | Top-left of the fighter rectangle, in tiles. Streamed and interpolated. |
facing | int | public | -1 or 1: which way the muzzle points. |
lastHitTick | int | public | When you were last hit — the front end fades a flash from it. |
deaths, score | int | public | Times fallen off (bumped on every respawn), and points from rivals falling off — your own score resets to 0 when you fall. A change in deaths is watched by the client-only gamehoster-player-smooth.js body, which snaps the fighter across a respawn instead of interpolating. |
vx, vy | number | Persistent | Velocity. Horizontal is run-speed×direction; vertical is the jump/gravity value. |
dir | int | Persistent | Held run intent (-1/0/1) from the move command. |
onGround | bool | Persistent | Standing on a platform this tick (you can only jump then). |
controllable | bool | Persistent | False while flying from a hit — you steer again only on landing. |
jumpHeld, dropHeld, dropUsed | bool | Persistent | Jump-key hold (jump off every landing while held), down-key hold, and the one-drop-per-press latch. |
firePhase, fireBlockUntil | int | Persistent | The tick offset your auto-fire lands on, and a tick until which a hit mutes it. |
The seven public fields are the schema; the ten Persistent fields
(vx, vy, dir, onGround,
controllable, jumpHeld, dropHeld, dropUsed,
firePhase, fireBlockUntil) are the free-form server-only bag.
gamehoster-player-join.js
Runs the moment a player connects: it routes them into an instance that still has room (or
opens a fresh one), then sets up their fighter. It writes the public state (name, a random
column well above the level so they fall in from off the top of the screen, facing, the
last-hit stamp and the death/score counters), then seeds the server-only physics and timers —
velocity, run intent, the grounded and controllable flags, the key-hold latches, and a fire
phase so everyone's constant shooting is staggered — into the fighter's
Player.Persistent bag, which is never streamed. No colour is set: that is a
front-end choice.
gamehoster-player-leave.js
When a player leaves, their in-flight bullets are destroyed; the level and everyone else are untouched.
gamehoster-player-view.js
Per-viewer relevance, run once per viewer at the send rate. The level is small and holds at most five fighters, so there is no fog of war: it simply marks every other player and every bullet and hit as relevant, and the whole arena is shown to everyone.
gamehoster-player-smooth.js
Client-only, and it ships with the game's baked client. By default the client interpolates
every numeric field between the two server samples straddling the render tick, which is exactly
right while a fighter runs and falls. This body overrides that default for one case only: a
respawn. A fighter that drops off the bottom reappears at a fresh column above
the top of the screen, and interpolating position across that jump would slide it straight up
the map. It watches the public deaths counter — while the smoothed counter still
lags the authoritative one it snaps x/y to the latest
value instead of interpolating across the gap. Every other field, and normal running and
falling, keeps the default smoothing. See Frontend.