diff options
Diffstat (limited to 'src/mods.cpp')
-rw-r--r-- | src/mods.cpp | 295 |
1 files changed, 199 insertions, 96 deletions
diff --git a/src/mods.cpp b/src/mods.cpp index 1b1bdb07b..0e583b2db 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -21,13 +21,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <fstream> #include "mods.h" #include "filesys.h" -#include "util/strfnd.h" #include "log.h" #include "subgame.h" #include "settings.h" -#include "util/strfnd.h" #include "convert_json.h" #include "exceptions.h" +#include "porting.h" static bool parseDependsLine(std::istream &is, std::string &dep, std::set<char> &symbols) @@ -110,28 +109,6 @@ std::map<std::string, ModSpec> getModsInPath(std::string path, bool part_of_modp return result; } -std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods) -{ - std::map<std::string, ModSpec> result; - for(std::map<std::string,ModSpec>::iterator it = mods.begin(); - it != mods.end(); ++it) - { - ModSpec mod = (*it).second; - if(mod.is_modpack) - { - std::map<std::string, ModSpec> content = - flattenModTree(mod.modpack_content); - result.insert(content.begin(),content.end()); - result.insert(std::make_pair(mod.name,mod)); - } - else //not a modpack - { - result.insert(std::make_pair(mod.name,mod)); - } - } - return result; -} - std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods) { std::vector<ModSpec> result; @@ -154,78 +131,32 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods) return result; } -ModConfiguration::ModConfiguration(std::string worldpath) +ModConfiguration::ModConfiguration(const std::string &worldpath): + m_unsatisfied_mods(), + m_sorted_mods(), + m_name_conflicts() { - SubgameSpec gamespec = findWorldSubgame(worldpath); - - // Add all game mods and all world mods - addModsInPath(gamespec.gamemods_path); - addModsInPath(worldpath + DIR_DELIM + "worldmods"); - - // check world.mt file for mods explicitely declared to be - // loaded or not by a load_mod_<modname> = ... line. - std::string worldmt = worldpath+DIR_DELIM+"world.mt"; - Settings worldmt_settings; - worldmt_settings.readConfigFile(worldmt.c_str()); - std::vector<std::string> names = worldmt_settings.getNames(); - std::set<std::string> include_mod_names; - for(std::vector<std::string>::iterator it = names.begin(); - it != names.end(); ++it) - { - std::string name = *it; - // for backwards compatibility: exclude only mods which are - // explicitely excluded. if mod is not mentioned at all, it is - // enabled. So by default, all installed mods are enabled. - if (name.compare(0,9,"load_mod_") == 0 && - worldmt_settings.getBool(name)) - { - include_mod_names.insert(name.substr(9)); - } - } - - // Collect all mods that are also in include_mod_names - std::vector<ModSpec> addon_mods; - for(std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin(); - it_path != gamespec.addon_mods_paths.end(); ++it_path) - { - std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path)); - for(std::vector<ModSpec>::iterator it = addon_mods_in_path.begin(); - it != addon_mods_in_path.end(); ++it) - { - ModSpec& mod = *it; - if(include_mod_names.count(mod.name) != 0) - addon_mods.push_back(mod); - else - worldmt_settings.setBool("load_mod_" + mod.name, false); - } - } - worldmt_settings.updateConfigFile(worldmt.c_str()); - - addMods(addon_mods); +} - // report on name conflicts - if(!m_name_conflicts.empty()){ - std::string s = "Unresolved name conflicts for mods "; - for(std::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); +void ModConfiguration::printUnsatisfiedModsError() const +{ + for (std::vector<ModSpec>::const_iterator it = m_unsatisfied_mods.begin(); + it != m_unsatisfied_mods.end(); ++it) { + ModSpec mod = *it; + errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: "; + for (UNORDERED_SET<std::string>::iterator dep_it = mod.unsatisfied_depends.begin(); + dep_it != mod.unsatisfied_depends.end(); ++dep_it) + errorstream << " \"" << *dep_it << "\""; + errorstream << std::endl; } - - // get the mods in order - resolveDependencies(); } -void ModConfiguration::addModsInPath(std::string path) +void ModConfiguration::addModsInPath(const std::string &path) { addMods(flattenMods(getModsInPath(path))); } -void ModConfiguration::addMods(std::vector<ModSpec> new_mods) +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. @@ -243,8 +174,8 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods) std::set<std::string> seen_this_iteration; - for(std::vector<ModSpec>::const_iterator it = new_mods.begin(); - it != new_mods.end(); ++it){ + for (std::vector<ModSpec>::const_iterator it = new_mods.begin(); + it != new_mods.end(); ++it) { const ModSpec &mod = *it; if(mod.part_of_modpack != (bool)want_from_modpack) continue; @@ -283,6 +214,73 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods) } } +void ModConfiguration::addModsFormConfig(const std::string &settings_path, const std::set<std::string> &mods) +{ + Settings conf; + std::set<std::string> load_mod_names; + + conf.readConfigFile(settings_path.c_str()); + std::vector<std::string> names = conf.getNames(); + for (std::vector<std::string>::iterator it = names.begin(); + it != names.end(); ++it) { + std::string name = *it; + if (name.compare(0,9,"load_mod_")==0 && conf.getBool(name)) + load_mod_names.insert(name.substr(9)); + } + + std::vector<ModSpec> addon_mods; + for (std::set<std::string>::const_iterator i = mods.begin(); + i != mods.end(); ++i) { + std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*i)); + for (std::vector<ModSpec>::const_iterator it = addon_mods_in_path.begin(); + it != addon_mods_in_path.end(); ++it) { + const ModSpec& mod = *it; + if (load_mod_names.count(mod.name) != 0) + addon_mods.push_back(mod); + 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 (std::vector<ModSpec>::iterator it = addon_mods.begin(); + it != addon_mods.end(); ++it) + load_mod_names.erase((*it).name); + std::vector<ModSpec> UnsatisfiedMods = getUnsatisfiedMods(); + for (std::vector<ModSpec>::iterator it = UnsatisfiedMods.begin(); + it != UnsatisfiedMods.end(); ++it) + load_mod_names.erase((*it).name); + if (!load_mod_names.empty()) { + errorstream << "The following mods could not be found:"; + for (std::set<std::string>::iterator it = load_mod_names.begin(); + it != load_mod_names.end(); ++it) + errorstream << " \"" << (*it) << "\""; + errorstream << std::endl; + } +} + +void ModConfiguration::checkConflictsAndDeps() +{ + // report on name conflicts + if (!m_name_conflicts.empty()) { + std::string s = "Unresolved name conflicts for mods "; + for (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 @@ -296,19 +294,19 @@ void ModConfiguration::resolveDependencies() // of each mod, split mods into satisfied and unsatisfied std::list<ModSpec> satisfied; std::list<ModSpec> unsatisfied; - for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin(); - it != m_unsatisfied_mods.end(); ++it){ + for (std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin(); + it != m_unsatisfied_mods.end(); ++it) { ModSpec mod = *it; mod.unsatisfied_depends = mod.depends; // check which optional dependencies actually exist - for(std::set<std::string>::iterator it_optdep = mod.optdepends.begin(); - it_optdep != mod.optdepends.end(); ++it_optdep){ + for (UNORDERED_SET<std::string>::iterator it_optdep = mod.optdepends.begin(); + it_optdep != mod.optdepends.end(); ++it_optdep) { std::string optdep = *it_optdep; - if(modnames.count(optdep) != 0) + 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()) + if (mod.unsatisfied_depends.empty()) satisfied.push_back(mod); else unsatisfied.push_back(mod); @@ -338,8 +336,36 @@ void ModConfiguration::resolveDependencies() m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end()); } +ServerModConfiguration::ServerModConfiguration(const std::string &worldpath): + ModConfiguration(worldpath) +{ + SubgameSpec gamespec = findWorldSubgame(worldpath); + + // Add all game mods and all world mods + addModsInPath(gamespec.gamemods_path); + addModsInPath(worldpath + DIR_DELIM + "worldmods"); + + // Load normal mods + std::string worldmt = worldpath + DIR_DELIM + "world.mt"; + addModsFormConfig(worldmt, gamespec.addon_mods_paths); +} + +#ifndef SERVER +ClientModConfiguration::ClientModConfiguration(const std::string &path): + ModConfiguration(path) +{ + std::set<std::string> paths; + std::string path_user = porting::path_user + DIR_DELIM + "clientmods"; + paths.insert(path); + paths.insert(path_user); + + std::string settings_path = path_user + DIR_DELIM + "mods.conf"; + addModsFormConfig(settings_path, paths); +} +#endif + #if USE_CURL -Json::Value getModstoreUrl(std::string url) +Json::Value getModstoreUrl(const std::string &url) { std::vector<std::string> extra_headers; @@ -356,3 +382,80 @@ Json::Value getModstoreUrl(std::string url) } #endif + +ModMetadata::ModMetadata(const std::string &mod_name): + m_mod_name(mod_name), + m_modified(false) +{ + m_stringvars.clear(); +} + +void ModMetadata::clear() +{ + Metadata::clear(); + m_modified = true; +} + +bool ModMetadata::save(const std::string &root_path) +{ + Json::Value json; + for (StringMap::const_iterator it = m_stringvars.begin(); + it != m_stringvars.end(); ++it) { + json[it->first] = it->second; + } + + if (!fs::PathExists(root_path)) { + if (!fs::CreateAllDirs(root_path)) { + errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '" + << root_path << "' tree cannot be created." << std::endl; + return false; + } + } else if (!fs::IsDir(root_path)) { + errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '" + << root_path << "' is not a directory." << std::endl; + return false; + } + + bool w_ok = fs::safeWriteToFile(root_path + DIR_DELIM + m_mod_name, + Json::FastWriter().write(json)); + + if (w_ok) { + m_modified = false; + } else { + errorstream << "ModMetadata[" << m_mod_name << "]: failed write file." << std::endl; + } + return w_ok; +} + +bool ModMetadata::load(const std::string &root_path) +{ + m_stringvars.clear(); + + std::ifstream is((root_path + DIR_DELIM + m_mod_name).c_str(), std::ios_base::binary); + if (!is.good()) { + return false; + } + + Json::Reader reader; + Json::Value root; + if (!reader.parse(is, root)) { + errorstream << "ModMetadata[" << m_mod_name << "]: failed read data " + "(Json decoding failure)." << std::endl; + return false; + } + + const Json::Value::Members attr_list = root.getMemberNames(); + for (Json::Value::Members::const_iterator it = attr_list.begin(); + it != attr_list.end(); ++it) { + Json::Value attr_value = root[*it]; + m_stringvars[*it] = attr_value.asString(); + } + + return true; +} + +bool ModMetadata::setString(const std::string &name, const std::string &var) +{ + m_modified = Metadata::setString(name, var); + return m_modified; +} |