kovagoadiandClaude d87c779d94
Continuous Integration / lint-and-security (pull_request) Successful in 41s
Continuous Integration / tests-and-coverage (pull_request) Successful in 28s
Add game event support to the admin client
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]>
2026-08-31 18:14:40 +02:00
2026-06-05 23:18:09 +02:00
2026-06-05 23:23:55 +02:00

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:
    python3 -m venv venv
    source venv/bin/activate  # Linux/macOS
    
  2. 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

S
Description
No description provided
Readme
429 KiB
Languages
Python 86.6%
Squirrel 12.7%
Dockerfile 0.7%