Messages¶
The mailbox, its reports and battle reports behind client.messages.
messages.service¶
The mailbox, mail and battle reports.
Classes:
| Name | Description |
|---|---|
MessagesService |
The mailbox, mail and battle reports. |
Attributes:
| Name | Type | Description |
|---|---|---|
BattleReportDetail |
|
MessagesService
¶
MessagesService(client: EmpireClient)
Bases: BaseService
The mailbox, mail and battle reports.
Accessible via client.messages. The mailbox is kept as the client keeps it:
the login data's sne section fills it, each sne push row replaces the
message with its id or is added, and dms and ams answers remove or archive.
Client: CastleMessageData (bundle lines 134955-134991); GBDCommand.exec
(bundle line 129381) and SNECommand.exec (bundle line 125499) both call
parse_SNE (bundle line 134961)
Methods:
| Name | Description |
|---|---|
on_new_messages |
Call |
read |
Read a message's body. |
mark_read |
Mark a message read. The client has no handler for an answer, so none is waited for. |
archive |
Move a message to the archive, which holds 20. |
delete |
Delete one message. |
delete_many |
Delete several messages at once. |
send_message |
Send a message to a player. |
get_battle_report |
Read a battle report from the mailbox. |
forward_battle_report |
Forward a battle report to other players. |
Attributes:
| Name | Type | Description |
|---|---|---|
mailbox |
list[MessageInfo]
|
The messages from the login data and the pushes since, in the order they first arrived. |
mailbox
property
¶
mailbox: list[MessageInfo]
The messages from the login data and the pushes since, in the order they first arrived.
on_new_messages
¶
on_new_messages(callback: Callable[[SystemNotificationEvent], object]) -> None
Call callback with the login data's sne section and each sne push, after mailbox is updated.
Removing a callback not registered is a no-op.
Unregister a callback with on_new_messages.remove(callback).
read
¶
read(message_id: int, timeout: float = 5.0) -> ReadMessageResponse
Read a message's body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message_id
|
int
|
The message's |
required |
Raises:
| Type | Description |
|---|---|
MessageUnavailableError
|
error 66 (no such message) or 225 (too old to read), which the client
answers alike ( |
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
mark_read
¶
mark_read(message_id: int) -> None
Mark a message read. The client has no handler for an answer, so none is waited for.
The message in mailbox is marked read too, as the client marks its own copy.
archive
¶
archive(message_id: int, timeout: float = 5.0) -> ArchiveMessageResponse
Move a message to the archive, which holds 20.
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
delete
¶
Delete one message.
Returns:
| Type | Description |
|---|---|
list[int]
|
The ids the server reports deleted |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
delete_many
¶
Delete several messages at once.
Returns:
| Type | Description |
|---|---|
list[int]
|
The ids the server reports deleted |
Raises:
| Type | Description |
|---|---|
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
send_message
¶
Send a message to a player.
The limits are the client's: a subject of at most 20 characters, and a
text of at most 1300 with at least 3 that are not whitespace
(ClientConstMessage.isValidText, bundle line 44214). The receiver's
name field takes 15 characters, more on special and crossplay servers,
so its length is left to the server.
Raises:
| Type | Description |
|---|---|
ValueError
|
the receiver is empty, or the subject or text breaks a limit |
CommandError
|
the server refused the message, e.g. error 68 (no such receiver), 69 (the receiver ignores you) or 70 (a bad word) |
EmpireTimeoutError / ConnectionClosedError
|
see |
get_battle_report
¶
get_battle_report(message_id: int, detail: BattleReportDetail = 'short', timeout: float = 5.0) -> BattleReport
Read a battle report from the mailbox.
The short log comes first, a bls with IM 0 as the client always sends it;
the middle and detail logs are then asked for by its log_id, one request each,
as the client asks when its battle report dialogs open them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message_id
|
int
|
A battle log's |
required |
detail
|
BattleReportDetail
|
|
'short'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
MessageUnavailableError
|
error 66 (no such message) or 225 (too old to read) for the short
log, which the client shows as a log that does not exist ( |
CommandError / EmpireTimeoutError / ConnectionClosedError
|
see |
Client: CastleMessageData.getBattleLogShort, getBattleLogMiddle and
getBattleLogDetailed (bundle lines 134905-134907)
forward_battle_report
¶
Forward a battle report to other players.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message_id
|
int
|
A battle log's |
required |
player_ids
|
list[int]
|
The recipients. The client offers the members of your alliance
other than you, |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
no recipients |
CommandError
|
the server refused; the client handles no error specially |
EmpireTimeoutError / ConnectionClosedError
|
see |
Client: CastleForwardMessageDialog.sendMessage (bundle line 60740),
MFBCommand.executeCommand (bundle line 125396)
messages.models.mailbox¶
Mailbox, mail and report protocol models.
Commands:
- sne: New or changed mailbox messages (push)
- rms, mmr, ams, dms, sms: Read, mark read, archive, delete and send mail
- bsd: Spy report (the client's S2C_SPY_LOG_DETAIL)
- mfs: Forward a spy report
Classes:
| Name | Description |
|---|---|
MessageInfo |
One mailbox message: an entry of |
SpyLogHeader |
What a spy log's header says: |
BattleLogHeader |
What a battle log's header says. |
PatchNoteHeader |
What a patch note's header says: |
EventAnnouncementHeader |
What an event announcement's header says: |
SystemNotificationEvent |
New mailbox messages, pushed by the server. |
GetSpyReportRequest |
Request a spy report from the mailbox. |
ForwardSpyLogRequest |
Forward a spy report to other players. |
SpyReportArea |
The spied area, a spy report's |
SpyReportResponse |
A spy report. |
ReadMessageRequest |
Read a message's body. |
ReadMessageResponse |
A message's body. |
MarkMessageReadRequest |
Mark a message read. |
ArchiveMessageRequest |
Move a message to the archive. |
ArchiveMessageResponse |
The archived message. |
DeleteMessageRequest |
Delete one message. |
DeleteMessagesRequest |
Delete several messages. |
DeleteMessagesResponse |
The deleted messages. |
SendMessageRequest |
Send a message to a player. |
SendMessageResponse |
The answer to sending a message. |
Functions:
| Name | Description |
|---|---|
repair_header |
A message header decoded as the client decodes it before reading it. |
Attributes:
| Name | Type | Description |
|---|---|---|
SPY_VALIDITY |
|
|
MAX_MAILBOX_SIZE |
Messages the mailbox holds. Client: |
|
MAX_MAILBOX_ARCHIVE_SIZE |
Archived messages the mailbox holds. Client: |
|
MAX_MAILBOX_BATTLE_AND_SPY_REPORTS |
Battle and spy reports the mailbox holds. Client: |
|
MAX_SUBJECT_LENGTH |
Characters the client's subject field takes. Client: |
|
MAX_TEXT_LENGTH |
Characters the client's text field takes. Client: |
|
MIN_TEXT_LENGTH |
Non-whitespace characters a text needs. Client: |
SPY_VALIDITY
module-attribute
¶
SpyConst.SPY_VALIDITY (dll line 19739): seconds a spy report's army stays current.
MAX_MAILBOX_SIZE
module-attribute
¶
Messages the mailbox holds. Client: MessageConst.MAX_MAILBOX_SIZE (dll line 19516)
MAX_MAILBOX_ARCHIVE_SIZE
module-attribute
¶
Archived messages the mailbox holds. Client: MessageConst.MAX_MAILBOX_ARCHIVE_SIZE (dll line 19516)
MAX_MAILBOX_BATTLE_AND_SPY_REPORTS
module-attribute
¶
Battle and spy reports the mailbox holds. Client: MAX_MAILBOX_BATTLE_AND_SPY_REPORTS (dll line 19516)
MAX_SUBJECT_LENGTH
module-attribute
¶
Characters the client's subject field takes. Client: MessageConst.MAX_LENGTH_SUBJECT (dll line 19516)
MAX_TEXT_LENGTH
module-attribute
¶
Characters the client's text field takes. Client: MessageConst.MAX_LENGTH_TEXT (dll line 19516)
MIN_TEXT_LENGTH
module-attribute
¶
Non-whitespace characters a text needs. Client: ClientConstMessage.MINIMUM_LENGTH (bundle line 44219)
MessageInfo
pydantic-model
¶
Bases: BasePayload
One mailbox message: an entry of sne's MSG.
Client: AMessageVO.loadFromParamArray (bundle line 3808), reached through
CastleMessageData.parse_SNE and CastleMessageFactory.parseMessage (bundle line 135102).
Fields:
-
message_id(int) -
message_type(EnumOrInt[MessageType]) -
header(str) -
sender_name(str) -
sender_id(ClientInt) -
seconds_since_sent(int | float | None) -
is_read(bool) -
is_archived(bool) -
is_forwarded(bool)
Validators:
-
_from_row -
_seconds→seconds_since_sent -
_no_text→header,sender_name -
_one_flag→is_read,is_archived -
_int_one_flag→is_forwarded
message_type
pydantic-field
¶
message_type: EnumOrInt[MessageType]
Message type; a plain int for a type the client does not define
seconds_since_sent
pydantic-field
¶
Seconds since the message was sent; None when the row has no number for it
decoded_header
property
¶
decoded_header: str
header decoded as the client decodes it before reading it; see repair_header.
subject
property
¶
subject: str | None
The subject of player mail, the alliance newsletter and every type the client reads as mail.
None for the types whose header has a layout of its own (reports, notices, offers).
Client: MessageUserVO.parseMessageHeader and parseSubject (bundle line 61340)
turn the decoded header's carriage returns and newlines into spaces
is_spy_log
property
¶
is_spy_log: bool
A spy log: a message about a player's area (SPY_PLAYER) or an NPC's (SPY_NPC).
is_battle_log
property
¶
is_battle_log: bool
A battle log: a battle report, read with client.messages.get_battle_report.
spy_log_header
¶
spy_log_header() -> SpyLogHeader | None
This message's spy log header, or None when it is no readable spy log.
battle_log_header
¶
battle_log_header() -> BattleLogHeader | None
This message's battle log header, or None when it is no readable battle log.
patch_note_header
¶
patch_note_header() -> PatchNoteHeader | None
This message's patch note header, or None when it is no patch note.
event_announcement_header
¶
event_announcement_header() -> EventAnnouncementHeader | None
This message's event announcement header, or None when it is no event announcement.
SpyLogHeader
dataclass
¶
SpyLogHeader(log_type: SpyLogType | None, result: LogResult | None, area_type: int | None = None, kingdom_id: int | None = None, owner_id: int | None = None, area_name: str | None = None, area_id: int | None = None)
What a spy log's header says: subtypeSpy+subtypeResult+areaType#kingdomID+ownerID+areaName.
The part after # is areaName+areaID instead for a sabotage or
plague monk log about a player's area that ended in the attacker's
success or the defender's failure. A number the header lacks, or that
parseInt cannot read, is None; so is a subtype or result the client
defines no constant for.
It is read from the header as repair_header decodes it, as
AMessageVO.loadFromParamArray (bundle line 3809) decodes every header
before parsing it.
Client: CastleMessageFactory.parseMessage (bundle line 135102),
MessageSpyPlayerVO.parseMessageHeader (bundle lines 137651-137653),
MessageSpyNpcVO.parseMessageHeader (bundle lines 137635-137636)
Methods:
| Name | Description |
|---|---|
from_message |
The header of a spy log, or None when the message is no spy log or its header has no |
Attributes:
| Name | Type | Description |
|---|---|---|
log_type |
SpyLogType | None
|
|
result |
LogResult | None
|
|
area_type |
int | None
|
|
kingdom_id |
int | None
|
|
owner_id |
int | None
|
|
area_name |
str | None
|
|
area_id |
int | None
|
|
spies_lost |
bool
|
The mission failed: the attacker failed or the defender succeeded. |
has_army_report |
bool
|
A military mission that succeeded, whose report lists the spied army. |
spies_lost
property
¶
spies_lost: bool
The mission failed: the attacker failed or the defender succeeded.
Client: AMessageSpyVO.isFailedSpyLog (bundle line 40204)
has_army_report
property
¶
has_army_report: bool
A military mission that succeeded, whose report lists the spied army.
Client: MessageSpyPlayerVO.hasDetailedSpyLog (bundle line 137672),
MessageSpyNpcVO.hasDetailedSpyLog (bundle line 137642)
from_message
classmethod
¶
from_message(message: MessageInfo) -> SpyLogHeader | None
The header of a spy log, or None when the message is no spy log or its header has no # part.
MessageSpyPlayerVO reads nothing from a header without the part
after #, and MessageSpyNpcVO throws on one.
BattleLogHeader
dataclass
¶
BattleLogHeader(area_type: int | None, attack_type: BattleLogAttackType | None, result: LogResult | None, treasure_map_id: int | None = -1, treasure_map_area_type: int | None = -1, kingdom_id: int | None = None, owner_id: int | None = None, area_name: str = '')
What a battle log's header says.
The header is areaType+subtypeAttack+subtypeResult[+tMapID[+tMapAreaType]],
then #kingdomID+ownerID[+areaName].
A number the header lacks, or that parseInt cannot read, is None; the
two treasure map numbers are -1 when the header has none, as the client
keeps them. An attack type or result the client defines no constant for is None.
It is read from the header as repair_header decodes it.
Client: CastleMessageFactory.parseMessage (bundle line 135117),
MessageBattleLogVO.parseMessageHeader (bundle lines 135579-135580),
MessageConst.SUBTYPE_META_DATA_SPLITTER (dll line 19516)
Methods:
| Name | Description |
|---|---|
from_message |
The header of a battle log, or None when the message is no battle log or its header has no |
Attributes:
| Name | Type | Description |
|---|---|---|
area_type |
int | None
|
|
attack_type |
BattleLogAttackType | None
|
|
result |
LogResult | None
|
|
treasure_map_id |
int | None
|
|
treasure_map_area_type |
int | None
|
|
kingdom_id |
int | None
|
|
owner_id |
int | None
|
|
area_name |
str
|
|
from_message
classmethod
¶
from_message(message: MessageInfo) -> BattleLogHeader | None
The header of a battle log, or None when the message is no battle log or its header has no # part.
MessageBattleLogVO throws on a header without the part after #.
PatchNoteHeader
dataclass
¶
What a patch note's header says: teaserId+patchNoteId+collected.
A number the header lacks, or that parseInt cannot read, is None.
Client: MessageChangelistVO.parseMessageHeader (bundle line 136053), reached through
CastleMessageFactory.parseMessage (bundle line 135102)
Methods:
| Name | Description |
|---|---|
from_message |
The header of a patch note, or None when the message is no patch note. |
Attributes:
| Name | Type | Description |
|---|---|---|
teaser_id |
int | None
|
|
patch_note_id |
int | None
|
|
collected |
bool
|
|
from_message
classmethod
¶
from_message(message: MessageInfo) -> PatchNoteHeader | None
The header of a patch note, or None when the message is no patch note.
EventAnnouncementHeader
dataclass
¶
What an event announcement's header says: subtype#eventId#durationTS.
event is None when the header has no event part; end_timestamp is None when it has
no end the client can compare, and such an announcement never expires, as for the client.
Client: MessageEventAnnouncementVO.parseMessageHeader (bundle line 136509), split on
MessageConst.SUBTYPE_META_DATA_SPLITTER (dll line 19516)
Methods:
| Name | Description |
|---|---|
from_message |
The header of an event announcement, or None when the message is no event announcement. |
is_expired |
Whether the announced event's time is over, by the wall clock ( |
Attributes:
| Name | Type | Description |
|---|---|---|
event |
Event | int | None
|
|
end_timestamp |
int | float | None
|
|
from_message
classmethod
¶
from_message(message: MessageInfo) -> EventAnnouncementHeader | None
The header of an event announcement, or None when the message is no event announcement.
SystemNotificationEvent
pydantic-model
¶
Bases: BaseResponse
New mailbox messages, pushed by the server.
Command: sne
Client: SNECommand.exec (bundle line 125499), CastleMessageData.parse_SNE (bundle line 134961).
Fields:
-
error_code(int) -
messages(list[MessageInfo])
Validators:
-
_readable_rows→messages
GetSpyReportRequest
pydantic-model
¶
Bases: BaseRequest
Request a spy report from the mailbox.
Command: bsd Payload: {"MID": message_id}
Client: C2SGetSpyLog (bundle line 139994), sent by
CastleSpyData.getSpyLog (bundle line 139963) for every spy log opened
Fields:
ForwardSpyLogRequest
pydantic-model
¶
Bases: BaseRequest
Forward a spy report to other players.
Command: mfs Payload: {"MID": message_id, "PID": [player_id, ...]}
The server answers error 167 (ALREADY_HAS_SPY_REPORT) when a recipient has the report already, which the client takes as done.
Client: C2SForwardSpyLogVO (bundle lines 128594-128595), whose
constructor sets MID before PID; MFSCommand.executeCommand
(bundle line 125409)
Fields:
-
message_id(int) -
player_ids(list[int])
SpyReportArea
pydantic-model
¶
Bases: BasePayload
The spied area, a spy report's AI block.
Which of the optional keys the client reads depends on the area's type:
RT for an outpost, village, monument or resource isle, DL for a
dungeon or an NPC owner, SPC for a faction area, DAR and DDCID
for a daimyo castle or township, ACVC and AID for an alliance
battle ground tower.
Client: InteractiveMapobjectVO.parseAreaInfoBattleLog (bundle line 3638)
and its overrides: OutpostMapobjectVO (18787), DaimyoCastleMapObjectVO
(19652), FactionInteractiveMapobjectVO (21579), MonumentMapobjectVO
(21656), DaimyoTownshipMapObjectVO (21739), DungeonMapobjectVO
(22058), VillageMapobjectVO (22627), ABGAllianceTowerMapobjectVO
(32282), ResourceIsleMapobjectVO (34610), AAlienInvasionMapobjectVO
(41548); CastleSpyLogVO.parseSpyLog (bundle line 60579) for DP,
DL and RT
Fields:
-
area_type(MapItemType) -
x(int) -
y(int) -
kingdom(Kingdom) -
name(str) -
map_id(int | None) -
skin_id(ClientInt) -
owner_id(int | None) -
level(int | None) -
keep_level(ClientInt) -
wall_level(ClientInt) -
gate_level(ClientInt) -
tower_level(ClientInt) -
moat_level(ClientInt) -
area_subtype(int | None) -
special_camp_id(int | None) -
daimyo_rank(int | None) -
daimyo_camp_id(int | None) -
victory_count(int | None) -
alliance_id(int | None)
name
pydantic-field
¶
name: str = ''
Wire key N
The area's name; empty for an NPC area the game names
skin_id
pydantic-field
¶
skin_id: ClientInt = 0
Wire key EID
Unique id of the castle skin item, 0 for none
owner_id
pydantic-field
¶
owner_id: int | None = None
Wire key DP
The owner's player id, below 0 for an NPC; for a faction invasion camp, its dungeon type
level
pydantic-field
¶
level: int | None = None
Wire key DL
The NPC's level, when owner_id is below 0
area_subtype
pydantic-field
¶
area_subtype: int | None = None
Wire key RT
Outpost type, village type or monument type, or a resource isle's isle id
special_camp_id
pydantic-field
¶
special_camp_id: int | None = None
Wire key SPC
A faction area's special camp id
daimyo_rank
pydantic-field
¶
daimyo_rank: int | None = None
Wire key DAR
A daimyo castle's or township's rank
daimyo_camp_id
pydantic-field
¶
daimyo_camp_id: int | None = None
Wire key DDCID
A daimyo castle's or township's difficulty camp id
SpyReportResponse
pydantic-model
¶
Bases: BaseResponse
A spy report.
Command: bsd Payload:
{"MID": message_id, "CID": area_object_id, "SID": spy_owner_id, "PID": owner_id,
"SC": spies, "GC": guards, "SA": accuracy_or_damage, "SR": risk,
"S": [left, middle, right, keep, stronghold, support, reserve], "AS": seconds_since_spy,
"B": {castellan}, "LS": [legend_skill_id, ...], "R": [collectables], "RS": cooldown_seconds,
"OI": {owner record}, "SO": {owner record}, "AI": {area}, "DAR": daimyo_rank}
An AI block whose AT, X or Y is missing or not one the
library knows reads as area None, where the client builds an empty
map object or fails: deliberately more lenient, so the rest of the report
still parses.
A report with no army lists no defenders because the mission brought none back, not because the area is empty. The server answers error 130 (NO_SPY_DATA) or 66 (NO_SUCH_MESSAGE) when there is no report, which the client shows as "no spy data".
Client: BSDCommand.executeCommand (bundle lines 125223-125226),
CastleSpyLogVO.parseSpyLog (bundle line 60579),
CastleSpyArmyInfoVO.parseArmyInfo (bundle line 30699)
Fields:
-
error_code(int) -
message_id(int | None) -
area_object_id(ClientInt) -
spy_owner_id(ClientInt) -
owner_id(ClientInt) -
spy_count(ClientInt) -
guard_count(ClientInt) -
accuracy_or_damage(ClientInt) -
risk(ClientInt) -
army(SpyArmyBlock) -
seconds_since_spy(int) -
defending_castellan(Castellan | None) -
legend_skill_ids(tuple[EnumOrInt['LegendSkill'], ...]) -
resources(CollectableRows) -
dungeon_cooldown_seconds(int | None) -
owner(MapObject | None) -
spy_owner(MapObject | None) -
area(SpyReportArea | None)
Validators:
-
_daimyo_rank_into_the_area -
_skill_ids→legend_skill_ids -
_castellan_or_none→defending_castellan -
_owner_or_none→owner,spy_owner -
_area_or_none→area -
_no_age_without_an_army
area_object_id
pydantic-field
¶
area_object_id: ClientInt = 0
Wire key CID
The spied area's object id, -1 when it has none
spy_owner_id
pydantic-field
¶
spy_owner_id: ClientInt = 0
Wire key SID
Player id of whoever sent the spies
owner_id
pydantic-field
¶
owner_id: ClientInt = 0
Wire key PID
Player id of the spied area's owner; below 0 for an NPC
accuracy_or_damage
pydantic-field
¶
accuracy_or_damage: ClientInt = 0
Wire key SA
Accuracy percent, or damage percent for sabotage and plague monks
army
pydantic-field
¶
army: SpyArmyBlock = None
Wire key S
The spied defenders by the position they hold; None when the report has no army
seconds_since_spy
pydantic-field
¶
seconds_since_spy: int = -1
Wire key AS
Seconds between the spying and this reply; -1 without an army
defending_castellan
pydantic-field
¶
defending_castellan: Castellan | None = None
Wire key B
The castellan defending the spied area, without its equipment; None when there is none
legend_skill_ids
pydantic-field
¶
Wire key LS
The defender's legend skills
resources
pydantic-field
¶
resources: CollectableRows = ()
Wire key R
What an economy mission saw (CastleSpyLogVO.parseSpyLog reads it with CollectableParserS2CParamList.createList, bundle line 60579)
dungeon_cooldown_seconds
pydantic-field
¶
dungeon_cooldown_seconds: int | None = None
Wire key RS
Seconds until the spied dungeon can be attacked again; 0 or less when it can be now
ReadMessageRequest
pydantic-model
¶
Bases: BaseRequest
Read a message's body.
Command: rms Payload: {"MID": message_id}
Client: C2SReadMessagesVO (bundle line 19620), sent by
CastleMessageData.getBodyForTextMessage (bundle line 134908)
Fields:
ReadMessageResponse
pydantic-model
¶
Bases: BaseResponse
A message's body.
Command: rms
Client: RMSCommand.executeCommand (bundle line 125455) hands MTXT and
ABI on; CastleReadDialog.displayCurrentMessage (bundle line 138112)
decodes MTXT as chat text. Only the attack order dialogs read ABI, through
CastleBookmarkData.parseBookmarkObject (bundle lines 53040, 91271, 33427-33429)
Fields:
Validators:
-
_bookmark→bookmark
MarkMessageReadRequest
pydantic-model
¶
Bases: BaseRequest
Mark a message read.
Command: mmr Payload: {"MID": message_id}
The client has no handler for an mmr answer.
Client: C2SMarkMessageReadVO (bundle line 90970), sent when a message is opened (bundle line 90961)
Fields:
ArchiveMessageRequest
pydantic-model
¶
Bases: BaseRequest
Move a message to the archive.
Command: ams Payload: {"MID": message_id}
Client: C2SArchiveMessageVO (bundle line 135032), sent by
CastleMessageData.archiveMessage (bundle line 134988)
Fields:
ArchiveMessageResponse
pydantic-model
¶
Bases: BaseResponse
The archived message.
Command: ams
Client: AMSCommand.executeCommand (bundle line 125141), CastleMessageData.parseAMS (bundle line 134962)
Fields:
-
error_code(int) -
message_id(int | None)
DeleteMessageRequest
pydantic-model
¶
Bases: BaseRequest
Delete one message.
Command: dms Payload: {"MID": message_id}
Client: C2SDeleteMessageVO (bundle line 135068), sent by
CastleMessageData.deleteMessage (bundle line 134987)
Fields:
DeleteMessagesRequest
pydantic-model
¶
Bases: BaseRequest
Delete several messages.
Command: dms Payload: {"MIDS": [message_id, ...]}
Client: C2SDeleteMessagesVO (bundle line 106651), sent by the inbox's
delete-all (bundle line 39344)
Fields:
-
message_ids(list[int])
DeleteMessagesResponse
pydantic-model
¶
Bases: BaseResponse
The deleted messages.
Command: dms
Client: DMSCommand.exec (bundle line 125282) reads MID as one id or a list of ids
Fields:
-
error_code(int) -
message_ids(list[int])
Validators:
-
_one_or_many→message_ids
SendMessageRequest
pydantic-model
¶
Bases: BaseRequest
Send a message to a player.
Command: sms Payload: {"RN": receiver_name, "MH": subject, "TXT": text}; subject and text encoded as chat text
Client: C2SSendMessageVO (bundle line 135085), sent by
CastleMessageData.sendNewMessage (bundle line 134986)
Fields:
create
classmethod
¶
create(receiver_name: str, subject: str, text: str) -> SendMessageRequest
A message, subject and text encoded as C2SSendMessageVO encodes them.
SendMessageResponse
pydantic-model
¶
Bases: BaseResponse
The answer to sending a message.
Command: sms
Client: SMSCommand.executeCommand (bundle line 125470); on error 70
(USAGE_OF_BADWORDS) it reads the first bad word from BW
Fields:
messages.models.battle_logs¶
Battle report protocol models.
Commands:
- bls: A battle report's short log, the overview
- blm: Its middle log: the waves per flank, the courtyard and the commanders
- bld: Its detail log: every unit per flank and wave
- mfb: Forward a battle report
Classes:
| Name | Description |
|---|---|
GetBattleLogShortRequest |
Request a battle report's short log. |
GetBattleLogMiddleRequest |
Request a battle report's middle log. |
GetBattleLogDetailRequest |
Request a battle report's detail log. |
ForwardBattleLogRequest |
Forward a battle report to other players. |
ForwardBattleLogResponse |
The answer to forwarding a battle report; the client reads nothing from it. |
BattleLogUnit |
One unit or tool type in a battle log: |
SideUnits |
One side's units in a battle log: |
UnitsBySide |
The attacker's and the defender's units: |
FlankTotals |
One flank's totals in a middle log: |
MiddlePlayerWave |
One side of a middle log wave: |
MiddleWave |
One wave of a middle log: |
DetailFlank |
One flank of a detail log wave: |
DetailPlayerWave |
One side of a detail log wave: |
DetailWave |
One wave of a detail log: |
AbilityWaveValue |
Where a general's ability took effect: |
BattleLogAbility |
A general's ability that took effect in the battle: |
BattleParticipant |
One player in a battle: a |
BattleLogArea |
The area fought over, a short log's |
BattleLogMeta |
A short log's |
BattleLogShortResponse |
A battle report's short log: who fought, who won, the loot and the area. |
MiddleCourtyard |
The courtyard in a middle log: |
BattleLogMiddleResponse |
A battle report's middle log: the waves per flank, the courtyard, the support tools and the commanders. |
BattleLogDetailResponse |
A battle report's detail log: every unit and tool type per flank and wave. |
BattleReport |
A battle report as |
Attributes:
| Name | Type | Description |
|---|---|---|
ATTACKER_FRONT |
|
|
DEFENDER_FRONT |
|
ATTACKER_FRONT
module-attribute
¶
BattleParticipant.front of an attacking player. Client: BattleLogVO.hasWonState (bundle line 138387)
DEFENDER_FRONT
module-attribute
¶
BattleParticipant.front of a defending player. Client: BattleLogVO.isDefender (bundle line 138388)
GetBattleLogShortRequest
pydantic-model
¶
Bases: BaseRequest
Request a battle report's short log.
Command: bls Payload: {"MID": message_id, "IM": 0 or 1}
With IM 1 the reply carries the middle and detail logs too, as blm
and bld. The client itself sends IM 0.
Client: C2SBattleLogShortVO (bundle line 135059), sent by
CastleMessageData.getBattleLogShort (bundle line 134905)
Fields:
Validators:
-
_one_or_zero→include_details
GetBattleLogMiddleRequest
pydantic-model
¶
Bases: BaseRequest
Request a battle report's middle log.
Command: blm Payload: {"LID": log_id}
Client: C2SBattleLogMiddleVO (bundle line 135050), sent by
CastleMessageData.getBattleLogMiddle (bundle line 134906)
Fields:
GetBattleLogDetailRequest
pydantic-model
¶
Bases: BaseRequest
Request a battle report's detail log.
Command: bld Payload: {"LID": log_id}
Client: C2SBattleLogDetailVO (bundle line 135041), sent by
CastleMessageData.getBattleLogDetailed (bundle line 134907)
Fields:
ForwardBattleLogRequest
pydantic-model
¶
Bases: BaseRequest
Forward a battle report to other players.
Command: mfb Payload: {"MID": message_id, "PID": [player_id, ...]}
Client: C2SForwardBattleLogVO (bundle lines 128585-128586), whose
constructor sets MID before PID; sent by
CastleForwardMessageDialog.sendMessage (bundle line 60740)
Fields:
-
message_id(int) -
player_ids(list[int])
ForwardBattleLogResponse
pydantic-model
¶
Bases: BaseResponse
The answer to forwarding a battle report; the client reads nothing from it.
Command: mfb
Client: MFBCommand.executeCommand (bundle line 125396)
Fields:
BattleLogUnit
pydantic-model
¶
Bases: BasePayload
One unit or tool type in a battle log: [wod_id, amount, lost].
Client: LogUnitVO.initByParams (bundle line 61363)
Fields:
Validators:
-
_from_row
SideUnits
pydantic-model
¶
Bases: BasePayload
One side's units in a battle log: [player_id, [wod_id, amount, lost], ...].
The client keeps the wire order of units only until it sorts them by
the game's unit order; this model keeps the wire order.
Client: BattleLogVO.parseYardDetailed and parseSupportTools (bundle lines 138341, 138352)
skip the first entry and read the rest as LogUnitVO (bundle line 61363)
Fields:
Validators:
UnitsBySide
pydantic-model
¶
Bases: BasePayload
The attacker's and the defender's units: [attacker_row, defender_row], each a SideUnits row.
Client: BattleLogVO.parseYardDetailed and parseSupportTools (bundle lines 138341, 138352)
Fields:
Validators:
-
_from_row
FlankTotals
pydantic-model
¶
Bases: BasePayload
One flank's totals in a middle log: [soldiers, tools_used, soldiers_lost].
Client: FlankInfoVO.initByParams (bundle line 61395)
Fields:
Validators:
-
_from_row
MiddlePlayerWave
pydantic-model
¶
Bases: BasePayload
One side of a middle log wave: [player_id, left, middle, right], each flank a FlankTotals row.
A pre-combat wave's sides have no player id: [left, middle, right].
A flank the row lacks is None.
Client: MediumPlayerBattleWaveVO.initByParams (bundle line 138761)
Fields:
-
player_id(int | float | None) -
left(FlankTotals | None) -
middle(FlankTotals | None) -
right(FlankTotals | None)
Validators:
-
_from_row
player_id
pydantic-field
¶
The side's player id; None in a pre-combat wave
MiddleWave
pydantic-model
¶
Bases: BasePayload
One wave of a middle log: [attacker, defender], each a MiddlePlayerWave row.
Client: MediumBattleWaveVO.initByParams (bundle line 138735)
Fields:
Validators:
-
_from_row
DetailFlank
pydantic-model
¶
Bases: BasePayload
One flank of a detail log wave: [soldiers, tools, effects].
effects is [wall_bonus, gate_bonus, moat_bonus] and comes only for the
defending side; without it the three bonuses are None.
Client: FlankVO.initByParams and parseEffects (bundle lines 138694, 138705)
Fields:
-
soldiers(list[BattleLogUnit]) -
tools(list[BattleLogUnit]) -
wall_bonus(int | None) -
gate_bonus(int | None) -
moat_bonus(int | None)
Validators:
DetailPlayerWave
pydantic-model
¶
Bases: BasePayload
One side of a detail log wave: [player_id, left, middle, right, courtyard], each a DetailFlank row.
A flank the row lacks is empty, as the client reads it.
Client: DetailedPlayerBattleWaveVO.initByParams (bundle line 138635)
Fields:
-
player_id(int | float | None) -
left(DetailFlank) -
middle(DetailFlank) -
right(DetailFlank) -
courtyard(DetailFlank)
Validators:
player_id
pydantic-field
¶
The side's player id, as the server sends it
DetailWave
pydantic-model
¶
Bases: BasePayload
One wave of a detail log: [attacker, defender], each a DetailPlayerWave row.
Client: DetailedBattleWaveVO.initByParams (bundle line 138614)
Fields:
Validators:
-
_from_row
AbilityWaveValue
pydantic-model
¶
Bases: BasePayload
Where a general's ability took effect: [wave_id, value, flank_name].
Client: BattleLogAbilityVO.parseFromParamOj (bundle line 138588); getValueForWave and
isTriggerdInWave (bundle lines 138593-138595) match the wave and the flank name;
CastleBattleLogPopUpDialog.getFlankNameBattleLog (bundle lines 135871-135877) names the flanks
Fields:
-
wave_id(ClientInt) -
value(ClientNumber) -
flank_name(EnumOrStr[BattleLogFlank] | None)
Validators:
-
_from_row -
_name→flank_name
flank_name
pydantic-field
¶
flank_name: EnumOrStr[BattleLogFlank] | None = None
The flank; None when the entry names none
BattleLogAbility
pydantic-model
¶
Bases: BasePayload
A general's ability that took effect in the battle: [ability_id, [[wave_id, value, flank_name], ...]].
Client: BattleLogAbilityVO.parseFromParamOj (bundle line 138584)
Fields:
-
ability_id(EnumOrInt['GeneralAbility'] | None) -
wave_values(list[AbilityWaveValue])
Validators:
-
_from_row -
_readable→wave_values
BattleParticipant
pydantic-model
¶
Bases: BasePayload
One player in a battle: a PBI row.
The client looks each player up among the owner records (PI) and stops
reading the rows at one it has no record for; this model keeps every row.
Join player_id to the reply's owners for the player's record.
Client: BattleLogVO.loadFromParamArrayPBI (bundle line 138313), BattleParticipantVO
(bundle line 138788); FactionConst.BLUE_FACTION 0 and RED_FACTION 1 (dll line 19333)
Fields:
-
player_id(ClientInt) -
front(ClientInt) -
start_army_size(ClientInt) -
lost_units(ClientInt) -
loot(CollectableRows) -
fame_points(ClientInt) -
xp(ClientInt) -
kingstower_bonus(ClientInt) -
faction_points(ClientInt) -
highest_fame_title_bonus(ClientInt) -
wounded_units(ClientInt) -
morale_boost(ClientInt) -
capital_tokens(ClientInt) -
reputation_blue(ClientInt) -
reputation_red(ClientInt)
Validators:
-
_from_row
lost_units
pydantic-field
¶
lost_units: ClientInt = 0
Soldiers the player lost; the server sends it as 0 or a negative number
loot
pydantic-field
¶
loot: CollectableRows = ()
What the player looted (BattleLogVO.loadFromParamArrayPBI reads it with CollectableParserS2CParamList.createList, bundle line 138318)
highest_fame_title_bonus
pydantic-field
¶
highest_fame_title_bonus: ClientInt = 0
Bonus from the highest glory title
BattleLogArea
pydantic-model
¶
Bases: SpyReportArea
The area fought over, a short log's AI block.
The reply's SSID, DAR and, for an alliance battle ground tower, its
AID, N, ACVC and E are read into this block, as the client
copies them.
Client: BattleLogVO.fillFromParamObject (bundle lines 138294-138303) and
InteractiveMapobjectVO.parseAreaInfoBattleLog (bundle line 3638) with its
overrides (see SpyReportArea)
Fields:
-
area_type(MapItemType) -
x(int) -
y(int) -
kingdom(Kingdom) -
name(str) -
map_id(int | None) -
skin_id(ClientInt) -
owner_id(int | None) -
level(int | None) -
keep_level(ClientInt) -
wall_level(ClientInt) -
gate_level(ClientInt) -
tower_level(ClientInt) -
moat_level(ClientInt) -
area_subtype(int | None) -
special_camp_id(int | None) -
daimyo_rank(int | None) -
daimyo_camp_id(int | None) -
victory_count(int | None) -
alliance_id(int | None) -
treasure_map_node_id(ClientInt) -
alliance_crest(dict[str, Any] | None)
Validators:
-
_crest→alliance_crest
BattleLogMeta
dataclass
¶
BattleLogMeta(area_type: int, attack_type: BattleLogAttackType | None, result: LogResult | None, treasure_map_id: int = 0, treasure_map_area_type: str | int = 0)
A short log's MS: area_type+attack_type+result[+treasure_map_id[+treasure_map_area_type]].
A battle log's mailbox header starts with the same numbers. A subtype or result the client defines no constant for is None.
Client: BattleLogVO.addMetadataFromMessage (bundle line 138334) and the
metaData_* getters (bundle lines 138461-138469)
Methods:
| Name | Description |
|---|---|
parse |
Read |
Attributes:
| Name | Type | Description |
|---|---|---|
area_type |
int
|
|
attack_type |
BattleLogAttackType | None
|
|
result |
LogResult | None
|
|
treasure_map_id |
int
|
|
treasure_map_area_type |
str | int
|
|
parse
classmethod
¶
parse(text: str) -> BattleLogMeta
Read MS; a missing number reads as 0, as the client's int() reads it.
SupporterWounded
pydantic-model
¶
Bases: BasePayload
Soldiers a supporting player had wounded: a WSU row, [player_id, wounded_units].
Client: BattleLogVO.updateSupporterWoundedUnitCount (bundle lines 138329-138332)
Fields:
Validators:
-
_from_row
BattleLogShortResponse
pydantic-model
¶
Bases: BaseResponse
A battle report's short log: who fought, who won, the loot and the area.
Command: bls Payload:
{"MID": message_id, "LID": log_id, "MT": 6, "DW": defender_won, "MS": "area+attack+result",
"AI": {area}, "PBI": [[player_id, front, army, lost, loot, fame, xp, ...]], "PI": [{owner}],
"AL": {commander}, "DB": {castellan}, "PS": seconds_since_battle, ...}
With IM 1 it carries the middle and detail logs as blm and bld.
The server answers error 66 (NO_SUCH_MESSAGE) or 225 (MESSAGEDATA_TOO_OLD)
for a log it no longer has, which the client shows as a log that does not exist.
Client: BLSCommand.executeCommand (bundle lines 125190-125196),
CastleMessageData.parseBattleLogShort (bundle line 134992),
BattleLogVO.fillFromParamObject (bundle line 138294), which reads EQF and GF (bundle
line 138304) and ASCT, AAT, AAC and AAN (bundle line 138312)
Fields:
-
error_code(int) -
message_id(int | None) -
log_id(ClientInt) -
message_type(EnumOrInt[MessageType]) -
defender_won(bool) -
meta(str) -
area(BattleLogArea | None) -
participants(list[BattleParticipant]) -
owners(list[MapObject]) -
honor(ClientInt) -
survival_rate(ClientInt) -
found_equipment(Equipment | None) -
found_gem(EnumOrInt['Gem'] | None) -
found_minute_skip(Collectable | None) -
rage_points(int) -
jump_disabled(bool) -
seconds_since_battle(int | float | None) -
attacker_home_castle_id(int | None) -
attacker_had_hospital(bool) -
attacker_hospital_full(bool) -
defender_home_castle_id(int | None) -
defender_had_hospital(bool) -
defender_hospital_full(bool) -
attacker_only_auxiliaries(bool) -
defender_only_auxiliaries(bool) -
supporters_wounded(tuple[SupporterWounded, ...]) -
attacking_commander(Commander | None) -
defending_castellan(Castellan | None) -
auto_skip_costs(CurrencyIdRows) -
auto_skip_rubies(ClientInt) -
auto_skip_type(EnumOrInt[AutoSkipCooldownType] | None) -
advisor_type(EnumOrInt[AttackAdvisorType]) -
advisor_movement_count(ClientInt) -
advisor_movement_number(ClientInt) -
attacker_alliance_subscribers(ClientInt) -
attacker_had_subscription(bool) -
defender_alliance_subscribers(ClientInt) -
defender_had_subscription(bool) -
old_charge_points(int) -
old_charge_rank(int) -
new_charge_points(int) -
new_charge_rank(int) -
defender_old_charge_points(int) -
defender_old_charge_rank(int) -
defender_new_charge_points(int) -
defender_new_charge_rank(int) -
includes_details(bool) -
middle(BattleLogMiddleResponse | None) -
detail(BattleLogDetailResponse | None)
Validators:
-
_copies_into_the_area -
_truthy→defender_won,jump_disabled -
_one→attacker_had_subscription,defender_had_subscription,includes_details -
_parsed_one→attacker_had_hospital,attacker_hospital_full,defender_had_hospital,defender_hospital_full,attacker_only_auxiliaries,defender_only_auxiliaries -
_castle_id→attacker_home_castle_id,defender_home_castle_id -
_charge_points→old_charge_points,old_charge_rank,new_charge_points,new_charge_rank,defender_old_charge_points,defender_old_charge_rank,defender_new_charge_points,defender_new_charge_rank -
_rage→rage_points -
_seconds→seconds_since_battle -
_meta→meta -
_list→auto_skip_costs -
_supporters→supporters_wounded -
_or_zero→advisor_type,advisor_movement_count,advisor_movement_number -
_found_equipment→found_equipment -
_found_gem→found_gem -
_participants→participants -
_owners→owners -
_area→area -
_commander→attacking_commander,defending_castellan -
_nested_log→middle,detail -
_minute_skip→found_minute_skip
log_id
pydantic-field
¶
log_id: ClientInt = 0
Wire key LID
The battle log id, for the middle and detail logs
message_type
pydantic-field
¶
message_type: EnumOrInt[MessageType] = 0
Wire key MT
The message type: BATTLE_LOG for a battle log
meta
pydantic-field
¶
meta: str = ''
Wire key MS
area_type+attack_type+result[+treasure_map_id[+map_area_type]]
found_equipment
pydantic-field
¶
found_equipment: Equipment | None = None
Wire key EQF
Equipment found in the battle; None for none
found_gem
pydantic-field
¶
found_gem: EnumOrInt['Gem'] | None = None
Wire key GF
Gem found in the battle
found_minute_skip
pydantic-field
¶
found_minute_skip: Collectable | None = None
Wire key MSF
The time skip found in the battle, one of its currency; None for none
rage_points
pydantic-field
¶
rage_points: int = -1
Wire key RP
Rage points; -1 when the reply has none
jump_disabled
pydantic-field
¶
jump_disabled: bool = False
Wire key DJ
The client offers no jump to the area
seconds_since_battle
pydantic-field
¶
Wire key PS
Seconds since the battle; None when the reply has none
attacker_home_castle_id
pydantic-field
¶
attacker_home_castle_id: int | None = None
Wire key AHC
The attacker's home castle
attacker_had_hospital
pydantic-field
¶
attacker_had_hospital: bool = False
Wire key AHH
The attacker had a hospital
attacker_hospital_full
pydantic-field
¶
attacker_hospital_full: bool = False
Wire key AHF
The attacker's hospital was full
defender_home_castle_id
pydantic-field
¶
defender_home_castle_id: int | None = None
Wire key DHC
The defender's home castle
defender_had_hospital
pydantic-field
¶
defender_had_hospital: bool = False
Wire key DHH
The defender had a hospital
defender_hospital_full
pydantic-field
¶
defender_hospital_full: bool = False
Wire key DHF
The defender's hospital was full
attacker_only_auxiliaries
pydantic-field
¶
attacker_only_auxiliaries: bool = False
Wire key AUA
The attacker fought with auxiliaries only
defender_only_auxiliaries
pydantic-field
¶
defender_only_auxiliaries: bool = False
Wire key DUA
The defender fought with auxiliaries only
supporters_wounded
pydantic-field
¶
supporters_wounded: tuple[SupporterWounded, ...] = ()
Wire key WSU
Soldiers each supporting player had wounded
attacking_commander
pydantic-field
¶
attacking_commander: Commander | None = None
Wire key AL
The attacking commander
defending_castellan
pydantic-field
¶
defending_castellan: Castellan | None = None
Wire key DB
The defending castellan
auto_skip_costs
pydantic-field
¶
auto_skip_costs: CurrencyIdRows = ()
Wire key ASMS
The currencies the auto-skip cost (an amount above 0) or refunded (0 or below), as sent
auto_skip_rubies
pydantic-field
¶
auto_skip_rubies: ClientInt = 0
Wire key ASC
Rubies the auto-skip cost; 0 or less for none
auto_skip_type
pydantic-field
¶
auto_skip_type: EnumOrInt[AutoSkipCooldownType] | None = None
Wire key ASCT
How the target's cooldown was skipped; None when the reply has none
advisor_type
pydantic-field
¶
advisor_type: EnumOrInt[AttackAdvisorType] = AttackAdvisorType.NONE
Wire key AAT
The attack advisor that sent the attack
advisor_movement_count
pydantic-field
¶
advisor_movement_count: ClientInt = 0
Wire key AAC
Movements the attack advisor sent
advisor_movement_number
pydantic-field
¶
advisor_movement_number: ClientInt = 0
Wire key AAN
This movement's number among them
attacker_alliance_subscribers
pydantic-field
¶
attacker_alliance_subscribers: ClientInt = 0
Wire key AAS
Subscribed members of the attacker's alliance
attacker_had_subscription
pydantic-field
¶
attacker_had_subscription: bool = False
Wire key AHP
The attacker had a subscription
defender_alliance_subscribers
pydantic-field
¶
defender_alliance_subscribers: ClientInt = 0
Wire key DAS
Subscribed members of the defender's alliance
defender_had_subscription
pydantic-field
¶
defender_had_subscription: bool = False
Wire key DHP
The defender had a subscription
old_charge_points
pydantic-field
¶
old_charge_points: int = 0
Wire key CPO
The attacker's charge points before
old_charge_rank
pydantic-field
¶
old_charge_rank: int = 0
Wire key CRO
The attacker's charge rank before
new_charge_points
pydantic-field
¶
new_charge_points: int = 0
Wire key CPN
The attacker's charge points after
new_charge_rank
pydantic-field
¶
new_charge_rank: int = 0
Wire key CRN
The attacker's charge rank after
defender_old_charge_points
pydantic-field
¶
defender_old_charge_points: int = 0
Wire key DCPO
The defender's charge points before
defender_old_charge_rank
pydantic-field
¶
defender_old_charge_rank: int = 0
Wire key DCRO
The defender's charge rank before
defender_new_charge_points
pydantic-field
¶
defender_new_charge_points: int = 0
Wire key DCPN
The defender's charge points after
defender_new_charge_rank
pydantic-field
¶
defender_new_charge_rank: int = 0
Wire key DCRN
The defender's charge rank after
includes_details
pydantic-field
¶
includes_details: bool = False
Wire key IM
The reply carries the middle and detail logs
middle
pydantic-field
¶
middle: BattleLogMiddleResponse | None = None
Wire key blm
The middle log, when includes_details
detail
pydantic-field
¶
detail: BattleLogDetailResponse | None = None
Wire key bld
The detail log, when includes_details
winners
property
¶
winners: list[BattleParticipant]
The players on the winning side.
Client: BattleLogVO.loadFromParamArrayPBI and hasWonState (bundle lines 138322, 138387)
auto_skip_paid
property
¶
auto_skip_paid: tuple[Collectable, ...]
What the auto-skip cost: the currencies with an amount above 0, then auto_skip_rubies when above 0.
Client: BattleLogVO.fillFromParamObject (bundle lines 138308-138311), _autoSkipCosts[0]
auto_skip_refunded
property
¶
auto_skip_refunded: tuple[Collectable, ...]
What the auto-skip refunded: the currencies with an amount of 0 or below, as a positive amount.
Client: BattleLogVO.fillFromParamObject (bundle lines 138308-138309), _autoSkipCosts[1]
owner_of
¶
owner_of(participant: BattleParticipant) -> MapObject | None
The owner record for a participant, or None when the reply has none.
MiddleCourtyard
pydantic-model
¶
Bases: BasePayload
The courtyard in a middle log: [[player_id, troops, lost], [player_id, troops, lost]].
Client: BattleLogVO.parseYard (bundle line 138375)
Fields:
-
attacker_troops(int) -
attacker_lost(int) -
defender_troops(int) -
defender_lost(int) -
has_defender_info(bool)
Validators:
-
_from_rows
attacker_troops
pydantic-field
¶
attacker_troops: int = 0
Attacking soldiers that reached the courtyard
attacker_lost
pydantic-field
¶
attacker_lost: int = 0
Of those, lost; the server sends it as 0 or a negative number
BattleLogMiddleResponse
pydantic-model
¶
Bases: BaseResponse
A battle report's middle log: the waves per flank, the courtyard, the support tools and the commanders.
Command: blm Payload:
{"LID": log_id, "Y": [[player_id, troops, lost], ...], "W": [[attacker, defender], ...],
"PW": [attacker, defender], "SD": [[player_id, [wod_id, amount, lost], ...], ...],
"RW": [soldiers, tools_used, soldiers_lost], "AL": {commander}, "DB": {castellan},
"PS": seconds_since_battle, "AA": [abilities], "DA": [abilities], "DUST": used, ...}
W sides start with the player id, PW sides do not. The reply's
S and EW are not read by the client.
Client: BLMCommand.executeCommand (bundle line 125173),
CastleMessageData.parseBattleLogMiddle (bundle line 135002),
BattleLogVO.parseMiddle (bundle line 138369)
Fields:
-
error_code(int) -
log_id(ClientInt) -
courtyard(MiddleCourtyard) -
waves(list[MiddleWave]) -
pre_combat_wave(MiddleWave | None) -
support_tools(UnitsBySide) -
reinforcements(FlankTotals) -
attacking_commander(Commander | None) -
defending_castellan(Castellan | None) -
jump_disabled(bool) -
seconds_since_battle(int | float | None) -
attacker_triggered_gems(tuple[EnumOrInt['Gem'], ...]) -
defender_triggered_gems(tuple[EnumOrInt['Gem'], ...]) -
attacker_legend_skill_ids(tuple[EnumOrInt['LegendSkill'], ...]) -
defender_legend_skill_ids(tuple[EnumOrInt['LegendSkill'], ...]) -
defender_used_support_tools(bool) -
attacker_abilities(list[BattleLogAbility]) -
defender_abilities(list[BattleLogAbility])
Validators:
-
_waves→waves -
_pre_combat→pre_combat_wave -
_reinforcements→reinforcements -
_block→courtyard,support_tools -
_commander→attacking_commander,defending_castellan -
_truthy→jump_disabled,defender_used_support_tools -
_seconds→seconds_since_battle -
_ids→attacker_triggered_gems,defender_triggered_gems,attacker_legend_skill_ids,defender_legend_skill_ids -
_abilities→attacker_abilities,defender_abilities
pre_combat_wave
pydantic-field
¶
pre_combat_wave: MiddleWave | None = None
Wire key PW
What each side brought before the waves; None when not sent
reinforcements
pydantic-field
¶
reinforcements: FlankTotals
Wire key RW
The reinforcement wave's totals
attacking_commander
pydantic-field
¶
attacking_commander: Commander | None = None
Wire key AL
The attacking commander
defending_castellan
pydantic-field
¶
defending_castellan: Castellan | None = None
Wire key DB
The defending castellan
jump_disabled
pydantic-field
¶
jump_disabled: bool = False
Wire key DJ
The client offers no jump to the area
seconds_since_battle
pydantic-field
¶
Wire key PS
Seconds since the battle; None when the reply has none
attacker_triggered_gems
pydantic-field
¶
Wire key AGT
The attacker's gems that triggered
defender_triggered_gems
pydantic-field
¶
Wire key DGT
The defender's gems that triggered
attacker_legend_skill_ids
pydantic-field
¶
Wire key ALS
The attacker's legend skills
defender_legend_skill_ids
pydantic-field
¶
Wire key DLS
The defender's legend skills
defender_used_support_tools
pydantic-field
¶
defender_used_support_tools: bool = False
Wire key DUST
The defender used support tools
attacker_abilities
pydantic-field
¶
attacker_abilities: list[BattleLogAbility]
Wire key AA
The attacking general's abilities that took effect
defender_abilities
pydantic-field
¶
defender_abilities: list[BattleLogAbility]
Wire key DA
The defending general's abilities that took effect
BattleLogDetailResponse
pydantic-model
¶
Bases: BaseResponse
A battle report's detail log: every unit and tool type per flank and wave.
Command: bld Payload:
{"LID": log_id, "Y": [[player_id, [wod_id, amount, lost], ...], ...],
"W": [[attacker, defender], ...], "PW": [attacker, defender], "EW": [attacker, defender],
"RW": [[wod_id, amount, lost], ...]}
Each side of a wave is [player_id, left, middle, right, courtyard] and
each flank [soldiers, tools, effects]. The client sorts the unit lists
by the game's unit order; these models keep the wire order.
Client: BLDCommand.executeCommand (bundle line 125156),
CastleMessageData.parseBattleLogDetail (bundle line 135005),
BattleLogVO.parseDetails (bundle line 138335)
Fields:
-
error_code(int) -
log_id(ClientInt) -
courtyard(UnitsBySide) -
waves(list[DetailWave]) -
pre_combat_wave(DetailWave | None) -
post_combat_wave(DetailWave | None) -
reinforcements(list[BattleLogUnit])
Validators:
-
_waves→waves -
_extra_wave→pre_combat_wave,post_combat_wave -
_block→courtyard -
_reinforcements→reinforcements
pre_combat_wave
pydantic-field
¶
pre_combat_wave: DetailWave | None = None
Wire key PW
What each side brought before the waves; None when not sent
post_combat_wave
pydantic-field
¶
post_combat_wave: DetailWave | None = None
Wire key EW
What fought after the waves; None when not sent
BattleReport
dataclass
¶
BattleReport(short: BattleLogShortResponse, middle: BattleLogMiddleResponse | None = None, detail: BattleLogDetailResponse | None = None)
A battle report as MessagesService.get_battle_report reads it: the short log, and the others asked for.
Attributes:
| Name | Type | Description |
|---|---|---|
short |
BattleLogShortResponse
|
|
middle |
BattleLogMiddleResponse | None
|
|
detail |
BattleLogDetailResponse | None
|
|