Merge branch 'main' into claude/silly-lederberg-231f6c
Continuous Integration / lint-and-security (pull_request) Successful in 20s
Continuous Integration / tests-and-coverage (pull_request) Successful in 24s

Conflicted only on the README feature list, where main's list_cargo()
bullet and this branch's bridge bullet were added at the same spot. Kept
both: list_cargo() with the other cargo queries, and the bridge one last,
since it is about what all of them run against.

list_cargo() landing also makes tests/test_gamescript.py's command check
meaningful in the other direction -- the bridge has answered list_cargo
all along with nothing sending it, which is exactly the asymmetry that
check tolerates on purpose.

Co-Authored-By: Claude <[email protected]>
This commit is contained in:
2026-08-31 19:12:41 +02:00
co-authored by Claude
7 changed files with 117 additions and 4 deletions
+24
View File
@@ -769,6 +769,30 @@ class OpenTTDAdminClient:
payload["via_station"] = via_station
return await self._gs_query(payload, timeout, f"get_station_cargo({station_id}, {cargo_id})")
async def list_cargo(self, timeout=5.0):
"""Fetch the running game's cargo table via the AdminBridge GameScript, id to label.
Every other reply names a cargo by its bare numeric id — get_station()'s and
get_station_cargo()'s cargo_id, the cargo_waiting events, the per-cargo load on vehicle
events. Those ids index the cargo table the loaded NewGRFs build, so they mean different
things in different games and are not worth hardcoding; resolve them against this list
instead. Auto-subscribes to Gamescript updates on first use; if you manage update
frequencies yourself, ensure update_frequency(Gamescript, Automatic) is active before
calling.
Returns a dict with a "cargo" list holding every cargo type in the game, in no particular
order (index it by "cargo_id"; a cargo's position in the list is not its id). Each entry:
- "cargo_id": the id used by all the replies above
- "label": the cargo label ("PASS", "COAL", ...), or "" if the GameScript could not
read it
- "freight": 1 for freight cargo, 0 for the rest (passengers, mail, ...)
Raises asyncio.TimeoutError if no reply arrives (e.g. game paused, GS not loaded) and
ConnectionError if the admin connection drops while waiting. The GameScript reports no
error for this command, so unlike get_station() it never raises ValueError.
"""
return await self._gs_query({"command": "list_cargo"}, timeout, "list_cargo")
async def get_dispatch(self, vehicle_id, timeout=5.0):
"""Fetch an authoritative snapshot of a vehicle's scheduled dispatch state via the AdminBridge GS.