Files
EscapistsReintroduced/tests/test_aircraft_control_bridge.py
jakub cab998fb64 Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
2026-09-20 16:42:52 +02:00

143 lines
11 KiB
Python

"""Actual registry dispatch/ownership/lease guards; no engine or flight claim."""
from policy_harness import ROOT, body, run
import re
FLIGHT=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFlight.c'
BRIDGE=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftControlBridge.c'
PROBES=[ROOT/'addons/core/Scripts/Game/ERR/10_AI'/name for name in ('ERR_NativeAircraftControlProbe.c','ERR_NativeAircraftRouteProbe.c')]
def cpp(text):
text=text.replace('protected ','').replace('ERR_AircraftFlight flight','ERR_AircraftFlight* flight')
for old,new in [('Vehicle.Cast(', 'CastVehicle('),('ERR_GameMode.Get()', 'Mode::Get()'),('ERR_Actors.Group(', 'Actors::GetGroup('),('SCR_FactionManager.SGetFaction(', 'Factions::Get('),('Replication.IsServer()', 'server'),('CompartmentAccessComponent.GetVehicleIn(', 'Compartment::GetVehicleIn(')]:text=text.replace(old,new)
for name in ('GetGame()','s_Owners','flight','controller','owner','m_Mode','m_Aircraft','m_Group','m_Pilot','m_Controller','m_Seat','m_Agent','m_Qualification'):
text=text.replace(name+'.',name+'->')
return text.replace('ERR_AircraftFlightState.RELINQUISHED','11')
# Preserve the actual callback admission and ControlLease boundary; substitute
# only the downstream native actuator work, exercised by existing flight tests.
prepare=body(FLIGHT,'void PrepareActions(')
prefix=prepare[:prepare.index(' if (!m_Readback')]
heli=body(FLIGHT,'void PrepareHelicopter(')
heli_prefix=heli[:heli.index(' controller.SetCollectiveMode')]
run(r'''
#include <cassert>
#include <string>
#include <map>
#include <vector>
using string=std::string;
bool server=true;
struct Entity;struct Group;struct Agent;struct Actions{};struct Controller;
using Vehicle=Entity*;using IEntity=Entity*;using ActionManager=Actions*;
using CharacterControllerComponent=Controller*;using HelicopterControllerComponent=Controller*;
struct Group {int faction=1;int GetFaction(){return faction;}};
struct Entity {bool vehicle=false;int world=1,faction=1;Entity* in=nullptr;Group* group=nullptr;int GetWorld(){return world;}};
Entity* CastVehicle(Entity* v){return v&&v->vehicle?v:nullptr;}
struct Seat {Entity* occupant=nullptr;Entity* GetOccupant(){return occupant;}};
struct Controller {Entity* owner=nullptr;Seat* seat=nullptr;Entity* GetOwner(){return owner;}Seat* GetPilotCompartmentSlot(){return seat;}};
struct Agent {Entity* actor=nullptr;Actions* actions=nullptr;Entity* GetControlledEntity(){return actor;}Actions* GetActionManager(){return actions;}};
struct Mode {bool active=true;int world=1;static Mode* current;bool Active(){return active;}int GetWorld(){return world;}int EnemyFaction(){return 1;}static Mode* Get(){return current;}};Mode* Mode::current=nullptr;
struct Game {int world=1;bool manager=true;Actions global;int GetWorld(){return world;}bool GetPlayerManager(){return manager;}Actions* GetInputManager(){return &global;}}game;Game* currentGame=&game;Game* GetGame(){return currentGame;}
struct Actors {static Group* GetGroup(Entity* p){return p->group;}};
struct Factions {static int Get(Entity* p){return p->faction;}};
struct Compartment {static Entity* GetVehicleIn(Entity* p){return p->in;}};
struct Certificate {Entity* aircraft=nullptr;Entity* pilot=nullptr;int token=7;bool ClaimedBy(Entity* a,Entity* p,int t){return aircraft==a&&pilot==p&&token==t;}};
struct ERR_AircraftFlight;
struct Registry {std::map<Entity*,ERR_AircraftFlight*> entries;ERR_AircraftFlight* Get(Entity* e){auto found=entries.find(e);return found==entries.end()?nullptr:found->second;}void Remove(Entity* e){entries.erase(e);}}registry;
struct ERR_AircraftFlight {
static Registry* s_Owners;
Mode* m_Mode=nullptr;Entity* m_Aircraft=nullptr;Group* m_Group=nullptr;Entity* m_Pilot=nullptr;
Controller* m_Controller=nullptr;Controller* m_Character=nullptr;Seat* m_Seat=nullptr;Agent* m_Agent=nullptr;Actions* m_Actions=nullptr;Certificate* m_Qualification=nullptr;
int m_State=1,m_Token=7,actionsWritten=0,helicopterWritten=0,releases=0;bool humans=false,control=true,restore=false;string m_Reason;
bool Finished(){return m_State>=8&&m_State<=11;}bool NoPlayers(){return !humans;}bool ControlLease(){return control;}
void Release(bool value){restore=value;releases++;s_Owners->Remove(m_Aircraft);}void RecordState(){}
''' + cpp(body(FLIGHT,'protected bool Owns(')) + cpp(body(FLIGHT,'protected bool Lease(')).replace('ERR_AircraftFlightState.IDLE','0') + cpp(body(FLIGHT,'protected void Relinquish(')) + cpp(body(FLIGHT,'static void DispatchActions(')) + cpp(body(FLIGHT,'static void DispatchHelicopter(')) + cpp(prefix) + ' actionsWritten++;\n }\n' + cpp(heli_prefix) + ' helicopterWritten++;\n }\n' + r'''
};
Registry* ERR_AircraftFlight::s_Owners=&registry;
struct Setup {
Mode mode;Entity aircraft,pilot,passenger;Group group;Seat seat;Controller heli,character;Actions actions;Agent agent;Certificate certificate;ERR_AircraftFlight flight;
Setup(){
registry.entries.clear();server=true;currentGame=&game;game.world=1;game.manager=true;Mode::current=&mode;
aircraft.vehicle=true;pilot.in=&aircraft;pilot.group=&group;passenger.in=&aircraft;
seat.occupant=&pilot;heli.owner=&aircraft;heli.seat=&seat;character.owner=&pilot;agent.actor=&pilot;agent.actions=&actions;certificate.aircraft=&aircraft;certificate.pilot=&pilot;
flight.m_Mode=&mode;flight.m_Aircraft=&aircraft;flight.m_Group=&group;flight.m_Pilot=&pilot;flight.m_Controller=&heli;flight.m_Character=&character;flight.m_Seat=&seat;flight.m_Agent=&agent;flight.m_Actions=&actions;flight.m_Qualification=&certificate;registry.entries[&aircraft]=&flight;
}
void Actor(bool player=false){ERR_AircraftFlight::DispatchActions(&character,&pilot,&actions,.05f,player);}
void Heli(){ERR_AircraftFlight::DispatchHelicopter(&heli);}
};
int main(){
{Setup x;x.Actor();x.Heli();assert(x.flight.actionsWritten==1&&x.flight.helicopterWritten==1&&x.flight.releases==0);}
for(int state:{1,2,3,4,5,6,7,12}){Setup x;x.flight.m_State=state;x.Actor();x.Heli();assert(x.flight.actionsWritten==1&&x.flight.helicopterWritten==1);}
for(int state:{0,8,9,10,11}){Setup x;x.flight.m_State=state;x.Actor();x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0);}
{Setup x;registry.entries.clear();x.Actor();x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0&&registry.entries.empty());}
{Setup x;server=false;x.Actor();x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0&&x.flight.releases==0);}
{Setup x;x.Actor(true);assert(x.flight.actionsWritten==0);}
{Setup x;Actions wrong;ERR_AircraftFlight::DispatchActions(&x.character,&x.pilot,&wrong,.05f,false);ERR_AircraftFlight::DispatchActions(&x.character,&x.passenger,&x.actions,.05f,false);Controller wrongController;wrongController.owner=&x.aircraft;ERR_AircraftFlight::DispatchHelicopter(&wrongController);assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0&&x.flight.releases==0);}
// Actual ownership predicates must revoke stale ownership before any actuator
// boundary, including a foreign passenger's native callback after capture.
for(int fault=0;fault<13;fault++){
Setup x;Mode replacement;Actions replacementActions;Entity other;Group otherGroup;Seat otherSeat;Agent otherAgent;
switch(fault){
case 0:Mode::current=&replacement;break;
case 1:game.world=2;break;
case 2:x.mode.active=false;break;
case 3:x.aircraft.world=2;break;
case 4:x.pilot.world=2;break;
case 5:x.seat.occupant=&other;break;
case 6:x.heli.seat=&otherSeat;break;
case 7:x.pilot.group=&otherGroup;otherGroup.faction=2;break;
case 8:x.aircraft.faction=2;break;
case 9:x.flight.humans=true;break;
case 10:x.agent.actions=&replacementActions;break;
case 11:x.agent.actions=&game.global;x.flight.m_Actions=&game.global;break;
case 12:x.pilot.in=nullptr;break;
}
x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0&&x.flight.releases==1&&!x.flight.restore&&registry.entries.empty());
x.Actor();x.Heli();assert(x.flight.releases==1);
}
{Setup x;x.flight.humans=true;ERR_AircraftFlight::DispatchActions(&x.character,&x.passenger,&x.actions,.05f,false);assert(x.flight.releases==1&&x.flight.actionsWritten==0);}
for(int fault=0;fault<5;fault++){
Setup x;ERR_AircraftFlight other;
if(fault==0)x.flight.m_Token=0;
if(fault==1)x.certificate.token=8;
if(fault==2)x.flight.m_State=8;
if(fault==3)x.flight.m_Qualification=nullptr;
if(fault==4)registry.entries[&x.aircraft]=&other;
x.Actor();x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0);
}
{Setup x;x.flight.control=false;x.Actor();x.Heli();assert(x.flight.actionsWritten==0&&x.flight.helicopterWritten==0);}
}
''')
# Both fixture layers still observe their hook, but cannot write or dispatch.
for path in PROBES:
for signature in ('void PrepareActions(', 'void PrepareHelicopter('):
method=body(path,signature)
assert 'm_Flight.Prepare' not in method and 'Dispatch' not in method
assert 'Hooks++' in method
source=BRIDGE.read_text()
assert '#ifdef' not in source and 'new ' not in source
actor=body(BRIDGE,'override void OnPrepareControls(')
heli=body(BRIDGE,'protected override event void OnPrepareControls(')
assert actor.count('ERR_AircraftFlight.DispatchActions(')==1 and actor.count('super.OnPrepareControls(')==1
assert actor.index('DispatchActions(')<actor.index('super.OnPrepareControls(')
assert heli.count('ERR_AircraftFlight.DispatchHelicopter(')==1 and heli.count('super.OnPrepareControls(')==1
assert heli.index('super.OnPrepareControls(')<heli.index('DispatchHelicopter(')
# Execute the actual native hook methods around a recording super boundary.
run(r'''
#include <cassert>
#include <vector>
using IEntity=int;using ActionManager=int;std::vector<int> events;
struct ERR_AircraftFlight {static void DispatchActions(void*,int,int,float,bool){events.push_back(1);}static void DispatchHelicopter(void*){events.push_back(3);}};
struct CharacterBase {void OnPrepareControls(int,int,float,bool){events.push_back(2);}};
struct HelicopterBase {void OnPrepareControls(){events.push_back(2);}};
struct Character:CharacterBase {
''' + actor.replace('override ','').replace('super.','CharacterBase::').replace('ERR_AircraftFlight.','ERR_AircraftFlight::') + r'''
};
struct Helicopter:HelicopterBase {
''' + heli.replace('protected ','').replace('override ','').replace('event ','').replace('super.','HelicopterBase::').replace('ERR_AircraftFlight.','ERR_AircraftFlight::') + r'''
};
int main(){Character c;c.OnPrepareControls(1,2,.05f,false);assert((events==std::vector<int>{1,2}));events.clear();Helicopter h;h.OnPrepareControls();assert((events==std::vector<int>{2,3}));}
''')
print('PASS: actual production bridge lookup, lease/world/mode/seat/capture guards, no allocation, native super ordering and read-only fixture hooks')