Changeset View
Standalone View
source/ps/Mod.cpp
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | |||||||||||||||||||||||||||||||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | |||||||||||||||||||||||||||||||||
* | * | |||||||||||||||||||||||||||||||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | |||||||||||||||||||||||||||||||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | |||||||||||||||||||||||||||||||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | |||||||||||||||||||||||||||||||||
* (at your option) any later version. | * (at your option) any later version. | |||||||||||||||||||||||||||||||||
* | * | |||||||||||||||||||||||||||||||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | |||||||||||||||||||||||||||||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||||||||||||||||||||||||||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||||||||||||||||||||||||||||||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||||||||||||||||||||||||||||||||
* | * | |||||||||||||||||||||||||||||||||
* 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" | |||||||||||||||||||||||||||||||||
StanUnsubmitted Not Done Inline Actions
Stan: | ||||||||||||||||||||||||||||||||||
#include <algorithm> | ||||||||||||||||||||||||||||||||||
#include <boost/algorithm/string/split.hpp> | ||||||||||||||||||||||||||||||||||
#include <boost/algorithm/string/classification.hpp> | ||||||||||||||||||||||||||||||||||
#include <unordered_map> | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsI think boost and other <> includes are ordered alphabetically. @wraitii @vladislavbelov Stan: I think boost and other <> includes are ordered alphabetically. @wraitii @vladislavbelov | ||||||||||||||||||||||||||||||||||
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 56 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; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
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(); | |||||||||||||||||||||||||||||||||
for (const CStr& mod : g_modsLoaded) | for (const CStr& mod : g_modsLoaded) | |||||||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||||||
// Ignore user and mod mod as they are irrelevant for compatibility checks | // Ignore user and mod mod as they are irrelevant for compatibility checks | |||||||||||||||||||||||||||||||||
if (mod == "mod" || mod == "user") | if (mod == "mod" || mod == "user") | |||||||||||||||||||||||||||||||||
continue; | continue; | |||||||||||||||||||||||||||||||||
CStr version; | CStr version; | |||||||||||||||||||||||||||||||||
JS::RootedValue modData(rq.cx); | JS::RootedValue modData(rq.cx); | |||||||||||||||||||||||||||||||||
if (scriptInterface.GetProperty(availableMods, mod.c_str(), &modData)) | if (scriptInterface.GetProperty(availableMods, mod.c_str(), &modData)) | |||||||||||||||||||||||||||||||||
scriptInterface.GetProperty(modData, "version", version); | scriptInterface.GetProperty(modData, "version", version); | |||||||||||||||||||||||||||||||||
g_LoadedModVersions.push_back({mod, version}); | g_LoadedModVersions.push_back({mod, version}); | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
const std::vector<CStr>& Mod::GetModsFromArguments(const CmdLineArgs& args, int flags) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
const bool initMods = (flags & INIT_MODS) == INIT_MODS; | ||||||||||||||||||||||||||||||||||
const bool addUser = !InDevelopmentCopy() && !args.Has("noUserMod"); | ||||||||||||||||||||||||||||||||||
const bool addPublic = (flags & INIT_MODS_PUBLIC) == INIT_MODS_PUBLIC; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsPlease, follow CC. The same below. vladislavbelov: Please, follow CC. The same below. | ||||||||||||||||||||||||||||||||||
if (!initMods) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
// Add the user mod if it should be present | ||||||||||||||||||||||||||||||||||
if (addUser && (g_modsLoaded.empty() || g_modsLoaded.back() != "user")) | ||||||||||||||||||||||||||||||||||
g_modsLoaded.push_back("user"); | ||||||||||||||||||||||||||||||||||
return g_modsLoaded; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsProbably need <vector> Stan: Probably need <vector> | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsdo you need ordering here and below else use unordered_map and the correct include. Stan: do you need ordering here and below else use unordered_map and the correct include. | ||||||||||||||||||||||||||||||||||
g_modsLoaded = args.GetMultiple("mod"); | ||||||||||||||||||||||||||||||||||
if (addPublic) | ||||||||||||||||||||||||||||||||||
g_modsLoaded.insert(g_modsLoaded.begin(), "public"); | ||||||||||||||||||||||||||||||||||
g_modsLoaded.insert(g_modsLoaded.begin(), "mod"); | ||||||||||||||||||||||||||||||||||
// Add the user mod if not explicitly disabled or we have a dev copy so | ||||||||||||||||||||||||||||||||||
// that saved files end up in version control and not in the user mod. | ||||||||||||||||||||||||||||||||||
if (addUser) | ||||||||||||||||||||||||||||||||||
g_modsLoaded.push_back("user"); | ||||||||||||||||||||||||||||||||||
return g_modsLoaded; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
void Mod::SetDefaultMods(const CmdLineArgs& args, int flags) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
const bool addUser = !InDevelopmentCopy() && !args.Has("noUserMod"); | ||||||||||||||||||||||||||||||||||
g_modsLoaded.clear(); | ||||||||||||||||||||||||||||||||||
g_modsLoaded.insert(g_modsLoaded.begin(), "mod"); | ||||||||||||||||||||||||||||||||||
// Add the user mod if not explicitly disabled or we have a dev copy so | ||||||||||||||||||||||||||||||||||
// that saved files end up in version control and not in the user mod. | ||||||||||||||||||||||||||||||||||
if (addUser) | ||||||||||||||||||||||||||||||||||
g_modsLoaded.push_back("user"); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
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; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsNot clear what to what relation. vladislavbelov: Not clear what to what relation. | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
g_modsLoaded = mods; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsDo you need ordering? Stan: Do you need ordering? | ||||||||||||||||||||||||||||||||||
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" || mod == "user") | ||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsWhat's going to happen if some of these properties are not defined? vladislavbelov: What's going to happen if some of these properties are not defined? | ||||||||||||||||||||||||||||||||||
Done Inline Actionsthey will be "undefined", if some other mod would want to refer to one without version, dependencies will be not met, if with name, it will be not met, if undefined dependencies actually mod page itself breaks Silier: they will be "undefined", if some other mod would want to refer to one without version… | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsI think we shouldn't break the page if a mod is broken. Because a user might not know what's causing the error. vladislavbelov: I think we shouldn't break the page if a mod is broken. Because a user might not know what's… | ||||||||||||||||||||||||||||||||||
JS::RootedValue modData(rq.cx); | ||||||||||||||||||||||||||||||||||
// Requested mod is not available, fail | ||||||||||||||||||||||||||||||||||
if (!scriptInterface.HasProperty(availableMods, mod.c_str())) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
g_incompatibleMods.push_back(mod); | ||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsstatic const wraitii: static const | ||||||||||||||||||||||||||||||||||
if (!scriptInterface.GetProperty(availableMods, mod.c_str(), &modData)) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
g_incompatibleMods.push_back(mod); | ||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
std::vector<CStr> deps; | ||||||||||||||||||||||||||||||||||
boost::split(deps, dependencies, boost::is_any_of(","), boost::token_compress_on); | ||||||||||||||||||||||||||||||||||
modDependencies.emplace(mod, deps); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
static const std::vector<CStr> toCheck = { "<=", ">=", "=", "<", ">" }; | ||||||||||||||||||||||||||||||||||
for (const CStr& mod : mods) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
if (mod == "mod" || mod == "user") | ||||||||||||||||||||||||||||||||||
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; | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsMight be a full name. vladislavbelov: Might be a full name. | ||||||||||||||||||||||||||||||||||
//0ad | ||||||||||||||||||||||||||||||||||
const CStr modToCheck = dep.substr(0, pos); | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionscould also static, but maybe not worth it here wraitii: could also static, but maybe not worth it here | ||||||||||||||||||||||||||||||||||
//0.0.24 | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsI wonder if there is a smarter way to do it, e.g. using a lambda function. Stan: I wonder if there is a smarter way to do it, e.g. using a lambda function.
See also https… | ||||||||||||||||||||||||||||||||||
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) | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionssize_t. vladislavbelov: `size_t`. | ||||||||||||||||||||||||||||||||||
if (!CompareVersionStrings(it->second, op, versionToCheck)) | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionssize_t. vladislavbelov: `size_t`. | ||||||||||||||||||||||||||||||||||
g_incompatibleMods.push_back(mod); | ||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsoperator[] instead of at. vladislavbelov: operator[] instead of at. | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
return g_incompatibleMods.empty(); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsThat's bad and implementation defined. vladislavbelov: That's bad and implementation defined. | ||||||||||||||||||||||||||||||||||
Done Inline Actionswhat do you refer to? Silier: what do you refer to? | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsThe difference of two unsigned values. vladislavbelov: The difference of two unsigned values. | ||||||||||||||||||||||||||||||||||
Done Inline Actionsyes, you are correct I totally missed that :) Silier: yes, you are correct I totally missed that :) | ||||||||||||||||||||||||||||||||||
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 = "-,_"; | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsdoes this handle lteq ? Stan: does this handle lteq ? | ||||||||||||||||||||||||||||||||||
Done Inline Actionsyes it does, as essentially it is or less or equal and both cases are handled :) Silier: yes it does, as essentially it is or less or equal and both cases are handled :) | ||||||||||||||||||||||||||||||||||
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; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
JS::Value Mod::GetLoadedModsWithVersions(const ScriptInterface& scriptInterface) | JS::Value Mod::GetLoadedModsWithVersions(const ScriptInterface& scriptInterface) | |||||||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||||||
ScriptRequest rq(scriptInterface); | ScriptRequest rq(scriptInterface); | |||||||||||||||||||||||||||||||||
JS::RootedValue returnValue(rq.cx); | JS::RootedValue returnValue(rq.cx); | |||||||||||||||||||||||||||||||||
scriptInterface.ToJSVal(rq, &returnValue, g_LoadedModVersions); | scriptInterface.ToJSVal(rq, &returnValue, g_LoadedModVersions); | |||||||||||||||||||||||||||||||||
return returnValue; | return returnValue; | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
Show All 17 Lines |