Skip to main content

Resolution results

HTTP 202 means the server stored a plan, nothing more — the actions have not resolved yet. Their results turn up in the next state.data.events array.

Start from event_type, then read the fields listed for that event:

{
"event_id": "3f360e7e-d9bd-4f48-9a51-5cf751b04075",
"tick": 10583,
"event_type": "UNIT_MOVE_FAILED",
"reason_code": "MOVE_BLOCKED_TERRAIN",
"actor_id": "9d3e4941-2816-4a39-a220-df8cd95e877d",
"position": [120, 85]
}
Looking forGo to
Unit self-destructionUnit lifecycle events
Core self-destructionEconomy and Core events
HP recoveryHealing events
Core damage, repair, or spawningEconomy and Core events
Harvesting or depositingWorker events
Sweeps, shots, and damageCombat events
Unit movement or Core migrationMovement events
Beacon actions or respawningBeacon and respawn events

Field rules

FieldPresence and meaning
event_idAlways present. Use this UUID to avoid processing the same event twice after reconnecting.
tickAlways present. Tick in which the result was resolved.
event_typeAlways present. Read this before the other optional fields.
reason_codePresent only when the event has a defined reason. Successful events do not send an empty string.
actor_idThe player's Core or Unit whose action produced the result, when there is one.
target_idThe affected Core or Unit, when exposing it is part of the result.
positionCell relevant to the resolved result. Its exact meaning is stated in each catalog row.
valuesEvent-specific object. Keys are stable per event row; the object is omitted when no values apply.

Optional fields that do not apply are left out rather than sent as null.

Unit lifecycle events

event_typereason_codeIDs and positionvaluesMeaning
UNIT_SELF_DESTRUCTEDabsentactor_id: removed Unit; position: its final cellabsentThe owner deliberately removed the Unit before movement and spawn pricing.
WORKER_CARGO_DROPPEDabsentactor_id: dead Worker; position: its final cell{amount: int}The Worker's complete cargo amount was added to the resource pile on this cell.

Self-destruction also increments the owner's units_lost. It creates no attack damage or destruction participation. A Beacon carrier additionally receives BEACON_DROPPED_ON_DEATH.

Healing events

event_typereason_codeIDs and positionvaluesMeaning
UNIT_HEAL_SUCCEEDEDabsentactor_id: healed Unit; position: shared Core cell{amount: int, hp: int, cost: int}The Unit recovered amount HP, now has hp, and spent the equal cost.
UNIT_HEAL_FAILEDHP_FULL, NOT_AT_OWN_CORE, CORE_MOVING, or INSUFFICIENT_RESOURCESactor_id: Unit; position: Unit cellabsentPost-combat Unit healing could not start; nothing was spent.
CORE_HEAL_SUCCEEDEDabsentactor_id: healed Core; position: Core cell{amount: int, hp: int, cost: int}The Core recovered amount HP, now has hp, and spent the equal cost.
CORE_HEAL_FAILEDHP_FULL or INSUFFICIENT_RESOURCESactor_id: Core; position: Core cellabsentPost-combat Core healing could not start; nothing was spent.

A Unit killed by combat is gone before the healing phase, so it produces no heal event and spends no resource. unit_hp_recovered and core_hp_recovered count actual HP restored over the player's lifetime.

Economy and Core events

event_typereason_codeIDs and positionvaluesMeaning
CORE_DAMAGEDATTACKtarget_id: Core; position: Core cell{damage: int, shield_damage: int, hp_damage: int}Total combat damage and how it was split between shield and HP.
CORE_DESTROYEDATTACK or SELF_DESTRUCTtarget_id: destroyed Core; position: destruction cellFor ATTACK, {destroyed_by: string[]} when participants can be named; absent for SELF_DESTRUCTThe player's Core and remaining Units were removed. A replacement spawn is attempted later in the same Tick.
CORE_RESOURCE_OVERFLOW_DESTROYEDabsentactor_id: Core; position: Core cell{amount: int, capacity: int}Population fell and resources above the new capacity were destroyed.
CORE_RESOURCES_CAPTUREDabsentactor_id: winner's surviving Core; target_id: destroyed Core; position: destruction cell{amount: int, available: int, destroyed: int, capacity: int}The highest-damage player stored amount from the victim's available inventory; destroyed did not fit. amount may be zero. No event is emitted if the winner's Core also died.
CORE_ACTION_FAILEDCORE_NOT_MOVING or CORE_ALREADY_MOVINGactor_id: Core; position: Core cellabsentCANCEL_MOVE was used on a normal Core, or an incompatible Core action was used during migration.
CORE_REPAIR_FAILEDSHIELD_FULL or INSUFFICIENT_RESOURCESactor_id: Core; position: Core cellabsentOne-shield repair could not be applied.
CORE_REPAIR_SUCCEEDEDabsentactor_id: Core; position: Core cell{shield: int, cost: int}Shield after repair and resources spent.
CORE_SPAWN_FAILEDCELL_UNIT_LIMITactor_id: Core; position: Core cell{limit: int}Core cell has reached the occupiable-entity limit.
CORE_SPAWN_FAILEDINSUFFICIENT_RESOURCESactor_id: Core; position: Core cell{required: int}Resources are below the actual dynamic price settled from post-combat population.
CORE_SPAWN_FAILEDDETERMINISTIC_ID_COLLISIONactor_id: Core; position: Core cellabsentDefensive collision check rejected the deterministic spawn ID.
CORE_SPAWN_SUCCEEDEDabsentactor_id: Core; target_id: new Unit; position: Core cell{unit_type: UnitType, cost: int}One Unit was created; cost is the actual dynamic price charged.

