Files
EscapistsReintroduced/tests/test_admin_player_tools.py
T
jakub 1b1a8b5173 Add mission customization, HQ support and aggression-driven AI
Integrate faction and random settings menus, generated objectives, scouts, convoys, air support, mortar stations and body-intel recovery. Add identified-body aggression, finite hunter UAZ deployment, dedicated objective defence and maximum AI skill with separate reduced aim accuracy. Include policy tests, native probes, evidence and documentation.
2026-09-23 15:07:14 +02:00

118 lines
13 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/00_Core/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;').replace('string.Format(', 'String::Format(').replace('ERR_DebugAI.', 'DebugAI::').replace('ERR_DebugIntel.', 'DebugIntel::')
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){}
static String Format(String text,int n){text.replace(text.find("%1"),2,std::to_string(n));return text;}
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;}}
namespace DebugAI {int calls=0;int KillNearby(Mode* m,Entity* e){assert(m&&e);calls++;return 3;}}
namespace DebugIntel {int calls=0;bool success=true;bool SpawnSix(Mode* m,Entity* e){assert(m&&e);calls++;return success;}}
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;int before=DebugIntel::calls;f.command("cheat intel");assert(DebugIntel::calls==before+1&&f.replies[0].find("Spawned 6")!=String::npos&&f.actor.inventory.submissions==0);Global::admin=false;f.command("cheat intel");assert(DebugIntel::calls==before+1);}
{Fixture f;DebugIntel::success=false;f.command("cheat intel");assert(f.replies[0].find("Could not place")!=String::npos);DebugIntel::success=true;}
{Fixture f;int before=DebugAI::calls;f.command("killais");assert(DebugAI::calls==before+1&&f.replies[0].find("Killed 3")!=String::npos);Global::admin=false;f.command("killais");assert(DebugAI::calls==before+1);}
{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/00_Core/ERR_PlayerAdminCommands.c').read_text()
session=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/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')