Skip to content
TAURION
01Moves Reference

Moves Reference

Every on-chain command a player can send, with full JSON schemas.

For players: what is a "move"?#

Taurion is a fully on-chain game: there is no central server that decides what happens. Instead, every action you take — moving a vehicle, mining ore, buying an item, creating a character — is a small instruction (a "move") that you broadcast to the Xaya blockchain. A piece of software called the GSP (Game State Processor) reads each move, checks the rules, and updates the shared game world the same way for everyone. This document lists every kind of move the GSP understands.

You normally never write these moves by hand — your game client builds them for you. But because the rules below are exactly what the network enforces, they tell you precisely what is and isn't possible: how much a character costs, why a transfer might silently fail, how trading fees are split, and so on.

A few words you'll see throughout:

  • Move — one instruction to the game (e.g. "character 5, start mining").
  • vCHI / Cubit — the in-game coin used for fees, trading, and refining.
  • WCHI / CHI — the real Polygon cryptocurrency, used only to create characters and to buy vCHI in the burnsale.
  • Faction — the team you join when you start: Red, Green, or Blue. It is permanent.
  • Character / vehicle — your unit on the map. A character pilots a vehicle (e.g. a Raider) and can equip fitments (gear like guns or scanners).
  • Building / foundation — a structure on the map. A foundation is a half-built building; once enough resources are dropped into it, it finishes into a full building.
  • Item codes — internally items use short codes (e.g. raw a); this doc shows the player-facing display name beside each, e.g. Trimideum (raw a).

The rest of this document is the precise technical specification, with JSON examples and source citations for developers building a game client.


This document is the authoritative, exhaustive specification of every move type the Taurion Game State Processor (GSP) accepts. It is derived directly from the GSP source (the single source of truth), primarily:

  • src/moveprocessor.cpp / src/moveprocessor.hpp — top-level dispatch, account, character, building, and coin (vCHI) moves; god-mode admin.
  • src/services.cpp / src/services.hpp — in-building service operations (s).
  • src/trading.cpp / src/trading.hpp — DEX trading operations (x).
  • src/jsonutils.cpp — JSON parsing limits for IDs, coin amounts, quantities, coords.
  • src/movement.cpp — waypoint encoding/decoding.
  • proto/roconfig/params.pb.text — economy/consensus parameters.

Display names are the ones shown in the game UI; the codes are what appears on-chain. Internal codes are shown in backticks, e.g. Trimideum (raw a), Raider (rv st).

Conventions in this doc

  • "vCHI" / "Cubit" is the in-game coin (the GSP code uses the names Amount, "coins", and "vCHI" interchangeably). 1 vCHI = COIN = 100,000,000 satoshi (database/amount.hpp:31).
  • "WCHI"/"CHI" is the real Polygon currency, sent via the move's out field (dev payment / burn). Character creation and the burnsale are paid in WCHI.
  • All IDs (characters, buildings, DEX orders) must be integers in the range (0, 999999999) exclusive — IdFromJson, src/jsonutils.cpp:148-155.
  • All item quantities must be integers in (0, 2^50]QuantityFromJson, src/jsonutils.cpp:137-145 with MAX_QUANTITY = 1<<50 (database/inventory.hpp:54).
  • All vCHI amounts must be integers in [0, 100000000000]CoinAmountFromJson, src/jsonutils.cpp:127-135 (MAX_COIN_AMOUNT = 100,000,000,000).
  • The processor is forgiving: invalid sub-commands are silently skipped and logged as warnings; the rest of the move still executes. There is no all-or-nothing rejection except for the structural CHECKs noted below.

1. The Top-Level Move Envelope#

Every Taurion transaction reaches the GSP as one element of a JSON array. Xaya wraps the player's game move in this envelope (ExtractMoveBasics, src/moveprocessor.cpp:71-108):

{
  "name": "domob",
  "move": { /* the game move object — see below */ },
  "out": {
    "0xb960bECa8623165a3094a629d6A4775857a14d28": 5.0,
    "0x2659deaa71B51124bA6e6493b795486b3027Dbc9": 1.0
  }
}
Field Type Meaning
name string The Xaya account/player name making the move. Must be a string (a non-string aborts the whole block with a CHECK — src/moveprocessor.cpp:89).
move object The game move. If it is not an object (number, string, bool, array), the whole move is ignored — src/moveprocessor.cpp:82-86. An empty object {} is accepted but does nothing.
out object (optional) Map of payout address → WCHI amount. Only two addresses are meaningful to the GSP.

Dev payment & burn (the out map)#

ExtractMoveBasics (src/moveprocessor.cpp:95-105) reads two amounts from out:

  • paidToDev — WCHI sent to the dev address. Mainnet dev address: 0xb960bECa8623165a3094a629d6A4775857a14d28 (proto/roconfig/params.pb.text:26, a Xaya-team multisig). This pays for character creation only.
  • burnt — WCHI sent to the burn address 0x2659deaa71B51124bA6e6493b795486b3027Dbc9 (proto/roconfig/params.pb.text:29). This feeds the burnsale (mint vCHI).

A non-numeric value here aborts the block with a CHECK (xaya::ChiAmountFromJson, src/moveprocessor.cpp:100; test InvalidDataFromXaya, moveprocessor_tests.cpp:136-139). Overpaid/unused amounts are logged as a warning but cause no error (src/moveprocessor.cpp:1355-1358).

Order of processing within one move#

Critical for combining sub-commands in one transaction (MoveProcessor::ProcessOne, src/moveprocessor.cpp:1287-1359):

  1. Account row auto-created if it doesn't exist (uninitialised) — line 1299.
  2. Coin operations (vc) — first, even for uninitialised accounts, so vCHI works as a standalone currency and explicit transfers get priority over implicit spends — line 1312.
  3. Game-start gate: if the GameStart fork is not yet active, processing stops here. (A fork here is a rule change that switches on at a fixed block height — the GameStart fork is the moment the game world opens.) Only coin/burnsale ops work before game start — lines 1317-1318 (test GameStartTests, moveprocessor_tests.cpp:451-501).
  4. DEX operations (x) — line 1322 (work even for uninitialised accounts).
  5. Account update (ainit) — line 1326.
  6. If the account is still uninitialised after step 5, the rest of the move is skipped — lines 1333-1339.
  7. Character updates (c) — before creation, so a freshly created char can't be acted on in the same move — line 1347.
  8. Character creation (nc) — line 1348.
  9. Building updates (b) — line 1350.
  10. Service operations (s) — line 1351.

A single move object may contain any combination of the top-level keys vc, x, a, c, nc, b, s.

Top-level move keys (summary)#

