1
1
forked from 0ad/0ad

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.
This commit is contained in:
wraitii 2021-05-20 14:36:42 +00:00
parent 1e297fe212
commit 3bcf360107
13 changed files with 204 additions and 199 deletions

View File

@ -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]);

View File

@ -99,7 +99,6 @@ that of Atlas depending on commandline parameters.
#include <chrono>
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();

View File

@ -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<CStr> m_ArgsWithoutName;
};
extern CmdLineArgs g_CmdLineArgs;
#endif // INCLUDED_CMDLINEARGS

View File

@ -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<CStr>& 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<CStr> 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<CStr> 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

View File

@ -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<CStr>& 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<CStr>& 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.

View File

@ -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<CStr>& Mod::GetIncompatibleMods() const
return m_IncompatibleMods;
}
const std::vector<CStr>& Mod::GetFailedMods() const
{
return m_FailedMods;
}
const std::vector<CStr>& 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<CStr>& mods)
bool Mod::EnableMods(const ScriptInterface& scriptInterface, const std::vector<CStr>& 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<CStr, int> 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<CStr>& mods, const JS::RootedValue& availableMods)
std::vector<CStr> Mod::CheckForIncompatibleMods(const ScriptInterface& scriptInterface, const std::vector<CStr>& mods, const JS::RootedValue& availableMods) const
{
ScriptRequest rq(scriptInterface);
std::vector<CStr> incompatibleMods;
std::unordered_map<CStr, std::vector<CStr>> modDependencies;
std::unordered_map<CStr, CStr> 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<CStr, CStr>::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>& scriptContext)
void Mod::CacheEnabledModVersions(const ScriptInterface& scriptInterface)
{
ScriptInterface scriptInterface("Engine", "CacheEnabledModVersions", scriptContext);
ScriptRequest rq(scriptInterface);
JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface));

View File

@ -23,14 +23,11 @@
#include <vector>
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<CStr>& GetEnabledMods() const;
const std::vector<CStr>& GetIncompatibleMods() const;
const std::vector<CStr>& 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>& scriptContext);
void EnableDefaultMods(const ScriptInterface& scriptInterface);
const std::vector<CStr>& GetModsFromArguments(const CmdLineArgs& args, int flags);
bool AreModsCompatible(const ScriptInterface& scriptInterface, const std::vector<CStr>& mods, const JS::RootedValue& availableMods);
bool CheckAndEnableMods(const ScriptInterface& scriptInterface, const std::vector<CStr>& 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<CStr>& 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<CStr> CheckForIncompatibleMods(const ScriptInterface& scriptInterface, const std::vector<CStr>& mods, const JS::RootedValue& availableMods) const;
bool CompareVersionStrings(const CStr& required, const CStr& op, const CStr& version) const;
std::vector<CStr> m_ModsLoaded;
// Of the currently loaded mods, these are the incompatible with the engine and cannot be loaded.
std::vector<CStr> m_IncompatibleMods;
std::vector<CStr> m_FailedMods;
std::vector<std::vector<CStr>> m_LoadedModVersions;
};

View File

@ -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"

View File

@ -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<SimulationCommand> 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<std::vector<CStr>> replayMods;
Script::GetProperty(rq, attribs, "mods", replayMods);
std::vector<CStr> mods;
for (const std::vector<CStr>& 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);

View File

@ -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 <vector>
struct SimulationCommand;
class CSimulation2;
class ScriptInterface;

View File

@ -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"

View File

@ -33,17 +33,16 @@ Mod* ModGetter(const ScriptRequest&, JS::CallArgs&)
bool SetModsAndRestartEngine(const ScriptInterface& scriptInterface, const std::vector<CStr>& 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<HasFailedMods> (rq, "HasFailedMods");
ScriptFunction::Register<&Mod::GetFailedMods, ModGetter>(rq, "GetFailedMods");
ScriptFunction::Register<HasIncompatibleMods> (rq, "HasIncompatibleMods");
ScriptFunction::Register<&Mod::GetIncompatibleMods, ModGetter>(rq, "GetIncompatibleMods");
ScriptFunction::Register<&SetModsAndRestartEngine>(rq, "SetModsAndRestartEngine");
}
}

View File

@ -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());
}
};