forked from teamnwah/openmw-tes3coop
[Server] Load mods in dependencies order
This commit is contained in:
parent
66283943c5
commit
7a0b45d456
3 changed files with 142 additions and 64 deletions
|
@ -13,7 +13,6 @@ void EventController::Init(LuaState &lua)
|
|||
|
||||
eventsTable["register"] = [&lua](int event, sol::function func, sol::this_environment te) {
|
||||
sol::environment& env = te;
|
||||
string modname = env["ModInfo"]["name"];
|
||||
lua.getEventCtrl().registerEvent(event, env, func);
|
||||
};
|
||||
eventsTable["stop"] = [&lua](int event) {
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <apps/openmw-mp/Quests.hpp>
|
||||
#include <apps/openmw-mp/Spells.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include "LuaState.hpp"
|
||||
#include "EventController.hpp"
|
||||
#include "luaUtils.hpp"
|
||||
|
@ -230,11 +232,11 @@ LuaState::LuaState()
|
|||
sol::environment LuaState::openScript(std::string homePath, std::string modname)
|
||||
{
|
||||
cout << "Loading file: " << homePath + "/mods/" + modname + "/main.lua" << endl;
|
||||
|
||||
sol::environment env(*lua, sol::create, lua->globals());
|
||||
std::string package_path = env["package"]["path"];
|
||||
env["package"]["path"] = Utils::convertPath(homePath + "/mods/" + modname + "/?.lua") + ";" + package_path;
|
||||
package_path = env["package"]["path"];
|
||||
//env["ModInfo"] = sol::create;
|
||||
|
||||
lua->set_function("getDataFolder", [homePath, modname]() -> const string {
|
||||
return homePath + "/data/" + modname + '/';
|
||||
|
@ -246,29 +248,6 @@ sol::environment LuaState::openScript(std::string homePath, std::string modname)
|
|||
|
||||
lua->script_file(homePath + "/mods/" + modname + "/main.lua", env);
|
||||
|
||||
sol::table modinfo = env["ModInfo"];
|
||||
if (!modinfo.valid())
|
||||
throw runtime_error("ModInfo table for \"" + modname + "\" not found");
|
||||
|
||||
if (modinfo["name"].get_type() != sol::type::string)
|
||||
throw runtime_error(modname + R"(: ModInfo["name"] undefined or not string")");
|
||||
|
||||
if (modinfo["version"].get_type() != sol::type::string)
|
||||
throw runtime_error(modname + R"(: ModInfo["version"] undefined or not string")");
|
||||
|
||||
sol::table dependencies = modinfo["dependencies"];
|
||||
|
||||
if (dependencies.get_type() != sol::type::table)
|
||||
throw runtime_error(modname + R"(: ModInfo["dependencies"] undefined or not table")");
|
||||
|
||||
string name = env["ModInfo"]["name"];
|
||||
|
||||
mods.emplace(name, env);
|
||||
sol::table loaded = dataEnv["Core"]["loadedMods"];
|
||||
loaded.add(name);
|
||||
|
||||
cout << "modname: " << name << endl;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
|
@ -355,18 +334,131 @@ int CompVersion(const string &wishVersion, const string &depVersionFound)
|
|||
return 0;
|
||||
}
|
||||
|
||||
vector<vector<ServerPluginInfo>::iterator> loadOrderSolver(vector<ServerPluginInfo> &mods)
|
||||
{
|
||||
vector<vector<ServerPluginInfo>::iterator> notResolved;
|
||||
vector<vector<ServerPluginInfo>::iterator> result;
|
||||
|
||||
for (auto it = mods.begin(); it != mods.end(); ++it)
|
||||
{
|
||||
for (auto &dependency : it->dependencies)
|
||||
{
|
||||
const std::string &depNameRequest = dependency.first;
|
||||
const std::string &depVersionRequest = dependency.second;
|
||||
|
||||
const auto &depEnvIt = find_if(mods.begin(), mods.end(), [&depNameRequest](const auto &val) {
|
||||
return val.name == depNameRequest;
|
||||
});
|
||||
|
||||
if (depEnvIt != mods.end())
|
||||
{
|
||||
const string &depVersionFound = depEnvIt->version;
|
||||
if (CompVersion(depVersionRequest, depVersionFound) != 0)
|
||||
{
|
||||
stringstream sstr;
|
||||
sstr << depNameRequest << ": version \"" << depVersionFound
|
||||
<< "\" is not applicable for \"" << it->name << "\" expected \""
|
||||
<< depVersionRequest + "\"";
|
||||
throw runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
else // dependency not found.
|
||||
{
|
||||
stringstream sstr;
|
||||
sstr << depNameRequest + " \"" << depVersionRequest << "\" not found.";
|
||||
throw runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (it->dependencies.empty()) // if server plugin doesn't have any dependencies we can safely put it on result
|
||||
result.push_back(it);
|
||||
else
|
||||
notResolved.push_back(it);
|
||||
}
|
||||
|
||||
while (true) // Iterate notResolved until it becomes empty
|
||||
{
|
||||
for (auto it = notResolved.begin(); it != notResolved.end();)
|
||||
{
|
||||
bool resolved = true;
|
||||
for (auto &dep : (*it)->dependencies)
|
||||
{
|
||||
auto found = find_if(result.begin(), result.end(), [&dep](auto &a) {
|
||||
return dep.first == a->name;
|
||||
});
|
||||
if (found != result.end())
|
||||
continue; // if dependency already in correct order, otherwise hold plugin in unresolved
|
||||
|
||||
// check if someone depends on this plugin
|
||||
found = find_if(notResolved.begin(), notResolved.end(), [&dep](auto &a) {
|
||||
return dep.first == a->name;
|
||||
});
|
||||
|
||||
if (found != result.end()) // check for circular dependency
|
||||
{
|
||||
|
||||
auto checkCircularDep = find_if((*found)->dependencies.begin(),
|
||||
(*found)->dependencies.end(), [&it](auto &a) {
|
||||
return (*it)->name == a.first; // found circular dependency
|
||||
});
|
||||
if (checkCircularDep != (*found)->dependencies.end())
|
||||
{
|
||||
stringstream sstr;
|
||||
sstr << "Found circular dependency: \"" << (*it)->name << "\" and \"" << (*found)->name
|
||||
<< "\" depend on each other" << endl;
|
||||
throw runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
|
||||
resolved = false;
|
||||
break;
|
||||
}
|
||||
if (resolved)
|
||||
{
|
||||
result.push_back(*it);
|
||||
it = notResolved.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (notResolved.empty())
|
||||
break;
|
||||
}
|
||||
|
||||
return move(result);
|
||||
}
|
||||
|
||||
void LuaState::loadMods()
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
auto funcLoadMods = [this](path path) {
|
||||
auto readConfig = [this](path path) {
|
||||
const auto mainScript = "main.lua";
|
||||
if (!is_directory(path / "mods"))
|
||||
throw runtime_error(path.string() + ": No such directory.");
|
||||
for (const auto &modDir : directory_iterator(path / "mods"))
|
||||
{
|
||||
if (is_directory(modDir.status()) && exists(modDir.path() / mainScript))
|
||||
openScript(path.string(), modDir.path().filename().string());
|
||||
{
|
||||
boost::property_tree::ptree pt;
|
||||
auto _path = path.string() + "/mods/" + modDir.path().filename().string();
|
||||
boost::property_tree::read_json(_path + "/modinfo.json", pt);
|
||||
|
||||
ServerPluginInfo modInfo;
|
||||
|
||||
|
||||
modInfo.path = {path.string(), modDir.path().filename().string()};
|
||||
modInfo.author = pt.get<string>("author");
|
||||
modInfo.version = pt.get<string>("version");
|
||||
|
||||
for(const auto &v : pt.get_child("dependencies"))
|
||||
modInfo.dependencies.emplace_back(v.first, v.second.get_value<string>());
|
||||
|
||||
auto name = pt.get<string>("name");
|
||||
|
||||
modInfo.name = name;
|
||||
mods.push_back(move(modInfo));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -389,49 +481,24 @@ void LuaState::loadMods()
|
|||
|
||||
addGlobalPackagePath(envServerDir.string() + "/lib/lua/?/init.lua;" + envServerDir.string() + "/lib/lua/?.lua");
|
||||
addGlobalCPath(envServerDir.string() + "lib/?" + libExt);
|
||||
funcLoadMods(envServerDir);
|
||||
|
||||
readConfig(envServerDir);
|
||||
|
||||
if (envServerUserDir != nullptr)
|
||||
{
|
||||
funcLoadMods(envServerUserDir);
|
||||
readConfig(envServerUserDir);
|
||||
addGlobalPackagePath(string(envServerUserDir) + "/lib/lua/?/init.lua;" + string(envServerUserDir) + "/lib/lua/?.lua");
|
||||
|
||||
}
|
||||
|
||||
for (auto &mod : mods)
|
||||
{
|
||||
const string &modName = mod.first;
|
||||
sol::environment &modEnv = mod.second;
|
||||
sol::table dependencies = modEnv["ModInfo"]["dependencies"];
|
||||
for (auto &dependency : dependencies)
|
||||
{
|
||||
int idx = dependency.first.as<int>();
|
||||
sol::table dep = dependency.second;
|
||||
const std::string depNameRequest = dep[1];
|
||||
const std::string depVersionRequest = dep[2];
|
||||
|
||||
cout << "\"" << idx << "\": \"" << depNameRequest << "\" \"" << depVersionRequest << "\"" << endl;
|
||||
const auto &depEnvIt = mods.find(depNameRequest);
|
||||
if (depEnvIt != mods.end())
|
||||
auto sortedPluginList = loadOrderSolver(mods);
|
||||
|
||||
for(auto &&mod : sortedPluginList)
|
||||
{
|
||||
string depVersionFound = depEnvIt->second["ModInfo"]["version"];
|
||||
if (CompVersion(depVersionRequest, depVersionFound) != 0)
|
||||
{
|
||||
stringstream sstr;
|
||||
sstr << depNameRequest << ": version \"" << depVersionFound
|
||||
<< "\" is not applicable for \"" << modName << "\" expected \""
|
||||
<< depVersionRequest + "\"";
|
||||
throw runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stringstream sstr;
|
||||
sstr << depNameRequest + " \"" << depVersionRequest << "\" not found.";
|
||||
throw runtime_error(sstr.str());
|
||||
}
|
||||
}
|
||||
mod->env = openScript(mod->path.first, mod->path.second);
|
||||
|
||||
sol::table loaded = dataEnv["Core"]["loadedMods"];
|
||||
loaded.add(mod->name);
|
||||
cout << "modname: " << mod->name << endl;
|
||||
}
|
||||
|
||||
dataEnv["Core"]["LOADED_MODS"] = mods.size();
|
||||
|
|
|
@ -16,6 +16,16 @@
|
|||
class EventController;
|
||||
//class TimerController;
|
||||
|
||||
struct ServerPluginInfo
|
||||
{
|
||||
std::string name;
|
||||
std::pair<std::string, std::string> path; // homePath, modname
|
||||
std::string version;
|
||||
std::string author;
|
||||
std::vector<std::pair<std::string, std::string>> dependencies; // name, requestedVersion
|
||||
sol::environment env;
|
||||
};
|
||||
|
||||
class LuaState
|
||||
{
|
||||
public:
|
||||
|
@ -46,5 +56,7 @@ private:
|
|||
std::unique_ptr<TimerController> timerCtrl;
|
||||
std::unique_ptr<ActorController> actorCtrl;
|
||||
std::unique_ptr<ObjectController> objectCtrl;
|
||||
std::unordered_map<std::string, sol::environment> mods;
|
||||
|
||||
std::vector<ServerPluginInfo> mods;
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue