Files
EscapistsReintroduced/tests/test_local_admission.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

90 lines
6.5 KiB
Python

"""Execute actual resolver/context/latch bodies with opaque native handles.
UUID generation/nullness and native session/controller APIs are substituted;
this is not authentication, native GenV4 or multiplayer evidence.
"""
from policy_harness import ROOT, body, run
import re
source=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_LocalAdmission.c'
s=source.read_text().split('\nmodded class ERR_GameMode')[0]
s=s[s.index('class ERR_LocalAdmissionRules'):]
s=s.replace('class ERR_LocalAdmissionRules','struct ERR_LocalAdmissionRules').replace('class ERR_LocalAdmission\n','struct ERR_LocalAdmission\n')
s=s.replace('\n}\n','\n};\n').replace('protected ','')
s=s.replace('void ERR_LocalAdmission(', 'ERR_LocalAdmission(')
s=s.replace('ERR_GameMode', 'Mode').replace('SCR_PlayerController', 'Controller').replace('PlayerManager', 'Manager')
s=re.sub(r'\b(Mode|Controller|Manager) (mode|controller|manager|m_Mode|m_Controller)\b',r'\1* \2',s)
s=s.replace('Mode.Get()', 'Mode::Get()').replace('RplMode.None','0')
s=s.replace('Replication.IsServer()', 'isServer').replace('RplSession.Mode()', 'sessionMode')
s=s.replace('GetGame().', 'GetGame()->').replace('manager.', 'manager->').replace('controller.', 'controller->')
s=s.replace('System.GetCLIParam', 'System::GetCLIParam').replace('string project;', 'std::string project;')
s=s.replace('bool connected, sameManager;', 'bool connected=false, sameManager=false;').replace('int controllerId;', 'int controllerId=0;')
s=s.replace('ERR_LocalAdmissionRules.', 'ERR_LocalAdmissionRules::')
s=s.replace('UUID.NULL_UUID','UUID::NULL_UUID').replace('UUID.GenV4()', 'UUID::GenV4()').replace('null','nullptr')
# Enforce initializes managed fields; retain that contract in this C++ model.
s=s.replace('Mode* m_Mode;', 'Mode* m_Mode=nullptr;').replace('Controller* m_Controller;', 'Controller* m_Controller=nullptr;')
s=re.sub(r'\b(bool|int) (m_\w+);',r'\1 \2=0;',s)
run('''#include <cassert>
#include <string>
int generated=0;bool isServer=true;int sessionMode=0;
struct UUID {int value=0;bool IsNull(){return value<=0;}bool operator==(UUID other){return value==other.value;}static UUID NULL_UUID;static UUID GenV4(){return UUID{++generated+100};}};
UUID UUID::NULL_UUID{0};
struct Controller {int id=1;int GetPlayerId(){return id;}};
struct Manager {int count=1;bool connected=true;Controller* controller=nullptr;int GetPlayerCount(){return count;}bool IsPlayerConnected(int id){return connected;}Controller* GetPlayerController(int id){return controller;}};
struct Mode {static Mode* current;static Mode* Get(){return current;}};Mode* Mode::current=nullptr;
struct Game {Manager* manager=nullptr;Controller* local=nullptr;Manager* GetManager(){return manager;}Controller* GetPlayerController(){return local;}};
Game* game=nullptr;Game* GetGame(){return game;}void Print(const char*){}
namespace System {bool GetCLIParam(const char*,std::string&){return false;}}
template<class... Args> void PrintFormat(Args... args) {}
'''+s+'''
int main(){
// Exact None only; every context condition independently matters.
for(int mode=-1;mode<5;++mode)for(int count=0;count<3;++count)for(int flags=0;flags<16;++flags)
assert(ERR_LocalAdmissionRules::Context(flags&1,mode,count,flags&2,flags&4,flags&8,1,1)==(mode==0&&count==1&&flags==15));
assert(!ERR_LocalAdmissionRules::Context(true,0,1,true,true,true,0,0));
assert(!ERR_LocalAdmissionRules::Context(true,0,1,true,true,true,1,2));
Mode round,nextRound;Mode::current=&round;Controller owner,replacement;Manager manager;manager.controller=&owner;Game world{&manager,&owner};game=&world;
ERR_LocalAdmission admission(&round);
UUID selected=admission.Resolve(&round,1,&owner,UUID{0});assert(!selected.IsNull()&&admission.LocalKey()&&generated==1);
assert(admission.Resolve(&round,1,&owner,UUID{0})==selected&&generated==1);
assert(admission.Resolve(&round,1,&owner,UUID{55})==selected&&generated==1); // late BI arrival cannot make fresh life
assert(admission.Validate());
assert(!admission.Disconnected(2));assert(admission.Validate());
assert(admission.Disconnected(1));assert(!admission.Validate());
assert(admission.Resolve(&round,1,&owner,UUID{55}).IsNull()); // irreversible within this mode
ERR_LocalAdmission native(&round);assert(native.Resolve(&round,1,&owner,UUID{55})==UUID{55});
assert(!native.LocalKey()&&native.Resolve(&round,1,&owner,UUID{0})==UUID{55}&&generated==1);
native.Stop();assert(native.Resolve(&round,1,&owner,UUID{0}).IsNull());assert(!native.Validate());
// Replacement, mode switch, manager/local mismatch and additional connections latch.
for(int fault=0;fault<6;++fault){
sessionMode=0;Mode::current=&round;manager.controller=&owner;world.local=&owner;manager.count=1;manager.connected=true;
ERR_LocalAdmission a(&round);assert(!a.Resolve(&round,1,&owner,UUID{0}).IsNull());
if(fault==0)sessionMode=1;
if(fault==1)manager.controller=&replacement;
if(fault==2)world.local=&replacement;
if(fault==3)manager.count=2;
if(fault==4)manager.connected=false;
if(fault==5)Mode::current=&nextRound;
assert(!a.Validate());
sessionMode=0;Mode::current=&round;manager.controller=&owner;world.local=&owner;manager.count=1;manager.connected=true;
assert(!a.Validate());assert(a.Resolve(&round,1,&owner,UUID{88}).IsNull());
}
// A newly created round owns a fresh key. Old resolver cannot be reused.
Mode::current=&nextRound;ERR_LocalAdmission next(&nextRound);UUID nextKey=next.Resolve(&nextRound,1,&owner,UUID{0});assert(!(nextKey==selected));
assert(admission.Resolve(&nextRound,1,&owner,UUID{0}).IsNull());
// Fresh network modes remain native even on an absent controller/local world,
// after Stop, on null backend data, and with changing native identities.
for(int mode: {1,2,3}){
sessionMode=mode;int before=generated;ERR_LocalAdmission network(&nextRound);game=nullptr;
assert(network.Resolve(&nextRound,9,nullptr,UUID{41})==UUID{41});
assert(network.Resolve(&nextRound,9,nullptr,UUID{0}).IsNull());
network.Stop();assert(network.Validate());
assert(network.Resolve(&nextRound,9,nullptr,UUID{42})==UUID{42});
assert(!network.Selected()&&!network.LocalKey()&&generated==before);game=&world;
}
// Incomplete local context cannot generate a key and cannot later self-repair.
sessionMode=0;manager.count=0;int before=generated;ERR_LocalAdmission invalid(&nextRound);
assert(invalid.Resolve(&nextRound,1,&owner,UUID{0}).IsNull()&&generated==before);
manager.count=1;assert(invalid.Resolve(&nextRound,1,&owner,UUID{0}).IsNull());
}
''')
print('PASS: actual local admission context/selection/latch, same-round late identity, replacement/mode failure, fresh round and untouched network path')