Simulation: Building Timers (BETA)

Add timed actions to your game — building upgrades, crafting, research, or any action that takes real time to complete. Includes build queues (one upgrade at a time), passive resource generation, and collecting completed results.

This is a self-contained recipe. Everything you need — config, client code, and patterns — is on this page.


Where Config Goes

All JSON config on this page goes in your project's server config — either a single config.json file or split across files in a config/ directory. Each file must have a top-level "simulation" key. The CLI uploads config automatically when you rundot deploy.

my-game/
├── config.json                      ← option A: all-in-one (add "simulation" key)
├── config/                          ← option B: split files for complex games
│   └── buildings.config.json
├── game.config.json                 ← game ID + build settings only (separate)
└── package.json

game.config.json is for CLI metadata only. Simulation config goes in config.json or the config/ directory.


What You'll Build

  • Buildings that take real time to upgrade (e.g., 5 minutes, scaling with level)

  • A build queue that limits players to one upgrade at a time

  • Passive resource generators (produce bricks/coins over time)

  • Collecting accumulated resources from generators

  • Countdown timers in the client UI


Server Configuration

Building Entities

Each building is an entity. Its quantity represents its level.

Key fields:

  • stackable: true — quantity represents the building's level

  • requiresManualCollection: true — timed recipe outputs are held until the player explicitly collects

  • metadata.baseCost / costMultiplier — used in formula-based input costs

  • metadata.baseDuration — used in formula-based recipe duration

Building Upgrade Recipe (Entity-Scoped)

A single recipe handles all building upgrades. {{entity}} resolves to the specific building at execution time.

How this works:

  • concurrency: "global:building_upgrade" — only one building upgrade can run at a time across all buildings. Starting a second upgrade fails.

  • guards.alreadyConstructed — the building must exist (level ≥ 1) before it can be upgraded

  • duration — scales with level: baseDuration + level * baseDuration. A building with baseDuration: 180000 (3 min) at level 3 takes 3 + 3×3 = 12 minutes.

  • inputs.currency_bricks — cost scales exponentially: floor(100 * 1.6 ^ level)

  • outputs.{{entity}}: 1 — increments the building's level by 1

Resource Generation (Auto-Restart Timer)

A generator building produces resources on a repeating timer:

Key pattern:

  1. brick_generation auto-restarts every 5 minutes, producing bricks into pending_bricks based on the quarry's generation rate

  2. maxRestartCondition with maxValueFormula stops generation when storage is full

  3. collect_bricks is an instant recipe that transfers pending bricks to the player's currency (capped by storage)

Auto-Recalculating Derived Stats (Triggers)

When a building is upgraded, recalculate dependent stats automatically:

These recipes fire automatically whenever the watched entity changes. The output formula computes the delta needed to reach the new correct value.


Client-Side Implementation

Step 1: Read Building State

Step 2: Check Upgrade Cost and Availability

Step 3: Start an Upgrade

Step 4: Track Active Upgrades and Show Countdown

Step 5: Collect Completed Upgrades

When the timer finishes, the upgrade must be collected before the level increases:

You can also check for completed-but-uncollected runs on app launch:

Step 6: Collect Generated Resources


Cooldown Timers

For actions that can be performed once per time period (not building upgrades — more like "claim daily reward" or "free booster every 4 hours"):

The reward is granted immediately via beginEffects. The recipe's 24-hour duration acts as a cooldown — attempting to execute again before it expires returns an error.


Common Variations

Multiple Build Queues

Allow N simultaneous upgrades (e.g., 2 builder slots):

Instant Finish with Premium Currency

Let players skip the timer:

Level-Gated Buildings

Use guards to require a minimum town hall level before upgrading:


Best Practices

  • Use concurrency: "global:<key>" to enforce build queue limits across all building types.

  • Use entity-scoped recipes ({{entity}}) with formulas for scaling costs and durations — one recipe handles all buildings.

  • Store cost/duration parameters in entity metadata so formulas can read them dynamically.

  • Use trigger recipes for derived stats so they auto-recalculate when dependencies change.

  • Use requiresManualCollection: true on building entities so players see an upgrade-complete animation before the level updates.

  • Separate pending and collected resources (e.g., pending_bricks vs currency_bricks) so players must interact with the generator to claim.

  • Subscribe to activeRuns for countdown timers — don't calculate durations client-side.

  • Check for uncollected completed runs on app launch to handle upgrades that finished while offline.

Last updated