kovagoadiandClaude 25953bea06
Continuous Integration / lint-and-security (pull_request) Successful in 20s
Continuous Integration / tests-and-coverage (pull_request) Successful in 25s
Fix ruff lint findings across client, protocol, and tests
Resolve 51 findings from the I/RUF/BLE/TRY002/S110/PLR0402 rule set:

- Sort imports and __all__ (I001, RUF022, PLR0402). The sys.path.insert
  calls in check_public_calls.py and tests/test_e2e.py still precede the
  openttd imports that depend on them.
- Replace unused unpacked values with _ (RUF059) and annotate the two
  timetable lookup tables as ClassVar (RUF012).
- Narrow the best-effort excepts in OpenTTDClient.quit and
  OpenTTDAdminClient.quit to (OSError, SocketClosed) and log at debug
  rather than swallowing silently (BLE001, S110). The test doubles now
  raise an OSError subclass so they still exercise that branch.
- Narrow the gamescript JSON fallback to json.JSONDecodeError. The broad
  catch in receive_packet keeps a noqa: it guards untrusted wire data and
  must degrade to a no-op packet instead of killing the connection.
- Use contextlib.suppress instead of try/except/pass in tests.

ruff check . is clean, 102 tests pass, coverage stays at 100%.

Co-Authored-By: Claude <[email protected]>
2026-08-26 20:57:56 +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.

🛠 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%