Key Handler What it does Section
vc TryCoinOperation vCHI mint / burn / transfer 2
a TryAccountUpdate account init (choose faction) 3
nc TryCharacterCreation create new character(s) 4
c TryCharacterUpdates all character actions 5
b TryBuildingUpdates building config / transfer 6
s TryServiceOperations in-building services 7
x TryDexOperations DEX trading 8

2. Coin (vCHI) Moves — vc#

Handled by ParseCoinTransferBurn (src/moveprocessor.cpp:475-557) and TryCoinOperation (src/moveprocessor.cpp:2425-2475). The vc value must be an object; anything else is ignored. Sub-operations execute in a fixed order: mint → burn → transfer (test MintBeforeBurnBeforeTransfer, moveprocessor_tests.cpp:394-427).

{ "name": "domob", "move": { "vc": {
  "m": {},
  "b": 90,
  "t": { "alice": 20, "bob": 5 }
}}}
Field Type Meaning / units Validation
m object Mint vCHI via the burnsale. Must be an empty object {}. Mints coins in exchange for the WCHI in out.burn. Non-empty / non-object is ignored (src/moveprocessor.cpp:489-500).
b integer Amount of vCHI to burn (destroy from own balance). Must be a valid coin amount; if it exceeds balance, the burn is dropped (set to 0) — src/moveprocessor.cpp:502-516.
t object Map of recipient name → vCHI amount to transfer. Each invalid entry skipped; transfers that would exceed remaining balance skipped (src/moveprocessor.cpp:518-553).

Detailed rules#

  • Balance accounting: transfers/burns are applied against a running total; once balance is exhausted, later entries are skipped (not failed). Burn has priority over transfers (burn parsed first). CHECK_LE(total, balance) guarantees no overdraft (src/moveprocessor.cpp:555).
  • Self-transfers are no-ops and do not consume balance, so the balance stays available for the other recipients in the same t map (src/moveprocessor.cpp:544-548; test SelfTransfer, moveprocessor_tests.cpp:354-361).
  • Transfer order within t is by key (the JSON object iteration order, which is sorted): test TransferOrder (moveprocessor_tests.cpp:429-447) shows {"z":10,"a":101,"middle":99} from a balance of 100 results in a skipped (too big), middle paid 99, z skipped (only 1 left).
  • Extra fields in vc are tolerated (test ExtraFieldsAreFine, moveprocessor_tests.cpp:298-305).
  • Recipients that don't have an account row get an uninitialised one created automatically (src/moveprocessor.cpp:2465-2472).
  • vCHI ops are allowed before game start and for uninitialised accounts.

The burnsale (minting vCHI from WCHI)#

m: {} plus a WCHI burn (out→burn address) buys vCHI at staged prices (ComputeBurnsaleAmount, src/burnsale.cpp). Stages (proto/roconfig/params.pb.text:49-52):

Stage vCHI available (amount_sold) Price (price_sat, WCHI satoshi per vCHI)
1 10,000,000,000 10,000 (0.0001 WCHI)
2 10,000,000,000 20,000 (0.0002 WCHI)
3 10,000,000,000 50,000 (0.0005 WCHI)
4 20,000,000,000 100,000 (0.001 WCHI)

Minted coins are added to balance, tracked in the account's burnsale_balance proto field, and the "burnsale" money-supply counter is incremented (src/moveprocessor.cpp:2437-2444). Example (test Minting, moveprocessor_tests.cpp:363-378): burning 1,000,000 WCHI in stage 1 mints 10,000,000,000 vCHI. Leftover WCHI after the burnsale caps out is unused.

Cost: the WCHI you put in out.burn. No vCHI cost (it produces vCHI).


3. Account Moves — a#

Handled by TryAccountUpdateMaybeInitAccount (src/moveprocessor.cpp:2366-2423). The only account operation is initialisation (choosing a faction). This is irreversible.

{ "name": "domob", "move": { "a": { "init": { "faction": "b" } } } }
Path Type Meaning Validation
a object The account-update container. Non-object ignored.
a.init object Account initialisation. Non-object ignored (src/moveprocessor.cpp:2370-2371).
a.init.faction string Faction code: "r" Red, "g" Green, "b" Blue (database/faction.cpp:50-55). Must be a valid string; "a" (ancient) and any other value are rejected (src/moveprocessor.cpp:2381-2401).

Rules#

  • init must contain exactly one field (faction); extra fields reject the whole init (src/moveprocessor.cpp:2403-2407; test InvalidInitialisation, moveprocessor_tests.cpp:195-211). Note: extra fields outside init but inside a are fine (test uses {"a":{"x":42,"init":{"faction":"b"}}}).
  • An already-initialised account cannot change faction (src/moveprocessor.cpp:2375-2379; test InitialisationOfExistingAccount, moveprocessor_tests.cpp:213-224).
  • Faction "a" (ANCIENT) is explicitly rejected — only the three player factions are valid (src/moveprocessor.cpp:2395-2397).
  • Init + character creation can be combined in one move (account update runs first): test InitialisationAndCharacterCreation (moveprocessor_tests.cpp:226-242).

Cost: none (vCHI/WCHI). It is a free state change.


4. Character Creation — nc#

Handled by TryCharacterCreation (src/moveprocessor.cpp:110-169). The nc value must be an array. Each element creates one character.

{
  "name": "domob",
  "move": { "nc": [ {}, {} ] },
  "out": { "0xb960bECa8623165a3094a629d6A4775857a14d28": 20.0 }
}
Field Type Meaning Validation
nc array One creation request per element. Non-array ignored (:116-117).
nc[i] object Must be an empty object {}zero fields (:137-141). Faction is taken from the account, not specified here. Non-object or any extra field (even {"faction":"r"}) → that entry skipped (test InvalidCommands, moveprocessor_tests.cpp:519-531).

Rules#

  • The account must already be initialised (have a faction). Otherwise no chars are created (test AccountNotInitialised, moveprocessor_tests.cpp:533-541). The new character inherits the account's faction (src/moveprocessor.cpp:121-123).
  • Cost: character_cost × COIN in WCHI per character, paid via out to the dev address. character_cost = 10 (proto/roconfig/params.pb.text:3), so 10 WCHI per character. The cost is deducted from paidToDev for each created char (src/moveprocessor.cpp:144-166).
  • If paidToDev runs out, processing the nc array returns immediately (no point trying later entries) — src/moveprocessor.cpp:145-154. Underpaying by 1 satoshi rejects creation (test DevPayment, moveprocessor_tests.cpp:584-605).
  • Character limit: character_limit = 20 per account (proto/roconfig/params.pb.text:4). At the limit, creation stops (src/moveprocessor.cpp:156-163; test CharacterLimit, moveprocessor_tests.cpp:634-655).
  • vCHI airdrop (TEST ONLY): each created character grants the owner VCHI_AIRDROP = 1000 vCHI (src/moveprocessor.cpp:56-57, 1366-1371). The source marks this FIXME: For the full game, remove this. Test VChiAirdrop (moveprocessor_tests.cpp:569-582) confirms 2,000 vCHI for two characters.
  • The character is spawned at a faction spawn building (SpawnCharacter). Spawn building IDs: Red→6, Green→4, Blue→5 (proto/roconfig/params.pb.text:33-47).

