Files
EscapistsReintroduced/tests/test_intel_reroll.py
T
jakub cab998fb64 Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
2026-09-20 16:42:52 +02:00

98 lines
13 KiB
Python

"""Execute actual Intel reroll methods against adversarial async inventory adapters."""
import re
from policy_harness import ROOT, body, run
service=ROOT/'addons/core/Scripts/Game/ERR/20_Intel/ERR_IntelService.c'
batch=ROOT/'addons/core/Scripts/Game/ERR/20_Intel/ERR_IntelReroll.c'
def adapt(text,cls):
text=text.replace('protected ', '').replace('null','nullptr')
text=re.sub(r'array<([^>]+)> (\w+) = \{\};',r'Array<\1> \2;',text)
text=text.replace('set<IEntity> cohort = new set<IEntity>();','Set<IEntity> cohort;')
text=re.sub(r'foreach \((\w+) (\w+) : ([^)]+)\)',r'for (\1 \2 : \3)',text)
for n in ('Replication','ERR_Actors','Math','ERR_Resources','ERR_DiscoveryService','SCR_InventoryStorageManagerComponent','InventoryItemComponent'):
text=text.replace(n+'.',n+'::')
text=text.replace('SCR_InventoryStorageManagerComponent inventory','Inventory* inventory').replace('InventoryItemComponent item','Item* item')
for n in ('m_Mode','m_Service','m_Reroll','entry','request','inventory','actor','group','member','known','folder','item'):
text=text.replace(n+'.',n+'->')
text=text.replace('Actor.FindComponent(', 'Actor->FindComponent(').replace('Removing.FindComponent(', 'Removing->FindComponent(').replace('Removing.GetParent()', 'Removing->GetParent()')
text=text.replace('Settings().','Settings()->').replace('Discovery().','Discovery()->')
text=text.replace('FindComponent(SCR_InventoryStorageManagerComponent)','FindComponent(INVENTORY)').replace('FindComponent(InventoryItemComponent)','FindComponent(ITEM)').replace('FindComponent(ERR_DocumentComponent)','FindComponent(DOCUMENT)')
text=text.replace('purpose: EStoragePurpose.PURPOSE_ANY','0').replace('cb: ', '')
text=text.replace('Operation.', 'Operation->')
text=text.replace('new ERR_IntelRerollEntry()', 'new Entry()').replace('new ERR_IntelReroll(', 'new Batch(').replace('new ERR_IntelRequest()', 'new Request()').replace('new ERR_IntelInsertCallback()', 'new Callback()')
text=re.sub(r'^.*Print\([^\n]*$', ';',text,flags=re.M)
text=re.sub(r'^(\w+) (\w+)\(',lambda m:m[1]+' '+cls+'::'+m[2]+'(',text)
# Defaults belong only to declarations in C++.
text=text.replace('bool failed = false, bool skipped = false','bool failed, bool skipped')
return text
service_methods=[('void RegisterGroup(', 'void RegisterGroup(SCR_AIGroup group);'),('void Tick(', 'void Tick(float now);'),('bool BeginReroll(', 'bool BeginReroll();'),('bool HasPendingInsertion(', 'bool HasPendingInsertion(IEntity actor);'),('void Unsettled(', 'void Unsettled(IEntity actor);'),('bool Settled(', 'bool Settled(IEntity actor);'),('void Stop(', 'void Stop();')]
batch_methods=[('void Add(', 'void Add(IEntity actor);'),('bool Running(', 'bool Running();'),('void Tick(', 'void Tick();'),('protected void Finish(', 'void Finish(ERR_IntelRerollEntry entry,bool failed=false,bool skipped=false);'),('protected void Fail(', 'void Fail(ERR_IntelRerollEntry entry,string reason);'),('protected void Process(', 'void Process(ERR_IntelRerollEntry entry);'),('void Stop(', 'void Stop();')]
code=r'''
#include <cassert>
#include <algorithm>
#include <vector>
#include <set>
#include <string>
using string=std::string;
template<class T> struct Array:std::vector<T>{int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T a){this->push_back(a);}void Remove(int i){this->erase(this->begin()+i);}void Clear(){this->clear();}bool Contains(T a){return std::find(this->begin(),this->end(),a)!=this->end();}};
template<class T> struct Set:std::set<T>{bool Contains(T a){return this->count(a);}void Insert(T a){this->insert(a);}void RemoveItem(T a){this->erase(a);}void Clear(){this->clear();}};
namespace Replication{bool server=true;bool IsServer(){return server;}}
namespace Math{int rolls=0;int RandomInt(int,int){rolls++;return 0;}int Min(int a,int b){return std::min(a,b);}}
const int INVENTORY=1,ITEM=2,DOCUMENT=3;
struct Actor;using IEntity=Actor*;
struct Item{bool attached=false;bool GetParentSlot(){return attached;}static Item* Cast(void* p){return (Item*)p;}};using InventoryItemComponent=Item;
struct Callback{bool Finished=false,Succeeded=false;};
struct Inventory{Array<IEntity> folders;int spawns=0,removes=0;bool acceptSpawn=true,acceptRemove=true;bool TrySpawnPrefabToStorage(int,int,Callback*){spawns++;return acceptSpawn;}bool TryRemoveItemFromInventory(IEntity){removes++;return acceptRemove;}static Inventory* Cast(void* p){return (Inventory*)p;}};using SCR_InventoryStorageManagerComponent=Inventory;
struct Actor{bool alive=true,player=false,document=false,parent=false,hasInventory=true;Item item;Inventory inv;void* FindComponent(int kind){if(kind==INVENTORY)return hasInventory?&inv:nullptr;if(kind==ITEM)return &item;return document?this:nullptr;}bool GetParent(){return parent;}};
struct Agent{IEntity actor;IEntity GetControlledEntity(){return actor;}};using AIAgent=Agent*;
struct Group{Array<AIAgent> agents;void GetAgents(Array<AIAgent>& out){out=agents;}};using SCR_AIGroup=Group*;
namespace ERR_Actors{bool Alive(IEntity a){return a&&a->alive;}bool Player(IEntity a){return a&&a->player;}}
namespace ERR_Resources{int IntelFolder(){return 1;}}
struct Discovery{Set<IEntity> spent;bool capacity=true;static void Folders(Inventory* i,Array<IEntity>& out){out=i->folders;}bool IsSpent(IEntity a){return spent.Contains(a);}bool CanMarkSpent(IEntity a){return capacity;}bool MarkSpent(IEntity a){if(!capacity)return false;spent.Insert(a);return true;}};using ERR_DiscoveryService=Discovery;
struct Settings{int chance=100;int Get(const char*){return chance;}};
struct Mode{bool active=true;struct Settings settings;struct Discovery discovery;Set<IEntity> owned;bool Active(){return active;}struct Settings* Settings(){return &settings;}struct Discovery* Discovery(){return &discovery;}void Own(IEntity a){owned.Insert(a);}};
struct Entry{string WaitingFor="inventory";Callback* Operation=nullptr;IEntity Actor=nullptr,Removing=nullptr;int Attempts=0;bool GiveIntel=false,Inserting=false,Accepted=false,Done=false;};using ERR_IntelRerollEntry=Entry*;
struct Request{Callback* Operation=nullptr;IEntity Actor=nullptr;int Attempts=0;bool Accepted=false;};using ERR_IntelRequest=Request*;
struct Service;struct Batch;
struct Batch{Mode* m_Mode;Service* m_Service;Array<ERR_IntelRerollEntry> m_Entries;int m_Percent=0,m_Next=0,m_Completed=0,m_Failed=0,m_Skipped=0;bool m_Cancelled=false;Batch(Mode* m,Service* s,int p):m_Mode(m),m_Service(s),m_Percent(p){}
'''+ '\n'.join(x[1] for x in batch_methods)+r'''
};
struct Service{Mode* m_Mode;Array<ERR_IntelRequest> m_Requests;Set<IEntity> m_Rolled,m_Unsettled;Batch* m_Reroll=nullptr;bool m_Stopped=false;Service(Mode* m):m_Mode(m){}
'''+ '\n'.join(x[1] for x in service_methods)+r'''
};
'''+ '\n'.join(adapt(body(service,x[0]),'Service') for x in service_methods)+ '\n'.join(adapt(body(batch,x[0]),'Batch') for x in batch_methods)+r'''
void Add(Service& s,Actor& a){s.m_Rolled.Insert(&a);}
void Drain(Service& s,int n=70){for(int i=0;i<n;i++)s.Tick(i);}
int main(){
// Authority, terminal/stopped rounds, snapshot chance/cohort and duplicate guard.
Mode m;Service s(&m);Actor a,late;Add(s,a);Replication::server=false;assert(!s.BeginReroll());Replication::server=true;m.active=false;assert(!s.BeginReroll());m.active=true;m.settings.chance=0;assert(s.BeginReroll());assert(!s.BeginReroll());m.settings.chance=100;Add(s,late);Drain(s);assert(s.m_Reroll->m_Entries.Count()==1&&s.m_Reroll->m_Percent==0&&a.inv.spawns==0&&late.inv.spawns==0&&s.m_Reroll->m_Completed==1);
// Old unaccepted work cancelled, accepted native spawn retained and observed.
Mode pendingMode;Service pending(&pendingMode);Actor p,folder;Add(pending,p);auto req=new Request();req->Actor=&p;pending.m_Requests.Insert(req);pendingMode.settings.chance=0;assert(pending.BeginReroll());assert(pending.m_Requests.IsEmpty());Drain(pending);assert(p.inv.spawns==0);
auto accepted=new Request();accepted->Actor=&p;accepted->Accepted=true;accepted->Operation=new Callback();pending.m_Requests.Insert(accepted);assert(pending.BeginReroll());pending.Tick(0);assert(pending.m_Requests.Count()==1&&p.inv.removes==0&&p.inv.spawns==0);p.inv.folders.Insert(&folder);folder.item.attached=true;pending.Tick(1);assert(pending.m_Requests.Count()==1&&p.inv.removes==0);accepted->Operation->Finished=true;accepted->Operation->Succeeded=true;pending.Tick(1);assert(pending.m_Requests.IsEmpty()&&p.inv.removes==1&&pendingMode.discovery.IsSpent(&folder)&&pendingMode.owned.Contains(&folder));pending.Tick(2);assert(p.inv.removes==1);p.inv.folders.Clear();folder.item.attached=false;pending.Tick(3);assert(!pending.m_Reroll->Running()&&p.inv.spawns==0);
// Accepted insertion never repeated. A vanished/late native insertion blocks rerolls.
Mode delayedMode;Service delayed(&delayedMode);Actor d;Add(delayed,d);assert(delayed.BeginReroll());Drain(delayed);assert(d.inv.spawns==1&&delayed.m_Reroll->m_Failed==1&&delayed.m_Unsettled.Contains(&d));assert(delayed.BeginReroll());Drain(delayed);assert(d.inv.spawns==1&&delayed.m_Reroll->m_Failed==1);Actor arrived;d.inv.folders.Insert(&arrived);assert(delayed.BeginReroll());delayed.Tick(0);assert(d.inv.removes==0&&delayed.m_Unsettled.Contains(&d));
// Arbitrary transferred folder never acknowledges a pending reroll insertion.
Mode extraMode;Service extra(&extraMode);Actor e,unrelated;Add(extra,e);assert(extra.BeginReroll());extra.Tick(0);assert(e.inv.spawns==1);e.inv.folders.Insert(&unrelated);extra.Tick(1);assert(extra.m_Reroll->Running()&&!extra.BeginReroll()&&e.inv.spawns==1);auto op=extra.m_Reroll->m_Entries[0]->Operation;op->Finished=true;op->Succeeded=true;extra.Tick(2);assert(!extra.m_Reroll->Running()&&extra.m_Reroll->m_Failed==0);
// A surviving collected/spent folder or targeted mission document is untouched.
Mode safeMode;Service safe(&safeMode);Actor collector,spent,mission,doc;Add(safe,collector);Add(safe,mission);collector.inv.folders.Insert(&spent);safeMode.discovery.spent.Insert(&spent);mission.inv.folders.Insert(&doc);doc.document=true;assert(safe.BeginReroll());Drain(safe);assert(safe.m_Reroll->m_Skipped==2&&collector.inv.removes==0&&mission.inv.removes==0&&safeMode.discovery.IsSpent(&spent));
// Newly player-controlled/dead cohort members skipped without touching inventory.
Mode skipMode;Service skip(&skipMode);Actor player,dead;Add(skip,player);Add(skip,dead);assert(skip.BeginReroll());player.player=true;dead.alive=false;Drain(skip);assert(skip.m_Reroll->m_Skipped==2&&player.inv.spawns==0&&dead.inv.spawns==0);
// Removal ownership race and spent capacity refusal never create replacement.
Mode raceMode;Service race(&raceMode);Actor ai,f;Add(race,ai);ai.inv.folders.Insert(&f);assert(race.BeginReroll());race.Tick(0);assert(ai.inv.removes==1);ai.inv.folders.Clear();f.item.attached=true;race.Tick(1);assert(race.m_Reroll->m_Skipped==1&&ai.inv.spawns==0);
Mode capMode;Service cap(&capMode);Actor ca,cf;Add(cap,ca);ca.inv.folders.Insert(&cf);capMode.discovery.capacity=false;assert(cap.BeginReroll());Drain(cap);assert(cap.m_Reroll->m_Failed==1&&ca.inv.removes==0&&ca.inv.spawns==0);
// Rejected native removal preserves usable unspent documents for a retry.
Mode rejectMode;Service reject(&rejectMode);Actor ra,rf;Add(reject,ra);ra.inv.folders.Insert(&rf);ra.inv.acceptRemove=false;rejectMode.settings.chance=0;assert(reject.BeginReroll());reject.Tick(0);assert(reject.m_Reroll->Running()&&reject.m_Reroll->m_Failed==0&&!rejectMode.discovery.IsSpent(&rf)&&!rejectMode.owned.Contains(&rf));ra.inv.acceptRemove=true;reject.Tick(1);assert(ra.inv.removes==2&&rejectMode.discovery.IsSpent(&rf)&&rejectMode.owned.Contains(&rf));ra.inv.folders.Clear();rf.item.attached=false;reject.Tick(2);assert(!reject.m_Reroll->Running()&&reject.m_Reroll->m_Failed==0);
// Persistent refusal exhausts exactly thirty native attempts without spending.
Mode refusalMode;Service refusal(&refusalMode);Actor pa,pf;Add(refusal,pa);pa.inv.folders.Insert(&pf);pa.inv.acceptRemove=false;assert(refusal.BeginReroll());Drain(refusal);assert(pa.inv.removes==30&&refusal.m_Reroll->m_Failed==1&&!refusalMode.discovery.IsSpent(&pf)&&!refusalMode.owned.Contains(&pf)&&refusal.m_Reroll->m_Entries[0]->WaitingFor=="removal-refused");
// Tick budget, bounded missing inventory, Stop prevents further work/restart.
Mode manyMode;Service many(&manyMode);Actor actors[20];for(auto& actor:actors){actor.hasInventory=false;Add(many,actor);}assert(many.BeginReroll());many.Tick(0);int attempts=0;for(auto entry:many.m_Reroll->m_Entries)attempts+=entry->Attempts;assert(attempts==8);Drain(many,100);assert(many.m_Reroll->m_Failed==20);many.Stop();assert(!many.BeginReroll()&&many.m_Rolled.empty()&&many.m_Requests.IsEmpty()&&many.m_Reroll->m_Cancelled);for(auto entry:many.m_Reroll->m_Entries)assert(!entry->Actor&&!entry->Removing);
}
'''
run(code)
source=batch.read_text()
assert 'DeleteEntity' not in source and 'CallLater' not in source
assert source.index('CanMarkSpent(folder)') < source.index('TryRemoveItemFromInventory(folder)') < source.index('Discovery().MarkSpent(folder)')
assert 'm_Mode.Own(folder)' in source
assert '%3/%4 AI processed, %5 failed, %6 skipped.' in source
assert 'ERR_IntelService Intel()' in service.read_text()
print('PASS: actual reroll snapshot, authority, cancellation, ownership, accepted operations, quarantine, bounded work and stop methods; no native runtime claim')