/* Minetest-c55 Copyright (C) 2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "mods.h" #include "filesys.h" #include "strfnd.h" #include "log.h" #include "subgame.h" #include "settings.h" std::map getModsInPath(std::string path) { std::map result; std::vector dirlist = fs::GetDirListing(path); for(u32 j=0; j depends; std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(), std::ios_base::binary); while(is.good()) { std::string dep; std::getline(is, dep); dep = trim(dep); if(dep != "") depends.insert(dep); } ModSpec spec(modname, modpath, depends); result.insert(std::make_pair(modname,spec)); } } return result; } std::map flattenModTree(std::map mods) { std::map result; for(std::map::iterator it = mods.begin(); it != mods.end(); ++it) { ModSpec mod = (*it).second; if(mod.is_modpack) { std::map 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 flattenMods(std::map mods) { std::vector result; for(std::map::iterator it = mods.begin(); it != mods.end(); ++it) { ModSpec mod = (*it).second; if(mod.is_modpack) { std::vector content = flattenMods(mod.modpack_content); result.reserve(result.size() + content.size()); result.insert(result.end(),content.begin(),content.end()); } else //not a modpack { // infostream << "inserting mod " << mod.name << std::endl; result.push_back(mod); } } return result; } std::vector filterMods(std::vector mods, std::set exclude_mod_names) { std::vector result; for(std::vector::iterator it = mods.begin(); it != mods.end(); ++it) { ModSpec& mod = *it; if(exclude_mod_names.count(mod.name) == 0) result.push_back(mod); } return result; } void ModConfiguration::addModsInPathFiltered(std::string path, std::set exclude_mods) { addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods)); } void ModConfiguration::addMods(std::vector new_mods) { // Step 1: remove mods in sorted_mods from unmet dependencies // of new_mods. new mods without unmet dependencies are // temporarily stored in satisfied_mods std::vector satisfied_mods; for(std::vector::iterator it = m_sorted_mods.begin(); it != m_sorted_mods.end(); ++it) { ModSpec mod = *it; for(std::vector::iterator it_new = new_mods.begin(); it_new != new_mods.end(); ++it_new) { ModSpec& mod_new = *it_new; //infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl; mod_new.unsatisfied_depends.erase(mod.name); } } // split new mods into satisfied and unsatisfied for(std::vector::iterator it = new_mods.begin(); it != new_mods.end(); ++it) { ModSpec mod_new = *it; if(mod_new.unsatisfied_depends.empty()) satisfied_mods.push_back(mod_new); else m_unsatisfied_mods.push_back(mod_new); } // Step 2: mods without unmet dependencies can be appended to // the sorted list. while(!satisfied_mods.empty()) { ModSpec mod = satisfied_mods.back(); m_sorted_mods.push_back(mod); satisfied_mods.pop_back(); for(std::list::iterator it = m_unsatisfied_mods.begin(); it != m_unsatisfied_mods.end(); ) { ModSpec& mod2 = *it; mod2.unsatisfied_depends.erase(mod.name); if(mod2.unsatisfied_depends.empty()) { satisfied_mods.push_back(mod2); it = m_unsatisfied_mods.erase(it); } else ++it; } } } ModConfiguration::ModConfiguration(std::string worldpath) { // Add all world mods and all game mods addModsInPath(worldpath + DIR_DELIM + "worldmods"); SubgameSpec gamespec = findWorldSubgame(worldpath); addModsInPath(gamespec.gamemods_path); // check world.mt file for mods explicitely declared to be // loaded or not by a load_mod_ = ... line. std::string worldmt = worldpath+DIR_DELIM+"world.mt"; Settings worldmt_settings; worldmt_settings.readConfigFile(worldmt.c_str()); std::vector names = worldmt_settings.getNames(); std::set exclude_mod_names; for(std::vector::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)) { exclude_mod_names.insert(name.substr(9)); } } for(std::set::const_iterator i = gamespec.addon_mods_paths.begin(); i != gamespec.addon_mods_paths.end(); ++i) addModsInPathFiltered((*i),exclude_mod_names); }