destroyed_by lists participant usernames in a deterministic order. You only see it for an attack, and only when at least one participant can be named. A SELF_DESTRUCT Core event has no attacker, damage, participation, or resource capture; combat destruction takes priority when both would apply.

Worker events

event_typereason_codeIDs and positionvaluesMeaning
DEPOSIT_FAILEDWORKER_EMPTYactor_id: Worker; position: Worker cellabsentWorker has no cargo.
DEPOSIT_FAILEDCORE_NOT_PRESENTactor_id: Worker; position: Worker cellabsentOwned Core is absent or not on the same cell.
DEPOSIT_FAILEDCORE_MOVINGactor_id: Worker; target_id: Core; position: Worker cellabsentThe colocated Core is migration-restricted this Tick.
DEPOSIT_FAILEDCORE_RESOURCE_FULLactor_id: Worker; target_id: Core; position: shared cell{capacity: int}The Core is full at max(10, population × 5); cargo is unchanged.
DEPOSIT_SUCCEEDEDabsentactor_id: Worker; target_id: Core; position: shared cell{amount: int, capacity: int, remaining: int}amount entered Core storage and remaining stayed on the Worker.
HARVEST_FAILEDNOT_RESOURCE_CELLactor_id: Worker; position: Worker cellabsentCurrent terrain is not a resource cell.
HARVEST_FAILEDCARGO_FULLactor_id: Worker; position: Worker cellabsentWorker already carries resources.
HARVEST_FAILEDRESOURCE_DEPLETEDactor_id: Worker; position: consumed pointabsentAnother eligible empty Worker with a lower UUID won this same point in the Tick.
HARVEST_SUCCEEDEDabsentactor_id: Worker; position: resource cell{amount: int, source: "RESOURCE_NODE" or "DROPPED_CARGO"}Cargo was loaded from a natural point or recovered from a Worker cargo pile.
BEACON_HARVEST_BONUSabsentactor_id: Worker; position: Worker cell{amount: int}Bonus portion of the harvest granted by carrying the Beacon.

For every resource position, all eligible empty Workers are ordered by UUID raw bytes. Only the lowest UUID succeeds and consumes the point. All other contenders receive RESOURCE_DEPLETED, even if their player holds the Beacon. Replenishment does not create player events; later complete states expose newly available points only when they are visible.

Recovering DROPPED_CARGO never takes more than the pile contains and does not increment resources_harvested or beacon_bonus_resources_harvested.

Combat events

event_typereason_codeIDs and positionvaluesMeaning
SWEEP_RESOLVEDabsentactor_id: Vanguard; position: swept adjacent cell{targets_hit: int}Sweep resolved; 0 is a valid result.
SHOT_MISSEDalways SHOT_MISSEDactor_id: Ranger; optional target_id: requested precision UUID; position: submitted expected_cellabsentShot failed dynamically. A cell-shot miss has no target_id; the detailed cause is intentionally hidden.
SHOT_HITabsentactor_id: Ranger; target_id: hit Core or Unit; position: target cell{damage: int}Valid shot contributed damage.
UNIT_DAMAGEDATTACKtarget_id: damaged Unit; position: Unit cell{damage: int, hp: int}Simultaneous combat damage and HP afterward, clamped to 0. hp: 0 means the Unit was destroyed.
DESTRUCTION_PARTICIPATIONUNIT or COREtarget_id: destroyed object; position: destruction cellabsentThis player contributed at least one damage to the destroyed object.

