Skip to main content

Python SDK reference

  • Package: arena-hero
  • Import: arena_hero
  • Python: 3.11 or newer

All public models are typed, immutable Pydantic models. State received from the server is validated before it reaches your loop.

Clients

ArenaHeroClient

The synchronous client:

ArenaHeroClient(
*,
api_key: str,
base_url: str = "https://api.arenahero.io",
websocket_url: str | None = None,
request_timeout: float = 5.0,
request_retries: int = 2,
reconnect_min_delay: float = 0.25,
reconnect_max_delay: float = 5.0,
max_message_size: int = 2 * 1024 * 1024,
)

AsyncArenaHeroClient

The asynchronous client accepts the same arguments:

AsyncArenaHeroClient(
*,
api_key: str,
base_url: str = "https://api.arenahero.io",
websocket_url: str | None = None,
request_timeout: float = 5.0,
request_retries: int = 2,
reconnect_min_delay: float = 0.25,
reconnect_max_delay: float = 5.0,
max_message_size: int = 2 * 1024 * 1024,
)
ArgumentMeaning
api_keyRequired credential sent as Authorization: Bearer ….
base_urlHTTP API base. The command endpoint is derived from it.
websocket_urlWebSocket endpoint. When omitted, it is derived from base_url.
request_timeoutTimeout in seconds for one HTTP command attempt.
request_retriesNumber of safe HTTP retries after the first attempt.
reconnect_min_delayInitial WebSocket reconnect delay in seconds.
reconnect_max_delayMaximum WebSocket reconnect delay in seconds.
max_message_sizeMaximum accepted WebSocket message size in bytes.

The SDK reads none of these values from environment variables.

turns()

Synchronous: ArenaHeroClient.turns() -> Iterator[Turn]

Asynchronous: AsyncArenaHeroClient.turns() -> AsyncIterator[AsyncTurn]

Yields each actionable Tick once. Receipts are still processed internally and stored in latest_receipts.

events()

Synchronous: ArenaHeroClient.events() -> Iterator[Tick | Turn | Received]

Asynchronous: AsyncArenaHeroClient.events() -> AsyncIterator[Tick | AsyncTurn | Received]

Yields the complete application-level WebSocket stream:

EventWhen it appearsWhat to do
TickA new Tick has been announced.Record it; there is no state to act on yet.
Turn / AsyncTurnThe complete player state is ready.Read it, queue actions, then submit.
ReceivedA plan from AGENT or MANUAL was stored.Replace the earlier receipt for that source and Tick.

Only one events() or turns() iterator may consume a client at a time.

latest_receipts

from arena_hero import CommandSource


agent_receipt = game.latest_receipts.get(CommandSource.AGENT)
manual_receipt = game.latest_receipts.get(CommandSource.MANUAL)

This read-only mapping contains the latest current-Tick Received value for each source. It is cleared when a new Tick begins.

submit()

Submit an already-built complete plan:

accepted = game.submit(plan, idempotency_key="agent-10583-plan-1")

Asynchronous:

accepted = await game.submit(plan, idempotency_key="agent-10583-plan-1")

The return type is Accepted. Omitting idempotency_key makes the SDK generate one. If a network failure leaves the result uncertain, the SDK retries the exact same request bytes with the same key.

A custom key must contain 8–128 visible ASCII bytes with no spaces.

close()

Closes the active WebSocket and HTTP connection pool. Prefer with or async with, which closes the client automatically.

Turn

Turn and AsyncTurn expose the same state and control interface. Their only difference is that AsyncTurn.submit() must be awaited.

State

AttributeTypeMeaning
tickintTick this state and plan belong to.
statePlayerStateComplete authoritative player-state model.
resourcesintResources currently stored in the Core.
core`CoreNone`
unitstuple[Unit, ...]All controlled Units.
workerstuple[Worker, ...]Controlled Workers.
vanguardstuple[Vanguard, ...]Controlled Vanguards.
rangerstuple[Ranger, ...]Controlled Rangers.
visible_enemies`tuple[UnitViewCoreView, ...]`
terraintuple[TerrainView, ...]Visible resource and obstacle batches.
resource_cellsfrozenset[Position]Visible resource cells.
obstacle_cellsfrozenset[Position]Visible obstacle cells.
beaconChampionBeaconCurrent visibility-limited Beacon view.
eventstuple[ResolutionEvent, ...]Private results from the previous Tick.
planCommandPlanComplete plan currently queued in memory.

Position is tuple[int, int] in (x, y) order.

Methods

MethodMeaning
unit(unit_id)Find one controlled Unit by UUID or UUID string.
clear()Remove every queued Unit and Core action.
submit(idempotency_key=None)Submit the complete queued plan.

Calling an action on a Turn after a newer Tick arrives raises TurnClosedError. Never reuse controller objects from an old Turn.

Unit controls

All controlled Unit objects expose:

MemberType or signature
viewUnitView
idUUID
positionPosition
hpint
unit_typeUnitType
move(direction)Queue a one-cell move.
pickup_beacon()Pick up the Beacon on the current cell.
drop_beacon()Drop a carried Beacon.
wait()Queue an explicit WAIT.
clear_action()Remove this Unit from the queued plan.