5. Character Update Moves — c#

Handled by TryCharacterUpdates (src/moveprocessor.cpp:171-249) and PerformCharacterUpdate (src/moveprocessor.cpp:1846-1905). This is the workhorse move; it contains all character actions.

Envelope shapes#

c may be:

  • a single object (one operation), or
  • an array of objects (batched operations).

(src/moveprocessor.cpp:179-192)

Each operation object must have an id identifying the target character. id may be:

  • a single integer ID, or
  • an array of IDs — the same operation is applied to each (src/moveprocessor.cpp:206-247; test MultipleCharacters, moveprocessor_tests.cpp:769-805).
{ "name": "domob", "move": { "c": [
  { "id": 1, "wp": "<encoded>" },
  { "id": [2, 3], "send": "alice" }
]}}

Validation for every c operation#

  • id must parse as a valid ID; invalid IDs in a list are skipped individually (src/moveprocessor.cpp:219-228; test InvalidUpdate, moveprocessor_tests.cpp:876-918).
  • The character must exist (:230-236) and be owned by name (:238-244). A failed owner check does not abort other operations (test OwnerCheck, moveprocessor_tests.cpp:859-874).
  • All actions within one c operation execute in the fixed order defined by PerformCharacterUpdate (see each sub-action below). Many can be combined.

Sub-action execution order (one c entry)#

src/moveprocessor.cpp:1846-1905:

  1. send (transfer character)
  2. prospect (start prospecting)
  3. v (change vehicle)
  4. fit (set fitments)
  5. mine (start mining)
  6. wp (set waypoints)
  7. wpx (extend waypoints)
  8. speed (chosen speed)
  9. fb (found building)
  10. ref (mobile refining)
  11. drop (drop loot)
  12. pu (pick up loot)
  13. eb (enter building)
  14. xb (exit building)

This ordering is deliberate; e.g. mining before waypoints prevents "move + mine" simultaneously; drop before pickup frees cargo; enter before exit means specifying both = just enter.


5.1 Transfer character — send#

MaybeTransferCharacter (src/moveprocessor.cpp:1413-1451).

{ "id": 1, "send": "alice" }
Field Type Meaning
send string Recipient account name. Non-string ignored.

Rules: recipient must be an initialised account of the same faction (:1431-1445) and must not be at the character limit (:1422-1429). Invalid transfers are silently skipped (test InvalidTransfer, moveprocessor_tests.cpp:824-857). A character created in the same move cannot be transferred (updates run before creation; test CreationAndUpdate, moveprocessor_tests.cpp:729-747). Cost: none.


5.2 Movement — wp (set waypoints) and wpx (extend)#

ParseCharacterWaypoints (src/moveprocessor.cpp:559-619), MaybeSetCharacterWaypoints (:1453-1483); extension in ParseCharacterWaypointExtension (:621-659), MaybeExtendCharacterWaypoints (:1485-1499).

{ "id": 1, "wp": "DwAAAB4AAAA...", "speed": 750000 }
Field Type Meaning
wp string | null Encoded waypoint path. null ⇒ stop movement (clear waypoints).
wpx string Encoded waypoints to append to existing path.
speed integer Chosen speed cap (see 5.3).

Waypoint encoding (src/movement.cpp:49-114): the array of {x,y} hex coordinates is JSON-serialised, deflate-compressed (zlib raw, windowBits −15), and base64-encoded. Decoding limits: max uncompressed size MAX_WAYPOINT_SIZE = 1<<20 bytes, max compression ratio 3 (src/movement.cpp:80). The client must produce this exact format; a plain JSON array string is invalid (test BasicWaypoints, moveprocessor_tests.cpp:980-1035 shows "foo", [], {x,y} etc. all rejected).

wp rules:

  • Must be a string or explicit null (:580-587).
  • Character must not be busy (no ongoing operation) — :589-595.
  • Character must not be inside a building:597-604.
  • Setting waypoints stops any current movement and stops mining (StopCharacter, StopMining, :1464-1465).
  • A character with speed == 0 cannot move: waypoints are ignored (it is still stopped) — :1472-1478; test WaypointsWithZeroSpeed (moveprocessor_tests.cpp:1056-1076).
  • wp: null clears movement entirely (test EmptyWaypoints, moveprocessor_tests.cpp:1037-1054).

wpx rules:

  • Only valid if the character is already moving (has existing waypoints), or sets wp in the same move (extension runs after wp) — :638-644; test WaypointExtension (moveprocessor_tests.cpp:1078-1146).
  • wpx: null is invalid (not the "stop" sentinel; only wp honours null).

Cost: none.


5.3 Chosen speed — speed#

MaybeSetCharacterSpeed (src/moveprocessor.cpp:1380-1409).

Field Type Meaning
speed unsigned integer Voluntary speed cap, in the same units as the character's intrinsic speed (milli-tiles/block).

Rules:

  • Only valid if the character has active movement (processed after wp), so you can set waypoints + speed in one move (:1388-1394; test ChosenSpeedWorks, moveprocessor_tests.cpp:1160-1177).
  • Must be > 0 and ≤ MAX_CHOSEN_SPEED = 1,000,000 (1k tiles/block, src/moveprocessor.hpp:59, src/moveprocessor.cpp:1397-1403). Floats, negatives, 0, objects, and 1000001 are all rejected (test ChosenSpeedInvalid, moveprocessor_tests.cpp:1179-1217).

Cost: none.


5.4 Prospecting — prospect#

ParseCharacterProspecting (src/moveprocessor.cpp:833-879), MaybeStartProspecting (:1520-1544).

{ "id": 1, "prospect": {} }
Field Type Meaning
prospect object Must be an empty object {}.

