diff options
Diffstat (limited to 'src/content/mods.cpp')
-rw-r--r-- | src/content/mods.cpp | 372 |
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) |