Skip to content
TAURION
03Game State Schema

Game State Schema

The complete shape of the world state returned by the GSP.

In plain language (start here if you are a player)#

Taurion has no central server that "owns" the game. Instead, every player's computer can run a small program called the Game State Processor (GSP) that reads the blockchain and works out the current state of the world: where every vehicle is, who owns which building, what loot is on the ground, how much each account has in its wallet, and so on. The GSP then hands that world to the game client (the 3D map you actually look at) as one big bundle of data in JSON format — JSON is just a plain-text way of writing structured data that programs and humans can both read.

This document lists everything in that bundle. You do not need to read code to follow it: each section describes one kind of thing in the game (an account, a vehicle/character, a building, a region of the map, an ongoing job like prospecting, and so on) and shows a real example. Where the game's internal short codes appear (for example raw a for the ore Trimideum, or rv st for the Raider vehicle), the human-readable display name is shown next to the code. A few recurring technical terms are explained the first time they appear.

For developers building a new client, the rest of the document is exact: field names, types, when each field is present or absent, and the source line in the GSP that produces it.


This document is the authoritative reference for the game-state JSON that a Taurion client receives from the Game State Processor (GSP). It describes every entity type, every field, its type, units, semantics, when it is present or absent, and a realistic example for each.

The single source of truth is the GSP serializer src/gamestatejson.cpp and its test file src/gamestatejson_tests.cpp, plus the underlying protocol-buffer definitions in proto/. Field names and shapes shown here are produced exactly by gamestatejson.cpp — they are not a paraphrase.

Codebase layout note. The serializer is src/gamestatejson.cpp, but the database accessors and protos it uses live at the repository top level (database/, proto/), not under src/. Citations below use the path as it appears on disk.


1. How the client obtains this JSON#

All state JSON is produced by class GameStateJson (src/gamestatejson.hpp:39) — the serializer, i.e. the code that turns the GSP's internal database/protocol-buffer state into the JSON shapes shown here. It is exposed to clients through the GSP's JSON-RPC server (src/pxrpcserver.cpp) and a thin REST wrapper (src/rest.cpp). Each RPC reads a consistent database snapshot at the current confirmed block.

RPC method Serializer entry point Returns Source
getaccounts Accounts() array of account objects src/pxrpcserver.cpp:504, src/gamestatejson.cpp:716
getbuildings Buildings() array of building objects src/pxrpcserver.cpp:515, src/gamestatejson.cpp:745
getcharacters Characters() array of character objects src/pxrpcserver.cpp:526, src/gamestatejson.cpp:752
getgroundloot GroundLoot() array of ground-loot objects src/pxrpcserver.cpp:537, src/gamestatejson.cpp:759
getongoings OngoingOperations() array of ongoing-operation objects src/pxrpcserver.cpp:548, src/gamestatejson.cpp:766
getregions {fromheight} Regions(h) array of region objects modified since h src/pxrpcserver.cpp:559, src/gamestatejson.cpp:773
getmoneysupply MoneySupply() money-supply + burnsale object src/pxrpcserver.cpp:582, src/gamestatejson.cpp:643
getprizestats PrizeStats() prizes object src/pxrpcserver.cpp:593, src/gamestatejson.cpp:692
gettradehistory {building,item} TradeHistory(item,building) array of trade objects src/pxrpcserver.cpp:606, src/gamestatejson.cpp:780
getbootstrapdata BootstrapData() {regions:[...]} (all regions) src/pxrpcserver.cpp:618, src/gamestatejson.cpp:805

The official web client calls these directly: getbuildings, getaccounts, getregions, getgroundloot, getongoings, getbootstrapdata, and getcharacters.

FullState() (src/gamestatejson.cpp:789) bundles everything into one object with keys accounts, buildings, characters, groundloot, ongoings, moneysupply, regions (since height 0), prizes. The header notes it is "mainly for debugging and testing, not production" — production clients should call the targeted RPCs above. The full-state object is what the test file asserts against, so every example below is a fragment of that object.

{
  "accounts":    [ ... ],
  "buildings":   [ ... ],
  "characters":  [ ... ],
  "groundloot":  [ ... ],
  "ongoings":    [ ... ],
  "moneysupply": { ... },
  "regions":     [ ... ],
  "prizes":      { ... }
}

getbootstrapdata / /bootstrap.json.gz#

BootstrapData() returns {"regions": <all regions>} — i.e. Regions(0), every region (not only recently modified ones). It is large and expensive, so the REST layer serves a gzip-cached copy at /bootstrap.json.gz, refreshed every --rest_bootstrap_refresh_seconds (default 3600 s) (src/rest.cpp:22, src/rest.cpp:59). The official client uses bootstrap data on startup.

getregions height limit#

getregions takes fromheight and returns only regions changed since that height. The GSP rejects a fromheight that is more than MAX_REGIONS_HEIGHT_DIFFERENCE blocks behind the tip with error GETREGIONS_FROM_TOO_LOW (src/pxrpcserver.cpp:49, :567). That limit is 2 * 60 * 24 * 3 = 8640 blocks (roughly three days at ~30 s/block; src/pxrpcserver.cpp:49). Clients that fall too far behind must re-fetch the full set via getbootstrapdata.


2. Conventions used by the serializer#

These rules apply to every entity and are essential to reading the JSON.

2.1 Absent vs. null#

The serializer omits a key entirely when the corresponding data is absent — it does not emit null. The test harness encodes this: in gamestatejson_tests.cpp, an expected value of null for a key asserts the key is absent from the real output (src/gamestatejson_tests.cpp:73). So whenever an example below shows "x": null, it means "the key x is not present". Clients must treat "key missing" and "feature absent" as equivalent.