Rules:

  • Non-object or non-empty object rejected (:840-849; test Invalid, moveprocessor_tests.cpp:2937-2963).
  • Character must have the prospecting_blocks ability (:851-855).
  • Must not be busy (:857-862) and not inside a building (:864-870).
  • The region (derived from the character's position) must be prospectable per CanProspectRegion — not currently being prospected, and either never prospected or past prospection_expiry_blocks (5000 mainnet, 100 testnet — proto/roconfig/params.pb.text:9, test_params.pb.text:5). Re-prospecting an expired region clears the old result (:1530-1532; test Reprospecting, moveprocessor_tests.cpp:2913-2935).
  • On success: movement stops, an ongoing operation is created lasting prospecting_blocks blocks, and the region records the prospecting character (:1527-1543; test Success, moveprocessor_tests.cpp:2877-2911).
  • With multiple characters racing for the same region in one block, the first valid one wins (test MultipleCharacters/OrderOfCharactersInAMove, moveprocessor_tests.cpp:3000-3064).

Cost: none (time cost only — the busy period).


5.5 Mining — mine#

ParseCharacterMining (src/moveprocessor.cpp:881-954), MaybeStartMining (:1546-1557).

{ "id": 1, "mine": {} }
Field Type Meaning
mine object Must be an empty object {}.

Rules:

  • Non-object / non-empty object rejected (:887-897; test InvalidMoveJson, moveprocessor_tests.cpp:3139-3153).
  • Character must have the mining ability (:899-903), not be busy (:905-910), not be inside a building (:912-917), and not be moving (has_movement() ⇒ reject) — :925-930.
  • Region must be prospected (:932-940) and have resource left > 0 (:942-951).
  • Sets mining.active = true. If the move both sets waypoints and mines, mining is processed first then waypoints clear it ⇒ ends up not mining (test MiningAndWaypointsInSameMove, moveprocessor_tests.cpp:3197-3213).
  • Setting wp: null (stop) clears mining.active (test WaypointsStopMining, moveprocessor_tests.cpp:3108-3122).

Cost: none.


5.6 Change vehicle — v#

ParseChangeVehicle (src/moveprocessor.cpp:981-1038), MaybeChangeVehicle (:1591-1613).

{ "id": 1, "v": "rv st" }
Field Type Meaning
v string Internal item code of the target vehicle, e.g. Raider (rv st), Looter (rv s), Barracuda (bv st), Scarab (gv st).

Rules:

  • Must be a string naming a valid vehicle item (data->has_vehicle()) — :1021-1026.
  • Character must have full HP (armour and shield both at max) — :992-998, HasFullHp (:963-977). Test NoFullHp (moveprocessor_tests.cpp:1576-1599).
  • Character must be inside a building (:1000-1006) that is not a foundation (:1012-1019).
  • The owner must own a unit of that vehicle in the building inventory (the vehicle the character is currently in does not count) — :1028-1035; test ChangeToSameType (moveprocessor_tests.cpp:1673-1699).

On execute (:1591-1613): the character's current inventory is dropped to the building inventory, all fitments removed (returned to inventory), the old vehicle is added back to inventory, the new vehicle removed from inventory, and stats are re-derived (HP reset to the new max). Test BasicUpdate (moveprocessor_tests.cpp:1615-1636). Cost: none (no fee — just the item swap).


5.7 Set fitments — fit#

ParseSetFitments (src/moveprocessor.cpp:1040-1123), MaybeSetFitments (:1615-1637).

{ "id": 1, "fit": ["lf gun", "lf scanner"] }

(here Light Rail Gun (lf gun) and Graviton Spectrometer (lf scanner)).

Field Type Meaning
fit array of strings The complete desired fitment list (replaces all current fitments). A fitment is a piece of equipment slotted onto the vehicle (weapon, scanner, cargo expander, etc.). Each must be a valid fitment item code.

Rules:

  • Must be an array; each element a string naming a valid fitment (data->has_fitment()) — :1046, :1076-1093. Non-array, object values, and non-fitment items reject the whole command (tests InvalidFormat/InvalidItems, moveprocessor_tests.cpp:1290-1326).
  • Character must have full HP (:1049-1055), be inside a building (:1057-1063), and that building must not be a foundation (:1067-1073).
  • Required fitments must be available in the owner's building inventory — existing fitments already on the character count toward availability (they're conceptually removed then re-added) — :1095-1111; test ExistingFitmentsReused (moveprocessor_tests.cpp:1440-1456). The check happens before the inventory-drop, so items in the character's cargo don't help (test ItemsNotAvailable, moveprocessor_tests.cpp:1384-1403).
  • The combination must be valid for the vehicle (CheckVehicleFitments) — slot/complexity limits — :1113-1120; test InvalidFitments (moveprocessor_tests.cpp:1405-1418).

On execute: drop cargo to building, remove old fitments, consume new ones from building inventory, attach them, re-derive stats. Vehicle change (v) is processed before fit, so you can change vehicle and refit in one move (test ChangeVehicleBeforeFitmentsAndPickup, moveprocessor_tests.cpp:1701-1731). Cost: none.


5.8 Found building (place foundation) — fb#

ParseFoundBuilding (src/moveprocessor.cpp:1169-1251), MaybeFoundBuilding (:1639-1669).

{ "id": 1, "fb": { "t": "b r", "rot": 3 } }
Field Type Meaning
fb object Foundation placement request. Exactly two fields (t, rot) — :1180. A foundation is the empty shell of a building you place on the map and later finish by dropping in resources.
fb.t string Building type code, e.g. a Refinery (b r). Must be a valid, constructible building.
fb.rot unsigned integer 0–5 Shape rotation in 60° steps (ParseBuildingConfig, :1153-1159).

Rules:

  • fb must have exactly the two fields and parse (:1180-1184; test InvalidFormat, moveprocessor_tests.cpp:1751-1793 rejects bad type, rot: -1, rot: 6, rot: 4.0, extra/missing fields).
  • Character must not be busy (:1186-1191) and not be inside a building (:1193-1199).
  • The building type must have construction data (has_construction()) — types like itemmaker that cannot be constructed are rejected (:1201-1206; test UnconstructibleBuilding, moveprocessor_tests.cpp:1821-1830).
  • If the building's construction specifies a faction, it must match the character's faction (:1208-1220; test FactionCheck, moveprocessor_tests.cpp:1860-1883).
  • The character must hold the foundation resources in cargo (construction.foundation map) — e.g. real refinery b r needs Agarite (mat a) ×6,400,000 and Borolium (mat b) ×1,600,000 (proto/roconfig/buildings/b_r.pb.text:35-41). Insufficient ⇒ reject (:1222-1235; test NotEnoughResources, moveprocessor_tests.cpp:1832-1842).
  • The footprint must fit on the map (CanPlaceBuilding) with the character temporarily removed from obstacle checks (:1237-1248; test CannotPlaceBuilding, moveprocessor_tests.cpp:1844-1858).