Every Unit may have at most one queued action. A later method call replaces its earlier action.

Worker

Extra state:

MemberTypeMeaning
cargointResources currently carried.

Extra controls:

MethodMeaning
harvest()Harvest the resource on the current cell.
deposit()Deposit cargo while sharing a cell with the Core.

Vanguard

MethodMeaning
sweep(direction)Attack the adjacent cell in one direction.

Ranger

ranger.shoot(target)
ranger.shoot(target_id, expected_cell=(120, 85))

target may be a visible Unit, Core, UnitView, or CoreView. The SDK copies its UUID and current position into the command. If you pass only a UUID or UUID string, expected_cell is required.

The server still resolves the shot using the game rules. Building a valid command does not guarantee a hit.

Core controls

The Core controller exposes:

MemberType or signature
viewCoreView
idUUID
positionPosition
hpint
shieldint
spawn(unit_type)Spawn UnitType.WORKER, VANGUARD, or RANGER.
repair_shield()Spend resources to repair shield.
start_move(direction)Start moving the Core.
cancel_move()Cancel current Core movement.
pickup_beacon()Pick up the Beacon on the current cell.
drop_beacon()Drop a carried Beacon.
wait()Queue an explicit WAIT.
clear_action()Remove the queued Core action.

The Core has one action slot. As with Units, a later call replaces its earlier queued action.

State models

PlayerState

FieldType
statusPlayerStatus
respawn_at_tick`int
resourcesint
populationint
population_tierint
upkeep_next_tickint
champion_beaconChampionBeacon
objects`tuple[TerrainView
eventstuple[ResolutionEvent, ...]

See State model for field semantics and visibility rules.

Object models

ModelMain fields
UnitViewkind, id, controlled, position, hp, unit_type, cargo
CoreViewkind, id, controlled, position, hp, shield, state, movement fields
TerrainViewkind, positions
ChampionBeaconposition, status, carrier_id

The controller classes (Worker, Vanguard, Ranger, Core) are convenient views over controlled objects. Enemy objects remain immutable UnitView or CoreView models.

ResolutionEvent

FieldType
event_idUUID
tickint
event_typestr
reason_code`str
actor_id`UUID
target_id`UUID
position`Position
values`dict[str, Any]

Event names and reason codes stay as strings so newer server values do not break an older SDK. See Resolution results for their meanings.

Tick, Received, and Accepted

ModelFields
Ticktick
Receivedtick, source, received_at, plan
Acceptedaccepted, tick, source, received_at

Accepted is the HTTP 202 acknowledgement. Received is the canonical plan broadcast over the WebSocket to every connected client for that player.

Command models

Most code should queue actions through a Turn. Advanced callers can construct the wire models directly:

from uuid import UUID

from arena_hero import CommandPlan, Direction, MoveAction


plan = CommandPlan(
tick=10583,
unit_actions={
UUID("9d3e4941-2816-4a39-a220-df8cd95e877d"): MoveAction(
direction=Direction.UP
)
},
)

accepted = game.submit(plan)

Public action models:

Unit actionRequired data
WaitActionnone
MoveActiondirection
HarvestActionnone
DepositActionnone
SweepActiondirection
ShootActiontarget_id, expected_cell
PickupBeaconActionnone
DropBeaconActionnone
Core actionRequired data
WaitActionnone
SpawnActionunit_type
RepairShieldActionnone
StartMoveActiondirection
CancelMoveActionnone
PickupBeaconActionnone
DropBeaconActionnone

CommandPlan.unit_actions maps Unit UUIDs to actions. CommandPlan.core_action is one Core action or None.

Enums

EnumValues
DirectionUP, DOWN, LEFT, RIGHT
UnitTypeWORKER, VANGUARD, RANGER
PlayerStatusACTIVE, RESPAWNING
CoreStateNORMAL, MOVING
CommandSourceAGENT, MANUAL
BeaconStatusGROUND, CARRIED

Direction.delta returns the corresponding (dx, dy) tuple.

Errors

All SDK exceptions inherit from ArenaHeroError.

ExceptionMeaning
ConfigurationErrorA constructor option or idempotency key is invalid, the client is closed, or two iterators were started.
AuthenticationErrorThe WebSocket handshake rejected the API key.
PolicyViolationErrorThe WebSocket closed with policy code 1008.
ProtocolErrorA server message does not match the public protocol.
APIErrorThe command API returned a structured rejection.
TransportErrorA network operation still failed after safe retries.
TurnClosedErrorCode tried to change a Turn after it stopped being current.
InvalidActionErrorA local target or action cannot be represented safely.

APIError exposes status_code, error, message, and details.

Gameplay failures are not Python exceptions. They arrive in the next Turn.events as ResolutionEvent values.

Connection behavior

The SDK:

  • sends the API key only in the Authorization header;
  • disables WebSocket message compression to match the server;
  • handles protocol Ping/Pong;
  • reconnects transient WebSocket failures with jittered exponential backoff;
  • stops reconnecting after close code 1008;
  • treats every state as a complete replacement;
  • safely retries uncertain command submissions with identical bytes and the same idempotency key.

The server's command window is global and may already be partly spent when a Turn arrives. Build and submit the plan promptly. Read Reliable command loop for the full timing and recovery rules.