Files
openttd-client/README.md
T
kovagoadiandClaude ba26b59c40
Continuous Integration / lint-and-security (pull_request) Successful in 20s
Continuous Integration / tests-and-coverage (pull_request) Successful in 25s
Add list_cargo() to the admin client
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]>
2026-08-31 18:33:05 +02:00

88 lines
5.0 KiB
Markdown

# 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 with `OpenTTDAdminClient.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 optional `from_station`/`via_station` filters.
- **Cargo Table:** `OpenTTDAdminClient.list_cargo()` names the bare `cargo_id`s 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 with `await wait_for_event()` or an `on_event` callback; filter by kind, company, vehicle, station or cargo.
## 🛠 Setup
### Prerequisites
- Python 3.11+
- A running OpenTTD server (preferably JGRPP)
### Installation
1. Create and activate a virtual environment:
```bash
python3 -m venv venv
source venv/bin/activate # Linux/macOS
```
2. Install dependencies:
```bash
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".
```bash
python3 main.py [Username] [CompanyID]
```
Example:
```bash
python3 main.py MyBot 0
```
### Module Integration
You can use the `openttd` package in your own projects:
```python
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:
```bash
pytest -m "not e2e"
```
For detailed instructions on E2E testing and coverage reports, see the [Testing Guide](file:///home/kovagoadi/openttd-client/docs/TESTING.md).
## 📜 Documentation
- [Architecture & Design](docs/ARCHITECTURE.md)
- [Protocol Internals (PAKE/Encryption)](docs/PROTOCOL.md)
- [Vehicle Timetables Usage Guide](docs/TIMETABLES.md)
- [Game Events Usage Guide](docs/EVENTS.md)
- [Contributor Guide](docs/CONTRIBUTING.md)