Player
The files that describe a Pong player: the one field they supply when they join, the per-instance state the engine keeps for them (their side, paddle, score and ready flag) while they are connected, and the two scripts the server runs: one the moment they join, one when they leave.
gamehoster-player-join-schema.json
The shape of the data a client must send to join. Pong asks for one thing: the player's display
name, which the join script copies onto their state.
[
{
"gamehoster-player-join-schema-name": "name",
"gamehoster-player-join-schema-type": "string"
}
]
gamehoster-player-schema.json
The per-instance state carried for each player. name, side,
ready and score are sent
(server-authoritative, pushed to everyone). The paddle position y is
shared and smoothed: the owner predicts it from their
moveY commands and the client reconciles it against the
server's snapshots.
[
{
"gamehoster-player-schema-name": "name",
"gamehoster-player-schema-type": "string",
"gamehoster-player-schema-sync": "sent",
"gamehoster-player-schema-audience": "all"
},
{
"gamehoster-player-schema-name": "side",
"gamehoster-player-schema-type": "string",
"gamehoster-player-schema-sync": "sent",
"gamehoster-player-schema-audience": "all"
},
{
"gamehoster-player-schema-name": "ready",
"gamehoster-player-schema-type": "bool",
"gamehoster-player-schema-default": false,
"gamehoster-player-schema-sync": "sent",
"gamehoster-player-schema-audience": "all"
},
{
"gamehoster-player-schema-name": "score",
"gamehoster-player-schema-type": "int",
"gamehoster-player-schema-default": 0,
"gamehoster-player-schema-sync": "sent",
"gamehoster-player-schema-audience": "all"
},
{
"gamehoster-player-schema-name": "y",
"gamehoster-player-schema-type": "number",
"gamehoster-player-schema-default": 240,
"gamehoster-player-schema-sync": "shared",
"gamehoster-player-schema-audience": "all",
"gamehoster-player-schema-smooth": true
}
]
gamehoster-player-join.js
Runs on the server as a player connects. It first matchmakes: it joins a game still
waiting for an opponent, or creates a fresh one. Then it sets up their state: the free
side (left or right), their supplied name, a zero score, a cleared ready
flag, and a paddle centred on the court.
// Runs on the server the moment a player connects. It first routes them into an
// instance (a game still waiting for an opponent, or a fresh one), then sets up
// their per-instance state (a free side, their name, a centred paddle).
// route: join a waiting game that has room, else start a new one
const open = Gamehoster.Instance.List().find(id =>
Gamehoster.Instance.Info(id).players < Gamehoster.Instance.Info(id).capacity &&
Gamehoster.Instance.State.Get(id, "phase") === "waiting")
Gamehoster.Instance.Join(open !== undefined ? open : Gamehoster.Instance.Create())
// set up: whichever side is free. Now that we have joined, Gamehoster.Context.playerId
// names us and Context.instanceId our court.
const me = Gamehoster.Context.playerId
const here = Gamehoster.Context.instanceId
const taken = Gamehoster.Player.List()
.filter(id => id !== me)
.map(id => Gamehoster.Player.State.Get(id, "side"))
Gamehoster.Player.State.Set(me, "side", taken.includes("left") ? "right" : "left")
Gamehoster.Player.State.Set(me, "name", Gamehoster.Join.Get("name"))
Gamehoster.Player.State.Set(me, "score", 0)
Gamehoster.Player.State.Set(me, "ready", false)
Gamehoster.Player.State.Set(me, "y", Gamehoster.Instance.State.Get(here, "courtH") / 2)
gamehoster-player-leave.js
Runs when a player disconnects. The engine reclaims their paddle; if a match was in play, the
instance drops back to waiting so it can pair up with the next player who arrives.
// A player has left. The engine reclaims their paddle; if a match was in play,
// drop back to waiting for a new opponent.
const here = Gamehoster.Context.instanceId
if (Gamehoster.Instance.State.Get(here, "phase") === "playing")
Gamehoster.Instance.State.Set(here, "phase", "waiting")