145 lines
13 KiB
Python
145 lines
13 KiB
Python
"""Actual opt-in screenshot helper lifecycle with native interfaces adapted."""
|
|
import re
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/ERR_OrdinaryScenarioLaunch.c'
|
|
signatures=['protected static bool Enabled(', 'static void Queue(ERR_GameMode', 'protected bool Valid(', 'void Cancel()', 'protected void Capture(', 'static void ClearWorld(']
|
|
# Cancel() also exists in the launch helper; select the screenshot class first.
|
|
source=p.read_text();helper=source.split('class ERR_OrdinaryScreenshotRequest : Managed',1)[1].split('modded class ArmaReforgerScripted',1)[0]
|
|
def method(signature):
|
|
start=helper.index(signature);open_=helper.index('{',start);depth=1;i=open_+1
|
|
while depth:
|
|
depth+=(helper[i]=='{')-(helper[i]=='}');i+=1
|
|
return helper[start:i]
|
|
def adapt(s):
|
|
s=s.replace('protected ','').replace('null','nullptr').replace('ERR_OrdinaryScreenshotRequest','Probe')
|
|
for typ in ('ERR_GameMode','PlayerController','ERR_PlayerLife','PlayerManager','Probe'):
|
|
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
|
|
for n in ('System','Replication','ERR_GameMode','ERR_Phase','ERR_Actors'):
|
|
s=s.replace(n+'.',n+'::')
|
|
for n in ('mode','controller','life','request','manager','current','m_Mode','m_Controller','s_Request'):
|
|
s=s.replace(n+'.',n+'->')
|
|
s=s.replace('GetGame().','GetGame()->').replace('Players().','Players()->')
|
|
s=s.replace('OnStop().Insert(request->Cancel)','OnStop().Insert(request, &Probe::Cancel)').replace('OnStop().Remove(Cancel)','OnStop().Remove(this, &Probe::Cancel)')
|
|
s=s.replace('CallLater(request->Capture, 3000, false)','CallLater(request, &Probe::Capture, 3000, false)').replace('Remove(Capture)','Remove(this, &Probe::Capture)')
|
|
return s
|
|
methods='\n'.join(adapt(method(sig)) for sig in signatures)
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <map>
|
|
using string=std::string;using UUID=int;
|
|
void Print(const char*){}
|
|
namespace Replication{bool server=true;bool IsServer(){return server;}}
|
|
namespace System{bool console=false;std::map<string,string> flags;int captures=0;string output;
|
|
bool IsConsoleApp(){return console;}bool GetCLIParam(string key,string& out){auto p=flags.find(key);if(p==flags.end())return false;out=p->second;return true;}
|
|
void MakeScreenshot(string path){captures++;output=path;}
|
|
}
|
|
namespace ERR_Phase{const int PRISON=1;}
|
|
struct Actor{bool alive=true;};using IEntity=Actor*;
|
|
namespace ERR_Actors{bool Alive(IEntity a){return a&&a->alive;}}
|
|
struct ERR_PlayerLife{int AccountId=11;};
|
|
struct PlayerController{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 Roster{ERR_PlayerLife* life=nullptr;IEntity actor=nullptr;ERR_PlayerLife* CurrentActor(IEntity a){return a&&a==actor?life:nullptr;}};
|
|
struct Probe;
|
|
struct Event{Probe* owner=nullptr;void(Probe::*callback)()=nullptr;int inserts=0,removes=0;
|
|
void Insert(Probe* p,void(Probe::*f)()){owner=p;callback=f;inserts++;}void Remove(Probe* p,void(Probe::*f)()){if(owner==p&&callback==f){owner=nullptr;callback=nullptr;removes++;}}void Invoke();};
|
|
struct Queue:Event{int delay=0;bool repeat=true;void CallLater(Probe* p,void(Probe::*f)(),int d,bool r){Insert(p,f);delay=d;repeat=r;}};
|
|
struct ERR_GameMode{static ERR_GameMode* current;bool active=true;int phase=1,world=7;Roster roster;Event stop;
|
|
static ERR_GameMode* Get(){return current;}int GetWorld(){return world;}bool Active(){return active;}int Phase(){return phase;}Roster* Players(){return &roster;}Event& OnStop(){return stop;}};ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
struct Game{int world=7;PlayerController* local=nullptr;PlayerManager manager;Queue queue;int GetWorld(){return world;}PlayerController* GetPlayerController(){return local;}PlayerManager* GetPlayerManager(){return &manager;}Queue& GetCallqueue(){return queue;}};Game* game_;Game* GetGame(){return game_;}
|
|
int yardMeasurements=0;
|
|
struct Probe{void YardDiagnostic(){assert(m_Mode);yardMeasurements++;}static Probe* s_Request;static bool s_Attempted;ERR_GameMode* m_Mode=nullptr;PlayerController* m_Controller=nullptr;IEntity m_Actor=nullptr;ERR_PlayerLife* m_Life=nullptr;UUID m_Account=0;int m_PlayerId=0;bool m_Finished=false;
|
|
'''+methods.replace('GetCallqueue().Remove','GetCallqueue().Remove').replace('GetGame()->GetCallqueue())','true)')+r'''
|
|
};
|
|
Probe* Probe::s_Request=nullptr;bool Probe::s_Attempted=false;
|
|
void Event::Invoke(){if(owner&&callback){auto p=owner;auto f=callback;(p->*f)();}}
|
|
struct Fixture{Game game;ERR_GameMode mode;Actor actor;ERR_PlayerLife life;PlayerController controller;
|
|
Fixture(){game_=&game;ERR_GameMode::current=&mode;controller.actor=&actor;game.local=&controller;game.manager.controller=&controller;game.manager.actor=&actor;mode.roster.actor=&actor;mode.roster.life=&life;System::flags={{"errOrdinaryScenario","core"},{"errOrdinaryScreenshot","1"}};System::console=false;Replication::server=true;System::captures=0;yardMeasurements=0;}
|
|
void queue(){Probe::Queue(&mode,&controller,&actor);}~Fixture(){Probe::ClearWorld();game_=nullptr;ERR_GameMode::current=nullptr;}
|
|
};
|
|
int main(){
|
|
for(string project: {"","core","chat","other"})for(string flag:{"","0","1","true"}){Fixture f;System::flags["errOrdinaryScenario"]=project;System::flags["errOrdinaryScreenshot"]=flag;f.queue();bool allowed=(project=="core"||project=="chat")&&flag=="1";assert(f.game.queue.inserts==(allowed?1:0));f.game.queue.Invoke();assert(System::captures==(allowed?1:0)&&yardMeasurements==(allowed?1:0));}
|
|
{Fixture f;System::flags.clear();f.queue();assert(f.game.queue.inserts==0);}
|
|
{Fixture f;System::console=true;f.queue();assert(f.game.queue.inserts==0);}
|
|
{Fixture f;Replication::server=false;f.queue();assert(f.game.queue.inserts==0);}
|
|
{Fixture f;f.queue();f.queue();assert(f.game.queue.inserts==1&&f.game.queue.delay==3000&&!f.game.queue.repeat);Probe* pending=Probe::s_Request;f.game.queue.Invoke();assert(System::captures==1&&System::output=="$logs:err-ordinary-prison");assert(f.mode.stop.owner==nullptr&&f.game.queue.owner==nullptr);pending->Capture();f.queue();assert(System::captures==1&&f.game.queue.inserts==1);}
|
|
for(int mutation=0;mutation<13;mutation++){Fixture f;f.queue();PlayerController replacement;Actor other;ERR_PlayerLife otherLife;ERR_GameMode otherMode;
|
|
if(mutation==0)f.actor.alive=false;
|
|
if(mutation==1)f.controller.actor=&other;
|
|
if(mutation==2)f.game.local=&replacement;
|
|
if(mutation==3)f.game.manager.controller=&replacement;
|
|
if(mutation==4)f.game.manager.actor=&other;
|
|
if(mutation==5)f.mode.roster.actor=&other;
|
|
if(mutation==6)f.mode.roster.life=&otherLife;
|
|
if(mutation==7)f.life.AccountId=99;
|
|
if(mutation==8)f.controller.id=2;
|
|
if(mutation==9)f.mode.phase=2;
|
|
if(mutation==10)f.mode.active=false;
|
|
if(mutation==11)f.game.world=8;
|
|
if(mutation==12)ERR_GameMode::current=&otherMode;
|
|
f.game.queue.Invoke();assert(System::captures==0&&yardMeasurements==0&&f.mode.stop.owner==nullptr&&f.game.queue.owner==nullptr);
|
|
}
|
|
{Fixture f;f.queue();Probe* pending=Probe::s_Request;f.mode.stop.Invoke();pending->Capture();assert(System::captures==0&&f.game.queue.owner==nullptr&&f.mode.stop.owner==nullptr);f.queue();assert(f.game.queue.inserts==1);}
|
|
{Fixture f;f.queue();Probe* pending=Probe::s_Request;Probe::ClearWorld();pending->Capture();assert(System::captures==0&&f.game.queue.owner==nullptr&&f.mode.stop.owner==nullptr);f.queue();f.game.queue.Invoke();assert(System::captures==1);}
|
|
}
|
|
''')
|
|
assert source.index('#ifndef WORKBENCH')<source.index('class ERR_OrdinaryScreenshotRequest')<source.rindex('#endif')
|
|
cleanup=body(p,'override protected void OnBeforeWorldCleanup(')
|
|
assert cleanup.index('ERR_OrdinaryScreenshotRequest.ClearWorld()')<cleanup.index('super.OnBeforeWorldCleanup()')
|
|
spawn=body(p,'override void PlayerSpawned(')
|
|
assert spawn.index('Players().CurrentActor(actor)')<spawn.index('ERR_OrdinaryScreenshotRequest.Queue(this, local, actor)')
|
|
assert helper.count('System.MakeScreenshot(')==1
|
|
for forbidden in ('SetControlledEntity','Teleport','SetOrigin','SetWorldTransform','SetActionValue','SetCamera','CreateCamera','TrySpawn','SpawnEntity','SetUnconscious'):
|
|
assert forbidden not in helper,forbidden
|
|
print('PASS: actual optional screenshot helper flag/identity/world/life revalidation, one queued attempt, delayed capture and mission/world cleanup; no native image/presentation claim')
|
|
|
|
# Execute the actual bounded, read-only hierarchy measurement separately. Native
|
|
# entity bounds/transforms are adapter inputs; this does not certify native sizes.
|
|
yard=method('protected void YardDiagnostic(').replace('protected ','')
|
|
yard=yard.replace('array<','Arr<').replace('vector ','Vec ').replace('ResourceName ','string ')
|
|
yard=yard.replace('foreach (ERR_LocationComponent location : locations)','for (ERR_LocationComponent location : locations)')
|
|
yard=yard.replace('ERR_LocationKind.PRISON','ERR_LocationKind::PRISON').replace('DecalEntity.Cast(entity)','CastDecal(entity)')
|
|
for name in ('m_Mode','location','owner','prison','entity','decal','child'):
|
|
yard=yard.replace(name+'.',name+'->')
|
|
yard=yard.replace('GetPrefabData().','GetPrefabData()->')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
using string=std::string;
|
|
template<class T>struct Arr:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}void Insert(T x){this->push_back(x);}};
|
|
struct Vec{float v[3]={0,0,0};float& operator[](int i){return v[i];}};
|
|
int messages=0,measurements=0;std::vector<Vec> projected;
|
|
void Print(const char*){messages++;}
|
|
template<class...T>void PrintFormat(const char*,T...){messages++;}
|
|
struct Prefab{string name="{BE6B8D56349038A6}Prefabs/ERR/Prison.et";string GetPrefabName(){return name;}};
|
|
struct Entity;using IEntity=Entity*;using DecalEntity=Entity*;
|
|
struct TypeName{string ToString(){return "DecalEntity";}};
|
|
struct Entity{int world=7;bool isDecal=false,hasPrefab=true;Prefab prefab;Entity* child=nullptr;Entity* sibling=nullptr;Vec mins{{98,10,198}},maxs{{102,11,202}},origin{{100,10,200}};int reads=0;
|
|
int GetWorld(){return world;}Prefab* GetPrefabData(){return hasPrefab?&prefab:nullptr;}void GetWorldTransform(Vec* out){reads++;out[3]=origin;}
|
|
void GetWorldBounds(Vec& a,Vec& b){reads++;measurements++;a=mins;b=maxs;}TypeName Type(){return {};}
|
|
Vec CoordToLocal(Vec c){for(int i=0;i<3;i++)c[i]-=origin[i];projected.push_back(c);return c;}
|
|
Entity* GetChildren(){return child;}Entity* GetSibling(){return sibling;}};
|
|
DecalEntity CastDecal(IEntity e){return e&&e->isDecal?e:nullptr;}
|
|
namespace ERR_LocationKind{const int PRISON=0;}
|
|
struct Location{Entity* owner=nullptr;int kind=0;string id="everon-prison-1";int Kind(){return kind;}string Id(){return id;}Entity* GetOwner(){return owner;}};using ERR_LocationComponent=Location*;
|
|
struct Mode{Arr<ERR_LocationComponent> locations;void Locations(Arr<ERR_LocationComponent>& out){out=locations;}int GetWorld(){return 7;}};
|
|
struct Probe{Mode* m_Mode;
|
|
'''+yard.replace('IEntity prison;','IEntity prison = nullptr;').replace('int decals;','int decals = 0;')+r'''
|
|
};
|
|
void reset(){messages=measurements=0;projected.clear();}
|
|
int main(){
|
|
Entity prison,decal,external;decal.isDecal=external.isDecal=true;prison.child=&decal;prison.sibling=&external;Location location{&prison};Mode mode{{&location}};Probe p{&mode};
|
|
p.YardDiagnostic();assert(measurements==1&&projected.size()==8&&external.reads==0);assert(projected.front()[0]==-2&&projected.front()[1]==0&&projected.front()[2]==-2);assert(projected.back()[0]==2&&projected.back()[1]==1&&projected.back()[2]==2);
|
|
for(int mismatch=0;mismatch<5;mismatch++){reset();Location candidate{&prison};Mode other{{&candidate}};Probe q{&other};if(mismatch==0)candidate.kind=1;if(mismatch==1)candidate.id="other-prison";if(mismatch==2)prison.prefab.name="different";if(mismatch==3)prison.world=8;if(mismatch==4)prison.hasPrefab=false;q.YardDiagnostic();assert(measurements==0);prison.prefab.name="{BE6B8D56349038A6}Prefabs/ERR/Prison.et";prison.world=7;prison.hasPrefab=true;}
|
|
reset();mode.locations.push_back(&location);p.YardDiagnostic();assert(measurements==0);mode.locations.pop_back();
|
|
reset();decal.sibling=&decal;p.YardDiagnostic();assert(measurements==0);decal.sibling=nullptr;
|
|
reset();std::vector<Entity> chain(10);prison.child=&chain[0];for(int i=0;i<9;i++)chain[i].child=&chain[i+1];chain[9].isDecal=true;p.YardDiagnostic();assert(measurements==0);
|
|
reset();std::vector<Entity> wide(65);prison.child=&wide[0];for(int i=0;i<64;i++)wide[i].sibling=&wide[i+1];wide[64].isDecal=true;p.YardDiagnostic();assert(measurements==0);
|
|
reset();mode.locations=Arr<ERR_LocationComponent>(65,&location);p.YardDiagnostic();assert(measurements==0);
|
|
}
|
|
''')
|
|
print('PASS: actual yard diagnostic exact registered prison ownership, eight native-bound corner projections, no root-sibling escape, bounded duplicate/deep/wide hierarchy traversal; native projection/mask still unmeasured')
|