3 Copyright (C) 2013 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 spec.is_modpack = true;
50 result.insert(std::make_pair(modname,spec));
52 else // not a modpack, add the modspec
54 std::set<std::string> depends;
55 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
56 std::ios_base::binary);
60 std::getline(is, dep);
66 ModSpec spec(modname, modpath, depends);
67 result.insert(std::make_pair(modname,spec));
73 std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
75 std::map<std::string, ModSpec> result;
76 for(std::map<std::string,ModSpec>::iterator it = mods.begin();
77 it != mods.end(); ++it)
79 ModSpec mod = (*it).second;
82 std::map<std::string, ModSpec> content =
83 flattenModTree(mod.modpack_content);
84 result.insert(content.begin(),content.end());
85 result.insert(std::make_pair(mod.name,mod));
89 result.insert(std::make_pair(mod.name,mod));
95 std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
97 std::vector<ModSpec> result;
98 for(std::map<std::string,ModSpec>::iterator it = mods.begin();
99 it != mods.end(); ++it)
101 ModSpec mod = (*it).second;
104 std::vector<ModSpec> content = flattenMods(mod.modpack_content);
105 result.reserve(result.size() + content.size());
106 result.insert(result.end(),content.begin(),content.end());
111 // infostream << "inserting mod " << mod.name << std::endl;
112 result.push_back(mod);
118 std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
119 std::set<std::string> exclude_mod_names)
121 std::vector<ModSpec> result;
122 for(std::vector<ModSpec>::iterator it = mods.begin();
123 it != mods.end(); ++it)
126 if(exclude_mod_names.count(mod.name) == 0)
127 result.push_back(mod);
132 void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
134 addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
138 void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
140 // Step 1: remove mods in sorted_mods from unmet dependencies
141 // of new_mods. new mods without unmet dependencies are
142 // temporarily stored in satisfied_mods
143 std::vector<ModSpec> satisfied_mods;
144 for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
145 it != m_sorted_mods.end(); ++it)
148 for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
149 it_new != new_mods.end(); ++it_new)
151 ModSpec& mod_new = *it_new;
152 //infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
153 mod_new.unsatisfied_depends.erase(mod.name);
157 // split new mods into satisfied and unsatisfied
158 for(std::vector<ModSpec>::iterator it = new_mods.begin();
159 it != new_mods.end(); ++it)
161 ModSpec mod_new = *it;
162 if(mod_new.unsatisfied_depends.empty())
163 satisfied_mods.push_back(mod_new);
165 m_unsatisfied_mods.push_back(mod_new);
168 // Step 2: mods without unmet dependencies can be appended to
170 while(!satisfied_mods.empty())
172 ModSpec mod = satisfied_mods.back();
173 m_sorted_mods.push_back(mod);
174 satisfied_mods.pop_back();
175 for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
176 it != m_unsatisfied_mods.end(); )
179 mod2.unsatisfied_depends.erase(mod.name);
180 if(mod2.unsatisfied_depends.empty())
182 satisfied_mods.push_back(mod2);
183 it = m_unsatisfied_mods.erase(it);
191 ModConfiguration::ModConfiguration(std::string worldpath)
193 // Add all world mods and all game mods
194 addModsInPath(worldpath + DIR_DELIM + "worldmods");
195 SubgameSpec gamespec = findWorldSubgame(worldpath);
196 addModsInPath(gamespec.gamemods_path);
198 // check world.mt file for mods explicitely declared to be
199 // loaded or not by a load_mod_<modname> = ... line.
200 std::string worldmt = worldpath+DIR_DELIM+"world.mt";
201 Settings worldmt_settings;
202 worldmt_settings.readConfigFile(worldmt.c_str());
203 std::vector<std::string> names = worldmt_settings.getNames();
204 std::set<std::string> exclude_mod_names;
205 for(std::vector<std::string>::iterator it = names.begin();
206 it != names.end(); ++it)
208 std::string name = *it;
209 // for backwards compatibility: exclude only mods which are
210 // explicitely excluded. if mod is not mentioned at all, it is
211 // enabled. So by default, all installed mods are enabled.
212 if (name.compare(0,9,"load_mod_") == 0 &&
213 !worldmt_settings.getBool(name))
215 exclude_mod_names.insert(name.substr(9));
219 for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
220 i != gamespec.addon_mods_paths.end(); ++i)
221 addModsInPathFiltered((*i),exclude_mod_names);