"""Execute the saved-terrain write/recovery rules with failing filesystem operations. Native discovery, transitions and multiplayer are separate engine checks. """ from policy_harness import ROOT, body, run core = ROOT/'addons/core/Scripts/Game/ERR/00_Core' key = body(core/'ERR_TerrainCatalog.c', 'static string Key(') key = key.replace('"0123456789abcdef".IndexOf', 'string("0123456789abcdef").IndexOf') pref = core/'ERR_TerrainPreference.c' code = '\n'.join(body(pref, s) for s in ['void ERR_TerrainPreference(', 'ResourceName Scenario(', 'protected bool Read(', 'static bool Parse(', 'bool Save(']) for a,b in [('void ERR_TerrainPreference(', 'Preference('), ('protected ', ''), ('out ResourceName ', 'ResourceName& '), ('Replication.', 'Replication::'), ('ERR_TerrainCatalog.', 'Catalog::'), ('FileIO.', 'FileIO::'), ('LogLevel.', 'LogLevel::'), ('JsonLoadContext json = new JsonLoadContext();', 'JsonLoadContext json;'), ('JsonSaveContext json = new JsonSaveContext();', 'JsonSaveContext json;')]: code = code.replace(a,b) code = code.replace('ERR_ProfileConfig.', 'ERR_ProfileConfig::').replace('m_Path == PATH', 'm_Path == \"$profile:EscapistsTerrain.json\"') run(r''' #include #include #include #include struct string:std::string { using std::string::string; string(std::string s):std::string(s){} bool IsEmpty()const{return empty();} int Length()const{return size();} string Substring(int a,int b)const{return substr(a,b);} int IndexOf(string s)const{auto p=find(s);return p==npos?-1:int(p);} void ToLower(){std::transform(begin(),end(),begin(),[](unsigned char c){return std::tolower(c);});} }; using ResourceName=string; namespace Replication {bool server=true;bool IsServer(){return server;}} namespace LogLevel {int ERROR=1;} void Print(string,int){} std::map files; string failWrite,failCopy,corruptCopy; struct JsonLoadContext { string path; bool LoadFromFile(string p){path=p;return files.count(p)&&files[p]!="CORRUPT";} bool ReadValue(string,ResourceName& v){v=files[path];return true;} }; struct JsonSaveContext { string value; void EnableTypeDiscriminator(bool){} void WriteValue(string,string v){value=v;} bool SaveToFile(string p){if(p==failWrite)return false;files[p]=value;return true;} }; namespace FileIO { bool FileExists(string p){return files.count(p);} bool CopyFile(string a,string b){if(b==failCopy)return false;if(!files.count(a))return false;files[b]=b==corruptCopy?"CORRUPT":files[a];return true;} void DeleteFile(string p){files.erase(p);} } // Isolated custom paths retain standalone transactions; unified default paths // are covered by the central store regression and native fixture. namespace ERR_ProfileConfig { bool Load(string path,JsonLoadContext& json){return json.LoadFromFile(path);} bool Exists(string path){return FileIO::FileExists(path);} bool Save(string,JsonSaveContext&){assert(false);return false;} } struct Catalog {''' + key + r'''}; const string PATH="terrain.json"; struct Preference { string m_Path;ResourceName m_Scenario;bool m_ReadOnly=false; ''' + code + r''' }; int main(){ const string everon="{C1E20667DCE324ED}Missions/ERR_Everon.conf"; const string nogova="{469880DFE1B48F95}Missions/Nogova_TerrainTest.conf"; assert(Catalog::Key(everon)=="c1e20667dce324ed"); for(string bad:{"", "../outside.conf", "{ZZZZZZZZZZZZZZZZ}Missions/x.conf", "{C1E20667DCE324E}x"})assert(Catalog::Key(bad).empty()); Preference fresh;assert(fresh.Scenario().empty()&&files.empty()); assert(fresh.Save(everon));Preference restart;assert(restart.Scenario()==everon); assert(!restart.Save("../outside.conf")&&restart.Scenario()==everon); assert(restart.Save(nogova));assert(files[PATH+".bak"]==everon); Preference again;assert(again.Scenario()==nogova); // Temp-write, backup-copy and final-copy failure must not change the selected map. failWrite=PATH+".tmp";assert(!again.Save(everon)&&again.Scenario()==nogova&&files[PATH]==nogova);failWrite=""; failCopy=PATH+".bak";assert(!again.Save(everon)&&again.Scenario()==nogova&&files[PATH]==nogova);failCopy=""; failCopy=PATH;assert(!again.Save(everon)&&again.Scenario()==nogova&&files[PATH]==nogova);failCopy=""; // Readback detects a damaged final write; a retained backup permits recovery. corruptCopy=PATH;assert(!again.Save(everon)&&again.Scenario()==nogova);corruptCopy=""; Preference recovered;assert(recovered.Scenario()==nogova); assert(recovered.Save(""));Preference cleared;assert(cleared.Scenario().empty()); files[PATH]="CORRUPT";files[PATH+".bak"]="CORRUPT";Preference damaged; assert(damaged.Scenario().empty()&&!damaged.Save(everon)&&files[PATH]=="CORRUPT"); files.clear();failCopy=PATH;Preference firstFailure;assert(!firstFailure.Save(everon)&&!files.count(PATH));failCopy=""; Replication::server=false;Preference client;assert(!client.Save(everon)&&!files.count(PATH)); } ''') print('PASS: saved terrain GUIDs, defaults, restart persistence, clearing, backup recovery, corrupt-file preservation, write failures, and client write denial') prepare = body(core/'ERR_TerrainSelection.c', 'bool ERR_PrepareTerrainSelection(') for a,b in [('World world', 'World* world'), ('SCR_MissionHeader header', 'SCR_MissionHeader* header'), ('SCR_MissionHeader.Cast', 'SCR_MissionHeader::Cast'), ('header.', 'header->'), ('Replication.', 'Replication::'), ('SCR_Global.', 'SCR_Global::'), ('ERR_TerrainCatalog.', 'Catalog::'), ('LogLevel.', 'LogLevel::'), ('ERR_Terrains().', 'ERR_Terrains()->')]: prepare=prepare.replace(a,b) run(r''' #include #include struct string:std::string {using std::string::string;string(std::string s):std::string(s){}bool IsEmpty(){return empty();}}; using ResourceName=string; namespace Replication{bool server=true;bool IsServer(){return server;}} namespace SCR_Global{bool editor=false;bool IsEditMode(){return editor;}} namespace LogLevel{int ERROR=1;} void Print(string,int){} struct World{}; struct SCR_MissionHeader { ResourceName resource="nogova"; ResourceName GetHeaderResourceName(){return resource;} static SCR_MissionHeader* Cast(SCR_MissionHeader* p){return p;} }; struct Catalog{static string Key(ResourceName r){return r;}}; struct Selection{ResourceName destination;ResourceName Destination(){return destination;}}; struct Game{ World* m_ERRTerrainSelectionWorld=nullptr; ResourceName m_ERRTerrainDestination,m_ERRLastTerrainRedirect; float m_ERRTerrainSelectionWait=0; Selection selection;bool mode=true;SCR_MissionHeader* header=nullptr; Selection* ERR_Terrains(){return &selection;} bool GetGameMode(){return mode;}SCR_MissionHeader* GetMissionHeader(){return header;} ''' + prepare + r''' }; int main(){ World world;SCR_MissionHeader h;Game g;g.header=&h; // Empty or unavailable saved destination preserves the server-selected scenario. assert(!g.ERR_PrepareTerrainSelection(&world)); g.selection.destination="everon"; Replication::server=false;assert(!g.ERR_PrepareTerrainSelection(&world));Replication::server=true; SCR_Global::editor=true;assert(!g.ERR_PrepareTerrainSelection(&world));SCR_Global::editor=false; g.mode=false;assert(!g.ERR_PrepareTerrainSelection(&world));g.mode=true; g.header=nullptr;assert(!g.ERR_PrepareTerrainSelection(&world));g.header=&h; assert(g.ERR_PrepareTerrainSelection(&world)); assert(g.m_ERRTerrainDestination=="everon"&&g.m_ERRTerrainSelectionWorld==&world&&g.m_ERRTerrainSelectionWait==0); // Failing to reach a requested destination cannot cause an infinite reload. assert(!g.ERR_PrepareTerrainSelection(&world)); h.resource="everon";assert(!g.ERR_PrepareTerrainSelection(&world)&&g.m_ERRLastTerrainRedirect.empty()); // Saving in a running world does not mutate the captured startup destination. g.selection.destination="nogova";assert(g.m_ERRTerrainDestination=="everon"); assert(g.ERR_PrepareTerrainSelection(&world)&&g.m_ERRTerrainDestination=="nogova"); } ''') print('PASS: startup terrain capture protects client/editor/menu worlds, default behavior, same-map loads and reload-loop recovery') update = body(core/'ERR_TerrainSelection.c', 'override void OnUpdate(') for a,b in [('override ', ''), ('BaseWorld world', 'World* world'), ('super.', 'Base::'), ('Replication.', 'Replication::'), ('GameStateTransitions.', 'Transitions::'), ('GameProject.', 'Projects::'), ('PlayerManagerKickReason.', 'KickReason::'), ('LogLevel.', 'LogLevel::'), ('PlayerManager players', 'PlayerManager* players'), ('players.', 'players->'), ('array', 'Array'), ('array', 'Array'), ('foreach (', 'for (')]: update=update.replace(a,b) run(r''' #include #include #include struct string:std::string {using std::string::string;string(std::string s):std::string(s){}bool IsEmpty(){return empty();}}; using ResourceName=string; templatestruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}}; namespace Replication{bool server=true;bool IsServer(){return server;}} namespace LogLevel{int ERROR=1;} namespace KickReason{int KICK=1;} void Print(string,int){}templatevoid PrintFormat(T...){} struct World{}; namespace Projects{ Array loaded={"vanilla","escape","nogova","equipment"}; void GetLoadedAddons(Array& out){out=loaded;} bool IsVanillaAddon(string id){return id=="vanilla";} } namespace Transitions{ bool busy=false;int requests=0;string target,addons; bool IsTransitionRequestedOrInProgress(){return busy;} bool RequestScenarioChangeTransition(string d,string,string mods){requests++;target=d;addons=mods;return true;} } struct PlayerManager{ Array connected;int kicks=0; int GetPlayerCount(){return connected.size();} void GetPlayers(Array& out){out=connected;} void KickPlayer(int,int reason,int timeout){assert(reason==KickReason::KICK&&timeout==0);kicks++;connected.clear();} }; struct Base{void OnUpdate(World*,float){}}; struct Game:Base{ string m_ERRTerrainDestination="everon"; World* m_ERRTerrainSelectionWorld=nullptr;World* current=nullptr; float m_ERRTerrainSelectionWait=0,m_ERRTerrainDisconnectWait=0,m_ERRTerrainEmptyWait=0; PlayerManager players; World* GetWorld(){return current;}PlayerManager* GetPlayerManager(){return &players;} ''' + update + r''' }; int main(){ World w,other; Game g;g.current=&w;g.m_ERRTerrainSelectionWorld=&w;g.players.connected={1,2}; for(int i=0;i<12;i++)g.OnUpdate(&w,.25f); assert(!Transitions::requests&&!g.players.kicks); // A terrain change must never carry connected clients into the new world. for(int i=0;i<4;i++)g.OnUpdate(&w,.25f); assert(g.players.kicks==2&&!Transitions::requests); for(int i=0;i<4;i++)g.OnUpdate(&w,.25f); assert(Transitions::requests==1&&Transitions::target=="everon"); assert(Transitions::addons=="escape,nogova,equipment"); for(int i=0;i<100;i++)g.OnUpdate(&w,.25f); assert(Transitions::requests==1); // Consumed once, no live polling of menu saves. Game busy;busy.current=&w;busy.m_ERRTerrainSelectionWorld=&w;Transitions::busy=true; for(int i=0;i<64;i++)busy.OnUpdate(&w,.25f); assert(busy.m_ERRTerrainDestination.empty()&&Transitions::requests==1);Transitions::busy=false; Game stale;stale.current=&other;stale.m_ERRTerrainSelectionWorld=&w;stale.OnUpdate(&w,4); assert(stale.m_ERRTerrainDestination.empty()&&Transitions::requests==1); Game client;client.current=&w;client.m_ERRTerrainSelectionWorld=&w;Replication::server=false;client.OnUpdate(&w,4); assert(client.m_ERRTerrainDestination.empty()&&Transitions::requests==1);Replication::server=true; Projects::loaded={"vanilla"};Game empty;empty.current=&w;empty.m_ERRTerrainSelectionWorld=&w;empty.OnUpdate(&w,4); assert(empty.m_ERRTerrainDestination.empty()&&Transitions::requests==1); } ''') print('PASS: actual transition waits for native startup, disconnects without bans, requires an empty session, retains every loaded mod, bounds waiting and consumes requests once')