Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2013 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 "subgame.h"
21 : #include "porting.h"
22 : #include "filesys.h"
23 : #include "settings.h"
24 : #include "log.h"
25 : #include "strfnd.h"
26 : #include "defaultsettings.h" // for override_default_settings
27 : #include "mapgen.h" // for MapgenParams
28 : #include "util/string.h"
29 :
30 : #ifndef SERVER
31 : #include "client/tile.h" // getImagePath
32 : #endif
33 :
34 0 : bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
35 : {
36 0 : std::string conf_path = game_path + DIR_DELIM + "minetest.conf";
37 0 : return conf.readConfigFile(conf_path.c_str());
38 : }
39 :
40 5 : bool getGameConfig(const std::string &game_path, Settings &conf)
41 : {
42 10 : std::string conf_path = game_path + DIR_DELIM + "game.conf";
43 10 : return conf.readConfigFile(conf_path.c_str());
44 : }
45 :
46 3 : std::string getGameName(const std::string &game_path)
47 : {
48 6 : Settings conf;
49 3 : if(!getGameConfig(game_path, conf))
50 0 : return "";
51 3 : if(!conf.exists("name"))
52 0 : return "";
53 3 : return conf.get("name");
54 : }
55 :
56 54 : struct GameFindPath
57 : {
58 : std::string path;
59 : bool user_specific;
60 12 : GameFindPath(const std::string &path, bool user_specific):
61 : path(path),
62 12 : user_specific(user_specific)
63 12 : {}
64 : };
65 :
66 4 : Strfnd getSubgamePathEnv() {
67 8 : std::string sp;
68 4 : char *subgame_path = getenv("MINETEST_SUBGAME_PATH");
69 :
70 4 : if(subgame_path) {
71 0 : sp = std::string(subgame_path);
72 : }
73 :
74 8 : return Strfnd(sp);
75 : }
76 :
77 3 : SubgameSpec findSubgame(const std::string &id)
78 : {
79 3 : if(id == "")
80 0 : return SubgameSpec();
81 6 : std::string share = porting::path_share;
82 6 : std::string user = porting::path_user;
83 6 : std::vector<GameFindPath> find_paths;
84 :
85 6 : Strfnd search_paths = getSubgamePathEnv();
86 :
87 3 : while(!search_paths.atend()) {
88 0 : std::string path = search_paths.next(":");
89 0 : find_paths.push_back(GameFindPath(
90 0 : path + DIR_DELIM + id, false));
91 0 : find_paths.push_back(GameFindPath(
92 0 : path + DIR_DELIM + id + "_game", false));
93 : }
94 :
95 6 : find_paths.push_back(GameFindPath(
96 9 : user + DIR_DELIM + "games" + DIR_DELIM + id + "_game", true));
97 6 : find_paths.push_back(GameFindPath(
98 9 : user + DIR_DELIM + "games" + DIR_DELIM + id, true));
99 6 : find_paths.push_back(GameFindPath(
100 9 : share + DIR_DELIM + "games" + DIR_DELIM + id + "_game", false));
101 6 : find_paths.push_back(GameFindPath(
102 9 : share + DIR_DELIM + "games" + DIR_DELIM + id, false));
103 : // Find game directory
104 6 : std::string game_path;
105 3 : bool user_game = true; // Game is in user's directory
106 8 : for(u32 i=0; i<find_paths.size(); i++){
107 8 : const std::string &try_path = find_paths[i].path;
108 8 : if(fs::PathExists(try_path)){
109 3 : game_path = try_path;
110 3 : user_game = find_paths[i].user_specific;
111 3 : break;
112 : }
113 : }
114 3 : if(game_path == "")
115 0 : return SubgameSpec();
116 6 : std::string gamemod_path = game_path + DIR_DELIM + "mods";
117 : // Find mod directories
118 6 : std::set<std::string> mods_paths;
119 3 : if(!user_game)
120 1 : mods_paths.insert(share + DIR_DELIM + "mods");
121 3 : if(user != share || user_game)
122 3 : mods_paths.insert(user + DIR_DELIM + "mods");
123 6 : std::string game_name = getGameName(game_path);
124 3 : if(game_name == "")
125 0 : game_name = id;
126 6 : std::string menuicon_path;
127 : #ifndef SERVER
128 3 : menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
129 : #endif
130 : return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
131 3 : menuicon_path);
132 : }
133 :
134 1 : SubgameSpec findWorldSubgame(const std::string &world_path)
135 : {
136 2 : std::string world_gameid = getWorldGameId(world_path, true);
137 : // See if world contains an embedded game; if so, use it.
138 2 : std::string world_gamepath = world_path + DIR_DELIM + "game";
139 1 : if(fs::PathExists(world_gamepath)){
140 0 : SubgameSpec gamespec;
141 0 : gamespec.id = world_gameid;
142 0 : gamespec.path = world_gamepath;
143 0 : gamespec.gamemods_path= world_gamepath + DIR_DELIM + "mods";
144 0 : gamespec.name = getGameName(world_gamepath);
145 0 : if(gamespec.name == "")
146 0 : gamespec.name = "unknown";
147 0 : return gamespec;
148 : }
149 1 : return findSubgame(world_gameid);
150 : }
151 :
152 1 : std::set<std::string> getAvailableGameIds()
153 : {
154 1 : std::set<std::string> gameids;
155 2 : std::set<std::string> gamespaths;
156 1 : gamespaths.insert(porting::path_share + DIR_DELIM + "games");
157 1 : gamespaths.insert(porting::path_user + DIR_DELIM + "games");
158 :
159 2 : Strfnd search_paths = getSubgamePathEnv();
160 :
161 1 : while(!search_paths.atend()) {
162 0 : gamespaths.insert(search_paths.next(":"));
163 : }
164 :
165 9 : for(std::set<std::string>::const_iterator i = gamespaths.begin();
166 6 : i != gamespaths.end(); i++){
167 4 : std::vector<fs::DirListNode> dirlist = fs::GetDirListing(*i);
168 4 : for(u32 j=0; j<dirlist.size(); j++){
169 2 : if(!dirlist[j].dir)
170 0 : continue;
171 : // If configuration file is not found or broken, ignore game
172 4 : Settings conf;
173 2 : if(!getGameConfig(*i + DIR_DELIM + dirlist[j].name, conf))
174 0 : continue;
175 : // Add it to result
176 2 : const char *ends[] = {"_game", NULL};
177 4 : std::string shorter = removeStringEnd(dirlist[j].name, ends);
178 2 : if(shorter != "")
179 0 : gameids.insert(shorter);
180 : else
181 2 : gameids.insert(dirlist[j].name);
182 : }
183 : }
184 2 : return gameids;
185 : }
186 :
187 1 : std::vector<SubgameSpec> getAvailableGames()
188 : {
189 1 : std::vector<SubgameSpec> specs;
190 2 : std::set<std::string> gameids = getAvailableGameIds();
191 9 : for(std::set<std::string>::const_iterator i = gameids.begin();
192 6 : i != gameids.end(); i++)
193 2 : specs.push_back(findSubgame(*i));
194 2 : return specs;
195 : }
196 :
197 : #define LEGACY_GAMEID "minetest"
198 :
199 1 : bool getWorldExists(const std::string &world_path)
200 : {
201 3 : return (fs::PathExists(world_path + DIR_DELIM + "map_meta.txt") ||
202 2 : fs::PathExists(world_path + DIR_DELIM + "world.mt"));
203 : }
204 :
205 6 : std::string getWorldGameId(const std::string &world_path, bool can_be_legacy)
206 : {
207 12 : std::string conf_path = world_path + DIR_DELIM + "world.mt";
208 12 : Settings conf;
209 6 : bool succeeded = conf.readConfigFile(conf_path.c_str());
210 6 : if(!succeeded){
211 0 : if(can_be_legacy){
212 : // If map_meta.txt exists, it is probably an old minetest world
213 0 : if(fs::PathExists(world_path + DIR_DELIM + "map_meta.txt"))
214 0 : return LEGACY_GAMEID;
215 : }
216 0 : return "";
217 : }
218 6 : if(!conf.exists("gameid"))
219 0 : return "";
220 : // The "mesetint" gameid has been discarded
221 6 : if(conf.get("gameid") == "mesetint")
222 0 : return "minetest";
223 6 : return conf.get("gameid");
224 : }
225 :
226 3 : std::vector<WorldSpec> getAvailableWorlds()
227 : {
228 3 : std::vector<WorldSpec> worlds;
229 6 : std::set<std::string> worldspaths;
230 3 : worldspaths.insert(porting::path_user + DIR_DELIM + "worlds");
231 3 : infostream<<"Searching worlds..."<<std::endl;
232 18 : for(std::set<std::string>::const_iterator i = worldspaths.begin();
233 12 : i != worldspaths.end(); i++){
234 3 : infostream<<" In "<<(*i)<<": "<<std::endl;
235 6 : std::vector<fs::DirListNode> dirvector = fs::GetDirListing(*i);
236 6 : for(u32 j=0; j<dirvector.size(); j++){
237 3 : if(!dirvector[j].dir)
238 0 : continue;
239 6 : std::string fullpath = *i + DIR_DELIM + dirvector[j].name;
240 6 : std::string name = dirvector[j].name;
241 : // Just allow filling in the gameid always for now
242 3 : bool can_be_legacy = true;
243 6 : std::string gameid = getWorldGameId(fullpath, can_be_legacy);
244 6 : WorldSpec spec(fullpath, name, gameid);
245 3 : if(!spec.isValid()){
246 0 : infostream<<"(invalid: "<<name<<") ";
247 : } else {
248 3 : infostream<<name<<" ";
249 3 : worlds.push_back(spec);
250 : }
251 : }
252 3 : infostream<<std::endl;
253 : }
254 : // Check old world location
255 : do{
256 3 : std::string fullpath = porting::path_user + DIR_DELIM + "world";
257 3 : if(!fs::PathExists(fullpath))
258 3 : break;
259 0 : std::string name = "Old World";
260 0 : std::string gameid = getWorldGameId(fullpath, true);
261 0 : WorldSpec spec(fullpath, name, gameid);
262 0 : infostream<<"Old world found."<<std::endl;
263 0 : worlds.push_back(spec);
264 : }while(0);
265 3 : infostream<<worlds.size()<<" found."<<std::endl;
266 6 : return worlds;
267 : }
268 :
269 0 : bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec)
270 : {
271 : // Override defaults with those provided by the game.
272 : // We clear and reload the defaults because the defaults
273 : // might have been overridden by other subgame config
274 : // files that were loaded before.
275 0 : g_settings->clearDefaults();
276 0 : set_default_settings(g_settings);
277 0 : Settings game_defaults;
278 0 : getGameMinetestConfig(gamespec.path, game_defaults);
279 0 : override_default_settings(g_settings, &game_defaults);
280 :
281 0 : infostream << "Initializing world at " << path << std::endl;
282 :
283 0 : fs::CreateAllDirs(path);
284 :
285 : // Create world.mt if does not already exist
286 0 : std::string worldmt_path = path + DIR_DELIM "world.mt";
287 0 : if (!fs::PathExists(worldmt_path)) {
288 0 : std::ostringstream ss(std::ios_base::binary);
289 0 : ss << "gameid = " << gamespec.id
290 0 : << "\nbackend = sqlite3"
291 0 : << "\ncreative_mode = " << g_settings->get("creative_mode")
292 0 : << "\nenable_damage = " << g_settings->get("enable_damage")
293 0 : << "\n";
294 0 : if (!fs::safeWriteToFile(worldmt_path, ss.str()))
295 0 : return false;
296 :
297 0 : infostream << "Wrote world.mt (" << worldmt_path << ")" << std::endl;
298 : }
299 :
300 : // Create map_meta.txt if does not already exist
301 0 : std::string map_meta_path = path + DIR_DELIM + "map_meta.txt";
302 0 : if (!fs::PathExists(map_meta_path)){
303 0 : verbosestream << "Creating map_meta.txt (" << map_meta_path << ")" << std::endl;
304 0 : fs::CreateAllDirs(path);
305 0 : std::ostringstream oss(std::ios_base::binary);
306 :
307 0 : Settings conf;
308 0 : MapgenParams params;
309 :
310 0 : params.load(*g_settings);
311 0 : params.save(conf);
312 0 : conf.writeLines(oss);
313 0 : oss << "[end_of_params]\n";
314 :
315 0 : fs::safeWriteToFile(map_meta_path, oss.str());
316 : }
317 0 : return true;
318 3 : }
319 :
|