From 3bcf3601070c23632f4420df32ccef97863fca9c Mon Sep 17 00:00:00 2001 From: wraitii Date: Thu, 20 May 2021 14:36:42 +0000 Subject: [PATCH] Mod and mod-mounting cleanup and improvements. - Non-visual replays now automatically try to load the replay mods. This removes the annoyance that -mod=public usually had to be passed. - MountMods is no longer called in InitVfs but later, making it possible to load the game in one pass & simplifying things considerably. - Explicitly ignore duplicates when loading mods - Interface cleanup: failed mods and incompatible mods were redundant, only incompatible mods is kept. - Interface cleanup: `AreModsCompatible`becomes `CheckForIncompatibleMods`, which becomes a private pure function, simplifying the control flow somewhat. - Interface cleanup: `CheckAndEnableMods` is just `EnableMods`, which explicitly updates loaded & incompatible mods. - `CacheEnabledModVersions` becomes private and is called on behalf of the user, removing the need to be careful about updating that (fixes my concern at 44ec2e324e) Overall, the logic around mounting & enabled mods should be easier to understand. Differential Revision: https://code.wildfiregames.com/D3982 This was SVN commit r25474. --- binaries/data/mods/mod/gui/modmod/modmod.js | 8 +- source/main.cpp | 10 --- source/ps/GameSetup/CmdLineArgs.h | 4 +- source/ps/GameSetup/GameSetup.cpp | 85 +++++++++----------- source/ps/GameSetup/GameSetup.h | 3 +- source/ps/Mod.cpp | 86 +++++++++------------ source/ps/Mod.h | 42 ++++++---- source/ps/ModIo.cpp | 1 + source/ps/Replay.cpp | 79 ++++++++++++------- source/ps/Replay.h | 4 +- source/ps/VisualReplay.cpp | 1 + source/ps/scripting/JSInterface_Mod.cpp | 11 ++- source/ps/tests/test_Mod.h | 69 ++++++++--------- 13 files changed, 204 insertions(+), 199 deletions(-) diff --git a/binaries/data/mods/mod/gui/modmod/modmod.js b/binaries/data/mods/mod/gui/modmod/modmod.js index 3db913e70e..6da87d70ea 100644 --- a/binaries/data/mods/mod/gui/modmod/modmod.js +++ b/binaries/data/mods/mod/gui/modmod/modmod.js @@ -61,7 +61,7 @@ var g_ModsCompatibility = []; */ var g_InstalledMods; -var g_HasFailedMods; +var g_HasIncompatibleMods; var g_FakeMod = { "name": translate("This mod does not exist"), @@ -79,11 +79,11 @@ var g_ColorDependenciesNotMet = "255 100 100"; function init(data, hotloadData) { g_InstalledMods = data && data.installedMods || hotloadData && hotloadData.installedMods || []; - g_HasFailedMods = Engine.HasFailedMods(); + g_HasIncompatibleMods = Engine.HasIncompatibleMods(); initMods(); initGUIButtons(data); - if (g_HasFailedMods) + if (g_HasIncompatibleMods) Engine.PushGuiPage("page_incompatible_mods.xml", {}); } @@ -117,7 +117,7 @@ function getMod(folder) function loadEnabledMods() { - if (g_HasFailedMods) + if (g_HasIncompatibleMods) g_ModsEnabled = Engine.GetFailedMods().filter(folder => folder != "mod"); else g_ModsEnabled = Engine.GetEnabledMods().filter(folder => !!g_Mods[folder]); diff --git a/source/main.cpp b/source/main.cpp index f252a28cae..84d913897a 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -99,7 +99,6 @@ that of Atlas depending on commandline parameters. #include -extern CmdLineArgs g_CmdLineArgs; extern CStrW g_UniqueLogPostfix; // Marks terrain as modified so the minimap can repaint (is there a cleaner way of handling this?) @@ -587,18 +586,10 @@ static void RunGameOrAtlas(int argc, const char* argv[]) if (isNonVisualReplay) { - if (!args.Has("mod")) - { - LOGERROR("At least one mod should be specified! Did you mean to add the argument '-mod=public'?"); - CXeromyces::Terminate(); - return; - } - Paths paths(args); g_VFS = CreateVfs(); // Mount with highest priority, we don't want mods overwriting this. g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE, VFS_MAX_PRIORITY); - MountMods(paths, g_Mods.GetModsFromArguments(args, INIT_MODS)); { CReplayPlayer replay; @@ -698,7 +689,6 @@ static void RunGameOrAtlas(int argc, const char* argv[]) // Do not install mods again in case of restart (typically from the mod selector) modsToInstall.clear(); - g_Mods.ClearIncompatibleMods(); Shutdown(0); MainControllerShutdown(); diff --git a/source/ps/GameSetup/CmdLineArgs.h b/source/ps/GameSetup/CmdLineArgs.h index 77c63a73cd..028c3fa4db 100644 --- a/source/ps/GameSetup/CmdLineArgs.h +++ b/source/ps/GameSetup/CmdLineArgs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -75,4 +75,6 @@ private: std::vector m_ArgsWithoutName; }; +extern CmdLineArgs g_CmdLineArgs; + #endif // INCLUDED_CMDLINEARGS diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 94ab83b255..c1c9899fc5 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -441,7 +441,7 @@ static void InitVfs(const CmdLineArgs& args, int flags) // Engine localization files (regular priority, these can be overwritten). g_VFS->Mount(L"l10n/", paths.RData()/"l10n"/""); - MountMods(paths, g_Mods.GetModsFromArguments(args, flags)); + // Mods will be mounted later. // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. @@ -848,26 +848,6 @@ bool Autostart(const CmdLineArgs& args); */ bool AutostartVisualReplay(const std::string& replayFile); -bool EnableModsOrSetDefault(const CmdLineArgs& args, const std::vector& mods, bool fromConfig) -{ - ScriptInterface scriptInterface("Engine", "CheckAndEnableMods", g_ScriptContext); - if (g_Mods.CheckAndEnableMods(scriptInterface, mods)) - return true; - // Here we refuse to start as there is no gui anyway - if (args.Has("autostart-nonvisual")) - { - if (fromConfig) - LOGERROR("Trying to start with incompatible mods from configuration file: %s.", boost::algorithm::join(g_Mods.GetIncompatibleMods(), ", ")); - else - LOGERROR("Trying to start with incompatible mods: %s.", boost::algorithm::join(g_Mods.GetIncompatibleMods(), ", ")); - return false; - } - g_Mods.SetDefaultMods(); - RestartEngine(); - return false; -} - - bool Init(const CmdLineArgs& args, int flags) { h_mgr_init(); @@ -901,7 +881,42 @@ bool Init(const CmdLineArgs& args, int flags) const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024; g_ScriptContext = ScriptContext::CreateContext(contextSize, heapGrowthBytesGCTrigger); - g_Mods.CacheEnabledModVersions(g_ScriptContext); + // On the first Init (INIT_MODS), check for command-line arguments + // or use the default mods from the config and enable those. + // On later engine restarts (e.g. the mod selector), we will skip this path, + // to avoid overwriting the newly selected mods. + if (flags & INIT_MODS) + { + ScriptInterface modInterface("Engine", "Mod", g_ScriptContext); + std::vector mods; + if (args.Has("mod")) + mods = args.GetMultiple("mod"); + else + { + CStr modsStr; + CFG_GET_VAL("mod.enabledmods", modsStr); + boost::split(mods, modsStr, boost::algorithm::is_space(), boost::token_compress_on); + } + + if (!g_Mods.EnableMods(modInterface, mods, flags & INIT_MODS_PUBLIC)) + { + // In non-visual mode, fail entirely. + if (args.Has("autostart-nonvisual")) + { + LOGERROR("Trying to start with incompatible mods: %s.", boost::algorithm::join(g_Mods.GetIncompatibleMods(), ", ")); + return false; + } + LOGWARNING("Invalid mods specified, starting with default mods."); + g_Mods.EnableDefaultMods(modInterface); + } + // Sanity check. + if (!g_Mods.GetIncompatibleMods().empty()) + { + LOGERROR("Trying to start with incompatible mods: %s.", boost::algorithm::join(g_Mods.GetIncompatibleMods(), ", ")); + return false; + } + } + MountMods(Paths(args), g_Mods.GetEnabledMods()); // Special command-line mode to dump the entity schemas instead of running the game. // (This must be done after loading VFS etc, but should be done before wasting time @@ -923,32 +938,6 @@ bool Init(const CmdLineArgs& args, int flags) ISoundManager::CreateSoundManager(); #endif - // Check if there are mods specified on the command line, - // or if we already set the mods (~INIT_MODS), - // else check if there are mods that should be loaded specified - // in the config and load those (by aborting init and restarting - // the engine). - if ((flags & INIT_MODS) == INIT_MODS) - { - if (!args.Has("mod")) - { - CStr modstring; - CFG_GET_VAL("mod.enabledmods", modstring); - if (!modstring.empty()) - { - std::vector mods; - boost::split(mods, modstring, boost::is_any_of(" "), boost::token_compress_on); - if (!EnableModsOrSetDefault(args, mods, true)) - return false; - - RestartEngine(); - return false; - } - } - else if (!EnableModsOrSetDefault(args, g_Mods.GetEnabledMods(), false)) - return false; - } - new L10n; // Optionally start profiler HTTP output automatically diff --git a/source/ps/GameSetup/GameSetup.h b/source/ps/GameSetup/GameSetup.h index a806eebed2..87bf6398b8 100644 --- a/source/ps/GameSetup/GameSetup.h +++ b/source/ps/GameSetup/GameSetup.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -89,6 +89,7 @@ extern const std::vector& GetMods(const CmdLineArgs& args, int flags); * Make sure to call CacheEnabledModVersions after every call to this. */ extern void MountMods(const Paths& paths, const std::vector& mods); + /** * Returns true if successful, false if mods changed and restart_engine was called. * In the latter case the caller should call Shutdown() with SHUTDOWN_FROM_CONFIG. diff --git a/source/ps/Mod.cpp b/source/ps/Mod.cpp index 1b7b76bf0b..afe9b83c77 100644 --- a/source/ps/Mod.cpp +++ b/source/ps/Mod.cpp @@ -24,7 +24,6 @@ #include "lib/file/vfs/vfs.h" #include "lib/utf8.h" #include "ps/Filesystem.h" -#include "ps/GameSetup/CmdLineArgs.h" #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Paths.h" #include "ps/Profiler2.h" @@ -160,58 +159,50 @@ const std::vector& Mod::GetIncompatibleMods() const return m_IncompatibleMods; } -const std::vector& Mod::GetFailedMods() const -{ - return m_FailedMods; -} - -const std::vector& Mod::GetModsFromArguments(const CmdLineArgs& args, int flags) -{ - const bool initMods = (flags & INIT_MODS) == INIT_MODS; - const bool addPublic = (flags & INIT_MODS_PUBLIC) == INIT_MODS_PUBLIC; - - if (!initMods) - return m_ModsLoaded; - - m_ModsLoaded = args.GetMultiple("mod"); - - if (addPublic) - m_ModsLoaded.insert(m_ModsLoaded.begin(), "public"); - - m_ModsLoaded.insert(m_ModsLoaded.begin(), "mod"); - - return m_ModsLoaded; -} - -void Mod::SetDefaultMods() -{ - m_ModsLoaded.clear(); - m_ModsLoaded.insert(m_ModsLoaded.begin(), "mod"); -} - -void Mod::ClearIncompatibleMods() +void Mod::EnableDefaultMods(const ScriptInterface& scriptInterface) { m_IncompatibleMods.clear(); - m_FailedMods.clear(); + m_ModsLoaded.clear(); + m_ModsLoaded.insert(m_ModsLoaded.begin(), "mod"); + CacheEnabledModVersions(scriptInterface); } -bool Mod::CheckAndEnableMods(const ScriptInterface& scriptInterface, const std::vector& mods) +bool Mod::EnableMods(const ScriptInterface& scriptInterface, const std::vector& mods, const bool addPublic) { - ScriptRequest rq(scriptInterface); + m_IncompatibleMods.clear(); + m_ModsLoaded.clear(); - JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); - if (!AreModsCompatible(scriptInterface, mods, availableMods)) + std::unordered_map counts; + for (const CStr& mod : mods) { - m_FailedMods = mods; - return false; + // Ignore duplicates. + if (counts.try_emplace(mod, 0).first->second++ > 0) + continue; + m_ModsLoaded.emplace_back(mod); } - m_ModsLoaded = mods; - return true; + + if (addPublic && counts["public"] == 0) + m_ModsLoaded.insert(m_ModsLoaded.begin(), "public"); + + if (counts["mod"] == 0) + m_ModsLoaded.insert(m_ModsLoaded.begin(), "mod"); + + ScriptRequest rq(scriptInterface); + JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); + m_IncompatibleMods = CheckForIncompatibleMods(scriptInterface, m_ModsLoaded, availableMods); + + for (const CStr& mod : m_IncompatibleMods) + m_ModsLoaded.erase(std::find(m_ModsLoaded.begin(), m_ModsLoaded.end(), mod)); + + CacheEnabledModVersions(scriptInterface); + + return m_IncompatibleMods.empty(); } -bool Mod::AreModsCompatible(const ScriptInterface& scriptInterface, const std::vector& mods, const JS::RootedValue& availableMods) +std::vector Mod::CheckForIncompatibleMods(const ScriptInterface& scriptInterface, const std::vector& mods, const JS::RootedValue& availableMods) const { ScriptRequest rq(scriptInterface); + std::vector incompatibleMods; std::unordered_map> modDependencies; std::unordered_map modNameVersions; for (const CStr& mod : mods) @@ -224,12 +215,12 @@ bool Mod::AreModsCompatible(const ScriptInterface& scriptInterface, const std::v // Requested mod is not available, fail if (!Script::HasProperty(rq, availableMods, mod.c_str())) { - m_IncompatibleMods.push_back(mod); + incompatibleMods.push_back(mod); continue; } if (!Script::GetProperty(rq, availableMods, mod.c_str(), &modData)) { - m_IncompatibleMods.push_back(mod); + incompatibleMods.push_back(mod); continue; } @@ -274,13 +265,13 @@ bool Mod::AreModsCompatible(const ScriptInterface& scriptInterface, const std::v const std::unordered_map::iterator it = modNameVersions.find(modToCheck); if (it == modNameVersions.end()) { - m_IncompatibleMods.push_back(mod); + incompatibleMods.push_back(mod); continue; } // 0.0.25(0ad) , <=, 0.0.24(required version) if (!CompareVersionStrings(it->second, op, versionToCheck)) { - m_IncompatibleMods.push_back(mod); + incompatibleMods.push_back(mod); continue; } break; @@ -289,7 +280,7 @@ bool Mod::AreModsCompatible(const ScriptInterface& scriptInterface, const std::v } - return m_IncompatibleMods.empty(); + return incompatibleMods; } bool Mod::CompareVersionStrings(const CStr& version, const CStr& op, const CStr& required) const @@ -326,9 +317,8 @@ bool Mod::CompareVersionStrings(const CStr& version, const CStr& op, const CStr& } -void Mod::CacheEnabledModVersions(const shared_ptr& scriptContext) +void Mod::CacheEnabledModVersions(const ScriptInterface& scriptInterface) { - ScriptInterface scriptInterface("Engine", "CacheEnabledModVersions", scriptContext); ScriptRequest rq(scriptInterface); JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); diff --git a/source/ps/Mod.h b/source/ps/Mod.h index 2466695f99..f0ec266053 100644 --- a/source/ps/Mod.h +++ b/source/ps/Mod.h @@ -23,14 +23,11 @@ #include -class CmdLineArgs; - -extern CmdLineArgs g_CmdLineArgs; - #define g_Mods (Mod::Instance()) class Mod { + friend class TestMod; public: // Singleton-like interface. static Mod& Instance(); @@ -38,22 +35,19 @@ public: JS::Value GetAvailableMods(const ScriptInterface& scriptInterface) const; const std::vector& GetEnabledMods() const; const std::vector& GetIncompatibleMods() const; - const std::vector& GetFailedMods() const; /** - * This reads the version numbers from the launched mods. - * It caches the result, since the reading of zip files is slow and - * JS pages can request the version numbers too often easily. - * Make sure this is called after each MountMods call. + * Enable the default mods. De-activates any non-default mod currently enabled. */ - void CacheEnabledModVersions(const shared_ptr& scriptContext); + void EnableDefaultMods(const ScriptInterface& scriptInterface); - const std::vector& GetModsFromArguments(const CmdLineArgs& args, int flags); - bool AreModsCompatible(const ScriptInterface& scriptInterface, const std::vector& mods, const JS::RootedValue& availableMods); - bool CheckAndEnableMods(const ScriptInterface& scriptInterface, const std::vector& mods); - bool CompareVersionStrings(const CStr& required, const CStr& op, const CStr& version) const; - void SetDefaultMods(); - void ClearIncompatibleMods(); + /** + * Enables specified mods (& mods required by the engine). + * @param addPublic - if true, enable the public mod. + * @return whether the mods were enabled successfully. This can fail if e.g. mods are incompatible. + * If true, GetEnabledMods() should be non-empty, GetIncompatibleMods() empty. Otherwise, GetIncompatibleMods() is non-empty. + */ + bool EnableMods(const ScriptInterface& scriptInterface, const std::vector& mods, const bool addPublic); /** * Get the loaded mods and their version. @@ -73,9 +67,23 @@ public: JS::Value GetEngineInfo(const ScriptInterface& scriptInterface) const; private: + /** + * This reads the version numbers from the launched mods. + * It caches the result, since the reading of zip files is slow and + * JS pages can request the version numbers too often easily. + * Make sure this is called after each MountMods call. + */ + void CacheEnabledModVersions(const ScriptInterface& scriptInterface); + + /** + * Checks a list of @a mods and returns the incompatible mods, if any. + */ + std::vector CheckForIncompatibleMods(const ScriptInterface& scriptInterface, const std::vector& mods, const JS::RootedValue& availableMods) const; + bool CompareVersionStrings(const CStr& required, const CStr& op, const CStr& version) const; + std::vector m_ModsLoaded; + // Of the currently loaded mods, these are the incompatible with the engine and cannot be loaded. std::vector m_IncompatibleMods; - std::vector m_FailedMods; std::vector> m_LoadedModVersions; }; diff --git a/source/ps/ModIo.cpp b/source/ps/ModIo.cpp index a381c094df..22c639f068 100644 --- a/source/ps/ModIo.cpp +++ b/source/ps/ModIo.cpp @@ -31,6 +31,7 @@ #include "maths/MD5.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" +#include "ps/GameSetup/CmdLineArgs.h" #include "ps/GameSetup/Paths.h" #include "ps/Mod.h" #include "ps/ModInstaller.h" diff --git a/source/ps/Replay.cpp b/source/ps/Replay.cpp index ef9f37ac59..3fb4ac7931 100644 --- a/source/ps/Replay.cpp +++ b/source/ps/Replay.cpp @@ -24,8 +24,11 @@ #include "lib/file/file_system.h" #include "lib/res/h_mgr.h" #include "lib/tex/tex.h" -#include "ps/Game.h" #include "ps/CLogger.h" +#include "ps/Game.h" +#include "ps/GameSetup/GameSetup.h" +#include "ps/GameSetup/CmdLineArgs.h" +#include "ps/GameSetup/Paths.h" #include "ps/Loader.h" #include "ps/Mod.h" #include "ps/Profile.h" @@ -36,6 +39,7 @@ #include "ps/VisualReplay.h" #include "scriptinterface/Object.h" #include "scriptinterface/ScriptContext.h" +#include "scriptinterface/ScriptInterface.h" #include "scriptinterface/ScriptRequest.h" #include "scriptinterface/ScriptStats.h" #include "scriptinterface/JSON.h" @@ -208,43 +212,65 @@ void CReplayPlayer::Replay(const bool serializationtest, const int rejointesttur const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024; g_ScriptContext = ScriptContext::CreateContext(contextSize, heapGrowthBytesGCTrigger); - g_Mods.CacheEnabledModVersions(g_ScriptContext); - - g_Game = new CGame(false); - if (serializationtest) - g_Game->GetSimulation2()->EnableSerializationTest(); - if (rejointestturn >= 0) - g_Game->GetSimulation2()->EnableRejoinTest(rejointestturn); - if (ooslog) - g_Game->GetSimulation2()->EnableOOSLog(); - - // Need some stuff for terrain movement costs: - // (TODO: this ought to be independent of any graphics code) - new CTerrainTextureManager; - g_TexMan.LoadTerrainTextures(); - - // Initialise h_mgr so it doesn't crash when emitting sounds - h_mgr_init(); - std::vector commands; u32 turn = 0; u32 turnLength = 0; { - ScriptRequest rq(g_Game->GetSimulation2()->GetScriptInterface()); std::string type; while ((*m_Stream >> type).good()) { if (type == "start") { - std::string line; - std::getline(*m_Stream, line); + std::string attribsStr; + { + // TODO: it'd be nice to not create a scriptInterface to load JSON. + ScriptInterface scriptInterface("Engine", "Replay", g_ScriptContext); + ScriptRequest rq(scriptInterface); + std::getline(*m_Stream, attribsStr); + JS::RootedValue attribs(rq.cx); + if (!Script::ParseJSON(rq, attribsStr, &attribs)) + { + LOGERROR("Error parsing JSON attributes: %s", attribsStr); + // TODO: do something cleverer than crashing. + ENSURE(false); + } + + // Load the mods specified in the replay. + std::vector> replayMods; + Script::GetProperty(rq, attribs, "mods", replayMods); + std::vector mods; + for (const std::vector& ModAndVersion : replayMods) + if (!ModAndVersion.empty()) + mods.emplace_back(ModAndVersion[0]); + + // Ignore the return value, we check below. + g_Mods.EnableMods(scriptInterface, mods, false); + MountMods(Paths(g_CmdLineArgs), g_Mods.GetEnabledMods()); + + CheckReplayMods(scriptInterface, attribs); + } + + g_Game = new CGame(false); + if (serializationtest) + g_Game->GetSimulation2()->EnableSerializationTest(); + if (rejointestturn >= 0) + g_Game->GetSimulation2()->EnableRejoinTest(rejointestturn); + if (ooslog) + g_Game->GetSimulation2()->EnableOOSLog(); + + // Need some stuff for terrain movement costs: + // (TODO: this ought to be independent of any graphics code) + new CTerrainTextureManager; + g_TexMan.LoadTerrainTextures(); + + // Initialise h_mgr so it doesn't crash when emitting sounds + h_mgr_init(); + + ScriptRequest rq(g_Game->GetSimulation2()->GetScriptInterface()); JS::RootedValue attribs(rq.cx); - ENSURE(Script::ParseJSON(rq, line, &attribs)); - - CheckReplayMods(g_Game->GetSimulation2()->GetScriptInterface(), attribs); - + ENSURE(Script::ParseJSON(rq, attribsStr, &attribs)); g_Game->StartGame(&attribs, ""); // TODO: Non progressive load can fail - need a decent way to handle this @@ -265,6 +291,7 @@ void CReplayPlayer::Replay(const bool serializationtest, const int rejointesttur std::string line; std::getline(*m_Stream, line); + ScriptRequest rq(g_Game->GetSimulation2()->GetScriptInterface()); JS::RootedValue data(rq.cx); Script::ParseJSON(rq, line, &data); Script::FreezeObject(rq, data, true); diff --git a/source/ps/Replay.h b/source/ps/Replay.h index 61d4c8348a..a5590ae1d5 100644 --- a/source/ps/Replay.h +++ b/source/ps/Replay.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,6 +22,8 @@ #include "ps/CStr.h" #include "scriptinterface/ScriptTypes.h" +#include + struct SimulationCommand; class CSimulation2; class ScriptInterface; diff --git a/source/ps/VisualReplay.cpp b/source/ps/VisualReplay.cpp index 42cdbb2308..74d580a868 100644 --- a/source/ps/VisualReplay.cpp +++ b/source/ps/VisualReplay.cpp @@ -28,6 +28,7 @@ #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/Game.h" +#include "ps/GameSetup/CmdLineArgs.h" #include "ps/GameSetup/Paths.h" #include "ps/Mod.h" #include "ps/Pyrogenesis.h" diff --git a/source/ps/scripting/JSInterface_Mod.cpp b/source/ps/scripting/JSInterface_Mod.cpp index ae81dd324b..b1409a563c 100644 --- a/source/ps/scripting/JSInterface_Mod.cpp +++ b/source/ps/scripting/JSInterface_Mod.cpp @@ -33,17 +33,16 @@ Mod* ModGetter(const ScriptRequest&, JS::CallArgs&) bool SetModsAndRestartEngine(const ScriptInterface& scriptInterface, const std::vector& mods) { - g_Mods.ClearIncompatibleMods(); - if (!g_Mods.CheckAndEnableMods(scriptInterface, mods)) + if (!g_Mods.EnableMods(scriptInterface, mods, false)) return false; RestartEngine(); return true; } -bool HasFailedMods() +bool HasIncompatibleMods() { - return g_Mods.GetFailedMods().size() > 0; + return g_Mods.GetIncompatibleMods().size() > 0; } void RegisterScriptFunctions(const ScriptRequest& rq) @@ -51,8 +50,8 @@ void RegisterScriptFunctions(const ScriptRequest& rq) ScriptFunction::Register<&Mod::GetEngineInfo, ModGetter>(rq, "GetEngineInfo"); ScriptFunction::Register<&Mod::GetAvailableMods, ModGetter>(rq, "GetAvailableMods"); ScriptFunction::Register<&Mod::GetEnabledMods, ModGetter>(rq, "GetEnabledMods"); - ScriptFunction::Register (rq, "HasFailedMods"); - ScriptFunction::Register<&Mod::GetFailedMods, ModGetter>(rq, "GetFailedMods"); + ScriptFunction::Register (rq, "HasIncompatibleMods"); + ScriptFunction::Register<&Mod::GetIncompatibleMods, ModGetter>(rq, "GetIncompatibleMods"); ScriptFunction::Register<&SetModsAndRestartEngine>(rq, "SetModsAndRestartEngine"); } } diff --git a/source/ps/tests/test_Mod.h b/source/ps/tests/test_Mod.h index 0e9ab7cc2f..09a4fa409f 100644 --- a/source/ps/tests/test_Mod.h +++ b/source/ps/tests/test_Mod.h @@ -29,6 +29,7 @@ class TestMod : public CxxTest::TestSuite { + Mod m_Mods; public: void test_version_check() { @@ -42,45 +43,45 @@ public: CStr version = "0.0.24";// 0ad version // 0.0.24 = 0.0.24 - TS_ASSERT(g_Mods.CompareVersionStrings(version, eq, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, lt, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, gt, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, leq, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, geq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, eq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, lt, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, gt, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, leq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, geq, required)); // 0.0.23 <= 0.0.24 version = "0.0.23"; - TS_ASSERT(!g_Mods.CompareVersionStrings(version, eq, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, lt, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, gt, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, leq, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, geq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, eq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, lt, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, gt, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, leq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, geq, required)); // 0.0.25 >= 0.0.24 version = "0.0.25"; - TS_ASSERT(!g_Mods.CompareVersionStrings(version, eq, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, lt, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, gt, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, leq, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, geq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, eq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, lt, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, gt, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, leq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, geq, required)); // 0.0.9 <= 0.1.0 version = "0.0.9"; required = "0.1.0"; - TS_ASSERT(!g_Mods.CompareVersionStrings(version, eq, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, lt, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, gt, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, leq, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, geq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, eq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, lt, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, gt, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, leq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, geq, required)); // 5.3 <= 5.3.0 version = "5.3"; required = "5.3.0"; - TS_ASSERT(!g_Mods.CompareVersionStrings(version, eq, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, lt, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, gt, required)); - TS_ASSERT(g_Mods.CompareVersionStrings(version, leq, required)); - TS_ASSERT(!g_Mods.CompareVersionStrings(version, geq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, eq, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, lt, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, gt, required)); + TS_ASSERT(m_Mods.CompareVersionStrings(version, leq, required)); + TS_ASSERT(!m_Mods.CompareVersionStrings(version, geq, required)); } void test_compatible() @@ -148,38 +149,32 @@ public: mods.clear(); mods.push_back("public"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); mods.clear(); mods.push_back("mod"); mods.push_back("public"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); mods.clear(); mods.push_back("public"); mods.push_back("good"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); mods.clear(); mods.push_back("public"); mods.push_back("good2"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); mods.clear(); mods.push_back("public"); mods.push_back("wrong"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(!g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(!m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); mods.clear(); mods.push_back("public"); mods.push_back("does_not_exist"); - g_Mods.ClearIncompatibleMods(); - TS_ASSERT(!g_Mods.AreModsCompatible(script, mods, availableMods)); + TS_ASSERT(!m_Mods.CheckForIncompatibleMods(script, mods, availableMods).empty()); } };