Protocol models¶
The services cover the common commands. For everything else, and for full control, send the request models yourself. Every command has a request model (what you send) and a response model (what comes back), exported from the command's area package.
from empire_core.alliance import AllianceChatMessageRequest
from empire_core.castle import GetCastlesRequest, GetCastlesResponse
request = AllianceChatMessageRequest.create("Hello 100%!")
client.frame(request)
# once the login has joined room 1:
# '%xt%EmpireEx_21%acm%1%{"M":"Hello 100%!"}%'
client.send(request) # fire and forget
response = client.send(GetCastlesRequest(), wait=True) # or wait for the reply
castles = client.request(GetCastlesRequest(), GetCastlesResponse)
client.frame(request) is the frame as this session sends it, in its zone and
the room it joined. request.to_packet(zone=..., room_id=...) builds one for
any zone and room.
Three ways to send¶
| Call | Returns | On a server refusal |
|---|---|---|
client.send(request) |
None, without waiting |
nothing: no reply is awaited |
client.send(request, wait=True) |
the parsed response | raises CommandError |
client.request(request, ResponseType) |
the response, checked to be ResponseType |
raises CommandError |
Waiting calls also raise EmpireTimeoutError, ConnectionClosedError,
NetworkError or PacketError; see Error handling. How a reply
is matched to its request is drawn on
Login and requests.
Models¶
Models are pydantic v2 models with snake_case fields; each field's wire key is its alias, and both are accepted when you build one:
from empire_core import AttackWave, WaveFlank
from empire_core.gamedata import Unit
AttackWave(left=WaveFlank(units={Unit.SWORDMAN: 100}))
AttackWave.model_validate({"L": {"U": [[601, 100]]}}) # the same wave, from its wire form
Prefer field names: mypy (with pydantic's plugin) and pyright, and so Pylance, check those calls for missing fields, wrong types and typos, and reject wire keys.
model_dump(by_alias=True) gives the wire form back. The
API reference lists every model's fields with their
wire keys and descriptions.
Subscribing to raw packets¶
Services register typed handlers for the pushes they care about. To see every packet of one command yourself, subscribe on the connection:
def on_packet(packet):
print(packet.command_id, packet.payload)
client.connection.subscribe("acm", on_packet)
client.connection.unsubscribe("acm", on_packet)
Subscribers run on the receive thread and must not wait for replies there.
Adding a command
Contributing explains how to add a request and response model, and the tests that check each one against the game client's own command list.
API: Protocol