123 lines
14 KiB
Python
123 lines
14 KiB
Python
"""Execute observer policies/lifecycle against native read-boundary adapters, not a game."""
|
|
from policy_harness import ROOT, body, run
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/ERR_OrdinaryInputObserver.c'
|
|
def cpp(s):
|
|
s=s.replace('protected ', '').replace('out string nonce','string& nonce')
|
|
for t in ('ERR_GameMode','PlayerController','IEntity','ERR_PlayerLife','ERR_OrdinaryInputObserver','PlayerManager','SCR_InventoryMenuUI','Widget'):
|
|
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')
|
|
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'):
|
|
s=s.replace(v+'.',v+'->')
|
|
for scope in ('System','Replication','ERR_Phase','ERR_GameMode','ERR_Actors','SCR_InventoryMenuUI','ERR_OrdinaryInputObserverRules','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)')
|
|
s=s.replace('.Insert(request->Cancel)', '.Insert()').replace('.Remove(Cancel)', '.Remove()')
|
|
s=s.replace('.CallLater(request->Sample, 100, false)', '.CallLater()').replace('->CallLater(request->Sample, 100, false)', '->CallLater()').replace('->CallLater(Sample, 100, false)', '->CallLater()').replace('->Remove(Sample)', '->Remove()')
|
|
s=s.replace('null','nullptr').replace('bool open;', 'bool open = false;')
|
|
s=s.replace('nonce.Length()', 'int(nonce.size())').replace('allowed.Contains(nonce[i])', '(allowed.find(nonce[i]) != string::npos)')
|
|
return s
|
|
rules='\n'.join(cpp(body(P,x)) for x in ('static bool Nonce(', 'static bool Within(', 'static bool Stationary('))
|
|
methods='\n'.join(cpp(body(P,x)) for x in ('protected static bool Enabled(', 'static void Queue(', 'protected bool Valid(', 'protected void Record(', 'protected void Capture(', 'protected void Observe(', 'protected void Sample(', 'protected void End(', 'void Cancel(', 'static void ClearWorld('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
using string=std::string;using UUID=string;
|
|
struct vector {float x=0,y=0,z=0;float Length(){return std::sqrt(x*x+y*y+z*z);}static float Distance(vector a,vector b){return vector{a.x-b.x,a.y-b.y,a.z-b.z}.Length();}};
|
|
namespace Math {float AbsFloat(float x){return std::abs(x);}}
|
|
namespace System {bool console=false;int now=0;std::map<string,string> cli;std::vector<string> shots;bool IsConsoleApp(){return console;}int GetTickCount(){return now;}bool GetCLIParam(string key,string& value){if(!cli.count(key))return false;value=cli[key];return true;}void MakeScreenshot(string path){shots.push_back(path);}}
|
|
namespace Replication {bool server=true;bool IsServer(){return server;}}
|
|
namespace ERR_Phase {enum {PRISON,PREPARING,DEFEAT};}
|
|
struct BaseWorld {float time=0;float GetWorldTime(){return time;}};
|
|
struct CharacterControllerComponent {bool unconscious=false,disabled=false;vector input,velocity;bool IsUnconscious(){return unconscious;}bool GetDisableMovementControls(){return disabled;}vector GetMovementInput(){return input;}vector GetVelocity(){return velocity;}};
|
|
struct IEntity {BaseWorld* world=nullptr;CharacterControllerComponent* component=nullptr;bool conscious=true;vector origin;BaseWorld* GetWorld(){return world;}CharacterControllerComponent* FindComponent(int){return component;}vector GetOrigin(){return origin;}};
|
|
namespace ERR_Actors {bool CanFight(IEntity* actor){return actor&&actor->conscious;}}
|
|
struct PlayerController {int id=1;IEntity* actor=nullptr;int GetPlayerId(){return id;}IEntity* GetControlledEntity(){return actor;}};
|
|
struct ERR_PlayerLife {UUID AccountId="private-account";};
|
|
struct Roster {IEntity* actor=nullptr;ERR_PlayerLife* life=nullptr;ERR_PlayerLife* CurrentActor(IEntity* a){return a==actor?life:nullptr;}};
|
|
struct Invoker {int inserted=0,removed=0;void Insert(){inserted++;}void Remove(){removed++;}};
|
|
struct ERR_GameMode {static ERR_GameMode* current;BaseWorld* world=nullptr;bool active=true;int phase=ERR_Phase::PRISON;Roster roster;Invoker stop;static ERR_GameMode* Get(){return current;}BaseWorld* GetWorld(){return world;}bool Active(){return active;}int Phase(){return phase;}Roster* Players(){return &roster;}Invoker& OnStop(){return stop;}};ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
struct PlayerManager {PlayerController* controller=nullptr;IEntity* actor=nullptr;PlayerController* GetPlayerController(int){return controller;}IEntity* GetPlayerControlledEntity(int){return actor;}};
|
|
struct Widget {bool visible=true;bool IsVisible(){return visible;}};
|
|
struct SCR_InventoryMenuUI {static SCR_InventoryMenuUI* current;CharacterControllerComponent* character=nullptr;Widget* root=nullptr;static SCR_InventoryMenuUI* GetInventoryMenu(){return current;}CharacterControllerComponent* GetCharacterController(){return character;}Widget* GetRootWidget(){return root;}};SCR_InventoryMenuUI* SCR_InventoryMenuUI::current=nullptr;
|
|
struct MenuManager {SCR_InventoryMenuUI* top=nullptr;SCR_InventoryMenuUI* GetTopMenu(){return top;}};
|
|
struct InputManager {float forward=0;float GetActionValue(string name){assert(name=="CharacterForward");return forward;}};
|
|
struct Callqueue {int queued=0,removed=0;void CallLater(){queued++;}void Remove(){removed++;}};
|
|
struct Game {BaseWorld* world=nullptr;PlayerController* controller=nullptr;PlayerManager manager;MenuManager menus;InputManager input;Callqueue queue;BaseWorld* GetWorld(){return world;}PlayerController* GetPlayerController(){return controller;}PlayerManager* GetPlayerManager(){return &manager;}MenuManager* GetMenuManager(){return &menus;}InputManager* GetInputManager(){return &input;}Callqueue* GetCallqueue(){return &queue;}} game;Game* GetGame(){return &game;}
|
|
std::vector<string> events;int samples=0;
|
|
void PrintFormat(const char*,string nonce,int,int,float,string detail){assert(nonce=="Run42");events.push_back(detail);}
|
|
void PrintFormat(const char*,string nonce,int,int,float,vector,float,vector,vector,bool){assert(nonce=="Run42");samples++;}
|
|
struct ERR_OrdinaryInputObserverRules {
|
|
'''+rules+r'''
|
|
};
|
|
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;
|
|
UUID m_Account;int m_PlayerId=0;string m_Nonce;bool m_Screenshots=false,m_Finished=false,m_Ready=false,m_Opened=false,m_Closed=false;SCR_InventoryMenuUI* m_Menu=nullptr;
|
|
int m_Start=0,m_Previous=0,m_Samples=0,m_BaselineSince=-1;vector m_BaselineOrigin;float m_LastWorldTime=0;
|
|
'''+methods+r'''
|
|
};
|
|
ERR_OrdinaryInputObserver* ERR_OrdinaryInputObserver::s_Request=nullptr;bool ERR_OrdinaryInputObserver::s_Attempted=false;
|
|
struct Fixture {ERR_GameMode mode;BaseWorld world;PlayerController controller;IEntity actor;CharacterControllerComponent character;ERR_PlayerLife life;Widget widget;SCR_InventoryMenuUI menu;
|
|
Fixture(){game={};System::cli={{"errOrdinaryScenario","core"},{"errOrdinaryInputObserve","Run42"},{"errOrdinaryInputScreenshots","1"}};System::now=0;System::console=false;System::shots.clear();Replication::server=true;events.clear();samples=0;ERR_OrdinaryInputObserver::s_Request=nullptr;ERR_OrdinaryInputObserver::s_Attempted=false;SCR_InventoryMenuUI::current=nullptr;ERR_GameMode::current=&mode;mode.world=&world;mode.roster.actor=&actor;mode.roster.life=&life;actor.world=&world;actor.component=&character;controller.actor=&actor;game.world=&world;game.controller=&controller;game.manager.controller=&controller;game.manager.actor=&actor;menu.character=&character;menu.root=&widget;}
|
|
ERR_OrdinaryInputObserver* queue(){ERR_OrdinaryInputObserver::Queue(&mode,&controller,&actor);return ERR_OrdinaryInputObserver::s_Request;}
|
|
void sample(int ms){System::now=ms;world.time=ms;ERR_OrdinaryInputObserver::s_Request->Sample();}
|
|
void baseline(){for(int t=100;t<=2100;t+=100)sample(t);assert(ERR_OrdinaryInputObserver::s_Request->m_Ready);}
|
|
void open(){SCR_InventoryMenuUI::current=&menu;game.menus.top=&menu;}
|
|
void close(){SCR_InventoryMenuUI::current=nullptr;game.menus.top=nullptr;}
|
|
};
|
|
int count(string value){int n=0;for(auto event:events)if(event==value)n++;return n;}
|
|
int main(){
|
|
assert(ERR_OrdinaryInputObserverRules::Nonce("aZ09"));for(string n:std::vector<string>{"","../bad","a-b","a_b","line\n","a b","$logs:x",string(33,'x')})assert(!ERR_OrdinaryInputObserverRules::Nonce(n));
|
|
assert(ERR_OrdinaryInputObserverRules::Within(0,44900,44999,449));assert(!ERR_OrdinaryInputObserverRules::Within(0,44900,45000,449));assert(!ERR_OrdinaryInputObserverRules::Within(0,100,99,2));assert(!ERR_OrdinaryInputObserverRules::Within(0,1,2,450));
|
|
for(int fault=0;fault<6;fault++){Fixture f;
|
|
if(fault==0)System::cli.erase("errOrdinaryInputObserve");
|
|
if(fault==1)System::cli["errOrdinaryInputObserve"]="../bad";
|
|
if(fault==2)System::cli["errOrdinaryScenario"]="other";
|
|
if(fault==3)System::console=true;
|
|
if(fault==4)Replication::server=false;
|
|
if(fault==5)f.mode.roster.life=nullptr;
|
|
assert(!f.queue()&&game.queue.queued==0&&!ERR_OrdinaryInputObserver::s_Attempted);}
|
|
for(int fault=0;fault<14;fault++){Fixture f;auto p=f.queue();assert(p&&p->Valid());BaseWorld otherWorld;PlayerController otherController;IEntity otherActor;CharacterControllerComponent otherCharacter;ERR_PlayerLife otherLife;ERR_GameMode otherMode;
|
|
if(fault==0)ERR_GameMode::current=&otherMode;
|
|
if(fault==1)game.world=&otherWorld;
|
|
if(fault==2)f.mode.active=false;
|
|
if(fault==3)f.mode.phase=ERR_Phase::DEFEAT;
|
|
if(fault==4)game.controller=&otherController;
|
|
if(fault==5)f.controller.id=2;
|
|
if(fault==6)game.manager.controller=&otherController;
|
|
if(fault==7)game.manager.actor=&otherActor;
|
|
if(fault==8)f.controller.actor=&otherActor;
|
|
if(fault==9)f.actor.conscious=false;
|
|
if(fault==10)f.actor.component=&otherCharacter;
|
|
if(fault==11)f.character.unconscious=true;
|
|
if(fault==12)f.mode.roster.life=&otherLife;
|
|
if(fault==13)f.life.AccountId="changed";
|
|
assert(!p->Valid());f.sample(100);assert(p->m_Finished&&samples==0&&game.queue.removed==1&&f.mode.stop.removed==1);p->Cancel();assert(game.queue.removed==1);}
|
|
{Fixture f;auto p=f.queue();f.baseline();assert(count("READY")==1&&System::shots.size()==1);game.input.forward=1;f.character.input={0,0,1};f.actor.origin={0,0,1};f.sample(2200);assert(samples==22&&!p->m_Opened);game.input.forward=0;f.character.input={};f.character.disabled=true;f.open();f.sample(2300);assert(p->m_Opened&&count("INVENTORY_OPEN")==1&&System::shots.size()==2);f.sample(2400);assert(System::shots.size()==2);f.close();f.sample(2500);assert(p->m_Closed&&count("INVENTORY_CLOSED")==1&&System::shots.size()==3);f.sample(2600);assert(System::shots.size()==3);for(auto path:System::shots)assert(path.find("$logs:err-input-Run42-")==0);p->Cancel();int writes=samples;f.sample(2700);assert(samples==writes);ERR_OrdinaryInputObserver::ClearWorld();assert(ERR_OrdinaryInputObserver::s_Attempted&&!ERR_OrdinaryInputObserver::s_Request);assert(!f.queue());}
|
|
{Fixture f;auto p=f.queue();System::cli["errOrdinaryInputScreenshots"]="0";p->m_Screenshots=false;f.open();for(int t=100;t<2300;t+=100)f.sample(t);assert(!p->m_Ready);f.close();for(int t=2300;t<=4300;t+=100)f.sample(t);assert(p->m_Ready&&System::shots.empty());}
|
|
for(int fault=0;fault<4;fault++){Fixture f;auto p=f.queue();f.baseline();f.open();
|
|
if(fault==0)f.menu.character=nullptr;
|
|
if(fault==1)f.menu.root=nullptr;
|
|
if(fault==2)f.widget.visible=false;
|
|
if(fault==3)game.menus.top=nullptr;
|
|
f.sample(2200);assert(p->m_Finished&&!p->m_Opened&&System::shots.size()==1);}
|
|
{Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);SCR_InventoryMenuUI replacement=f.menu;SCR_InventoryMenuUI::current=&replacement;game.menus.top=&replacement;f.sample(2300);assert(p->m_Finished&&!p->m_Closed);}
|
|
{Fixture f;auto p=f.queue();f.sample(100);f.sample(200);f.sample(1000);assert(p->m_BaselineSince==-1);f.sample(1100);assert(!p->m_Ready);f.sample(45000);assert(p->m_Finished&&count("FINISHED observation-bound")==1);}
|
|
{Fixture f;auto p=f.queue();for(int t=1;t<=450;t++)f.sample(t);assert(p->m_Finished&&samples==450&&count("FINISHED sample-bound")==1);}
|
|
{Fixture f;auto p=f.queue();f.sample(100);System::now=200;f.world.time=99;p->Sample();assert(p->m_Finished&&count("CANCELLED world-clock-reversed")==1);}
|
|
{Fixture f;auto p=f.queue();f.sample(100);System::now=99;f.world.time=101;p->Sample();assert(p->m_Finished);}
|
|
}
|
|
''')
|
|
s=P.read_text()
|
|
for forbidden in ('SetActionValue(', 'ActivateContext(', 'OpenMenu(', 'CloseMenu(', 'OpenInventory(', 'CloseInventory(', 'SetCamera(', 'SetOrigin(', 'SetWorldTransform(', 'SetVelocity(', 'SetHealth(', 'SetUnconscious(', 'SetInvulnerable(', 'SpawnEntity'):
|
|
assert forbidden not in s,forbidden
|
|
assert s.startswith('#ifndef WORKBENCH')
|
|
assert 's_Attempted = false' not in body(P,'static void ClearWorld(')
|
|
assert s.count('System.MakeScreenshot(')==1
|
|
for line in s.splitlines():
|
|
if 'PrintFormat(' in line:assert all(x not in line for x in ('m_Account','m_PlayerId','m_Life','m_Controller','m_Actor'))
|
|
print('PASS: actual observer nonce, admission pins, baseline/menu sequencing, snapshots, bounded clocks/samples, cleanup and one-attempt latch; native untested')
|