Skip to content

Defense

Castle defense setups behind client.defense.

defense.service

The defense of your own castles and of castles you could support.

The setters take the keep, wall and moat as get_own_defense returns them, so a setup can be read, changed and sent back.

Classes:

Name Description
DefenseService

The defense of your own castles and of castles you could support.

Attributes:

Name Type Description
logger

logger module-attribute

logger = logging.getLogger(__name__)

DefenseService

DefenseService(client: 'EmpireClient')

Bases: BaseService

The defense of your own castles and of castles you could support.

Reached as client.defense.

Methods:

Name Description
get_own_defense

Get the keep, wall and moat setup of one of your own castles.

set_keep

Set the keep's tools, support tools and unit settings of one of your castles.

set_wall

Set the wall's tools and unit split of one of your castles.

set_moat

Set the moat's tools of one of your castles.

get_support_defense_info

Get the defense of another alliance member's castle, as the client asks before sending support.

get_own_defense

get_own_defense(castle_x: int, castle_y: int, castle_id: int, kingdom: Kingdom | None = None, timeout: float = 5.0) -> GetDefenseResponse

Get the keep, wall and moat setup of one of your own castles.

Parameters:

Name Type Description Default
castle_x int

The castle's map x

required
castle_y int

The castle's map y

required
castle_id int

The castle's id: CastleInfo.castle_id from client.castle.get_all() or Castle.id from client.state.get_castles()

required
kingdom Kingdom | None

The castle's kingdom; None sends -1, as the client does

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
GetDefenseResponse

The dfc reply: units, wall, keep, moat, priorities and castellan.

Raises:

Type Description
CommandError / EmpireTimeoutError / ConnectionClosedError

see EmpireClient.send

Client: C2SDefenceCompleteVO (bundle line 32769), sent by CastleDefenceDialog.updateDefenceData (bundle line 15978)

set_keep

set_keep(castle_x: int, castle_y: int, castle_id: int, keep: KeepDefense, timeout: float = 5.0) -> KeepDefense

Set the keep's tools, support tools and unit settings of one of your castles.

Sends keep.slots, keep.support_tool_slots, keep.min_attacking_units_for_tools and keep.unit_composition.

Parameters:

Name Type Description Default
castle_x int

The castle's map x

required
castle_y int

The castle's map y

required
castle_id int

The castle's id, as for get_own_defense

required
keep KeepDefense

The keep setup, get_own_defense(...).keep changed as wanted

required
timeout float

Timeout in seconds

5.0

Returns:

Type Description
KeepDefense

The dfk reply: the keep setup the server stored.

Raises:

Type Description
CommandError / EmpireTimeoutError / ConnectionClosedError

see EmpireClient.send

Client: C2SDefenceKeepVO (bundle line 66597), sent by CastleDefenceDialog.sendKeepData (bundle line 16041); DFKCommand (bundle line 123495)

set_wall

set_wall(castle_x: int, castle_y: int, castle_id: int, wall: WallDefense, timeout: float = 5.0) -> WallDefense

Set the wall's tools and unit split of one of your castles.

Sends each section's slots, unit_percent (a whole percent, as the client rounds its slider) and unit_composition.

Parameters:

Name Type Description Default
castle_x int

The castle's map x

required
castle_y int

The castle's map y

required
castle_id int

The castle's id, as for get_own_defense

required
wall WallDefense

The wall setup, get_own_defense(...).wall changed as wanted

required
timeout float

Timeout in seconds

5.0

Returns:

Type Description
WallDefense

The dfw reply: the wall setup the server stored.

Raises:

Type Description
CommandError / EmpireTimeoutError / ConnectionClosedError

see EmpireClient.send

Client: C2SDefenceWallVO (bundle line 66616), sent by CastleDefenceDialog.sendWallData (bundle line 16045) with calcUnitPercentArray (bundle line 15985); DFWCommand (bundle line 123525)

set_moat

set_moat(castle_x: int, castle_y: int, castle_id: int, moat: MoatDefense, timeout: float = 5.0) -> MoatDefense

Set the moat's tools of one of your castles.

Parameters:

Name Type Description Default
castle_x int

The castle's map x

required
castle_y int

