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"
  }
]
pong/gamehoster-player/gamehoster-player-join-schema.json

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
  }
]
pong/gamehoster-player/gamehoster-player-schema.json

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)
pong/gamehoster-player/gamehoster-player-join.js

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")
pong/gamehoster-player/gamehoster-player-leave.js