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.

KeyTypeValues / notes
gamehoster-game-titlestringDisplay name.
gamehoster-game-browsestringnone (placed straight in) or instances (pick a live instance, or create one).
gamehoster-game-createstringnone / public / password / private: whether players may create their own instance, and how it is listed.
gamehoster-game-tickRatenumberThe simulation rate: server logic steps per second (dt = 1/tickRate).
gamehoster-game-sendRatenumberThe 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-capacitynumberThe most players one instance holds.
gamehoster-game-botsMaxnumberOptional bot padding: the most bots an instance holds. Moved here from the instance schema — it is game config, not streamed state.
gamehoster-game-botsZeroAtnumberOptional: 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-originsstring / arrayOptional. 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.

FunctionWhat 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 keyTypeValues / notes
gamehoster-player-join-schema-namestringThe field's name.
gamehoster-player-join-schema-typestringRequired: a type (number, int, bool, string).
gamehoster-player-join-schema-defaultoptionalA 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.

FunctionWhat it does
Gamehoster.Context.playerIdThe 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 keyTypeValues / notes
typestringRequired: number, int, bool, string. See Types.
visibilitystringpublic (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 / maxnumberOptional 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.
decimalsintOptional, on a number: a non-negative count of significant decimal places to keep, so the value quantises to that precision on the wire.
defaultValue 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.

FunctionWhat it does
Gamehoster.Context.playerIdThe 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.

FunctionWhat it does
Gamehoster.Context.playerIdThe 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.

FunctionWhat it does
Gamehoster.Context.playerIdThis 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.

FunctionWhat it does
Gamehoster.Context.playerIdYou, 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.

FunctionWhat it does
Gamehoster.Context.id · .kind · .type · .isSelfThe 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.

FunctionWhat 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.

FunctionWhat it does
Gamehoster.Context.id · .kind · .type · .isSelfThe 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.

FileRuns on
gamehoster-frontend-input-mouse-move.jsThe pointer moving; under capture it carries the lock delta in Mouse.dx/dy.
gamehoster-frontend-input-mouse-enter.js · -mouse-leave.jsThe pointer entering or leaving the root; Escape releasing a capture fires leave.
gamehoster-frontend-input-mouse-button-<left|middle|right>-<press|release>.jsA mouse button going down or coming up.
gamehoster-frontend-input-keyboard-<down|up|left|right|space|enter>-<press|release>.jsAn arrow, space or enter going down or coming up.
gamehoster-frontend-input-keyboard-<letter|number>-<press|release>.jsAny letter or any digit; the character is in Gamehoster.Event.key.
gamehoster-frontend-input-keyboard-any-<press|release>.jsAny 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 }
}
The front-end input settings: capture the pointer, and throttle aim to 20 a second.
KeyTypeValues / notes
mouseCaptureboolTurn on pointer lock, so mouse-move gives the delta and the cursor is hidden.
rateobjectA 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.

FunctionWhat it does
Gamehoster.Context.playerIdThis 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.

FunctionWhat it does
Gamehoster.Context.playerIdThis 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 keyTypeValues / notes
typestringRequired: number, int, bool, string.
visibilitystringpublic / private; see the player schema.
min / max / decimalsnumberOptional numeric range hints; see the player schema.
defaultValue 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.

FunctionWhat 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.

FunctionWhat 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.

FunctionWhat 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.

FunctionWhat 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 keyTypeValues / notes
typestringRequired: 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"] }
pong's moveY manifest: it moves only the sender's paddle

writes 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.

KeyTypeValues / notes
writesarrayDotted 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.

FunctionWhat it does
Gamehoster.Command.Get(param)A value from the command schema.
Gamehoster.Context.playerIdThe 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 keyTypeValues / notes
typestringRequired: number, int, bool, string.
visibilitystringpublic / private; see the player schema.
min / max / decimalsnumberOptional numeric range hints; see the player schema.
defaultValue 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.

FunctionWhat it does
Gamehoster.Context.entityIdThis 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.

FunctionWhat it does
Gamehoster.Context.entityIdThis 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.

FunctionWhat it does
Gamehoster.Context.entityIdThis 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.

KeyTypeValues / notes
gamehoster-bot-likelihoodnumberThe 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.

FunctionWhat it does
Gamehoster.Bot.idThis 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.

FunctionWhat it does
Gamehoster.Bot.idThis 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

Only the client-side rendering bodies ship to the client; no simulation body does. The generated front-end library is the generic runtime (the snapshot store and 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.