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]>
This commit is contained in:
+35
-1
@@ -1,5 +1,5 @@
|
||||
import struct
|
||||
from enum import IntEnum
|
||||
from enum import IntEnum, StrEnum
|
||||
|
||||
import monocypher
|
||||
from openttd_protocol.wire.exceptions import SocketClosed
|
||||
@@ -220,6 +220,40 @@ class NetworkAuthenticationMethod(IntEnum):
|
||||
X25519_PAKE = 1
|
||||
X25519_AuthorizedKey = 2
|
||||
|
||||
class GameEventType(StrEnum):
|
||||
"""Event kinds the AdminBridge GameScript can push over the Admin Network.
|
||||
|
||||
These are the values of the "event" field of each event dict, and what
|
||||
OpenTTDAdminClient.subscribe_events() and wait_for_event() take. They are plain strings,
|
||||
so a bare "vehicle_arrive" works everywhere a member does.
|
||||
|
||||
The first three are synthesised by the GameScript sampling game state on an interval,
|
||||
because the engine raises no event for them; the rest are engine events forwarded as they
|
||||
happen. VehicleLost, VehicleWaitingInDepot and VehicleUnprofitable have deliberately no
|
||||
entry here: the engine only ever raises those for AI companies, never for a GameScript.
|
||||
"""
|
||||
|
||||
# Polled: derived by diffing successive samples of the game state.
|
||||
VehicleArrive = "vehicle_arrive"
|
||||
VehicleDepart = "vehicle_depart"
|
||||
CargoWaiting = "cargo_waiting"
|
||||
# Forwarded straight from the engine's own GameScript events.
|
||||
VehicleCrashed = "vehicle_crashed"
|
||||
StationFirstVehicle = "station_first_vehicle"
|
||||
IndustryOpen = "industry_open"
|
||||
IndustryClose = "industry_close"
|
||||
TownFounded = "town_founded"
|
||||
CompanyNew = "company_new"
|
||||
CompanyInTrouble = "company_in_trouble"
|
||||
CompanyBankrupt = "company_bankrupt"
|
||||
SubsidyOffer = "subsidy_offer"
|
||||
SubsidyOfferExpired = "subsidy_offer_expired"
|
||||
SubsidyAwarded = "subsidy_awarded"
|
||||
SubsidyExpired = "subsidy_expired"
|
||||
# Emitted by the bridge itself, never subscribed to: one poll produced more events than
|
||||
# fit in the per-poll cap and `count` of them were discarded.
|
||||
EventsDropped = "events_dropped"
|
||||
|
||||
class OpenTTDProtocol(TCPProtocol):
|
||||
"""Low-level OpenTTD TCP protocol handler with encryption support."""
|
||||
PacketType = PacketGameType
|
||||
|
||||
Reference in New Issue
Block a user