Files
EscapistsReintroduced/tests/test_vehicle_security.py
T
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

176 lines
12 KiB
Python

"""Execute staged/production security methods; native held UI/replication pending."""
import os,re
from pathlib import Path
from policy_harness import ROOT,body,run
base=Path(os.environ.get('ERR_VEHICLE_SECURITY_ROOT',str(ROOT)))
component=base/'addons/core/Scripts/Game/ERR/00_Core/ERR_VehicleSecurityComponent.c'
actions=base/'addons/core/Scripts/Game/ERR/00_Core/ERR_VehicleSecurityActions.c'
def translate(value):
value=value.replace('out string reason','string& reason').replace('string.Empty','string{}').replace('override ','')
for t in ('ERR_GameMode','Physics','ERR_VehicleSecurityComponent'):
value=re.sub(r'\b'+t+r' (\w+)',t+r'* \1',value)
for t in ('ERR_GameMode','ERR_Actors','Replication','CompartmentAccessComponent','ERR_VehicleSecurityComponent','vector','Vehicle','ERR_Phase'):
value=value.replace(t+'.',t+'::')
for var in ('mode','physics','user','entity','security'):
value=value.replace(var+'.',var+'->')
value=value.replace('GetOwner().','GetOwner()->').replace('mode->Players().','mode->Players()->')
value=value.replace('super.','Base::').replace('null','nullptr')
value=re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])',r'\1f',value)
return value
# Execute the actual preparation hook separately from breach eligibility.
configuration=translate(body(component,'bool Configure('))
run("""#include <cassert>
struct Entity {bool vehicle=true,alive=true;};using IEntity=Entity*;
struct Vehicle {static IEntity Cast(IEntity e){return e&&e->vehicle?e:nullptr;}};
struct ERR_Phase {static const int PREPARING=1,ACTIVE=2;};
struct ERR_GameMode {int phase=ERR_Phase::PREPARING;static ERR_GameMode* current;
static ERR_GameMode* Get(){return current;}int Phase(){return phase;}};
ERR_GameMode* ERR_GameMode::current=nullptr;
struct ERR_Actors {static IEntity observed;static bool Alive(IEntity e){observed=e;return e&&e->alive;}};
IEntity ERR_Actors::observed=nullptr;
struct Replication {static bool server;static int bumps;static bool IsServer(){return server;}static void BumpMe(){bumps++;}};
bool Replication::server=true;int Replication::bumps=0;
struct Security {IEntity owner=nullptr;bool m_Configured=false,m_Breached=false;IEntity GetOwner(){return owner;}
"""+configuration+"""
};
int main(){
// Reject every invalid preparation boundary without replication or state changes.
for(int variant=0;variant<6;variant++){
ERR_GameMode mode;ERR_GameMode::current=&mode;Entity vehicle;Security s;s.owner=&vehicle;
Replication::server=true;Replication::bumps=0;
if(variant==0)Replication::server=false;
if(variant==1)ERR_GameMode::current=nullptr;
if(variant==2)mode.phase=ERR_Phase::ACTIVE;
if(variant==3)s.owner=nullptr;
if(variant==4)vehicle.vehicle=false;
if(variant==5)vehicle.alive=false;
assert(!s.Configure()&&!s.m_Configured&&!s.m_Breached&&Replication::bumps==0);
}
ERR_GameMode mode;ERR_GameMode::current=&mode;Replication::server=true;
Entity vehicle,unrelated;unrelated.alive=false;Security s;s.owner=&vehicle;
assert(s.Configure()&&s.m_Configured&&!s.m_Breached&&Replication::bumps==1);
assert(ERR_Actors::observed==&vehicle); // Liveness is checked on this component's exact vehicle.
assert(s.Configure()&&Replication::bumps==1&&!s.m_Breached);
s.m_Breached=true;assert(s.Configure()&&s.m_Breached&&s.m_Configured&&Replication::bumps==1);
// Invalid repeat calls do not relock or reset configured state.
vehicle.alive=false;assert(!s.Configure()&&s.m_Breached&&s.m_Configured&&Replication::bumps==1);vehicle.alive=true;
mode.phase=ERR_Phase::ACTIVE;assert(!s.Configure()&&s.m_Breached&&s.m_Configured&&Replication::bumps==1);
mode.phase=ERR_Phase::PREPARING;Replication::server=false;
assert(!s.Configure()&&s.m_Breached&&s.m_Configured&&Replication::bumps==1);
}
""")
methods='\n'.join(translate(body(component,s)) for s in ('bool Locked(', 'static bool Motion(', 'static bool Reach(', 'bool CanBreach(', 'bool TryBreach('))
run('''#include <cassert>
#include <cmath>
#include <string>
using string=std::string;
struct vector {float x=0; float Length(){return x;} static float Distance(vector a,vector b){return std::abs(a.x-b.x);} };
struct Physics {vector speed,angular; vector GetVelocity(){return speed;} vector GetAngularVelocity(){return angular;} };
struct Entity {bool player=true,awake=true,alive=true,aboard=false; vector pos; Physics* physics=nullptr; vector GetOrigin(){return pos;} Physics* GetPhysics(){return physics;} vector CoordToParent(vector){return pos;} };
using IEntity=Entity*;
struct Roster {IEntity admitted=nullptr; bool CurrentActor(IEntity e){return e&&e==admitted;} };
struct ERR_GameMode {bool active=true;Roster roster;bool rosterExists=true; static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}bool Active(){return active;} Roster* Players(){return rosterExists?&roster:nullptr;} };
ERR_GameMode* ERR_GameMode::current=nullptr;
struct ERR_Actors {static bool Player(IEntity e){return e&&e->player;}static bool CanFight(IEntity e){return e&&e->alive&&e->awake;}static bool Alive(IEntity e){return e&&e->alive;} };
struct Replication {static bool server;static int bumps; static bool IsServer(){return server;}static void BumpMe(){bumps++;} };bool Replication::server=true;int Replication::bumps=0;
struct CompartmentAccessComponent {static bool GetVehicleIn(IEntity e){return e&&e->aboard;} };
struct Security {bool m_Configured=true,m_Breached=false,empty=true,clear=true;IEntity owner;vector m_LockAnchor; IEntity GetOwner(){return owner;}bool EmptyVehicle(){return empty;}bool ClearReach(IEntity,vector){return clear;}
'''+methods+'''
};
int main(){
Physics physics;Entity vehicle,player,other;vehicle.physics=&physics;
ERR_GameMode mode; ERR_GameMode::current=&mode;mode.roster.admitted=&player;
Security s;s.owner=&vehicle;string reason;
assert(s.CanBreach(&player,reason)&&reason.empty());
for(float bad : {-1.f,0.251f}) assert(!Security::Motion(bad,0));
assert(Security::Motion(0.25,0.1)&&!Security::Motion(0,0.101));
assert(Security::Reach(2)&&!Security::Reach(-1)&&!Security::Reach(2.001));
s.m_Configured=false;assert(!s.TryBreach(&player));s.m_Configured=true;
mode.active=false;assert(!s.TryBreach(&player));mode.active=true;
player.player=false;assert(!s.TryBreach(&player));player.player=true;
player.awake=false;assert(!s.TryBreach(&player));player.awake=true;
player.alive=false;assert(!s.TryBreach(&player));player.alive=true;
player.aboard=true;assert(!s.TryBreach(&player));player.aboard=false;
vehicle.alive=false;assert(!s.TryBreach(&player));vehicle.alive=true;
vehicle.physics=nullptr;assert(!s.TryBreach(&player));vehicle.physics=&physics;
physics.speed.x=0.26;assert(!s.TryBreach(&player));physics.speed.x=0;
physics.angular.x=0.11;assert(!s.TryBreach(&player));physics.angular.x=0;
s.empty=false;assert(!s.TryBreach(&player));s.empty=true;
player.pos.x=2.01;assert(!s.TryBreach(&player));player.pos.x=2;
s.clear=false;assert(!s.CanBreach(&player,reason)&&reason.find("obstructed")!=string::npos);assert(!s.TryBreach(&player));s.clear=true;
// Body replacement, absent roster and direct replica execution cannot unlock.
assert(!s.TryBreach(&other));mode.roster.admitted=nullptr;assert(!s.TryBreach(&player));mode.roster.admitted=&player;
mode.rosterExists=false;assert(!s.TryBreach(&player));mode.rosterExists=true;
Replication::server=false;assert(!s.TryBreach(&player));Replication::server=true;
ERR_GameMode::current=nullptr;assert(!s.TryBreach(&player));ERR_GameMode::current=&mode;
assert(Replication::bumps==0&&s.Locked());
assert(s.TryBreach(&player)&&!s.Locked()&&Replication::bumps==1);
assert(!s.TryBreach(&player)&&!s.TryBreach(&other)&&Replication::bumps==1);
}
''')
class Section:
def __init__(self,name,next_name=None):
source=actions.read_text();self.source=source[source.index('class '+name):]
if next_name:self.source=self.source[:self.source.index('class '+next_name)]
def read_text(self):return self.source
for name,next_name in [('ERR_SecureGetInAction','ERR_SecureOpenVehicleDoorAction'),('ERR_SecureOpenVehicleDoorAction',None)]:
section=Section(name,next_name)
methods='\n'.join(translate(body(section,s)) for s in ('override bool CanBePerformedScript(', 'override void PerformAction('))
run('''#include <cassert>
#include <string>
using IEntity=int;
using string=std::string;
struct ERR_VehicleSecurityComponent {bool locked=true;static ERR_VehicleSecurityComponent* security;static ERR_VehicleSecurityComponent* Find(IEntity){return security;}bool Locked(){return locked;}};
ERR_VehicleSecurityComponent* ERR_VehicleSecurityComponent::security=nullptr;
struct Base {bool allowed=true;int calls=0;IEntity GetOwner(){return 42;}void SetCannotPerformReason(std::string){}bool CanBePerformedScript(IEntity){return allowed;}void PerformAction(IEntity,IEntity){calls++;}};
struct Action:Base {
'''+methods+'''
};
int main(){Action a;ERR_VehicleSecurityComponent s;ERR_VehicleSecurityComponent::security=&s;
a.PerformAction(42,7);assert(a.calls==0);s.locked=false;a.allowed=false;a.PerformAction(42,7);assert(a.calls==0);
a.allowed=true;a.PerformAction(43,7);assert(a.calls==0);a.PerformAction(42,7);assert(a.calls==1);
s.locked=true;a.PerformAction(42,7);assert(a.calls==1);
ERR_VehicleSecurityComponent::security=nullptr;a.PerformAction(42,7);assert(a.calls==2); // unrelated owner delegates native policy
}
''')
# The held action invokes only the owning security component on the server; its
# TryBreach path above performs fresh checks rather than trusting availability.
execution=translate(body(Section('ERR_ForceVehicleLockAction','ERR_SecureGetInAction'),'override void PerformAction('))
run("""#include <cassert>
using IEntity=int;
struct Replication {static bool server;static bool IsServer(){return server;}};bool Replication::server=false;
struct ERR_VehicleSecurityComponent {int calls=0,last=0;static ERR_VehicleSecurityComponent* security;static ERR_VehicleSecurityComponent* Find(IEntity){return security;}void TryBreach(IEntity user){calls++;last=user;}};
ERR_VehicleSecurityComponent* ERR_VehicleSecurityComponent::security=nullptr;
struct Action {IEntity GetOwner(){return 42;}
"""+execution+"""
};
int main(){Action a;ERR_VehicleSecurityComponent s;ERR_VehicleSecurityComponent::security=&s;
a.PerformAction(42,7);assert(s.calls==0);Replication::server=true;
a.PerformAction(43,7);assert(s.calls==0);a.PerformAction(42,7);assert(s.calls==1&&s.last==7);
ERR_VehicleSecurityComponent::security=nullptr;a.PerformAction(42,7);assert(s.calls==1);
}
""")
# Exact resource overrides cover both front seats, three separately attached rear
# seats and all four door actions without replacing physical vehicle resources.
prefabs=base/'addons/core/Prefabs/ERR/Vehicles'
uaz=(prefabs/'SupplyUAZ.et').read_text();rear=(prefabs/'SupplyUAZRearSeats.et').read_text()
assert '"{259EE7B78C51B624}Prefabs/Vehicles/Wheeled/UAZ469/UAZ469.et"' in uaz
assert '"{780AAF0A51AEE61A}Prefabs/Vehicles/Wheeled/UAZ469/VehParts/Seats/UAZ469_seats_rear.et"' in rear
assert uaz.count('OpenDoorAction ERR_SecureOpenVehicleDoorAction')==4
assert uaz.count('CompartmentAction ERR_SecureGetInAction')==2
assert rear.count('CompartmentAction ERR_SecureGetInAction')==3
for slot in ['Passenger_l02','Passenger_r02','Passenger_m02']:assert 'CargoCompartmentSlot '+slot in rear
assert 'SupplyUAZRearSeats.et' in uaz and 'Duration 4' in uaz and 'VisibilityRange 2' in uaz
assert uaz.count('-0.95 1.05 0.3')==2 # reach anchor matches visible held-action context
for signature,result in [('override bool HasLocalEffectOnlyScript(',False),('override bool CheckOnServerFirstScript(',True),('override bool CanBroadcastScript(',False)]:
assert 'return '+str(result).lower()+';' in body(Section('ERR_ForceVehicleLockAction','ERR_SecureGetInAction'),signature)
assert '[RplProp()] protected bool m_Breached;' in component.read_text()
assert 'modded class' not in actions.read_text()+component.read_text()
print('PASS: actual preparation Configure guards/idempotence/breach preservation, security eligibility/final current-body server mutation and all scoped action guards; all five seat/four door resource overrides. Native hold, prefab merge, replication and boarding remain unverified.')