]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mods.cpp
Last set of minor cleanups
[dragonfireclient.git] / src / mods.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "mods.h"
21 #include "filesys.h"
22 #include "strfnd.h"
23 #include "log.h"
24 #include "subgame.h"
25 #include "settings.h"
26
27 std::map<std::string, ModSpec> getModsInPath(std::string path)
28 {
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++){
32                 if(!dirlist[j].dir)
33                         continue;
34                 std::string modname = dirlist[j].name;
35                 // Ignore all directories beginning with a ".", especially
36                 // VCS directories like ".git" or ".svn"
37                 if(modname[0] == '.')
38                         continue;
39                 std::string modpath = path + DIR_DELIM + modname;
40
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
45                 {
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));
50                 }
51                 else // not a modpack, add the modspec
52                 {
53                         std::set<std::string> depends;
54                         std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
55                                 std::ios_base::binary);
56                         while(is.good())
57                         {
58                                 std::string dep;
59                                 std::getline(is, dep);
60                                 dep = trim(dep);        
61                                 if(dep != "")
62                                         depends.insert(dep);
63                         }
64
65                         ModSpec spec(modname, modpath, depends);
66                         result.insert(std::make_pair(modname,spec));
67                 }
68         }
69         return result;
70 }
71
72 std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
73 {
74         std::map<std::string, ModSpec> result;
75         for(std::map<std::string,ModSpec>::iterator it = mods.begin();
76                 it != mods.end(); ++it)
77         {
78                 ModSpec mod = (*it).second;
79                 if(!mod.modpack_content.empty()) //is a modpack
80                 {
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));
85                 } 
86                 else //not a modpack
87                 {
88                         result.insert(std::make_pair(mod.name,mod));
89                 }
90         }
91         return result;
92 }
93
94 std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
95 {
96         std::vector<ModSpec> result;
97         for(std::map<std::string,ModSpec>::iterator it = mods.begin();
98                 it != mods.end(); ++it)
99         {
100                 ModSpec mod = (*it).second;
101                 if(!mod.modpack_content.empty()) //is a modpack
102                 {
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());
106                         
107                 } 
108                 else //not a modpack
109                 {
110                         // infostream << "inserting mod " << mod.name << std::endl;
111                         result.push_back(mod);
112                 }
113         }
114         return result;
115 }
116
117 std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
118                                                                 std::set<std::string> exclude_mod_names)
119 {
120         std::vector<ModSpec> result;
121         for(std::vector<ModSpec>::iterator it = mods.begin();
122                 it != mods.end(); ++it)
123         {
124                 ModSpec& mod = *it;
125                 if(exclude_mod_names.count(mod.name) == 0)
126                         result.push_back(mod);
127         }       
128         return result;
129 }
130
131 void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
132 {
133         addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
134 }
135
136
137 void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
138 {
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)
145         {
146                 ModSpec mod = *it;
147                 for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
148                         it_new != new_mods.end(); ++it_new)
149                 {
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);
153                 }
154         }
155
156         // split new mods into satisfied and unsatisfied
157         for(std::vector<ModSpec>::iterator it = new_mods.begin();
158                 it != new_mods.end(); ++it)
159         {
160                 ModSpec mod_new = *it;
161                 if(mod_new.unsatisfied_depends.empty())
162                         satisfied_mods.push_back(mod_new);
163                 else
164                         m_unsatisfied_mods.push_back(mod_new);
165         }
166
167         // Step 2: mods without unmet dependencies can be appended to
168         // the sorted list.
169         while(!satisfied_mods.empty())
170         {
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(); )
176                 {
177                         ModSpec& mod2 = *it;
178                         mod2.unsatisfied_depends.erase(mod.name);
179                         if(mod2.unsatisfied_depends.empty())
180                         {
181                                 satisfied_mods.push_back(mod2);
182                                 it = m_unsatisfied_mods.erase(it);
183                         }
184                         else
185                                 ++it;
186                 }       
187         }
188 }
189
190 ModConfiguration::ModConfiguration(std::string worldpath)
191 {
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);
196
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)
206         {       
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))
213                 {
214                         exclude_mod_names.insert(name.substr(9));
215                 }
216         }
217
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);
221 }