Retain aircraft through native landing and qualify bounded input diagnostics

This commit is contained in:
2026-09-19 20:34:18 +02:00
parent f870443b9b
commit 2fba12f987
129 changed files with 50369 additions and 88 deletions
@@ -0,0 +1,227 @@
// Explicit finite retention only. A future HQ owner supplies existing assets and
// delivered orders, retains this service, and ticks it even on an empty server.
class ERR_AircraftServiceEntry
{
Vehicle Aircraft;
SCR_AIGroup Group;
IEntity Pilot;
PilotCompartmentSlot Seat;
ref ERR_AircraftRouteFlight Flight;
int Token;
float Deadline;
vector Destination;
bool Pending;
bool CancelRequested;
bool Retired;
string Reason;
}
class ERR_AircraftService
{
static const int MAX_AIRCRAFT = 4;
static const int MAX_ORDERS = 128;
protected static ref map<Vehicle, ERR_AircraftService> s_Retainers = new map<Vehicle, ERR_AircraftService>();
protected ERR_GameMode m_Mode;
protected BaseWorld m_World;
protected ref array<ref ERR_AircraftServiceEntry> m_Entries = {};
protected ref set<int> m_UsedTokens = new set<int>();
protected bool m_Stopping;
void ERR_AircraftService(ERR_GameMode mode)
{
m_Mode = mode;
if (GetGame()) m_World = GetGame().GetWorld();
if (!Context()) m_Stopping = true;
}
protected bool Context()
{
return Replication.IsServer() && GetGame() && m_World && GetGame().GetWorld() == m_World && m_Mode && m_Mode == ERR_GameMode.Get() && m_Mode.GetWorld() == m_World && m_Mode.Active();
}
protected ERR_AircraftServiceEntry Find(Vehicle aircraft)
{
if (!aircraft) return null;
foreach (ERR_AircraftServiceEntry entry : m_Entries) if (entry.Aircraft == aircraft) return entry;
return null;
}
protected bool Original(ERR_AircraftServiceEntry entry)
{
if (!Context() || !entry || !entry.Aircraft || !entry.Group || !entry.Pilot || !entry.Seat || !GetGame().GetPlayerManager()) return false;
if (entry.Aircraft.GetWorld() != m_World || entry.Pilot.GetWorld() != m_World || entry.Group.GetWorld() != m_World) return false;
if (entry.Group.GetFaction() != m_Mode.EnemyFaction() || SCR_FactionManager.SGetFaction(entry.Aircraft) != m_Mode.EnemyFaction() || ERR_Actors.Group(entry.Pilot) != entry.Group) return false;
if (entry.Group.GetPlayerCount(true) > 0 || ERR_Actors.Player(entry.Pilot)) return false;
HelicopterControllerComponent controller = HelicopterControllerComponent.Cast(entry.Aircraft.FindComponent(HelicopterControllerComponent));
if (!controller || controller.GetOwner() != entry.Aircraft || controller.GetPilotCompartmentSlot() != entry.Seat || entry.Seat.GetOccupant() != entry.Pilot || CompartmentAccessComponent.GetVehicleIn(entry.Pilot) != entry.Aircraft) return false;
array<int> players = {}; GetGame().GetPlayerManager().GetPlayers(players);
foreach (int id : players)
{
IEntity actor = GetGame().GetPlayerManager().GetPlayerControlledEntity(id);
if (actor && (actor == entry.Pilot || CompartmentAccessComponent.GetVehicleIn(actor) == entry.Aircraft)) return false;
}
return true;
}
protected bool PhysicalReady(ERR_AircraftServiceEntry entry)
{
if (!Original(entry) || !ERR_Actors.Alive(entry.Aircraft) || !ERR_Actors.CanFight(entry.Pilot) || !ERR_CasualtyLoadingRules.AircraftReady(entry.Aircraft)) return false;
HelicopterControllerComponent controller = HelicopterControllerComponent.Cast(entry.Aircraft.FindComponent(HelicopterControllerComponent));
VehicleHelicopterSimulation simulation = controller.GetSimulation();
FuelManagerComponent fuel = FuelManagerComponent.Cast(entry.Aircraft.FindComponent(FuelManagerComponent));
SCR_VehicleDamageManagerComponent damage = SCR_VehicleDamageManagerComponent.Cast(entry.Aircraft.FindComponent(SCR_VehicleDamageManagerComponent));
Physics physics = entry.Aircraft.GetPhysics();
if (controller.IsEngineOn() || !simulation || !simulation.IsValid() || !simulation.HasAnyGroundContact() || simulation.RotorCount() != 2) return false;
if (!fuel || !fuel.HasFuel() || !damage || !damage.GetEngineFunctional() || damage.GetMovementDamage() >= 1 || damage.IsOnFire()) return false;
if (!physics || !physics.IsDynamic() || !physics.IsActive()) return false;
for (int rotor = 0; rotor < 2; rotor++) if (simulation.RotorGetState(rotor) == RotorState.DESTROYED) return false;
return true;
}
bool Register(Vehicle aircraft, SCR_AIGroup group, IEntity pilot, PilotCompartmentSlot seat)
{
if (!Context() || m_Stopping || !aircraft || !group || !pilot || !seat || m_Entries.Count() >= MAX_AIRCRAFT || Find(aircraft) || s_Retainers.Get(aircraft)) return false;
foreach (ERR_AircraftServiceEntry known : m_Entries)
if (known.Pilot == pilot || known.Group == group || known.Seat == seat) return false;
ERR_AircraftServiceEntry entry = new ERR_AircraftServiceEntry();
entry.Aircraft = aircraft; entry.Group = group; entry.Pilot = pilot; entry.Seat = seat;
if (!PhysicalReady(entry)) return false;
m_Entries.Insert(entry); s_Retainers.Set(aircraft, this);
return true;
}
bool Ready(Vehicle aircraft)
{
ERR_AircraftServiceEntry entry = Find(aircraft);
if (!Context() || m_Stopping || !entry || entry.Pending || entry.Retired || s_Retainers.Get(aircraft) != this) return false;
return PhysicalReady(entry);
}
bool StartRoute(Vehicle aircraft, ERR_AircraftRouteEnvelope qualification, int token, float deadline)
{
if (!Context() || m_Stopping || !qualification || token <= 0 || m_UsedTokens.Contains(token) || m_UsedTokens.Count() >= MAX_ORDERS || !Ready(aircraft)) return false;
float now = m_World.GetWorldTime();
if (!ERR_AircraftFlightRules.Deadline(now, deadline)) return false;
ERR_AircraftServiceEntry entry = Find(aircraft);
// Commit the attempt before calling the native-backed controller. No retry of
// this token, including a partially initialized or conflicting Start refusal.
m_UsedTokens.Insert(token); entry.Token = token; entry.Deadline = deadline;
entry.Destination = qualification.Destination(); entry.CancelRequested = false;
entry.Flight = new ERR_AircraftRouteFlight(); entry.Pending = true;
bool started = entry.Flight.StartRoute(m_Mode, entry.Aircraft, entry.Group, entry.Pilot, entry.Seat, qualification, token, deadline);
if (!started)
{
entry.Retired = true; entry.Reason = "flight start refused; no automatic retry or replacement";
// A started lease must not be dropped even if a future controller reports
// refusal after committing it. Unclaimed IDLE refusals have no active flight.
entry.Pending = entry.Flight.OrderToken() > 0 && !entry.Flight.Finished();
if (entry.Pending) { entry.CancelRequested = true; entry.Flight.Cancel(entry.Reason); }
Finish(entry);
return false;
}
entry.Reason = "flight active";
return true;
}
protected void Finish(ERR_AircraftServiceEntry entry)
{
if (!entry || !entry.Pending || !entry.Flight || !entry.Flight.Finished()) return;
entry.Pending = false;
int state = entry.Flight.State();
entry.Reason = entry.Flight.Reason();
if (state != ERR_AircraftFlightState.COMPLETE && state != ERR_AircraftFlightState.CANCELLED)
{
entry.Retired = true;
if (entry.Reason.IsEmpty()) entry.Reason = "flight ended without verified landing";
}
// COMPLETE/CANCELLED is historical. Ready always rechecks the physical asset.
}
bool Cancel(Vehicle aircraft, int token, string reason)
{
if (!Context()) return false;
ERR_AircraftServiceEntry entry = Find(aircraft);
if (!entry || !entry.Pending || !entry.Flight || entry.Token != token || token <= 0 || entry.CancelRequested) return false;
entry.CancelRequested = true;
entry.Flight.Cancel(reason); Finish(entry);
return true;
}
void Tick()
{
if (!Replication.IsServer()) return;
bool context = Context();
if (!context) m_Stopping = true;
foreach (ERR_AircraftServiceEntry entry : m_Entries)
{
if (entry.Pending && entry.Flight)
{
// Controller owns expiry/capture/terminal rules. It must keep receiving Tick
// during cancellation; there is no no-player pause in this service.
entry.Flight.Tick(); Finish(entry);
}
if (!context || !Original(entry))
{
entry.Retired = true;
if (entry.Reason.IsEmpty()) entry.Reason = "original mission or asset ownership lost";
}
}
ReleaseRetainers();
}
void Stop()
{
if (!Replication.IsServer() || m_Stopping) return;
m_Stopping = true;
foreach (ERR_AircraftServiceEntry entry : m_Entries)
{
if (!entry.Pending || !entry.Flight) continue;
entry.CancelRequested = true;
entry.Flight.Cancel("aircraft service stopping"); Finish(entry);
}
// Closing admissions is not flight completion. Caller retains/ticks until
// Drained; terminal-mode teardown is reported by the controller as relinquish.
ReleaseRetainers();
}
protected void ReleaseRetainers()
{
if (!m_Stopping || !Drained()) return;
foreach (ERR_AircraftServiceEntry entry : m_Entries)
if (entry.Aircraft && s_Retainers.Get(entry.Aircraft) == this) s_Retainers.Remove(entry.Aircraft);
}
bool Drained()
{
foreach (ERR_AircraftServiceEntry entry : m_Entries) if (entry.Pending) return false;
return true;
}
bool Stopping() { return m_Stopping; }
int RegisteredCount() { return m_Entries.Count(); }
int AttemptedOrders() { return m_UsedTokens.Count(); }
bool Pending(Vehicle aircraft) { ERR_AircraftServiceEntry entry = Find(aircraft); return entry && entry.Pending; }
bool Retired(Vehicle aircraft) { ERR_AircraftServiceEntry entry = Find(aircraft); return !entry || entry.Retired; }
int OrderToken(Vehicle aircraft)
{
int token = 0;
ERR_AircraftServiceEntry entry = Find(aircraft);
if (entry) token = entry.Token;
return token;
}
float OrderDeadline(Vehicle aircraft)
{
float deadline = -1;
ERR_AircraftServiceEntry entry = Find(aircraft);
if (entry) deadline = entry.Deadline;
return deadline;
}
int FlightState(Vehicle aircraft)
{
int state = -1;
ERR_AircraftServiceEntry entry = Find(aircraft);
if (entry && entry.Flight) state = entry.Flight.State();
return state;
}
string Reason(Vehicle aircraft)
{
string reason = "unregistered aircraft";
ERR_AircraftServiceEntry entry = Find(aircraft);
if (entry) reason = entry.Reason;
return reason;
}
vector Destination(Vehicle aircraft) { vector destination = "0 0 0"; ERR_AircraftServiceEntry entry = Find(aircraft); if (entry) destination = entry.Destination; return destination; }
void ~ERR_AircraftService()
{
// Destruction is teardown, never proof of landing. Normal shutdown must use
// Stop + retained Tick until Drained. Entity ownership stays with its creator.
Stop();
foreach (ERR_AircraftServiceEntry entry : m_Entries)
if (entry.Aircraft && s_Retainers.Get(entry.Aircraft) == this) s_Retainers.Remove(entry.Aircraft);
}
}
@@ -4,6 +4,19 @@
modded class ERR_AircraftRouteFlight
{
float ERR_NativeRouteStageDeadline() { return m_StageDeadline; }
int ERR_NativeRouteActionUpdates() { return m_ActionUpdates; }
int ERR_NativeRouteHelicopterUpdates() { return m_HelicopterUpdates; }
bool ERR_NativeRouteFreshReadback(float now) { return m_Readback && m_LastControl >= 0 && now >= m_LastControl && now - m_LastControl <= 1000; }
}
// Read-only fixture access to the service's exact retained controller.
modded class ERR_AircraftService
{
ERR_AircraftRouteFlight ERR_NativeOriginalFlight(Vehicle aircraft)
{
ERR_AircraftServiceEntry entry = Find(aircraft);
if (entry) return entry.Flight;
return null;
}
}
modded class SCR_CharacterControllerComponent
{
@@ -30,6 +43,14 @@ class ERR_NativeAircraftRouteRules
if (cancellation) return state == ERR_AircraftFlightState.CANCELLED && cancellationCount == 1;
return state == ERR_AircraftFlightState.COMPLETE && cancellationCount == 0;
}
static bool CanStop(int state, bool contact, float height, float speed, float vertical, float upright, float displacement)
{
return state == ERR_AircraftFlightState.TRANSLATING && !contact && displacement >= 3 && ERR_AircraftFlightRules.Hover(height, speed, vertical, upright);
}
static bool CancelProof(bool airborneControl, bool landingHeld, bool sameDeadline, bool sameToken, bool frozenPoint, bool insideCorridor, float targetError, bool arrived)
{
return airborneControl && landingHeld && sameDeadline && sameToken && frozenPoint && insideCorridor && targetError >= 0 && targetError <= 0.75 && !arrived;
}
static bool TranslateProof(bool proven, bool held, float requestedDistance, float displacement, float targetError)
{
return proven && held && requestedDistance >= 10 && requestedDistance <= 28.05 && displacement >= requestedDistance - 0.75 && targetError >= 0 && targetError <= 0.75;
@@ -57,6 +78,7 @@ class ERR_NativeAircraftRouteProbe
protected FuelManagerComponent m_Fuel;
protected ref ERR_AircraftRouteEnvelope m_Envelope;
protected ref ERR_AircraftRouteFlight m_Flight;
protected ref ERR_AircraftService m_Service;
protected float m_Deadline;
protected float m_StageDeadline;
protected float m_Stable = -1;
@@ -66,6 +88,14 @@ class ERR_NativeAircraftRouteProbe
protected float m_ControlDeadline;
protected int m_CancelCount;
protected bool m_CancelFixture;
protected float m_CancelStable = -1;
protected float m_CancelAt = -1;
protected float m_LandingStable = -1;
protected int m_CancelActionUpdates;
protected int m_CancelHelicopterUpdates;
protected bool m_ContinuedAirborneControl;
protected bool m_LandingHeld;
protected vector m_CancelLandingPoint;
protected bool m_TranslationFixture;
protected bool m_TranslationObserved;
protected bool m_TranslationHeld;
@@ -99,7 +129,8 @@ class ERR_NativeAircraftRouteProbe
{
m_Mode = mode;
if (!GetGame() || !GetGame().GetWorld() || !Enabled() || s_Current) { m_Stopped = true; return; }
string outcome; System.GetCLIParam("errSmokeOutcome", outcome); m_CancelFixture = false; m_TranslationFixture = true;
string cancellation; m_CancelFixture = System.GetCLIParam("errSmokeRouteServiceCancel", cancellation) && cancellation == "1";
m_TranslationFixture = true;
m_Deadline = GetGame().GetWorld().GetWorldTime() + 300000;
s_Current = this;
}
@@ -197,6 +228,31 @@ class ERR_NativeAircraftRouteProbe
}
return false;
}
protected bool RetainedIdentity()
{
return m_Service && m_Flight && m_Service.ERR_NativeOriginalFlight(m_Aircraft) == m_Flight && m_Service.RegisteredCount() == 1 && m_Service.AttemptedOrders() == 1 && m_Service.OrderToken(m_Aircraft) == ORDER_TOKEN && m_Service.OrderDeadline(m_Aircraft) == m_ControlDeadline;
}
protected bool StartRetainedRoute()
{
if (m_Service || m_Flight) return Check(false, "one retained aircraft service only");
m_Service = new ERR_AircraftService(m_Mode);
if (!Check(m_Service.Register(m_Aircraft, m_Group, m_Pilot, m_Seat) && m_Service.RegisteredCount() == 1 && m_Service.Ready(m_Aircraft), "retention service registers one exact physical aircraft, original pilot/group/seat and current readiness")) return false;
m_ControllerStarts++;
bool started = m_Service.StartRoute(m_Aircraft, m_Envelope, ORDER_TOKEN, m_ControlDeadline);
m_Flight = m_Service.ERR_NativeOriginalFlight(m_Aircraft);
return Check(started && RetainedIdentity() && m_Service.Pending(m_Aircraft), "actual controller accepts one qualified original pilot/aircraft order through retention service");
}
protected bool StopRetainedRoute(float now)
{
if (m_CancelCount != 0 || !RetainedIdentity() || m_Service.Stopping()) return Check(false, "one explicit retained service stop only");
m_CancelCount = 1; m_CancelAt = now;
m_CancelActionUpdates = m_Flight.ERR_NativeRouteActionUpdates();
m_CancelHelicopterUpdates = m_Flight.ERR_NativeRouteHelicopterUpdates();
m_Service.Stop();
m_CancelLandingPoint = m_Flight.LandingPoint();
PrintFormat("ERR AIRCRAFT SERVICE STOP: token=%1 originalDeadline=%2 now=%3 landingPoint=%4 pending=%5 actions=%6 helicopterUpdates=%7", m_Flight.OrderToken(), m_ControlDeadline, now, m_CancelLandingPoint, m_Service.Pending(m_Aircraft), m_CancelActionUpdates, m_CancelHelicopterUpdates);
return Check(RetainedIdentity() && m_Service.Stopping() && m_Service.Pending(m_Aircraft) && !m_Service.Drained() && !m_Service.Ready(m_Aircraft) && m_Flight.State() == ERR_AircraftFlightState.LANDING && !m_Flight.Finished() && m_Flight.ERR_NativeControlDeadline() == m_ControlDeadline && m_Envelope.Contains(m_CancelLandingPoint, 1), "airborne service Stop retains original pending flight, frozen qualified landing point and immutable lease");
}
protected void Diagnostic(float now)
{
if (!m_Aircraft || !m_Controller || !m_Simulation || now < m_NextLog) return;
@@ -227,7 +283,7 @@ class ERR_NativeAircraftRouteProbe
if (m_Stopped) return -1;
if (!Enabled() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || now >= m_Deadline) { Check(false, "active exact mission and bounded300-second fixture"); return -1; }
if (!NoPlayers()) { Check(false, "fixture remains unclaimed by any human"); return -1; }
if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) { m_Stable = -1; m_TranslationStable = -1; }
if (m_LastTick >= 0 && (now < m_LastTick || now - m_LastTick > 1500)) { m_Stable = -1; m_TranslationStable = -1; m_CancelStable = -1; m_LandingStable = -1; }
m_LastTick = now;
if (m_Step == 0)
{
@@ -268,15 +324,13 @@ class ERR_NativeAircraftRouteProbe
m_Envelope = ERR_AircraftRouteEnvelope.QualifyRoute(m_Mode, m_Aircraft, m_Pilot, m_Destination);
if (!Check(m_Envelope != null, "actual full corridor, both pads and complete native rotor/model footprint issue exact route qualification")) return -1;
m_InitialFuel = m_Fuel.GetTotalFuel();
m_Flight = new ERR_AircraftRouteFlight(); m_ControllerStarts++;
m_ControlDeadline = Math.Min(m_Deadline, now + 240000);
bool started;
started = m_Flight.StartRoute(m_Mode, m_Aircraft, m_Group, m_Pilot, m_Seat, m_Envelope, ORDER_TOKEN, m_ControlDeadline);
if (!Check(started, "actual controller accepts one qualified original pilot/aircraft order")) return -1;
if (!StartRetainedRoute()) return -1;
m_Step = 3; return 0;
}
if (m_Step == 3)
{
if (!RetainedIdentity()) { Check(false, "service retains exact original flight, token and deadline"); return -1; }
if (!OriginalOccupancy()) { Check(false, "original pilot remains conscious and in its exact seat/group"); return -1; }
bool engine = m_Controller.IsEngineOn(); if (engine && !m_LastEngine) m_EngineStarts++; m_LastEngine = engine;
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
@@ -297,12 +351,41 @@ class ERR_NativeAircraftRouteProbe
else if (m_TranslationStable < 0) m_TranslationStable = now;
if (m_TranslationStable >= 0 && now - m_TranslationStable >= 5000) m_TranslationHeld = true;
}
m_Flight.Tick();
if (m_CancelFixture && m_CancelCount == 0)
{
bool canStop = m_TranslationObserved && ERR_NativeAircraftRouteRules.CanStop(m_Flight.State(), contact, height, m_Simulation.GetVelocity().Length(), m_Simulation.GetVelocity()[1], transform[1][1], vector.DistanceXZ(transform[3], m_TranslationOrigin));
if (!canStop) m_CancelStable = -1;
else if (m_CancelStable < 0) m_CancelStable = now;
if (m_CancelStable >= 0 && now - m_CancelStable >= 500 && !StopRetainedRoute(now)) return -1;
}
if (m_CancelCount == 1)
{
int state = m_Flight.State();
if (!m_Service.Stopping() || m_Service.Ready(m_Aircraft) || m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline || m_Flight.OrderToken() != ORDER_TOKEN || m_Flight.LandingPoint() != m_CancelLandingPoint || !m_Envelope.Contains(m_CancelLandingPoint, 1)) { Check(false, "stopped service retains immutable original lease and qualified frozen landing point"); return -1; }
if (state != ERR_AircraftFlightState.LANDING && state != ERR_AircraftFlightState.SHUTDOWN && state != ERR_AircraftFlightState.CANCELLED) { Check(false, "service Stop permits only retained landing, shutdown or cancellation"); return -1; }
if (!m_Flight.Finished() && (!m_Service.Pending(m_Aircraft) || m_Service.Drained())) { Check(false, "airborne or shutting-down order remains pending until actual terminal observation"); return -1; }
if (state == ERR_AircraftFlightState.LANDING && !contact && height > 1 && m_Flight.ERR_NativeRouteActionUpdates() > m_CancelActionUpdates + 10 && m_Flight.ERR_NativeRouteHelicopterUpdates() > m_CancelHelicopterUpdates + 10 && m_Flight.ERR_NativeRouteFreshReadback(now)) m_ContinuedAirborneControl = true;
bool cancelledLanded = landed && vector.DistanceXZ(transform[3], m_CancelLandingPoint) <= 0.75;
if (!cancelledLanded) m_LandingStable = -1;
else if (m_LandingStable < 0) m_LandingStable = now;
}
m_Service.Tick();
if (m_CancelCount == 1 && m_Flight.State() == ERR_AircraftFlightState.SHUTDOWN)
{
if (!Check(landed && m_LandingStable >= 0 && now - m_LandingStable >= 2000, "service cancellation reaches shutdown only after two observed stable grounded seconds")) return -1;
m_LandingHeld = true;
}
if (!m_Flight.Finished()) return 0;
PrintFormat("ERR AIRCRAFT ROUTE FIXTURE RESULT: state=%1 token=%2 engineStarts=%3 controllerStarts=%4 wakes=%5 pilotHooks=%6 helicopterHooks=%7 reason=%8", m_Flight.State(), m_Flight.OrderToken(), m_EngineStarts, m_ControllerStarts, m_Wakes, m_PilotHooks, m_HelicopterHooks, m_Flight.Reason());
bool complete = ERR_NativeAircraftRouteRules.Terminal(m_CancelFixture, m_Flight.State(), m_CancelCount);
complete = complete && ERR_NativeAircraftRouteRules.PhysicalFinish(OriginalOccupancy(), landed, !m_Controller.IsEngineOn(), m_InitialFuel - m_Fuel.GetTotalFuel(), m_Wakes, m_EngineStarts, m_ControllerStarts, m_PilotHooks, m_HelicopterHooks);
if (m_TranslationFixture)
if (m_CancelFixture)
{
bool cancelled = ERR_NativeAircraftRouteRules.CancelProof(m_ContinuedAirborneControl, m_LandingHeld, m_Flight.ERR_NativeControlDeadline() == m_ControlDeadline, m_Flight.OrderToken() == ORDER_TOKEN, m_Flight.LandingPoint() == m_CancelLandingPoint, m_Envelope.Contains(m_CancelLandingPoint, 1), vector.DistanceXZ(transform[3], m_CancelLandingPoint), m_Flight.Arrived());
PrintFormat("ERR AIRCRAFT SERVICE CANCEL RESULT: stops=%1 elapsed=%2 actualAirborneControl=%3 landingHeld=%4 frozenLanding=%5 finalPosition=%6 targetError=%7", m_CancelCount, now - m_CancelAt, m_ContinuedAirborneControl, m_LandingHeld, m_CancelLandingPoint, transform[3], vector.DistanceXZ(transform[3], m_CancelLandingPoint));
complete = complete && cancelled && m_Service.Stopping() && !m_Service.Ready(m_Aircraft);
}
else if (m_TranslationFixture)
{
float requestedDistance = vector.DistanceXZ(m_TranslationOrigin, m_TranslationTarget);
float displacement = vector.DistanceXZ(transform[3], m_TranslationOrigin);
@@ -312,6 +395,7 @@ class ERR_NativeAircraftRouteProbe
complete = complete && translated && m_Flight.Arrived() && vector.DistanceXZ(transform[3], m_Destination) <= 0.75;
}
if (!Check(complete && m_Flight.OrderToken() == ORDER_TOKEN, "actual control order completes on original physical grounded aircraft with engine off, finite fuel use and exact pilot")) return -1;
if (!Check(RetainedIdentity() && m_Service.Drained() && !m_Service.Pending(m_Aircraft) && !m_Service.Retired(m_Aircraft), "retention service observes completed flight drained with original token/deadline and no pending order")) return -1;
m_Complete = true; Stop(); return 1;
}
return 0;
@@ -321,11 +405,12 @@ class ERR_NativeAircraftRouteProbe
if (m_Stopped) return;
m_Stopped = true;
if (s_Current == this) s_Current = null;
if (m_Flight && !m_Flight.Finished() && m_CancelCount == 0) m_Flight.Cancel("native fixture stopped");
if (m_Service && !m_Service.Stopping()) m_Service.Stop();
// End-of-test disposal is not successful flight or a production cancellation.
// These assets are fixture-owned only, never registered with mission cleanup.
// Release the controller before deleting disposable unclaimed physical assets.
m_Flight = null;
// Retain service/controller through guarded disposal; then observe ownership
// loss before releasing their references. No direct second Cancel or Tick.
bool context = GetGame() && GetGame().GetWorld() && GetGame().GetPlayerManager() && m_Mode && m_Mode == ERR_GameMode.Get();
bool faction = context && (!m_Group || (!m_PilotRequested && m_Group.GetAgentsCount() == 0 && !m_Group.GetFaction()) || m_Group.GetFaction() == m_Mode.EnemyFaction()) && (!m_Aircraft || SCR_FactionManager.SGetFaction(m_Aircraft) == m_Mode.EnemyFaction());
bool groupUnchanged = context && (!m_Group || m_Group.GetAgentsCount() == 0 || (m_Group.GetAgentsCount() == 1 && m_Pilot && ERR_Actors.Group(m_Pilot) == m_Group));
@@ -335,6 +420,9 @@ class ERR_NativeAircraftRouteProbe
ERR_Actors.DeleteSpawned(m_Group); ERR_Actors.DeleteSpawned(m_Pilot); ERR_Actors.DeleteSpawned(m_Aircraft);
}
else Print("ERR AIRCRAFT ROUTE FIXTURE CLEANUP: ownership changed; captured entities preserved");
if (m_Service) m_Service.Tick();
m_Flight = null;
m_Service = null;
m_Envelope = null;
}
void ~ERR_NativeAircraftRouteProbe() { Stop(); }
@@ -132,7 +132,11 @@ modded class ERR_GameMode
m_ERRSmokeDone = true;
GetGame().GetCallqueue().Remove(ERR_SmokeTick);
ERR_StopHQPatrol();
Print("ERR SMOKE COMPLETE: actual immutable short aircraft route, destination hold and contact landing; experimental mechanism, not HQ transport");
string serviceCancel;
string routeResult = "actual immutable short aircraft route, destination hold and contact landing; experimental mechanism, not HQ transport";
if (System.GetCLIParam("errSmokeRouteServiceCancel", serviceCancel) && serviceCancel == "1")
routeResult = "actual airborne service Stop retains original control through frozen-point contact landing and engine shutdown; no destination arrival or HQ transport claim";
Print("ERR SMOKE COMPLETE: " + routeResult);
return;
}
if (m_ERRTestAircraftControl)
@@ -74,6 +74,10 @@ class ERR_Actors
// One server-owned implementation is installed by the AI addon.
class ERR_CommandService
{
// Maintain already admitted physical work even while tactical decisions pause
// on an empty server. Never start new orders or renew their deadlines here.
// Stop owns terminal teardown; this callback runs only in an active round.
void Maintain(float now) {}
void Tick(float now) {}
void Stop() {}
void RegisterGroup(SCR_AIGroup group) {}
@@ -371,6 +371,9 @@ class ERR_GameMode : SCR_BaseGameMode
if (RefreshRadioFeedback()) Replication.BumpMe();
m_Recovery.Tick();
if (!Active()) return;
if (m_Command) m_Command.Maintain(now);
// Maintenance can finish or stop the round; do not resume tactical work.
if (!Active()) return;
if (GetGame().GetPlayerManager().GetPlayerCount() == 0)
{
m_NoSurvivorsSince = -1;