The castle's map y

required
castle_id int

The castle's id, as for get_own_defense

required
moat MoatDefense

The moat setup, get_own_defense(...).moat changed as wanted

required
timeout float

Timeout in seconds

5.0

Returns:

Type Description
MoatDefense

The dfm reply: the moat setup the server stored.

Raises:

Type Description
CommandError / EmpireTimeoutError / ConnectionClosedError

see EmpireClient.send

Client: C2SDefenceMoatVO (bundle line 66607), sent by CastleDefenceDialog.sendMoatData (bundle line 16049); DFMCommand (bundle line 123510)

get_support_defense_info

get_support_defense_info(target_x: int, target_y: int, source_x: int | None = None, source_y: int | None = None, timeout: float = 5.0) -> GetSupportDefenseResponse

Get the defense of another alliance member's castle, as the client asks before sending support.

The server rejects your own castle with NO_SELF_DESTRUCTION (92); use get_own_defense for that.

Parameters:

Name Type Description Default
target_x int

Map x of the castle to support

required
target_y int

Map y of the castle to support

required
source_x int | None

Map x of your castle the support would leave from (defaults to your first castle)

None
source_y int | None

Map y of your castle the support would leave from (defaults to your first castle)

None
timeout float

Timeout in seconds

5.0

Returns:

Type Description
GetSupportDefenseResponse

GetSupportDefenseResponse with defense info: defense_positions.total()

GetSupportDefenseResponse

counts every defender.

Raises:

Type Description
ValueError

No source coordinates given and no own castle known

CommandError / EmpireTimeoutError / ConnectionClosedError

see EmpireClient.send

Client: C2SSupportDefenceInfoVO (bundle line 72078), sent by CastleStartAttackDialog.supportDefence (bundle line 14833)

defense.models

Defense protocol models.

Commands:

  • dfc: Read a castle's keep, wall and moat setup
  • dfk: Set the keep's tools and unit settings
  • dfw: Set the wall's tools and unit split
  • dfm: Set the moat's tools
  • sdi: Defense of a castle you could send support to

Classes:

Name Description
GetDefenseRequest

Read the defense setup of one of your castles.

WallSection

One wall section's setup as the dfw reply carries it; a missing

WallDefense

The wall setup: the dfw reply, also nested in dfc.

KeepDefense

The keep setup: the dfk reply, also nested in dfc.

MoatDefense

The moat setup: the dfm reply, also nested in dfc.

GetDefenseResponse

A castle's whole defense setup.

ChangeKeepDefenseRequest

Set the keep's tools, support tools and unit settings.

WallSectionSetup

One wall section in a dfw request; the client has no defaults.

ChangeWallDefenseRequest

Set the wall's tools and unit split per section.

ChangeMoatDefenseRequest

Set the moat's tools.

GetSupportDefenseRequest

Get the defense of a castle you could send support to, before sending it.

GetSupportDefenseResponse

Defense information for an alliance member's castle.

logger module-attribute

logger = logging.getLogger(__name__)

GetDefenseRequest pydantic-model

Bases: BaseRequest

Read the defense setup of one of your castles.

Command: dfc Payload: {"CX": castle_x, "CY": castle_y, "AID": area_id, "KID": kingdom}

KID is -1 unless a kingdom is given: the VO defaults it to -1, and every call site passes -1 or leaves it out.

Client: C2SDefenceCompleteVO (bundle line 32769); every call site passes the castle's absAreaPos and objectId, with KID -1 or omitted (CastleDefenceDialog.updateDefenceData, bundle line 15978)

Fields:

Validators:

castle_x pydantic-field

castle_x: int

Wire key CX

Castle map x

castle_y pydantic-field

castle_y: int

Wire key CY

Castle map y

area_id pydantic-field

area_id: int

Wire key AID

The castle's id: CastleInfo.castle_id from client.castle.get_all() or Castle.id

kingdom_id pydantic-field

kingdom_id: Kingdom | None = None

Wire key KID

The castle's kingdom; None for none

to_payload

to_payload() -> dict[str, Any]

accepts_reply

accepts_reply(payload: Any) -> bool

Whether a dfc reply is about this castle: its area row A, when sent, lies at CX/CY.

