"""Actual native-end hook/shutdown/active bodies with engine boundaries substituted.""" from policy_harness import ROOT, body, run SOURCE=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_GameMode.c' def cpp(s): s=s.replace('protected override ', '').replace('protected ', '') for a,b in [('Replication.','Replication::'),('ERR_MissionRules.','Rules::'),('ERR_Phase.','Phase::'),('ERR_ObserverReason.','Observer::'),('SCR_AIGroup.','SCR_AIGroup::'),('ERR_Actors.','ERR_Actors::'),('super.OnGameModeEnd(', 'NativeEnd(')]:s=s.replace(a,b) s=s.replace('array connected = {};','std::vector connected;').replace('foreach (','for (') s=s.replace('SCR_PlayerController controller = SCR_PlayerController.Cast(', 'SCR_PlayerController* controller = SCR_PlayerController::Cast(').replace('controller.ERR_SetObserver(', 'controller->ERR_SetObserver(') s=s.replace('GetGame().','GetGame()->').replace('GetCallqueue().','GetCallqueue()->').replace('GetPlayerManager().','GetPlayerManager()->') return s methods='\n'.join(cpp(body(SOURCE, signature)) for signature in ('bool Active(', 'protected override void OnGameModeEnd(', 'protected void StopServices(')) startGuard=body(SOURCE,'protected void StartScenario(').split('if (!m_Factions',1)[0].replace('protected ','') run(r'''#include #include #include #include using ERR_Phase=int;using IEntity=int;using SCR_GameModeEndData=int; struct string:std::string {using std::string::string;using std::string::operator=;bool IsEmpty(){return empty();}}; namespace Phase {enum {PREPARING,PRISON,RADIO_SEARCH,RADIO_DEFENCE,EXTRACTION,VICTORY,DEFEAT};} namespace Observer {enum {NONE};} struct Replication {static bool server;static int bumps;static bool IsServer(){return server;}static void BumpMe(){bumps++;}}; bool Replication::server=true;int Replication::bumps=0; struct Rules {static bool Terminal(int phase){return phase==Phase::VICTORY||phase==Phase::DEFEAT;}}; struct Queue {std::vector removed;void Remove(int method){removed.push_back(method);}}; struct SCR_PlayerController {int releases=0;static SCR_PlayerController* Cast(SCR_PlayerController* p){return p;}void ERR_SetObserver(int){releases++;}}; struct Manager {SCR_PlayerController player;void GetPlayers(std::vector& ids){ids={1};}SCR_PlayerController* GetPlayerController(int){return &player;}}; struct Game {Queue queue;Manager manager;Queue* GetCallqueue(){return &queue;}Manager* GetPlayerManager(){return &manager;}} game; Game* GetGame(){return &game;} struct SCR_AIGroup {static bool Cast(int e){return e==10;}}; struct ERR_Actors {static int deleted;static void DeleteSpawned(int e){assert(e==10);deleted++;}};int ERR_Actors::deleted=0; struct Command {int stops=0;explicit operator bool()const{return true;}void Stop(){stops++;}}; struct PhaseEvent {int count=0,oldPhase=-1,newPhase=-1;std::function callback;void Invoke(int old,int next){count++;oldPhase=old;newPhase=next;if(callback)callback();}}; struct StopEvent {int count=0;std::function callback;void Invoke(){count++;if(callback)callback();}}; struct ForceBoundary {int clears=0;void Clear(){clears++;}}; struct Mode { ForceBoundary m_Forces,m_ForceAgents; int forceTicks=0;void TickForces(float){forceTicks++;} bool m_Stopped=false,m_NativeEndHandled=false; int m_Phase=Phase::PRISON,StartScenario=1,ServerTick=2,BindLocalObserver=3; string m_EndReason;string m_PreparationError; Command m_Command;PhaseEvent m_OnPhaseChanged;StopEvent m_OnStop; std::vector m_OwnedEntities={10,11}; int published=0,feedback=0,nativeEnds=0,receivedData=0,nativeWork=0,escapeSnapshot=77; bool RefreshRadioFeedback(){feedback++;return true;}void PublishEscapeResults(){published++;} void NativeEnd(int data){assert(!Replication::server||m_Stopped);nativeEnds++;receivedData=data;} '''+methods+r''' }; struct StartGuard {bool m_Stopped=false,m_NativeEndHandled=false;int m_Phase=Phase::PREPARING;string m_PreparationError;int nativeWork=0; '''+cpp(startGuard)+r'''nativeWork++;} }; int main(){ // Every unfinished phase closes before native listeners/restart, preserving data. for(int phase=Phase::PREPARING;phase<=Phase::EXTRACTION;phase++) { Mode m;m.m_Phase=phase;auto removed=game.queue.removed.size();int deleted=ERR_Actors::deleted; m.OnGameModeEnd(314); assert(m.m_Phase==Phase::DEFEAT&&m.m_Stopped&&m.m_NativeEndHandled&&!m.Active()); assert(m.m_OnPhaseChanged.count==1&&m.m_OnPhaseChanged.oldPhase==phase); assert(m.m_OnStop.count==1&&m.m_Command.stops==1&&ERR_Actors::deleted==deleted+1); assert(game.queue.removed.size()==removed+4&&game.queue.removed[removed]==m.BindLocalObserver&&game.queue.removed[removed+1]==m.StartScenario&&game.queue.removed[removed+2]==m.ServerTick&&game.queue.removed[removed+3]==m.BindLocalObserver); assert(m.nativeEnds==1&&m.receivedData==314&&m.escapeSnapshot==77&&m.published==1&&m.m_Forces.clears==1&&m.m_ForceAgents.clears==1); m.OnGameModeEnd(999);m.StopServices(); assert(m.nativeEnds==1&&m.receivedData==314&&m.m_OnStop.count==1&&m.m_OnPhaseChanged.count==1&&m.published==1); } // Existing successor terminal snapshots/reason are not replaced or republished. for(int terminal:{Phase::VICTORY,Phase::DEFEAT}) { Mode m;m.m_Phase=terminal;m.m_EndReason="original result";m.StopServices(); m.OnGameModeEnd(451);assert(m.m_Phase==terminal&&m.escapeSnapshot==77&&m.m_EndReason=="original result"); assert(m.m_OnPhaseChanged.count==0&&m.published==0&&m.m_OnStop.count==1&&m.nativeEnds==1&&m.receivedData==451); } // Reentrant end notification from successor listeners cannot schedule twice. Mode reentrant;reentrant.m_OnPhaseChanged.callback=[&](){reentrant.OnGameModeEnd(2);}; reentrant.m_OnStop.callback=[&](){reentrant.OnGameModeEnd(3);reentrant.StopServices();}; reentrant.OnGameModeEnd(1);assert(reentrant.nativeEnds==1&&reentrant.receivedData==1&&reentrant.m_OnStop.count==1); // Client closes local Active immediately, without mutating replicated phase/result. Replication::server=false;Mode client;client.OnGameModeEnd(44); assert(!client.Active()&&client.m_Phase==Phase::PRISON&&!client.m_Stopped&&client.published==0&&client.m_OnPhaseChanged.count==0&&client.nativeEnds==1); Replication::server=true; // An already dequeued preparation callback is independently inert after stop/end. for(int fault=0;fault<4;fault++){ StartGuard g;if(fault==0)g.m_Stopped=true;if(fault==1)g.m_NativeEndHandled=true;if(fault==2)g.m_Phase=Phase::DEFEAT;if(fault==3)g.m_PreparationError="failed"; g.StartScenario();assert(g.nativeWork==0); } StartGuard normal;normal.StartScenario();assert(normal.nativeWork==1); } ''') source=SOURCE.read_text();hook=body(SOURCE,'protected override void OnGameModeEnd(') assert hook.count('super.OnGameModeEnd(endData)')==1 assert hook.index('StopServices()') < hook.index('super.OnGameModeEnd(endData)') assert 'Transition(' not in hook and 'EndGameMode(' not in hook assert 'm_Extraction.' not in hook and 'EndGameData.Create' not in hook assert 'if (!m_NativeEndHandled) EndGameMode(' in body(SOURCE,'bool Transition(') probe=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_NativeEndProbe.c').read_text() assert probe.startswith('#ifdef WORKBENCH') assert probe.count('ERR SMOKE COMPLETE:')==1 and probe.count('EndGameMode(m_ERREndData)')==1 assert 'super.OnGameModeEnd' not in probe assert 'm_Phase = ERR_Phase.' not in probe assert 'GetEndGameData() == m_ERREndData' in probe assert 'StartScenario(); ServerTick(); StopServices();' in probe assert 'erri-native-end-round.json' in probe print('PASS: actual native-end shutdown, existing terminal snapshots, reentrancy, unchanged native end data and stale preparation guards') bind=body(SOURCE,'protected void BindLocalObserver(') assert bind.index('m_Stopped || m_NativeEndHandled') < bind.index('controller.ERR_StartObserverMonitor()') assert hook.index('Remove(BindLocalObserver)') < hook.index('if (Replication.IsServer())')