State¶
The thread-safe game state that server pushes keep current.
state.manager¶
The game state, kept current from server packets.
Classes:
| Name | Description |
|---|---|
GameState |
Manages game state parsed from server packets. |
Attributes:
| Name | Type | Description |
|---|---|---|
MovementEventCallback |
|
|
EventCallback |
|
|
EventsCallback |
|
|
MOVEMENT_PARSE_WARN_INTERVAL |
|
MovementEventCallback
module-attribute
¶
GameState
¶
Bases: MovementState, CastleState, AreaState, PlayerState, EventState, CommanderState, AllianceState, ProgressState, QuestState, AccountState, InventoryState
Manages game state parsed from server packets.
State is mutated by the network receive thread and read from user threads, so all mutation and snapshot reads are guarded by a lock.
The public attributes stay readable directly, but callers that read
several fields at once (or iterate a container) should use the snapshot
accessors — get_local_player(), get_special_currencies(), get_castles(),
get_all_movements(), get_events(), get_alliance_chat(), get_commanders(),
get_skills(), get_own_alliance(), get_quests() — which copy under the lock, or hand out read-only
models (get_research(), get_boosts() and the other progress accessors). Mutation paths swap
containers instead of editing them in place, so an unlocked reader that
already holds one never sees it change underneath.
Callbacks (the on_* registrations) run on one callback thread, never
on the receive thread: one at a time, in the order their packets were
applied, so two callbacks never run at once and the events for one
movement arrive in order. A callback may wait for a reply, but everything
queued behind it waits too, so hand long work to another thread.
Freshness
Nothing here polls the server: every field is as old as the last packet that carried it, and some packets arrive only once per session. Cached values are therefore not automatically current:
=================================== ========================== ======================================
State Refreshed by Force a refresh with
=================================== ========================== ======================================
castle name/coords, castle list ``gcl``, ``mir`` (pushed) re-login
castle resources/units/details ``dcl`` ``client.castle.get_details(id)``
castle units (also) ``rue`` (pushed) ``client.castle.get_details(id)``
castle resources (also) ``grc``, the replies that ``client.castle.get_resources(id)``
carry one (``ebu``, ...)
castle open-gate counter ``gcl``, ``kik`` (pushed) re-login
castle unlocked units and horses ``gpc`` (pushed) re-login
joined area, slum level, discount ``jaa``, ``csl``/``gab`` ``client.castle.join(id)``
(pushed)
joined castle's mines ``gsm`` (pushed), ``jaa``, ``client.castle.join(id)``
``cmr``
joined castle's resource carts ``rci`` (pushed), ``jaa``, ``client.castle.join(id)``
``rcc``
player identity/level/XP ``gpi``/``gxp``/``glu`` re-login
player coins/rubies, VIP, alliance ``gcu``/``vip``/``gal`` re-login
honor, beginner protection ``gho``/``uap`` re-login
special currencies ``sce`` (pushed) --
spies owned, before boosts ``gms`` (pushed) re-login
commanders and castellans ``gli``, the replies that ``client.commanders.get_all()``
carry one (``arl``, ...)
legend and sceat skills ``skl``, ``ego`` ``client.skills.get_skills()``
your alliance's details, members ``ain``, ``acn``, ``akm``, ``client.alliance.get_alliance_info(id)``
..., ``acm`` (online)
alliance chat history ``acl``, ``acm`` (pushed) --
research ``rei``, ``res``, ``msr`` re-login
boosters, premium, slots, festival ``boi``, booster replies, re-login
``bfs``
might points ``gmu`` (pushed) re-login
glory points ``ufa`` (pushed) re-login
Berimond points ``ufp`` (pushed only) --
top-X ranks, Storm Islands title ``uar`` (pushed) re-login
achievements ``vli`` (pushed) re-login
relocation ``gri`` (pushed) re-login
plague monks ``cpi``, ``cpm``, ``sbp`` re-login
daily reset ``drt`` (login only) re-login
attack counter ``gai`` (pushed) re-login
officers' school training ``gatp``, ``gtp`` re-login
boosted global effects ``bie`` (pushed) re-login
gift packages ``pgl`` re-login
ruby wishing well ``rww`` re-login
new relics flag ``nrf`` (pushed) re-login
gems and relic gems ``ggm``, ``gec`` (pushed) re-login
loot boxes, key progress ``gls`` (pushed) re-login
inventory space ``esl``, the crafting and re-login
selling replies
kingdoms, transfers between them ``kpi``, the transfer re-login
replies
mercenary missions ``mpe`` re-login
tax collection ``txi``, ``txs``, ``txc``, ``client.castle.get_tax_info()``
``btx``
construction item expiry ``nec`` (pushed) re-login
resource citizen ``irc`` (pushed) --
running events, scores, ends ``sei``/``tei`` (pushed), ``client.events.refresh()``
``see``/``tee``, ``pep``,
``fjf``, ``bst``, ``cqs``
active quests, quest book ``qli`` (pushed after --
login), ``qst``, ``qfi``,
``msp``
daily quests ``dql`` (pushed) re-login
movements ``gam``, ``abr``/``asr``, ``client.movements.get_movements()``
your sends' replies
(``cra``, ``cds``, ...)
=================================== ========================== ======================================
Every player section above is sent inside the login gbd and again as a
push of its own when it changes. When the connection is lost, or the
client is closed, everything is reset (see reset). The next
login's gbd refills the player and castles, and the gam the server pushes
after it (seen live) the movements; get_last_packet_time("gbd") and
get_last_packet_time("gam") say when.
In practice a castle's resources often reflects login time and nothing
else, so use the freshness accessors before trusting them:
get_castle_last_updated / get_castle_age,
get_player_last_updated, get_events_last_updated, and get_last_packet_time /
get_packet_times for per-packet timestamps. All timestamps are
wall-clock (time.time()) seconds, and None means "never seen",
which is different from "seen and empty".
Methods:
| Name | Description |
|---|---|
update_from_packet |
Central update router — parses packet and updates state. |
get_last_packet_time |
When a packet (or gbd sub-packet) of this kind was last applied. |
get_packet_times |
Snapshot of every tracked packet/sub-packet timestamp. |
update_from_packet
¶
Central update router — parses packet and updates state.
error_code is the packet's status; a command the client reads only
from a successful reply is skipped, unstamped, when it is not 0.
Every packet, handled or not, also advances movements, as the movement clock does between packets.
get_last_packet_time
¶
When a packet (or gbd sub-packet) of this kind was last applied.
Accepts the wire ids this manager tracks — "gbd", "gam", "dcl", "abr", "asr", the send replies ("cra",
"cam", "abgcam", "cds", "csm", "cat", "crm", "css", "tde", "cdd", "cpm", "thm", "ldt"), "mcm", "mrm",
"mfc", "glu", "mir", "fjf", "bst", "sce", "see", "tee", "pep", "acm", "aqi", "acn", "cal", "ufp", "bfs",
the officers' school's "gtp" (only a successful one),
the quest pushes "qli", "qst", "qpg", "qfi", "msp" and the campaign's "cqs",
the castle pushes "rue", "kik", "fbe", "cbx", "gdb", "gcb", the gem push "gec", the joined area's "jaa",
"cmr", "rcc" and the map read "gaa" (refused too) — and the login sections "gpi", "gxp", "gcu", "vip",
"gal", "gcl", "gho", "uap", "gpc", "gms", "sei", "tei", "gli", "skl", "ain", "acl", "rei", "boi", "gmu",
"ufa", "uar", "vli", "gri", "cpi", "dql", "gai", "gatp", "bie", "pgl", "rww", "nrf", "ggm", "gls", "esl",
"kpi", "mpe", "txi", "nec", "irc" and "grc", stamped whether they came inside a gbd, as a push of their own or
inside a reply that carries one ("sei" from a fjf or bst, "gli" from an arl, "ain" from an akm, "rei"
from a res, "kpi" from a kut, ...), plus "gac" and "drt", which only come inside a gbd. "gsm" and "rci"
are stamped whenever mines or resource carts are applied, from their push or a jaa, cmr or rcc reply;
"csl" and "gab" from their push or a jaa, the push even when no area was joined to apply it to. A send
reply is stamped even when the server refused the send; the commands the client reads only from a
successful reply (the event ones, the login section ones, the castle ones, the quest ones, "gec", "acm",
"aqi", "ufp" and "bfs") are not, nor is a login section that was not applied: unreadable, not valid, or
another alliance's "ain". None means none was ever seen;
packets this manager ignores are never recorded.
state.base¶
State shared by the GameState mixins: the lock, tracked data and callback dispatch.
Classes:
| Name | Description |
|---|---|
StateBase |
The data every GameState mixin reads and writes, created once in |
Functions:
| Name | Description |
|---|---|
read_section |
A section block read as |
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
CALLBACK_QUEUE_WARN_DEPTH |
|
|
CALLBACK_QUEUE_WARN_INTERVAL |
|
|
MovementEventCallback |
|
MovementEventCallback
module-attribute
¶
StateBase
¶
The data every GameState mixin reads and writes, created once in __init__.
Methods:
| Name | Description |
|---|---|
reset |
Forget all the session sent: player, castles, joined area, movements, events, login sections, timestamps. |
shutdown |
Stop the movement clock and shut the callback executor down. Call when done with the client. |
Attributes:
| Name | Type | Description |
|---|---|---|
callback_queue_depth |
int
|
Callbacks queued on the callback thread and not finished yet, the running one included. |
callback_queue_depth
property
¶
callback_queue_depth: int
Callbacks queued on the callback thread and not finished yet, the running one included.
reset
¶
Forget all the session sent: player, castles, joined area, movements, events, login sections, timestamps.
Registered callbacks stay, and so does the record of attacks and occupations
already announced to on_incoming_attack and on_occupation_started.
Fires no callback: a movement that is dropped here was not seen to
arrive or be removed. The client resets its data
the same way when the connection is lost; the next login's gbd, and the
gam the server pushes after it (seen live), rebuild it.
Client: CastleConnectionLostCommand.execute (bundle line 120254) runs
CastleDestroyGameCommand, whose destroyGameSpecificObjects
(line 120270) calls CastleModel.resetModels; CastleArmyData.reset
(line 133620) starts the movement map over.
shutdown
¶
Stop the movement clock and shut the callback executor down. Call when done with the client.
Each starts again when next needed, so a client logged in again keeps working. Until this
runs (client.close() runs it), the movement clock's thread keeps the state and its callbacks
in memory until the last movement it knows of has passed.
state.movements¶
Movement tracking: gam and its pushes, arrival, removal and the movement queries.
Classes:
| Name | Description |
|---|---|
MovementState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
MOVEMENT_PARSE_WARN_INTERVAL |
|
|
MOVEMENT_CLOCK_MAX_WAIT |
|
MovementState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
on_incoming_attack |
Register a callback for new hostile attack movements. |
on_incoming_attack_updated |
Register a callback for changes to an attack |
on_incoming_attack_withdrawn |
Register a callback for announced attacks the server removes before they arrive. |
on_occupation_started |
Register a callback for occupations of your areas or your alliance members'. |
on_occupation_updated |
Register a callback for changes to an occupation |
on_occupation_ended |
Register a callback for occupations |
on_movement_recalled |
Register a callback for your own recalled movements. |
on_movement_arrived |
Register a callback for movements reaching their target. |
on_movement_removed |
Register a callback for movements the server removes ( |
get_all_movements |
Get all tracked movements (stale ones pruned first). |
get_incoming_movements |
Other players' armies heading to the local player (see |
get_outgoing_movements |
The local player's armies heading to their targets, returns excluded. |
get_incoming_attacks |
Get all incoming attack movements. |
get_announced_attacks |
The attacks |
get_occupations |
The occupations |
reannounce |
Fire |
get_movement_by_id |
Get a tracked movement by its |
on_incoming_attack
¶
on_incoming_attack(callback: Callable[[Movement], object]) -> None
Register a callback for new hostile attack movements.
Fires once per newly seen attack that is not the local player's own, is not on its way home and had not already landed when first seen, and is aimed at you or the daimyo township (an alien attack only at you), whoever sends it, or at another member of your alliance. An attack on an alliance member fires when a player the movement names sends it, when an alien attack or the alliance nomad camp does, or when an NPC that is not a dungeon owner does (an outpost, capital or metropolis owner, the plague monk, or an NPC id the client does not know); robber barons, camps, event dungeons and the other dungeon owners do not fire it. An alliance member's attack on someone outside the alliance does not fire. Each packet that carries an attack judges it again, with the owner records seen so far, so one whose attacker's record comes later fires then.
Fires once per attack movement id, also across a reconnect: an attack still on its way when the connection drops is not announced again when the next login lists it. The record of an attack is dropped when it arrives, when the server removes it, or once its travel time is over.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_incoming_attack.remove(callback).
on_incoming_attack_updated
¶
Register a callback for changes to an attack on_incoming_attack announced.
callback(old, new) fires on a later packet for the same attack that
changes its army (units, estimated_size), its arrival
(estimated_arrival, by two seconds or more, as a speed-up
does), its target (target_id, target_area_id,
target_x, target_y) or its commander (commander, its
equipment and area effects included). A packet that changes
none of these does not fire it, nor does the packet that announces the
attack, nor any packet once the attack has arrived or been removed.
After a reconnect the first listing of an attack has nothing to compare
with, so it does not fire either. old is the Movement as state had
it before the packet, new the one it holds now.
The client keeps no history to compare with: parseMapMovementArray
(bundle line 133626) replaces a movement's object with each packet, so
this is derived by comparing the two.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_incoming_attack_updated.remove(callback).
on_incoming_attack_withdrawn
¶
on_incoming_attack_withdrawn(callback: Callable[[Movement], object]) -> None
Register a callback for announced attacks the server removes before they arrive.
Fires once, with the attack as state last had it, when mrm removes an
attack that on_incoming_attack announced while its travel time
is not up yet. An attack removed within two seconds of its estimated
arrival (estimated_arrival can run a second late), at or after it,
or one state no longer tracks, does not fire.
on_movement_removed still fires for the same mrm, first.
Derived, not reported: the server does not say why it removes a
movement, and neither does the client. CastleArmyData.parse_MRM
(bundle line 133633) only drops it, and the client removes an arrived
movement on its own timer (updateMapmovements, bundle line 133670,
once currentProgress reaches 1), so a removal before arrival is the
attack turning back or being called off.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_incoming_attack_withdrawn.remove(callback).
on_occupation_started
¶
on_occupation_started(callback: Callable[[Movement], object]) -> None
Register a callback for occupations of your areas or your alliance members'.
An occupation (MovementType.SIEGE or OCCUPY_FACTION, both parsed
as SiegeMapmovementVO, bundle line 133809) follows a capture attack
that landed and won: the game's movement overview lists it as
"Occupying forces" (RenderSiege, bundle line 67408,
dialog_moveOverview_siege). Its travel time is the time the
occupier must hold the area (the battle log's dialog_battleLog_youSiege24h);
once it runs out the area is captured. It is no attack, so
on_incoming_attack never reports it; the capture attack before
it is an ordinary attack (isConquerMovement, bundle line 14404).
Fires once per newly seen occupation that is not the local player's own, is not on its way home and had not already ended when first seen, and holds an area of yours or the daimyo township, or of another member of your alliance, whoever sends it. One on someone outside the alliance does not fire. As for attacks, each packet judges it again, and it fires once per movement id, also across a reconnect.
The client raises no attack warning for an occupation:
SiegeMapmovementVO (bundle line 33173) is a BasicMapmovementVO,
and CastleArmyData.checkAllAttackMovements (bundle line 133659)
counts only ArmyAttackMapmovementVO (its type check, 133664). These
are the occupations its movement list shows: FilterAttack (bundle
line 67995) those whose target owner is you, which the daimyo
township's is (getOwnerInfoVO, bundle line 138994), and
FilterAllianceIncoming (bundle line 67971) those, not yours, whose
target owner is another member of your alliance. Leaving out your own
and returning occupations is the library's choice, as for attacks:
neither filter checks the direction, and FilterAttack (67996) does
not check the sender either.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_occupation_started.remove(callback).
on_occupation_updated
¶
Register a callback for changes to an occupation on_occupation_started announced.
callback(old, new) fires on the same changes, and with the same
exceptions, as on_incoming_attack_updated does for an attack:
a changed estimated_arrival is a changed end of the occupation
time. An occupation's army is its A block
(SiegeMapmovementVO.parseArmy, bundle line 33176), read into
units.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_occupation_updated.remove(callback).
on_occupation_ended
¶
Register a callback for occupations on_occupation_started announced leaving state.
callback(movement, captured) fires once per announced occupation,
with it as state last had it. captured is True when its time ran
out: at its arrival, after on_movement_arrived, or when mrm
removes it within two seconds of its estimated arrival or later, after
on_movement_removed. The game then reports the area captured
("managed to occupy your outpost for long enough and has now captured
it", dialog_siegeMessage_yourOutpostWasConquered). captured is
False when mrm removes it earlier, after on_movement_removed:
the occupation was broken ("Occupying forces driven off!",
dialog_messageHeader_siegeCancelledByPlayer,
dialog_siegeMessage_siegeCancelled). One that ends while no session
is logged in is not reported: state drops it with the session (see
reset) and the next login does not list it.
Derived, not reported, as on_incoming_attack_withdrawn is: the
server does not say why it removes a movement, and the reports above
come as messages of their own.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_occupation_ended.remove(callback).
on_movement_recalled
¶
on_movement_recalled(callback: MovementEventCallback) -> None
Register a callback for your own recalled movements.
Fires on the mcm reply to a recall, with the movement as it now
stands: on its way home (is_returning). A plain removal (mrm)
fires on_movement_removed instead.
Accepts either signature (see on_movement_arrived):
def on_recalled(movement_id: int) -> None: ...
def on_recalled(movement_id: int, movement: Movement | None) -> None: ...
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_movement_recalled.remove(callback).
on_movement_arrived
¶
on_movement_arrived(callback: MovementEventCallback) -> None
Register a callback for movements reaching their target.
The server sends no arrival packet: as in the game client, a movement
arrives once its travel time is up (estimated_arrival). A timer
of the state's own fires it then, with no packet needed, and every
packet and movement query checks too. It fires once per movement, and
not for a movement first seen after it had already arrived.
The client checks on its update tick (CastleArmyData.executeUpdate,
bundle line 133669, running updateMapmovements, line 133670); the
timer is the library's way to the same moment.
An army that stays at its target (a stationed support) is kept in
state until its wait is over (estimated_end); every other
movement is removed before callbacks run. An army's way home is a
movement too, so it fires when the army gets back; check
movement.is_returning to tell the two apart.
Two signatures are supported, picked per callback from its own parameter list:
def on_arrived(movement_id: int) -> None: ...
def on_arrived(movement_id: int, movement: Movement | None) -> None: ...
Prefer the second form: the id alone says nothing about what arrived.
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_movement_arrived.remove(callback).
on_movement_removed
¶
on_movement_removed(callback: MovementEventCallback) -> None
Register a callback for movements the server removes (mrm).
The server does not say why: a battle ending, a finished recall and a
support sent home all look the same; on_incoming_attack_withdrawn
tells an announced attack removed before it arrives, and
on_occupation_ended an announced occupation leaving. movement is
None if state was not tracking it. Accepts either signature (see
on_movement_arrived).
Runs on the callback thread, in packet order (see GameState).
Unregister a callback with on_movement_removed.remove(callback).
get_all_movements
¶
Get all tracked movements (stale ones pruned first).
get_incoming_movements
¶
Other players' armies heading to the local player (see Movement.is_incoming); occupations are not.
get_occupations lists those the library announced.
get_outgoing_movements
¶
The local player's armies heading to their targets, returns excluded.
get_incoming_attacks
¶
Get all incoming attack movements.
Attacks whose travel time is up are dropped first, so this never reports one that has landed.
get_announced_attacks
¶
The attacks on_incoming_attack announced that are still on their way, in the order announced.
Each is the Movement as state holds it now, from the latest packet that
carried it. Unlike get_incoming_attacks, which lists the attacks
aimed at you, this is what the callback reported, attacks on alliance
members included. An attack leaves it when it arrives or the server
removes it. After a reconnect, or a close() and login(), an
attack is listed again once a packet lists it, without being announced
again (see reannounce): a client handed to a new holder does
not announce to it what it announced before.
Library bookkeeping: the client keeps no record of what it announced.
get_occupations
¶
The occupations on_occupation_started announced that have not ended, in the order announced.
Listed as get_announced_attacks lists attacks; an occupation
leaves the list as on_occupation_ended reports it.
reannounce
¶
Fire on_incoming_attack or on_occupation_started again for an announced movement.
For a consumer whose callback could not act on the announcement (an
alert that failed to send). The callbacks get the Movement as state
holds it now, on the callback thread, behind those already queued.
Nothing else changes: the movement stays announced, so no packet
announces it again. A client.listen() stream gets it as an
ordinary incoming_attack or occupation_started event.
After a reconnect, a movement announced before it is known again
only once a packet lists it: the movement list the server pushes
after the login. on_session_restored fires once that list has
come, or once config.request_timeout passed without it (a
warning is logged), so even there it may not be listed yet. Until
then the callbacks cannot fire. With when_listed they are queued
instead, and fire once, with the Movement as the next packet that
lists it has it, however often this was called. The queued call is
dropped without firing if no packet lists the movement before its
travel time is over, the server removes it, or it is listed again
with its arrival behind it. An AccountPool
release drops it too.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
movement_id
|
int
|
The movement to announce again. |
required |
when_listed
|
bool
|
Queue the callbacks for an announced movement no packet has listed since a reconnect, instead of returning False. |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the callbacks were queued, now or with |
bool
|
the next listing; False if |
bool
|
announced, arrived or ended, or removed), or without |
bool
|
|
Library bookkeeping: the client announces nothing (see on_incoming_attack).
state.announcer¶
Announcing movements: which attacks and occupations count as on you, and the events they fire.
Classes:
| Name | Description |
|---|---|
AttackEvents |
Incoming attacks: only one removed before it arrives is reported, as withdrawn. |
OccupationEvents |
Occupations: each one leaving is reported as ended, captured when its time ran out. |
MovementAnnouncer |
The attacks and occupations a state announced, judged as its store reports movements stored, arrived, removed. |
Attributes:
| Name | Type | Description |
|---|---|---|
ETA_CHANGE_THRESHOLD |
|
|
UPDATED_ATTACK_FIELDS |
|
|
QueuedCall |
|
UPDATED_ATTACK_FIELDS
module-attribute
¶
UPDATED_ATTACK_FIELDS = ('units', 'estimated_size', 'target_id', 'target_area_id', 'target_x', 'target_y', 'commander')
AttackEvents
dataclass
¶
AttackEvents(announced: BoundEvent[Callable[[Movement], object]], updated: BoundEvent[Callable[[Movement, Movement], object]], withdrawn: BoundEvent[Callable[[Movement], object]])
Incoming attacks: only one removed before it arrives is reported, as withdrawn.
Methods:
| Name | Description |
|---|---|
leaving |
The calls for an announced attack leaving state, at its arrival or removed before it. |
Attributes:
| Name | Type | Description |
|---|---|---|
announced |
BoundEvent[Callable[[Movement], object]]
|
|
updated |
BoundEvent[Callable[[Movement, Movement], object]]
|
|
withdrawn |
BoundEvent[Callable[[Movement], object]]
|
|
leaving
¶
leaving(mov: Movement, arrived: bool) -> list[QueuedCall]
The calls for an announced attack leaving state, at its arrival or removed before it.
OccupationEvents
dataclass
¶
OccupationEvents(announced: BoundEvent[Callable[[Movement], object]], updated: BoundEvent[Callable[[Movement, Movement], object]], ended: BoundEvent[Callable[[Movement, bool], object]])
Occupations: each one leaving is reported as ended, captured when its time ran out.
Methods:
| Name | Description |
|---|---|
leaving |
The calls for an announced occupation leaving state, at its arrival or removed before it. |
Attributes:
| Name | Type | Description |
|---|---|---|
announced |
BoundEvent[Callable[[Movement], object]]
|
|
updated |
BoundEvent[Callable[[Movement, Movement], object]]
|
|
ended |
BoundEvent[Callable[[Movement, bool], object]]
|
|
leaving
¶
leaving(mov: Movement, arrived: bool) -> list[QueuedCall]
The calls for an announced occupation leaving state, at its arrival or removed before it.
MovementAnnouncer
¶
MovementAnnouncer(attacks: AttackEvents, occupations: OccupationEvents, fire: Callable[..., None])
The attacks and occupations a state announced, judged as its store reports movements stored, arrived, removed.
The store calls it under the state lock, and fire queues an event's
callbacks on the callback thread. It lives as long as the state, so a
reconnect does not announce the same movement again.
Methods:
| Name | Description |
|---|---|
events_for |
The events of the kind of announced movement |
stored |
Fire the callbacks for a movement a packet stored, |
arrived |
Forget an arrived movement; the calls to queue after its arrival if it was announced. |
removed |
Forget a movement |
prune |
Forget announced movements whose end has passed while no session tracked them. |
listed |
The |
reannounce |
Fire the announced callbacks again for an announced movement; False if it is not one. |
Attributes:
| Name | Type | Description |
|---|---|---|
attacks |
|
|
occupations |
|
|
announced |
dict[int, float]
|
|
reannounce_when_listed |
set[int]
|
|
events_for
¶
events_for(mov: Movement) -> AttackEvents | OccupationEvents | None
The events of the kind of announced movement mov is, None if it is neither attack nor occupation.
stored
¶
Fire the callbacks for a movement a packet stored, old being the one it replaces.
arrived says its arrival is behind it (reported, or past when it was
first seen); alliance_id is the local player's alliance, None outside one.
An attack is judged on every packet that carries it, as the client counts
its attack warnings anew (CastleArmyData.checkAllAttackMovements, bundle
line 133659), so one whose owner record comes later still fires, once; an
occupation likewise. A later packet for an announced attack or occupation
fires the updated callback when it changes what that callback reports,
and the announced callbacks if reannounce queued it and it has
not arrived.
arrived
¶
arrived(mov: Movement) -> list[QueuedCall]
Forget an arrived movement; the calls to queue after its arrival if it was announced.
removed
¶
removed(mid: int, mov: Movement | None, *, arrived: bool) -> list[QueuedCall]
Forget a movement mrm removed; the calls to queue after its removal if it was announced.
arrived says its arrival is behind it, as for stored. One removed
within two seconds of its estimated arrival or later counts as arrived.
prune
¶
prune(now: float) -> None
Forget announced movements whose end has passed while no session tracked them.
listed
¶
listed(events: AttackEvents | OccupationEvents, movements: Mapping[int, Movement]) -> list[Movement]
The movements of one kind still announced, in the order announced.
reannounce
¶
Fire the announced callbacks again for an announced movement; False if it is not one.
mov is the movement as tracked, None while no packet since a reconnect
listed it; when_listed then queues the callbacks for stored.
state.castles¶
Castle tracking: the castle list (gcl), castle details (dcl), castle resources (grc), units received (rue), the open-gate counter reset (kik) and unlocked units and horses (gpc).
Classes:
| Name | Description |
|---|---|
CastleState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
CastleState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_permanent_castle |
One of your castles' unlocked units and horses, from the last gpc that named it. |
get_castle_horse_ids |
The horses one of your castles can send movements with, as gpc sent them. |
get_castles |
Get a snapshot of the player's castles. |
get_castle_last_updated |
When this castle's detail data (resources, units) was last refreshed. |
get_castle_age |
Seconds since this castle's detail data was refreshed, or |
get_permanent_castle
¶
get_permanent_castle(castle_id: int) -> PermanentCastle | None
One of your castles' unlocked units and horses, from the last gpc that named it.
The kingdom is the castle's own, from the castle list. None when the
castle is not in the castle list or no gpc named it.
Raises:
| Type | Description |
|---|---|
AmbiguousCastleError
|
the id repeats across your kingdoms |
Client: CastlePermanentCastleData.getCastleByWorldAreaId (bundle line 139145)
get_castle_horse_ids
¶
The horses one of your castles can send movements with, as gpc sent them.
Look each up with GameData.require().get_horse, or use
client.castle.get_horses. None when get_permanent_castle is.
get_castles
¶
Get a snapshot of the player's castles.
The list itself is current, but each castle's resources, units
and other dcl-only detail fields are as old as the last dcl packet for
that castle — frequently the one from login, and all-zero when no dcl
ever arrived. Check get_castle_last_updated /
get_castle_age before treating them as live.
get_castle_last_updated
¶
When this castle's detail data (resources, units) was last refreshed.
Only a dcl stamps it. A reply carrying the castle's grc (client.castle.get_resources,
building, recruiting, starting research, ...) updates its resources without stamping this;
get_last_packet_time("grc") says when the last one came.
Returns a wall-clock time.time() timestamp, or None if no dcl
packet ever refreshed this castle — in which case resources and
units are defaults, not measurements. Refresh with
client.castle.get_details(castle_id).
Raises:
| Type | Description |
|---|---|
AmbiguousCastleError
|
the id is listed in several of your kingdoms |
state.area¶
The joined area: the jaa reply, its mines (gsm) and resource carts (rci), slum level (csl) and builder discount (gab), and the building pushes (fbe, cbx, gdb, gcb) as callbacks.
Classes:
| Name | Description |
|---|---|
AreaState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
AreaState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
on_building_finished |
Register a callback for a building in the joined castle that finished ( |
on_building_xp |
Register a callback for XP a building in the joined castle gave ( |
on_buildings_changed |
Register a callback for buildings of the joined castle that were damaged ( |
get_joined_area |
The area joined, with its slum level and builder discount; |
get_mines |
The joined castle's mines by object id, as the last |
get_resource_carts |
The joined castle's resource carts, wood, stone and food, as the last |
get_resource_cart |
The cart of one resource in the last |
on_building_finished
¶
on_building_finished(callback: Callable[[BuildingFinished], object]) -> None
Register a callback for a building in the joined castle that finished (fbe).
Called with the building's object id and the XP it gave. Runs on the callback
thread, in packet order (see GameState).
Client: FBECommand.exec (bundle line 122879), AreaDataUpdater.parseCBX (bundle line 131521)
Unregister a callback with on_building_finished.remove(callback).
on_building_xp
¶
on_building_xp(callback: Callable[[BuildingXP], object]) -> None
Register a callback for XP a building in the joined castle gave (cbx).
Client: CBXCommand.executeCommand (bundle line 123345)
Unregister a callback with on_building_xp.remove(callback).
on_buildings_changed
¶
on_buildings_changed(callback: Callable[[DamagedBuildings], object]) -> None
Register a callback for buildings of the joined castle that were damaged (gdb) or
whose efficiency changed (gcb, a BuildingEfficiencyChanged).
Called with the buildings' new rows. State does not keep the joined castle's buildings: the build, upgrade and repair replies change them too.
Client: GDBCommand and GCBCommand (bundle lines 122998, 122968),
IsoUpdaterData.updateMultipleObjectInfos (bundle line 130259)
Unregister a callback with on_buildings_changed.remove(callback).
get_joined_area
¶
get_joined_area() -> JoinedArea | None
The area joined, with its slum level and builder discount; None before any join.
client.castle.join joins one. Login joins none. A map read (gaa) moves the session
off the castle but keeps this, as the client keeps its active area; join again before
anything castle-scoped.
get_mines
¶
get_mines() -> dict[int, MineStatus]
The joined castle's mines by object id, as the last gsm sent them.
next_collect_seconds counted from get_last_packet_time ("gsm"):
the client counts it down from then. Empty before any gsm and after a map read
(gaa); refreshed by client.castle.join and client.castle.collect_mine.
get_resource_carts
¶
get_resource_carts() -> list[ResourceCart]
The joined castle's resource carts, wood, stone and food, as the last rci sent them.
remaining_seconds counts from get_last_packet_time ("rci"). Empty
before any rci; a map read keeps them, as the client does; refreshed by client.castle.join and
client.castle.collect_resource_cart.
get_resource_cart
¶
get_resource_cart(cart_type: ResourceCartType) -> ResourceCart | None
The cart of one resource in the last rci; None before any.
The client looks a cart up by its place in RC; this matches each
cart's own RT, so an entry that could not be read moves no other cart.
Client: CastleResourceCartsData.getResourceCartData (bundle line 28802)
state.player¶
Local player tracking: gpi/gxp/gcu/vip/gho/uap, alliance (gal), special currencies (sce) and spies.
Classes:
| Name | Description |
|---|---|
PlayerState |
|
Functions:
| Name | Description |
|---|---|
xp_for_level |
Total XP at which |
legend_level_for_xp |
Legend level reached with |
xp_for_legend_level |
Total XP at which |
level_progress |
Legend level and the XP at which the current and next level start. |
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
LEVEL_CAP |
|
|
LEGEND_LEVEL_CAP |
|
PlayerState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_local_player |
Get a snapshot of the local player, or None before login. |
get_special_currencies |
Get a snapshot of the special currencies ( |
get_max_spies |
The spies you own before boosts, from |
get_player_last_updated |
When any local-player field was last refreshed, or |
get_local_player
¶
get_local_player() -> Player | None
Get a snapshot of the local player, or None before login.
Returns a copy taken under the lock, with detached special_currencies,
castles and beginner_protection containers, so several fields can be read consistently
while the receive thread is updating state. state.local_player
remains available for direct access but is a live object.
The Castle objects inside the snapshot are the live ones, as with
get_castles().
get_special_currencies
¶
Get a snapshot of the special currencies (Currency -> amount).
These are the generic currencies from sce (Currency.FAST_TRAVEL_FEATHERS,
Currency.SKIP_1_MINUTE, ...), not items; a key newer than the generated
enum is kept as its str. Empty before login, or if no sce packet has
arrived yet — use get_last_packet_time("sce") to tell those apart
from "empty".
get_max_spies
¶
get_max_spies() -> MaxSpiesResponse | None
The spies you own before boosts, from gms; None until the login gbd brings it.
The model is replaced, never edited, on each gms, so the one returned stays as it was.
client.spy.total_spies() adds the boosts, read from state.
get_player_last_updated
¶
get_player_last_updated() -> float | None
When any local-player field was last refreshed, or None.
Player fields arrive in the login gbd and again as a push whenever the server changes them (gpi, gxp, gcu, vip, gal, gcl, gho, uap, glu, mir, sce). A stamp far in the past means no push has arrived since, not that the values were re-confirmed.
xp_for_level
¶
Total XP at which level starts. Client: PlayerConst.getXPFromLevel.
legend_level_for_xp
¶
Legend level reached with xp total XP. Client: PlayerConst.getLegendLevelFromXP.
xp_for_legend_level
¶
Total XP at which legend_level starts. Client: PlayerConst.getXPFromLegendLevel.
state.events¶
The running events: sei and tei entries, see and tee ends, pep scores, cqs campaigns, and their callbacks.
Classes:
| Name | Description |
|---|---|
EventState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
EventCallback |
|
|
EventsCallback |
|
EventState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
on_event_added |
Register a callback for an event that starts: a |
on_event_removed |
Register a callback for an event that ends: a |
on_events_updated |
Register a callback for every |
get_events |
The running events by id, in the order they started; a copy. |
get_event |
A running event, or None when it is not running. |
is_event_active |
Whether an event runs and has not ended. |
get_events_last_updated |
When a sei, tei, see, tee or pep was last applied, wall clock; None before any. |
on_event_added
¶
on_event_added(callback: Callable[[SpecialEvent], object]) -> None
Register a callback for an event that starts: a sei or tei entry for an event not running.
Called with the event as that entry left it. Runs on the callback thread, in packet
order (see GameState).
Client: CastleSpecialEventEvent.ADD_SPECIALEVENT from parseServerEventData (bundle line 139814)
Unregister a callback with on_event_added.remove(callback).
on_event_removed
¶
on_event_removed(callback: Callable[[SpecialEvent], object]) -> None
Register a callback for an event that ends: a see or tee, or its time running out.
Called with the event as it last stood. A disconnect (see reset) fires none.
Client: CastleSpecialEventEvent.REMOVE_SPECIALEVENT from removeEventById (bundle line 139840),
reached from parse_SEE, parseTEE and executeUpdateForEvents (bundle lines 139826, 139915, 139836)
Unregister a callback with on_event_removed.remove(callback).
on_events_updated
¶
on_events_updated(callback: Callable[[dict[int, SpecialEvent]], object]) -> None
Register a callback for every sei, non-empty tei, pep and cqs applied.
Called with every running event (a copy of get_events), after the
on_event_added and on_event_removed callbacks of the same packet.
Client: onEventDataParsed (bundle line 139818) sends REFRESH_SPECIALEVENT for every
event and SERVER_DATA_PARSED; AScoreEventVO.setRankAndPoints (bundle line 15044)
sends UPDATE_POINTS for a pep; CQSCommand.exec (bundle line 126495) sends
CAMPAIGN_UPDATED for a cqs
Unregister a callback with on_events_updated.remove(callback).
get_events
¶
get_events() -> dict[int, SpecialEvent]
The running events by id, in the order they started; a copy.
Each is the model its type has (EVENT_CLASSES), or a
plain SpecialEvent. The models never change: a
later packet replaces them.
get_event
¶
get_event(event: Literal[FACTION]) -> BerimondEvent | None
get_event(event: Literal[POINT_EVENT]) -> PointEvent | None
get_event(event: Literal[BEGGING_KNIGHTS]) -> BeggingKnightsEvent | None
get_event(event: Literal[LONG_TERM_POINT_EVENT]) -> LongTermPointEvent | None
get_event(event: Literal[ALLIANCE_ALIEN_INVASION, RED_ALLIANCE_ALIEN_INVASION]) -> AlienInvasionEvent | None
get_event(event: Literal[ALLIANCE_NOMAD_INVASION]) -> NomadInvasionEvent | None
get_event(event: Literal[SAMURAI_INVASION]) -> SamuraiInvasionEvent | None
get_event(event: Literal[FACTION_INVASION]) -> FactionInvasionEvent | None
get_event(event: Literal[ALLIANCE_RAIDBOSS_EVENT]) -> RaidBossEvent | None
get_event(event: Literal[TEMP_SERVER]) -> TempServerEvent | None
get_event(event: Literal[DONATION_EVENT]) -> DonationEvent | None
get_event(event: Literal[GACHA_DECO2X2, CHRISTMAS_GACHA, EASTER_GACHA, SUMMER_GACHA, ANNIVERSARY_GACHA, HALLOWEEN_GACHA, BLACK_FRIDAY_GACHA, CARNIVAL_GACHA]) -> GachaEvent | None
get_event(event: Literal[SEASON_LEAGUE]) -> KingdomsLeagueEvent | None
get_event(event: Literal[GLOBAL_EFFECT]) -> GlobalEffectEvent | None
get_event(event: Literal[GLOBAL_EFFECT_BUFF]) -> GlobalEffectBuffEvent | None
get_event(event: Literal[ALLI_TOURNAMENT]) -> AllianceTournamentEvent | None
get_event(event: Literal[ALLIANCE_MOBILIZATION_EVENT]) -> AllianceMobilizationEvent | None
get_event(event: Literal[LUCKY_WHEEL, SALE_DAYS_LUCKY_WHEEL]) -> LuckyWheelEvent | None
get_event(event: Literal[ARTIFACT_19, ARTIFACT_23, ARTIFACT_29, ARTIFACT_30, ARTIFACT_67]) -> ArtifactEvent | None
get_event(event: Literal[THORNKING, SEAQUEEN, UNDERWORLD]) -> SeasonEvent | None
get_event(event: Literal[FAMEBOOSTER]) -> FameBoosterEvent | None
get_event(event: Literal[PRIME_ALLI_BONUS, ALLI_PAYMENT_BONUS]) -> AllianceBonusEvent | None
get_event(event: Literal[RELIC_ENCHANTER_PRIME_SALE, SEASON_PASS_PRIME_SALE]) -> DiscountSaleEvent | None
get_event(event: Literal[SKIP_FOR_FREE]) -> SkipForFreeEvent | None
get_event(event: Literal[FORTUNE_TELLER]) -> FortuneTellerEvent | None
get_event(event: Literal[TOURNAMENT]) -> TournamentEvent | None
get_event(event: Literal[TIME_LIMITED_CAMPAIGN_EVENT]) -> CampaignEvent | None
get_event(event: Literal[TIME_LIMITED_CAMPAIGN_QUEST_EVENT]) -> CampaignQuestEvent | None
get_event(event: Event | int) -> SpecialEvent | None
get_event(event: Event | int) -> SpecialEvent | None
A running event, or None when it is not running.
Typed as the model of its type (EVENT_CLASSES) when
event names one, so get_event(Event.SAMURAI_INVASION) is a
SamuraiInvasionEvent | None; any other event or a bare id is a SpecialEvent.
is_event_active
¶
Whether an event runs and has not ended.
Client: CastleSpecialEventData.isEventActive (bundle line 139892); the client also
waits for some events' graphics to load, which the library has none of.
state.commanders¶
The player's commanders and castellans (gli) and legend and sceat skills (skl).
Classes:
| Name | Description |
|---|---|
CommanderState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
CommanderState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_commanders |
Your commanders and castellans as the last |
get_skills |
Your legend and sceat skills as the last |
get_commanders
¶
get_commanders() -> CommanderRoster | None
Your commanders and castellans as the last gli listed them; None until one arrived.
The login gbd brings them, and so does every reply that carries the list (gli,
arl, gla, seq, sdi, sti and the attack and conquer info replies).
A copy: changing it changes nothing in state.
state.alliance¶
Your own alliance's details (ain) and the alliance chat history (acl, acm).
Classes:
| Name | Description |
|---|---|
AllianceState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
AllianceState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_own_alliance |
Your alliance's details and members, from the last |
get_alliance_chat |
The alliance chat history, oldest first; a copy of the list, of read-only messages. |
get_own_alliance
¶
get_own_alliance() -> AllianceInfo | None
Your alliance's details and members, from the last ain about it; None in no alliance or before one.
The login gbd brings it, and so do client.alliance.get_alliance_info(your_id) and the
replies that carry it (acd, acn, ado, akm, arm, cal). An acm marks its
sender online here. A copy: changing it changes nothing in state.
get_alliance_chat
¶
get_alliance_chat() -> list[ChatMessageData]
The alliance chat history, oldest first; a copy of the list, of read-only messages.
The login gbd's acl starts it, an acl reply (client.alliance.get_chat_log())
replaces it, and every acm adds one. Leaving the alliance, a gal with no alliance
and a disconnect start it over.
state.progress¶
The player's progress: research, boosts, might, points, titles, achievements, relocation and plague monks.
Classes:
| Name | Description |
|---|---|
ProgressState |
|
ProgressState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_research |
Your finished and running research, from the last |
get_boosts |
Your boosters, premium account, production slots and festival; None until a |
get_might |
Your might points, from the last |
get_glory_points |
Your glory points, from the last |
get_faction_points |
Your Berimond points, from the last |
get_title_ranks |
Your top-X ranks, Storm Islands title and displayed title systems, from the last |
get_achievements |
Your achievement points, finished achievements and progress; None until a |
get_relocation |
Your castle relocations, from the last |
get_plague_monks |
Your plague monks, from the last |
get_research
¶
get_research() -> ResearchInfoResponse | None
Your finished and running research, from the last rei; None until the login data brought one.
get_boosts
¶
get_boosts() -> BoosterInfoResponse | None
Your boosters, premium account, production slots and festival; None until a boi arrived.
Every booster any boi listed is there, as the last one that listed it left it.
get_might
¶
get_might() -> MightPointsResponse | None
Your might points, from the last gmu; None until one arrived.
get_glory_points
¶
get_glory_points() -> GloryPointsResponse | None
Your glory points, from the last ufa; None until one arrived.
get_faction_points
¶
get_faction_points() -> FactionPointsResponse | None
Your Berimond points, from the last ufp push; None until one arrived (the login data has none).
get_title_ranks
¶
get_title_ranks() -> TitleRanksResponse | None
Your top-X ranks, Storm Islands title and displayed title systems, from the last uar; None before one.
get_achievements
¶
get_achievements() -> AchievementsResponse | None
Your achievement points, finished achievements and progress; None until a vli arrived.
Finished achievements from every vli are there, and each achievement's progress as
the last vli that listed it left it.
get_relocation
¶
get_relocation() -> RelocationInfoResponse | None
Your castle relocations, from the last gri; None until one arrived.
get_plague_monks
¶
get_plague_monks() -> PlagueMonkInfoResponse | None
Your plague monks, from the last cpi; None until one arrived.
state.account¶
Account sections: the daily reset, attack counter, officers' school, boosted global effects, gifts, wishing well and new relics.
Classes:
| Name | Description |
|---|---|
AccountState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
ACCOUNT_SECTIONS |
|
ACCOUNT_SECTIONS
module-attribute
¶
AccountState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_daily_reset |
When the daily reset comes, from the login data's |
get_attack_counter |
Your attack counter, from the last |
get_officer_training |
The officers' school training program running, from the last |
get_boosted_global_effects |
The global effects the running booster event boosts, from the last |
get_player_gifts |
Your gift packages and how many you may still send, from the last |
get_wishing_well |
The ruby wishing well, from the last |
get_new_relics |
Whether new relics wait to be seen, from the last |
get_daily_reset
¶
get_daily_reset() -> DailyResetResponse | None
When the daily reset comes, from the login data's drt; None until it arrived.
get_attack_counter
¶
get_attack_counter() -> AttackCounterResponse | None
Your attack counter, from the last gai; None until one arrived.
get_officer_training
¶
get_officer_training() -> OfficerTraining | None
The officers' school training program running, from the last gatp and the gtp since.
None when none runs; get_last_packet_time("gatp") tells "none runs" from "none arrived yet".
get_boosted_global_effects
¶
get_boosted_global_effects() -> BoostedGlobalEffectsResponse | None
The global effects the running booster event boosts, from the last bie; None until one arrived.
get_player_gifts
¶
get_player_gifts() -> PlayerGiftsResponse | None
Your gift packages and how many you may still send, from the last pgl; None until one arrived.
get_wishing_well
¶
get_wishing_well() -> WishingWellResponse | None
The ruby wishing well, from the last rww; None until one arrived.
get_new_relics
¶
get_new_relics() -> NewRelicsResponse | None
Whether new relics wait to be seen, from the last nrf; None until one arrived.
state.inventory¶
Inventory and economy sections: gems, loot boxes, inventory space, kingdoms, mercenaries, tax, construction item expiry and the resource citizen.
Classes:
| Name | Description |
|---|---|
InventoryState |
|
Attributes:
| Name | Type | Description |
|---|---|---|
INVENTORY_SECTIONS |
|
INVENTORY_SECTIONS
module-attribute
¶
InventoryState
¶
Bases: StateBase
Methods:
| Name | Description |
|---|---|
get_gems |
The gems and relic gems in your inventory; None until a |
get_loot_boxes |
Your loot boxes and key progress; None until a |
get_inventory_space |
The equipment and gem inventory space, from the last |
get_kingdoms |
Your kingdoms and the transfers to them; None until a |
get_mercenary_missions |
The mercenary camp's missions, from the last |
get_tax |
The tax collection, from the last |
get_construction_item_expiry |
When the next construction item expires, from the last |
get_resource_pool |
What the citizen in your castle carries, from the last |
get_gems
¶
get_gems() -> GemInventoryResponse | None
The gems and relic gems in your inventory; None until a ggm or gec arrived.
A ggm lists them whole, and each gec push adds or takes some away.
get_loot_boxes
¶
get_loot_boxes() -> LootBoxesResponse | None
Your loot boxes and key progress; None until a gls arrived.
The loot boxes are the last gls's, and each type's key progress as the last gls that
listed it left it.
get_inventory_space
¶
get_inventory_space() -> InventorySpace | None
The equipment and gem inventory space, from the last esl; None until one arrived.
get_kingdoms
¶
get_kingdoms() -> KingdomInfoResponse | None
Your kingdoms and the transfers to them; None until a kpi arrived.
Every kingdom any kpi listed is there, as the last one that listed it left it; the
transfers are the last kpi's.
get_mercenary_missions
¶
get_mercenary_missions() -> MercenaryMissionsResponse | None
The mercenary camp's missions, from the last mpe; None until one arrived.
get_tax
¶
get_tax() -> TaxInfo | None
The tax collection, from the last txi; None until one arrived.
Its remaining_seconds is as of that packet, get_last_packet_time("txi").
A copy: changing it changes nothing in state.
get_construction_item_expiry
¶
get_construction_item_expiry() -> ConstructionItemExpiryResponse | None
When the next construction item expires, from the last nec; None until one arrived.
get_resource_pool
¶
get_resource_pool() -> ResourcePoolResponse | None
What the citizen in your castle carries, from the last irc push; None until one arrived.
Joining a castle drops it, as in the client (JAACommand, bundle line 130194); the citizen being
collected or leaving does not, so it can be stale until the next irc.
state.models¶
The state's models: the local player, castles and resources.
Classes:
| Name | Description |
|---|---|
Resources |
A castle's resources: stock, storage capacity, hourly production and plunder-safe amount. |
Alliance |
The local player's alliance membership, from the |
Castle |
A castle, outpost or metropolis owned by the logged-in player. |
Player |
The logged-in player, as tracked by |
JoinedArea |
The castle or area last joined ( |
Attributes:
| Name | Type | Description |
|---|---|---|
CastleKey |
A castle's (kingdom, object id): ids repeat across kingdoms, as the client's castle list is kept per kingdom. |
CastleKey
module-attribute
¶
A castle's (kingdom, object id): ids repeat across kingdoms, as the client's castle list is kept per kingdom.
Resources
pydantic-model
¶
Bases: EmpireModel
A castle's resources: stock, storage capacity, hourly production and plunder-safe amount.
Stock comes from the castle's dcl entry; capacity, production and the
plunder-safe amount from its gpa production area.
Client: DetailedCastleVO.parseData
Fields:
-
wood(int) -
stone(int) -
food(int) -
coal(int) -
oil(int) -
glass(int) -
iron(int) -
aquamarine(int) -
honey(int) -
mead(int) -
beef(int) -
capacity(StorageCapacity) -
production(ResourceProduction) -
safe(SafeAmount)
Alliance
pydantic-model
¶
Bases: EmpireModel
The local player's alliance membership, from the gal login section.
Client: CastleUserData.parse_GAL.
Fields:
-
id(int) -
name(str) -
rank(int) -
current_fame(int) -
is_searching(bool)
Validators:
-
_searching_flag→is_searching
model_config
class-attribute
¶
model_config: ConfigDict = ConfigDict(extra='ignore', populate_by_name=True)
Castle
pydantic-model
¶
Bases: EmpireModel
A castle, outpost or metropolis owned by the logged-in player.
Name, position, kingdom and the open-gate counter come from the castle
list (gcl); everything else from the castle's dcl entry, kept
whole as details and None until one has been received. units
also follows each rue push (units received), which details does
not; a kik push on a Monday sets every open_gate_counter to 0.
Client: CastleListVO.parseCastleList (bundle line 13698),
DetailedCastleVO.parseData, CastleUserCastleListDetailed.parse_rue
(bundle line 140918), CastleListVO.resetOpenGateCounter (bundle line 13728)
Not to be confused with empire_core.castle.models.castles.CastleInfo,
which is the parsed protocol model for another player's castle.
Fields:
-
id(int) -
name(str) -
kingdom_id(Kingdom) -
x(int) -
y(int) -
resources(Resources) -
units(WodAmounts) -
open_gate_counter(int) -
details(DetailedCastleInfo | None) -
raw_data(dict[str, Any])
model_config
class-attribute
¶
model_config: ConfigDict = ConfigDict(extra='ignore', populate_by_name=True)
kingdom_id
pydantic-field
¶
Wire key KID
The kingdom the castle is in
open_gate_counter
pydantic-field
¶
open_gate_counter: int = 0
Wire key OGC
How often the gate has been opened since the last Monday reset
details
pydantic-field
¶
details: DetailedCastleInfo | None = None
The castle's latest details; None until they arrive
Player
pydantic-model
¶
Bases: EmpireModel
The logged-in player, as tracked by StateManager.
Some fields are still stored under their raw wire keys (PID, PN,
...), each with a snake_case read-only property (id, name, ...);
the rest are snake_case fields aliased to their wire keys. Prefer the
snake_case names.
Client: CastleUserData (parse_GPI, parse_GXP, parse_GHO,
parse_UAP, parse_GAL), CurrencyData.parseGCU /
parseSCE and CastleVIPData.parse_VIP.
Fields:
-
PID(int) -
PN(str) -
AID(int | None) -
level(int) -
xp(int) -
legendary_level(int) -
xp_for_current_level(int) -
xp_to_next_level(int) -
coins(int) -
rubies(int) -
special_currencies(dict[EnumOrStr[Currency], int]) -
vip_points(int) -
vip_level(int) -
vip_time_left(int) -
used_premium_commanders(int) -
alliance(Alliance | None) -
honor(int) -
ranking(int) -
beginner_protection(dict[Kingdom, bool]) -
PF(int) -
VF(int) -
castles(dict[CastleKey, Castle]) -
E(str | None)
model_config
class-attribute
¶
model_config: ConfigDict = ConfigDict(extra='ignore', populate_by_name=True)
legendary_level
pydantic-field
¶
legendary_level: int = 0
Wire key LL
Legend level, computed from XP once level reaches 70, else 0
xp_for_current_level
pydantic-field
¶
xp_for_current_level: int = 0
Wire key XPFCL
Total XP at which the current level (or legend level) starts, computed from level and XP
xp_to_next_level
pydantic-field
¶
xp_to_next_level: int = 0
Wire key XPTNL
Total XP at which the next level (or legend level) starts, computed from level and XP
special_currencies
pydantic-field
¶
Special currency -> amount (Currency.FAST_TRAVEL_FEATHERS, Currency.SKIP_1_MINUTE, ...), generic currencies in the item data, not items; a key newer than the generated enum stays a str
vip_time_left
pydantic-field
¶
vip_time_left: int = 0
Wire key VRS
Seconds of VIP time left when last read
used_premium_commanders
pydantic-field
¶
used_premium_commanders: int = 0
Wire key UPG
Premium commanders used today from the VIP level's free ones
beginner_protection
pydantic-field
¶
Whether the player is under beginner protection, by kingdom
PF
pydantic-field
¶
PF: int = 0
Premium flag, 1 when set; see premium_flag. Filled only from a gpi carrying PF, where the client does not read it (it reads PF from gpf)
VF
pydantic-field
¶
VF: int = 0
VIP flag, 1 when set; see vip_flag. Filled only from a gpi carrying VF, where the client does not read it (it reads VF only on map owner info)
xp_progress
property
¶
xp_progress: float
How far through the current level the player is, as a percentage (0-100).
0 when the level has no XP range: before any gxp, or at the legend level cap. Clamped, because the client puts XP just past level 70 in legend level 1, whose range starts 250 XP later.
castles
pydantic-field
¶
The player's castles by (kingdom, castle id)
JoinedArea
pydantic-model
¶
Bases: EmpireModel
The castle or area last joined (jaa, the reply to client.castle.join), and the
slum level and builder discount it was sent with or pushed since (csl, gab).
Client: JAACommand.executeCommand (bundle line 130190),
AreaFactory.parseAreaInfo (bundle line 130226), AreaDataUpdater.parseJAA
(bundle line 131496), AreaDataSlum (bundle line 131303), AreaDataCommonInfo.parseGAB
(bundle line 130992)
Fields:
-
kingdom_id(Kingdom | None) -
castle_id(int | None) -
slum_level(int) -
builder_discount(int | float)