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:
parent
1e297fe212
commit
3bcf360107
@ -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]);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user