On execute (:1639-1669): a new building is created (foundation = true) at the character's position, foundation resources are consumed from cargo, and the character automatically enters the new foundation. You can then drop more resources (drop runs after fb) and/or exit (xb runs last). Tests Success, FoundationBeforeDrop, FoundationBeforeExit (moveprocessor_tests.cpp:1885-1960). Cost: the foundation resources (items), no vCHI.

See Section 6.4 for how a foundation becomes a finished building.


5.9 Mobile refining — ref#

TryMobileRefining (src/moveprocessor.cpp:407-423), parsed by ServiceOperation::ParseMobileRefining (src/services.cpp:1245-1275). This lets a character with the refining proto refine ore in the field (no building needed).

{ "id": 1, "ref": { "i": "raw a", "n": 12 } }
Field Type Meaning
ref object Refining request. Exactly two fields (:1254).
ref.i string Item code of the resource to refine (e.g. Trimideum raw a).
ref.n integer Amount of raw resource to consume. Must be a multiple of the per-step input.

Rules (shared with the building refining service, 7.1):

  • Item must exist and be refinable (has_refines()) — src/services.cpp:176-195.
  • n > 0 and an exact multiple of input_units (adjusted by the character's refining efficiency modifier) — src/services.cpp:197-225. Otherwise invalid (test Invalid, moveprocessor_tests.cpp:2284-2298: n:10 rejected when one step is not 10).
  • The character must hold ≥ n of the resource in cargo.
  • Cost: steps × refData.cost() vCHI, paid from the owner's account, with no service fee (mobile, not in a building) — src/services.cpp:986-997. Test Works (moveprocessor_tests.cpp:2300-2312): refining 12 of test ore (per-step 6) = 2 steps, costs 20 vCHI, yields outputs.
  • Refining always produces less cargo volume than it consumes, so cargo never overflows (src/services.cpp:255-266).
  • Processed before drop/pu, so outputs can be dropped and freed cargo reused (tests BeforeDrop, BeforePickup, moveprocessor_tests.cpp:2314-2364).

5.10 Drop loot — drop#

MaybeDropLoot (src/moveprocessor.cpp:1749-1793), parsed by ParseDropPickupFungible (:812-831).

{ "id": 1, "drop": { "f": { "raw a": 5, "mat b": 2 } } }
Field Type Meaning
drop object Drop container. Exactly one field, f (:824-828).
drop.f object Map of item code → quantity to drop from the character's cargo.

Rules:

  • f must be an object; the container must have only f (extra fields ⇒ ignore) — :818-828; test InvalidDrop (moveprocessor_tests.cpp:2419-2448).
  • Unknown item codes are skipped (ParseFungibleQuantities, :777-808); invalid quantities skipped; dropping more than held drops only what's held (MoveFungibleBetweenInventories, :1684-1745; test BasicDrop, moveprocessor_tests.cpp:2481-2513).
  • Destination depends on location:
    • In a foundation ⇒ items go to the building's construction_inventory, and this may trigger construction start if requirements are now met (:1764-1774; tests DropInFoundation/StartBuildingConstruction).
    • In a finished building ⇒ items go to the owner's building inventory (:1776-1782).
    • On the map ⇒ items go to ground loot at the character's tile (:1784-1792).

Cost: none.


5.11 Pick up loot — pu#

MaybePickupLoot (src/moveprocessor.cpp:1795-1844).

{ "id": 1, "pu": { "f": { "raw a": 100 } } }
Field Type Meaning
pu object Pickup container. Exactly one field, f.
pu.f object Map of item code → quantity to pick up.

Rules:

  • Same parsing as drop (ParseDropPickupFungible); test InvalidPickUp (moveprocessor_tests.cpp:2450-2479).
  • Limited by the character's free cargo space; partial pickups happen if cargo fills (:1806, maxSpace arg to MoveFungibleBetweenInventories). Items are processed alphabetically by code, which matters when cargo fills mid-pickup (test OrderOfItems, moveprocessor_tests.cpp:2619-2650).
  • Source:
    • In a foundation ⇒ cannot pick up (no inventory) — :1816-1823; test PickupInFoundation.
    • In a finished building ⇒ from the owner's building inventory (:1826-1832).
    • On the map ⇒ from ground loot (:1836-1842).
  • drop runs before pu, so you can drop to free cargo then pick up in one move (test RelativeOrder, moveprocessor_tests.cpp:2594-2617).

Cost: none.


5.12 Enter building — eb#

ParseEnterBuilding (src/moveprocessor.cpp:661-727), MaybeEnterBuilding (:1501-1509).

{ "id": 1, "eb": 100 }
Field Type Meaning
eb integer | null Building ID to queue entering, or null to cancel a queued enter.

Rules:

  • If already inside a building, cannot queue entering another (:670-677; test AlreadyInBuilding, moveprocessor_tests.cpp:2098-2114).
  • null clears the queued enter target (:681-689; test ValidClear).
  • Otherwise must be a valid existing building ID (:691-707). Invalid values ({}, strings, 0, -10, nonexistent IDs) are rejected and leave the prior state unchanged (tests InvalidSet/InvalidClear, moveprocessor_tests.cpp:1979-2038).
  • Can only enter ancient buildings or buildings of the character's own faction (:709-720).
  • Setting "enter" only sets a flag; it is valid even when the character is busy (test BusyIsFine, moveprocessor_tests.cpp:2080-2096). Actual entering happens later when the character reaches the building.

Cost: none.


5.13 Exit building — xb#

ParseExitBuilding (src/moveprocessor.cpp:729-767), MaybeExitBuilding (:1511-1518).

{ "id": 1, "xb": {} }
Field Type Meaning
xb object Must be an empty object {}.

Rules:

  • Must be an empty object; non-empty/non-object rejected (:734-744; test Invalid, moveprocessor_tests.cpp:2118-2143).
  • Character must not be busy (:746-752) and must currently be inside a building (:754-760).
  • On success the character is placed on a random free tile within ~5 of the building centre (LeaveBuilding; test Valid, moveprocessor_tests.cpp:2180-2198).
  • eb is processed before xb; sending both while inside = just enter (the exit becomes invalid). When inside and sending xb+eb, the result is exit only / enter-target cleared (test EnterAndExitWhenInside, moveprocessor_tests.cpp:2200-2217). Because xb runs last, you can pick up items from inside and exit in the same move (test InventoryBeforeExit, moveprocessor_tests.cpp:2219-2241).

Cost: none.


6. Building Moves — b#

TryBuildingUpdates (src/moveprocessor.cpp:343-405), TryBuildingUpdate (:326-341). Only a building's owner can update it. Like c, the b value can be a single object or an array of objects, each with an id.

{ "name": "domob", "move": { "b": [
  { "id": 102, "sf": 10, "xf": 250 },
  { "id": 102, "send": "alice" }
]}}
Field Type Meaning
b[i].id integer Building ID. Must exist (:379-384).
b[i].sf unsigned integer Service-fee percent (see 6.1).
b[i].xf unsigned integer DEX-fee basis points (see 6.2).
b[i].send string Transfer building to another account (see 6.3).

Common validation#

  • id must parse (:371-377).
  • Ancient buildings cannot be updated by anyone (:386-392; test AncientCannotBeUpdated, moveprocessor_tests.cpp:3295-3305).
  • Only the owner may update (:394-401; test NotOwner, moveprocessor_tests.cpp:3307-3317).
  • A single b entry may set sf, xf, and send together (TryBuildingUpdate, :326-341). Config updates and transfer are independent.

6.1 Service fee — sf#

MaybeUpdateServiceFee (src/moveprocessor.cpp:258-273).

  • Type must be an unsigned integer (floats, negatives, strings rejected) — :261.
  • Range 0 … MAX_SERVICE_FEE_PERCENT = 1000 percent (src/moveprocessor.cpp:48, :265-269). Above 1000 rejected (test SetServiceFee, moveprocessor_tests.cpp:3359-3414).
  • This is the markup charged to other-faction players using the building's services. The percentage is applied to the base cost, rounded up (src/services.cpp:1011-1015).

6.2 DEX fee — xf#

MaybeUpdateDexFee (src/moveprocessor.cpp:279-294).

  • Unsigned integer, range 0 … MAX_DEX_FEE_BPS = 3000 basis points (30%) — src/moveprocessor.cpp:54, :286-290. (A basis point is one hundredth of a percent, so 100 bps = 1%, and 3000 bps = 30%.) Test SetDexFee (moveprocessor_tests.cpp:3416-3471).
  • This is the building owner's cut on DEX trades in addition to the base fee dex_fee_bps = 300 (3% mainnet; proto/roconfig/params.pb.text:19). See Section 8.

Delayed application of config changes#

sf/xf changes do not apply immediately. They are scheduled as an ongoing operation that takes effect after building_update_delay blocks — 120 blocks mainnet, 10 testnet (proto/roconfig/params.pb.text:18, test_params.pb.text:9). PerformBuildingConfigUpdate (src/moveprocessor.cpp:1907-1921). Multiple config updates to the same building in one block create multiple scheduled ops (test ArrayUpdate, moveprocessor_tests.cpp:3319-3357).

6.3 Transfer building — send#

MaybeTransferBuilding (src/moveprocessor.cpp:298-324).

  • send must be a string naming an initialised account of the same faction as the building (:307-321). Uninitialised / wrong-faction / nonexistent / non-string all rejected (test Transfer, moveprocessor_tests.cpp:3473-3516).
  • Transfer applies immediately (PerformBuildingTransfer, :1923-1930), unlike config changes.

Cost (all building moves): none.

6.4 Building construction (foundation → finished)#

Construction is not a b move; it is driven by dropping resources. A foundation (created by fb) accumulates resources in its construction_inventory via drop. When the full requirement (construction.full_building map) is met, construction auto-starts and runs for construction.blocks blocks (MaybeStartBuildingConstruction, src/moveprocessor.cpp:1771-1774; test StartBuildingConstruction, moveprocessor_tests.cpp:2771-2836). Example real refinery b r (proto/roconfig/buildings/b_r.pb.text): foundation costs mat a×6.4M + mat b×1.6M; full building additionally needs mat a×128M + mat b×32M; blocks: 10.


7. Service Operations — s#

TryServiceOperations (src/moveprocessor.cpp:425-446), ServiceOperation::Parse (src/services.cpp:1169-1243). The s value must be an array of operation objects. Each operation happens inside a building identified by b, with a type t.

{ "name": "domob", "move": { "s": [
  { "b": 100, "t": "ref", "i": "raw a", "n": 6 },
  { "b": 100, "t": "fix", "c": 200 }
]}}

Common envelope#

Field Type Meaning
b integer Building ID. Must exist and not be a foundation (src/services.cpp:1192-1205).
t string Operation type: ref, fix, rve, cp, bld.

The account must be initialised; the building must offer the requested service (offered_services in the building config) — IsFullyValid/IsSupported (src/services.cpp:1064-1103). The account must have enough vCHI for base + fee (:1091-1100).

Cost model (all s operations)#

GetCosts (src/services.cpp:986-1016):

  • Base cost = operation-specific (below), paid in vCHI by the account.
  • Service fee = ceil(base × sf% / 100) paid to the building owner, but fee is zero if the building is ancient or owned by the operator themselves (:999-1009). On execute, base+fee is deducted; fee credited to owner (:1127-1145).

s is processed after coin ops and character updates (tests ServicesAfterCoinOperations, ServicesAfterCharacterUpdates, moveprocessor_tests.cpp:3597-3643). Test Works (moveprocessor_tests.cpp:3549-3571) shows a full multi-op example.

7.1 Refining — ref#

RefiningOperation (src/services.cpp:68-266). Same as mobile refining (5.9) but in a building.

Field Type Meaning
i string Resource to refine.
n integer Amount; must be an exact multiple of the per-step input units.

Object must have exactly 4 fields (b,t,i,n) — ParseItemAmount, src/services.cpp:1147-1167. Base cost = steps × refData.cost(). Consumes the resource from the building inventory, produces refined outputs.

7.2 Armour repair — fix#

RepairOperation (src/services.cpp:273-437).

Field Type Meaning
c integer Character ID to repair (must be owned by the operator, inside this building, not busy, with missing armour).

Object must have exactly 3 fields (b,t,c) — :426-428. Base cost = ceil(missingArmourHp × armour_repair_cost_millis / 1000) vCHI. With armour_repair_cost_millis = 100 (proto/roconfig/params.pb.text:12) this is 1 vCHI per 10 HP repaired (src/services.cpp:376-391). Repair takes ceil(missingHp / armour_repair_hp_per_block) blocks, with armour_repair_hp_per_block = 100 (proto/roconfig/params.pb.text:11, src/services.cpp:404-418). Shields regenerate naturally and are not part of repair.

7.3 Reverse engineering — rve#

RevEngOperation (src/services.cpp:444-594). Converts artefacts into blueprints.

Field Type Meaning
i string Artefact type, e.g. Ancient Artefact (common) art c.
n integer Number of artefacts to reverse-engineer.

Exactly 4 fields. Base cost = n × revEngData.cost(). Each artefact is consumed and, per a probability roll, may yield a blueprint chosen from the artefact's possible_outputs (filtered to neutral items + the operator's faction). Success chance decreases as more of that item type have already been found (RevEngSuccessChance(existingCount), :580-593) — a global rarity mechanic. Uses randomness (xaya::Random).

