Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/ps/Mod.cpp
Show All 13 Lines | |||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "ps/Mod.h" | #include "ps/Mod.h" | ||||
#include <algorithm> | |||||
#include "lib/file/file_system.h" | #include "lib/file/file_system.h" | ||||
#include "lib/file/vfs/vfs.h" | #include "lib/file/vfs/vfs.h" | ||||
#include "lib/utf8.h" | #include "lib/utf8.h" | ||||
#include "ps/Filesystem.h" | #include "ps/Filesystem.h" | ||||
#include "ps/GameSetup/GameSetup.h" | #include "ps/GameSetup/GameSetup.h" | ||||
#include "ps/GameSetup/Paths.h" | #include "ps/GameSetup/Paths.h" | ||||
#include "ps/Pyrogenesis.h" | #include "ps/Pyrogenesis.h" | ||||
#include "scriptinterface/ScriptInterface.h" | #include "scriptinterface/ScriptInterface.h" | ||||
#include <algorithm> | |||||
#include <boost/algorithm/string/split.hpp> | |||||
#include <boost/algorithm/string/classification.hpp> | |||||
#include <unordered_map> | |||||
std::vector<CStr> g_modsLoaded; | std::vector<CStr> g_modsLoaded; | ||||
std::vector<CStr> g_incompatibleMods; | |||||
std::vector<CStr> g_failedMods; | |||||
std::vector<std::vector<CStr>> g_LoadedModVersions; | std::vector<std::vector<CStr>> g_LoadedModVersions; | ||||
CmdLineArgs g_args; | CmdLineArgs g_args; | ||||
JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface) | JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface) | ||||
{ | { | ||||
ScriptRequest rq(scriptInterface); | ScriptRequest rq(scriptInterface); | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | for (DirectoryNames::iterator iter = modDirsUser.begin(); iter != modDirsUser.end(); ++iter) | ||||
// Valid mod, add it to our structure | // Valid mod, add it to our structure | ||||
JS_SetProperty(rq.cx, obj, utf8_from_wstring(iter->string()).c_str(), json); | JS_SetProperty(rq.cx, obj, utf8_from_wstring(iter->string()).c_str(), json); | ||||
} | } | ||||
return JS::ObjectValue(*obj); | return JS::ObjectValue(*obj); | ||||
} | } | ||||
const std::vector<CStr>& Mod::GetEnabledMods() | |||||
{ | |||||
return g_modsLoaded; | |||||
} | |||||
const std::vector<CStr>& Mod::GetIncompatibleMods() | |||||
{ | |||||
return g_incompatibleMods; | |||||
} | |||||
const std::vector<CStr>& Mod::GetFailedMods() | |||||
{ | |||||
return g_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 g_modsLoaded; | |||||
g_modsLoaded = args.GetMultiple("mod"); | |||||
if (addPublic) | |||||
g_modsLoaded.insert(g_modsLoaded.begin(), "public"); | |||||
g_modsLoaded.insert(g_modsLoaded.begin(), "mod"); | |||||
return g_modsLoaded; | |||||
} | |||||
void Mod::SetDefaultMods(const CmdLineArgs& args, int flags) | |||||
{ | |||||
g_modsLoaded.clear(); | |||||
g_modsLoaded.insert(g_modsLoaded.begin(), "mod"); | |||||
} | |||||
void Mod::ClearIncompatibleMods() | |||||
{ | |||||
g_incompatibleMods.clear(); | |||||
g_failedMods.clear(); | |||||
} | |||||
bool Mod::CheckAndEnableMods(const ScriptInterface& scriptInterface, const std::vector<CStr>& mods) | |||||
{ | |||||
ScriptRequest rq(scriptInterface); | |||||
JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); | |||||
if (!AreModsCompatible(scriptInterface, mods, availableMods)) | |||||
{ | |||||
g_failedMods = mods; | |||||
return false; | |||||
} | |||||
g_modsLoaded = mods; | |||||
return true; | |||||
} | |||||
bool Mod::AreModsCompatible(const ScriptInterface& scriptInterface, const std::vector<CStr>& mods, const JS::RootedValue& availableMods) | |||||
{ | |||||
ScriptRequest rq(scriptInterface); | |||||
std::unordered_map<CStr, std::vector<CStr>> modDependencies; | |||||
std::unordered_map<CStr, CStr> modNameVersions; | |||||
for (const CStr& mod : mods) | |||||
{ | |||||
if (mod == "mod") | |||||
continue; | |||||
JS::RootedValue modData(rq.cx); | |||||
// Requested mod is not available, fail | |||||
if (!scriptInterface.HasProperty(availableMods, mod.c_str())) | |||||
{ | |||||
g_incompatibleMods.push_back(mod); | |||||
continue; | |||||
} | |||||
if (!scriptInterface.GetProperty(availableMods, mod.c_str(), &modData)) | |||||
{ | |||||
g_incompatibleMods.push_back(mod); | |||||
continue; | |||||
} | |||||
std::vector<CStr> dependencies; | |||||
CStr version; | |||||
CStr name; | |||||
scriptInterface.GetProperty(modData, "dependencies", dependencies); | |||||
scriptInterface.GetProperty(modData, "version", version); | |||||
scriptInterface.GetProperty(modData, "name", name); | |||||
modNameVersions.emplace(name, version); | |||||
modDependencies.emplace(mod, dependencies); | |||||
} | |||||
static const std::vector<CStr> toCheck = { "<=", ">=", "=", "<", ">" }; | |||||
for (const CStr& mod : mods) | |||||
{ | |||||
if (mod == "mod") | |||||
continue; | |||||
const std::unordered_map<CStr, std::vector<CStr>>::iterator res = modDependencies.find(mod); | |||||
if (res == modDependencies.end()) | |||||
continue; | |||||
const std::vector<CStr> deps = res->second; | |||||
if (deps.empty()) | |||||
continue; | |||||
for (const CStr& dep : deps) | |||||
{ | |||||
if (dep.empty()) | |||||
continue; | |||||
// 0ad<=0.0.24 | |||||
for (const CStr& op : toCheck) | |||||
{ | |||||
const int pos = dep.Find(op.c_str()); | |||||
if (pos == -1) | |||||
continue; | |||||
//0ad | |||||
const CStr modToCheck = dep.substr(0, pos); | |||||
//0.0.24 | |||||
const CStr versionToCheck = dep.substr(pos + op.size()); | |||||
const std::unordered_map<CStr, CStr>::iterator it = modNameVersions.find(modToCheck); | |||||
if (it == modNameVersions.end()) | |||||
{ | |||||
g_incompatibleMods.push_back(mod); | |||||
continue; | |||||
} | |||||
// 0.0.25(0ad) , <=, 0.0.24(required version) | |||||
if (!CompareVersionStrings(it->second, op, versionToCheck)) | |||||
{ | |||||
g_incompatibleMods.push_back(mod); | |||||
continue; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return g_incompatibleMods.empty(); | |||||
} | |||||
bool Mod::CompareVersionStrings(const CStr& version, const CStr& op, const CStr& required) | |||||
{ | |||||
std::vector<CStr> versionSplit; | |||||
std::vector<CStr> requiredSplit; | |||||
static const std::string toIgnore = "-,_"; | |||||
boost::split(versionSplit, version, boost::is_any_of(toIgnore), boost::token_compress_on); | |||||
boost::split(requiredSplit, required, boost::is_any_of(toIgnore), boost::token_compress_on); | |||||
boost::split(versionSplit, versionSplit[0], boost::is_any_of("."), boost::token_compress_on); | |||||
boost::split(requiredSplit, requiredSplit[0], boost::is_any_of("."), boost::token_compress_on); | |||||
const bool eq = op.Find("=") != -1; | |||||
const bool lt = op.Find("<") != -1; | |||||
const bool gt = op.Find(">") != -1; | |||||
const size_t min = std::min(versionSplit.size(), requiredSplit.size()); | |||||
for (size_t i = 0; i < min; ++i) | |||||
{ | |||||
const int diff = versionSplit[i].ToInt() - requiredSplit[i].ToInt(); | |||||
if (gt && diff > 0 || lt && diff < 0) | |||||
return true; | |||||
if (gt && diff < 0 || lt && diff > 0 || eq && diff) | |||||
return false; | |||||
} | |||||
const size_t versionSize = versionSplit.size(); | |||||
const size_t requiredSize = requiredSplit.size(); | |||||
if (versionSize == requiredSize) | |||||
return eq; | |||||
return versionSize < requiredSize ? lt : gt; | |||||
} | |||||
void Mod::CacheEnabledModVersions(const shared_ptr<ScriptContext>& scriptContext) | void Mod::CacheEnabledModVersions(const shared_ptr<ScriptContext>& scriptContext) | ||||
{ | { | ||||
ScriptInterface scriptInterface("Engine", "CacheEnabledModVersions", scriptContext); | ScriptInterface scriptInterface("Engine", "CacheEnabledModVersions", scriptContext); | ||||
ScriptRequest rq(scriptInterface); | ScriptRequest rq(scriptInterface); | ||||
JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); | JS::RootedValue availableMods(rq.cx, GetAvailableMods(scriptInterface)); | ||||
g_LoadedModVersions.clear(); | g_LoadedModVersions.clear(); | ||||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator