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.
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/00_Core/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')
|