7.4 Blueprint copy — cp#

BlueprintCopyOperation (src/services.cpp:601-735). A blueprint is the recipe used to construct a vehicle or item. An original blueprint can be copied repeatedly; this operation makes single-use copies (item code suffix bpc, "blueprint copy") from an original.

Field Type Meaning
i string Original blueprint item (must be an original blueprint).
n integer Number of copies to make.

Exactly 4 fields. Base cost = n × bp_copy_cost × complexity (bp_copy_cost = 1 mainnet; proto/roconfig/params.pb.text:13). The original is consumed (1) and an ongoing op produces n copies, one every construction_blocks × complexity blocks (GetBpCopyBlocks, :739-751; construction_blocks = 1 mainnet, :16).

7.5 Construction (vehicle / item) — bld#

ConstructionOperation (src/services.cpp:763-951). Builds vehicles or fitments from blueprints.

Field Type Meaning
i string Blueprint (original or copy bpc).
n integer Number of items to construct.

Exactly 4 fields. Requires the building to offer vehicle_construction (for vehicle outputs) or item_construction (otherwise) — :833-843. Base cost = n × construction_cost × complexity (construction_cost = 1 mainnet, :15). Consumes the construction_resources per output and the blueprint(s): 1 original or n copies (:884-898, :931-934). If the output item has a faction, it must match the operator's faction (:854-867). Each item takes construction_blocks × complexity blocks (GetConstructionBlocks, :955-960).


