Instance

An instance is one running match of Quake 0 — one level, up to five fighters, and all the bullets in flight. This folder holds the instance's constants (the level itself and every physics number) and the authoritative update that runs the whole game each tick.

gamehoster-instance-schema.json

Everything here is constant for the life of the instance and sent once to every viewer, so the front end can size and draw the level exactly as the server simulates it. The level is a 30×12 string, but it is not fixed here — it is chosen per instance in instance-create (see below). The rest are physics numbers: gravity, jumpV (tuned so a jump rises about 1.5 tiles — just onto the next ledge up), runSpeed, maxFall (capped below a tile per tick so a fall can never tunnel through a floor), the playerW/ playerH hit-box, bulletSpeed, fireEvery (5 ticks — twice as fast as Asteroids), the knockX/knockY hit impulse (a hit always launches you a little upward), fireBlock (60 ticks ≈ 2 s of muted fire after a hit), deathBelow (50 tiles under the level), and the bullet-grid padding.

gamehoster-instance-create.js

Runs once when an instance is made, and does two things. First it picks this instance's level: five variants are laid out right in the code as arrays of strings, one row per line so each reads exactly like the platforms it makes — a floor cell is an _ and everything else is empty air. One is chosen at random and stored in the sent level state. A row shorter than the width is padded as empty, and anything past the width is ignored, so the maps are easy to edit without counting spaces.

const LEVELS = [
  [
    "                              ",
    "          ___        ___      ",
    "          ___        ___      ",
    "                     ______   ",
    "             ____       ___   ",
    "  ___        ____  __         ",
    "  ___   __         __         ",
    ...
    "____     ____     ___    ____ "
  ],
  ... four more ...
]
const pick = LEVELS[Math.floor(Gamehoster.Entropy() * LEVELS.length)]
One of the five levels. Mostly open air, with platforms of mixed width built into vertical stacks — one in the square directly above another — so you climb a stack straight up rather than hopping lone ledges two rows apart. Every platform is reachable a single jump at a time, and the ground is full of holes to fall through.

Second, it builds the one piece of server-only persistent state the update keeps: the bullet grid — one bucket per tile, extended ten tiles past every edge (bullets can fly off the floating level while a knocked-off player is still being shot at). Each tick the update clears and re-fills it, so a fighter's hit test only looks at the handful of buckets its body overlaps.

gamehoster-instance-update.js

The authoritative per-tick pass. The whole game runs here, in order, so nothing races:

  1. Move each fighter. Horizontal motion is direct — run-speed × your held direction, changeable in mid-air. Vertical motion is a velocity under gravity, kicked up by a jump and zeroed on landing. Platforms are one-way: only while descending do we test the two bottom corners against every floor line the bottom edge crossed this tick, and stop at the first solid one. Jumping up, you pass straight through. Pressing down punches you just below the current floor so you fall to the next one — once per press.
  2. Separate any overlap. No two fighters are ever left in exactly the same place: a pair whose centres are too close is nudged apart, so a point-blank shot always has somewhere to go and can connect.
  3. Move the bullets in straight lines, turn any that cross a floor into a hit effect, drop any that leave the padded grid, and file the rest into the bullet grid.
  4. Test bodies. For each fighter, check only the nearby buckets with an exact segment-vs-rectangle test. A hit flings them the way the bullet was travelling (so a shot never flips you back on itself), locks their facing, mutes their fire for two seconds and takes their control until they land.
  5. Drop the fallen. Anyone more than fifty tiles below the level counts one death, scores a point for everyone else, and respawns above the level to fall back in.
  6. Fire. Each fighter, on its own phase, sends a bullet at the nearest rival — or, at point-blank range, in a random direction, which still lands because they overlap.