diff --git a/binaries/system/readme.txt b/binaries/system/readme.txt index 2d32479ce0..a208a902cb 100644 --- a/binaries/system/readme.txt +++ b/binaries/system/readme.txt @@ -58,6 +58,9 @@ Advanced / diagnostic: -serializationtest checks simulation state each turn for serialization errors; on test failure, error is displayed and logs created in oos_log within the game's log folder. NOTE: game will run much slower with this option! +-rejointest=N simulates a rejoin and checks simulation state each turn for serialization + errors; this is similar to a serialization test but much faster and + less complete. It should be enough for debugging most rejoin OOSes. Windows-specific: -wQpcTscSafe allow timing via QueryPerformanceCounter despite the fact diff --git a/source/main.cpp b/source/main.cpp index 86961a6f41..fb330c4222 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -472,7 +472,10 @@ static void RunGameOrAtlas(int argc, const char* argv[]) { CReplayPlayer replay; replay.Load(replayFile); - replay.Replay(args.Has("serializationtest"), args.Has("ooslog")); + replay.Replay( + args.Has("serializationtest"), + args.Has("rejointest") ? args.Get("rejointest").ToInt() : -1, + args.Has("ooslog")); } g_VFS.reset(); diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index 8df64c7615..ab177a128b 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -171,6 +171,9 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args) if (args.Has("serializationtest")) g_ConfigDB.SetValueString(CFG_COMMAND, "serializationtest", "true"); + + if (args.Has("rejointest")) + g_ConfigDB.SetValueString(CFG_COMMAND, "rejointest", args.Get("rejointest")); } diff --git a/source/ps/Replay.cpp b/source/ps/Replay.cpp index f380bd3ab7..17c139fe2a 100644 --- a/source/ps/Replay.cpp +++ b/source/ps/Replay.cpp @@ -114,7 +114,7 @@ void CReplayPlayer::Load(const std::string& path) ENSURE(m_Stream->good()); } -void CReplayPlayer::Replay(bool serializationtest, bool ooslog) +void CReplayPlayer::Replay(bool serializationtest, int rejointestturn, bool ooslog) { ENSURE(m_Stream); @@ -130,6 +130,8 @@ void CReplayPlayer::Replay(bool serializationtest, bool ooslog) g_Game = new CGame(true, false); if (serializationtest) g_Game->GetSimulation2()->EnableSerializationTest(); + if (rejointestturn > 0) + g_Game->GetSimulation2()->EnableRejoinTest(rejointestturn); if (ooslog) g_Game->GetSimulation2()->EnableOOSLog(); diff --git a/source/ps/Replay.h b/source/ps/Replay.h index be5b042864..6bdda6e5ca 100644 --- a/source/ps/Replay.h +++ b/source/ps/Replay.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2016 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -97,7 +97,7 @@ public: ~CReplayPlayer(); void Load(const std::string& path); - void Replay(bool serializationtest, bool ooslog); + void Replay(bool serializationtest, int rejointestturn, bool ooslog); private: std::istream* m_Stream; diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index 8be12e1025..fef97bf016 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -51,7 +51,8 @@ class CSimulation2Impl public: CSimulation2Impl(CUnitManager* unitManager, shared_ptr rt, CTerrain* terrain) : m_SimContext(), m_ComponentManager(m_SimContext, rt), - m_EnableOOSLog(false), m_EnableSerializationTest(false), + m_EnableOOSLog(false), m_EnableSerializationTest(false), m_RejoinTestTurn(-1), m_TestingRejoin(false), + m_SecondaryTerrain(nullptr), m_SecondaryContext(nullptr), m_SecondaryComponentManager(nullptr), m_SecondaryLoadedScripts(nullptr), m_MapSettings(rt->m_rt), m_InitAttributes(rt->m_rt) { m_SimContext.m_UnitManager = unitManager; @@ -65,6 +66,9 @@ public: { CFG_GET_VAL("ooslog", m_EnableOOSLog); CFG_GET_VAL("serializationtest", m_EnableSerializationTest); + CFG_GET_VAL("rejointest", m_RejoinTestTurn); + if (m_RejoinTestTurn <= 0) // Handle bogus values of the arg + m_RejoinTestTurn = -1; } if (m_EnableOOSLog) @@ -76,6 +80,11 @@ public: ~CSimulation2Impl() { + delete m_SecondaryTerrain; + delete m_SecondaryContext; + delete m_SecondaryComponentManager; + delete m_SecondaryLoadedScripts; + UnregisterFileReloadFunc(ReloadChangedFileCB, this); } @@ -130,6 +139,14 @@ public: // Functions and data for the serialization test mode: (see Update() for relevant comments) bool m_EnableSerializationTest; + int m_RejoinTestTurn; + bool m_TestingRejoin; + + // Secondary simulation + CTerrain* m_SecondaryTerrain; + CSimContext* m_SecondaryContext; + CComponentManager* m_SecondaryComponentManager; + std::set* m_SecondaryLoadedScripts; struct SerializationTestState { @@ -332,6 +349,10 @@ void CSimulation2Impl::Update(int turnLength, const std::vector secondaryLoadedScripts; - ENSURE(LoadDefaultScripts(secondaryComponentManager, &secondaryLoadedScripts)); - ResetComponentState(secondaryComponentManager, false, false); + if (startRejoinTest) + debug_printf("Initializing the secondary simulation\n"); + + delete m_SecondaryTerrain; + m_SecondaryTerrain = new CTerrain(); + + delete m_SecondaryContext; + m_SecondaryContext = new CSimContext(); + m_SecondaryContext->m_Terrain = m_SecondaryTerrain; + + delete m_SecondaryComponentManager; + m_SecondaryComponentManager = new CComponentManager(*m_SecondaryContext, scriptInterface.GetRuntime()); + m_SecondaryComponentManager->LoadComponentTypes(); + + delete m_SecondaryLoadedScripts; + m_SecondaryLoadedScripts = new std::set(); + ENSURE(LoadDefaultScripts(*m_SecondaryComponentManager, m_SecondaryLoadedScripts)); + ResetComponentState(*m_SecondaryComponentManager, false, false); // Load the trigger scripts after we have loaded the simulation. { - JSContext* cx2 = secondaryComponentManager.GetScriptInterface().GetContext(); + JSContext* cx2 = m_SecondaryComponentManager->GetScriptInterface().GetContext(); JSAutoRequest rq2(cx2); JS::RootedValue mapSettingsCloned(cx2, - secondaryComponentManager.GetScriptInterface().CloneValueFromOtherContext( + m_SecondaryComponentManager->GetScriptInterface().CloneValueFromOtherContext( scriptInterface, m_MapSettings)); - ENSURE(LoadTriggerScripts(secondaryComponentManager, mapSettingsCloned, &secondaryLoadedScripts)); + ENSURE(LoadTriggerScripts(*m_SecondaryComponentManager, mapSettingsCloned, m_SecondaryLoadedScripts)); } // Load the map into the secondary simulation @@ -393,21 +426,23 @@ void CSimulation2Impl::Update(int turnLength, const std::vectorLoadMap(mapfilename, scriptInterface.GetJSRuntime(), JS::UndefinedHandleValue, - &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure + m_SecondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, m_SecondaryContext, INVALID_PLAYER, true); // throws exception on failure } LDR_EndRegistering(); ENSURE(LDR_NonprogressiveLoad() == INFO::OK); + ENSURE(m_SecondaryComponentManager->DeserializeState(primaryStateBefore.state)); + } - ENSURE(secondaryComponentManager.DeserializeState(primaryStateBefore.state)); - + if (m_EnableSerializationTest || m_TestingRejoin) + { SerializationTestState secondaryStateBefore; - ENSURE(secondaryComponentManager.SerializeState(secondaryStateBefore.state)); + ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateBefore.state)); if (serializationTestDebugDump) - ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateBefore.debug, false)); + ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateBefore.debug, false)); if (serializationTestHash) - ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateBefore.hash, false)); + ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateBefore.hash, false)); if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() || primaryStateBefore.hash != secondaryStateBefore.hash) @@ -420,19 +455,19 @@ void CSimulation2Impl::Update(int turnLength, const std::vectorGetScriptInterface(), commands)); SerializationTestState secondaryStateAfter; - ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state)); + ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateAfter.state)); if (serializationTestHash) - ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateAfter.hash, false)); + ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateAfter.hash, false)); if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() || primaryStateAfter.hash != secondaryStateAfter.hash) { // Only do the (slow) dumping now we know we're going to need to report it ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false)); - ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateAfter.debug, false)); + ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateAfter.debug, false)); ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter); } @@ -588,6 +623,16 @@ CSimulation2::~CSimulation2() // Forward all method calls to the appropriate CSimulation2Impl/CComponentManager methods: +void CSimulation2::EnableSerializationTest() +{ + m->m_EnableSerializationTest = true; +} + +void CSimulation2::EnableRejoinTest(int rejoinTestTurn) +{ + m->m_RejoinTestTurn = rejoinTestTurn; +} + void CSimulation2::EnableOOSLog() { if (m->m_EnableOOSLog) @@ -599,11 +644,6 @@ void CSimulation2::EnableOOSLog() debug_printf("Writing ooslogs to %s\n", m->m_OOSLogPath.string8().c_str()); } -void CSimulation2::EnableSerializationTest() -{ - m->m_EnableSerializationTest = true; -} - entity_id_t CSimulation2::AddEntity(const std::wstring& templateName) { return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity()); diff --git a/source/simulation2/Simulation2.h b/source/simulation2/Simulation2.h index ee341b8f8e..cdec350a1d 100644 --- a/source/simulation2/Simulation2.h +++ b/source/simulation2/Simulation2.h @@ -54,8 +54,9 @@ public: CSimulation2(CUnitManager* unitManager, shared_ptr rt, CTerrain* terrain); ~CSimulation2(); - void EnableOOSLog(); void EnableSerializationTest(); + void EnableRejoinTest(int rejoinTestTurn); + void EnableOOSLog(); /** * Load all scripts in the specified directory (non-recursively),