59 lines
4.8 KiB
Python
59 lines
4.8 KiB
Python
"""Run actual companion dispatch and discovery ledger bodies; native RPC is separate."""
|
|
from policy_harness import ROOT, body, run
|
|
admin = ROOT / 'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminService.c'
|
|
provider = ROOT / 'addons/core/Scripts/Game/ERR/20_Intel/ERR_IntelAdminProvider.c'
|
|
discovery = ROOT / 'addons/core/Scripts/Game/ERR/00_Core/ERR_DiscoveryService.c'
|
|
execute = body(admin, 'void Execute(')
|
|
assert execute.index('if (!Replication.IsServer()') < execute.index('if (!authorized)') < execute.index('if (Extension(mode, arguments, replies))')
|
|
extension = body(provider, 'override bool Execute(')
|
|
extension = (extension.replace('override ', '').replace('bool Execute(', 'bool Extension(').replace('array<string>', 'Array<string>&')
|
|
.replace('ERR_GameMode mode', 'Mode* mode').replace('ERR_IntelService intel', 'Intel* intel')
|
|
.replace('ERR_HQ hq', 'HQ* hq').replace('ERR_HQ.Cast(', 'HQ::Cast(')
|
|
.replace('super.Extension(', 'Base::Extension(').replace('Math.Max(', 'std::max(')
|
|
.replace('mode.', 'mode->').replace('intel.', 'intel->').replace('hq.', 'hq->')
|
|
.replace('Settings().', 'Settings()->').replace('Discovery().', 'Discovery()->').replace('Journal().', 'Journal()->'))
|
|
ledger = '\n'.join(body(discovery, signature) for signature in ('bool IsSpent(', 'bool CanMarkSpent(', 'bool MarkSpent('))
|
|
ledger = ledger.replace('Replication.', 'Replication::')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <set>
|
|
using string=std::string;
|
|
using IEntity=int;
|
|
template<class T> struct Array:std::vector<T>{void Insert(T v){this->push_back(v);}bool IsEmpty(){return this->empty();}};
|
|
template<class T> struct Set:std::set<T>{bool Contains(T v){return this->count(v);}void Insert(T v){this->insert(v);}int Count(){return this->size();}};
|
|
namespace Replication { bool server=true; bool IsServer(){return server;} }
|
|
struct Number {int n;string ToString(){return std::to_string(n);}};
|
|
struct Settings {Number Get(string key){return {key=="aiIntelChancePercent"?60:(key=="showAllSites"?0:(key=="radioIntelSiteCount"?5:15))};}};
|
|
struct Intel {int starts=0;bool canStart=true;bool BeginReroll(){starts++;return canStart;}string RerollStatus(){return "batch status";}};
|
|
struct Journal {int Sequence(){return 0;}void Read(int,Array<string>&,int&) {}};
|
|
struct HQ {struct Journal journal;static HQ* Cast(HQ* h){return h;}string Status(){return "hq";}struct Journal* Journal(){return &journal;}};
|
|
struct Gate {string RadioIntelStatus(){return "radio progress";}};
|
|
struct Mode {Gate gate;Gate*Discovery(){return &gate;}struct Settings settings;struct Intel* intel=nullptr;struct HQ* hq=nullptr;struct Settings* Settings(){return &settings;}struct Intel* Intel(){return intel;}struct HQ* Command(){return hq;}};
|
|
struct Base {bool Extension(Mode*,string,Array<string>&){return false;}};
|
|
struct Admin:Base {
|
|
'''+extension+r'''
|
|
};
|
|
struct Ledger {Set<IEntity> m_Spent;
|
|
'''+ledger+r'''
|
|
};
|
|
int main(){
|
|
Admin admin;Mode mode;Array<string> replies;Intel intel;mode.intel=&intel;
|
|
assert(admin.Extension(&mode,"intel show",replies));assert(replies.size()==5&&replies[0]=="aiIntelChancePercent=60"&&replies[1]=="radioIntelSiteCount=5 (next round)"&&replies[2]=="radio progress"&&replies[3]=="showAllSites=0"&&replies[4]=="intelRevealChancePercent=15"&&intel.starts==0);
|
|
replies.clear();assert(admin.Extension(&mode,"intel reroll show",replies));assert(intel.starts==0&&replies.size()==1&&replies[0]=="batch status");
|
|
replies.clear();assert(admin.Extension(&mode,"intel reroll",replies));assert(intel.starts==1&&replies.size()==1);
|
|
intel.canStart=false;replies.clear();assert(admin.Extension(&mode,"intel reroll",replies));assert(intel.starts==2&&replies.size()==2&&replies[1]=="batch status");
|
|
mode.intel=nullptr;replies.clear();assert(admin.Extension(&mode,"intel reroll",replies));assert(replies.size()==1&&intel.starts==2);
|
|
for(string invalid:{"intel reroll all","intel reroll show extra","intel reroll set 50","intel showing","intel"}) {replies.clear();assert(!admin.Extension(&mode,invalid,replies));assert(replies.empty()&&intel.starts==2);}
|
|
Ledger ledger;assert(!ledger.CanMarkSpent(0)&&!ledger.MarkSpent(0)&&!ledger.IsSpent(0));
|
|
Replication::server=false;assert(!ledger.MarkSpent(1)&&ledger.m_Spent.empty());Replication::server=true;
|
|
assert(ledger.MarkSpent(1)&&ledger.MarkSpent(1)&&ledger.IsSpent(1)&&ledger.m_Spent.size()==1);
|
|
for(int i=2;i<=4096;i++)assert(ledger.MarkSpent(i));
|
|
assert(!ledger.CanMarkSpent(4097)&&ledger.CanMarkSpent(1)&&!ledger.MarkSpent(4097)&&!ledger.IsSpent(4097)&&ledger.MarkSpent(1)&&ledger.m_Spent.size()==4096);
|
|
Replication::server=false;assert(!ledger.MarkSpent(1)&&!ledger.MarkSpent(4097)&&ledger.m_Spent.size()==4096);
|
|
}
|
|
''')
|
|
print('PASS: actual Intel commands distinguish status/start/failure, preserve authorization gate and reject malformed requests; spent ledger is idempotent, server-only and bounded')
|