Files
EscapistsReintroduced/tests/test_aircraft_control_bridge.py
T

143 lines
10 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/ai/Scripts/Game/ERR/ERR_AircraftFlight.c'
BRIDGE=ROOT/'addons/ai/Scripts/Game/ERR/ERR_AircraftControlBridge.c'
PROBES=[ROOT/'addons/ai/Scripts/Game/ERR'/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')