Files
Every file in a game definition, with the keys it holds or the library functions it may call. A game is a
single directory. At its top level sit the game config and its startup script, plus the
gamehoster-commands/, gamehoster-entities/ and optional
gamehoster-bots/ folders; the two player schemas and
their scripts live in a gamehoster-player/ folder, and the instance schema and its update
files in a gamehoster-instance/ folder. Names prefixed
gamehoster- are fixed structure; the names you choose (fields, commands, entity types) are
unprefixed. JSON files list their keys; JS files are raw function bodies and list the Gamehoster functions
callable inside them. Every body runs on the server, the only simulator, with one
exception: the client predict body
(gamehoster-player-predict.js) runs on the
client to predict your own object, and it alone is handed Gamehoster.Input,
Gamehoster.Authoritative and Gamehoster.Lead(). Every JS body may call
Gamehoster.Time() (ms),
Gamehoster.Tick() (int), Gamehoster.Random(), a repeatable float in [0,1) seeded by
the current tick and context, and Gamehoster.Entropy(), true unpredictable randomness for
authoritative rolls. The usage guide explains them in context.
gamehoster-game.json
Game-wide configuration: title, how players browse and create, the two clocks, and capacity. See Usage → Games.
| Key | Type | Values / notes |
|---|---|---|
gamehoster-game-title | string | Display name. |
gamehoster-game-browse | string | none (placed straight in) or instances (pick a live instance, or create one). |
gamehoster-game-create | string | none / public / password / private: whether players may create their own instance, and how it is listed. |
gamehoster-game-tickRate | number | The simulation rate: server logic steps per second (dt = 1/tickRate). |
gamehoster-game-sendRate | number | The network rate: how often the server emits a per-player packet. Much lower than the tick rate (e.g. tick 60, send 20). Rendering has no rate: the front end runs its own frame loop and reads an interpolated view. |
gamehoster-game-capacity | number | The most players one instance holds. |
gamehoster-game-botsMax | number | Optional bot padding: the most bots an instance holds. Moved here from the instance schema — it is game config, not streamed state. |
gamehoster-game-botsZeroAt | number | Optional: the human count at which the bot target falls to zero. With botsMax, sets how bots ease out as players arrive. Also game config. |
gamehoster-game-origins | string / array | Optional. The web origin(s) allowed to open this game's socket, usually the one site that embeds it ("https://example.com"), or a list. "*.example.com" matches any subdomain; "*", or omitting the key, allows any origin. Requests with no Origin (non-browser clients) are always allowed. The game is served at wss://<domain>/<game> regardless; this only gates which pages may connect. |
gamehoster-game-startup.js
An optional script that runs once, on the server, when the game first starts up, before any player connects. Use it for one-time setup, such as creating a set of standing instances to drop into. See Usage → Games.
| Function | What it does |
|---|---|
Gamehoster.Instance.List() | The ids of the live instances. |
Gamehoster.Instance.Info(id) | { players, capacity, … } for an instance. |
Gamehoster.Instance.State.Get(id, name) | Read an instance's state field. |
Gamehoster.Instance.Create() | Create a new instance, returns its id. |
gamehoster-player/gamehoster-player-join-schema.json
What a player provides on connect, fixed for the whole session. An array of field
objects, each with fully-qualified keys and no visibility
(join data is fixed, never streamed state). See
Usage → Players.
| Entry key | Type | Values / notes |
|---|---|---|
gamehoster-player-join-schema-name | string | The field's name. |
gamehoster-player-join-schema-type | string | Required: a type (number, int, bool, string). |
gamehoster-player-join-schema-default | optional | A default value; omit it to force the player to supply one. |
gamehoster-player/gamehoster-player-join.js
Runs on the server the moment a player connects, and does two jobs: it routes the player
into an instance (picking a live one that still has room or creating a fresh one) and then
sets them up, filling their per-instance state from their fixed join data.
Gamehoster.Context.playerId is the joining player; once they have joined,
Gamehoster.Context.instanceId names their instance. See Usage → Players.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | The joining player's id. |
Gamehoster.Join.Get(name) | A join-schema value the player supplied. |
Gamehoster.Instance.List() | Ids of the live instances, to route between. |
Gamehoster.Instance.Info(id) | { players, capacity, … } for an instance, to choose between them. |
Gamehoster.Instance.State.Get(id, name) | Read an instance's state field, e.g. to filter by phase. |
Gamehoster.Instance.Join(id) | Place this player into that instance. |
Gamehoster.Instance.Create() | Make a fresh instance, returns its id. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read the other players in the instance. |
Gamehoster.Player.State.Set(Gamehoster.Context.playerId, name, value) | Fill the joining player's per-instance state. |
gamehoster-player/gamehoster-player-schema.json
The player's per-instance state (the fields they carry inside an instance) in the
compact, typed form: a map keyed by field name, each entry an object
{ type, visibility, [min], [max], [decimals], [default] }. See
Usage → Players. The instance and entity schemas share this exact
shape. Each field declares a visibility of public or private
(owner-only). State that no client should see is not a schema field — it lives in a
server-only persistent bag instead.
| Entry key | Type | Values / notes |
|---|---|---|
type | string | Required: number, int, bool, string. See Types. |
visibility | string | public (the default: streamed to every player who may see the object, whenever it changes) or private (owner-only: on a player, streamed only to that player; nobody else sees it). Private is only meaningful on players — instances and entities have no owner. |
min / max | number | Optional range hints on a bounded number or int: the lowest and highest value it takes. Used to validate and to pack it compactly on the wire. Omit on an unbounded counter. |
decimals | int | Optional, on a number: a non-negative count of significant decimal places to keep, so the value quantises to that precision on the wire. |
default | — | Value on create. |
gamehoster-player/gamehoster-player-leave.js
Backend: cleanup when a player leaves their instance. Gamehoster.Context.playerId is the
leaving player and Gamehoster.Context.instanceId their instance. See
Usage → Players.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | The leaving player's id. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) / .Set(id, name, value) | Read or write the players still in the instance. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities. |
Gamehoster.Entity.Destroy(id) | Remove an entity they owned. |
gamehoster-player/gamehoster-player-view.js
Optional: per-viewer relevance (fog of war). It runs on the backend at the
send rate, viewer-centric (Gamehoster.Context.playerId is the viewer),
and marks which players and entities are relevant to that viewer with
Gamehoster.View.Include(id). Relevance is the outer gate: a thing you are not shown sends you
none of its fields, and one becoming relevant arrives in an update's
appear list (leaving, in its gone
list), so include a thing slightly before it is needed to pre-fetch it. Omit this file and
everything is relevant to everyone: full visibility. Large data (maps, level geometry) is modelled as
entities so it streams once on appear, not per tick. See
Usage → Players.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | The viewer whose relevance set is being built. |
Gamehoster.View.Include(id) | Mark a player or entity relevant to this viewer (also the pre-fetch hook for soon-relevant things). |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players, to decide who is relevant. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities, to decide what is relevant. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
gamehoster-player/gamehoster-player-update.js
The player's own per-tick update, run on the server: the current shape, one authoritative
body per player. It reads any field of the players and entities in the instance and, by convention, writes that
player's own state: their movement and other per-tick change. It may call Gamehoster.Random()
(repeatable) and Gamehoster.Entropy() (true randomness). Gamehoster.Context.playerId
is the player and Gamehoster.Context.instanceId the instance. Optional: a player
may have this one body, the legacy pair below, or
neither; when this file is present the engine runs it and ignores the pair. See
the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | This player's id. |
Gamehoster.Player.State.Get(Gamehoster.Context.playerId, name) / .Set(Gamehoster.Context.playerId, name, value) | Read / write this player's state. |
Gamehoster.Player.Persistent(Gamehoster.Context.playerId).Get() / .Set(value) | This player's server-only persistent bag: free-form scratch (velocities, cooldowns, timers, AI memory) kept between ticks and never streamed. The home of the old server-only fields. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read other players' state. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities' state. |
gamehoster-player/gamehoster-player-predict.js
Optional, and the one body that runs on the client rather than the server. The generated
client runtime runs it each tick for the local player's own object only, fed your live input,
so your object moves the instant you act instead of a round trip later. In one body it simulates that object
forward from live input and reconciles the result toward the authoritative state (extrapolated to the present),
snapping on a big jump such as a respawn. Everything else on screen is interpolated; only your own object is
predicted, and the body writes only your own object's predicted state.
Gamehoster.Context.playerId is you and Gamehoster.Context.instanceId your instance.
See Internals → Frontend.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | You, the local player being predicted. |
Gamehoster.Input.Get(name) | A live local input value the renderer feeds through conn.input (e.g. thrust held, aim angle). |
Gamehoster.Authoritative.Get(id, field) | The newest authoritative value of any object's field, straight from the server, un-interpolated. |
Gamehoster.Lead() | How many ticks the present leads the newest authoritative data, for extrapolating a known position to now. |
Gamehoster.Tick() | The present (leading-edge) tick. |
Gamehoster.Player.State.Get(id, name) | Read state: your own predicted state for yourself, the newest authoritative state for anyone else. |
Gamehoster.Player.State.Set(Gamehoster.Context.playerId, name, value) | Write your own object's predicted state. A write to any other id is ignored. |
Gamehoster.Instance.State.Get(id, name) · Gamehoster.Entity.State.Get(id, name) | Read the newest authoritative instance and entity state. |
gamehoster-<kind>-enter.js — client-only, per object
Optional, and one of the three client-only per-object bodies. Sits beside an object's schema —
gamehoster-player/gamehoster-player-enter.js, or
gamehoster-entities/<type>/gamehoster-entity-enter.js — and runs once, in the
browser, the moment that object first appears to this viewer.
Its job is to seed the object's front-end
persistent bag: a per-object, client-side scratch the matching
-smooth.js body reads while rendering (a last-seen spawn
counter, a cached colour). It runs on the client only and touches no game state; a game that ships neither body
renders exactly as before. Distinct from the protocol's appear delta — this is the code that runs
on appear. See Internals → Frontend.
| Function | What it does |
|---|---|
Gamehoster.Context.id · .kind · .type · .isSelf | The object appearing: its id, its kind (player / entity), its type, and whether it is your own object. |
Gamehoster.State.Get(name) | A field of this object's first authoritative state, to seed from. |
Gamehoster.Persist.Get(name) / .Set(name, value) | The object's front-end persistent bag: read or seed a per-object client-side value the -smooth.js body and the renderer (as object.data) later read. |
Gamehoster.Tick() | The render tick this object appeared on. |
gamehoster-<kind>-smooth.js — client-only, per object
Optional, another client-only per-object body. Sits beside an object's schema
(gamehoster-player/gamehoster-player-smooth.js,
gamehoster-entities/<type>/gamehoster-entity-smooth.js) and runs each display frame, in the
browser, to override the default interpolation per field. The client still eases every numeric
field between snapshots by default; this body only changes the fields it chooses to. A body that does nothing
leaves full default smoothing in place. Its canonical use is respawn / teleport: interpolating a
position across a respawn would slide the object across the map, so the body watches a public counter
(deaths / spawns) and Sets the field to Latest while the
smoothed counter still lags — snapping instead of sliding. See
Internals → Frontend.
| Function | What it does |
|---|---|
Gamehoster.Smooth(name) | The default interpolated value of a field, the value the client would draw with no override. |
Gamehoster.Latest(name) | The newest authoritative value of a field, straight from the server, un-interpolated. |
Gamehoster.Prev(name) · .Next(name) · .Frac() | The two authoritative samples straddling the render tick, and the fraction between them, for computing a custom blend. |
Gamehoster.Lerp(a, b, t) · .LerpWrap(a, b, t, range) · .Wrap(v, range) | Interpolation helpers: a plain lerp, a lerp that takes the short way round a wrapping range (an angle), and a wrap into [0, range). |
Gamehoster.Persist.Get(name) / .Set(name, value) | The object's front-end persistent bag, seeded by -enter.js. |
Gamehoster.Set(name, value) | Override the rendered value of a field for this frame. |
Gamehoster.Tick() | The render tick being drawn. |
gamehoster-<kind>-exit.js — client-only, per object
Optional, the third client-only per-object body. Sits beside an object's schema
(gamehoster-player/gamehoster-player-exit.js,
gamehoster-entities/<type>/gamehoster-entity-exit.js) and runs once, in the
browser, the moment that object disappears from this viewer,
before its record is dropped. It is the front-end analogue of a leave, for a departure effect. It still sees the
object's last state and its persistent bag. It runs on the client only and touches no game state; a game that
ships none of the three renders exactly as before. See
Internals → Frontend.
| Function | What it does |
|---|---|
Gamehoster.Context.id · .kind · .type · .isSelf | The object disappearing: its id, its kind (player / entity), its type, and whether it is your own object. |
Gamehoster.State.Get(name) | A field of this object's last authoritative state. |
Gamehoster.Persist.Get(name) / .Set(name, value) | The object's front-end persistent bag, as the -enter.js and -smooth.js bodies left it. |
Gamehoster.Tick() | The render tick this object disappeared on. |
gamehoster-frontend/gamehoster-frontend-setup.js — client-only, once
Optional, and the first of the game-wide abstraction bodies (a game either builds on the
abstraction with these, or writes a manual renderer against Gamehoster.connect).
Runs once when the game starts and the root element is known. Its job is to reach the root
(Gamehoster.Root()) and stash whatever the render loop needs in the game's persistent bag
(Gamehoster.Persistent.Set): the canvas, its drawing context, image caches. See
Internals → Frontend → The abstraction.
gamehoster-frontend/gamehoster-frontend-render.js — client-only, each frame
Optional. Runs every animation frame to draw. It reads the world off Gamehoster:
Instance, Players and Entities (Maps of views
carrying interpolated fields plus current/prev/data), Frame
(number/time/dt/tick), and Mouse/Keys
for input state. It draws into the context set up in -setup.js; it never touches a netId or the socket.
gamehoster-frontend/gamehoster-frontend-update.js — client-only, each update
Optional. Runs once per server update, after every per-object appear / leave handler. It is where
front-end state that must follow the authoritative frame is kept — a leaderboard sort, a score memory, a global
counter — reading each object's current (newest authoritative) rather than the interpolated draw, and
writing to a persistent bag.
gamehoster-frontend/gamehoster-frontend-tick.js — client-only, each tick
Optional. Runs each client tick (the game's tick rate), for logic paced to ticks rather than display frames.
gamehoster-frontend/gamehoster-frontend-input-<event>.js — client-only, per event
Optional. Input is a set of per-event bodies in gamehoster-frontend/, each named
for the event it runs on and run on that event. The client attaches a DOM listener only for a family (mouse or
keyboard) that has at least one body, so a pointer-only game takes no key listener and a game with no input body
leaves the keyboard and pointer untouched. Inside a body you read
Gamehoster.Mouse or
Gamehoster.Keys, send a command with
Gamehoster.Command(name, params), and feed local
prediction with Gamehoster.Input.Set. See
Functions → Input.
| File | Runs on |
|---|---|
gamehoster-frontend-input-mouse-move.js | The pointer moving; under capture it carries the lock delta in Mouse.dx/dy. |
gamehoster-frontend-input-mouse-enter.js · -mouse-leave.js | The pointer entering or leaving the root; Escape releasing a capture fires leave. |
gamehoster-frontend-input-mouse-button-<left|middle|right>-<press|release>.js | A mouse button going down or coming up. |
gamehoster-frontend-input-keyboard-<down|up|left|right|space|enter>-<press|release>.js | An arrow, space or enter going down or coming up. |
gamehoster-frontend-input-keyboard-<letter|number>-<press|release>.js | Any letter or any digit; the character is in Gamehoster.Event.key. |
gamehoster-frontend-input-keyboard-any-<press|release>.js | Any key; it fires alongside the specific body, with the key in Gamehoster.Event.key. |
gamehoster-frontend/gamehoster-frontend-settings.json — client-only
Optional. The front-end input settings, two keys. rate maps a command name to a cap in sends a
second: the client paces that command to the cap and coalesces to the latest, the usual home for a pointer
aim. mouseCapture turns on pointer lock: the client locks the pointer on a button press,
mouse-move then reports movement as
Gamehoster.Mouse.dx/dy, the cursor is hidden and recentred, and Escape releases it,
firing mouse-leave. See Functions → Input.
{
"mouseCapture": true,
"rate": { "aim": 20 }
}
| Key | Type | Values / notes |
|---|---|---|
mouseCapture | bool | Turn on pointer lock, so mouse-move gives the delta and the cursor is hidden. |
rate | object | A map of command name to a per-second cap; a capped command coalesces to its latest send. |
gamehoster-player/gamehoster-player-update-backend.js
Legacy pair with
gamehoster-player-update-frontend.js,
superseded by the single gamehoster-player-update.js
above and still accepted for players not yet converted (the engine runs the single body if present, and falls
back to this pair otherwise). The player's own per-tick update: an optional second body run on the server
after gamehoster-player-update-frontend.js.
Both run on the server each tick, both are authoritative, and both may write any field and call
Gamehoster.Entropy(); splitting a player's per-tick logic across the two files is a convenience,
not a trust boundary. By convention a player update writes that player's own state.
Gamehoster.Context.playerId is the player and Gamehoster.Context.instanceId the
instance. Have one file, both, or neither. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | This player's id. |
Gamehoster.Player.State.Get(Gamehoster.Context.playerId, name) / .Set(Gamehoster.Context.playerId, name, value) | Read / write this player's state. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read other players. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities. |
gamehoster-player/gamehoster-player-update-frontend.js
Legacy pair with
gamehoster-player-update-backend.js,
superseded by the single gamehoster-player-update.js;
the engine runs the single body if present and falls back to this pair otherwise. The player's own per-tick
update, run on the server. It reads any field of the players and entities in the
instance and, by convention, writes that player's own state: their movement and other per-tick change. It may
call Gamehoster.Random() (repeatable) and Gamehoster.Entropy() (true randomness).
Gamehoster.Context.playerId is the player and Gamehoster.Context.instanceId the
instance. Optional, like its
backend counterpart: a player may have this file,
gamehoster-player-update-backend.js, both,
or neither. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.playerId | This player's id. |
Gamehoster.Player.State.Get(Gamehoster.Context.playerId, name) / .Set(Gamehoster.Context.playerId, name, value) | Read / write this player's state. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read other players' state. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities' state. |
gamehoster-instance/gamehoster-instance-schema.json
The instance's own state, the fields that describe the whole room rather than any one player or entity:
the phase (waiting / playing / over), the winner, and
the fixed tuning constants that shape a match. Same compact form as the
player schema: a map keyed by field name, each entry
{ type, visibility, [min], [max], [decimals], [default] }. Instance state stays small and
always relevant (phase, score, constants); it is never gated by view.
An instance has no owner, so its fields are effectively all public. See
Usage → Instances.
| Entry key | Type | Values / notes |
|---|---|---|
type | string | Required: number, int, bool, string. |
visibility | string | public / private; see the player schema. |
min / max / decimals | number | Optional numeric range hints; see the player schema. |
default | — | Value on create. |
gamehoster-instance/gamehoster-instance-create.js
Optional, server-only. Runs once, on the server, the moment an instance is created — before
its first tick and before any player is in it — with only Gamehoster.Context.instanceId in scope.
The place to set an instance up before anyone can see it: seed its server-only
persistent scratch (a spatial grid, an object pool) with
Gamehoster.Instance.Persistent.Set(…), spawn the opening entity set, or write initial instance
state. See Usage → Instances.
| Function | What it does |
|---|---|
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Instance.Persistent.Get() / .Set(value) | The instance's server-only scratch, kept between ticks and never sent — a Map or any structure. |
Gamehoster.Entity.Spawn(type, state) | Create an entity, returns its id. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) / .Set(id, name, value) | Read / write entity state. |
gamehoster-instance/gamehoster-instance-update.js
The instance update, run on the server: the current shape, one authoritative body for step 3
of the tick. The place for global change: the phase machine (waiting → playing → over), scoring, the serve,
cross-cutting logic, and spawning or destroying entities. It may call Gamehoster.Random() and
Gamehoster.Entropy(). Gamehoster.Context.instanceId is the instance; there is no
current player here. Optional: an instance may have this one body, the
legacy pair below, or neither; when this file is present
the engine runs it and ignores the pair. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Player.List() | Ids of players in this instance. |
Gamehoster.Player.State.Get(id, name) / .Set(id, name, value) | Read / write any player's state. |
Gamehoster.Entity.List(type) | Ids of entities of a type. |
Gamehoster.Entity.State.Get(id, name) / .Set(id, name, value) | Read / write any entity's state. |
Gamehoster.Entity.Spawn(type, state) | Create an entity, returns its id. |
Gamehoster.Entity.Destroy(id) | Remove an entity. |
gamehoster-instance/gamehoster-instance-update-backend.js
Legacy pair with
gamehoster-instance-update-frontend.js,
superseded by the single gamehoster-instance-update.js
above; the engine runs the single body if present and falls back to this pair otherwise. The instance update's
optional second body: step 3 of the tick, run on the server
after gamehoster-instance-update-frontend.js.
The place for global, authoritative change: the phase machine
(waiting → playing → over), scoring, the serve, cross-cutting logic, and spawning or destroying entities.
It may write any field and call Gamehoster.Entropy().
Gamehoster.Context.instanceId is the instance; there is no current player here. See
the tick.
| Function | What it does |
|---|---|
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Player.List() | Ids of players in this instance. |
Gamehoster.Player.State.Get(id, name) / .Set(id, name, value) | Read / write any player's state. |
Gamehoster.Entity.List(type) | Ids of entities of a type. |
Gamehoster.Entity.State.Get(id, name) / .Set(id, name, value) | Read / write any entity's state. |
Gamehoster.Entity.Spawn(type, state) | Create an entity, returns its id. |
Gamehoster.Entity.Destroy(id) | Remove an entity. |
gamehoster-instance/gamehoster-instance-update-frontend.js
Legacy pair with
gamehoster-instance-update-backend.js,
superseded by the single gamehoster-instance-update.js;
the engine runs the single body if present and falls back to this pair otherwise.
The instance update's first body: step 3 of the tick, run on the server. It reads and writes the instance's
own state and the players and entities in it, the same authoritative surface as its
backend counterpart. May call
Gamehoster.Random() and Gamehoster.Entropy().
Gamehoster.Context.instanceId is the instance. Optional, like its
backend counterpart: a slot may have either file, both, or
neither. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players' state. |
Gamehoster.Entity.List(type) | Ids of entities of a type. |
Gamehoster.Entity.State.Get(id, name) / .Set(id, name, value) | Read / write entity state. |
gamehoster-commands/<name>/gamehoster-command-schema.json
One directory per command under gamehoster-commands/: holding this parameter schema, the
gamehoster-command.json writes manifest, and the
handler. The schema is the command's input parameters, keyed by name:
each entry is { type } (for example an axis number, or a
{ from, to } pair). Command parameters carry no
visibility; they are transient input, not stored state. An empty object is a
bare trigger with no parameters. See Usage → Commands.
| Entry key | Type | Values / notes |
|---|---|---|
type | string | Required: a type (number, int, bool, string). |
gamehoster-commands/<name>/gamehoster-command.json
The command's declarative dependency manifest, one key, writes: the list
of state fields the handler may change, as dotted paths
player.<field> (the sender's own state) or instance.<field>. A
command whose only effect is a server-side spawn or authoritative change writes [].
{ "writes": ["player.y"] }
moveY manifest: it moves only the sender's paddlewrites is the handler's allowed-write set, enforced by the
write firewall. When the handler runs, the engine buffers its
writes and, on return, commits only those that match a path in writes; any other write is
dropped. So a command can only ever change the sender's own player.<field> and the
instance.<field>s you list here, whatever the handler code attempts. See
Usage → Commands.
| Key | Type | Values / notes |
|---|---|---|
writes | array | Dotted field paths the handler may write: player.<field> or instance.<field>. Empty for spawn-only / server-authoritative commands. |
gamehoster-commands/<name>/gamehoster-command-handler.js
The single handler for a command, run on the server when the command reaches the next tick of the sender's
instance. Gamehoster.Context.playerId is the sending player and
Gamehoster.Context.instanceId their instance. It may only change the fields the command
writes; the write
firewall drops anything else. A command that isn't allowed right now is simply ignored: the handler reads
the sender's state and does nothing when it doesn't permit the action. It may call
Gamehoster.Random() and Gamehoster.Entropy(). See
Usage → Commands.
| Function | What it does |
|---|---|
Gamehoster.Command.Get(param) | A value from the command schema. |
Gamehoster.Context.playerId | The sending player's id. |
Gamehoster.Player.State.Get(Gamehoster.Context.playerId, name) / .Set(Gamehoster.Context.playerId, name, value) | Read / write the sender's own state. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) / .Set(Gamehoster.Context.instanceId, name, value) | Read / write the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read other players. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities. |
gamehoster-entities/<type>/gamehoster-entity-schema.json
One directory per entity type under gamehoster-entities/. The schema is the entity's own
state, in the same compact form as the player and
instance schemas: a map keyed by field name, each entry
{ type, visibility, [min], [max], [decimals], [default] }. Entities are the unit of
relevance, so large streamed data (maps, geometry, assets) is modelled as an
entity and sent once on appear. An entity has no owner, so its fields are effectively all
public. See Usage → Entities.
| Entry key | Type | Values / notes |
|---|---|---|
type | string | Required: number, int, bool, string. |
visibility | string | public / private; see the player schema. |
min / max / decimals | number | Optional numeric range hints; see the player schema. |
default | — | Value on create. |
gamehoster-entities/<type>/gamehoster-entity-update.js
The entity update, run on the server: the current shape, one authoritative body per entity for
step 2 of the tick. It reads the instance, players and other entities and, by convention, writes its own state:
pong's ball is one integration step here. It may call Gamehoster.Random() and
Gamehoster.Entropy(). Gamehoster.Context.entityId is the entity and
Gamehoster.Context.instanceId its instance. Optional: an entity may have this one
body, the legacy pair below, or neither; when this file
is present the engine runs it and ignores the pair. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.entityId | This entity's id. |
Gamehoster.Entity.State.Get(Gamehoster.Context.entityId, name) / .Set(Gamehoster.Context.entityId, name, value) | Read / write this entity's state. |
Gamehoster.Entity.Persistent(Gamehoster.Context.entityId).Get() / .Set(value) | This entity's server-only persistent bag: free-form scratch kept between ticks and never streamed. Where a value that isn't derivable from public state (and isn't just recomputed) lives. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read other entities. |
gamehoster-entities/<type>/gamehoster-entity-update-backend.js
Legacy pair with
gamehoster-entity-update-frontend.js,
superseded by the single gamehoster-entity-update.js
above; the engine runs the single body if present and falls back to this pair otherwise. The entity update's
optional second body: step 2 of the tick, run on the server
after gamehoster-entity-update-frontend.js.
Both run on the server, both are authoritative, and both may write any field and call
Gamehoster.Entropy(); by convention an entity update writes its own state.
Gamehoster.Context.entityId is the entity and Gamehoster.Context.instanceId its
instance. Omit it for an entity that needs only the one body, as pong's ball does. See
the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.entityId | This entity's id. |
Gamehoster.Entity.State.Get(Gamehoster.Context.entityId, name) / .Set(Gamehoster.Context.entityId, name, value) | Read / write this entity's state. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read other entities. |
gamehoster-entities/<type>/gamehoster-entity-update-frontend.js
Legacy pair with
gamehoster-entity-update-backend.js,
superseded by the single gamehoster-entity-update.js;
the engine runs the single body if present and falls back to this pair otherwise.
The entity update's first body: step 2 of the tick, run on the server. It reads the instance, players and
other entities and, by convention, writes its own state: pong's ball is one integration step here. May call
Gamehoster.Random() and Gamehoster.Entropy().
Gamehoster.Context.entityId is the entity and
Gamehoster.Context.instanceId its instance. Optional: an entity may have this
file, gamehoster-entity-update-backend.js,
both, or neither. See the tick.
| Function | What it does |
|---|---|
Gamehoster.Context.entityId | This entity's id. |
Gamehoster.Entity.State.Get(Gamehoster.Context.entityId, name) / .Set(Gamehoster.Context.entityId, name, value) | Read / write this entity's state. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players' state. |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read other entities' state. |
gamehoster-bots/<name>/gamehoster-bot.json
One directory per bot personality under gamehoster-bots/,
holding this settings file and the two bodies below. Today it carries a single key,
gamehoster-bot-likelihood: the weight with which this personality is chosen when the engine
adds a bot. A likelihood of 0 never appears; if every personality is 0 the game
runs with no bots. See Usage → Bots.
| Key | Type | Values / notes |
|---|---|---|
gamehoster-bot-likelihood | number | The weight for this personality in the weighted-random pick when a bot is added. 0 means it never appears. |
gamehoster-bots/<name>/gamehoster-bot-join.js
Optional, server-only. Runs once, the moment this bot is added to an instance, after the
game's own player join has already set the bot up as a player.
It prepares only what is unique to the bot — its private memory — through
Gamehoster.Bot.Persistent.Set(…). Like the update it is firewalled to no game-state
writes. Gamehoster.Context.playerId (also Gamehoster.Context.botId) is
the bot and Gamehoster.Context.instanceId its instance. See
Usage → Bots.
| Function | What it does |
|---|---|
Gamehoster.Bot.id | This bot's id — also its player id. |
Gamehoster.Bot.Persistent.Get() / .Set(value) | Read / replace this bot's server-only private memory, kept between updates and never sent to any client. |
gamehoster-bots/<name>/gamehoster-bot-update.js
The bot's brain, run on the server on its own bot timer — set by
gamehoster-game-botRate (default 10/s),
independent of the game tick. Each run it reads the world with the same surface a
player update has (players, entities, instance state, its own
Bot.Persistent) and acts only by sending the game's own
commands through Gamehoster.Bot.Send: they enter the
normal per-tick queue and run through the unmodified handler next tick. It writes no player, entity or
instance state directly. Gamehoster.Context.playerId (also
Gamehoster.Context.botId) is the bot and Gamehoster.Context.instanceId its
instance. See Usage → Bots.
| Function | What it does |
|---|---|
Gamehoster.Bot.id | This bot's id — also its player id, so it reads its own ship by that id. |
Gamehoster.Bot.Persistent.Get() / .Set(value) | Read / replace this bot's server-only private memory. |
Gamehoster.Bot.All() | Read-only Map(botId → { id, type, persistent }) of every bot in the instance, for coordinating between bots. |
Gamehoster.Bot.Send(type, params) | Queue one of the game's own commands as this bot; it runs through the handler next tick. The bot's only way to act. |
Gamehoster.Player.List() · Gamehoster.Player.State.Get(id, name) | Read players (a bot is one of them). |
Gamehoster.Entity.List(type) · Gamehoster.Entity.State.Get(id, name) | Read entities. |
Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, name) | Read the instance's own state. |
What ships to the client
connect), this game's config, and — where the game ships them — the baked client-only bodies: the
gamehoster-player-predict.js that predicts your own
object, and the per-object -enter.js and
-smooth.js bodies that seed and shape rendering. Every
simulation body runs on the server, the only simulator. Over the socket a player receives the
public fields of the objects
relevant to them, plus their own object's
private (owner-only) fields; another player's private fields never reach them. State that no client
should ever see is not a schema field at all — it lives in a server-only
persistent bag that is never serialised, so a secret is provably
absent from the wire, not merely hidden. A server-only computation reaches a client only once it is
written into a streamed field or an entity spawn.