Observe native prison lock geometry and requalify Core loops

This commit is contained in:
2026-09-19 18:58:12 +02:00
parent e10b8799e9
commit b7fba094d7
39 changed files with 9884 additions and 12 deletions
+125
View File
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Native gate record contracts and negative cases, without input or engine use."""
import copy
from pathlib import Path
import sys
import unittest
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools'))
from ordinary_gate_evidence import parse_gate_record,parse_gate_records,join_gate_snapshots,validate_gate_geometry
NONCE='gateTrial123'
def fixture():
lines=[f'SCRIPT : ERR INPUT OBSERVE: run={NONCE} sample=0 real=1000 world=10000 QUEUED']
for i in range(1,31):
t=1000+i*110;w=10000+i*110
lines.append(f'SCRIPT : ERR INPUT OBSERVE: run={NONCE} sample={i} real={t} world={w} SAMPLE origin=<6088.5,211.389,7158.5> forward=0 input=<0,0,0> velocity=<0,0,0> inventory=0')
if i==1:
lines.append(f'SCRIPT : ERR LOOK OBSERVE: run={NONCE} sample={i} real={t} BASIS native-player-camera-world-transform')
lines.append(f'SCRIPT : ERR LOOK OBSERVE: run={NONCE} sample={i} real={t} SAMPLE direction=<0,0,1> camera=<6088.58,213.019,7158.56> body=<6088.5,211.389,7158.5> bodyForward=<0,0,1> turn=<0,0,0>')
if i==1:
lines.append(f'SCRIPT : ERR GATE OBSERVE: run={NONCE} sample={i} real={t} BASIS registered-prison-lock-native-interaction')
lines.append(f'SCRIPT : ERR GATE OBSERVE: run={NONCE} sample={i} real={t} SAMPLE phase=1 selected=0 context=0 cachedPerformable=0 progressBound=0 performing=0 progress=0 duration=4 pressed=0')
lines.append(f'SCRIPT : ERR GATE OBSERVE: run={NONCE} sample={i} real={t} GEOMETRY body=<6088.5,211.389,7158.5> context=<6092.77,213.226,7154> radius=2 doorState=0 doorControl=0 prison=<6090,211.444,7160> gate=<6090,211.344,7154> gateForward=<0,0,1>')
if i==19:
lines.append(f'SCRIPT : ERR INPUT OBSERVE: run={NONCE} sample={i} real={t+1} world={w} READY')
lines.append(f'SCRIPT : ERR GATE OBSERVE: run={NONCE} sample=31 real=4410 CANCELLED native-player-or-prison-context-lost')
lines.append(f'SCRIPT : ERR LOOK OBSERVE: run={NONCE} sample=31 real=4410 CANCELLED menu-opened')
return '\n'.join(lines)+'\n'
def matching(raw,text):
return next(line for line in raw.splitlines() if text in line)
class GateEvidenceTests(unittest.TestCase):
def reject(self,raw,start=20,end=25):
result=validate_gate_geometry(raw,NONCE,start,end)
self.assertFalse(result['verified'],result);self.assertTrue(result['errors'])
def test_native_shape_parse_join_and_geometry_only(self):
raw=fixture();parsed=parse_gate_records(raw,NONCE);joined=join_gate_snapshots(raw,NONCE)
self.assertEqual(len(parsed['snapshots']),30)
self.assertEqual(len(parsed['records']),62)
self.assertEqual(joined['snapshots'][20]['input']['real_ms'],3200)
self.assertEqual(joined['snapshots'][20]['look']['body'],joined['snapshots'][20]['geometry']['body'])
result=validate_gate_geometry(raw,NONCE,20,25)
self.assertTrue(result['verified'],result)
self.assertEqual(result['snapshot_count'],6)
self.assertFalse(result['approach_verified']);self.assertFalse(result['held_action_verified'])
self.assertEqual(result['radius'],2)
self.assertEqual(result['prison'],[6090,211.444,7160])
self.assertTrue(validate_gate_geometry(raw,NONCE)['verified'])
self.assertIsNone(parse_gate_record('irrelevant native log line',NONCE))
def test_no_success_inferred_from_native_selected_or_full_progress(self):
raw=fixture().replace('selected=0 context=0 cachedPerformable=0 progressBound=0 performing=0 progress=0 duration=4 pressed=0',
'selected=1 context=1 cachedPerformable=1 progressBound=1 performing=1 progress=4 duration=4 pressed=1')
result=validate_gate_geometry(raw,NONCE,20,25)
self.assertTrue(result['verified'],result)
self.assertFalse(result['held_action_verified']);self.assertFalse(result['approach_verified'])
def test_malformed_foreign_unknown_and_numeric_records_rejected(self):
raw=fixture()
record=matching(raw,'ERR GATE OBSERVE: run='+NONCE+' sample=20 real=3200 SAMPLE')
for old,new in (('run='+NONCE,'run=foreign'),('phase=1','phase=3'),('selected=0','selected=2'),
('duration=4','duration=5'),('progress=0','progress=nan'),('progress=0','progress=1e999'),
('pressed=0','pressed=-1'),('progressBound=0','progressBound=1'),('performing=0','performing=1'),
('cachedPerformable=0','cachedPerformable=1'),('SAMPLE phase','UNKNOWN phase'),
('OBSERVE: ','OBSERVE:')):
with self.subTest(new=new):self.reject(raw.replace(record,record.replace(old,new)))
geometry=matching(raw,'sample=20 real=3200 GEOMETRY')
for old,new in (('radius=2','radius=0'),('radius=2','radius=11'),('doorState=0','doorState=2'),
('gateForward=<0,0,1>','gateForward=<0,0,0>'),('gateForward=<0,0,1>','gateForward=<0,0,2>'),
('gateForward=<0,0,1>','gateForward=<0,1,0>'),('body=<6088.5,211.389,7158.5>','body=<nan,0,1>')):
self.reject(raw.replace(geometry,geometry.replace(old,new)))
def test_pair_order_duplicates_gaps_and_missing_ack_refused(self):
raw=fixture();sample=matching(raw,'sample=20 real=3200 SAMPLE phase');geometry=matching(raw,'sample=20 real=3200 GEOMETRY')
for changed in (raw.replace(sample+'\n',''),raw.replace(geometry+'\n',''),raw.replace(sample,sample+'\n'+sample),
raw.replace(sample+'\n'+geometry,geometry+'\n'+sample),
raw.replace(geometry,geometry.replace('real=3200','real=3201')),
raw.replace(geometry,geometry.replace('sample=20','sample=19')),
'\n'.join(line for line in raw.splitlines() if 'BASIS registered-prison' not in line)):
self.reject(changed)
def test_native_three_centimetre_cross_api_difference_is_bounded(self):
raw=fixture();pose=matching(raw,'ERR LOOK OBSERVE: run='+NONCE+' sample=22 real=3420 SAMPLE')
accepted=raw.replace(pose,pose.replace('body=<6088.5,211.389,7158.5>','body=<6088.5,211.389,7158.47>'))
self.assertTrue(validate_gate_geometry(accepted,NONCE,20,25)['verified'])
self.reject(raw.replace(pose,pose.replace('body=<6088.5,211.389,7158.5>','body=<6088.5,211.389,7158.44>')))
geometry=matching(raw,'sample=22 real=3420 GEOMETRY')
self.reject(raw.replace(geometry,geometry.replace('body=<6088.5,211.389,7158.5>','body=<6088.5,211.389,7158.52>')))
def test_geometry_drift_and_clock_mismatch_refused(self):
raw=fixture();geometry=matching(raw,'sample=22 real=3420 GEOMETRY')
for old,new in (('context=<6092.77,213.226,7154>','context=<6092.80,213.226,7154>'),
('prison=<6090,211.444,7160>','prison=<6090.02,211.444,7160>'),
('gate=<6090,211.344,7154>','gate=<6090,211.344,7154.02>'),('radius=2','radius=2.01'),
('doorState=0','doorState=0.01'),('doorControl=0','doorControl=0.01'),
('gateForward=<0,0,1>','gateForward=<0.01,0,0.99995>')):
self.reject(raw.replace(geometry,geometry.replace(old,new)))
self.reject(raw.replace('sample=22 real=3420 SAMPLE phase','sample=22 real=3421 SAMPLE phase').replace('sample=22 real=3420 GEOMETRY','sample=22 real=3421 GEOMETRY'))
def test_phase_menu_and_cancellation_span(self):
raw=fixture();sample=matching(raw,'sample=22 real=3420 SAMPLE phase')
phase2=raw.replace(sample,sample.replace('phase=1','phase=2'))
self.reject(phase2)
self.assertTrue(validate_gate_geometry(phase2,NONCE,23,25)['verified'])
inp=matching(raw,'sample=22 real=3420 world=12420 SAMPLE origin')
self.reject(raw.replace(inp,inp.replace('inventory=0','inventory=1')))
self.reject(raw.replace('sample=31 real=4410 CANCELLED native-player-or-prison-context-lost','sample=24 real=3640 CANCELLED native-player-or-prison-context-lost'))
self.reject(raw+'SCRIPT : ERR GATE OBSERVE: run='+NONCE+' sample=31 real=4410 CANCELLED explicit-scope-lost\n')
def test_invalid_span_types_and_unknown_nonce(self):
raw=fixture()
for start,end in ((True,25),(20,False),(20,20),(25,20),(18,25),(20,451),(20,31),('20',25)):
result=validate_gate_geometry(raw,NONCE,start,end);self.assertFalse(result['verified'],result)
for nonce in (None,'','../unsafe','a'*33):
self.assertFalse(validate_gate_geometry(raw,nonce,20,25)['verified'])
def test_readonly_body_approach_not_credited_as_success(self):
raw=fixture()
for index in range(20,31):
real=1000+index*110;body=f'<6088.5,211.389,{7158.5-(index-19)*.1}>'
lines=[]
for line in raw.splitlines():
if f'sample={index} real={real} ' in line:
line=line.replace('<6088.5,211.389,7158.5>',body)
lines.append(line)
raw='\n'.join(lines)+'\n'
result=validate_gate_geometry(raw,NONCE,20,25)
self.assertTrue(result['verified'],result)
self.assertLess(result['final_context_distance_metres'],result['initial_context_distance_metres'])
self.assertFalse(result['approach_verified']);self.assertFalse(result['held_action_verified'])
if __name__=='__main__':unittest.main()
+99
View File
@@ -0,0 +1,99 @@
"""Execute actual gate observer bodies; fake only native boundaries, no engine claim."""
import re
from policy_harness import ROOT, body, run
P=ROOT/'addons/core/Scripts/Game/ERR/ERR_OrdinaryGateObserver.c'
def cpp(s):
s=s.replace('protected ', '').replace('out float ', 'float& ').replace('out bool ', 'bool& ')
types=('ERR_GameMode','BaseWorld','PlayerController','IEntity','ERR_PlayerLife','PlayerManager','BaseUserAction','ERR_UnlockPrisonAction','UserActionContext')
for typ in types:s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
s=s.replace('array<BaseUserAction>', 'Array<BaseUserAction*>').replace('array<bool>', 'Array<bool>')
s=s.replace('foreach (BaseUserAction* action : actions)', 'for (BaseUserAction* action : actions)')
s=s.replace('int matches;', 'int matches=0;')
for typ in ('BaseActionsManagerComponent','BaseDoorComponent','SCR_InteractionHandlerComponent'):
s=s.replace('FindComponent('+typ+')', 'FindComponent('+typ+'Key)')
s=s.replace(typ+'.Cast(', 'static_cast<'+typ+'*>(')
s=s.replace('ERR_UnlockPrisonAction.Cast(', 'dynamic_cast<ERR_UnlockPrisonAction*>(')
for name in ('mode','world','controller','actor','players','action','candidate','expected','context','m_Mode','m_World','m_Controller','m_Actor','m_Life','m_Gate','m_Manager','m_Lock','m_Context','m_Door','m_Handler','s_Request'):
s=s.replace(name+'.',name+'->')
for scope in ('System','Replication','RplSession','RplMode','ERR_Phase','ERR_GameMode','ERR_Actors','ERR_OrdinaryInputObserverRules','CompartmentAccessComponent'):
s=s.replace(scope+'.',scope+'::')
for getter in ('GetGame','Players','PrisonEscape','GetInputManager','GetMenuManager'):
s=s.replace(getter+'().',getter+'()->')
return s.replace('null','nullptr').replace('string.Format(', 'Format(')
accessor=cpp(body(P,'bool ERR_GateReadProgress('))
methods='\n'.join(cpp(body(P,s)) for s in ('protected static bool Enabled(', 'static void Sample(', 'protected bool BindGate(', 'protected bool Context(', 'protected void Cancel(', 'protected bool CachedPermission(', 'protected static bool FiniteVector(', 'protected void Observe('))
run(r'''
#include <cassert>
#include <cmath>
#include <limits>
#include <map>
#include <string>
#include <vector>
using string=std::string;using UUID=string;
template<class T> struct Array:std::vector<T> {using std::vector<T>::vector;int Count(){return this->size();}};
struct vector {float v[3]={0,0,0};float& operator[](int i){return v[i];}float Length(){return std::sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);}vector operator/(float n){return {{v[0]/n,v[1]/n,v[2]/n}};}};
vector Vector(float x,float y,float z){return {{x,y,z}};}
enum {BaseActionsManagerComponentKey,BaseDoorComponentKey,SCR_InteractionHandlerComponentKey};
struct BaseWorld{};
struct IEntity {BaseWorld* world=nullptr;bool living=true,vehicle=false;void* components[3]={};vector origin=Vector(6090,210,7158);BaseWorld* GetWorld(){return world;}void* FindComponent(int key){return components[key];}vector forward=Vector(0,0,1.5);vector GetOrigin(){return origin;}void GetTransform(vector out[4]){out[2]=forward;out[3]=origin;}};
struct BaseUserAction {virtual ~BaseUserAction()=default;IEntity* owner=nullptr;int id=4;float duration=4;IEntity* GetOwner(){return owner;}int GetActionID(){return id;}float GetActionDuration(){return duration;}};
struct ERR_UnlockPrisonAction:BaseUserAction{};
struct UserActionContext {Array<BaseUserAction*> actions;vector origin=Vector(6092,212,7154);float radius=2;void GetActionsList(Array<BaseUserAction*>& out){out=actions;}vector GetOrigin(){return origin;}float GetRadius(){return radius;}};
struct BaseActionsManagerComponent {UserActionContext* context=nullptr;BaseUserAction* action=nullptr;UserActionContext* GetContext(string){return context;}BaseUserAction* FindAction(int id){return action&&action->id==id?action:nullptr;}};
struct BaseDoorComponent {float state=0,control=0;float GetNormalizedDoorState(){return state;}float GetControlValue(){return control;}};
struct SCR_InteractionHandlerComponent {BaseUserAction* selected=nullptr;UserActionContext* current=nullptr;BaseUserAction* m_pLastUserAction=nullptr;UserActionContext* m_pLastContext=nullptr;float m_fCurrentProgress=0;bool m_bIsPerforming=false;IEntity* actor=nullptr;Array<BaseUserAction*> filtered;Array<bool> permitted;
BaseUserAction* GetSelectedAction(){return selected;}UserActionContext* GetCurrentContext(){return current;}IEntity* GetControlledEntity(){return actor;}void GetFilteredActions(Array<BaseUserAction*>& actions,Array<bool>& allowed){actions=filtered;allowed=permitted;}
'''+accessor+r'''
};
struct PlayerController:IEntity {int id=1;IEntity* actor=nullptr;int GetPlayerId(){return id;}IEntity* GetControlledEntity(){return actor;}};
struct PlayerManager {PlayerController* controller=nullptr;IEntity* actor=nullptr;PlayerController* GetPlayerController(int){return controller;}IEntity* GetPlayerControlledEntity(int){return actor;}};
struct ERR_PlayerLife {UUID AccountId="original-account";};
struct Roster {ERR_PlayerLife* life=nullptr;IEntity* actor=nullptr;ERR_PlayerLife* CurrentActor(IEntity* value){return value==actor?life:nullptr;}};
struct Prison {IEntity* gate=nullptr;bool registered=true,lost=false;bool Registered(){return registered;}bool GateLost(){return lost;}IEntity* Gate(){return gate;}};
namespace ERR_Phase {enum {PRISON,RADIO_SEARCH,EXTRACTION};}
struct ERR_GameMode {static ERR_GameMode* current;bool active=true;int phase=ERR_Phase::PRISON;BaseWorld* world=nullptr;Roster* roster=nullptr;Prison* prison=nullptr;static ERR_GameMode* Get(){return current;}bool Active(){return active;}int Phase(){return phase;}BaseWorld* GetWorld(){return world;}Roster* Players(){return roster;}Prison* PrisonEscape(){return prison;}vector prisonPosition=Vector(6090,210,7160);vector PrisonPosition(){return prisonPosition;}};ERR_GameMode* ERR_GameMode::current=nullptr;
struct InputManager {float pressed=0;ERR_GameMode* changeDuringRead=nullptr;float GetActionValue(string name){assert(name=="PerformAction");if(changeDuringRead)changeDuringRead->active=false;return pressed;}};
struct MenuManager {bool open=false;void* GetTopMenu(){return open?this:nullptr;}};
struct Game {BaseWorld* world=nullptr;PlayerController* controller=nullptr;PlayerManager* players=nullptr;InputManager* input=nullptr;MenuManager* menus=nullptr;BaseWorld* GetWorld(){return world;}PlayerController* GetPlayerController(){return controller;}PlayerManager* GetPlayerManager(){return players;}InputManager* GetInputManager(){return input;}MenuManager* GetMenuManager(){return menus;}} game;Game* activeGame=&game;Game* GetGame(){return activeGame;}
namespace System {bool console=false;std::map<string,string> cli;bool IsConsoleApp(){return console;}bool GetCLIParam(string key,string& value){if(!cli.count(key))return false;value=cli[key];return true;}}
namespace Replication {bool server=true;bool IsServer(){return server;}}
namespace RplMode {enum {None,Listen,Dedicated};}namespace RplSession {int mode=RplMode::None;int Mode(){return mode;}}
namespace ERR_OrdinaryInputObserverRules {bool Nonce(string n){return !n.empty()&&n.size()<=32&&n.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")==string::npos;}}
namespace ERR_Actors {bool CanFight(IEntity* actor){return actor&&actor->living;}}
namespace CompartmentAccessComponent {IEntity* GetVehicleIn(IEntity* actor){return actor->vehicle?actor:nullptr;}}
int headers=0,samples=0,geometries=0,cancels=0;string reason;bool lastSelected=false,lastContext=false,lastPermitted=false,lastBound=false,lastPerforming=false;float lastProgress=0;int lastPhase=0;
string Format(string,int phase,bool selected,bool context,bool permitted,bool bound,bool performing,float progress,float duration,float pressed){assert(duration==4);lastPhase=phase;lastSelected=selected;lastContext=context;lastPermitted=permitted;lastBound=bound;lastPerforming=performing;lastProgress=progress;return "SAMPLE";}
vector lastPrison,lastGate,lastForward;
string Format(string,vector,vector,float,float,float,vector prison,vector gate,vector forward){lastPrison=prison;lastGate=gate;lastForward=forward;return "GEOMETRY";}
void PrintFormat(string,string,int,int){headers++;}
void PrintFormat(string format,string,int,int,string value){if(format.find("CANCELLED")!=string::npos){cancels++;reason=value;}else if(value=="SAMPLE")samples++;else if(value=="GEOMETRY")geometries++;else assert(false);}
class ERR_OrdinaryGateObserver {public:
static ERR_OrdinaryGateObserver* s_Request;static bool s_Attempted;
ERR_GameMode* m_Mode=nullptr;BaseWorld* m_World=nullptr;PlayerController* m_Controller=nullptr;IEntity* m_Actor=nullptr;ERR_PlayerLife* m_Life=nullptr;UUID m_Account;IEntity* m_Gate=nullptr;BaseActionsManagerComponent* m_Manager=nullptr;ERR_UnlockPrisonAction* m_Lock=nullptr;UserActionContext* m_Context=nullptr;BaseDoorComponent* m_Door=nullptr;SCR_InteractionHandlerComponent* m_Handler=nullptr;
string m_Nonce;int m_Start=0,m_Previous=0,m_Index=0;bool m_Stopped=false;
'''+methods+r'''
};
ERR_OrdinaryGateObserver* ERR_OrdinaryGateObserver::s_Request=nullptr;bool ERR_OrdinaryGateObserver::s_Attempted=false;
struct Fixture {BaseWorld world,otherWorld;ERR_GameMode mode;IEntity actor,otherActor,gate,otherGate;PlayerController controller,otherController;PlayerManager players;Roster roster;ERR_PlayerLife life,otherLife;Prison prison;InputManager input;MenuManager menus;BaseActionsManagerComponent manager,otherManager;BaseDoorComponent door,otherDoor;UserActionContext context,otherContext;SCR_InteractionHandlerComponent handler,otherHandler;ERR_UnlockPrisonAction lock,otherLock;BaseUserAction foreignAction;
Fixture(){delete ERR_OrdinaryGateObserver::s_Request;ERR_OrdinaryGateObserver::s_Request=nullptr;ERR_OrdinaryGateObserver::s_Attempted=false;headers=samples=geometries=cancels=0;reason="";System::console=false;System::cli={{"errOrdinaryScenario","core"},{"errOrdinaryInputObserve","Run42"},{"errOrdinaryGateObserve","Run42"}};Replication::server=true;RplSession::mode=RplMode::None;activeGame=&game;ERR_GameMode::current=&mode;mode.world=&world;mode.roster=&roster;mode.prison=&prison;actor.world=gate.world=&world;controller.actor=&actor;controller.components[SCR_InteractionHandlerComponentKey]=&handler;players.controller=&controller;players.actor=&actor;roster.actor=&actor;roster.life=&life;prison.gate=&gate;gate.components[BaseActionsManagerComponentKey]=&manager;gate.components[BaseDoorComponentKey]=&door;manager.context=&context;manager.action=&lock;lock.owner=&gate;context.actions={&lock};handler.actor=&actor;handler.selected=handler.m_pLastUserAction=&lock;handler.current=handler.m_pLastContext=&context;handler.filtered={&lock};handler.permitted={true};game={&world,&controller,&players,&input,&menus};}
void sample(int index=1,int now=1100){ERR_OrdinaryGateObserver::Sample(&mode,&world,&controller,&actor,"Run42",1000,index,now);}
};
int main(){
{Fixture f;f.handler.m_bIsPerforming=true;f.handler.m_fCurrentProgress=2.25;f.input.pressed=1;f.sample();assert(headers==1&&samples==1&&geometries==1&&!cancels);assert(lastSelected&&lastContext&&lastPermitted&&lastBound&&lastPerforming&&lastProgress==2.25f);assert(lastPrison[2]==7160&&lastGate[2]==7158&&lastForward[2]==1);f.mode.phase=ERR_Phase::RADIO_SEARCH;f.handler.m_bIsPerforming=false;f.handler.m_fCurrentProgress=0;f.door.control=1;f.sample(2,1200);assert(samples==2&&headers==1&&!cancels&&lastPhase==ERR_Phase::RADIO_SEARCH);}
// Actual accessor never attributes accumulated progress from another selection/context.
for(int fault=0;fault<5;fault++){Fixture f;f.handler.m_bIsPerforming=true;f.handler.m_fCurrentProgress=3;BaseUserAction* expected=&f.lock;UserActionContext* ctx=&f.context;if(fault==0)expected=nullptr;if(fault==1)f.handler.selected=&f.foreignAction;if(fault==2)f.handler.current=&f.otherContext;if(fault==3)f.handler.m_pLastUserAction=&f.foreignAction;if(fault==4)f.handler.m_pLastContext=&f.otherContext;float progress=99;bool performing=true;assert(!f.handler.ERR_GateReadProgress(expected,ctx,progress,performing));assert(progress==0&&!performing);}
{Fixture f;f.handler.selected=&f.foreignAction;f.handler.m_fCurrentProgress=3;f.handler.m_bIsPerforming=true;f.sample();assert(samples==1&&!lastSelected&&!lastBound&&!lastPerforming&&lastProgress==0);}
// Cached flags cannot become positive via a missing/mismatched native array.
for(int fault=0;fault<4;fault++){Fixture f;if(fault==0)f.handler.filtered.clear();if(fault==1)f.handler.permitted.clear();if(fault==2)f.handler.filtered={&f.foreignAction};if(fault==3)f.handler.permitted={false};f.sample();assert(samples==1&&!lastPermitted);}
for(int fault=0;fault<8;fault++){Fixture f;if(fault==0)System::cli.erase("errOrdinaryGateObserve");if(fault==1)System::cli["errOrdinaryGateObserve"]="foreign";if(fault==2)System::cli["errOrdinaryInputObserve"]="foreign";if(fault==3)System::console=true;if(fault==4)Replication::server=false;if(fault==5)RplSession::mode=RplMode::Listen;if(fault==6)System::cli["errOrdinaryScenario"]="other";if(fault==7)RplSession::mode=RplMode::Dedicated;f.sample();assert(!samples&&!cancels&&!ERR_OrdinaryGateObserver::s_Attempted);}
// Registration cannot recover after initial absent/ambiguous/native-mismatched gate.
for(int fault=0;fault<8;fault++){Fixture f;if(fault==0)f.prison.registered=false;if(fault==1)f.prison.lost=true;if(fault==2)f.prison.gate=nullptr;if(fault==3)f.context.actions.clear();if(fault==4){f.otherLock.owner=&f.gate;f.context.actions.push_back(&f.otherLock);}if(fault==5)f.lock.owner=&f.otherGate;if(fault==6)f.lock.duration=3;if(fault==7)f.manager.action=&f.foreignAction;f.sample();assert(!samples&&cancels==1&&reason=="original-prison-lock-unavailable");f.prison.registered=true;f.prison.lost=false;f.prison.gate=&f.gate;f.context.actions={&f.lock};f.lock.owner=&f.gate;f.lock.duration=4;f.manager.action=&f.lock;f.sample(2,1200);assert(!samples&&cancels==1);}
for(int fault=0;fault<26;fault++){Fixture f;f.sample();if(fault==0)game.controller=&f.otherController;if(fault==1)f.controller.actor=&f.otherActor;if(fault==2)f.handler.actor=&f.otherActor;if(fault==3)f.players.actor=&f.otherActor;if(fault==4)f.players.controller=&f.otherController;if(fault==5)f.life.AccountId="replacement";if(fault==6)f.roster.life=&f.otherLife;if(fault==7)f.actor.world=&f.otherWorld;if(fault==8)f.gate.world=&f.otherWorld;if(fault==9)f.mode.active=false;if(fault==10)f.mode.phase=ERR_Phase::EXTRACTION;if(fault==11)f.actor.living=false;if(fault==12)f.actor.vehicle=true;if(fault==13)activeGame=nullptr;if(fault==14)f.menus.open=true;if(fault==15)f.prison.registered=false;if(fault==16)f.prison.gate=&f.otherGate;if(fault==17)f.prison.lost=true;if(fault==18)f.gate.components[BaseActionsManagerComponentKey]=&f.otherManager;if(fault==19)f.gate.components[BaseDoorComponentKey]=&f.otherDoor;if(fault==20)f.controller.components[SCR_InteractionHandlerComponentKey]=&f.otherHandler;if(fault==21)f.manager.context=&f.otherContext;if(fault==22)f.manager.action=&f.otherLock;if(fault==23)f.lock.owner=&f.otherGate;if(fault==24)f.lock.duration=5;if(fault==25)f.controller.id=0;f.sample(2,1200);assert(samples==1&&cancels==1&&reason=="native-player-or-prison-context-lost");f.sample(3,1300);assert(cancels==1);}
{Fixture f;f.input.changeDuringRead=&f.mode;f.sample();assert(!samples&&!headers&&cancels==1);}
for(int fault=0;fault<5;fault++){Fixture f;f.sample();int index=2,now=1200;if(fault==0)index=1;if(fault==1)index=3;if(fault==2)index=451;if(fault==3)now=1099;if(fault==4)now=46000;f.sample(index,now);assert(samples==1&&cancels==1&&reason=="sample-or-clock-bound");}
for(int fault=0;fault<9;fault++){Fixture f;if(fault==0)f.input.pressed=std::numeric_limits<float>::quiet_NaN();if(fault==1)f.handler.m_fCurrentProgress=-1;if(fault==2)f.door.state=2;if(fault==3)f.door.control=std::numeric_limits<float>::infinity();if(fault==4)f.context.radius=0;if(fault==5)f.actor.origin[0]=std::numeric_limits<float>::infinity();if(fault==6)f.mode.prisonPosition[1]=std::numeric_limits<float>::quiet_NaN();if(fault==7)f.gate.origin[0]=std::numeric_limits<float>::infinity();if(fault==8)f.gate.forward=Vector(0,0,0);f.sample();assert(!samples&&cancels==1&&reason=="invalid-native-action-sample");}
{Fixture f;System::cli["errOrdinaryScenario"]="chat";f.sample();System::cli.erase("errOrdinaryGateObserve");f.sample(2,1200);assert(cancels==1);System::cli["errOrdinaryGateObserve"]="Run42";f.sample(3,1300);assert(samples==1&&cancels==1);}
{Fixture f;f.sample();ERR_OrdinaryGateObserver::Sample(&f.mode,&f.otherWorld,&f.controller,&f.actor,"Run42",1000,2,1200);assert(cancels==1&&reason=="original-binding-changed");}
}
''')
print('PASS: actual gate observer binds native registered action/context and current life, reads accumulated progress, refuses stale bindings and never revives outside original bounds')
+7 -1
View File
@@ -7,7 +7,7 @@ def cpp(s):
s=s.replace(t+' mode',t+'* mode').replace(t+' controller',t+'* controller').replace(t+' actor',t+'* actor').replace(t+' life',t+'* life').replace(t+' request',t+'* request').replace(t+' current',t+'* current').replace(t+' manager',t+'* manager').replace(t+' menu',t+'* menu').replace(t+' root',t+'* root').replace(t+' expectedMenu',t+'* expectedMenu').replace(t+' expectedRoot',t+'* expectedRoot').replace(t+' overlay',t+'* overlay')
for v in ('mode','controller','actor','request','life','current','manager','menu','root','m_Mode','m_World','m_Controller','m_Actor','m_Character','m_Menu','s_Request','expectedMenu','expectedRoot','m_CaptureMenu','m_CaptureRoot','overlay','m_aHitZonePoints'):
s=s.replace(v+'.',v+'->')
for scope in ('System','Replication','ERR_Phase','ERR_GameMode','ERR_Actors','SCR_InventoryMenuUI','ERR_OrdinaryInputObserverRules','ERR_OrdinaryLookObserver','vector','Math'):
for scope in ('System','Replication','ERR_Phase','ERR_GameMode','ERR_Actors','SCR_InventoryMenuUI','ERR_OrdinaryInputObserverRules','ERR_OrdinaryLookObserver','ERR_OrdinaryGateObserver','vector','Math'):
s=s.replace(scope+'.',scope+'::')
s=s.replace('GetGame().','GetGame()->').replace('GetPlayerManager().','GetPlayerManager()->').replace('GetInputManager().','GetInputManager()->').replace('GetMenuManager().','GetMenuManager()->').replace('GetCallqueue().','GetCallqueue()->').replace('Players().','Players()->')
s=s.replace('CharacterControllerComponent.Cast(actor->FindComponent(CharacterControllerComponent))','actor->FindComponent(0)').replace('FindComponent(CharacterControllerComponent)','FindComponent(0)')
@@ -66,6 +66,12 @@ struct ERR_OrdinaryLookObserver {
assert(nonce=="Run42"&&index==samples&&now==System::now&&now>=started);
}
};
struct ERR_OrdinaryGateObserver {
static void Sample(ERR_GameMode* mode,BaseWorld* world,PlayerController* controller,IEntity* actor,string nonce,int started,int index,int now){
assert(mode==ERR_GameMode::Get()&&world==game.world&&controller==game.controller&&actor==controller->actor);
assert(nonce=="Run42"&&index==samples&&now==System::now&&now>=started);
}
};
struct ERR_OrdinaryInputObserver {
static ERR_OrdinaryInputObserver* s_Request;static bool s_Attempted;
ERR_GameMode* m_Mode=nullptr;BaseWorld* m_World=nullptr;PlayerController* m_Controller=nullptr;IEntity* m_Actor=nullptr;CharacterControllerComponent* m_Character=nullptr;ERR_PlayerLife* m_Life=nullptr;
+9
View File
@@ -13,6 +13,15 @@ import test_headless_input_exercise_runner as fixtures
from test_headless_input_exercise_runner import NONCE
class LookRunner(unittest.TestCase):
def test_gate_observation_is_independently_explicit_and_nonce_bound(self):
base=['gamescope','--backend','headless','--','/proton','run','/game']
self.assertNotIn('-errOrdinaryGateObserve',r.input_observer_command(base,'chat',None,NONCE))
self.assertEqual(r.input_observer_command(base,'chat',None,NONCE,gate=True)[-2:],['-errOrdinaryGateObserve',NONCE])
with self.assertRaises(ValueError):r.input_observer_command(base,'chat',None,NONCE,gate=1)
with self.assertRaises(ValueError):r.input_observer_command(base+['-errOrdinaryGateObserve','foreign'],'chat',None,NONCE)
with self.assertRaises(ValueError):r.validate_input_observer(SimpleNamespace(observe_prison_gate=True))
r.validate_input_observer(SimpleNamespace(observe_prison_gate=True,observe_player_input=True,project='chat',settle=50))
def test_optin_requires_all_existing_input_guards_before_side_effects(self):
valid=dict(exercise_relative_look=True,exercise_player_input=True,observe_player_input=True,inspect_headless_display=True,project='chat',settle=50)
r.validate_input_exercise(SimpleNamespace(**valid))