aboutsummaryrefslogtreecommitdiff
path: root/src/content/mods.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/content/mods.cpp')
-rw-r--r--src/content/mods.cpp372
1 files changed, 68 insertions, 304 deletions
diff --git a/src/content/mods.cpp b/src/content/mods.cpp
index f75119bbb..cec6fc2cd 100644
--- a/src/content/mods.cpp
+++ b/src/content/mods.cpp
@@ -69,7 +69,7 @@ bool parseDependsString(std::string &dep, std::unordered_set<char> &symbols)
return !dep.empty();
}
-void parseModContents(ModSpec &spec)
+bool parseModContents(ModSpec &spec)
{
// NOTE: this function works in mutual recursion with getModsInPath
@@ -79,91 +79,89 @@ void parseModContents(ModSpec &spec)
spec.modpack_content.clear();
// Handle modpacks (defined by containing modpack.txt)
- std::ifstream modpack_is((spec.path + DIR_DELIM + "modpack.txt").c_str());
- std::ifstream modpack2_is((spec.path + DIR_DELIM + "modpack.conf").c_str());
- if (modpack_is.good() || modpack2_is.good()) {
- if (modpack_is.good())
- modpack_is.close();
-
- if (modpack2_is.good())
- modpack2_is.close();
-
+ if (fs::IsFile(spec.path + DIR_DELIM + "modpack.txt") ||
+ fs::IsFile(spec.path + DIR_DELIM + "modpack.conf")) {
spec.is_modpack = true;
spec.modpack_content = getModsInPath(spec.path, spec.virtual_path, true);
+ return true;
+ } else if (!fs::IsFile(spec.path + DIR_DELIM + "init.lua")) {
+ return false;
+ }
- } else {
- Settings info;
- info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str());
- if (info.exists("name"))
- spec.name = info.get("name");
- else
- spec.deprecation_msgs.push_back("Mods not having a mod.conf file with the name is deprecated.");
-
- if (info.exists("author"))
- spec.author = info.get("author");
-
- if (info.exists("release"))
- spec.release = info.getS32("release");
-
- // Attempt to load dependencies from mod.conf
- bool mod_conf_has_depends = false;
- if (info.exists("depends")) {
- mod_conf_has_depends = true;
- std::string dep = info.get("depends");
- // clang-format off
- dep.erase(std::remove_if(dep.begin(), dep.end(),
- static_cast<int (*)(int)>(&std::isspace)), dep.end());
- // clang-format on
- for (const auto &dependency : str_split(dep, ',')) {
- spec.depends.insert(dependency);
- }
+ Settings info;
+ info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str());
+
+ if (info.exists("name"))
+ spec.name = info.get("name");
+ else
+ spec.deprecation_msgs.push_back("Mods not having a mod.conf file with the name is deprecated.");
+
+ if (info.exists("author"))
+ spec.author = info.get("author");
+
+ if (info.exists("release"))
+ spec.release = info.getS32("release");
+
+ // Attempt to load dependencies from mod.conf
+ bool mod_conf_has_depends = false;
+ if (info.exists("depends")) {
+ mod_conf_has_depends = true;
+ std::string dep = info.get("depends");
+ // clang-format off
+ dep.erase(std::remove_if(dep.begin(), dep.end(),
+ static_cast<int (*)(int)>(&std::isspace)), dep.end());
+ // clang-format on
+ for (const auto &dependency : str_split(dep, ',')) {
+ spec.depends.insert(dependency);
}
+ }
- if (info.exists("optional_depends")) {
- mod_conf_has_depends = true;
- std::string dep = info.get("optional_depends");
- // clang-format off
- dep.erase(std::remove_if(dep.begin(), dep.end(),
- static_cast<int (*)(int)>(&std::isspace)), dep.end());
- // clang-format on
- for (const auto &dependency : str_split(dep, ',')) {
- spec.optdepends.insert(dependency);
- }
+ if (info.exists("optional_depends")) {
+ mod_conf_has_depends = true;
+ std::string dep = info.get("optional_depends");
+ // clang-format off
+ dep.erase(std::remove_if(dep.begin(), dep.end(),
+ static_cast<int (*)(int)>(&std::isspace)), dep.end());
+ // clang-format on
+ for (const auto &dependency : str_split(dep, ',')) {
+ spec.optdepends.insert(dependency);
}
+ }
- // Fallback to depends.txt
- if (!mod_conf_has_depends) {
- std::vector<std::string> dependencies;
+ // Fallback to depends.txt
+ if (!mod_conf_has_depends) {
+ std::vector<std::string> dependencies;
- std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str());
+ std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str());
- if (is.good())
- spec.deprecation_msgs.push_back("depends.txt is deprecated, please use mod.conf instead.");
+ if (is.good())
+ spec.deprecation_msgs.push_back("depends.txt is deprecated, please use mod.conf instead.");
- while (is.good()) {
- std::string dep;
- std::getline(is, dep);
- dependencies.push_back(dep);
- }
+ while (is.good()) {
+ std::string dep;
+ std::getline(is, dep);
+ dependencies.push_back(dep);
+ }
- for (auto &dependency : dependencies) {
- std::unordered_set<char> symbols;
- if (parseDependsString(dependency, symbols)) {
- if (symbols.count('?') != 0) {
- spec.optdepends.insert(dependency);
- } else {
- spec.depends.insert(dependency);
- }
+ for (auto &dependency : dependencies) {
+ std::unordered_set<char> symbols;
+ if (parseDependsString(dependency, symbols)) {
+ if (symbols.count('?') != 0) {
+ spec.optdepends.insert(dependency);
+ } else {
+ spec.depends.insert(dependency);
}
}
}
-
- if (info.exists("description"))
- spec.desc = info.get("description");
- else if (fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc))
- spec.deprecation_msgs.push_back("description.txt is deprecated, please use mod.conf instead.");
}
+
+ if (info.exists("description"))
+ spec.desc = info.get("description");
+ else if (fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc))
+ spec.deprecation_msgs.push_back("description.txt is deprecated, please use mod.conf instead.");
+
+ return true;
}
std::map<std::string, ModSpec> getModsInPath(
@@ -218,240 +216,6 @@ std::vector<ModSpec> flattenMods(const std::map<std::string, ModSpec> &mods)
return result;
}
-ModConfiguration::ModConfiguration(const std::string &worldpath)
-{
-}
-
-void ModConfiguration::printUnsatisfiedModsError() const
-{
- for (const ModSpec &mod : m_unsatisfied_mods) {
- errorstream << "mod \"" << mod.name
- << "\" has unsatisfied dependencies: ";
- for (const std::string &unsatisfied_depend : mod.unsatisfied_depends)
- errorstream << " \"" << unsatisfied_depend << "\"";
- errorstream << std::endl;
- }
-}
-
-void ModConfiguration::addModsInPath(const std::string &path, const std::string &virtual_path)
-{
- addMods(flattenMods(getModsInPath(path, virtual_path)));
-}
-
-void ModConfiguration::addMods(const std::vector<ModSpec> &new_mods)
-{
- // Maintain a map of all existing m_unsatisfied_mods.
- // Keys are mod names and values are indices into m_unsatisfied_mods.
- std::map<std::string, u32> existing_mods;
- for (u32 i = 0; i < m_unsatisfied_mods.size(); ++i) {
- existing_mods[m_unsatisfied_mods[i].name] = i;
- }
-
- // Add new mods
- for (int want_from_modpack = 1; want_from_modpack >= 0; --want_from_modpack) {
- // First iteration:
- // Add all the mods that come from modpacks
- // Second iteration:
- // Add all the mods that didn't come from modpacks
-
- std::set<std::string> seen_this_iteration;
-
- for (const ModSpec &mod : new_mods) {
- if (mod.part_of_modpack != (bool)want_from_modpack)
- continue;
-
- if (existing_mods.count(mod.name) == 0) {
- // GOOD CASE: completely new mod.
- m_unsatisfied_mods.push_back(mod);
- existing_mods[mod.name] = m_unsatisfied_mods.size() - 1;
- } else if (seen_this_iteration.count(mod.name) == 0) {
- // BAD CASE: name conflict in different levels.
- u32 oldindex = existing_mods[mod.name];
- const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
- warningstream << "Mod name conflict detected: \""
- << mod.name << "\"" << std::endl
- << "Will not load: " << oldmod.path
- << std::endl
- << "Overridden by: " << mod.path
- << std::endl;
- m_unsatisfied_mods[oldindex] = mod;
-
- // If there was a "VERY BAD CASE" name conflict
- // in an earlier level, ignore it.
- m_name_conflicts.erase(mod.name);
- } else {
- // VERY BAD CASE: name conflict in the same level.
- u32 oldindex = existing_mods[mod.name];
- const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
- warningstream << "Mod name conflict detected: \""
- << mod.name << "\"" << std::endl
- << "Will not load: " << oldmod.path
- << std::endl
- << "Will not load: " << mod.path
- << std::endl;
- m_unsatisfied_mods[oldindex] = mod;
- m_name_conflicts.insert(mod.name);
- }
-
- seen_this_iteration.insert(mod.name);
- }
- }
-}
-
-void ModConfiguration::addModsFromConfig(
- const std::string &settings_path,
- const std::unordered_map<std::string, std::string> &modPaths)
-{
- Settings conf;
- std::unordered_map<std::string, std::string> load_mod_names;
-
- conf.readConfigFile(settings_path.c_str());
- std::vector<std::string> names = conf.getNames();
- for (const std::string &name : names) {
- const auto &value = conf.get(name);
- if (name.compare(0, 9, "load_mod_") == 0 && value != "false" &&
- value != "nil")
- load_mod_names[name.substr(9)] = value;
- }
-
- std::vector<ModSpec> addon_mods;
- std::unordered_map<std::string, std::vector<std::string>> candidates;
-
- for (const auto &modPath : modPaths) {
- std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(modPath.second, modPath.first));
- for (std::vector<ModSpec>::const_iterator it = addon_mods_in_path.begin();
- it != addon_mods_in_path.end(); ++it) {
- const ModSpec &mod = *it;
- const auto &pair = load_mod_names.find(mod.name);
- if (pair != load_mod_names.end()) {
- if (is_yes(pair->second) || pair->second == mod.virtual_path) {
- addon_mods.push_back(mod);
- } else {
- candidates[pair->first].emplace_back(mod.virtual_path);
- }
- } else {
- conf.setBool("load_mod_" + mod.name, false);
- }
- }
- }
- conf.updateConfigFile(settings_path.c_str());
-
- addMods(addon_mods);
- checkConflictsAndDeps();
-
- // complain about mods declared to be loaded, but not found
- for (const ModSpec &addon_mod : addon_mods)
- load_mod_names.erase(addon_mod.name);
-
- std::vector<ModSpec> unsatisfiedMods = getUnsatisfiedMods();
-
- for (const ModSpec &unsatisfiedMod : unsatisfiedMods)
- load_mod_names.erase(unsatisfiedMod.name);
-
- if (!load_mod_names.empty()) {
- errorstream << "The following mods could not be found:";
- for (const auto &pair : load_mod_names)
- errorstream << " \"" << pair.first << "\"";
- errorstream << std::endl;
-
- for (const auto &pair : load_mod_names) {
- const auto &candidate = candidates.find(pair.first);
- if (candidate != candidates.end()) {
- errorstream << "Unable to load " << pair.first << " as the specified path "
- << pair.second << " could not be found. "
- << "However, it is available in the following locations:"
- << std::endl;
- for (const auto &path : candidate->second) {
- errorstream << " - " << path << std::endl;
- }
- }
- }
- }
-}
-
-void ModConfiguration::checkConflictsAndDeps()
-{
- // report on name conflicts
- if (!m_name_conflicts.empty()) {
- std::string s = "Unresolved name conflicts for mods ";
- for (std::unordered_set<std::string>::const_iterator it =
- m_name_conflicts.begin();
- it != m_name_conflicts.end(); ++it) {
- if (it != m_name_conflicts.begin())
- s += ", ";
- s += std::string("\"") + (*it) + "\"";
- }
- s += ".";
- throw ModError(s);
- }
-
- // get the mods in order
- resolveDependencies();
-}
-
-void ModConfiguration::resolveDependencies()
-{
- // Step 1: Compile a list of the mod names we're working with
- std::set<std::string> modnames;
- for (const ModSpec &mod : m_unsatisfied_mods) {
- modnames.insert(mod.name);
- }
-
- // Step 2: get dependencies (including optional dependencies)
- // of each mod, split mods into satisfied and unsatisfied
- std::list<ModSpec> satisfied;
- std::list<ModSpec> unsatisfied;
- for (ModSpec mod : m_unsatisfied_mods) {
- mod.unsatisfied_depends = mod.depends;
- // check which optional dependencies actually exist
- for (const std::string &optdep : mod.optdepends) {
- if (modnames.count(optdep) != 0)
- mod.unsatisfied_depends.insert(optdep);
- }
- // if a mod has no depends it is initially satisfied
- if (mod.unsatisfied_depends.empty())
- satisfied.push_back(mod);
- else
- unsatisfied.push_back(mod);
- }
-
- // Step 3: mods without unmet dependencies can be appended to
- // the sorted list.
- while (!satisfied.empty()) {
- ModSpec mod = satisfied.back();
- m_sorted_mods.push_back(mod);
- satisfied.pop_back();
- for (auto it = unsatisfied.begin(); it != unsatisfied.end();) {
- ModSpec &mod2 = *it;
- mod2.unsatisfied_depends.erase(mod.name);
- if (mod2.unsatisfied_depends.empty()) {
- satisfied.push_back(mod2);
- it = unsatisfied.erase(it);
- } else {
- ++it;
- }
- }
- }
-
- // Step 4: write back list of unsatisfied mods
- m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
-}
-
-#ifndef SERVER
-ClientModConfiguration::ClientModConfiguration(const std::string &path) :
- ModConfiguration(path)
-{
- std::unordered_map<std::string, std::string> paths;
- std::string path_user = porting::path_user + DIR_DELIM + "clientmods";
- if (path != path_user) {
- paths["share"] = path;
- }
- paths["mods"] = path_user;
-
- std::string settings_path = path_user + DIR_DELIM + "mods.conf";
- addModsFromConfig(settings_path, paths);
-}
-#endif
ModMetadata::ModMetadata(const std::string &mod_name, ModMetadataDatabase *database):
m_mod_name(mod_name), m_database(database)