100 lines
8.4 KiB
Python
100 lines
8.4 KiB
Python
"""Native fixture cleanup and real SiteService admission wrapper, without engine claims."""
|
|
import os,re
|
|
from pathlib import Path
|
|
from policy_harness import ROOT,body,run
|
|
base=Path(os.environ.get('ERR_VEHICLE_NATIVE_ROOT',str(ROOT)))
|
|
source=base/'addons/core/Scripts/Game/ERR/ERR_NativeVehicleSecurityProbe.c'
|
|
stop=body(source,'void Stop(').replace('null','nullptr')
|
|
stop=stop.replace('ChimeraCharacter character = ChimeraCharacter.Cast(m_Player);','ChimeraCharacter* character = m_Player;')
|
|
for name in ('m_Damage','m_Mode','character'):stop=stop.replace(name+'.',name+'->')
|
|
stop=stop.replace('m_Mode->Players().','m_Mode->Players()->').replace('ERR_Actors.','ERR_Actors::').replace('CompartmentAccessComponent.','CompartmentAccessComponent::')
|
|
run('''#include <cassert>
|
|
struct Damage {bool enabled=true;int changes=0;void EnableDamageHandling(bool b){enabled=b;changes++;}};
|
|
struct ChimeraCharacter {bool awake=true,aboard=false;int teleports=0;void Teleport(int){teleports++;}};
|
|
struct Roster {ChimeraCharacter* current=nullptr;bool CurrentActor(ChimeraCharacter* e){return current==e;}};
|
|
struct Mode {Roster roster;bool hasRoster=true;Roster* Players(){return hasRoster?&roster:nullptr;}};
|
|
struct ERR_Actors{static bool CanFight(ChimeraCharacter* c){return c&&c->awake;}};
|
|
struct CompartmentAccessComponent {static bool GetVehicleIn(ChimeraCharacter* c){return c&&c->aboard;}};
|
|
struct Probe {bool m_Stopped=false,m_Moved=true,m_DamageChanged=true,m_PreviousDamageEnabled=true;Damage* m_Damage;Mode* m_Mode;ChimeraCharacter* m_Player;int m_Original=0;
|
|
'''+stop+'''
|
|
};
|
|
int main(){
|
|
Damage damage;Mode mode;ChimeraCharacter oldBody,replacement;mode.roster.current=&oldBody;
|
|
Probe p;p.m_Damage=&damage;p.m_Mode=&mode;p.m_Player=&oldBody;
|
|
damage.enabled=false;p.Stop();assert(damage.enabled&&damage.changes==1&&oldBody.teleports==1);
|
|
p.Stop();assert(damage.changes==1&&oldBody.teleports==1); // idempotent
|
|
p.m_Stopped=false;p.m_Moved=true;p.m_Damage=&damage;p.m_DamageChanged=true;p.m_PreviousDamageEnabled=false;mode.roster.current=&replacement;
|
|
p.Stop();assert(!damage.enabled&&damage.changes==2&&replacement.teleports==0&&oldBody.teleports==1); // restore exact old damage state, no stale-body teleport
|
|
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=false; // rejected before protection applied
|
|
p.Stop();assert(damage.changes==2);
|
|
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;p.m_PreviousDamageEnabled=true;mode.roster.current=&oldBody;oldBody.awake=false;
|
|
p.Stop();assert(damage.enabled&&damage.changes==3&&oldBody.teleports==1);
|
|
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;oldBody.awake=true;oldBody.aboard=true;
|
|
p.Stop();assert(damage.changes==4&&oldBody.teleports==1);
|
|
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;mode.hasRoster=false;
|
|
p.Stop();assert(damage.changes==5&&oldBody.teleports==1);
|
|
}
|
|
''')
|
|
text=source.read_text();tick=body(source,'int Tick(');inspect=body(source,'protected bool Inspect(')
|
|
assert tick.index('CurrentActor(m_Player)') < tick.index('m_Damage.EnableDamageHandling(false)') < tick.index('Teleport(transform)')
|
|
assert 'ERR_Actors.Spawn(' not in text and 'GetInVehicle(' not in text and 'SetTotalFuel' not in text
|
|
assert 'seats.Count() == 5' in inspect and 'slot.GetGetInAction()' in inspect
|
|
assert 'front == 2 && rear == 3' in inspect and 'doors.Count() == 4' in inspect
|
|
assert 'ERR_VehicleSecurityComponent.Find(action.GetOwner()) == m_Security' in inspect
|
|
assert '!action.CanBePerformedScript(m_Player)' in inspect and 'action.PerformAction(action.GetOwner(), m_Player);' in inspect
|
|
assert '!door.CanBePerformedScript(m_Player)' in inspect and 'door.PerformAction(door.GetOwner(), m_Player);' in inspect
|
|
assert 'm_Security.CanBreach(m_Player, reason)' in tick and '!m_Security.TryBreach(m_Player)' in tick
|
|
assert 'DriverAvailable()' in tick
|
|
assert 'm_DriverAction.CanBePerformedScript(m_Player)' in body(source,'protected bool DriverAvailable(')
|
|
transport=(base/'addons/core/Scripts/Game/ERR/ERR_NativeTransportProbe.c').read_text()
|
|
smoke=(base/'addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c').read_text()
|
|
assert 'new ERR_NativeTransportProbe(this, m_ERRBasePlayer)' in smoke and 'if (m_ERRTransportProbe) m_ERRTransportProbe.Stop();' in smoke
|
|
assert transport.index('m_SecurityProbe.Tick(now)') < transport.index('IEntity driver = Character(0)')
|
|
assert 'void Stop() { if (m_SecurityProbe) m_SecurityProbe.Stop(); }' in transport
|
|
print('PASS: actual native-prelude cleanup restores only original changed damage, refuses stale/incapacitated/aboard teleport; bounded five-seat/four-door fixture wiring checked. Native execution remains pending.')
|
|
|
|
# Existing transport tests did not extract CreateTransport. Test its real new
|
|
# Configure hook with native construction/FindComponent calls represented by doubles.
|
|
site=ROOT/'addons/core/Scripts/Game/ERR/ERR_SiteService.c'
|
|
create=body(site,'protected bool CreateTransport(').replace('protected ','').replace('null','nullptr')
|
|
for name in ('ERR_SiteRuntime','ERR_SiteTransport','CarControllerComponent','ERR_VehicleSecurityComponent'):
|
|
create=re.sub(r'\b'+name+r' (\w+)',name+r'* \1',create)
|
|
for name in ('site','spec','controller','security'):create=create.replace(name+'.',name+'->')
|
|
create=create.replace('site->Definition.','site->Definition->')
|
|
create=create.replace('ERR_Actors.','ERR_Actors::').replace('ERR_VehicleSecurityComponent.','ERR_VehicleSecurityComponent::')
|
|
create=create.replace('Vehicle.Cast(entity)','CastVehicle(entity)').replace('CarControllerComponent.Cast(site->Transport.FindComponent(CarControllerComponent))','FindController(site->Transport)')
|
|
run('''#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
using string=std::string;using vector=int;
|
|
int Vector(int x,int,int){return x;}
|
|
struct CarControllerComponent {bool parked=false;void SetPersistentHandBrake(bool b){parked=b;}};
|
|
struct Entity;
|
|
struct ERR_VehicleSecurityComponent {CarControllerComponent* controller;bool result=true;int calls=0;bool Configure(){assert(controller&&controller->parked);calls++;return result;}static ERR_VehicleSecurityComponent* Find(Entity* e);};
|
|
struct Entity {bool vehicle=true;CarControllerComponent* controller=nullptr;ERR_VehicleSecurityComponent* security=nullptr;};using IEntity=Entity*;
|
|
ERR_VehicleSecurityComponent* ERR_VehicleSecurityComponent::Find(Entity* e){return e->security;}
|
|
IEntity CastVehicle(IEntity e){return e->vehicle?e:nullptr;}
|
|
CarControllerComponent* FindController(IEntity e){return e->controller;}
|
|
struct ERR_SiteTransport {int Offset=0,ClearanceRadius=4,MaximumTerrainRange=1,Prefab=123,Heading=180;};
|
|
struct Definition {ERR_SiteTransport* Transport=nullptr;string Id="supply";};
|
|
struct ERR_SiteRuntime {struct Definition* Definition;IEntity Transport=nullptr;};
|
|
struct ERR_Actors {static IEntity spawned;static int calls;static int Ground(int p){return p;}static IEntity Spawn(int,int,int){calls++;return spawned;}};IEntity ERR_Actors::spawned=nullptr;int ERR_Actors::calls=0;
|
|
struct Array:std::vector<IEntity>{void Insert(IEntity e){push_back(e);}};
|
|
struct Service{bool clear=true;string failure;Array m_Entities;bool Fail(string s){failure=s;return false;}bool TransportClearance(int,int,int){return clear;}
|
|
'''+create+'''
|
|
};
|
|
int main(){Service service;Definition def;ERR_SiteRuntime site{&def};ERR_SiteTransport spec;
|
|
assert(service.CreateTransport(&site,0)&&ERR_Actors::calls==0);def.Transport=&spec;
|
|
service.clear=false;assert(!service.CreateTransport(&site,0)&&ERR_Actors::calls==0);service.clear=true;
|
|
assert(!service.CreateTransport(&site,0)&&service.m_Entities.empty());
|
|
Entity vehicle;CarControllerComponent controller;ERR_VehicleSecurityComponent security;ERR_Actors::spawned=&vehicle;
|
|
vehicle.vehicle=false;assert(!service.CreateTransport(&site,0)&&service.m_Entities.back()==&vehicle);vehicle.vehicle=true;
|
|
assert(!service.CreateTransport(&site,0));vehicle.controller=&controller;
|
|
assert(service.CreateTransport(&site,0)&&controller.parked); // ordinary vehicle has no security hook
|
|
vehicle.security=&security;security.controller=&controller;security.result=false;controller.parked=false;
|
|
assert(!service.CreateTransport(&site,0)&&security.calls==1&&service.failure.find("security configuration")!=string::npos);
|
|
security.result=true;controller.parked=false;assert(service.CreateTransport(&site,0)&&security.calls==2&&site.Transport==&vehicle);
|
|
}
|
|
''')
|
|
print('PASS: actual CreateTransport requires successful scoped Configure after handbrake, preserves ordinary vehicles, tracks spawned entity before failures and never treats failed lock registration as ready.')
|