Gessa Docs
Recipes

Recipe

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

  1. Phases: call ctx.match.setPhase with a phase key (for example lobby, 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.
  2. Scoreboard: on a scoring event, ctx.match.addScore for a subject key (the player id, or a team key for team games) and read standings with ctx.match.getScore. Mirror the number to the HUD with wireHudStateBinding.
  3. Objectives: ctx.match.patchObjective to update a named goal (captures, flags taken, waves cleared) that the UI and win check read.
  4. Win by points: when a getScore crosses the target, ctx.match.setPhase ended and record the winner. Win by elimination: findEntitiesByTag the living players or targets and declareWinCondition (or compareRemainingCount then setPhase) when the remaining count hits the threshold.
  5. Join / leave: handle event.player_joined, event.player_left, and event.match_ended in 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. declareWinCondition already carries writeState/emitEvent/requiresAuthority.
  • The four ai_safe handler 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_snapshot to confirm the scoring and lifecycle handlers are on the graph.
Was this helpful?Report an issueContact support

On this page