Protocol layer¶
The protocol/ layer is the (de)serialization boundary between the network's
raw strings and the client's typed state. It does no I/O: the
network hands it lines, it returns objects, and vice versa.
| Module | File | Role |
|---|---|---|
parser |
ai/src/protocol/parser.py |
Raw server line → typed message |
commands |
ai/src/protocol/commands.py |
Command builders (pure strings) |
messages |
ai/src/protocol/messages.py |
Message dataclasses/enums |
update |
ai/src/protocol/update.py |
State-update callbacks |
parser — server lines to messages¶
parse_server_line(line, client) (ai/src/protocol/parser.py:26) routes each
line through a match:
match line:
case "ok": return OkKo.OK
case "ko": return OkKo.KO
case "dead": return DeadMessage()
case _ if line.startswith("message "): return parse_broadcast(line)
case _ if line.startswith("eject: "): return parse_eject(line)
case _ if line.startswith("Elevation underway"): return ElevationResult(success=True, level=None)
case _ if line.startswith("Current level: "): ... # -> ElevationResult(success=True, level=k)
case _ if line.startswith("["): ... # Look or Inventory
case _: raise ValueError(...)
Key points:
ok/kobecome theOkKoenum;deadaDeadMessage.Elevation underwayandCurrent level: kboth become anElevationResult(success=True, ...), the latter carrying the new level.[...]lines: ambiguous betweenLookandInventory. Disambiguation relies onclient.command_queue.peek_last_command()(ai/src/protocol/parser.py:54-60) — if the last command wasLookit callsparse_look, if it wasInventoryit callsparse_inventory.
parse_look & parse_inventory¶
parse_look (ai/src/protocol/parser.py:66) strips the brackets, splits on
commas, then splits each tile on spaces → LookResult(tiles=...) (an empty tile
yields an empty list).
parse_inventory (ai/src/protocol/parser.py:82) splits the same way but
expects resource quantity pairs → InventoryResult(items={...}).
parse_eject (ai/src/protocol/parser.py:124) extracts the integer K after
eject: → EjectMessage(direction=K).
parse_broadcast is a stub
parse_broadcast (ai/src/protocol/parser.py:102) raises
NotImplementedError: inbound broadcasts are not handled.
Team coordination suffers as a result. See
Known limitations.
commands — command builders¶
commands.py (ai/src/protocol/commands.py) provides pure functions that
return the exact command string, without a trailing \n:
def forward() -> str: return "Forward"
def look() -> str: return "Look"
def inventory() -> str: return "Inventory"
def broadcast(text) -> str: return f"Broadcast {text}"
def take(obj) -> str: return f"Take {obj}"
def set(obj) -> str: return f"Set {obj}"
def incantation() -> str: return "Incantation"
# … right, left, connect_nbr, fork, eject
The \n is added on send
These builders do not produce the trailing newline: it is
Client.send_command that pushes command + "\n" into the OutputBuffer
(see Network). Keeping this convention avoids double \n.
messages — typed messages¶
messages.py (ai/src/protocol/messages.py) defines the data structures — no
logic, only containers (@dataclass(frozen=True) and one enum):
| Type | Contents |
|---|---|
OkKo |
Enum OK / KO |
LookResult |
tiles; optional pos and direction (re-injected by the FSM) |
InventoryResult |
items: dict[str, int] |
BroadcastMessage |
direction: int, text: str |
EjectMessage |
direction: int |
DeadMessage |
(empty) |
ElevationResult |
success: bool, level: int \| None |
ConnectNbrResult |
slots: int |
Message is the union typing any parsed server line.
ConnectNbrResult is never produced
ConnectNbrResult is defined but no parser builds it, because reproduction
(Fork / Connect_nbr) is not implemented. See
Strategy (FSM) and Known limitations.
update — state-update callbacks¶
update.py (ai/src/protocol/update.py) provides the callbacks wired by
DroneState at construction (see Internal state):
update_inventory(drone, client, line)(ai/src/protocol/update.py:15) parses the line, checks it really is anInventoryResult, then updatesdrone.inventoryanddrone.food. Crucially, it setsconfig.INIT = True— this flag is what later allows the main loop to exit ondrone.is_dead()(the drone is not declared dead until the first inventory has arrived).update_vision(drone, client, line)(ai/src/protocol/update.py:31) parses the line and stores theLookResultindrone.last_vision(otherwiseNone).
Going further¶
- Network & command queue — where the raw lines come from and how
peek_last_commanddisambiguates[...]. - Internal state — how inventory and vision feed the state.
- Known limitations —
parse_broadcast,ConnectNbrResult.