Everything on the admin GameScript channel so far has been request/reply. This adds the other direction: subscribe_events() opens a push stream so a bot can react to the game instead of polling it, consumed either by awaiting wait_for_event() or via an on_event callback. Both see every event; an event goes to at most one waiter, and unclaimed ones sit in a bounded buffer. Sixteen kinds, from two sources. The engine raises no GameScript event for a vehicle reaching a stop or cargo arriving, so vehicle_arrive, vehicle_depart and cargo_waiting are synthesised by the bridge sampling state every `interval` ticks and diffing against the previous sample -- which means a stop shorter than the interval is never reported, and the first sample only establishes a baseline. The rest (crashes, industries, towns, companies, subsidies) are engine events forwarded verbatim. vehicle_lost, vehicle_waiting_in_depot and vehicle_unprofitable are deliberately absent: the engine raises those only for AI companies, so a GameScript can never observe them. The server-side half lives in the AdminBridge GameScript, which is not in this repo -- docker/config is gitignored -- so it has to be updated separately for any of this to work. Also repoints the scheduled-dispatch E2E test at a dedicated vehicle (DISPATCH_VEHICLE_ID). It had been silently skipping because vehicle 7 carries a hand-built annual dispatch schedule, which left eight dispatch methods unverified end to end while check_public_calls.py reported them green off static analysis of the call sites. Co-Authored-By: Claude <[email protected]>
87 lines
4.7 KiB
Markdown
87 lines
4.7 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.
|
|
- **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)
|