14 KiB
Physical aircraft dispatch through the existing HQ
This is an implementation plan based on the current mission and the staged retention service, not a claim that normal missions have operational enemy aircraft. The smallest useful end-to-end feature is one finite, separately authored enemy aircraft receiving a delivered HQ order to one explicitly authored response landing site. The pilot, radio carrier, airframe and destination must exist before the report. Neither detection nor order failure may spawn replacements, find an invented destination, commandeer the extraction helicopter, or create virtual forces.
There are two material prerequisites: the normal scenario currently has no such enemy aircraft/site binding, and the experimental route controller only accepts18–20.05m legs. A dispatch hook alone cannot deliver a useful response between the existing Everon sites. Do not turn a20m airport test into a claimed operational deployment or silently tile many short legs to bypass that limit.
Existing source boundaries
| Source/interface | Current behavior and integration consequence |
|---|---|
ERR_HQ.Observe → ERR_HQUnit.PendingReport/Uplink |
Reads real registered-group perception and snapshots observed position/time. Aircraft must use this delivered knowledge, not inspect Target.GetOrigin() or player positions for destination selection. |
ERR_HQ.Tick → ERR_HQRoute.Advance()==1 → Accept |
Only completed uplinks populate m_Contacts; reports are deduplicated by observed target/time, with Core ContactFresh allowing120seconds. Queue aircraft consideration after this existing acceptance path, without a second perception coordinator. |
ERR_HQ.SupportContacts |
Returns delivered-contact snapshots through the mortar predicate:60seconds, a surviving target reference, no distress and no aircraft targets. It is a suitable internal feed only for an explicitly ground-contact aircraft response with those narrower semantics; do not broaden it to undelivered reports. |
ERR_HQ.Downlink(group,kind,detail,sourceId) |
Returns a physical recipient route and assigns a new journal message ID. The caller must retain/advance it. A nonnull route is not delivery. Bind the exact recipient radio and original group, not merely any later replacement group member. |
ERR_HQRadioNode.Valid, ERR_HQRoute.Advance |
Radio carriers are physical, conscious and inventory-backed; links are500m, at most three relay hops,2seconds per transmission with30-second route timeout. Keep these rules and LOST events. No network fallback when a pilot/RTO lacks a usable radio. |
ERR_HQ.RegisterGroup/ReserveGroup |
Publish the finite crew group once, then reserve it as support. ERR_HQUnit.Eligible intentionally rejects mounted members; do not change that infantry predicate to admit helicopters. Keep aircraft orders in their own retained state under the same HQ. |
ERR_AircraftService.Register/Ready/StartRoute/Cancel/Tick/Stop |
Retains the exact aircraft/group/pilot/seat and controller. It provides physical readiness, single-attempt tokens, retirement and bounded cancellation, but does not authenticate an HQ order. Only the HQ-owned dispatcher should call StartRoute for mission aircraft. |
ERR_AircraftRouteEnvelope.QualifyRoute → Claim |
Qualification is specific to actual aircraft/pilot/home/destination, full geometry and native rotor/model footprint; Claim expires after2seconds and permits at most0.1m initial displacement. Qualify after downlink delivery immediately before StartRoute, not before a potentially30-second radio trip. |
ERR_AircraftControlBridge |
Remains the sole actuator through native actor/controller callbacks. HQ/service integration must not add a second PrepareControls dispatcher, physics writes or pilot input manager. |
ERR_HQReport.Snapshot preserves the original MessageId, Source, ObservedAt and observed coordinates. New downlink IDs and aircraft order tokens are separate identities; none may overwrite ObservedAt to keep a response fresh. The existing mortar report ledger demonstrates a bounded consumed-observation ledger, but is per battery and must not become an accidental shared aircraft/mortar budget.
The missing normal-mission inputs
addons/core/Configs/ERR/EveronSites.conf defines the prison, supplies, radio, extraction, medical stash and outpost. Its only transport entry is the western-cache UAZ. ERR_SiteService.Begin creates the dedicated extraction helicopter at the extraction site and registers it through SetExtractionVehicle; that vehicle supports the players' escape loop and is not an enemy-response asset, regardless of its vanilla prefab affiliation. Existing native aircraft fixtures allocate disposable aircraft and use an airport survey fallback; neither is a production catalog.
The next content change must therefore author one own-repository enemy-aircraft composition/world binding with an explicit original pilot/group/seat and physical radio inventory, plus named destination marker(s) and an explicit report-sector/site-to-destination association. Bind existing entities by owned component/reference, once; do not search all nearby helicopters and adopt an available one. An AI addon-owned binding can refer to Core site IDs without making Core depend on AI classes. Preserve the extraction composition, scenario identities, and original workspace.
A destination marker is a declared mission task location, not evidence it is currently safe. At dispatch, qualify its actual position and the entire route; any missing marker, movement, capture, geometry obstruction, player occupancy or failed qualification means no dispatch. The first role should be fixed-site deployment/response, not a pursuit landing at a prisoner's last known coordinate. Do not derive an arbitrary20m offset simply because the controller currently accepts it. Troop transport/unloading, attack runs, ammunition/fuel servicing and return trips are separate capabilities; the current single-pilot route alone proves none of them.
Concrete next owner and order flow
A separately owned ERR_HQAircraftOrders.c should be retained by the existing ERR_HQ, and wrap one ERR_AircraftService. Limit the first binding to one physical asset and one named response destination. Suggested entry points are BindAuthoredAsset(binding), ConsiderDelivered(report,now), TickOrders(now), MaintainFlights(now) and Stop(). These are proposed interfaces, not existing callable APIs.
- During normal scenario preparation, resolve and record the authored asset/crew/radio/destination identities and reserve the crew through HQ. Do not start it. Create the service only after the mode is active: its constructor sets
m_Stopping=trueifContext()is false, while the current HQ constructor runs during PREPARING. Copying the mortar initialization pattern would create a permanently stopped service. Registration must pass native ground/seat/fuel/damage/rotor readiness and the exact current enemy affiliation without waking or repairing the asset to obtain a pass. - HQ considers only an actually delivered, nonnegative, nonfuture, unexpired observation. Match its immutable observed position/source to an explicit authored response-site mapping. Reserve the original aircraft, crew and one order token before queuing a downlink. Claim a bounded per-aircraft observation-attempt ledger at queue time so repeated scheduling, failed radio routes or stale qualification cannot repeatedly dispatch the same observation. A genuinely newer delivered observation may be eligible later; a failed/retired aircraft never becomes a new asset.
- Queue one
ERR_HQ.Downlinkto the original registered crew. Keep the report snapshot, route, original RTO, destination identity/position, order token and source/downlink IDs together.Advance==-1, report expiry, capture, loss or destination change closes that attempt without replacement.Advance==1is the only path to start. Write lineage-bearing journal events for queued, lost, delivered, refused, started and terminal outcomes. - On delivery, repeat exact asset/RTO/destination/current-mode checks; ensure the report remains fresh and the same token still owns the reservation. Obtain a new qualification of the same authored endpoint, then immediately call
service.StartRouteonce with a fixed absolute deadline. No qualification retry loop, new token, deadline extension, retargeting, direct controller call or live target-position lookup is allowed. - Keep physical flight maintenance running until the original controller reaches terminal state. An accepted cancellation releases neither aircraft nor crew reservation while service.Pending is true. Only then may a healthy COMPLETE/CANCELLED asset pass service.Ready for a genuinely new order. FAILED/RELINQUISHED or captured assets remain unavailable. Fuel and crew remain physical and finite; no refill, repair, stock reset, seat replacement or respawn is part of this owner.
Decide freshness semantics before enabling normal dispatch. For a first ground-contact response borrowing SupportContacts, retain its60-second observation age throughout authorization (the general infantry/HQ ContactFresh allowance is120seconds) and cancel once it expires; cancellation remains a real retained landing/relinquish path. Engine start, climb and travel may exceed60seconds, so this conservative contract can cancel before arrival. Do not silently change freshness to match the240-second flight maximum. A later fixed-site deployment order allowed to finish after the triggering contact goes stale would need an explicit separate order policy and evidence, not a refreshed contact timestamp. Likewise, newer delivered observations could maintain a response only via actual new downlinks under the same immutable destination/deadline; do not add that complexity to the first integration implicitly.
Maintenance and teardown are required production work
Core ERR_GameMode.ServerTick returns when player count is zero before m_Command.Tick; normal HQ ticks therefore cannot be the sole flight owner. StopServices removes ServerTick, invokes command.Stop and deletes owned groups. The controller's per-callback deadline checks prevent unlimited writes, but do not substitute for retained cancellation/landing maintenance.
The narrow maintenance seam is now implemented in STAGE as a default-noop ERR_CommandService.Maintain(float now) in Core Scripts/Game/ERR/ERR_Actors.c, called once by Core before the empty-population return, ready for a future HQ override that ticks retained physical aircraft only. Core rechecks Active immediately after Maintain, so a maintenance-triggered terminal transition cannot fall through into tactical work. A future HQ override must handle only retained physical work; HQ's ordinary Tick continues to handle new report/order decisions and must not also tick the service. No HQ override or aircraft activation is included in this seam change. This preserves the existing no-player tactical pause while keeping already admitted physical work alive. Maintain must not start new responses or renew report/order clocks.
An explicit service Stop during an otherwise active round must close admission and continue this maintenance until Drained. At actual terminal-mode/world teardown, controller ownership checks deliberately relinquish; call service.Stop and a final service.Tick while the retained owner still exists, then release its records as teardown. Do not claim an airborne aircraft landed because the mode ended. Do not delay the player's result or commandeer extraction passengers to service an enemy flight. Exact authored entity disposal remains with its creator and must preserve captured/player-controlled objects.
Implementation split and readiness to enable
Assign one code owner ERR_HQAircraftOrders.c plus small coordinated HQ, command-interface and pre-population maintenance hunks. Assign a separate content/route owner the explicit aircraft/RTO/destination binding and the physical route qualification needed for that actual route. Keep ERR_AircraftService and the control bridge unchanged unless a concrete native integration fault requires a scoped fix. Registration/report-delivery/one-start/cancellation/loss must be exercised in the ordinary authored mission, not only a script that directly inserts a contact.
Current activation blocker: ERR_AircraftRouteRules.Length accepts only18–20.05m; the full corridor additionally requires near-level terrain within0.3m, low-object clearance and the large native footprint envelope. No existing normal-mission report-to-response destination pair is authored for this contract, and the controller is not qualified for field-length routes. The next useful runtime milestone is one real, specifically authored destination route with adequate physical range, then a real registered observer/RTO uplink→HQ→original crew downlink triggering that exact service order. Until those prerequisites exist, keep automatic aircraft dispatch disabled and journal the concrete lack of eligible authored support; do not ship an empty generic registration scaffold as completed air support.
The STAGE maintenance-seam implementation changes only the Core command interface and the existing ServerTick call site; it adds no scheduler, assets, aircraft owner or HQ override. Terminal StopServices ordering is unchanged: remove ServerTick, then command.Stop. tests/test_command_maintenance.py executes the actual default hook, Core tick and StopServices bodies with fake native boundaries, checking empty/populated ticks, authority/preparation/terminal refusal, maintenance-triggered termination and existing teardown behavior. Those tests do not establish native scheduling or flight coverage. No engine launch, asset mutation, network action or input operation was performed, and no new native, multiplayer or performance qualification is claimed.