2.2 Integers and ID encoding#

  • All integers go through IntToJson (src/jsonutils.cpp), which preserves signedness and 64-bit width. Entity IDs and coin/quantity amounts can exceed 2^53, so clients must parse them as 64-bit integers / BigInt, not JS numbers, to avoid precision loss.
  • Database IDs are positive integers; valid range is 0 < id < 999999999 (MAX_ID, src/jsonutils.cpp). A sentinel "no ID" internally is Database::EMPTY_ID; in JSON the corresponding key is simply omitted.

2.3 Coordinates#

A map coordinate (axial hex) is {"x": <int>, "y": <int>} (CoordToJson, src/jsonutils.cpp). Both are signed integers.

2.4 Factions#

Faction is a single-letter string (FactionToString, database/faction.cpp:27):

Code Faction
"r" Red
"g" Green
"b" Blue
"a" Ancient (NPC / neutral, e.g. ancient buildings)

2.5 Item codes vs. display names#

Inventory and item fields use internal item codes (hardcoded strings). Examples: raw a = "Trimideum", mat a = "Agarite", mat b = "Borolium", rv st = "Raider". The display names are the ones shown in the game UI; clients should map codes to these names for the UI. Prize items are stored under the synthetic code "<prize name> prize" (e.g. "gold prize", src/prospecting.cpp:160). Blueprint codes follow the pattern <item> bpo (original) and <item> bpc (copy), e.g. bow bpo, bow bpc (src/gamestatejson_tests.cpp:1166). The empty string "" is a valid item code seen in ground loot (src/gamestatejson_tests.cpp:1028).

2.6 Monetary unit (vCHI / "Cubit")#

The in-game coin is vCHI (also called Cubit). The base smallest unit is 1/COIN, where COIN = 100000000 (1e8) (database/amount.hpp:31). Account balances are stored in whole vCHI integers; burnsale prices are converted to floating CHI by dividing satoshi prices by COIN (see Money supply, §11).


3. Account#

Produced by Convert<Account> (src/gamestatejson.cpp:305) and post-processed in Accounts() (src/gamestatejson.cpp:716) to add DEX-reserved balances. Backed by proto/account.proto plus DB columns.

Field Type Units / values When present Semantics
name string Xaya p/ name always The account / player name.
minted int (uint64) vCHI always burnsale_balance: vCHI minted via the burnsale (the portion that carries over to the full game). account.proto:49.
balance.available int (uint64) vCHI always Spendable balance held by the account. gamestatejson.cpp:316.
balance.reserved int (int64) vCHI always (added by Accounts()) vCHI locked in the account's open DEX bid orders. gamestatejson.cpp:723,738.
balance.total int (int64) vCHI always (added by Accounts()) available + reserved. gamestatejson.cpp:739.
faction string r/g/b only if account initialised Player faction; chosen at first character creation. Absent until then. gamestatejson.cpp:321.
kills int (uint32) count only if initialised Total characters this account has killed. account.proto:33.
fame int (uint32) points only if initialised Account fame; default value for a fresh initialised account is 100 (see example). account.proto:38.

Initialisation. An account exists as soon as it holds a balance or minted vCHI, but faction, kills, and fame only appear once the account is "initialised" (a faction has been chosen). IsInitialised() gates them (gamestatejson.cpp:319). An account that bought vCHI in the burnsale but never picked a faction shows minted/balance but no faction/kills/fame.

Note: a new initialised account's default fame is 100, not 0 — see test KillsAndFame: "foo" set kills=10, never set fame, and reports fame:100 (gamestatejson_tests.cpp:553–555). "bar" set fame=42 explicitly.

Example — uninitialised account with open bid (from UninitialisedBalance, gamestatejson_tests.cpp:571): the account "bar" has a bid for 2 units at price 3 (reserving 2*3 = 6 vCHI):

{
  "name": "bar",
  "minted": 10,
  "balance": { "available": 42, "reserved": 6, "total": 48 }
}

(No faction/kills/fame because it is uninitialised.)

Example — initialised account:

{
  "name": "foo",
  "faction": "r",
  "kills": 10,
  "fame": 100,
  "minted": 0,
  "balance": { "available": 0, "reserved": 0, "total": 0 }
}

4. Character#

Produced by Convert<Character> (src/gamestatejson.cpp:251). Backed by proto/character.proto plus DB columns (ID, owner, faction, position, building, volatile movement, HP, regen, target, inventory).

Field Type Units / values When present Semantics
id int (uint64) DB id always Character ID. gamestatejson.cpp:256.
owner string Xaya name always Owning account. gamestatejson.cpp:257.
faction string r/g/b always Faction of the character. gamestatejson.cpp:258.
vehicle string item code always Vehicle item type the character is in, e.g. rv st (Raider). character.proto:82, gamestatejson.cpp:259.
fitments array of string item codes always (may be []) Fitments mounted on the vehicle, in order. character.proto:85, gamestatejson.cpp:261.
position coord hex only when on the map (not in a building) Current map position. gamestatejson.cpp:269.
inbuilding int (uint64) building id only when inside a building ID of the building the character is in; mutually exclusive with position. gamestatejson.cpp:266.
enterbuilding int (uint64) building id only if a pending "enter building" is set The building the character is trying to enter. gamestatejson.cpp:271.
speed int (uint32) milli-tiles per block always Movement speed of the vehicle (1000 = 1 tile/block). character.proto:114, gamestatejson.cpp:275.
inventory object see §4.1 always Cargo inventory. gamestatejson.cpp:276.
cargospace object see §4.2 always Cargo capacity breakdown. gamestatejson.cpp:277.
combat object see §4.3 always Combat data (HP, attacks, target, attackers). gamestatejson.cpp:274.
movement object see §4.4 only if there is any movement data Waypoints / chosen speed / partial step. gamestatejson.cpp:279.
busy int (uint64) ongoing op id only if the character is busy ID of the ongoing operation occupying the character (e.g. prospecting). character.proto:102, gamestatejson.cpp:283.
mining object see §4.5 only if the vehicle can mine Mining rate + active state. gamestatejson.cpp:286.
prospectingblocks int (uint32) blocks only if the vehicle can prospect Number of blocks this vehicle takes to finish a prospection. character.proto:111, gamestatejson.cpp:291.
refining object see §4.6 only if the vehicle is a mobile refinery Mobile-refinery inefficiency. gamestatejson.cpp:294.