8. DEX Trading Moves — x#

TryDexOperations (src/moveprocessor.cpp:448-473), DexOperation::Parse (src/trading.cpp:607-682). The DEX (decentralised exchange) is the in-game marketplace: players place buy orders (bids) and sell orders (asks) for items, priced in vCHI, and the GSP matches them automatically. The x value must be an array. All DEX activity happens inside a (non-foundation) building, where each account has a per-building inventory and orders are matched per building+item. DEX ops run before character updates and after coin ops (tests AfterCoinOperations/BeforeCharacterUpdates, moveprocessor_tests.cpp:3696-3745).

When you place an order, the cost is held aside (escrowed) until the order fills or is cancelled: a bid escrows the vCHI you'd pay, an ask escrows the items you'd sell.

The op type is inferred from which fields are present (the object size disambiguates):

Op Required fields Field count
Transfer b, i, n, t 4
Bid (buy) b, i, n, bp 4
Ask (sell) b, i, n, ap 4
Cancel c 1

Common fields:

Field Type Meaning
b integer Building ID (must exist, not a foundation — src/trading.cpp:111-139).
i string Item code being traded.
n integer Quantity (1 … 2^50).
t string (transfer) Recipient account name.
bp integer (bid) Buy price, vCHI per unit.
ap integer (ask) Sell price, vCHI per unit.
c integer (cancel) Order ID to cancel.
{ "name": "domob", "move": { "x": [
  { "b": 100, "i": "raw a", "n": 10, "t": "alice" },
  { "b": 100, "i": "raw a", "n": 50, "ap": 25 },
  { "b": 100, "i": "raw a", "n": 30, "bp": 20 },
  { "c": 12345 }
]}}

8.1 Item transfer — t#

TransferOperation (src/trading.cpp:156-225). Moves items between two accounts' inventories inside the same building. The sender must hold ≥ n of i in that building (:178-195). Self-transfer is a no-op. Recipient account auto-created if needed. Cost: none (no fee on transfers). Test Works (moveprocessor_tests.cpp:3677-3694).

8.2 Bid (buy order) — bp#

BidOperation (src/trading.cpp:344-418). Places a buy order at bp vCHI/unit. To be valid the account must hold the full n × bp vCHI (:360-376). The bid first matches existing asks at or below bp (QueryToMatchBid); matched items are credited to the buyer's building inventory and the seller is paid (minus fees). Matched units are charged at the resting ask's price, not at bp — so if you bid higher than the best ask, you pay the lower ask price and keep the difference (:386-396). Any unfilled remainder is placed as a resting bid, and only that remainder has its vCHI escrowed at bp/unit (:417).

8.3 Ask (sell order) — ap#

AskOperation (src/trading.cpp:423-500). Places a sell order at ap vCHI/unit. The account must hold at least n of i in the building (:439-456). The ask first matches existing bids at or above ap; matched units are paid at the resting bid's price, not at ap — so if you ask lower than the best bid, you are paid the higher bid price (:477-478). The seller is paid (minus fees) and items move to the buyer. Items for the unfilled remainder are escrowed when that remainder rests on the book (:499).

DEX fee model#

PayToSellerAndFee (src/trading.cpp:291-328). On each fill, the seller pays a fee:

  • totalBps = base dex_fee_bps (300 = 3% mainnet) + building owner's xf.
  • Total fee = ceil(cost × totalBps / 10000) (rounded up, so splitting orders can't dodge fees; max 1 vCHI rounding per fill).
  • Owner's portion = floor(cost × ownerBps / 10000) (rounded down).
  • The remainder beyond the owner's cut is effectively burned (paid neither to seller nor owner). Ancient buildings have ownerBps = 0 enforced (:304-305).

8.4 Cancel order — c#

CancelOrderOperation (src/trading.cpp:507-593). Cancels an order by ID; only the order's owner can cancel (:528-547). On cancel, escrow is refunded: a bid refunds quantity × price vCHI to the account; an ask refunds the items to the account's building inventory (:559-593).

Malformed DEX ops (e.g. {"x":"invalid"}, wrong field counts) are logged and skipped; valid ones in the same array still execute (test Works, moveprocessor_tests.cpp:3677-3694; src/moveprocessor.cpp:465-471).


9. God-Mode / Admin Moves (cmdgod)#

Admin only. These are not player moves. They arrive via the separate admin command channel (ProcessAdmin, src/moveprocessor.cpp:1265-1285), not the normal move array. Each admin entry is {"cmd": {...}}; the GSP reads cmd.god.

[ { "cmd": { "god": { "teleport": [ { "id": 1, "pos": { "x": 5, "y": -42 } } ] } } } ]

Gating: when god-mode is allowed#

HandleGodMode (src/moveprocessor.cpp:2346-2364) executes god commands only if params.god_mode is true; otherwise the command is logged and ignored (:2352-2356).

IMPORTANT — current deployment: god_mode: true is set in the base proto/roconfig/params.pb.text:31, which is the mainnet base config. As built, god mode is enabled on mainnet/Polygon, testnet, and regtest in this tree. The unit test GodModeDisabledTests (moveprocessor_tests.cpp:4088-4127) still asserts god mode is disabled on Chain::MAIN, so this test would fail against the current config — see Open Questions. The original design intent was god-mode = regtest-only.

The structure of admin processing: ProcessAdmin requires a JSON array (else CHECK abort — test InvalidAdminFromXaya, moveprocessor_tests.cpp:142-149); each element must be an object with a cmd. A non-object cmd or missing god is a silent no-op (test AllAdminDataAccepted, moveprocessor_tests.cpp:167-178). The whole god object processes these sub-commands in order (:2358-2363):

9.1 setchar — set vehicle/fitments#

MaybeGodSetChar (src/moveprocessor.cpp:2278-2342). Array of {id, v?, f?}.

  • id — character ID.
  • v — vehicle code (validated as a vehicle).
  • f — array of fitment codes (validated; invalid ones skipped).
  • Re-derives all stats and resets HP to max. Runs before sethp so sethp can override HP (:2358).

9.2 teleport — move characters#

MaybeGodTeleport (src/moveprocessor.cpp:1950-1990). Array of {id, pos} where pos is {x,y}. Teleports the character and stops its movement. Invalid entries skipped (test Teleport/InvalidTeleport, moveprocessor_tests.cpp:3765-3807). A pos with extra keys like z is invalid.

9.3 sethp — set HP / max HP#

MaybeGodAllSetHp (src/moveprocessor.cpp:2050-2058). Object with b (buildings) and c (characters) arrays. Each entry {id, a?, s?, ma?, ms?}:

  • a armour, s shield (current HP); ma max armour, ms max shield.
  • Only valid unsigned-int values are applied; others (floats, negatives, bools) ignored per-field (test SetHp, moveprocessor_tests.cpp:3809-3879).

9.4 build — create buildings#

MaybeGodBuild (src/moveprocessor.cpp:2063-2140). Array of {t, o, c, rot} (exactly 4 fields):

  • t building type, c centre {x,y}, rot 0–5.
  • o owner: a name string (account must be initialised; faction taken from it) or null for an ancient building.
  • Does not check placement validity (so tests can place freely) — :2120-2124. Buildings are created already-finished (finished_height set). Test Build (moveprocessor_tests.cpp:3881-3938).

9.5 drop — spawn loot#

MaybeGodDropLoot (src/moveprocessor.cpp:2168-2243). Array of tiles, each exactly 2 fields: a fungible map plus one target — either pos: {x,y} (ground) or building: {id, a} (building inventory for account a). Creates items from nothing. Tests ValidDropLoot/InvalidDropLoot (moveprocessor_tests.cpp:3940-4041).

9.6 giftcoins — mint vCHI to accounts#

MaybeGodGiftCoins (src/moveprocessor.cpp:2249-2276). Object of name → vCHI amount. Credits balances (creating accounts if needed) and increments the "gifted" money-supply counter. Non-integer amounts skipped (test GiftCoins, moveprocessor_tests.cpp:4043-4082).


10. Quick Reference: Constants & Limits#

Constant Value (mainnet) Source
COIN (1 vCHI in satoshi) 100,000,000 database/amount.hpp:31
character_cost (WCHI per char) 10 params.pb.text:3
character_limit (per account) 20 params.pb.text:4
VCHI_AIRDROP per new char (TEST) 1,000 moveprocessor.cpp:57
MAX_CHOSEN_SPEED 1,000,000 moveprocessor.hpp:59
MAX_SERVICE_FEE_PERCENT 1,000 (%) moveprocessor.cpp:48
MAX_DEX_FEE_BPS (owner) 3,000 (bps) moveprocessor.cpp:54
base dex_fee_bps 300 (3%) params.pb.text:19
building_update_delay 120 blocks params.pb.text:18 (testnet 10)
armour_repair_cost_millis 100 (1 vCHI/10 HP) params.pb.text:12
armour_repair_hp_per_block 100 params.pb.text:11
bp_copy_cost / construction_cost 1 / 1 params.pb.text:13 / :15
bp_copy_blocks / construction_blocks 1 / 1 params.pb.text:14 / :16
prospection_expiry_blocks 5,000 params.pb.text:9 (testnet 100)
min_region_ore / max_region_ore 2,000,000 / 100,000,000 params.pb.text:21-22 (testnet 10 / 20)
MAX_ID (exclusive) 999,999,999 jsonutils.cpp:40
MAX_QUANTITY 2^50 database/inventory.hpp:54
MAX_COIN_AMOUNT 100,000,000,000 jsonutils.cpp:38
MAX_WAYPOINT_SIZE (uncompressed) 1,048,576 bytes movement.cpp:45

Mainnet vs testnet/regtest differences#

proto/roconfig/test_params.pb.text (merged on TEST/MUMBAI and REGTEST/GANACHE): prospection_expiry_blocks 100, bp_copy_cost 100, bp_copy_blocks 10, construction_cost 100, construction_blocks 10, building_update_delay 10, dex_fee_bps 1000 (10%), min/max_region_ore 10/20, a small prize list. Chain routing: MAIN/POLYGON → base only; TEST/MUMBAI → +testnet merge; REGTEST/GANACHE → +testnet +regtest merge (proto/roconfig.cpp:88-110).


11. Open Questions#

  1. God-mode enabled on mainnet (build vs test contradiction). params.pb.text:31 has god_mode: true in the mainnet base config, so as-built the admin god commands run on mainnet/Polygon. But GodModeDisabledTests (moveprocessor_tests.cpp:4088-4127) asserts god mode is disabled on Chain::MAIN. Either the test is now stale or the config change is a deliberate/accidental override. A designer needs to confirm whether god-mode admin commands are intended to be live on production Polygon.

  2. vCHI airdrop on character creation. VCHI_AIRDROP = 1000 per new character is flagged FIXME: For the full game, remove this (moveprocessor.cpp:1366). It is unclear whether this is still wanted in the AAA relaunch economy or must be removed.

  3. Who receives the admin command channel. The source gates god-mode by params.god_mode but does not itself restrict who may submit admin commands — that is enforced by the surrounding Xaya/GSP daemon configuration (admin commands come from a privileged channel), which is outside moveprocessor.cpp. The exact provenance/authorisation of admin commands should be documented from the daemon layer.

  4. GameStart fork activation height on Polygon. Processing of all non-coin moves is gated on the GameStart fork being active (moveprocessor.cpp:1317). The actual activation height/parameters live in src/forks.* and the deployment config, not in the move processor; the precise mainnet value should be cross-checked there.

  5. Exact DEX order-matching priority (price-time priority, tie-breaking) is in database/dex.cpp (QueryToMatchBid/QueryToMatchAsk), not in the move processor. This reference covers the move schema; the matching algorithm itself warrants a dedicated section sourced from the DEX table code.