Commanders¶
Commanders, castellans, equipment and generals behind client.commanders, client.equipment and client.skills.
commanders.service¶
Commanders and castellans, their equipment, and generals and skills.
- CommandersService reads and renames the player's commanders and castellans, and says whether the premium commander leads for free.
- EquipmentService reads the equipment inventory and moves items on and off commanders and castellans.
- SkillsService reads a general's unlocked skills and the player's own, and changes generals: assigning one to a commander, choosing its abilities, unlocking and resetting its skills, and feeding it xp items. A general's skills widen the flanks whatever the target is; the player's legend skills widen them further and add waves, but only when both sides are at the level cap.
Classes:
| Name | Description |
|---|---|
CommandersService |
Your commanders and castellans, and renaming them. |
EquipmentService |
The equipment inventory, and putting items on and off commanders and castellans. |
SkillsService |
Generals, their abilities and skills, and the player's legend and sceat skills. |
CommandersService
¶
CommandersService(client: EmpireClient)
Bases: BaseService
Your commanders and castellans, and renaming them.
Reached as client.commanders.
Methods:
| Name | Description |
|---|---|
get_all |
Get commanders and castellans in one request. |
get_commanders |
Get all available commanders. |
get_castellans |
Get all available castellans. |
rename |
Rename a commander or castellan. |
free_premium_commanders |
How many more times today the premium commander ( |
premium_commander_is_free |
Whether leading with the premium commander now costs no rubies: a premium account |
premium_send |
Run |
get_all
¶
get_all(timeout: float = 5.0) -> GetCommandersResponse
Get commanders and castellans in one request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
GetCommandersResponse
|
The full gli response |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
get_commanders
¶
Get all available commanders.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
list[Commander]
|
List of Commander objects |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
get_castellans
¶
Get all available castellans.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
list[Castellan]
|
List of Castellan objects |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
rename
¶
rename(commander_id: int, name: str, timeout: float = 5.0) -> RenameCommanderResponse
Rename a commander or castellan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
commander_id
|
int
|
The leader's |
required |
name
|
str
|
The new name; the game's dialog allows 3 to 15 characters |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
RenameCommanderResponse
|
The arl response, which carries the updated commander list |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
free_premium_commanders
¶
How many more times today the premium commander (PREMIUM_COMMANDER_ID) can lead for free.
Your VIP level's free_premium_commanders_per_day less those used today, and 0
while no VIP time runs. It comes from state, kept current by the login data's
vip and every vip the server pushes. A premium send this client made
(premium_send) counts as used until the next vip arrives.
A premium account makes the premium commander free on top of this:
premium_commander_is_free counts both.
Example
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
now
|
float | None
|
Wall-clock time to measure the VIP time left at, |
None
|
Returns:
| Type | Description |
|---|---|
int | None
|
The count, 0 at least, or None before the login data brought your VIP |
Raises:
| Type | Description |
|---|---|
GameDataNotLoadedError
|
VIP time runs and the game data cannot be loaded |
Client: CastleVIPData.remainingPremiumCommanders (bundle line 47554), the
freePremiumCommandersPerDay of currentActiveVIPLevel (bundle line 47546), which
is a level with none unless vipModeActive (bundle line 47550); parse_VIP
(bundle line 47527) reads UPG, from GBDCommand (bundle line 129381),
VIPCommand (bundle line 129941) and SBPCommand (bundle line 128320)
premium_commander_is_free
¶
premium_commander_is_free() -> bool
Whether leading with the premium commander now costs no rubies: a premium account
runs, or free_premium_commanders is 1 or more.
False when neither is known yet.
Raises:
| Type | Description |
|---|---|
GameDataNotLoadedError
|
No premium account runs, VIP time does, and the game data cannot be loaded |
Client: CastlePostAttackDialog.startAttack (bundle line 38360), which sends with a
free premium commander left and otherwise asks for
getFinalCostsC2(TravelConst.TRAVEL_PREMIUM_COMMANDER_COSTS_C2) rubies, 0 while
premiumAccountVO.isActive; TRAVEL_PREMIUM_COMMANDER_COSTS_C2 is 125 (dll line 19872)
premium_send
¶
premium_send(commander_id: int, use_premium_commander: bool, send: Callable[[], bool], *, spend_rubies: bool = False) -> bool
Run send, refusing it first when the premium commander leads and may cost rubies, unless spend_rubies.
The army sends go through this: client.castle.send_support,
client.castle.send_troops and client.attack.send_attack. The premium commander
leads when use_premium_commander is set or commander_id is PREMIUM_COMMANDER_ID;
the client always sends the two together.
Library policy, not client behaviour: the server sends no vip with the send's
reply (CRACommand, bundle line 125954; CDSCommand, bundle line 125939; CATCommand,
bundle line 125924), so a
premium send that send reports accepted while no premium account runs counts as one
used in free_premium_commanders until the next vip arrives. The check and the
send hold one lock, so two premium sends at once cannot both take the last free one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
commander_id
|
int
|
The leader's |
required |
use_premium_commander
|
bool
|
The send's |
required |
send
|
Callable[[], bool]
|
Sends the request; True when the server accepted it |
required |
spend_rubies
|
bool
|
Send even when the premium commander may cost rubies |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
What |
Raises:
| Type | Description |
|---|---|
PremiumCommanderCostError
|
The premium commander leads, |
GameDataNotLoadedError
|
See |
Client: CastlePostAttackDialog.startAttack (bundle line 38360) sends BPC 1
exactly when the selected commander is TravelConst.COMMANDER_PREMIUM
EquipmentService
¶
Bases: BaseService
The equipment inventory, and putting items on and off commanders and castellans.
Reached as client.equipment. What a leader
wears comes with client.commanders (gli EQ).
Methods:
| Name | Description |
|---|---|
get_inventory |
The items in the player's inventory, the ones no leader wears. |
equip |
Put an item on a commander or castellan. |
unequip |
Take an item off a commander or castellan. |
get_inventory
¶
The items in the player's inventory, the ones no leader wears.
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
equip
¶
Put an item on a commander or castellan.
The client moves an item between leaders by taking it off the first
and then putting it on the second. The reply carries no data; re-read
client.commanders.get_all() to see the change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
equipment_id
|
int
|
An item in the inventory, its |
required |
commander_id
|
int
|
The leader's |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
False when the server rejects the request |
unequip
¶
Take an item off a commander or castellan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
equipment_id
|
int
|
An item the leader wears, the |
required |
commander_id
|
int
|
The leader's |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
False when the server rejects the request |
SkillsService
¶
SkillsService(client: EmpireClient)
Bases: BaseService
Generals, their abilities and skills, and the player's legend and sceat skills.
Reached as client.skills.
Methods:
| Name | Description |
|---|---|
get_generals |
Every general the player owns, with the skills each has unlocked. |
assign_general |
Assign a general to a commander; |
set_abilities |
Choose a general's abilities. |
unlock_skill |
Unlock a general skill; the skill id names the general. |
reset_skills |
Reset a general's skill tree. |
add_xp |
Feed a general xp items. |
get_skills |
The player's legend and sceat skills. |
on_skill_list |
Register a callback for every skill list the server sends. |
get_generals
¶
get_generals(timeout: float = 5.0) -> GetGeneralsResponse
Every general the player owns, with the skills each has unlocked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
GetGeneralsResponse
|
The |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
assign_general
¶
assign_general(commander_id: int, general_id: int, timeout: float = 5.0) -> AssignGeneralResponse
Assign a general to a commander; general_id=-1 takes its general away.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
commander_id
|
int
|
A |
required |
general_id
|
int
|
An |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
AssignGeneralResponse
|
The |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
set_abilities
¶
set_abilities(general_id: int, abilities: Iterable[tuple[int, int | None]], timeout: float = 5.0) -> bool
Choose a general's abilities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
general_id
|
int
|
An |
required |
abilities
|
Iterable[tuple[int, int | None]]
|
|
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the server accepted the change |
unlock_skill
¶
Unlock a general skill; the skill id names the general.
The general's new skills arrive with the next gie.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
skill_id
|
int
|
The skill level's |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the server accepted the unlock |
reset_skills
¶
Reset a general's skill tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
general_id
|
int
|
An |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the server accepted the reset |
add_xp
¶
Feed a general xp items.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
general_id
|
int
|
An |
required |
currency_id
|
int
|
The xp item's |
required |
amount
|
int
|
How many of the item to use |
required |
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the server accepted the items |
get_skills
¶
get_skills(timeout: float = 5.0) -> GetSkillsResponse
The player's legend and sceat skills.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Timeout in seconds |
5.0
|
Returns:
| Type | Description |
|---|---|
GetSkillsResponse
|
The |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
The request failed |
on_skill_list
¶
on_skill_list(callback: Callable[[SkillList], object]) -> None
Register a callback for every skill list the server sends.
That is each skl packet, including the reply to get_skills,
and the skl block of an ego push.
Client: SKLCommand.executeCommand (bundle line 129742) and
EGOCommand.executeCommand (bundle line 122801) both call parse_SKL.
Removing a callback not registered is a no-op.
Unregister a callback with on_skill_list.remove(callback).
commanders.models.equipment¶
Equipment models and equipment inventory commands.
Commands:
- gei: Get Equipment Inventory - the items no commander or castellan wears
- eeq: Equip Equipment - put an item on a commander or castellan, or take it off
Classes:
| Name | Description |
|---|---|
EquipmentBonus |
One bonus of an item that is not a relic: |
RelicBonus |
One bonus of a relic item: |
RelicGem |
The gem set in a relic item: |
RelicInfo |
What a relic item carries at index 12: |
Equipment |
An equipment item worn by a commander or castellan. |
GetEquipmentInventoryRequest |
Request the player's equipment inventory. |
GetEquipmentInventoryResponse |
The player's equipment inventory. |
EquipEquipmentRequest |
Put an item on a commander or castellan ( |
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
NO_GEM_ID |
|
EquipmentBonus
pydantic-model
¶
Bases: BasePayload
One bonus of an item that is not a relic: [effect_id, values].
Client: BasicEquipmentVO.parseBonuses (bundle line 7135), which wraps a
value that is not an array into one.
Fields:
Validators:
-
_from_row
RelicBonus
pydantic-model
¶
Bases: BasePayload
One bonus of a relic item: [relic_effect_id, power, values].
Client: RelicItemInfoVO.parseRelicBoni (bundle line 33743),
RelicBonusVO.parseRelicFromValueArray (bundle line 45132), which looks the
id up in the relic effect table and parses row[2].toString(), so a value
and a one-value array read the same.
Fields:
Validators:
-
_from_row
RelicGem
pydantic-model
¶
Bases: BasePayload
The gem set in a relic item: [gem_id, relic_type_id, relic_category_id, might, bonuses, enchantment_level].
Client: RelicGemVO.parseServerObject (bundle line 22222)
Fields:
-
gem_id(int) -
relic_type_id(int) -
relic_category_id(int) -
might(int | float) -
bonuses(list[RelicBonus]) -
enchantment_level(ClientInt)
Validators:
-
_from_row
RelicInfo
pydantic-model
¶
Bases: BasePayload
What a relic item carries at index 12: [relic_type_id, relic_category_id, might, gem].
Client: RelicEquipmentVO.parseEquipFromArray (bundle line 25039)
Fields:
-
relic_type_id(int) -
relic_category_id(int) -
might(int | float) -
gem(RelicGem | None)
Validators:
-
_from_row -
_gem_or_none→gem
Equipment
pydantic-model
¶
Bases: BasePayload
An equipment item worn by a commander or castellan.
Parsed from a positional EQ entry: [id, slot, wearer, rarity, graphic, bonuses, unique_id, set_id, enchantment_level, duration_seconds, gem_id, equipment_type] Entries are truncated by the server when trailing fields do not apply.
Index 5 holds the bonuses: a relic item (index 11 is 3) lists them as
relic_bonuses ([relic_effect_id, power, values]), any other item as
bonuses ([effect_id, values]). A hero item (slot 6, not a relic)
also keeps index 11 as alien_string, which the client matches against the
unique heroes (CastleEquipmentData.getUniqueHerosByAlienString, bundle
line 143843); the type is still read from it through int().
Client: BasicEquipmentVO.parseEquipFromArray (bundle line 7115),
CastleEquipmentFactory.createEquipmentVO (bundle line 18134),
RelicEquipmentVO.parseEquipFromArray (bundle line 25039),
CastleHeroVO.parseEquipFromArray (bundle line 40585),
BasicEquipmentVO.hasSetbonus (bundle line 7213).
Fields:
-
equipment_id(int) -
slot(int) -
wearer_type(int) -
rarity_id(ClientInt) -
graphic(int | str) -
bonuses(list[EquipmentBonus]) -
relic_bonuses(list[RelicBonus]) -
unique_id(ClientInt) -
set_id(ClientInt) -
enchantment_level(ClientInt) -
duration_seconds(int | float) -
gem_id(EnumOrInt['Gem'] | None) -
equipment_type(ClientInt) -
relic_info(RelicInfo | None) -
alien_string(str | None)
Validators:
-
_relic_info_or_none→relic_info -
_gem→gem_id -
_alien_text→alien_string -
_from_row
set_id
pydantic-field
¶
set_id: ClientInt = 0
Equipment set id, -1 for none; 0, which no set uses, when the row has none
duration_seconds
pydantic-field
¶
Seconds until the item expires; below 1 it is permanent
equipment_type
pydantic-field
¶
equipment_type: ClientInt = EquipmentType.GENERATED
EquipmentType value
relic_info
pydantic-field
¶
relic_info: RelicInfo | None = None
A relic item's type, category, might and gem; None for other items
alien_string
pydantic-field
¶
alien_string: str | None = None
A hero item's alien string, "effect_id&value" pairs joined by ","; None for other items. A number the server sends there is kept as its text
is_permanent
property
¶
is_permanent: bool
True when the item does not expire (the server sends -1).
is_relic
property
¶
is_relic: bool
True for a relic item, whose bonuses index the relic effect table.
GetEquipmentInventoryRequest
pydantic-model
¶
Bases: BaseRequest
Request the player's equipment inventory.
Command: gei
Payload: {}
Client: C2SGetEquipmentInventory (bundle line 19854)
GetEquipmentInventoryResponse
pydantic-model
¶
Bases: BaseResponse
The player's equipment inventory.
Command: gei
Payload: {"I": [EQ entry, ...]}, each entry laid out as a gli EQ entry.
Client: CastleEquipmentData.parse_GEI (bundle line 143731), which builds every
entry with CastleEquipmentFactory.createEquipmentVO (bundle line 18134).
Fields:
-
error_code(int) -
items(list[Equipment])
Validators:
-
_readable_items→items
EquipEquipmentRequest
pydantic-model
¶
Bases: BaseRequest
Put an item on a commander or castellan (equip=1), or take it off (equip=0).
Moving an item between two leaders takes two requests: the client takes it off the first, then puts it on the second. The reply has no body.
Command: eeq
Payload: {"EID": equipment_id, "LID": commander_id, "E": 0 or 1}
Client: C2SEquipEquipmentVO (bundle line 143914) sends E as int(n?1:0);
CastleEquipmentData.startDrag (bundle line 143801) sends 0 when an item is
lifted off a leader's slot, stopDrag (bundle line 143807) sends 1 when it is
dropped on one, and EquipmentEquipmentClickHandler.init (bundle line 65626)
handles the 0 reply as an unequip.
Fields:
-
equipment_id(int) -
commander_id(int) -
equip(int)
Validators:
-
_as_flag→equip
equipment_id
pydantic-field
¶
equipment_id: int
Wire key EID
Equipment.equipment_id: of an inventory item from client.equipment.get_inventory() to equip, of a worn one from client.commanders.get_all() to take off
commanders.models.inventory¶
The equipment and gem inventory, as the login data and its pushes send it.
Commands:
- ggm: the gems and relic gems you hold (gec: a change to the gems)
- esl: the equipment and gem inventory space
- nrf: whether new relics wait to be seen
Classes:
| Name | Description |
|---|---|
GemStack |
How many of one gem you hold. |
GemInventoryResponse |
The gems and relic gems in your inventory, not set in any equipment. |
InventorySpace |
Free and total space in the equipment and gem inventory. |
NewRelicsResponse |
Whether new relic equipment waits to be seen. |
GemStack
pydantic-model
¶
GemInventoryResponse
pydantic-model
¶
Bases: BaseResponse
The gems and relic gems in your inventory, not set in any equipment.
Command: ggm, as a login section of gbd and a reply. A gec push changes the gems;
client.state.get_gems() applies it with changed.
Client: GGMCommand (bundle line 123957), CastleGemData.parseGGM, updateInventory and
updateRelicInventory (bundle lines 144327, 144334, 144427); GECCommand (bundle line 123927) and
parse_GEC (bundle line 144326). The client keeps one entry per gem and takes a negative amount
off from the first entry of that gem on; here the gems are counted per id.
Fields:
-
error_code(int) -
gems(tuple[GemStack, ...]) -
relic_gems(tuple[RelicGem, ...])
Validators:
-
_gems→gems
relic_gems
pydantic-field
¶
Wire key RGEM
The relic gems you hold
changed
¶
changed(rows: Any) -> GemInventoryResponse
This inventory after a gec's GEM rows: [gem_id, amount], a negative amount taken away.
InventorySpace
pydantic-model
¶
Bases: BasePayload
Free and total space in the equipment and gem inventory.
Not a response model: its E is the equipment space, not an error code.
Command: esl, as a login section of gbd, a push and a reply, and inside the bgm, ceq,
cge, frc and seq replies.
Client: ESLCommand (bundle line 123895), CastleEquipmentData.parse_ESL (bundle line 143746) and
CastleGemData.parse_ESL (bundle line 144410). The client recounts the free gem space from the total
and the gems it holds whenever they change (updateInventorySpace, bundle line 144417); gem_space
is the value the server sent. The equipment inventory counts as full at equipment_space 0 or
less, and holds equipment_total_space - equipment_space items (isInventoryFull and
filledInventorySpace, bundle lines 143839, 143841).
Fields:
NewRelicsResponse
pydantic-model
¶
Bases: BaseResponse
Whether new relic equipment waits to be seen.
Command: nrf, as a login section of gbd and a push.
Client: NRFCommand (bundle line 124002), CastleEquipmentData.parseNRF (bundle line 143756)
Fields:
Validators:
-
_flag→has_new_relics
commanders.models.roster¶
Commander protocol models.
Commands:
- gli: Get Lords Info - the server name for the commander/castellan list
- arl: rename a commander or castellan
Classes:
| Name | Description |
|---|---|
SelectedAbility |
One ability slot of a general: a |
AlienEquipment |
Alien ( |
LeaderBase |
Fields shared by every gli entry. |
Commander |
A commander - the leader assigned to an attack or support movement. |
Castellan |
A castellan - the defensive counterpart of a commander ( |
GetCommandersRequest |
Request the commander and castellan list. |
CommanderRoster |
A player's commanders and castellans, the |
GetCommandersResponse |
Response containing commanders (C) and castellans (B). |
RenameCommanderRequest |
Rename a commander or castellan. |
RenameCommanderResponse |
Reply to a rename: the full commander and castellan list. |
Attributes:
| Name | Type | Description |
|---|---|---|
logger |
|
|
PICTURE_FACTION_CASTELLAN |
|
|
PICTURE_ISLAND_CASTELLAN |
|
|
FACTION_BARON_ID |
|
|
PREMIUM_COMMANDER_ID |
|
|
CASTELLAN_PICTURE_ORDER |
|
PICTURE_FACTION_CASTELLAN
module-attribute
¶
EquipmentConst.PICK_BARON_FACTION (dll line 19249)
PICTURE_ISLAND_CASTELLAN
module-attribute
¶
EquipmentConst.PICK_BARON_ISLAND (dll line 19249)
PREMIUM_COMMANDER_ID
module-attribute
¶
TravelConst.COMMANDER_PREMIUM (dll line 19872): the premium commander's commander_id;
the client offers it from ClientConstLevelRestrictions.MIN_LEVEL_SELECT_PREMIUM_COMMANDER, level 10
(bundle line 2139)
CASTELLAN_PICTURE_ORDER
module-attribute
¶
BaronVO.PIC_ID_ORDER (bundle line 43551): castellans are listed in this portrait order
SelectedAbility
pydantic-model
¶
Bases: BasePayload
One ability slot of a general: a [slot_id, ability_id] pair, as GASAIDS sends it and SAIDS takes it.
It serializes back to the pair, with -1 for an empty slot.
Client: GeneralVO.parseData (bundle line 26666) keeps the pairs, and
GeneralVO.getSelectedAbilities (bundle lines 26769-26771) reads [0] as the
slot and [1] as the ability, counting it only above 0; GeneralsAbilityDialog.onSave
(bundle line 27850) sends -1 for a cleared slot.
Fields:
-
slot_id(int) -
ability_id(EnumOrInt['GeneralAbility'] | None)
Validators:
-
_from_row -
_empty_slot→ability_id
AlienEquipment
pydantic-model
¶
Bases: BasePayload
Alien (AIE) or temporary (TAE) equipment: [effect_id, values] rows, or
[hero_rows, equipment_rows] when it carries a hero.
The block is split in two when it has two entries that are each empty or start with an array; otherwise every row is an equipment bonus.
Client: LordVO.parseLord (bundle lines 26471-26477), AlienLordHeroVO.parseAlienBoniData
(bundle line 67502), AlienLordEquipmentVO.parseAlienBoniData (bundle line 67479)
Fields:
-
hero_bonuses(tuple[EquipmentBonus, ...]) -
bonuses(tuple[EquipmentBonus, ...])
Validators:
-
_split -
_rows→hero_bonuses,bonuses
LeaderBase
pydantic-model
¶
Bases: BasePayload
Fields shared by every gli entry.
The wire protocol calls both kinds "lords" (command gli, field LID
on movement commands); the game UI says commander and castellan.
AIE (alien) or TAE (temporary) equipment stands in for EQ: the
client reads the first of them that is present, and only when EQ is
empty. See alien_bonuses.
Client: LordFactory.createLord (bundle line 26399), LordVO.parseLord (bundle line 26451),
LordVO.parseGeneral (bundle line 26480) and GeneralVO.parseData (bundle line 26666) for
ST, L and the general's XP, OXP, IN, LU, SIDS and GASAIDS. The
client reads those six only when the entry names a general above 0 and is a default commander
or was created with LordFactory.createLord(e, true), as a battle log's are. An entry cannot
tell the second case, so they are None unless sent, or filled with the client's defaults for a
default commander with a general; elsewhere the client ignores them.
Fields:
-
commander_id(int) -
wearer_id(ClientInt | None) -
picture_id(ClientInt) -
name(str) -
wins(ClientInt) -
defeats(ClientInt) -
win_spree(ClientInt) -
effects(CommanderEffects) -
area_effects(CommanderEffects) -
equipment(list[Equipment]) -
alien_equipment(AlienEquipment | None) -
temporary_equipment(AlienEquipment | None) -
alien_gem_ids(tuple[EnumOrInt['Gem'], ...]) -
general_id(ClientInt | None) -
star_level(ClientInt) -
level(ClientInt) -
general_xp(ClientInt | None) -
general_old_xp(ClientInt | None) -
general_is_new(bool | None) -
general_has_level_up(bool | None) -
general_skill_ids(tuple[EnumOrInt['GeneralSkill'], ...] | None) -
general_selected_abilities(tuple[SelectedAbility, ...] | None)
Validators:
-
_xp_or_zero→general_xp,general_old_xp -
_one_flag→general_is_new,general_has_level_up -
_skill_list→general_skill_ids -
_readable_slots→general_selected_abilities -
_default_commander_general -
_id_as_the_client_reads_it -
_alien_block→alien_equipment,temporary_equipment -
_gem_list→alien_gem_ids -
_note_raw_equipment -
_readable_equipment→equipment
commander_id
pydantic-field
¶
commander_id: int
Wire key ID
Commander id; for a default commander, its default-commander id
wearer_id
pydantic-field
¶
wearer_id: ClientInt | None = None
Wire key WID
2 for a commander, 1 for a castellan
alien_equipment
pydantic-field
¶
alien_equipment: AlienEquipment | None = None
Wire key AIE
Alien equipment; applies when equipment is empty
temporary_equipment
pydantic-field
¶
temporary_equipment: AlienEquipment | None = None
Wire key TAE
Temporary equipment; applies when equipment is empty and alien_equipment absent
alien_gem_ids
pydantic-field
¶
Wire key GEM
The gems added to the alien or temporary equipment
general_id
pydantic-field
¶
general_id: ClientInt | None = None
Wire key GID
The assigned general's id; -1 or None for none
star_level
pydantic-field
¶
star_level: ClientInt = 0
Wire key ST
The general's star level when the entry doubles as its general: a default commander with a general_id above 0
level
pydantic-field
¶
level: ClientInt = 0
Wire key L
The general's level when the entry doubles as its general: a default commander with a general_id above 0
general_xp
pydantic-field
¶
general_xp: ClientInt | None = None
Wire key XP
The general's experience; None when the entry does not carry it
general_old_xp
pydantic-field
¶
general_old_xp: ClientInt | None = None
Wire key OXP
The general's experience before the battle; None when the entry does not carry it
general_is_new
pydantic-field
¶
general_is_new: bool | None = None
Wire key IN
The general is newly unlocked; None when the entry does not say
general_has_level_up
pydantic-field
¶
general_has_level_up: bool | None = None
Wire key LU
The general gained a level; None when the entry does not say
general_skill_ids
pydantic-field
¶
Wire key SIDS
The general's unlocked skills; None when the entry has none
general_selected_abilities
pydantic-field
¶
general_selected_abilities: tuple[SelectedAbility, ...] | None = None
Wire key GASAIDS
The general's ability slots, filled or empty; None when the entry has none
uses_alien_equipment
property
¶
uses_alien_equipment: bool
Whether AIE/TAE stand in for an empty EQ, as LordVO.parseLord decides.
active_alien_equipment
property
¶
active_alien_equipment: AlienEquipment | None
The alien or temporary equipment standing in for an empty EQ, None when none does.
alien_hero_bonuses
property
¶
alien_hero_bonuses: list[EquipmentBonus]
The hero bonuses of the active alien or temporary equipment.
alien_bonuses
property
¶
alien_bonuses: list[EquipmentBonus]
The equipment bonuses of the active alien or temporary equipment, empty when EQ has items.
worn_items
¶
The items the client puts in the commander's slots, in slot order.
LordVO.parseLord (bundle line 26451) creates the helmet, armor, weapon,
artifact, skin and hero slots in that order and puts each EQ item in
its slot by row[1], so a later item replaces an earlier one and an item
for any other slot is not worn.
Commander
pydantic-model
¶
Bases: LeaderBase
A commander - the leader assigned to an attack or support movement.
Client: CommanderVO, built by LordFactory.createLord (bundle line 26399)
Fields:
-
commander_id(int) -
wearer_id(ClientInt | None) -
picture_id(ClientInt) -
name(str) -
wins(ClientInt) -
defeats(ClientInt) -
win_spree(ClientInt) -
effects(CommanderEffects) -
area_effects(CommanderEffects) -
equipment(list[Equipment]) -
alien_equipment(AlienEquipment | None) -
temporary_equipment(AlienEquipment | None) -
alien_gem_ids(tuple[EnumOrInt['Gem'], ...]) -
general_id(ClientInt | None) -
star_level(ClientInt) -
level(ClientInt) -
general_xp(ClientInt | None) -
general_old_xp(ClientInt | None) -
general_is_new(bool | None) -
general_has_level_up(bool | None) -
general_skill_ids(tuple[EnumOrInt['GeneralSkill'], ...] | None) -
general_selected_abilities(tuple[SelectedAbility, ...] | None)
Validators:
-
_xp_or_zero→general_xp,general_old_xp -
_one_flag→general_is_new,general_has_level_up -
_skill_list→general_skill_ids -
_readable_slots→general_selected_abilities -
_default_commander_general -
_id_as_the_client_reads_it -
_alien_block→alien_equipment,temporary_equipment -
_gem_list→alien_gem_ids -
_note_raw_equipment -
_readable_equipment→equipment
Castellan
pydantic-model
¶
Bases: LeaderBase
A castellan - the defensive counterpart of a commander (BaronVO).
Client: BaronVO.parseLord (bundle line 43534)
Fields:
-
commander_id(int) -
wearer_id(ClientInt | None) -
picture_id(ClientInt) -
name(str) -
wins(ClientInt) -
defeats(ClientInt) -
win_spree(ClientInt) -
effects(CommanderEffects) -
area_effects(CommanderEffects) -
equipment(list[Equipment]) -
alien_equipment(AlienEquipment | None) -
temporary_equipment(AlienEquipment | None) -
alien_gem_ids(tuple[EnumOrInt['Gem'], ...]) -
general_id(ClientInt | None) -
star_level(ClientInt) -
level(ClientInt) -
general_xp(ClientInt | None) -
general_old_xp(ClientInt | None) -
general_is_new(bool | None) -
general_has_level_up(bool | None) -
general_skill_ids(tuple[EnumOrInt['GeneralSkill'], ...] | None) -
general_selected_abilities(tuple[SelectedAbility, ...] | None) -
locked_in_castle_id(ClientInt)
Validators:
-
_xp_or_zero→general_xp,general_old_xp -
_one_flag→general_is_new,general_has_level_up -
_skill_list→general_skill_ids -
_readable_slots→general_selected_abilities -
_default_commander_general -
_id_as_the_client_reads_it -
_alien_block→alien_equipment,temporary_equipment -
_gem_list→alien_gem_ids -
_note_raw_equipment -
_readable_equipment→equipment
locked_in_castle_id
pydantic-field
¶
locked_in_castle_id: ClientInt = 0
Wire key LICID
Castle the castellan is locked in, -1 for none; 0 when the entry has none
is_locked_in_castle
property
¶
is_locked_in_castle: bool
True when locked_in_castle_id is 0 or more.
is_available_for_movement
¶
Whether the client offers this castellan for a movement in kingdom_id.
Not when it is locked in a castle. A faction-portrait castellan (VIS 5) is
compared with FactionConst.BARON_ID (-16), not a kingdom id, so it is never
available in a real kingdom; an island-portrait one (VIS 13) only in the
storm islands (4). The client also refuses a castellan that already leads one of
the player's movements, which this model cannot see.
Client: BaronVO.isAvailableForMovement (bundle line 43535),
LordVO.isAvailableForMovement (bundle line 26607)
GetCommandersRequest
pydantic-model
¶
Bases: BaseRequest
Request the commander and castellan list.
Command: gli Payload: {}
Client: C2SGetLordsInfoVO (bundle line 32779, no fields)
CommanderRoster
pydantic-model
¶
Bases: BasePayload
A player's commanders and castellans, the gli block.
The lists are in the client's order: commanders by commander_id, castellans
by their portrait's place in CASTELLAN_PICTURE_ORDER, an unlisted portrait first.
Client: CastleLordData.parse_GLI (bundle line 38553), onSortLord and onSortBaron
(bundle lines 38572-38573), BaronVO.parseLord (bundle line 43534)
Fields:
Validators:
-
_no_block -
_readable_entries→commanders,castellans -
_client_order
GetCommandersResponse
pydantic-model
¶
Bases: BaseResponse, CommanderRoster
Response containing commanders (C) and castellans (B).
Command: gli
Client: GLICommand.executeCommand (bundle line 123976) hands it to
CastleLordData.parse_GLI
Fields:
-
commanders(list[Commander]) -
castellans(list[Castellan]) -
error_code(int)
Validators:
-
_no_block -
_readable_entries→commanders,castellans -
_client_order
RenameCommanderRequest
pydantic-model
¶
Bases: BaseRequest
Rename a commander or castellan.
Command: arl Payload: {"LID": commander_id, "N": name}
The game's dialog allows 3 to 15 characters (EquipmentConst.LORD_NAME_MIN_LENGTH
and LORD_NAME_MAX_LENGTH, dll line 19249); the server's own rules were not traced.
Client: C2SRenameLordVO (bundle line 65748), sent by CastleRenameLordDialog.sendCommand
(bundle line 65738)
Fields:
-
commander_id(int) -
name(str)
RenameCommanderResponse
pydantic-model
¶
Bases: BaseResponse
Reply to a rename: the full commander and castellan list.
Command: arl
Client: ARLCommand.executeCommand (bundle line 123657), which passes gli to
CastleLordData.parse_GLI
Fields:
commander_roster
pydantic-field
¶
commander_roster: CommanderRoster
Wire key gli
Commanders and castellans after the rename
commanders.models.skills¶
Generals and legend skills: the two attacker-side inputs that size a wave.
A general's unlocked skills widen the flanks; the player's legend skills widen them further and add waves, but only in a legendary fight. Both are read with their own command and neither arrives with the commander list.
Classes:
| Name | Description |
|---|---|
SelectedAbility |
One ability slot of a general: a |
GetGeneralsRequest |
Request every general the player owns. |
OwnedGeneral |
One general, as |
AssignGeneralRequest |
Assign a general to a commander, or take the commander's general away. |
AssignGeneralResponse |
Reply to a general assignment: the full commander list. |
SetGeneralAbilitiesRequest |
Choose a general's abilities, one per slot. |
UnlockGeneralSkillRequest |
Unlock a general skill. The skill id names the general, so none is sent. |
ResetGeneralSkillsRequest |
Reset a general's skill tree. |
AddGeneralXpRequest |
Feed a general xp items. |
GetGeneralsResponse |
Response listing the player's generals. |
GetSkillsRequest |
Request the player's own skill lists. |
ActivatingSceatSkill |
A Hall of Legends sceat skill still being activated, an entry of |
SkillList |
The player's legend and sceat skills, the |
GetSkillsResponse |
Response listing the player's legend and sceat skills. |
ObjectUpdateEvent |
An object update pushed by the server. |
SelectedAbility
pydantic-model
¶
Bases: BasePayload
One ability slot of a general: a [slot_id, ability_id] pair, as GASAIDS sends it and SAIDS takes it.
It serializes back to the pair, with -1 for an empty slot.
Client: GeneralVO.parseData (bundle line 26666) keeps the pairs, and
GeneralVO.getSelectedAbilities (bundle lines 26769-26771) reads [0] as the
slot and [1] as the ability, counting it only above 0; GeneralsAbilityDialog.onSave
(bundle line 27850) sends -1 for a cleared slot.
Fields:
-
slot_id(int) -
ability_id(EnumOrInt['GeneralAbility'] | None)
Validators:
-
_from_row -
_empty_slot→ability_id
GetGeneralsRequest
pydantic-model
¶
Bases: BaseRequest
Request every general the player owns.
Command: gie Payload: {}
Client: C2SGetGeneralsInfoVO (bundle line 56405, no fields)
OwnedGeneral
pydantic-model
¶
Bases: BasePayload
One general, as GeneralVO.parseData reads it.
skill_ids is the useful part: the skills it has unlocked, which is what
the attack dialog accumulates for the flank and front unit bonuses.
Client: GeneralVO.parseData (bundle line 26666)
Fields:
-
general_id(int) -
experience(int) -
star_level(int) -
is_new(bool) -
has_level_up(bool) -
skill_ids(tuple[EnumOrInt['GeneralSkill'], ...]) -
selected_abilities(list[SelectedAbility]) -
fixed_level(int) -
old_experience(int) -
wins(int) -
defeats(int)
Validators:
-
_skill_ids→skill_ids -
_zero_when_missing→experience,old_experience,wins,defeats,star_level -
_no_fixed_level→fixed_level -
_one_flag→is_new,has_level_up -
_skip_malformed_slots→selected_abilities -
_star_level_from_fixed_level
star_level
pydantic-field
¶
star_level: int = 0
Wire key ST
Star level; when none is set, derived from fixed_level if that is a multiple of 10
skill_ids
pydantic-field
¶
Wire key SIDS
Unlocked skills
selected_abilities
pydantic-field
¶
selected_abilities: list[SelectedAbility]
Wire key GASAIDS
The general's ability slots, filled or empty
ability_ids
property
¶
The abilities this general has selected, one id per filled slot, from either side.
Client: GeneralVO.getSelectedAbilities (bundle line 26769) skips a slot whose ability id is not above 0.
attack_ability_ids
¶
attack_ability_ids(general_def: GeneralDef) -> list[int]
The abilities selected in this general's attack slots.
Client: GeneralVO.getSelectedAbilities(true) (bundle line 26769)
defense_ability_ids
¶
defense_ability_ids(general_def: GeneralDef) -> list[int]
The abilities selected in this general's defense slots.
Client: GeneralVO.getSelectedAbilities(false) (bundle line 26769)
AssignGeneralRequest
pydantic-model
¶
Bases: BaseRequest
Assign a general to a commander, or take the commander's general away.
Command: gla Payload: {"LID": commander_id, "GID": general_id}
Client: C2SGeneralAssignLord (bundle line 101985)
Fields:
-
commander_id(int) -
general_id(int)
AssignGeneralResponse
pydantic-model
¶
Bases: BaseResponse
Reply to a general assignment: the full commander list.
Command: gla Payload: {"gli": {"C": [..], "B": [..]}}
Client: GLACommand.executeCommand (bundle line 124218) passes gli to parse_GLI.
Fields:
commander_roster
pydantic-field
¶
commander_roster: CommanderRoster
Wire key gli
The commanders and castellans after the change
SetGeneralAbilitiesRequest
pydantic-model
¶
Bases: BaseRequest
Choose a general's abilities, one per slot.
Command: gaae Payload: {"GID": general_id, "SAIDS": [[slot_id, ability_id], ..]}
The ability dialog sends every slot it shows, with -1 for a cleared one. The server replies with no body.
Client: C2SGeneralSelectAbilities (bundle line 70767),
GeneralsAbilityDialog.onSave (bundle line 27846)
Fields:
general_id
pydantic-field
¶
general_id: int
Wire key GID
The general whose abilities are chosen, an OwnedGeneral.general_id from client.skills.get_generals()
abilities
pydantic-field
¶
abilities: list[SelectedAbility]
Wire key SAIDS
Every slot the dialog shows, each sent as [slot_id, ability_id], -1 for none
UnlockGeneralSkillRequest
pydantic-model
¶
Bases: BaseRequest
Unlock a general skill. The skill id names the general, so none is sent.
Command: guse Payload: {"ID": skill_id}
The server replies with no body.
Client: C2SGeneralUnlockSkillVO (bundle line 67469), GUSECommand (bundle line 124280)
Fields:
ResetGeneralSkillsRequest
pydantic-model
¶
Bases: BaseRequest
Reset a general's skill tree.
Command: grs Payload: {"GID": general_id}
The server replies with no body.
Client: C2SGeneralResetSkills (bundle line 67460), GRSCommand (bundle line 124242)
Fields:
AddGeneralXpRequest
pydantic-model
¶
Bases: BaseRequest
Feed a general xp items.
Command: gaxp Payload: {"GID": general_id, "CID": currency_id, "AMT": amount}
The server replies with no body.
Client: C2SGeneralAddXpVO (bundle line 74507), GAXPCommand (bundle line 124176)
Fields:
-
general_id(int) -
currency_id(int) -
amount(int)
GetGeneralsResponse
pydantic-model
¶
Bases: BaseResponse
Response listing the player's generals.
Command: gie Payload: {"G": [{"GID": .., "SIDS": [skill ids], ..}, ..]}
Client: GIECommand.executeCommand (bundle line 124204) hands it to
GeneralsData.parse_GIE (bundle line 113069), which updates each general by GID.
Fields:
Validators:
-
_readable_generals→generals
GetSkillsRequest
pydantic-model
¶
Bases: BaseRequest
Request the player's own skill lists.
Command: skl Payload: {}
Client: C2SGetSkillListVO (bundle line 112219, no fields)
ActivatingSceatSkill
pydantic-model
¶
Bases: BasePayload
A Hall of Legends sceat skill still being activated, an entry of SSA.
Client: CastleLegendSkillData.parse_SKL (bundle line 112051)
Fields:
-
skill_id(EnumOrInt['SceatSkill'] | None) -
remaining_seconds(ClientInt)
SkillList
pydantic-model
¶
Bases: BasePayload
The player's legend and sceat skills, the skl block.
SID are the legend skills, which apply only in a legendary fight, and
SIDS the Hall of Legends sceat skills, which always apply.
Client: CastleLegendSkillData.parse_SKL (bundle line 112051)
Fields:
-
legend_skill_ids(tuple[EnumOrInt['LegendSkill'], ...]) -
sceat_skill_ids(tuple[EnumOrInt['SceatSkill'], ...]) -
total_points(ClientInt) -
seconds_until_reset(int | float) -
reset_count(ClientInt) -
activating(list[ActivatingSceatSkill])
Validators:
-
_seconds→seconds_until_reset -
_skill_ids→legend_skill_ids,sceat_skill_ids -
_readable_entries→activating
legend_skill_ids
pydantic-field
¶
Wire key SID
Legend skills, which apply only in a legendary fight
sceat_skill_ids
pydantic-field
¶
Wire key SIDS
Hall of Legends sceat skills, which always apply
seconds_until_reset
pydantic-field
¶
Wire key RS
Seconds until the skills can be reset, fractions included
reset_count
pydantic-field
¶
reset_count: ClientInt = 0
Wire key RC
How many times the skills have been reset
activating
pydantic-field
¶
activating: list[ActivatingSceatSkill]
Wire key SSA
Sceat skills still being activated
GetSkillsResponse
pydantic-model
¶
Bases: BaseResponse, SkillList
Response listing the player's legend and sceat skills.
Command: skl Payload: {"SID": [legend skill ids], "SIDS": [sceat skill ids], "SP": total_points, "RS": seconds_until_reset, "RC": reset_count, "SSA": [{"ID": .., "RS": ..}, ..]}
Client: SKLCommand.executeCommand (bundle line 129742)
Fields:
-
legend_skill_ids(tuple[EnumOrInt['LegendSkill'], ...]) -
sceat_skill_ids(tuple[EnumOrInt['SceatSkill'], ...]) -
total_points(ClientInt) -
seconds_until_reset(int | float) -
reset_count(ClientInt) -
activating(list[ActivatingSceatSkill]) -
error_code(int)
Validators:
-
_seconds→seconds_until_reset -
_skill_ids→legend_skill_ids,sceat_skill_ids -
_readable_entries→activating
ObjectUpdateEvent
pydantic-model
¶
Bases: BaseResponse
An object update pushed by the server.
Only the skill list is read here; the area update the client also takes from it stays in the extra fields.
Command: ego
Client: EGOCommand.executeCommand (bundle line 122801)
Fields:
-
error_code(int) -
skills(SkillList | None)
Validators:
-
_no_skills→skills