Mutual exclusivity. A character is either on the map (position) or inside a building (inbuilding), never both (gamestatejson.cpp:266). Clients should branch on which key is present.

Example — on the map (Basic, gamestatejson_tests.cpp:111):

{
  "id": 1, "owner": "domob", "faction": "r",
  "speed": 750,
  "position": {"x": -5, "y": 2}
}

Example — inside a building (gamestatejson_tests.cpp:117):

{ "id": 2, "owner": "andy", "faction": "g", "inbuilding": 100 }

Example — vehicle + fitments (gamestatejson_tests.cpp:202):

{ "vehicle": "rv st", "fitments": ["turbo", "bomb"] }

A character with no fitments emits "fitments": [] (gamestatejson_tests.cpp:208).

4.1 inventory#

Convert<Inventory> (src/gamestatejson.cpp:237). One key:

"inventory": { "fungible": { "raw a": 5, "mat b": 10 } }
  • fungible — object mapping item code → integer count (inventory.proto:37). The amount is "some smallest fraction of the item" (typically whole units). Empty inventory ⇒ "fungible": {}. There are no non-fungible inventory entries in the current model.

4.2 cargospace#

GetCargoSpaceJsonObject (src/gamestatejson.cpp:199).

Field Type Units Semantics
total int (uint64) cargo units The vehicle's total cargo capacity (character.proto:117).
used int (uint64) cargo units Space consumed by the current inventory.
free int (uint64) cargo units total - used.

used is the sum over inventory of count * item.space for each item type (Character::UsedCargoSpace, database/character.cpp:230). In the test, 35 of item foo (which has space = 10) consumes 350 of 1000 (gamestatejson_tests.cpp:352):

"cargospace": { "total": 1000, "used": 350, "free": 650 }

4.3 combat#

GetCombatJsonObject (src/gamestatejson.cpp:131 for the base, :182 for the character overload that adds attackers). Backed by proto/combat.proto. The character version always includes hp; the others are conditional.

Field Type When present Semantics
hp object always Current/max/regeneration HP.
attacks array only if the entity has ≥1 attack List of weapon attacks.
target object only if a target is currently selected The current attack target.
attackers array of int only on characters, only if non-empty IDs of characters currently attacking this one (from damage lists). gamestatejson.cpp:187.

combat.hp

HpProtoToJson (src/gamestatejson.cpp:87). Two HP pools — armour (permanent) and shield (regenerating) (combat.proto:199).

"hp": {
  "max":          {"armour": 100, "shield": 10},
  "current":      {"armour": 42,  "shield": 5.001},
  "regeneration": {"shield": 1.001, "armour": 0.042}
}
  • max.armour, max.shield — integer maximum HP per pool (RegenData.max_hp, combat.proto:228).
  • current.armour, current.shield — current HP. Fractional HP are represented as a decimal: stored as whole HP plus "milli-HP" (1/1000 HP); the serializer converts full + millis/1000 (HpValueToJson, gamestatejson.cpp:75). E.g. shield 5 + 1 milli ⇒ 5.001. If millis == 0 the value is a plain integer.
  • regeneration — shield/armour regen rate, in HP per block, again rendered as decimal from regeneration_mhp ("milli-HP per block", combat.proto:231). regeneration_mhp.shield = 10011.001, regeneration_mhp.armour = 420.042 (gamestatejson_tests.cpp:298,314). Note: a regen of 0 is still emitted as 0 here because regeneration is built from a fake HP proto and always present (gamestatejson.cpp:169–171); see building example where armour regen is 0.

combat.attacks

Each entry comes from proto::Attack (combat.proto:93). Fields are conditional (gamestatejson.cpp:140):

Field Type When present Semantics
range int (uint32) if the attack has a range Attack range as L1 hex distance. Absent ⇒ the attack is centred on the attacker (e.g. an AoE around self). 0 ⇒ same-tile only. combat.proto:104.
area int (uint32) if the attack is AoE AoE radius around the target (if range present) or the attacker (if not). Absent ⇒ single-target. combat.proto:111.
friendlies bool (true) only if the attack targets friendlies Buff-type attack that affects allies (e.g. shield-regen boost). Emitted only when true. combat.proto:158, gamestatejson.cpp:148.
damage object {min,max} only if the attack does damage Inclusive damage range; actual damage uniform in [min,max]. combat.proto:116, gamestatejson.cpp:151.

Note the serializer does not emit the proto's armour_percent, shield_percent, weapon_size, effects, or gain_hp sub-fields — only range, area, friendlies, and damage.{min,max} are exposed.

