+ // 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::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 (const std::string &name : names) {
+ if (name.compare(0,9,"load_mod_")==0 && conf.getBool(name))
+ load_mod_names.insert(name.substr(9));
+ }
+
+ std::vector<ModSpec> addon_mods;
+ for (const std::string &i : mods) {
+ 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 (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 std::string &mod : load_mod_names)
+ errorstream << " \"" << mod << "\"";
+ errorstream << 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) + "\"";