"""Actual admission-exhaustion/tick rules; engine population and services substituted.""" import re from policy_harness import ROOT, body, run MODE = ROOT/'addons/core/Scripts/Game/ERR/ERR_GameMode.c' SPAWN = ROOT/'addons/core/Scripts/Game/ERR/ERR_SpawnLogic.c' def cpp(s): s=s.replace('protected ', '').replace('Replication.IsServer()', 'server').replace('Replication.BumpMe()', '(void)0') s=s.replace('array connected = {};','std::vector connected;').replace('connected.IsEmpty()', 'connected.empty()') s=s.replace('foreach (int id : connected)', 'for (int id : connected)') s=s.replace('ERR_PlayerLife life', 'Life* life').replace('SCR_PlayerController controller', 'Controller* controller').replace('PlayerController controller', 'Controller* controller') s=s.replace('SCR_PlayerController.Cast(', 'Controller::Cast(').replace('ERR_Phase.DEFEAT','5') for v in ('m_Roster','controller','life','m_Command','m_Extraction','m_RadioDefence','m_Recovery','GetGame()','GetWorld()','GetPlayerManager()'): s=s.replace(v+'.', v+'->') return s methods='\n'.join(cpp(body(MODE,x)) for x in ('void InitialAdmissionStarted(', 'void InitialAdmissionExhausted(', 'protected bool InitialAdmissionsExhausted(', 'protected void ServerTick(')) run(r''' #include #include #include #include bool server=true; struct Controller {void* actor=nullptr;void* GetControlledEntity(){return actor;}static Controller* Cast(Controller* c){return c;}void ERR_UpdateObserverServer(){}}; struct Life {bool Connected=true,UsedLife=false;void* Actor=nullptr;}; struct Roster {std::map lives;bool survivor=false;Life* Get(int id){auto i=lives.find(id);return i==lives.end()?nullptr:&i->second;}bool HasSurvivor(){return survivor;}}; struct Players {std::map connected;void GetPlayers(std::vector& v){for(auto p:connected)v.push_back(p.first);}int GetPlayerCount(){return connected.size();}Controller* GetPlayerController(int id){auto i=connected.find(id);return i==connected.end()?nullptr:i->second;}}; struct Game {Players players;Players* GetPlayerManager(){return &players;}} game;Game* GetGame(){return &game;} struct World {float now=0;float GetWorldTime(){return now;}} world; struct Service {void Tick(float=0){}void Maintain(float){}void Invoke(float){}}; struct Exhausted {std::map values;void Remove(int id){values.erase(id);}void Set(int id,Controller* c){values[id]=c;}bool Contains(int id){return values.count(id);}Controller* Get(int id){return values.at(id);}}; struct Mode { void TickForces(float){} bool m_StartedPlayers=false;int m_Phase=1;float m_NoSurvivorsSince=-1;std::string m_EndReason; Exhausted m_ExhaustedInitialAdmissions;Roster roster;Roster* m_Roster=&roster; Service service,m_OnTick;Service *m_Extraction=&service,*m_RadioDefence=&service,*m_Recovery=&service,*m_Command=&service; World* GetWorld(){return &world;}bool Active(){return m_Phase>0&&m_Phase<5;}void Transition(int p){m_Phase=p;}bool RefreshRadioFeedback(){return false;} '''+methods+r''' }; struct Fixture {Mode m;Controller first,second;Fixture(){server=true;world.now=0;game.players.connected={{1,&first}};m.roster.lives[1]=Life();}void tick(float t){world.now=t;m.ServerTick();}void exhaust(){m.InitialAdmissionExhausted(1,&first);}}; int main(){ // Sole exhausted first admission reaches existing grace, without a life/body. {Fixture f;f.exhaust();f.tick(0);f.tick(29999);assert(f.m.Active());f.tick(30000); assert(f.m.m_Phase==5&&!f.m.m_StartedPlayers&&!f.m.roster.lives[1].UsedLife); assert(f.m.m_EndReason=="No prisoners could be admitted to this round.");} // Not-yet-audited arrivals block the timer before they have any roster entry. {Fixture f;f.exhaust();f.tick(0);game.players.connected[2]=&f.second;f.tick(29000); assert(f.m.m_NoSurvivorsSince==-1);f.m.roster.lives[2]=Life();f.tick(60000);assert(f.m.Active()); f.m.InitialAdmissionExhausted(2,&f.second);f.tick(61000);f.tick(90999);assert(f.m.Active());f.tick(91000);assert(!f.m.Active());} // A retry/pending new attempt clears exhaustion immediately, not one tick later. {Fixture f;f.exhaust();f.tick(0);f.m.InitialAdmissionStarted(1);assert(f.m.m_NoSurvivorsSince==-1); f.tick(90000);assert(f.m.Active()&&!f.m.InitialAdmissionsExhausted()); f.exhaust();f.tick(91000);f.tick(120999);assert(f.m.Active());f.tick(121000);assert(!f.m.Active());} // Empty population and disconnect cleanup erase the initial failure grace. {Fixture f;f.exhaust();f.tick(0);game.players.connected.clear();f.tick(30000);assert(f.m.m_NoSurvivorsSince==-1&&f.m.Active()); f.m.InitialAdmissionStarted(1);game.players.connected[1]=&f.first;f.tick(60000);assert(f.m.Active());} // Recycled connection/controller, live body and invalid roster never inherit it. for(int fault=0;fault<5;fault++){Fixture f;f.exhaust();f.tick(0); if(fault==0)game.players.connected[1]=&f.second; if(fault==1)f.first.actor=&f.first; if(fault==2)f.m.roster.lives[1].UsedLife=true; if(fault==3)f.m.roster.lives[1].Actor=&f.first; if(fault==4)f.m.roster.lives[1].Connected=false; f.tick(30000);assert(f.m.Active()&&f.m.m_NoSurvivorsSince==-1);} // Invalid phase, authority, prior participation or controller cannot record failure. for(int fault=0;fault<6;fault++){Fixture f; if(fault==0){server=false;}if(fault==1){f.m.m_Phase=0;}if(fault==2){f.m.m_Phase=5;} if(fault==3){f.m.m_StartedPlayers=true;}if(fault==4){game.players.connected[1]=&f.second;}if(fault==5){f.m.roster.lives.clear();} f.exhaust();assert(f.m.m_ExhaustedInitialAdmissions.values.empty());} // Existing actual first spawn changes participation; normal death grace is unchanged. {Fixture f;f.exhaust();f.m.InitialAdmissionStarted(1);f.m.m_StartedPlayers=true;f.m.roster.lives[1].UsedLife=true;f.m.roster.survivor=true; f.tick(30000);assert(f.m.m_NoSurvivorsSince==-1&&f.m.Active());f.m.roster.survivor=false;f.tick(31000);f.tick(61000); assert(f.m.m_Phase==5&&f.m.m_EndReason=="No surviving prisoners remain.");} } ''') # Tie the tested state seam to actual budget exhaustion and new/disconnected admission. do_spawn=body(SPAWN,'protected override void DoSpawn_S(') assert do_spawn.index('Observe(playerId, ERR_ObserverReason.SPAWN_UNAVAILABLE);\n mode.InitialAdmissionExhausted')>do_spawn.index('if (attempts >= 30)') audit=body(SPAWN,'override void OnPlayerAuditSuccess_S(') assert audit.index('mode.InitialAdmissionStarted(playerId);')