ba26b59c40578d887e8d831edde0df36b5fea49d
The station queries and the cargo events name a cargo only by a 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 for the running game, so the same id is coal in one save and grain in another and callers had no way to resolve them. The AdminBridge GameScript has answered a list_cargo command all along; no method on OpenTTDAdminClient sent it. This adds the missing half, so no GameScript change is needed for it to work. It goes through _gs_query() like get_station(), inheriting the request_id correlation and the Gamescript auto-subscribe, with one difference worth knowing: the GS handler defines no error reply for this command, so unlike the other queries it can time out but can never raise ValueError. The reply lists cargo in GSCargoList order rather than by id -- against the dev server the ids come back 10 down to 0 -- so the docstring and PROTOCOL.md both warn to index the list by cargo_id and not by position. Also teaches the main_admin.py demo to resolve the labels before printing a station's cargo, which is what the bare ids in its output were asking for all along. Co-Authored-By: Claude <[email protected]>
Merge pull request 'Update debian:trixie-slim Docker digest to d7e1218' (#26) from renovate/debian-trixie-slim into main
OpenTTD Python Client
A high-performance, Object-Oriented Python client for OpenTTD servers, specifically optimized for JGR Patch Pack (JGRPP). This client handles the modern secure handshake, including X25519 PAKE authentication and AEAD stream encryption.
🚀 Features
- Secure Authentication: Full implementation of X25519 PAKE (Password-Authenticated Key Exchange).
- Stream Encryption: Automatic XChaCha20-Poly1305 authenticated encryption for all game traffic.
- Modular Design: Separates low-level binary protocol handling from high-level game logic.
- State Management: Handles the full join sequence including Map download and synchronization.
- Comprehensive Testing: Robustly tested with unit, logic, and E2E tests (including 100% coverage for unit/logic tests).
- Vehicle Listing: Query vehicle data via the Admin GameScript channel with
list_vehicles(). - Vehicle Timetables: Read and modify a vehicle's timetable (
change_timetable(),autofill_timetable(),set_timetable_start(),set_vehicle_on_time(),get_vehicle_timetable()) via real game-protocol commands. - Order Editing: Add and remove a vehicle's orders (
add_order()inserts a "go to station" stop,remove_order()deletes one) via real game-protocol commands. - Scheduled Dispatch (JGRPP): Edit a vehicle's dispatch schedules and departure slots over the game port (
add_dispatch_schedule(),add_dispatch_slot(),set_dispatch_duration(),set_scheduled_dispatch(), and more), and read them back authoritatively withOpenTTDAdminClient.get_dispatch()(via a patched GameScript API + the AdminBridge GS). - Authoritative Timetable Reads:
OpenTTDAdminClient.get_timetable()fetches the real, current timetable of any vehicle from the running game (via a patched GameScript API + the AdminBridge GS) — no company join needed, works for timetables set before connecting. - Station Listing: Enumerate stations via the Admin GameScript channel with
list_stations(). - Station Cargo Snapshots:
OpenTTDAdminClient.get_station()returns a station's live per-cargo state from the running game — both the real-time amount waiting and the planned flow through the cargodist link graph — over the AdminBridge GS (stock GameScript API, no server patch needed). - Cargo Flow Breakdown:
OpenTTDAdminClient.get_station_cargo()breaks one cargo type down by source station and next hop (routing destination) for both waiting (real-time) and planned amounts, with optionalfrom_station/via_stationfilters. - Cargo Table:
OpenTTDAdminClient.list_cargo()names the barecargo_ids the station queries and cargo events return — every cargo type in the running game with its label (PASS,COAL, …) and freight flag, read live so NewGRF-specific ids resolve correctly. - Game Events: react to the game instead of polling it —
subscribe_events()streams events over the AdminBridge GS as they happen: a vehicle reaching or leaving a stop (vehicle_arrive/vehicle_depart, with dwell time and cargo aboard), a station's waiting cargo changing (cargo_waiting), plus crashes, industries opening/closing, towns, companies and subsidies. Consume them withawait wait_for_event()or anon_eventcallback; filter by kind, company, vehicle, station or cargo.
🛠 Setup
Prerequisites
- Python 3.11+
- A running OpenTTD server (preferably JGRPP)
Installation
- Create and activate a virtual environment:
python3 -m venv venv source venv/bin/activate # Linux/macOS - Install dependencies:
pip install openttd-protocol pymonocypher
📖 Usage
Running the default client
The main.py script is configured to join the local server and the company "Én transport".
python3 main.py [Username] [CompanyID]
Example:
python3 main.py MyBot 0
Module Integration
You can use the openttd package in your own projects:
from openttd import OpenTTDClient
client = OpenTTDClient(host="127.0.0.1", username="BotName")
await client.connect(server_password="asd")
await client.join_company(company_id=0, company_password="asd123")
await client.joined.wait()
# Your logic here...
📂 Project Structure
main.py: Main entry point and usage example.lib/openttd/: Core package containing the protocol and client logic.docs/: Extensive documentation on architecture, protocol, and contributing.tests/: Comprehensive test suite (Logic, Protocol, E2E).
🧪 Testing
We maintain high test coverage. To run normal (non-E2E) tests:
pytest -m "not e2e"
For detailed instructions on E2E testing and coverage reports, see the Testing Guide.
📜 Documentation
Languages
Python
86.6%
Squirrel
12.7%
Dockerfile
0.7%