Index: source/simulation2/Simulation2.cpp =================================================================== --- source/simulation2/Simulation2.cpp +++ source/simulation2/Simulation2.cpp @@ -49,6 +49,336 @@ #include #include #include +#include + +namespace +{ +// set true to save human-readable state dumps before an error is detected, for debugging (but slow) +constexpr bool SERIALIZATION_TEST_DEBUG_DUMP{false}; +// set true to save and compare hash of state +constexpr bool SERIALIZATION_TEST_HASH{true}; + +bool LoadScripts(CComponentManager& componentManager, std::set* loadedScripts, const VfsPath& path) +{ + VfsPaths pathnames; + if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0) + return false; + + bool ok = true; + for (const VfsPath& scriptPath : pathnames) + { + if (loadedScripts) + loadedScripts->insert(scriptPath); + LOGMESSAGE("Loading simulation script '%s'", scriptPath.string8()); + if (!componentManager.LoadScript(scriptPath)) + ok = false; + } + return ok; +} + +bool LoadDefaultScripts(CComponentManager& componentManager, std::set* loadedScripts) +{ + return ( + LoadScripts(componentManager, loadedScripts, L"simulation/components/interfaces/") && + LoadScripts(componentManager, loadedScripts, L"simulation/helpers/") && + LoadScripts(componentManager, loadedScripts, L"simulation/components/") + ); +} + +bool LoadTriggerScripts(CComponentManager& componentManager, JS::HandleValue mapSettings, + std::set* loadedScripts) +{ + bool ok = true; + ScriptRequest rq(componentManager.GetScriptInterface()); + if (Script::HasProperty(rq, mapSettings, "TriggerScripts")) + { + std::vector scriptNames; + Script::GetProperty(rq, mapSettings, "TriggerScripts", scriptNames); + for (const std::string& triggerScript : scriptNames) + { + std::string scriptName = "maps/" + triggerScript; + if (loadedScripts) + { + if (loadedScripts->find(scriptName) != loadedScripts->end()) + continue; + loadedScripts->insert(scriptName); + } + LOGMESSAGE("Loading trigger script '%s'", scriptName.c_str()); + if (!componentManager.LoadScript(scriptName.data())) + ok = false; + } + } + return ok; +} + +void ResetComponentState(CComponentManager& componentManager, bool skipScriptedComponents, bool skipAI) +{ + componentManager.ResetState(); + componentManager.InitSystemEntity(); + componentManager.AddSystemComponents(skipScriptedComponents, skipAI); +} + +void UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector& commands) +{ + // TODO: the update process is pretty ugly, with lots of messages and dependencies + // between different components. Ought to work out a nicer way to do this. + + CComponentManager& componentManager = simContext.GetComponentManager(); + + CmpPtr cmpPathfinder(simContext, SYSTEM_ENTITY); + if (cmpPathfinder) + cmpPathfinder->SendRequestedPaths(); + + { + PROFILE2("Sim - Update Start"); + CMessageTurnStart msgTurnStart; + componentManager.BroadcastMessage(msgTurnStart); + } + + + CmpPtr cmpCommandQueue(simContext, SYSTEM_ENTITY); + if (cmpCommandQueue) + cmpCommandQueue->FlushTurn(commands); + + // Process newly generated move commands so the UI feels snappy + if (cmpPathfinder) + { + cmpPathfinder->StartProcessingMoves(true); + cmpPathfinder->SendRequestedPaths(); + } + // Send all the update phases + { + PROFILE2("Sim - Update"); + CMessageUpdate msgUpdate(turnLengthFixed); + componentManager.BroadcastMessage(msgUpdate); + } + + { + CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed); + componentManager.BroadcastMessage(msgUpdate); + } + + // Process move commands for formations (group proxy) + if (cmpPathfinder) + { + cmpPathfinder->StartProcessingMoves(true); + cmpPathfinder->SendRequestedPaths(); + } + + { + PROFILE2("Sim - Motion Unit"); + CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed); + componentManager.BroadcastMessage(msgUpdate); + } + { + PROFILE2("Sim - Update Final"); + CMessageUpdate_Final msgUpdate(turnLengthFixed); + componentManager.BroadcastMessage(msgUpdate); + } + + // Clean up any entities destroyed during the simulation update + componentManager.FlushDestroyedComponents(); + + // Compute AI immediately at turn's end. + CmpPtr cmpAIManager(simContext, SYSTEM_ENTITY); + if (cmpAIManager) + { + cmpAIManager->StartComputation(); + cmpAIManager->PushCommands(); + } + + // Process all remaining moves + if (cmpPathfinder) + { + cmpPathfinder->UpdateGrid(); + cmpPathfinder->StartProcessingMoves(false); + } +} + +struct SerializationTestState +{ + std::stringstream state; + std::stringstream debug; + std::string hash; +}; + +void DumpSerializationTestState(SerializationTestState& state, const OsPath& path, + const OsPath::String& suffix) +{ + if (!state.hash.empty()) + { + std::ofstream file (OsString(path / (L"hash." + suffix)).c_str(), + std::ofstream::out | std::ofstream::trunc); + file << Hexify(state.hash); + } + + if (!state.debug.str().empty()) + { + std::ofstream file (OsString(path / (L"debug." + suffix)).c_str(), + std::ofstream::out | std::ofstream::trunc); + file << state.debug.str(); + } + + if (!state.state.str().empty()) + { + std::ofstream file (OsString(path / (L"state." + suffix)).c_str(), + std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); + file << state.state.str(); + } +} + +void ReportSerializationFailure( + SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter, + SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter) +{ + const OsPath path = createDateIndexSubdirectory(psLogDir() / "serializationtest"); + debug_printf("Writing serializationtest-data to %s\n", path.string8().c_str()); + + // Clean up obsolete files from previous runs + wunlink(path / "hash.before.a"); + wunlink(path / "hash.before.b"); + wunlink(path / "debug.before.a"); + wunlink(path / "debug.before.b"); + wunlink(path / "state.before.a"); + wunlink(path / "state.before.b"); + wunlink(path / "hash.after.a"); + wunlink(path / "hash.after.b"); + wunlink(path / "debug.after.a"); + wunlink(path / "debug.after.b"); + wunlink(path / "state.after.a"); + wunlink(path / "state.after.b"); + + if (primaryStateBefore) + DumpSerializationTestState(*primaryStateBefore, path, L"before.a"); + if (primaryStateAfter) + DumpSerializationTestState(*primaryStateAfter, path, L"after.a"); + if (secondaryStateBefore) + DumpSerializationTestState(*secondaryStateBefore, path, L"before.b"); + if (secondaryStateAfter) + DumpSerializationTestState(*secondaryStateAfter, path, L"after.b"); + + debug_warn(L"Serialization test failure"); +} + +struct SimulationCommandClone +{ + player_id_t player; + Script::StructuredClone data; +}; + +class SecondarySimulation +{ +public: + SecondarySimulation(ScriptContext& scriptContext, const Script::StructuredClone mapSettings, + const Script::StructuredClone initAttributes, std::stringstream& state) + { + componentManager.emplace(context, scriptContext); + componentManager->LoadComponentTypes(); + + ENSURE(LoadDefaultScripts(*componentManager, &loadedScripts)); + ResetComponentState(*componentManager, false, false); + + ScriptRequest rq(componentManager->GetScriptInterface()); + // Load the trigger scripts after we have loaded the simulation. + { + JS::RootedValue rootedSettingsCloned{rq.cx}; + Script::ReadStructuredClone(rq, mapSettings, &rootedSettingsCloned); + ENSURE(LoadTriggerScripts(*componentManager, rootedSettingsCloned, &loadedScripts)); + } + + // Load the map into the secondary simulation + { + JS::RootedValue rootedInitAttributes{rq.cx}; + Script::ReadStructuredClone(rq, initAttributes, &rootedInitAttributes); + + LDR_BeginRegistering(); + std::unique_ptr mapReader = std::make_unique(); + + std::string mapType; + Script::GetProperty(rq, rootedInitAttributes, "mapType", mapType); + if (mapType == "random") + { + // TODO: support random map scripts + debug_warn(L"Serialization test mode does not support random maps"); + } + else + { + std::wstring mapFile; + Script::GetProperty(rq, rootedInitAttributes, "map", mapFile); + + VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); + // throws exception on failure + mapReader->LoadMap(mapfilename, scriptContext, JS::UndefinedHandleValue, &terrain, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + &context, INVALID_PLAYER, true); + } + + LDR_EndRegistering(); + ENSURE(LDR_NonprogressiveLoad() == INFO::OK); + } + ENSURE(componentManager->DeserializeState(state)); + } + + struct UpdateResult + { + SerializationTestState stateBefore; + SerializationTestState stateAfter; + }; + + std::optional Update(SerializationTestState& primaryStateBefore, + SerializationTestState& primaryStateAfter, const fixed turnLengthFixed, + std::vector commands) + { + SerializationTestState secondaryStateBefore; + ENSURE(componentManager->SerializeState(secondaryStateBefore.state)); + if (SERIALIZATION_TEST_DEBUG_DUMP) + ENSURE(componentManager->DumpDebugState(secondaryStateBefore.debug, false)); + if (SERIALIZATION_TEST_HASH) + ENSURE(componentManager->ComputeStateHash(secondaryStateBefore.hash, false)); + + if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() || + primaryStateBefore.hash != secondaryStateBefore.hash) + { + ReportSerializationFailure(&primaryStateBefore, nullptr, &secondaryStateBefore, nullptr); + } + + const ScriptRequest rq{componentManager->GetScriptInterface()}; + std::vector simulationCommands; + std::transform(commands.begin(), commands.end(), std::back_inserter(simulationCommands), + [&](const SimulationCommandClone& commandClone) + { + JS::RootedValue tmpCommand{rq.cx}; + Script::ReadStructuredClone(rq, commandClone.data, &tmpCommand); + Script::FreezeObject(rq, tmpCommand, true); + return SimulationCommand{commandClone.player, rq.cx, tmpCommand}; + }); + + UpdateComponents(context, turnLengthFixed, simulationCommands); + + SerializationTestState secondaryStateAfter; + ENSURE(componentManager->SerializeState(secondaryStateAfter.state)); + if (SERIALIZATION_TEST_HASH) + ENSURE(componentManager->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(componentManager->DumpDebugState(secondaryStateAfter.debug, false)); + return UpdateResult{std::move(secondaryStateBefore), std::move(secondaryStateAfter)}; + } + + return std::nullopt; + } + +private: + // order matters for destruction + std::optional componentManager; + CTerrain terrain; + CSimContext context{&terrain}; + std::set loadedScripts; +}; +} // anonymous namespace class CSimulation2Impl { @@ -93,16 +423,6 @@ ResetComponentState(m_ComponentManager, skipScriptedComponents, skipAI); } - static void ResetComponentState(CComponentManager& componentManager, bool skipScriptedComponents, bool skipAI) - { - componentManager.ResetState(); - componentManager.InitSystemEntity(); - componentManager.AddSystemComponents(skipScriptedComponents, skipAI); - } - - static bool LoadDefaultScripts(CComponentManager& componentManager, std::set* loadedScripts); - static bool LoadScripts(CComponentManager& componentManager, std::set* loadedScripts, const VfsPath& path); - static bool LoadTriggerScripts(CComponentManager& componentManager, JS::HandleValue mapSettings, std::set* loadedScripts); Status ReloadChangedFile(const VfsPath& path); static Status ReloadChangedFileCB(void* param, const VfsPath& path) @@ -112,7 +432,6 @@ int ProgressiveLoad(); void Update(int turnLength, const std::vector& commands); - static void UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector& commands); void Interpolate(float simFrameLength, float frameOffset, float realFrameLength); void DumpState(); @@ -139,98 +458,12 @@ int m_RejoinTestTurn{-1}; bool m_TestingRejoin{false}; - // Secondary simulation (NB: order matters for destruction). - std::unique_ptr m_SecondaryComponentManager; - std::unique_ptr m_SecondaryTerrain; - std::unique_ptr m_SecondaryContext; - std::unique_ptr> m_SecondaryLoadedScripts; - - struct SerializationTestState - { - std::stringstream state; - std::stringstream debug; - std::string hash; - }; - - void DumpSerializationTestState(SerializationTestState& state, const OsPath& path, const OsPath::String& suffix); - - void ReportSerializationFailure( - SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter, - SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter); + std::unique_ptr m_SecondarySimulation; void InitRNGSeedSimulation(); void InitRNGSeedAI(); - - static std::vector CloneCommandsFromOtherCompartment(const ScriptInterface& newScript, const ScriptInterface& oldScript, - const std::vector& commands) - { - std::vector newCommands; - newCommands.reserve(commands.size()); - - ScriptRequest rqNew(newScript); - for (const SimulationCommand& command : commands) - { - JS::RootedValue tmpCommand(rqNew.cx, Script::CloneValueFromOtherCompartment(newScript, oldScript, command.data)); - Script::FreezeObject(rqNew, tmpCommand, true); - SimulationCommand cmd(command.player, rqNew.cx, tmpCommand); - newCommands.emplace_back(std::move(cmd)); - } - return newCommands; - } }; -bool CSimulation2Impl::LoadDefaultScripts(CComponentManager& componentManager, std::set* loadedScripts) -{ - return ( - LoadScripts(componentManager, loadedScripts, L"simulation/components/interfaces/") && - LoadScripts(componentManager, loadedScripts, L"simulation/helpers/") && - LoadScripts(componentManager, loadedScripts, L"simulation/components/") - ); -} - -bool CSimulation2Impl::LoadScripts(CComponentManager& componentManager, std::set* loadedScripts, const VfsPath& path) -{ - VfsPaths pathnames; - if (vfs::GetPathnames(g_VFS, path, L"*.js", pathnames) < 0) - return false; - - bool ok = true; - for (const VfsPath& scriptPath : pathnames) - { - if (loadedScripts) - loadedScripts->insert(scriptPath); - LOGMESSAGE("Loading simulation script '%s'", scriptPath.string8()); - if (!componentManager.LoadScript(scriptPath)) - ok = false; - } - return ok; -} - -bool CSimulation2Impl::LoadTriggerScripts(CComponentManager& componentManager, JS::HandleValue mapSettings, std::set* loadedScripts) -{ - bool ok = true; - ScriptRequest rq(componentManager.GetScriptInterface()); - if (Script::HasProperty(rq, mapSettings, "TriggerScripts")) - { - std::vector scriptNames; - Script::GetProperty(rq, mapSettings, "TriggerScripts", scriptNames); - for (const std::string& triggerScript : scriptNames) - { - std::string scriptName = "maps/" + triggerScript; - if (loadedScripts) - { - if (loadedScripts->find(scriptName) != loadedScripts->end()) - continue; - loadedScripts->insert(scriptName); - } - LOGMESSAGE("Loading trigger script '%s'", scriptName.c_str()); - if (!componentManager.LoadScript(scriptName.data())) - ok = false; - } - } - return ok; -} - Status CSimulation2Impl::ReloadChangedFile(const VfsPath& path) { // Ignore if this file wasn't loaded as a script @@ -278,60 +511,6 @@ return ret; } -void CSimulation2Impl::DumpSerializationTestState(SerializationTestState& state, const OsPath& path, const OsPath::String& suffix) -{ - if (!state.hash.empty()) - { - std::ofstream file (OsString(path / (L"hash." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc); - file << Hexify(state.hash); - } - - if (!state.debug.str().empty()) - { - std::ofstream file (OsString(path / (L"debug." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc); - file << state.debug.str(); - } - - if (!state.state.str().empty()) - { - std::ofstream file (OsString(path / (L"state." + suffix)).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); - file << state.state.str(); - } -} - -void CSimulation2Impl::ReportSerializationFailure( - SerializationTestState* primaryStateBefore, SerializationTestState* primaryStateAfter, - SerializationTestState* secondaryStateBefore, SerializationTestState* secondaryStateAfter) -{ - const OsPath path = createDateIndexSubdirectory(psLogDir() / "serializationtest"); - debug_printf("Writing serializationtest-data to %s\n", path.string8().c_str()); - - // Clean up obsolete files from previous runs - wunlink(path / "hash.before.a"); - wunlink(path / "hash.before.b"); - wunlink(path / "debug.before.a"); - wunlink(path / "debug.before.b"); - wunlink(path / "state.before.a"); - wunlink(path / "state.before.b"); - wunlink(path / "hash.after.a"); - wunlink(path / "hash.after.b"); - wunlink(path / "debug.after.a"); - wunlink(path / "debug.after.b"); - wunlink(path / "state.after.a"); - wunlink(path / "state.after.b"); - - if (primaryStateBefore) - DumpSerializationTestState(*primaryStateBefore, path, L"before.a"); - if (primaryStateAfter) - DumpSerializationTestState(*primaryStateAfter, path, L"after.a"); - if (secondaryStateBefore) - DumpSerializationTestState(*secondaryStateBefore, path, L"before.b"); - if (secondaryStateAfter) - DumpSerializationTestState(*secondaryStateAfter, path, L"after.b"); - - debug_warn(L"Serialization test failure"); -} - void CSimulation2Impl::InitRNGSeedSimulation() { u32 seed = 0; @@ -377,9 +556,6 @@ * complete serialization test and allows us to reproduce OOSes on rejoin. */ - const bool serializationTestDebugDump = false; // set true to save human-readable state dumps before an error is detected, for debugging (but slow) - const bool serializationTestHash = true; // set true to save and compare hash of state - SerializationTestState primaryStateBefore; const ScriptInterface& scriptInterface = m_ComponentManager.GetScriptInterface(); @@ -390,102 +566,55 @@ if (m_EnableSerializationTest || m_TestingRejoin) { ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state)); - if (serializationTestDebugDump) + if (SERIALIZATION_TEST_DEBUG_DUMP) ENSURE(m_ComponentManager.DumpDebugState(primaryStateBefore.debug, false)); - if (serializationTestHash) + if (SERIALIZATION_TEST_HASH) ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false)); } - UpdateComponents(m_SimContext, turnLengthFixed, commands); - if (m_EnableSerializationTest || startRejoinTest) { if (startRejoinTest) debug_printf("Initializing the secondary simulation\n"); - m_SecondaryTerrain = std::make_unique(); - - m_SecondaryContext = std::make_unique(m_SecondaryTerrain.get()); - - m_SecondaryComponentManager = std::make_unique(*m_SecondaryContext, scriptInterface.GetContext()); - m_SecondaryComponentManager->LoadComponentTypes(); + const ScriptRequest rq{scriptInterface}; - m_SecondaryLoadedScripts = std::make_unique>(); - ENSURE(LoadDefaultScripts(*m_SecondaryComponentManager, m_SecondaryLoadedScripts.get())); - ResetComponentState(*m_SecondaryComponentManager, false, false); - - ScriptRequest rq(scriptInterface); - - // Load the trigger scripts after we have loaded the simulation. - { - ScriptRequest rq2(m_SecondaryComponentManager->GetScriptInterface()); - JS::RootedValue mapSettingsCloned(rq2.cx, Script::CloneValueFromOtherCompartment(m_SecondaryComponentManager->GetScriptInterface(), scriptInterface, m_MapSettings)); - ENSURE(LoadTriggerScripts(*m_SecondaryComponentManager, mapSettingsCloned, m_SecondaryLoadedScripts.get())); - } - - // Load the map into the secondary simulation + const Script::StructuredClone mapSettingsCloned{ + Script::WriteStructuredClone(rq, m_MapSettings)}; + const Script::StructuredClone initAttributesCloned{ + Script::WriteStructuredClone(rq, m_InitAttributes)}; - LDR_BeginRegistering(); - std::unique_ptr mapReader = std::make_unique(); - - std::string mapType; - Script::GetProperty(rq, m_InitAttributes, "mapType", mapType); - if (mapType == "random") - { - // TODO: support random map scripts - debug_warn(L"Serialization test mode does not support random maps"); - } - else - { - std::wstring mapFile; - Script::GetProperty(rq, m_InitAttributes, "map", mapFile); - - VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); - mapReader->LoadMap(mapfilename, scriptInterface.GetContext(), JS::UndefinedHandleValue, - m_SecondaryTerrain.get(), NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, m_SecondaryContext.get(), INVALID_PLAYER, true); // throws exception on failure - } - - LDR_EndRegistering(); - ENSURE(LDR_NonprogressiveLoad() == INFO::OK); - ENSURE(m_SecondaryComponentManager->DeserializeState(primaryStateBefore.state)); + m_SecondarySimulation = std::make_unique(scriptInterface.GetContext(), + mapSettingsCloned, initAttributesCloned, primaryStateBefore.state); } + UpdateComponents(m_SimContext, turnLengthFixed, commands); + if (m_EnableSerializationTest || m_TestingRejoin) { - SerializationTestState secondaryStateBefore; - ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateBefore.state)); - if (serializationTestDebugDump) - ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateBefore.debug, false)); - if (serializationTestHash) - ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateBefore.hash, false)); - - if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() || - primaryStateBefore.hash != secondaryStateBefore.hash) - { - ReportSerializationFailure(&primaryStateBefore, NULL, &secondaryStateBefore, NULL); - } - SerializationTestState primaryStateAfter; ENSURE(m_ComponentManager.SerializeState(primaryStateAfter.state)); - if (serializationTestHash) + if (SERIALIZATION_TEST_HASH) ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false)); - UpdateComponents(*m_SecondaryContext, turnLengthFixed, - CloneCommandsFromOtherCompartment(m_SecondaryComponentManager->GetScriptInterface(), scriptInterface, commands)); - SerializationTestState secondaryStateAfter; - ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateAfter.state)); - if (serializationTestHash) - ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateAfter.hash, false)); + const ScriptRequest rq{scriptInterface}; + std::vector commandsCloned; + std::transform(commands.begin(), commands.end(), std::back_inserter(commandsCloned), + [&](const SimulationCommand& command) + { + return SimulationCommandClone{command.player, + Script::WriteStructuredClone(rq, command.data)}; + }); - if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() || - primaryStateAfter.hash != secondaryStateAfter.hash) + std::optional secondaryResult{m_SecondarySimulation->Update( + primaryStateBefore, primaryStateAfter, turnLengthFixed, commandsCloned)}; + + if (secondaryResult) { - // Only do the (slow) dumping now we know we're going to need to report it ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false)); - ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateAfter.debug, false)); - ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter); + ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, + &secondaryResult->stateBefore, &secondaryResult->stateAfter); } } @@ -510,83 +639,6 @@ ++m_TurnNumber; } -void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector& commands) -{ - // TODO: the update process is pretty ugly, with lots of messages and dependencies - // between different components. Ought to work out a nicer way to do this. - - CComponentManager& componentManager = simContext.GetComponentManager(); - - CmpPtr cmpPathfinder(simContext, SYSTEM_ENTITY); - if (cmpPathfinder) - cmpPathfinder->SendRequestedPaths(); - - { - PROFILE2("Sim - Update Start"); - CMessageTurnStart msgTurnStart; - componentManager.BroadcastMessage(msgTurnStart); - } - - - CmpPtr cmpCommandQueue(simContext, SYSTEM_ENTITY); - if (cmpCommandQueue) - cmpCommandQueue->FlushTurn(commands); - - // Process newly generated move commands so the UI feels snappy - if (cmpPathfinder) - { - cmpPathfinder->StartProcessingMoves(true); - cmpPathfinder->SendRequestedPaths(); - } - // Send all the update phases - { - PROFILE2("Sim - Update"); - CMessageUpdate msgUpdate(turnLengthFixed); - componentManager.BroadcastMessage(msgUpdate); - } - - { - CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed); - componentManager.BroadcastMessage(msgUpdate); - } - - // Process move commands for formations (group proxy) - if (cmpPathfinder) - { - cmpPathfinder->StartProcessingMoves(true); - cmpPathfinder->SendRequestedPaths(); - } - - { - PROFILE2("Sim - Motion Unit"); - CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed); - componentManager.BroadcastMessage(msgUpdate); - } - { - PROFILE2("Sim - Update Final"); - CMessageUpdate_Final msgUpdate(turnLengthFixed); - componentManager.BroadcastMessage(msgUpdate); - } - - // Clean up any entities destroyed during the simulation update - componentManager.FlushDestroyedComponents(); - - // Compute AI immediately at turn's end. - CmpPtr cmpAIManager(simContext, SYSTEM_ENTITY); - if (cmpAIManager) - { - cmpAIManager->StartComputation(); - cmpAIManager->PushCommands(); - } - - // Process all remaining moves - if (cmpPathfinder) - { - cmpPathfinder->UpdateGrid(); - cmpPathfinder->StartProcessingMoves(false); - } -} - void CSimulation2Impl::Interpolate(float simFrameLength, float frameOffset, float realFrameLength) { PROFILE3("sim interpolate"); @@ -775,12 +827,12 @@ bool CSimulation2::LoadScripts(const VfsPath& path) { - return m->LoadScripts(m->m_ComponentManager, &m->m_LoadedScripts, path); + return ::LoadScripts(m->m_ComponentManager, &m->m_LoadedScripts, path); } bool CSimulation2::LoadDefaultScripts() { - return m->LoadDefaultScripts(m->m_ComponentManager, &m->m_LoadedScripts); + return ::LoadDefaultScripts(m->m_ComponentManager, &m->m_LoadedScripts); } void CSimulation2::SetStartupScript(const std::string& code) @@ -854,7 +906,7 @@ GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript); // Load the trigger scripts after we have loaded the simulation and the map. - m->LoadTriggerScripts(m->m_ComponentManager, m->m_MapSettings, &m->m_LoadedScripts); + LoadTriggerScripts(m->m_ComponentManager, m->m_MapSettings, &m->m_LoadedScripts); } int CSimulation2::ProgressiveLoad()