Client: DFCCommand (bundle line 123480) through parse_DFC (bundle line 134243), which reads the castle from A with parseWorldMapArea.

WallSection pydantic-model

Bases: BasePayload

One wall section's setup as the dfw reply carries it; a missing number reads as 0, as the client's int() gives.

Client: CastleDefenceData.parse_DFW (bundle line 134250)

Fields:

slots pydantic-field

slots: WodAmountSlots = ()

Wire key S

Wall tool slots

unit_percent pydantic-field

unit_percent: ClientInt = 0

Wire key UP

Share of the wall's units on this section

unit_composition pydantic-field

unit_composition: ClientInt = 0

Wire key UC

Unit composition of this section

WallDefense pydantic-model

Bases: BaseResponse

The wall setup: the dfw reply, also nested in dfc.

Command: dfw Client: DFWCommand.executeCommand (bundle line 123525), CastleDefenceData.parse_DFW (bundle line 134250)

Fields:

left pydantic-field

Wire key L

Left wall section

middle pydantic-field

middle: WallSection

Wire key M

Middle wall section

right pydantic-field

right: WallSection

Wire key R

Right wall section

unit_count pydantic-field

unit_count: ClientInt = 0

Wire key U

Units on the wall

unit_slot_count pydantic-field

unit_slot_count: ClientInt = 0

Wire key US

Units the wall can hold

defense pydantic-field

defense: ClientInt = 0

Wire key D

Wall defence, as a whole number

KeepDefense pydantic-model

Bases: BaseResponse

The keep setup: the dfk reply, also nested in dfc.

Command: dfk Client: DFKCommand.executeCommand (bundle line 123495), CastleDefenceData.parse_DFK (bundle line 134251)

Fields:

slots pydantic-field

slots: WodAmountSlots = ()

Wire key S

Keep tool slots

support_tool_slots pydantic-field

support_tool_slots: WodAmountSlots = ()

Wire key STS

Keep support-tool slots

alliance_unit_yard_limit pydantic-field

alliance_unit_yard_limit: ClientInt = 0

Wire key AUYL

Alliance unit yard limit

unit_yard_limit pydantic-field

unit_yard_limit: ClientInt = 0

Wire key UYL

Unit yard limit

unit_count pydantic-field

unit_count: ClientInt = 0

Wire key U

Units in the keep

unit_composition pydantic-field

unit_composition: ClientInt = 0

Wire key UC

Keep unit composition

min_attacking_units_for_tools pydantic-field

min_attacking_units_for_tools: ClientInt = 0

Wire key MAUCT

Minimum attacking units before the keep's tools are used

keep_unit_slot_count property

keep_unit_slot_count: int

Units the keep can hold: UYL - AUYL.

The client makes it unlimited on special servers and in daimyo townships, which this reply cannot tell.

MoatDefense pydantic-model

Bases: BaseResponse

The moat setup: the dfm reply, also nested in dfc.

Command: dfm Client: DFMCommand.executeCommand (bundle line 123510), CastleDefenceData.parse_DFM (bundle line 134252)

Fields:

left_slots pydantic-field

left_slots: WodAmountSlots = ()

Wire key LS

Left moat slots

middle_slots pydantic-field

middle_slots: WodAmountSlots = ()

Wire key MS

Middle moat slots

right_slots pydantic-field

right_slots: WodAmountSlots = ()

Wire key RS

Right moat slots

defense pydantic-field

defense: ClientInt = 0

Wire key D

Moat defence, as a whole number

GetDefenseResponse pydantic-model

Bases: BaseResponse

A castle's whole defense setup.

Command: dfc Client: DFCCommand.executeCommand (bundle line 123480), CastleDefenceData.parse_DFC (bundle line 134243). The server also sends MDS and RDS, which the client does not read.

Fields:

Validators:

  • _castellan_id_from_l
  • _castellan_or_none → castellan

unit_inventory pydantic-field

unit_inventory: UnitInventory

Wire key gui

The castle's unit inventory

area pydantic-field

area: MovementArea | None = None

Wire key A

The castle's map row

home_defense_workshop_level pydantic-field

home_defense_workshop_level: int | None = None

