Changeset View
Standalone View
source/ps/Mod.cpp
Show All 9 Lines | ||||||||||||||||||||||||||||||||||
* 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 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; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
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") | ||||||||||||||||||||||||||||||||||
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 All 10 Lines | 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}); | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
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); | |||||||||||||||||||||||||||||||||
Not Done Inline ActionsPlease, follow CC. The same below. vladislavbelov: Please, follow CC. The same below. | ||||||||||||||||||||||||||||||||||
return returnValue; | return returnValue; | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
JS::Value Mod::GetEngineInfo(const ScriptInterface& scriptInterface) | JS::Value Mod::GetEngineInfo(const ScriptInterface& scriptInterface) | |||||||||||||||||||||||||||||||||
{ | { | |||||||||||||||||||||||||||||||||
ScriptRequest rq(scriptInterface); | ScriptRequest rq(scriptInterface); | |||||||||||||||||||||||||||||||||
JS::RootedValue mods(rq.cx, Mod::GetLoadedModsWithVersions(scriptInterface)); | JS::RootedValue mods(rq.cx, Mod::GetLoadedModsWithVersions(scriptInterface)); | |||||||||||||||||||||||||||||||||
Not Done Inline ActionsProbably need <vector> Stan: Probably need <vector> | ||||||||||||||||||||||||||||||||||
JS::RootedValue metainfo(rq.cx); | JS::RootedValue metainfo(rq.cx); | |||||||||||||||||||||||||||||||||
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. | ||||||||||||||||||||||||||||||||||
ScriptInterface::CreateObject( | ScriptInterface::CreateObject( | |||||||||||||||||||||||||||||||||
rq, | rq, | |||||||||||||||||||||||||||||||||
&metainfo, | &metainfo, | |||||||||||||||||||||||||||||||||
"engine_version", engine_version, | "engine_version", engine_version, | |||||||||||||||||||||||||||||||||
"mods", mods); | "mods", mods); | |||||||||||||||||||||||||||||||||
scriptInterface.FreezeObject(metainfo, true); | scriptInterface.FreezeObject(metainfo, true); | |||||||||||||||||||||||||||||||||
return metainfo; | return metainfo; | |||||||||||||||||||||||||||||||||
} | } | |||||||||||||||||||||||||||||||||
Not Done Inline ActionsDo you need ordering? Stan: Do you need ordering? | ||||||||||||||||||||||||||||||||||
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… | ||||||||||||||||||||||||||||||||||
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 :) | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsstatic const wraitii: static const | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionscould also static, but maybe not worth it here wraitii: could also static, but maybe not worth it here | ||||||||||||||||||||||||||||||||||
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… | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsNot clear what to what relation. vladislavbelov: Not clear what to what relation. | ||||||||||||||||||||||||||||||||||
Not Done Inline ActionsMight be a full name. vladislavbelov: Might be a full name. | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionssize_t. vladislavbelov: `size_t`. | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionssize_t. vladislavbelov: `size_t`. | ||||||||||||||||||||||||||||||||||
Not Done Inline Actionsoperator[] instead of at. vladislavbelov: operator[] instead of at. | ||||||||||||||||||||||||||||||||||
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 :) |