Example (gamestatejson_tests.cpp:264):

"attacks": [
  { "range": 5, "damage": {"min": 2, "max": 10} },
  { "area": 1, "damage": {"min": 0, "max": 1} },
  { "area": 10, "friendlies": true }
]

combat.target

TargetIdToJson (src/gamestatejson.cpp:50). Present only when a target is selected.

"target": { "id": 5, "type": "character" }

type is "character" or "building" (gamestatejson.cpp:58–62); id is the target entity ID. (Buildings can also have a combat.target — see §6.)

combat.attackers

Array of character IDs currently attacking this character (only present when non-empty, only on characters). From the damage-list table (gamestatejson.cpp:187). Example: "attackers": [2] (gamestatejson_tests.cpp:486).

4.4 movement#

GetMovementJsonObject (src/gamestatejson.cpp:99). The whole object is omitted if it would be empty. Sub-fields are individually conditional.

Field Type When present Semantics
chosenspeed int (uint32) if a self-imposed speed cap is set The character travels at min(chosenspeed, actual speed); used to keep convoys together. movement.proto:47.
waypoints array of coord if the waypoint list is non-empty Remaining waypoints, in visit order; first element is the current target tile. movement.proto:40.
partialstep int (uint32) if there is partial progress to the next tile Volatile sub-tile movement progress. movement.proto:61.
blockedturns int (uint32) if the character has been blocked Number of consecutive turns blocked by an obstacle; movement stops after a configured number of retries. movement.proto:68.

Example (gamestatejson_tests.cpp:178):

"movement": {
  "partialstep": 5,
  "blockedturns": 3,
  "waypoints": [ {"x": -3, "y": 0}, {"x": 0, "y": 42} ]
}

4.5 mining#

GetMiningJsonObject (src/gamestatejson.cpp:215). Present only if the vehicle has mining capability (character.proto:105).

Field Type Units When present Semantics
rate.min int (uint64) units/block always (within mining) Minimum mining yield per block. character.proto:42.
rate.max int (uint64) units/block always Maximum mining yield per block (yield is random in [min,max]).
active bool always Whether the character is currently mining. character.proto:53.
region int (uint32) region id only if active The region currently being mined, derived from position. gamestatejson.cpp:229.

Examples (gamestatejson_tests.cpp:395,407):

"mining": { "rate": {"min": 0, "max": 5}, "active": false }
"mining": { "rate": {"min": 10, "max": 11}, "active": true, "region": 350146 }

4.6 refining (mobile refinery)#

gamestatejson.cpp:294. Present only if the vehicle can refine on the move (character.proto:123, MobileRefinery).

Field Type Units Semantics
inefficiency int percent The input ore cost relative to a stationary refinery, computed as StatModifier(input)(100). A mobile refinery produces the same outputs but consumes more input ore. gamestatejson.cpp:296.

In the test, input.percent = 100 (i.e. +100%) applied to a base of 100 gives 200 (gamestatejson_tests.cpp:451,463):

"refining": { "inefficiency": 200 }

StatModifier formula: base + base*percent/100 + absolute (src/modifier.hpp). So inefficiency = 200 means the mobile refinery uses 2× the ore a fixed refinery would for the same step.


5. Inventory (shared shape)#

The inventory object is reused by characters, ground loot, building per-account inventories, building construction inventories, and the building "reserved" section. Its shape is always:

{ "fungible": { "<item code>": <integer count>, ... } }

Source: Convert<Inventory> (src/gamestatejson.cpp:237), proto/inventory.proto. Zero-count entries are pruned upstream (only present items appear). The empty item code "" is permitted as a key (gamestatejson_tests.cpp:1028).


6. Building#

Produced by Convert<Building> (src/gamestatejson.cpp:407). Backed by proto/building.proto plus DB columns and external tables (inventories, DEX orders). The building's shape on the map is computed from its type, rotation, and centre.

Field Type Units / values When present Semantics
id int (uint64) DB id always Building ID. gamestatejson.cpp:414.
type string building type code always Building type (e.g. checkmark, r ss). gamestatejson.cpp:415.
foundation bool (true) only if it is a foundation true while the building is an unfinished foundation (a building under construction that players are still depositing materials into). Omitted (i.e. full building) otherwise. building.proto:55, gamestatejson.cpp:416.
faction string r/g/b/a always Owning faction; a = ancient/neutral. gamestatejson.cpp:419.
owner string Xaya name only if not Ancient Owning account. Absent for ancient buildings. gamestatejson.cpp:420.
centre coord hex always The building's centre tile. gamestatejson.cpp:422.
rotationsteps int (uint32) 0–5 always Clockwise 60° rotation steps of the base shape. building.proto:39, gamestatejson.cpp:424.
config object see §6.1 always Owner-configurable fees. gamestatejson.cpp:425.
tiles array of coord hex always All map tiles the building occupies (post-rotation, post-shift). gamestatejson.cpp:427.
combat object see §4.3 always Building combat data (HP, attacks, target). No attackers array for buildings. gamestatejson.cpp:432.
construction object see §6.2 only if foundation Construction progress + materials. gamestatejson.cpp:434.
inventories object see §6.3 only if not a foundation Per-account stored goods. gamestatejson.cpp:444.
reserved object see §6.4 only if not a foundation Per-account goods locked in open ask orders. gamestatejson.cpp:453.
orderbook object see §6.5 only if not a foundation DEX order book by item. gamestatejson.cpp:458.
age object see §6.6 always Founded/finished block heights. gamestatejson.cpp:461.

