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]>
Editing (game port, OpenTTDClient): a core of JGRPP's scheduled dispatch
DoCommands — set_scheduled_dispatch (enable/disable), add/remove schedule,
add/remove/clear slots, and set duration/start date. Adds the command IDs
to protocol.py.
Viewing (admin, OpenTTDAdminClient.get_dispatch): the GameScript API has no
dispatch support, so a new server patch (docker/patches/0002-*) adds
read-only GSOrder.GetScheduledDispatch* / IsScheduledDispatchEnabled
getters, an AdminBridge GameScript get_dispatch handler exposes them, and
get_dispatch() returns the live schedules and slots (mirrors get_timetable).
Note: set_dispatch_start_date values are normalised by the engine relative
to current game time, so they read back offset from the requested value.
Includes unit + e2e tests, a demo in main.py, and protocol/timetable docs.
The AdminBridge GameScript and the patched OpenTTD-patches clone live
outside this repo; the 0002 patch file is the durable source for the latter.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Timetables have no GameScript API surface, so this implements real
DoCommands over the game port (ClientCommand/ServerCommand) instead of
the Admin GameScript relay used for list_vehicles(): change_timetable(),
autofill_timetable(), set_timetable_start(), and set_vehicle_on_time()
send commands, while get_vehicle_timetable() reconstructs state purely
by observing ServerCommand broadcasts, since no query command exists.
Includes the custom varuint wire codec these commands require, a full
usage guide (docs/TIMETABLES.md), and a worked demo in main.py.
Co-Authored-By: Claude Sonnet 5 <[email protected]>