Game rules and automations
This page describes the two parts of the panel’s “Rules” screen: the game rule values that the game’s plugin reports (for example police thresholds) and the automations that do work automatically when an event happens. Read it if you want to tune the game’s behavior without shipping code or react automatically to an event.
What it does
- Game rule values: The game’s plugin reports which numbers are adjustable (name, unit, lower and upper bound, step, default). An operator changes these values in the panel and publishes them; the new values take effect in the game shortly (in about 6 seconds at the latest). Every publish becomes a new version and you can return to old values with one click.
- Automations: Rules of the form “when this event happens, if this condition is true, do this”. A rule attaches to character events (
level_up,xp_added…) and gives the character a stat, resource, flag, modifier or reward. Conditions are written as a CEL expression; in the panel a structured builder generates the CEL for you.
Game rule values are a panel surface: your game server does not write them with a REST endpoint; the plugin reads the values on the server. Automations are managed both from the panel and with REST (they are part of the characters module).
Where it lives in the panel
| What you want to do | Panel page |
|---|---|
| An overview of the Rules screen, topics and cards | Rules |
| Edit, publish and preview a game rule card | Rule card |
| See the publish history and the diff, roll back | Rule history |
| Create, edit, enable and disable an automation | Automation editor |
Note
Panel help pages are not translated yet; the panel links above point to the Turkish source, and the panel itself is shown in Turkish.
Game rule values
Model
- Catalog: Reported by the game’s plugin. There are topics, cards and each card’s parameters; every parameter comes with a unit (for example seconds, meters, percent) and bounds. If there is no catalog or the plugin does not report one, only Automations appear in the panel.
- Version: The effective values are the latest version. If there has been no publish, the plugin’s code defaults are used (“Kod varsayılanı”, the code default). Publishing and rolling back write a new version; history is not deleted.
- Apply time: If a parameter is
canlı(live), it is applied shortly after the publish; if it isyeniden başlatmada(on restart), the old value stays until the game server restarts, and the panel shows both together as “Yayında / Bekliyor” (Published / Pending). - Validation: Values are validated on the server against the catalog bounds, step and cross-field constraints; an invalid field is reported per field. The plugin re-validates with the same bounds; an invalid value does not break the game, and the plugin falls back to its code defaults.
- Permission: Reading is open to the four panel roles; publishing and rolling back only to the
super_adminandgame_managerroles. Publishing and rolling back require a justification of 8–500 characters and are written to the audit log. - Concurrency: A publish carries the version you saw (
expectedVersion); if someone else published while you were looking,409is returned and you need to refresh the tab. - Preview: The panel runs a scenario (for example a continuous speed violation) with the plugin’s real logic using today’s and the proposed values and shows the event timeline; it saves nothing.
- Client warning: If a parameter is
clientVisible, the game client reads it from the plugin’s query. If the client does not read that value yet, the panel warns you while publishing.
Effect on the game
Your game code does not read game rule values with REST. The plugin takes the values from a single snapshot at every step on the server; the values the client needs come through the plugin’s own query. This behavior is plugin-specific; today only the package-ride plugin reports a catalog. Third-party plugins reporting a catalog and being loaded is not supported yet; see Plugins: overview.
Automations
Model
- Rule:
key(a unique key),trigger_type(the trigger event),condition(a CEL expression),actions(a list of actions) andpriority. A rule is versioned; every publish is a new version. - Trigger:
flag_set,attribute_set,resource_changed,modifier_added,modifiers_removed,xp_added,level_up,ability_unlocked,event_emitted,character_rolled_back. An unknown type is rejected. The panel builder does not offer thecharacter_rolled_backoption. - Actions:
add_stat(key,delta),add_resource(key,delta),set_flag(key,value),add_modifier,remove_modifierandgrant_reward(what: a fixed amount of money{ "type": "currency", "key", "amount" }or a reward table{ "type": "table", "key" }). - CEL context: The condition has the variables
character,event,nowandseed; the functionshas_ability,base_statandclampcan be used. The context carries time and seed, a rule cannot read its own clock; there is nonow()orrandom(). Rules are compiled at registration, not at run time. - Budget (fixed): In one command chain at most 50 rules are evaluated and 200 actions are produced, the chain depth cannot exceed 5, and a condition expression cannot exceed 500 nodes. Exceeding it is fail-closed: the command is rejected with
429and which limit was exceeded is stated. Two rules that trigger each other are cut off by this limit.
Panel builder
The builder produces CEL from a “When → Conditions → Do” structure: condition rows (field, operator, value) are combined with all (&&) or any (||); at most 20 conditions and 10 actions can be added. Text values are escaped; line breaks and control characters are rejected. When the builder is used, the server recompiles the rule (the CEL text the client sends is not read for a builder rule). A new rule starts disabled; the “enable right away” option is unchecked. Before publishing you can do a dry run with a sample event; it is not required.
Calls your game makes
Rule endpoints are called with an API key:
Authorization: Bearer crx_live_<prefix>_<secret>
| Method and path | What it does |
|---|---|
POST /v1/rules | Saves a rule (201) |
GET /v1/rules | Lists rules (cursor, limit) |
GET /v1/rules/{key} | Reads the rule’s current version |
POST /v1/rules/{key}/enable, POST /v1/rules/{key}/disable | Enables / disables the rule (204) |
DELETE /v1/rules/{key} | Deletes the rule (204) |
POST /v1/rules/dry-run | Tries a rule against a character without writing |
Warning
Known limitation: The endpoints that write rules do not look at the key type; a valid client key can also save or delete a rule. If you embed a client key in the game, manage rules from your own server and do not call these endpoints from the client.
The TypeScript SDK exposes these endpoints in a typed way: createRule, listRules, getRule, deleteRule, enableRule, disableRule, dryRunRule (see TypeScript SDK). These endpoints take raw CEL text; the panel builder’s structured model exists only on the panel surface.
Saving a rule
POST /v1/rules
Authorization: Bearer crx_live_<prefix>_<secret>
Content-Type: application/json
{
"key": "lvl10_reward",
"trigger_type": "level_up",
"condition": "event.to == 10",
"actions": [
{ "type": "grant_reward", "what": { "type": "currency", "key": "gold", "amount": 1000 } },
{ "type": "set_flag", "key": "reached_level_10", "value": true }
],
"priority": 0
}
{ "key": "lvl10_reward", "current_version": 1, "enabled": true, "created_at": "2026-11-03T09:00:00Z" }
A rule saved with REST starts enabled right away (enabled: true); unlike the panel builder, to start it disabled call POST /v1/rules/{key}/disable after saving. event.to is the new level of the level-up event; event fields vary by trigger. The registration is rejected if the condition cannot be compiled, the trigger is unknown or a budget limit is exceeded.
Dry run
POST /v1/rules/dry-run
Authorization: Bearer crx_live_<prefix>_<secret>
Content-Type: application/json
{ "condition": "character.progression.level >= 10", "actions": [ { "type": "set_flag", "key": "veteran", "value": true } ], "character_id": "0192f0f2-…" }
{ "condition_result": true, "node_cost": 7, "would_produce_events": [ { "...": "…" } ], "would_grant": [], "warnings": [] }
A dry run writes no events and grants no rewards.
Versioning and replay safety
- Publishing and rolling back game rules: done by the panel with
Idempotency-KeyandexpectedVersion; the same key returns the same version again and writes a single audit record. - Automation publishing: every publish is a new version; old versions are not deleted, and “roll back” rewrites the old version as a new version.
- The REST rule endpoints have no
Idempotency-KeyorIf-Match.
Events
The actions a rule runs produce normal character events (flag_set, xp_added …); these can be delivered through webhooks. A reward produced by a rule also produces the reward_granted event.
Limits
- In a command chain: 50 rules, 200 actions, depth 5, 500 nodes per condition.
- In the panel builder: at most 20 conditions and 10 actions.
- A game rule justification is 8–500 characters.
- Game rule integer values are limited to ±(2^53−1) in the panel.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
429 (command) | The rule chain exceeded the budget | Simplify the rules that trigger each other |
400/422 on registration | The CEL could not be compiled, unknown trigger or field | Try the condition with a dry run first |
409 in the panel (publish) | Someone else published while you were looking | Refresh the tab and publish again |
Field error in the panel (422) | The value is outside the bound, step or constraint | Respect the bound the field shows |
| I published the value but the game behaves the old way | The parameter is applied yeniden başlatmada (on restart), or the 6 seconds have not passed | Look at the panel’s “Bekliyor” (Pending) indicator |
Working example
The two requests above (the lvl10_reward registration and the dry run) together form a complete flow: first try the condition with a dry run, then save it with POST /v1/rules, then make a character level up with an add_xp command and verify that the rule ran, following the flow on the Characters page.