Foundation vs. finished building are mutually exclusive in their extra sections: a foundation has construction but no inventories/reserved/ orderbook; a finished building has the latter three but no construction (gamestatejson.cpp:434 vs :442).

Example — basic finished building (gamestatejson_tests.cpp:625):

{
  "id": 1,
  "type": "checkmark",
  "owner": "foo",
  "faction": "r",
  "centre": {"x": 1, "y": 2},
  "rotationsteps": 0,
  "tiles": [
    {"x": 1, "y": 2}, {"x": 2, "y": 2}, {"x": 1, "y": 3}, {"x": 1, "y": 4}
  ]
}

Example — ancient building (gamestatejson_tests.cpp:651): "faction":"a" and no owner key.

6.1 config#

Convert(proto::Building::Config) (src/gamestatejson.cpp:329). Each sub-field is present only if set; if neither is configured the object is {}.

Field Type Units Semantics
servicefee int (uint32) percent Surcharge the building owner charges for in-building services, as a percentage of the service's base (burnt) fee. building.proto:88.
dexfee float percent Owner's DEX trading fee, paid by the seller out of received vCHI. Stored internally in basis points (dex_fee_bps); the serializer divides by 100 to give a percentage. building.proto:95, gamestatejson.cpp:336.

Example: dex_fee_bps = 1725"dexfee": 17.25; service_fee_percent = 42"servicefee": 42 (gamestatejson_tests.cpp:891,900). A building with no configured fees: "config": {} (both keys absent, gamestatejson_tests.cpp:882).

6.2 construction (foundations only)#

gamestatejson.cpp:434.

Field Type When present Semantics
inventory inventory object always (within construction) Materials deposited toward completing the building (the construction_inventory proto, building.proto:61).
ongoing int (uint64) only if a completion op is running ID of the ongoing "build" operation upgrading this foundation. building.proto:67, gamestatejson.cpp:437.

Example (gamestatejson_tests.cpp:695,705):

"construction": { "inventory": {"fungible": {"bar": 10}} }
"construction": { "ongoing": 42, "inventory": {"fungible": {}} }

6.3 inventories (finished buildings only)#

Map of account name → that account's inventory object, for goods stored inside the building (gamestatejson.cpp:444). Accounts with empty inventories are omitted (the map can be {}).

"inventories": {
  "andy":  {"fungible": {"bar": 1}},
  "domob": {"fungible": {"foo": 2}}
}

(gamestatejson_tests.cpp:737)

6.4 reserved (finished buildings only)#

Map of account name → an inventory object listing item quantities locked in that account's open ask orders in this building's DEX (gamestatejson.cpp:453, GetReservedQuantities, database/dex.cpp:308). Same shape as inventories.

"reserved": {
  "andy":  {"fungible": {"foo": 5}},
  "domob": {"fungible": {"foo": 4, "bar": 1}}
}

(gamestatejson_tests.cpp:741)

The vCHI reserved by bid orders is not here — it is folded into each account's balance.reserved (§3).

6.5 orderbook (finished buildings only)#

GetOrderbookInBuilding (src/gamestatejson.cpp:347). An object keyed by item code; each entry has the order book for that item:

Field Type Semantics
item string The item code (same as the map key).
bids array of order Buy orders, sorted best (highest) price first, then by ascending order ID. gamestatejson.cpp:395.
asks array of order Sell orders, sorted best (lowest) price first.

Each order object (gamestatejson.cpp:368):

Field Type Units Semantics
id int (uint64) DB id Order ID.
account string Xaya name Order owner.
quantity int units Quantity remaining on the order.
price int vCHI per unit Limit price per unit.

The DB query returns orders ascending by price; the serializer reverses bids so the best bid is first (gamestatejson.cpp:393). The cost of filling an order is quantity * price (see Trade history, §10).

Example (gamestatejson_tests.cpp:790):

"orderbook": {
  "foo": {
    "item": "foo",
    "bids": [
      { "id": 103, "account": "domob", "quantity": 1, "price": 3 },
      { "id": 102, "account": "andy",  "quantity": 1, "price": 2 },
      { "id": 101, "account": "domob", "quantity": 2, "price": 2 },
      { "id": 105, "account": "domob", "quantity": 3, "price": 1 }
    ],
    "asks": [
      { "id": 104, "account": "domob", "quantity": 1, "price": 8 },
      { "id": 109, "account": "domob", "quantity": 1, "price": 9 },
      { "id": 106, "account": "domob", "quantity": 1, "price": 10 }
    ]
  }
}

6.6 age#

gamestatejson.cpp:461. Block heights of building lifecycle events (building.proto:106).

Field Type When present Semantics
founded int (uint32) always Block height when the foundation was placed. building.proto:110.
finished int (uint32) only if not a foundation Block height when construction completed. Absent while still a foundation. building.proto:113, gamestatejson.cpp:463.

Example (gamestatejson_tests.cpp:940):

"age": { "founded": 10, "finished": 12 }

A foundation shows only {"founded": 10} (gamestatejson_tests.cpp:921).


7. Ground loot#

Produced by Convert<GroundLoot> (src/gamestatejson.cpp:470). GroundLoot() returns only non-empty piles (QueryNonEmpty, gamestatejson.cpp:763); an empty world yields [].

Field Type When present Semantics
position coord always Tile where the loot lies. gamestatejson.cpp:475.
inventory inventory object always Items dropped here (fungible map). gamestatejson.cpp:476.

Example (gamestatejson_tests.cpp:1037):

{
  "position": {"x": -1, "y": 20},
  "inventory": { "fungible": { "foo": 10 } }
}