The victim never gets a separate UNIT_DESTROYED event. Detect a kill from UNIT_DAMAGED.values.hp === 0, together with the Unit's absence from the new complete state.

Every dynamic Ranger failure carries the same SHOT_MISSED reason — an empty cell, a missing or moved precision target, bad range, or an obstacle-blocked line. A successful cell shot reports the server-selected object in SHOT_HIT.target_id.

Movement events

event_typereason_codeIDs and positionvaluesMeaning
UNIT_MOVE_SUCCEEDEDabsentactor_id: Unit; position: destinationabsentUnit completed its one-cell move.
UNIT_MOVE_FAILEDSee Unit movement reasonsactor_id: Unit; position: unchanged originabsentUnit stayed at its origin.
CORE_MOVE_STARTEDabsentactor_id: Core; position: origin{destination: Position, progress: int, required: int}A migration began. Current values are progress 1, required 4.
CORE_MOVE_PROGRESSabsentactor_id: Core; position: origin{progress: int, required: int}Existing migration advanced but did not yet move the Core.
CORE_MOVE_SUCCEEDEDabsentactor_id: Core; position: destinationabsentFinal migration Tick moved the Core.
CORE_MOVE_FAILEDSee resolved movement reasonsactor_id: Core; position: unchanged originabsentFinal migration move failed; Core returns to NORMAL.
CORE_MOVE_START_FAILEDSee start reasonsactor_id: Core; position: originabsentSTART_MOVE failed before migration state began.
CORE_MOVE_CANCELLEDabsentactor_id: Core; position: originabsentExisting migration was cancelled and Core returned to NORMAL.

Unit movement reasons:

  • MOVE_OUT_OF_BOUNDS: signed int64 coordinate overflow would occur;
  • MOVE_BLOCKED_TERRAIN: destination is obstacle terrain;
  • MOVE_CONTESTED: different owners claimed the same destination;
  • MOVE_SWAP_BLOCKED: two hostile entities attempted a two-way swap;
  • MOVE_DESTINATION_OCCUPIED: a hostile occupant is not successfully leaving;
  • MOVE_DEPENDENCY_FAILED: a required departure failed;
  • CELL_UNIT_LIMIT: the destination would exceed the entity limit.

CORE_MOVE_FAILED can use CORE_DESTINATION_TERRAIN_BLOCKED, MOVE_CONTESTED, MOVE_SWAP_BLOCKED, MOVE_DESTINATION_OCCUPIED, MOVE_DEPENDENCY_FAILED, or CELL_UNIT_LIMIT.

CORE_MOVE_START_FAILED can use CORE_DESTINATION_OUT_OF_BOUNDS, CORE_DESTINATION_TERRAIN_BLOCKED, CORE_DESTINATION_OCCUPIED, or CELL_UNIT_LIMIT.

Beacon and respawn events

event_typereason_codeIDs and positionvaluesMeaning
BEACON_PICKUP_FAILEDCORE_MOVING, ALREADY_CARRIED, or BEACON_NOT_PRESENTactor_id: Core or Unit; position: actor cellabsentPickup could not be completed.
BEACON_PICKED_UPabsentactor_id: new carrier; position: pickup cellabsentActor became the carrier.
BEACON_DROP_FAILEDCORE_MOVING or NOT_BEACON_CARRIERactor_id: Core or Unit; position: actor cellabsentDrop could not be completed.
BEACON_DROPPEDabsentactor_id: former carrier; position: drop cellabsentVoluntary drop completed.
BEACON_DROPPED_ON_DEATHabsentactor_id: destroyed carrier; position: death cellabsentBeacon automatically moved to the ground.
RESPAWN_DELAYEDNO_LEGAL_SPAWNno IDs or positionabsentNo legal deterministic spawn candidate was found; next attempt is one Tick later.
CORE_RESPAWNEDabsenttarget_id: new Core; position: spawn cell{resources: int, workers: int}Player became ACTIVE with starting resources and Workers.

Decoding example

function applyEvent(event) {
switch (event.event_type) {
case 'UNIT_MOVE_FAILED':
markUnitBlocked(event.actor_id, event.position, event.reason_code);
break;
case 'CORE_SPAWN_SUCCEEDED':
registerSpawn(
event.target_id,
event.position,
event.values.unit_type,
);
break;
case 'SHOT_MISSED':
// The protocol does not reveal why it missed.
break;
}
}

Replace your old objects with the new state.objects. Events are there to explain how the new state came about — do not replay them as patches against the old one.