Wire key HDWL

Defense workshop level, which unlocks the keep's support-tool slots

wall pydantic-field

wall: WallDefense | None = None

Wire key dfw

Wall setup

keep pydantic-field

keep: KeepDefense | None = None

Wire key dfk

Keep setup

moat pydantic-field

moat: MoatDefense | None = None

Wire key dfm

Moat setup

range_priority pydantic-field

range_priority: tuple[EnumOrInt['Unit'], ...] = ()

Wire key PR

The ranged units in the order the castle places them on the wall

melee_priority pydantic-field

melee_priority: tuple[EnumOrInt['Unit'], ...] = ()

Wire key PM

The melee units in the order the castle places them on the wall

gate_defense pydantic-field

gate_defense: ClientInt = 0

Wire key GD

Gate defence, as a whole number

castellan pydantic-field

castellan: Castellan | None = None

Wire key L

The castle's castellan; None when there is none

listed_castellan_id pydantic-field

listed_castellan_id: ClientInt = -1

The listed castellan's id; -1 when none is listed

castellan_id property

castellan_id: int

The castellan's id, int(L.ID); -1 when none is set, as the client starts from.

inventory

inventory() -> dict[UnitOrTool, int]

The castle's units as {Unit or Tool: amount}, the only inventory parse_DFC reads.

ChangeKeepDefenseRequest pydantic-model

Bases: BaseRequest

Set the keep's tools, support tools and unit settings.

The client builds the slot lists from its keep containers, one pair per slot; start from the dfc reply's dfk lists. Fields follow the client's key order.

Command: dfk Client: C2SDefenceKeepVO (bundle line 66597), built by CastleDefenceDialog.sendKeepData (bundle line 16041), which sends an empty STS when there is no support-tool container

Fields:

castle_x pydantic-field

castle_x: int

Wire key CX

Castle map x

castle_y pydantic-field

castle_y: int

Wire key CY

Castle map y

area_id pydantic-field

area_id: int

Wire key AID

The castle's id: CastleInfo.castle_id from client.castle.get_all() or Castle.id

min_attacking_units_for_tools pydantic-field

min_attacking_units_for_tools: int = 0

Wire key MAUCT

Minimum attacking units before the keep's tools are used

unit_composition pydantic-field

unit_composition: int = 50

Wire key UC

Keep unit composition

slots pydantic-field

slots: WodAmountSlots

Wire key S

Keep tool slots

support_tool_slots pydantic-field

support_tool_slots: WodAmountSlots = ()

Wire key STS

Keep support-tool slots

WallSectionSetup pydantic-model

Bases: BasePayload

One wall section in a dfw request; the client has no defaults.

Client: C2SDefenceWallVO (bundle line 66616)

Fields:

slots pydantic-field

slots: WodAmountSlots

Wire key S

Wall tool slots

unit_percent pydantic-field

unit_percent: int

Wire key UP

Share of the wall's units on this section

unit_composition pydantic-field

unit_composition: int

Wire key UC

Unit composition of this section

ChangeWallDefenseRequest pydantic-model

Bases: BaseRequest

Set the wall's tools and unit split per section.

Every section needs its slots, unit percent and unit composition; the client has no defaults for them. Start from the dfc reply's dfw.

Command: dfw Client: C2SDefenceWallVO (bundle line 66616), built by CastleDefenceDialog.sendWallData (bundle line 16045)

Fields:

castle_x pydantic-field

castle_x: int

Wire key CX

Castle map x

castle_y pydantic-field

castle_y: int

Wire key CY

Castle map y

area_id pydantic-field

area_id: int

Wire key AID

The castle's id: CastleInfo.castle_id from client.castle.get_all() or Castle.id

left pydantic-field

Wire key L

Left wall section

middle pydantic-field

Wire key M

Middle wall section

right pydantic-field

Wire key R

Right wall section

ChangeMoatDefenseRequest pydantic-model

Bases: BaseRequest

Set the moat's tools.

Start from the dfc reply's dfm lists.

Command: dfm Client: C2SDefenceMoatVO (bundle line 66607), built by CastleDefenceDialog.sendMoatData (bundle line 16049)

Fields:

castle_x pydantic-field

