Wire production aircraft callbacks and diagnose native prison action selection
This commit is contained in:
@@ -21,7 +21,7 @@ def cpp(s):
|
||||
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('))
|
||||
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 static string ChoiceIdentity(', 'protected void ObserveChoices(', 'protected void Observe('))
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
@@ -36,7 +36,8 @@ 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 NativeType {string name;string ToString(){return name;}};
|
||||
struct BaseUserAction {string className="BaseUserAction";NativeType Type(){return {className};}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;}};
|
||||
@@ -61,12 +62,15 @@ namespace RplMode {enum {None,Listen,Dedicated};}namespace RplSession {int 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;
|
||||
int headers=0,samples=0,geometries=0,cancels=0,choices=0;string lastChoice;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";}
|
||||
string Format(string,string name,int id){return name+":"+std::to_string(id);}
|
||||
string Format(string,int index,string identity,int permission){return std::to_string(index)+"="+identity+":"+std::to_string(permission);}
|
||||
string Format(string,string selected,bool context,int count,int permissions,int shown,bool truncated,string ordered){return "selected="+selected+" context="+std::to_string(context)+" actions="+std::to_string(count)+" permissions="+std::to_string(permissions)+" shown="+std::to_string(shown)+" truncated="+std::to_string(truncated)+" ordered=["+ordered+"]";}
|
||||
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);}
|
||||
void PrintFormat(string format,string,int,int,string value){if(format.find("ERR GATE CHOICE")!=string::npos){choices++;lastChoice=value;}else 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;
|
||||
@@ -75,10 +79,20 @@ class ERR_OrdinaryGateObserver {public:
|
||||
};
|
||||
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};}
|
||||
Fixture(){delete ERR_OrdinaryGateObserver::s_Request;ERR_OrdinaryGateObserver::s_Request=nullptr;ERR_OrdinaryGateObserver::s_Attempted=false;headers=samples=geometries=cancels=choices=0;lastChoice="";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;lock.className="ERR_UnlockPrisonAction";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(){
|
||||
|
||||
// Diagnostics are an independent opt-in beneath the same validated observer.
|
||||
{Fixture f;f.sample();assert(choices==0);System::cli["errOrdinaryGateChoices"]="foreign";f.sample(2,1200);assert(choices==0&&samples==2);}
|
||||
// A disabled stock door can be selected while the exact lock is cached available.
|
||||
{Fixture f;System::cli["errOrdinaryGateChoices"]="Run42";f.foreignAction.className="ERR_PrisonDoorAction";f.foreignAction.id=7;f.handler.filtered={&f.foreignAction,&f.lock};f.handler.permitted={false,true};f.handler.selected=&f.foreignAction;f.sample();assert(choices==1&&samples==1&&geometries==1);assert(!lastSelected&&lastContext&&lastPermitted);assert(lastChoice=="selected=ERR_PrisonDoorAction:7 context=1 actions=2 permissions=2 shown=2 truncated=0 ordered=[0=ERR_PrisonDoorAction:7:0,1=ERR_UnlockPrisonAction:4:1]");assert(f.handler.selected==&f.foreignAction&&f.handler.filtered[0]==&f.foreignAction);}
|
||||
// Preserve native ordering and missing cached data without guessing authority.
|
||||
{Fixture f;System::cli["errOrdinaryGateChoices"]="Run42";f.handler.selected=nullptr;f.handler.current=&f.otherContext;f.handler.filtered={nullptr,&f.lock};f.handler.permitted={false};f.sample();assert(choices==1&&!lastSelected&&!lastContext);assert(lastChoice=="selected=none:-1 context=0 actions=2 permissions=1 shown=2 truncated=0 ordered=[0=none:-1:0,1=ERR_UnlockPrisonAction:4:-1]");}
|
||||
{Fixture f;System::cli["errOrdinaryGateChoices"]="Run42";f.handler.filtered.clear();f.handler.permitted.clear();for(int i=0;i<12;i++){f.handler.filtered.push_back(&f.lock);f.handler.permitted.push_back(true);}f.sample();assert(choices==1);assert(lastChoice.find("actions=12 permissions=12 shown=8 truncated=1")!=string::npos);assert(lastChoice.find("7=ERR_UnlockPrisonAction:4:1")!=string::npos&&lastChoice.find("8=ERR_")==string::npos);f.prison.gate=&f.otherGate;f.sample(2,1200);assert(choices==1&&cancels==1);}
|
||||
// Direct diagnostic entry cannot bypass a stopped or replaced original binding.
|
||||
{Fixture f;f.sample();System::cli["errOrdinaryGateChoices"]="Run42";auto request=ERR_OrdinaryGateObserver::s_Request;request->m_Stopped=true;request->ObserveChoices(2,1200);assert(choices==0);request->m_Stopped=false;f.roster.life=&f.otherLife;request->ObserveChoices(2,1200);assert(choices==0);}
|
||||
{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);}
|
||||
|
||||
Reference in New Issue
Block a user