Skip to content

Commit

Permalink
[ServerModLoader] Dependencies: OK.
Browse files Browse the repository at this point in the history
  • Loading branch information
Unarelith committed Jul 4, 2020
1 parent e0d8423 commit 8bcc472
Showing 1 changed file with 49 additions and 27 deletions.
76 changes: 49 additions & 27 deletions source/server/lua/ServerModLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ namespace fs = ghc::filesystem;
struct ModEntry {
fs::path path;
std::string id;

std::vector<std::string> dependencies;
std::vector<ModEntry *> requiredBy;

bool isLoaded = false;
bool isValid = true;
};

void ServerModLoader::loadMods() {
Expand Down Expand Up @@ -93,58 +98,75 @@ void ServerModLoader::loadMods() {
gkError() << ("The mod at '" + entry.path().string() + "' doesn't contain a 'config.lua' file.").c_str();
}

{
// Small BFS to check cyclic dependencies
for (auto &modit : mods) {
std::queue<std::string> queue;
queue.emplace(modit.first);
while (!queue.empty()) {
std::string modID = queue.front();
queue.pop();

auto it = mods.find(modID);
if (it == mods.end())
break;
std::queue<ModEntry *> dependencyTree;

// Small BFS to check cyclic dependencies and build the dependency tree
for (auto &modit : mods) {
std::queue<ModEntry *> queue;
queue.emplace(&modit.second);
while (!queue.empty()) {
ModEntry *mod = queue.front();
queue.pop();

for (auto &dep : it->second.dependencies) {
if (dep == modit.first)
throw EXCEPTION("Cyclic dependency detected for mod '"
+ modit.second.id + "' in mod '" + it->second.id + "'");
if (mod->dependencies.empty())
dependencyTree.emplace(mod);

for (const std::string &dependencyID : mod->dependencies) {
if (dependencyID == modit.second.id) {
gkError() << ("Cyclic dependency detected for mod '" + modit.second.id + "' in mod '" + mod->id + "'").c_str();
mod->isValid = false;
break;
}

queue.emplace(dep);
auto it = mods.find(dependencyID);
if (it != mods.end()) {
it->second.requiredBy.emplace_back(mod);
queue.emplace(&it->second);
}
else {
gkError() << ("Mod '" + mod->id + "' cannot be loaded: Missing dependency '" + dependencyID + "'").c_str();
mod->isValid = false;
}
}
}
}

// TODO: Build a dependency graph
// Two types of dependencies should be handled
// TODO: Two types of dependencies should be handled
// - Required (cyclic depedencies not handled)
// - Optional (cyclic dependencies handled)

// TODO: Load 'mods/config.lua' to let user define a preferred mod path (see #82)

m_scriptEngine.init();
m_scriptEngine.luaCore().setModLoader(this);

for (auto &it : mods) {
if (fs::exists(it.second.path.string() + "/init.lua")) {
fs::current_path(it.second.path.string());
while (!dependencyTree.empty()) {
ModEntry *mod = dependencyTree.front();
dependencyTree.pop();

if (mod->isLoaded || !mod->isValid) continue;

if (fs::exists(mod->path.string() + "/init.lua")) {
fs::current_path(mod->path.string());

try {
m_scriptEngine.lua().safe_script_file("init.lua");
}
catch (const sol::error &e) {
gkError() << "Error: Failed to load mod at" << it.second.path.string();
gkError() << "Error: Failed to load mod at" << mod->path.string();
gkError() << e.what();
}

fs::current_path(basePath);

gkInfo() << "Mod" << it.first << "loaded";
gkInfo() << "Mod" << mod->id << "loaded";
}
else
gkError() << ("The mod at '" + it.second.path.string() + "' doesn't contain an 'init.lua' file.").c_str();
gkError() << ("The mod at '" + mod->path.string() + "' doesn't contain an 'init.lua' file.").c_str();

mod->isLoaded = true;

for (ModEntry *entry : mod->requiredBy) {
dependencyTree.emplace(entry);
}
}

for (auto &it : m_mods) {
Expand Down

0 comments on commit 8bcc472

Please sign in to comment.