Loot supports the empty-string item code as a valid key (gamestatejson_tests.cpp:1028).


8. Ongoing operation#

Produced by Convert<OngoingOperation> (src/gamestatejson.cpp:481). Backed by proto/ongoing.proto plus DB columns. The operation field is a discriminator; the remaining fields depend on its value.

8.1 Common fields#

Field Type When present Semantics
id int (uint64) always Operation ID. gamestatejson.cpp:489.
start_height int (uint32) always Block height when the operation started. ongoing.proto:149, gamestatejson.cpp:490.
end_height int (uint64) always Block height when the operation completes. Note: for multi-item ops (series construction, multi-copy blueprint copies) this is the operation height plus a per-op delta (see below). gamestatejson.cpp:567.
characterid int (uint64) only if attached to a character Associated character. gamestatejson.cpp:491.
buildingid int (uint64) only if attached to a building Associated building. gamestatejson.cpp:493.
operation string always Operation type discriminator (see below).

characterid and buildingid are mutually exclusive in practice (an op belongs to one entity). Example (gamestatejson_tests.cpp:1104):

{ "id": 1, "start_height": 3, "end_height": 5, "characterid": 42 }

end_height and the delta. For most operations end_height equals the DB "next-processing height". But operations that are processed in series (item construction from an original, multi-copy blueprint copy) finish one unit per processing step; the serializer adds (count - 1) * blocksPerUnit so end_height reflects the final completion (gamestatejson.cpp:500,525,547,567). See the blueprint-copy and item-build examples below.

8.2 operation discriminator values#

The oneof op (ongoing.proto:152) maps to these strings (gamestatejson.cpp:502):

operation Proto case Extra fields
"prospecting" prospection none
"armourrepair" armour_repair none
"bpcopy" blueprint_copy account, original, output
"construct" item_construction account, output, optional original
"build" building_construction none (uses buildingid)
"config" building_update newconfig

prospecting

{ "id": 1, "operation": "prospecting" }

(gamestatejson_tests.cpp:1132) The region being prospected is implicit from the character's position (ongoing.proto:29).

armourrepair

{ "id": 1, "operation": "armourrepair" }

(gamestatejson_tests.cpp:1150)

bpcopy (blueprint copy)

Field Type Semantics
account string Owner doing the copying (ongoing.proto:61).
original string Original blueprint item code being copied (original_type, ongoing.proto:64).
output object Map <copy item code> → num copies (copy_typenum_copies). gamestatejson.cpp:520.

The copies are produced one at a time; end_height includes (num_copies-1) * GetBpCopyBlocks(copy_type). In the test bow bpc takes 1000 blocks/copy, 42 copies starting at height 1 with op height 1001 ⇒ end_height = 1001 + 41*1000 = 42001 (gamestatejson_tests.cpp:1172):

{
  "id": 1,
  "operation": "bpcopy",
  "original": "bow bpo",
  "output": {"bow bpc": 42},
  "start_height": 1,
  "end_height": 42001
}

construct (item / vehicle construction)

Field Type When present Semantics
account string always Owner who receives the finished items (ongoing.proto:91).
output object always Map <output item code> → num items (output_typenum_items). gamestatejson.cpp:538.
original string only if built from an original blueprint The consumed-and-returned original blueprint type. Building from copies omits this and runs all in parallel. ongoing.proto:109, gamestatejson.cpp:542.

When original is present, items are produced in series and end_height adds (num_items-1) * GetConstructionBlocks(output_type). Test: bow takes 1000 blocks/item; 5 items from an original, op height 1001 ⇒ end_height = 5001; 42 items from copies (no original) ⇒ end_height = 1001 (gamestatejson_tests.cpp:1210):

{ "id": 1, "operation": "construct", "output": {"bow": 42},
  "start_height": 1, "end_height": 1001 }
{ "id": 2, "operation": "construct", "output": {"bow": 5},
  "original": "bow bpo", "start_height": 1, "end_height": 5001 }

build (finish building construction)

Upgrades a foundation to a full building. No extra fields; uses buildingid (gamestatejson_tests.cpp:1240):

{ "id": 1, "operation": "build", "buildingid": 42 }

config (building config update)

Delayed building-config change (delay prevents front-running). Carries the pending config (ongoing.proto:135, gamestatejson.cpp:560):

Field Type Semantics
newconfig object The pending Building.Config (same shape as §6.1: servicefee, dexfee).

Example (gamestatejson_tests.cpp:1281):

{
  "id": 1,
  "operation": "config",
  "newconfig": { "servicefee": 2, "dexfee": 1.5 }
}

With no fields set, "newconfig": {} (gamestatejson_tests.cpp:1266).


9. Region#

Produced by Convert<Region> (src/gamestatejson.cpp:572). Backed by proto/region.proto plus a DB column for resource-left. Regions(h) returns only regions whose state changed at height > h (QueryModifiedSince, gamestatejson.cpp:777); getbootstrapdata uses h=0 for all regions.

Field Type When present Semantics
id int always Region ID (integer, derived from the hex map). gamestatejson.cpp:579.
prospection object only if non-empty Prospection status — see below. gamestatejson.cpp:590.
resource object only if the region has been prospected The mineable resource left — see below. gamestatejson.cpp:593.

A region that was touched in the DB but reverted to a trivial state still appears in Regions results, with neither prospection nor resource (i.e. only id) — see test Empty (gamestatejson_tests.cpp:1324). A region never written at all is simply absent.

9.1 prospection#

gamestatejson.cpp:581. Sub-fields are conditional.

Field Type When present Semantics
inprogress int (uint64) only if a character is currently prospecting Character ID doing the prospection. region.proto:59, gamestatejson.cpp:582.
name string only if already prospected Xaya name of whoever prospected it (display only). region.proto:34, gamestatejson.cpp:586.
height int (uint32) only if already prospected Block height when it was prospected. region.proto:37, gamestatejson.cpp:587.

Examples (gamestatejson_tests.cpp:1346):

{ "id": 10, "prospection": { "name": "bar", "height": 107 } }
{ "id": 20, "prospection": { "inprogress": 42 } }

9.2 resource#

Present only once a region has prospection data (gamestatejson.cpp:593).

Field Type Units Semantics
type string item code The mineable resource type, e.g. sand, raw a. region.proto:48.
amount int units Resource units still mineable (GetResourceLeft); decreases as the region is mined; may be 0. gamestatejson.cpp:597.

Example (gamestatejson_tests.cpp:1369):

{ "id": 10, "resource": { "type": "sand", "amount": 150 } }

10. Trade history#

Produced by Convert<DexTrade> (src/gamestatejson.cpp:605), returned by gettradehistory(building, item) as an array, most-recent first. Backed by the DexHistoryTable. Querying an item/building with no history returns [] (gamestatejson_tests.cpp:1556).

Field Type Units Semantics
height int block Block height of the trade. gamestatejson.cpp:611.
timestamp int unix seconds Block timestamp of the trade. gamestatejson.cpp:612.
buildingid int (uint64) DB id Building whose DEX hosted the trade. gamestatejson.cpp:614.
item string item code Item traded. gamestatejson.cpp:615.
quantity int units Quantity traded. gamestatejson.cpp:617.
price int vCHI/unit Execution price per unit. gamestatejson.cpp:618.
cost int (int64) vCHI Total cost = quantity * price. gamestatejson.cpp:619.
seller string Xaya name Seller account. gamestatejson.cpp:622.
buyer string Xaya name Buyer account. gamestatejson.cpp:623.

Example (gamestatejson_tests.cpp:1565):

{
  "height": 10,
  "timestamp": 1024,
  "buildingid": 42,
  "item": "foo",
  "quantity": 2,
  "price": 3,
  "cost": 6,
  "seller": "domob",
  "buyer": "andy"
}

11. Money supply#

Produced by MoneySupply() (src/gamestatejson.cpp:643), returned by getmoneysupply. Reports the total vCHI in existence by source plus burnsale stage progress.

Field Type Semantics
total int (int64) Sum of all counted entry values (vCHI). gamestatejson.cpp:685.
entries object Map of money-source key → amount minted from that source. gamestatejson.cpp:660.
burnsale array Per-stage burnsale progress (see below). gamestatejson.cpp:664.

11.1 entries#

The valid keys are exactly "burnsale" and "gifted" (MoneySupply::GetValidKeys, database/moneysupply.cpp:93):

Key Meaning
burnsale vCHI minted by burning CHI in the burnsale.
gifted vCHI gifted by admin/god-mode.

God-mode gating of gifted. "God-mode" is an admin/testing mode in which privileged commands (such as giftcoins, which mints vCHI out of thin air) are honoured. The gifted key is emitted only when god-mode is off-gated: specifically, the serializer skips gifted when !params.god_mode(), and in that case CHECK_EQ(ms.Get("gifted"), 0) asserts no gifted coins exist (gamestatejson.cpp:653). When god-mode is on, gifted is included in entries (and total). See tests EntriesAndTotal (regtest, god-mode on, gifted present, gamestatejson_tests.cpp:1399) vs. GiftedOnMainnet (gifted absent, :1419).

Mainnet caveat — config vs. test disagree. The unit test roconfig_tests.cpp:69 asserts that on MAIN/TEST chains god_mode() is false (so gifted would be omitted), and only REGTEST has it true (:71). However, the checked-in base config proto/roconfig/params.pb.text:31 sets god_mode: true for the mainnet base, and roconfig.cpp does not clear it for MAIN. As shipped, that makes mainnet god-mode on, which would emit gifted and disable the CHECK_EQ guard — i.e. the stale test no longer matches the config. This is the same discrepancy flagged in Economy & Accounts and Game Overview; treat the live behaviour as "god-mode on (mainnet config), gifted present" until the config or test is reconciled. See Open question #4.

"moneysupply": {
  "total": 30,
  "entries": { "gifted": 10, "burnsale": 20 }
}

11.2 burnsale stages#

One entry per burnsale stage, computed by walking the configured stages and distributing the total burnsale amount across them (gamestatejson.cpp:664). Stage config comes from params.burnsale_stages (field proto/config.proto:601, message BurnsaleStage at proto/config.proto:517; mainnet values in proto/roconfig/params.pb.text:49).

Field Type Units Semantics
stage int 1-based Stage number. gamestatejson.cpp:674.
price float CHI per vCHI Price in this stage = price_sat / COIN (1e8). gamestatejson.cpp:675.
total int vCHI Total vCHI sellable in this stage (amount_sold). gamestatejson.cpp:676.
sold int vCHI How much of this stage has been sold so far. gamestatejson.cpp:677.
available int vCHI total - sold remaining in this stage. gamestatejson.cpp:678.

Mainnet stage configuration (proto/roconfig/params.pb.text:49–52):

Stage total (vCHI) price_sat price (CHI/vCHI)
1 10000000000 10000 0.0001
2 10000000000 20000 0.0002
3 10000000000 50000 0.0005
4 20000000000 100000 0.0010

