3 Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 std::map<std::string, ModSpec> getModsInPath(std::string path)
29 std::map<std::string, ModSpec> result;
30 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
31 for(u32 j=0; j<dirlist.size(); j++){
34 std::string modname = dirlist[j].name;
35 // Ignore all directories beginning with a ".", especially
36 // VCS directories like ".git" or ".svn"
39 std::string modpath = path + DIR_DELIM + modname;
41 // Handle modpacks (defined by containing modpack.txt)
42 std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(),
43 std::ios_base::binary);
44 if(modpack_is.good()) //a modpack, recursively get the mods in it
46 modpack_is.close(); // We don't actually need the file
47 ModSpec spec(modname,modpath);
48 spec.modpack_content = getModsInPath(modpath);
49 result.insert(std::make_pair(modname,spec));
51 else // not a modpack, add the modspec
53 std::set<std::string> depends;
54 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
55 std::ios_base::binary);
59 std::getline(is, dep);
65 ModSpec spec(modname, modpath, depends);
66 result.insert(std::make_pair(modname,spec));
72 std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
74 std::map<std::string, ModSpec> result;
75 for(std::map<std::string,ModSpec>::iterator it = mods.begin();
76 it != mods.end(); ++it)
78 ModSpec mod = (*it).second;
79 if(!mod.modpack_content.empty()) //is a modpack
81 std::map<std::string, ModSpec> content =
82 flattenModTree(mod.modpack_content);
83 result.insert(content.begin(),content.end());
84 result.insert(std::make_pair(mod.name,mod));
88 result.insert(std::make_pair(mod.name,mod));
94 std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
96 std::vector<ModSpec> result;
97 for(std::map<std::string,ModSpec>::iterator it = mods.begin();
98 it != mods.end(); ++it)
100 ModSpec mod = (*it).second;
101 if(!mod.modpack_content.empty()) //is a modpack
103 std::vector<ModSpec> content = flattenMods(mod.modpack_content);
104 result.reserve(result.size() + content.size());
105 result.insert(result.end(),content.begin(),content.end());
110 // infostream << "inserting mod " << mod.name << std::endl;
111 result.push_back(mod);
117 std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
118 std::set<std::string> exclude_mod_names)
120 std::vector<ModSpec> result;
121 for(std::vector<ModSpec>::iterator it = mods.begin();
122 it != mods.end(); ++it)
125 if(exclude_mod_names.count(mod.name) == 0)
126 result.push_back(mod);
131 void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
133 addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
137 void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
139 // Step 1: remove mods in sorted_mods from unmet dependencies
140 // of new_mods. new mods without unmet dependencies are
141 // temporarily stored in satisfied_mods
142 std::vector<ModSpec> satisfied_mods;
143 for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
144 it != m_sorted_mods.end(); ++it)
147 for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
148 it_new != new_mods.end(); ++it_new)
150 ModSpec& mod_new = *it_new;
151 //infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
152 mod_new.unsatisfied_depends.erase(mod.name);
156 // split new mods into satisfied and unsatisfied
157 for(std::vector<ModSpec>::iterator it = new_mods.begin();
158 it != new_mods.end(); ++it)
160 ModSpec mod_new = *it;
161 if(mod_new.unsatisfied_depends.empty())
162 satisfied_mods.push_back(mod_new);
164 m_unsatisfied_mods.push_back(mod_new);
167 // Step 2: mods without unmet dependencies can be appended to
169 while(!satisfied_mods.empty())
171 ModSpec mod = satisfied_mods.back();
172 m_sorted_mods.push_back(mod);
173 satisfied_mods.pop_back();
174 for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
175 it != m_unsatisfied_mods.end(); )
178 mod2.unsatisfied_depends.erase(mod.name);
179 if(mod2.unsatisfied_depends.empty())
181 satisfied_mods.push_back(mod2);
182 it = m_unsatisfied_mods.erase(it);
190 ModConfiguration::ModConfiguration(std::string worldpath)
192 // Add all world mods and all game mods
193 addModsInPath(worldpath + DIR_DELIM + "worldmods");
194 SubgameSpec gamespec = findWorldSubgame(worldpath);
195 addModsInPath(gamespec.gamemods_path);
197 // check world.mt file for mods explicitely declared to be
198 // loaded or not by a load_mod_<modname> = ... line.
199 std::string worldmt = worldpath+DIR_DELIM+"world.mt";
200 Settings worldmt_settings;
201 worldmt_settings.readConfigFile(worldmt.c_str());
202 std::vector<std::string> names = worldmt_settings.getNames();
203 std::set<std::string> exclude_mod_names;
204 for(std::vector<std::string>::iterator it = names.begin();
205 it != names.end(); ++it)
207 std::string name = *it;
208 // for backwards compatibility: exclude only mods which are
209 // explicitely excluded. if mod is not mentioned at all, it is
210 // enabled. So by default, all installed mods are enabled.
211 if (name.compare(0,9,"load_mod_") == 0 &&
212 !worldmt_settings.getBool(name))
214 exclude_mod_names.insert(name.substr(9));
218 for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
219 i != gamespec.addon_mods_paths.end(); ++i)
220 addModsInPathFiltered((*i),exclude_mod_names);