Gessa Docs
Recipes

Recipe

Timers countdowns cooldowns and enemy waves

A round clock ticks down, enemies arrive in waves, or an ability recharges after a cooldown. TimerComponent is a real atomic (pack timer.v1); its runtime fires RuntimeTimerFired on a server-owned interval. There is no RespawnTimerComponent or SpawnPoolComponent; those are this same pattern.
engine v1.0.234since script-semantic-patch-ops.v1, action-catalog.v1.0.232, timer.v1, spawn.v1, match.v1, persistence.v1Copy for LLM

Use this for

a round countdown or match clock; ability cooldowns and regen tickers; spawning enemies in timed waves; anything that repeats or fires after a delay on a server-owned schedule

Not for

reacting to player input (see input-mapping-actions-bindings); persistent save data across sessions (see saves-and-persistence); the win or lose transition itself (see match-flow-and-scoreboards)

Pairs with: Match flow phases scoreboards and win conditions, Spawn points checkpoints and respawn flow, UI HUD panels widgets and world nameplates, Projectiles bullets and ranged weapons

The recipe

Author with project_apply_script_semantic_patch using the ai_safe op pack.

  1. Attach the clock: project_add_component a TimerComponent with { "timerKey":"round", "intervalSeconds": 1, "repeating": true, "maxFirings": 0 } (0 means unlimited; intervalSeconds minimum is 0.01).
  2. Subscribe: addTimerHandler { "handlerKey":"onRoundTick", "timerKey":"round" }. The runtime delivers the timerKey and the firing iteration to the handler.
  3. Countdown: seed the remaining seconds with writeTypedStateValue { "field":"timeLeft", "valueType":"number", "value": 60 }, then each fire incrementNumericState { "field":"timeLeft", "by": -1 } and wireHudStateBinding it to the HUD. When it reaches 0, declareWinCondition or transition the match phase (see match-flow-and-scoreboards).
  4. Enemy waves: use a slower timer (for example intervalSeconds: 20). Each fire, spawnEntityFromTemplate { "prefabKey": <enemy> } a batch at spawn points, and incrementNumericState a wave counter to scale the next batch.
  5. Cooldown: a one-shot delay is a TimerComponent with repeating: false and maxFirings: 1, or a last-used timestamp compared against the current tick; set a ready boolean with setBooleanState when the cooldown elapses.

Pitfalls

  • intervalSeconds has a floor of 0.01; do not expect sub-10ms timers.
  • The timer is server-owned and authoritative; every client sees the same fires. Do not run a parallel client clock.
  • Keep the countdown value and the wave counter in durable state, not a local variable, so they survive a reload and read consistently on the HUD.
  • A repeating timer with maxFirings: 0 never stops on its own; disable it (patch enabled: false) when the round ends or waves are cleared, or it keeps spawning.
  • Give each timer a distinct timerKey; one entity can carry several timers and addTimerHandler routes by that key.

Verify

  • simulation_run (qa.run.start) to watch the countdown decrement to zero and a wave spawn on each interval.
  • project_get_graph_snapshot to confirm the TimerComponent and the timer handler are on the entity.
Was this helpful?Report an issueContact support

On this page