Example with 25,000,000,000 vCHI sold (fills stages 1–2, half of 3) (gamestatejson_tests.cpp:1442):

"burnsale": [
  { "stage": 1, "price": 0.0001, "total": 10000000000, "sold": 10000000000, "available": 0 },
  { "stage": 2, "price": 0.0002, "total": 10000000000, "sold": 10000000000, "available": 0 },
  { "stage": 3, "price": 0.0005, "total": 10000000000, "sold": 5000000000,  "available": 5000000000 },
  { "stage": 4, "price": 0.0010, "total": 20000000000, "sold": 0,           "available": 20000000000 }
]

12. Prizes#

Produced by PrizeStats() (src/gamestatejson.cpp:692), returned by getprizestats. An object keyed by prize tier name; one entry per configured prize (params.prizes, field proto/config.proto:611, message ProspectingPrize at proto/config.proto:498, proto/roconfig/params.pb.text).

Field Type Semantics
number int (uint32) Total prizes of this tier that exist. proto/config.proto:507, gamestatejson.cpp:701.
probability int (uint32) "1-in-N" base chance: per successful prospect, the chance to win this prize is 1/probability (modulated by safe-zone "low prize" reduction). proto/config.proto:510, src/prospecting.cpp:168.
found int (uint32) How many of this tier have already been won. Comes from item count of "<name> prize". gamestatejson.cpp:704.
available int number - found remaining. gamestatejson.cpp:708.

The internal won-item code is "<tier name> prize" (src/prospecting.cpp:160).

Probability detail. On a normal prospect the roll is ProbabilityRoll(100, 100 * probability) ⇒ effective 1/probability; inside a configured "low prize" safe zone the numerator is 55 instead of 100, i.e. odds are reduced to 55% of normal (src/prospecting.cpp:166–168).

Mainnet/testnet difference. Mainnet uses a large prize table (proto/roconfig/params.pb.text:54–110+), e.g. { name: "cash", number: 10, probability: 350000 }, { name: "Prison Key Card", number: 100, probability: 2655 }. Testnet replaces the list with three easy tiers (proto/roconfig/test_params.pb.text:25–27):

Tier number probability
gold 3 100
silver 1000 10
bronze 1 1

Example (testnet config, gamestatejson_tests.cpp:1506):

"prizes": {
  "gold":   { "number": 3,    "probability": 100, "found": 1,  "available": 2 },
  "silver": { "number": 1000, "probability": 10,  "found": 10, "available": 990 },
  "bronze": { "number": 1,    "probability": 1,   "found": 0,  "available": 1 }
}

13. Field-presence quick reference#

For client developers, the keys that are conditionally present (and what their absence means):

  • Account: faction/kills/fame absent ⇒ account not yet initialised.
  • Character: exactly one of position / inbuilding. enterbuilding, busy, mining, prospectingblocks, refining, movement all capability/state-gated. combat.target, combat.attacks, combat.attackers conditional; combat.hp always present.
  • Building: foundation,construction ⟺ foundation; inventories/reserved/orderbook ⟺ finished building. owner absent ⇒ ancient. age.finished absent ⇒ still a foundation. config sub-keys present only when the fee is set.
  • Region: prospection/resource absent ⇒ unprospected/untouched; prospection.inprogress ⇒ being prospected now; prospection.name/height ⇒ already prospected.
  • Ongoing: characterid/buildingid one-or-neither; per-operation extra fields per §8.2.
  • Money supply: gifted absent ⇒ god-mode off; present ⇒ god-mode on. Note the checked-in mainnet config has god-mode on (params.pb.text:31), despite a stale test asserting otherwise — see §11.1 and Open question #4.

Open questions#

  1. region.id width (resolved). Convert<Region> emits res["id"] = r.GetId() directly (not via IntToJson, gamestatejson.cpp:579), unlike all other IDs. Region::GetId() returns RegionMap::IdT, which is uint32_t (mapdata/regionmap.hpp:42, database/region.hpp:107). A uint32 always fits in a JS number (max ≈ 4.29e9 < 2^53), so — unlike 64-bit entity IDs — region.id is safe to parse as a plain number. Example values (10, 20,
    1. confirm this.
  2. combat sub-field coverage. The serializer deliberately exposes only range/area/friendlies/damage.{min,max} for attacks and omits proto fields like armour_percent, shield_percent, weapon_size, effects, gain_hp, low_hp_boosts, self_destructs, received_damage_modifier, hit_chance_modifier, target_size (combat.proto). Whether a future AAA UX needs these surfaced (they affect real combat math but are currently invisible to clients) is a design decision, not a documentation gap.
  3. Cargo unit definition. cargospace values are in abstract "cargo units" = count * item.space; the per-item space constants live in the item config protos (proto/roconfig/items/*.pb.text) and were not enumerated here (out of scope for the state-JSON topic — covered by the items/economy docs).
  4. gifted / god-mode: config vs. test disagree (unresolved). roconfig_tests.cpp:69 asserts mainnet god_mode() == false, but the checked-in base config proto/roconfig/params.pb.text:31 sets god_mode: true and roconfig.cpp does not clear it for MAIN. As-built, mainnet therefore has god-mode on, so gifted would be emitted and the CHECK_EQ(ms.Get("gifted"), 0) guard at gamestatejson.cpp:655 would not apply (it only runs when god-mode is off). Whether the released mainnet binary ships with god-mode on, or the config is meant to be flipped off (making the test correct again), could not be resolved from the GSP source alone; the move processor gates admin commands on params.god_mode() (src/moveprocessor.cpp:2352), so with the current config those commands are live on mainnet.