castle_x: int

Wire key CX

Castle map x

castle_y pydantic-field

castle_y: int

Wire key CY

Castle map y

area_id pydantic-field

area_id: int

Wire key AID

The castle's id: CastleInfo.castle_id from client.castle.get_all() or Castle.id

left_slots pydantic-field

left_slots: WodAmountSlots

Wire key LS

Left moat slots

middle_slots pydantic-field

middle_slots: WodAmountSlots

Wire key MS

Middle moat slots

right_slots pydantic-field

right_slots: WodAmountSlots

Wire key RS

Right moat slots

GetSupportDefenseRequest pydantic-model

Bases: BaseRequest

Get the defense of a castle you could send support to, before sending it.

Command: sdi Payload: {"TX": target_x, "TY": target_y, "SX": source_x, "SY": source_y}

The client sends it from the Support button, which it shows for a castle of another member of your alliance (and a few other areas, such as your own outposts), never for your own castle; the server answers your own castle with NO_SELF_DESTRUCTION (92). Read your own castle with dfc.

Client: C2SSupportDefenceInfoVO (bundle line 72078), sent by CastleStartAttackDialog.supportDefence (bundle line 14833); CastleMapobjectVO.canBeSupported (bundle line 18918), OutpostMapobjectVO.canBeSupported (bundle line 18820)

Fields:

target_x pydantic-field

target_x: int

Wire key TX

Map x of the castle to support

target_y pydantic-field

target_y: int

Wire key TY

Map y of the castle to support

source_x pydantic-field

source_x: int

Wire key SX

Map x of your castle the support would leave from

source_y pydantic-field

source_y: int

Wire key SY

Map y of your castle the support would leave from

GetSupportDefenseResponse pydantic-model

Bases: BaseResponse

Defense information for an alliance member's castle.

Command: sdi Payload:

{"SCID": source_castle_id,
 "S": [left, middle, right, keep, stronghold, support, reserve],
 "AS": spy_age_seconds, "B": {castellan}, "abe": {castellan}, "LS": [...],
 "gaa": {"AI": [...], "OI": [...]},
 "gui": {"I": [[wod_id, count], ...], "SHI": [...]},
 "gli": {"C": [...], "B": [...]},
 "UYL": yard_limit, "AUYL": alliance_yard_limit, "UWL": wall_limit}

The client reads S, AS, the castellan and LS into the same CastleSpyArmyInfoVO a spy report and an attack pre-calculation use. It takes the castellan from abe when the target is an alliance battleground tower and from B otherwise.

Client: SDICommand.executeCommand (bundle line 122352), which passes gli to CastleLordData.parse_GLI; CastleSupportDefenceVO.fillFromParamObject (bundle line 140066); CastleSpyArmyInfoVO.parseArmyInfo (bundle line 30699).

Fields:

Validators:

castle_id pydantic-field

castle_id: int = 0

Wire key SCID

defense_positions pydantic-field

defense_positions: SpyArmyBlock = None

Wire key S

The castle's defenders by the position they hold; None when the reply lists none

castellan pydantic-field

castellan: Castellan | None = None

Wire key B

The castle's castellan, without its equipment; None when there is none

tower_castellan pydantic-field

tower_castellan: Castellan | None = None

Wire key abe

The castellan of an alliance battleground tower, in place of castellan

unit_inventory pydantic-field

unit_inventory: UnitInventory

Wire key gui

Your own units and tools, and your stronghold units

commander_roster pydantic-field

commander_roster: CommanderRoster

Wire key gli

Your commanders and castellans

yard_limit pydantic-field

yard_limit: int = 0

Wire key UYL

Courtyard unit limit, the alliance share (available_yard_limit) included

available_yard_limit pydantic-field

available_yard_limit: int = 0

Wire key AUYL

The share of the courtyard limit open to alliance support

wall_limit pydantic-field

wall_limit: int = 0

Wire key UWL

Wall unit limit

get_max_defense

get_max_defense() -> int

Get the maximum defense capacity for this castle.

UYL (yard_limit) represents the total capacity including courtyard limit plus room for alliance support.

Returns:

Type Description
int

Maximum number of troops that can defend this castle.