Recipes
Match flow phases scoreboards and win conditions
A match moves through lobby, playing, and game-over, tracks scores, and ends when someone wins. This is the
match.v1 MatchHost plus the lifecycle.v1 events. Scores and objectives are warehouse-backed (objective.v1), so they persist and replicate consistently.engine v1.0.234since script-semantic-patch-ops.v1, action-catalog.v1.0.232, match.v1, lifecycle.v1, objective.v1, persistence.v1Copy for LLM
Use this for
lobby, playing, and game-over phases; a scoreboard and per-player or per-team scores; first-to-N and time-limit win conditions; objectives and round structure; reacting to players joining and leaving mid-match
Not for
the collectible pickup that raises a score (see collectibles-and-scoring); matchmaking rooms and replication (see multiplayer-replication-and-rooms); the countdown clock itself (see timers-countdowns-and-waves)
Pairs with: Collectibles pickups scoring and win condition, Teams team based matches and shared team score, Timers countdowns cooldowns and enemy waves, Multiplayer replication rooms and networked motion
The recipe
Author with project_apply_script_semantic_patch using the ai_safe op pack; the MatchHost calls live inside whole-body GessaScript (replaceScriptFromGessaScript).
- Phases: call
ctx.match.setPhasewith a phase key (for examplelobby,playing,ended). React to phase changes in handlers; drive the transition from a ready check, a timer (see timers-countdowns-and-waves), or a win. - Scoreboard: on a scoring event,
ctx.match.addScorefor a subject key (the player id, or a team key for team games) and read standings withctx.match.getScore. Mirror the number to the HUD withwireHudStateBinding. - Objectives:
ctx.match.patchObjectiveto update a named goal (captures, flags taken, waves cleared) that the UI and win check read. - Win by points: when a
getScorecrosses the target,ctx.match.setPhaseendedand record the winner. Win by elimination:findEntitiesByTagthe living players or targets anddeclareWinCondition(orcompareRemainingCountthensetPhase) when the remaining count hits the threshold. - Join / leave: handle
event.player_joined,event.player_left, andevent.match_endedin a GessaScript body to seat or drop players, pause a round that lost quorum, and finalize the scoreboard at the end.
Pitfalls
- Phase transitions and score writes are authority-side; do not also flip the phase or add score on a client.
declareWinConditionalready carrieswriteState/emitEvent/requiresAuthority. - The four
ai_safehandler shortcuts cover action, trigger, timer, tick, and custom events. Lifecycle handlers (player_joined,player_left,match_ended, phase enter/exit) are authored through whole-body GessaScript, not a dedicated shortcut op. - Scores live in the warehouse via the MatchHost; do not keep a parallel local tally, it will drift from what other clients see.
- Use a stable subject key for score. A per-session id resets each match; use the durable player id for a persistent leaderboard.
Verify
simulation_run(qa.run.start) to prove the match advances through its phases, scores accrue, and the win phase fires at the threshold.project_get_graph_snapshotto confirm the scoring and lifecycle handlers are on the graph.