110 lines
12 KiB
Python
110 lines
12 KiB
Python
"""Exercise actual admin utility command/state methods through native API adapters."""
|
|
from policy_harness import ROOT, body, run
|
|
source=ROOT/'addons/core/Scripts/Game/ERR/ERR_AdminPlayerTools.c'
|
|
def adapt(s):
|
|
s=s.replace('protected ', '').replace('null', 'nullptr')
|
|
s=s.replace('array<string> replies','Array<String>& replies')
|
|
s=s.replace('string ', 'String ').replace('ResourceName ', 'String ')
|
|
s=s.replace('args.Trim()', 'args.Trim()')
|
|
for typ in ['ERR_GameMode', 'SCR_PlayerController', 'PlayerManager', 'IEntity', 'DamageManagerComponent', 'SCR_InventoryStorageManagerComponent']:
|
|
pass
|
|
for name in ['actor','m_Actor','mode','controller','manager','m_Mode','m_Controller','m_Damage','inventory','m_Grants','Operation']:
|
|
s=s.replace(name+'.',name+'->')
|
|
s=s.replace('GetGame().','GetGame()->').replace('GetWorld().','GetWorld()->').replace('Players().','Players()->')
|
|
s=s.replace('ERR_GameMode.Get()', 'Mode::Get()').replace('Replication.IsServer()', 'Replication::IsServer()').replace('SCR_Global.IsAdmin(', 'Global::IsAdmin(').replace('ERR_Actors.Alive(', 'Actors::Alive(')
|
|
s=s.replace('FindComponent(DamageManagerComponent)', 'FindDamage()').replace('FindComponent(SCR_InventoryStorageManagerComponent)', 'FindInventory()')
|
|
s=s.replace('DamageManagerComponent.Cast(', 'Damage::Cast(').replace('SCR_InventoryStorageManagerComponent.Cast(', 'Inventory::Cast(')
|
|
s=s.replace('purpose: EStoragePurpose.PURPOSE_DEPOSIT, cb: m_Grants->Operation', '1, m_Grants->Operation')
|
|
s=s.replace('String item;', 'String item;')
|
|
return s
|
|
binding=adapt(body(source,'void Bind('))
|
|
constructor=adapt(body(source,'void ERR_AdminPlayerTools(')).replace('void ERR_AdminPlayerTools(ERR_AdminGrantState grants = nullptr)', 'Tools(GrantState* grants = nullptr)')
|
|
methods='\n'.join(adapt(body(source,signature)) for signature in [
|
|
'protected IEntity CurrentBody(', 'protected ResourceName ItemPrefab(', 'protected void RestoreDamage(',
|
|
'bool Execute(', 'void Tick(', 'void Stop('])
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <string>
|
|
#include <vector>
|
|
struct String:std::string {
|
|
using std::string::string;String()=default;String(const std::string& s):std::string(s){}
|
|
String Trim(){auto a=find_first_not_of(" \t\n");return a==npos?String():String(substr(a,find_last_not_of(" \t\n")-a+1));}
|
|
void ToLower(){std::transform(begin(),end(),begin(),[](unsigned char c){return std::tolower(c);});}
|
|
bool StartsWith(const char* p){return rfind(p,0)==0;}bool IsEmpty(){return empty();}
|
|
int Length(){return size();}String Substring(int start,int count){return substr(start,count);}
|
|
};
|
|
template<class T>struct Array:std::vector<T>{void Insert(T v){this->push_back(v);}};
|
|
struct ERR_AdminItemCallback{bool Finished=false,Succeeded=false;};
|
|
struct Damage{bool enabled=true;int changes=0;bool IsDamageHandlingEnabled(){return enabled;}void EnableDamageHandling(bool value){enabled=value;changes++;}static Damage* Cast(Damage* value){return value;}};
|
|
using DamageManagerComponent=Damage*;
|
|
struct Inventory{bool accept=true;int submissions=0;String prefab;ERR_AdminItemCallback* callback=nullptr;static Inventory* Cast(Inventory* value){return value;}
|
|
bool TrySpawnPrefabToStorage(String p,int purpose,ERR_AdminItemCallback* cb){assert(purpose==1);submissions++;prefab=p;callback=cb;return accept;}
|
|
};using SCR_InventoryStorageManagerComponent=Inventory*;
|
|
struct Entity{bool alive=true;Damage damage;Inventory inventory;bool hasDamage=true,hasInventory=true;Damage* FindDamage(){return hasDamage?&damage:nullptr;}Inventory* FindInventory(){return hasInventory?&inventory:nullptr;}};using IEntity=Entity*;
|
|
struct Controller{int id=1;Entity* actor=nullptr;int GetPlayerId(){return id;}Entity* GetControlledEntity(){return actor;}};using SCR_PlayerController=Controller*;
|
|
struct Manager{Controller* controller=nullptr;Entity* actor=nullptr;Controller* GetPlayerController(int){return controller;}Entity* GetPlayerControlledEntity(int){return actor;}};using PlayerManager=Manager*;
|
|
struct Roster{Entity* actor=nullptr;bool CurrentActor(Entity* a){return actor==a;}};
|
|
struct Mode{bool active=true;Roster roster;bool hasRoster=true;static Mode* current;static Mode* Get(){return current;}bool Active(){return active;}Roster* Players(){return hasRoster?&roster:nullptr;}};Mode* Mode::current=nullptr;using ERR_GameMode=Mode*;
|
|
struct Game{float now=0;Manager manager;Manager* GetPlayerManager(){return &manager;}Game* GetWorld(){return this;}float GetWorldTime(){return now;}}game;Game* GetGame(){return &game;}
|
|
namespace Replication{bool server=true;bool IsServer(){return server;}}
|
|
namespace Global{bool admin=true;bool IsAdmin(int){return admin;}}
|
|
namespace Actors{bool Alive(Entity* a){return a&&a->alive;}}
|
|
struct GrantState{IEntity Actor=nullptr;ERR_AdminItemCallback* Operation=nullptr;String Item;float Deadline=0;bool TimedOut=false;
|
|
'''+binding+r'''
|
|
};
|
|
struct Tools{
|
|
ERR_GameMode m_Mode=nullptr;SCR_PlayerController m_Controller=nullptr;IEntity m_Actor=nullptr;DamageManagerComponent m_Damage=nullptr;
|
|
bool m_PreviousDamage=false,m_God=false,m_Stopped=false;GrantState* m_Grants=new GrantState();
|
|
'''+constructor+methods+r'''
|
|
};
|
|
struct Fixture{
|
|
Mode mode;Controller controller;Entity actor;Tools tools;Array<String> replies;
|
|
Fixture(){Mode::current=&mode;controller.actor=&actor;mode.roster.actor=&actor;game.manager.controller=&controller;game.manager.actor=&actor;game.now=0;Global::admin=true;Replication::server=true;}
|
|
bool command(String args){replies.clear();return tools.Execute(&mode,&controller,args,replies);}void tick(){replies.clear();tools.Tick(replies);}
|
|
};
|
|
int main(){
|
|
{Fixture f;assert(!f.command("mission time show")&&f.replies.empty());assert(f.command(" GOD SET ON ")&&!f.actor.damage.enabled);assert(f.tools.m_God);f.command("god");assert(f.actor.damage.changes==1);f.command("god show");assert(f.replies[0].find("on")!=String::npos);f.command("ungod");assert(f.actor.damage.enabled&&f.actor.damage.changes==2);f.tools.Stop();assert(f.actor.damage.changes==2);}
|
|
{Fixture f;f.actor.damage.enabled=false;f.command("god");f.command("god set off");assert(!f.actor.damage.enabled);}
|
|
{Fixture f;f.command("god");Global::admin=false;f.tick();assert(f.actor.damage.enabled&&f.tools.m_Stopped&&f.replies.empty());Global::admin=true;f.command("god");assert(f.actor.damage.enabled);}
|
|
{Fixture f;f.command("god");Entity replacement;f.controller.actor=&replacement;game.manager.actor=&replacement;f.mode.roster.actor=&replacement;f.tick();assert(f.actor.damage.enabled&&replacement.damage.enabled&&f.tools.m_Stopped);}
|
|
{Fixture f;f.command("god");f.mode.active=false;f.tick();assert(f.actor.damage.enabled&&f.tools.m_Stopped);}
|
|
{Fixture f;Global::admin=false;f.command("cheat map");assert(f.actor.inventory.submissions==0);}
|
|
{Fixture f;Controller impostor;game.manager.controller=&impostor;f.command("god");assert(f.actor.damage.enabled);}
|
|
{Fixture f;f.mode.roster.actor=nullptr;f.command("cheat map");assert(f.actor.inventory.submissions==0);}
|
|
{Fixture f;f.actor.alive=false;f.command("god");assert(f.actor.damage.enabled);}
|
|
{Fixture f;f.actor.hasDamage=false;f.command("god");assert(!f.tools.m_God);}
|
|
{Fixture f;f.actor.hasInventory=false;f.command("cheat map");assert(!f.tools.m_Grants->Operation);}
|
|
{Fixture f;f.command("help god");assert(f.replies[0].find("god show")!=String::npos);f.command("help cheat");assert(f.replies[0].find("PASGT")!=String::npos&&f.actor.inventory.submissions==0);f.command("god invalid");assert(f.actor.damage.enabled);f.command("cheat invalid");assert(f.actor.inventory.submissions==0);}
|
|
{Fixture f;f.command("cheat map");assert(f.actor.inventory.submissions==1&&f.tools.m_Grants->Operation);for(int i=0;i<5;i++){f.command("cheat helmet");f.tick();}assert(f.actor.inventory.submissions==1);f.actor.inventory.callback->Finished=true;f.actor.inventory.callback->Succeeded=true;f.tick();assert(!f.tools.m_Grants->Operation&&f.replies.size()==1);f.tick();assert(f.replies.empty());f.command("cheat uniform");assert(f.actor.inventory.submissions==2&&f.actor.inventory.prefab.find("Jacket_US_BDU")!=String::npos);}
|
|
{Fixture f;f.command("cheat helmet");assert(f.actor.inventory.prefab.find("Helmet_PASGT_01_base")!=String::npos);f.actor.inventory.callback->Finished=true;f.actor.inventory.callback->Succeeded=false;f.tick();assert(f.replies.size()==1&&f.replies[0].find("could not")!=String::npos&&!f.tools.m_Grants->Operation);}
|
|
{Fixture f;f.actor.inventory.accept=false;f.command("cheat map");assert(f.actor.inventory.submissions==1&&!f.tools.m_Grants->Operation);f.tick();assert(f.replies.empty());}
|
|
{Fixture f;f.command("cheat map");game.now=29999;f.tick();assert(!f.tools.m_Grants->TimedOut);game.now=30000;f.tick();assert(f.tools.m_Grants->TimedOut&&f.replies.size()==1);f.actor.inventory.callback->Finished=true;f.actor.inventory.callback->Succeeded=true;f.tick();assert(f.replies.empty());f.command("cheat map");assert(f.actor.inventory.submissions==1);f.command("god");assert(!f.actor.damage.enabled);}
|
|
{Fixture f;f.command("cheat map");auto callback=f.actor.inventory.callback;f.tools.Stop();callback->Finished=true;callback->Succeeded=true;f.tick();assert(f.replies.empty()&&f.actor.inventory.submissions==1);}
|
|
{Fixture f;f.command("god");f.actor.alive=false;f.tick();assert(f.actor.damage.enabled&&f.tools.m_Stopped);}
|
|
|
|
// Same physical body keeps accepted insertion state after revoke/regrant.
|
|
{Fixture f;f.command("god");f.command("cheat map");auto shared=f.tools.m_Grants;auto pending=shared->Operation;Global::admin=false;f.tick();assert(f.actor.damage.enabled&&f.tools.m_Stopped);Global::admin=true;Tools renewed(shared);renewed.Execute(&f.mode,&f.controller,"cheat map",f.replies);assert(f.actor.inventory.submissions==1&&shared->Operation==pending);pending->Finished=true;pending->Succeeded=true;renewed.Tick(f.replies);assert(!shared->Operation);renewed.Execute(&f.mode,&f.controller,"cheat map",f.replies);assert(f.actor.inventory.submissions==2);}
|
|
// Timeout quarantine survives fresh sessions even after a late callback.
|
|
{Fixture f;f.actor.damage.enabled=false;f.command("god");f.command("cheat map");game.now=30000;f.tick();auto shared=f.tools.m_Grants;Global::admin=false;f.tick();assert(!f.actor.damage.enabled);Global::admin=true;shared->Operation->Finished=true;shared->Operation->Succeeded=true;Tools renewed(shared);renewed.Execute(&f.mode,&f.controller,"cheat helmet",f.replies);renewed.Tick(f.replies);assert(f.actor.inventory.submissions==1&&shared->TimedOut);}
|
|
// A different canonical body gets a fresh grant state, never god mode.
|
|
{Fixture f;f.command("god");f.command("cheat map");auto shared=f.tools.m_Grants;auto oldCallback=shared->Operation;f.tools.Stop();Entity next;f.controller.actor=&next;game.manager.actor=&next;f.mode.roster.actor=&next;Tools renewed(shared);renewed.Execute(&f.mode,&f.controller,"cheat helmet",f.replies);assert(f.actor.damage.enabled&&next.damage.enabled&&next.inventory.submissions==1&&shared->Actor==&next&&shared->Operation!=oldCallback);oldCallback->Finished=true;oldCallback->Succeeded=true;f.replies.clear();renewed.Tick(f.replies);assert(f.replies.empty());}
|
|
}
|
|
''')
|
|
s=source.read_text()
|
|
assert 'PURPOSE_DEPOSIT' in s and 'PURPOSE_ANY' not in s
|
|
assert 'FullHeal' not in s and 'DeleteEntity' not in s and 'CallLater' not in s
|
|
assert 'protected override void OnComplete() { Finish(true); }' in s
|
|
assert 'protected override void OnFailed() { Finish(false); }' in s
|
|
assert 'protected void Finish(bool success) { if (!Finished)' in s
|
|
print('PASS: actual admin body authorization, god restoration, vanilla item deposit, callback completion/failure, timeout quarantine and stop/revocation paths')
|
|
|
|
bridge=(ROOT/'addons/core/Scripts/Game/ERR/ERR_PlayerAdminCommands.c').read_text()
|
|
session=(ROOT/'addons/core/Scripts/Game/ERR/ERR_AdminSession.c').read_text()
|
|
assert 'protected ref ERR_AdminGrantState m_ERRAdminGrants = new ERR_AdminGrantState();' in bridge
|
|
assert 'new ERR_AdminSession(mode, this, token, m_ERRAdminGrants)' in bridge
|
|
assert session.count('new ERR_AdminPlayerTools(m_Grants)')==2
|
|
assert 'm_Grants' not in body(source, 'void Stop(')
|
|
print('PASS: same-body revoke/regrant preserves pending insertions and timeout quarantine; new-body binding isolates late callbacks and never inherits god mode')
|