Simulation: Config Reference (BETA)
Define your game's server-authoritative state, actions, and randomized rewards entirely through JSON configuration. This reference covers the full config schema — entities, recipes, loot tables, and lifecycle hooks.
This page is the config-authoring companion to the Simulation API, which documents the client SDK methods. If you're looking for a specific pattern (energy systems, gacha, timers), see the recipe docs first — they include complete configs inline.
Project Setup
Simulation config is uploaded to the server when you rundot deploy. There are two ways to organize it:
Option A: Single config.json File (Simple Games)
config.json File (Simple Games)For games with a small amount of simulation config, add a simulation key to your project's config.json file:
my-game/
├── config.json ← simulation config goes here
├── src/
├── dist/
├── game.config.json ← game ID + build settings (separate file)
└── package.json{
"simulation": {
"entities": { ... },
"recipes": { ... },
"lootTables": { ... }
}
}This is the same config.json used for leaderboard and rooms config. You can combine them in one file.
Option B: config/ Directory (Complex Games)
config/ Directory (Complex Games)For games with many entities and recipes, split config across multiple files in a config/ directory:
Each file must have a top-level "simulation" key:
The CLI deep-merges all *.config.json files into a single simulation config at deploy time. One file per domain is a good pattern.
Important
config.jsonandconfig/are for server config (simulation, leaderboard, rooms, etc.)game.config.jsonis a separate file for local CLI metadata only (gameId,relativePathToDistFolder,usesPreloader) — simulation config does not go there
Config Structure
Each config file's simulation object can contain any combination of these top-level keys:
Files are merged by key — if two files both define entities, their entities are combined. If two files define the same entity ID, the last file wins.
Entities
Entities are the atomic units of game state. Each entity tracks a numeric quantity per player (e.g., gold: 150, energy_current: 12, building_barracks: 3).
Entity Fields
tags
string[]
No
Categorize entities for filtering, subscriptions, and loot table lookups. Use any string.
stackable
boolean
No
Whether quantity can exceed 1. Default true.
clientViewable
boolean
No
Whether the client can read this entity's quantity via getStateAsync and subscriptions. Default false.
neverConsumable
boolean
No
If true, recipe inputs can check this entity but never deduct from it. Useful for cumulative counters and permanent unlocks.
requiresManualCollection
boolean
No
If true, timed recipe outputs for this entity require explicit collection via collectRecipeAsync.
metadata
object
No
Arbitrary key-value data readable by both config formulas and client code. Store display names, icons, costs, thresholds — anything your game needs.
Tagging Patterns
Tags enable powerful filtering throughout the system:
Client subscriptions filter by tags:
subscribeAsync({ tags: ['currency'] })Loot tables select entities by tag:
"includeTags": ["unit", "rarity:epic"]Recipes can be tagged for client discovery
Recipes
Recipes are server-authoritative actions. They consume inputs, produce outputs, and optionally run effects. Every game action — spending currency, opening packs, upgrading items, regenerating energy — is a recipe.
Instant Recipe
Executes immediately. Duration is 0.
Timed Recipe
Has a non-zero duration (milliseconds). Creates an active run that must complete before outputs are granted.
Auto-Restart Recipe
A timed recipe that automatically re-executes after completing. Ideal for regeneration timers and passive production.
Entity-Scoped Recipe
A single recipe definition that operates on different entities at runtime. Uses {{entity}} as a placeholder resolved when the recipe is executed.
At execution time, {{entity}} resolves to unit_orc_warrior, so the recipe reads unit_orc_warrior_level and increments it.
Recipe Fields
duration
number | formula
Milliseconds. 0 for instant. Supports formulas for dynamic durations.
scope
"player"
Execution scope. Currently "player" for all single-player recipes.
clientViewable
boolean
Whether the client can see this recipe in config and requirements checks.
endEffects
array
Side effects that run when a timed recipe completes.
autoRestart
boolean
Re-execute automatically after completion.
onConflict
"fail"
What to do when concurrency is violated.
maxRestartCondition
object
Stop auto-restart when entity reaches a threshold.
maxOfflineExecutionPeriod
number
Max milliseconds of offline catch-up for auto-restart recipes.
tags
string[]
Categorize recipes for client-side filtering.
metadata
object
Arbitrary data. Use hidden: true to suppress from UI.
Inputs
Recipe inputs define what is consumed when the recipe runs.
Fixed Amount
Formula-Based
Wallet (Premium Currency)
For dynamic wallet amounts passed at runtime:
Max Guard on Input
Limits how many times an entity can be incremented (useful for one-time or limited purchases):
Outputs
Fixed Amount
Formula-Based
Loot Rolls
For randomized rewards, outputs can contain rolls that reference loot tables:
table
string
Loot table ID to roll from.
count
number | { min, max }
How many times to roll. Fixed or random range.
guarantees
array
Minimum counts by tag (e.g., guarantee at least 1 rare).
pity
object
Pity system: increment a counter each roll, guarantee a tag when threshold is hit.
Guards
Guards are preconditions checked before a recipe can execute. If any guard fails, the recipe is rejected.
Guard formulas have access to entities.* for current inventory values.
Effects
Effects are side actions that run alongside a recipe. beginEffects fire when the recipe starts; endEffects fire when a timed recipe completes.
set — Set Entity to Value
set — Set Entity to Valueadd — Add to Entity
add — Add to EntityValue can be a formula:
trigger_recipe — Execute Another Recipe
trigger_recipe — Execute Another Recipetrigger_recipes_parallel — Execute Multiple Recipes
trigger_recipes_parallel — Execute Multiple Recipesdisable_recipe — Prevent Future Execution
disable_recipe — Prevent Future Executionassign_to_slot — Assign Item to Slot Container
assign_to_slot — Assign Item to Slot Containersend_notification — Push Notification
send_notification — Push NotificationSee Server-Authoritative API — Push Notifications for full details.
Concurrency
Controls how many instances of a recipe can run simultaneously.
"single"
Only one instance at a time. New executions fail if one is active.
"global:<key>"
Only one recipe with this key can run at a time across all recipes sharing the key.
"cooldown:<key>"
After completing, the recipe cannot be re-executed until the cooldown expires.
Always pair with "onConflict": "fail" so the client gets a clear rejection.
This means only one building can be upgrading at a time — starting a second upgrade fails.
Auto-Restart & Offline Catch-Up
For recipes that repeat on a timer (energy regen, resource production):
autoRestart
Re-execute immediately after each completion.
maxRestartCondition.entity
Entity to check before restarting.
maxRestartCondition.maxValue
Stop restarting when entity reaches this value. Also accepts maxValueFormula.
maxOfflineExecutionPeriod
Maximum milliseconds of offline time to catch up. 604800000 = 7 days.
When a player returns after being offline, the server calculates how many cycles would have completed and grants the accumulated outputs (capped by maxRestartCondition).
Triggers
Triggers automatically execute a recipe when watched entities change:
The recipe auto-fires whenever building_brick_storage or building_town_hall changes. Use this for derived stats that should recalculate when their dependencies change.
Formulas
Many recipe fields accept formula expressions instead of fixed values.
Syntax
Formulas are JavaScript-like expressions evaluated server-side:
Available Functions
floor, ceil, round, min, max, abs
Variables
Formulas can reference variables defined in a variables block:
Entity metadata field
{ "entity": "building_barracks", "field": "metadata.baseCost" }
Value from entity's metadata
Current inventory
{ "inventory": "energy_current" }
Player's current quantity
Inline entities
entities.energy_current
Same as inventory, used directly in formula strings
Template Variables
Available in entity-scoped recipes:
{{entity}}
The entity ID passed at execution time
{{profileId}} / {{executorId}}
Player who triggered the recipe
{{roomId}}
Current room ID (room-scoped recipes)
{{inputs.<key>}}
Recipe input values
{{results.<key>}}
Results from earlier effects
{{entities.<id>}}
Current inventory quantity
{{now}}
Current timestamp (ms)
{{metadata.<field>}}
Entity metadata (of the scoped entity)
Loot Tables
Loot tables define randomized reward pools.
Weighted Table
Each entry has an explicit weight. Higher weight = more likely.
Uniform (Tag-Filtered) Table
Dynamically selects from all entities matching tag filters. No explicit entries needed.
Guaranteed Table
Fixed drops — always grants the specified items.
Table Chaining
Weighted entries can reference other tables via tableRef instead of defining outcomes inline:
Loot Table Options
type
"weighted" | "uniform" | "guaranteed"
How entries are selected.
entries
array
Items, weights, and outcomes (weighted/guaranteed) or omitted (uniform).
filter
object
Tag-based entity selection (uniform tables).
filter.includeTags
string[]
Entity must have ALL of these tags.
filter.entityGuards
array
Additional per-entity checks.
newItemBias
object
Increase probability of items the player hasn't collected yet.
newItemBias.biasMultiplier
number
Multiplier applied to unowned items (e.g., 2.0 = 2x more likely).
wildcardConversion
object
Chance to convert drops into wildcard entities by rarity tag.
Wildcard Conversion
A percentage chance to replace a rolled item with a wildcard entity:
Lifecycle
The lifecycle block defines hooks that run at specific points in a player's lifecycle.
onStart
onStartRuns once when a new player first opens the game. Use it to set up initial state.
Key pattern: the onStart recipe disables itself (disable_recipe) to prevent re-execution, sets initial entity values, assigns starting items to slots, and triggers recurring timers.
Best Practices
Split config across multiple files by domain (e.g.,
energy-system.config.json,boosters.config.json). The platform merges them.Use
tagsliberally — they power subscriptions, loot table filters, and client-side discovery.Set
clientViewable: trueonly on entities and recipes the client needs. Keep internal state hidden.Use
metadatafor display data and formula variables. It's free-form and available everywhere.Use
formulaexpressions for scaling costs and dynamic outputs rather than creating separate recipes per level.Use entity-scoped recipes (
{{entity}}) to avoid duplicating recipes for every item/building/card.Always
disable_recipeon initialization recipes to prevent accidental re-execution.Set
maxOfflineExecutionPeriodon auto-restart recipes to prevent unbounded offline accumulation.Use
maxRestartConditionto cap regeneration (e.g., stop energy regen at max energy).
Last updated