Entity
The non-player things. Pong has one entity
type: the ball. There is only ever one on the court, spawned by the
serve. The ball moves every tick, so
it carries a schema and one update, both run authoritatively on the server.
<gamehoster-config-contentRoot>/
game.gamehoster.org/
gamehoster-games/
pong/
gamehoster-entities/
ball/
gamehoster-entity-schema.json
gamehoster-entity-update.js
gamehoster-entity-smooth.js
Contents
One folder per entity type; the ball holds a parameter schema and one update.
| Name | Type | Description |
|---|---|---|
ball/ | directory | The single ball: a schema, an update, and a smooth body. |
ball/
The single ball in play. Its fields are the position x, y, the velocity
vx, vy, and a resets counter the server bumps on every
re-serve, all public, so the ball streams to both players,
who draw it from the interpolated server frame a fixed buffer behind. The bounded position and
velocity fields carry min/max/decimals range hints; the
client-only gamehoster-entity-smooth.js
body uses resets to snap the ball across a re-serve instead of sliding it back across
the court.
gamehoster-entity-schema.json
[
{
"gamehoster-entity-schema-name": "x",
"gamehoster-entity-schema-type": "number",
"gamehoster-entity-schema-default": 400,
"gamehoster-entity-schema-visibility": "public",
"gamehoster-entity-schema-min": 0,
"gamehoster-entity-schema-max": 800,
"gamehoster-entity-schema-decimals": 1
},
{
"gamehoster-entity-schema-name": "y",
"gamehoster-entity-schema-type": "number",
"gamehoster-entity-schema-default": 240,
"gamehoster-entity-schema-visibility": "public",
"gamehoster-entity-schema-min": 0,
"gamehoster-entity-schema-max": 480,
"gamehoster-entity-schema-decimals": 1
},
{
"gamehoster-entity-schema-name": "vx",
"gamehoster-entity-schema-type": "number",
"gamehoster-entity-schema-default": 0,
"gamehoster-entity-schema-visibility": "public",
"gamehoster-entity-schema-min": -6,
"gamehoster-entity-schema-max": 6,
"gamehoster-entity-schema-decimals": 2
},
{
"gamehoster-entity-schema-name": "vy",
"gamehoster-entity-schema-type": "number",
"gamehoster-entity-schema-default": 0,
"gamehoster-entity-schema-visibility": "public",
"gamehoster-entity-schema-min": -6,
"gamehoster-entity-schema-max": 6,
"gamehoster-entity-schema-decimals": 2
},
{
"gamehoster-entity-schema-name": "resets",
"gamehoster-entity-schema-type": "int",
"gamehoster-entity-schema-default": 0,
"gamehoster-entity-schema-visibility": "public"
}
]
gamehoster-entity-update.js
// The ball's per-tick update, run authoritatively on the server: one integration step.
// x/y/vx/vy are public, so the ball's position streams to every viewer each send and the
// client interpolates it between packets. Bouncing and scoring are the instance update's job.
if (Gamehoster.Instance.State.Get(Gamehoster.Context.instanceId, "phase") !== "playing") return
const me = Gamehoster.Context.entityId
Gamehoster.Entity.State.Set(me, "x", Gamehoster.Entity.State.Get(me, "x") + Gamehoster.Entity.State.Get(me, "vx"))
Gamehoster.Entity.State.Set(me, "y", Gamehoster.Entity.State.Get(me, "y") + Gamehoster.Entity.State.Get(me, "vy"))
gamehoster-entity-smooth.js
Client-only. It sits beside the ball's schema and shapes how the ball eases each frame. Everything is
smoothed automatically; this body only overrides x/y across a re-serve, snapping
to the latest authoritative position (watched through the resets counter) instead of sliding
the ball back across the court.
// Client-only: how the ball's streamed fields are shown each render frame. By default the runtime
// interpolates every numeric field between the two samples straddling the render tick, which is
// exactly right while the ball flies across the court. But after a point the server RESETS the
// ball to the court centre, and interpolating position across that jump would slide the ball
// smoothly from where it went out back to the middle — a line straight across the map.
//
// The ball carries a public `resets` counter that the server bumps on every re-centre. While the
// interpolated (smoothed) counter still lags the authoritative one — i.e. the render tick has not
// yet reached the reset in the sample timeline — we SNAP position to the latest authoritative
// value instead of interpolating across the gap. Velocity and normal flight keep the default
// smoothing untouched.
if (Gamehoster.Smooth("resets") !== Gamehoster.Latest("resets")) {
Gamehoster.Set("x", Gamehoster.Latest("x"))
Gamehoster.Set("y", Gamehoster.Latest("y"))
}