1d73b40253003a6a37140e33daae2375cd13a61e
[oweals/minetest.git] / src / main.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-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 #ifdef _MSC_VER
21 #ifndef SERVER // Dedicated server isn't linked with Irrlicht
22         #pragma comment(lib, "Irrlicht.lib")
23         // This would get rid of the console window
24         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
25 #endif
26         #pragma comment(lib, "zlibwapi.lib")
27         #pragma comment(lib, "Shell32.lib")
28 #endif
29
30 #include "irrlicht.h" // createDevice
31
32 #include "mainmenumanager.h"
33 #include "irrlichttypes_extrabloated.h"
34 #include "debug.h"
35 #include "unittest/test.h"
36 #include "server.h"
37 #include "filesys.h"
38 #include "version.h"
39 #include "guiMainMenu.h"
40 #include "game.h"
41 #include "defaultsettings.h"
42 #include "gettext.h"
43 #include "log.h"
44 #include "quicktune.h"
45 #include "httpfetch.h"
46 #include "guiEngine.h"
47 #include "map.h"
48 #include "mapsector.h"
49 #include "fontengine.h"
50 #include "gameparams.h"
51 #include "database.h"
52 #ifndef SERVER
53 #include "client/clientlauncher.h"
54 #endif
55
56 #ifdef HAVE_TOUCHSCREENGUI
57 #include "touchscreengui.h"
58 #endif
59
60 #define DEBUGFILE "debug.txt"
61 #define DEFAULT_SERVER_PORT 30000
62
63 typedef std::map<std::string, ValueSpec> OptionList;
64
65 /**********************************************************************
66  * Private functions
67  **********************************************************************/
68
69 static bool get_cmdline_opts(int argc, char *argv[], Settings *cmd_args);
70 static void set_allowed_options(OptionList *allowed_options);
71
72 static void print_help(const OptionList &allowed_options);
73 static void print_allowed_options(const OptionList &allowed_options);
74 static void print_version();
75 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
76                                                          std::ostream &os);
77 static void print_modified_quicktune_values();
78
79 static void list_game_ids();
80 static void list_worlds();
81 static void setup_log_params(const Settings &cmd_args);
82 static bool create_userdata_path();
83 static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[]);
84 static void startup_message();
85 static bool read_config_file(const Settings &cmd_args);
86 static void init_debug_streams(int *log_level, const Settings &cmd_args);
87
88 static bool game_configure(GameParams *game_params, const Settings &cmd_args);
89 static void game_configure_port(GameParams *game_params, const Settings &cmd_args);
90
91 static bool game_configure_world(GameParams *game_params, const Settings &cmd_args);
92 static bool get_world_from_cmdline(GameParams *game_params, const Settings &cmd_args);
93 static bool get_world_from_config(GameParams *game_params, const Settings &cmd_args);
94 static bool auto_select_world(GameParams *game_params);
95 static std::string get_clean_world_path(const std::string &path);
96
97 static bool game_configure_subgame(GameParams *game_params, const Settings &cmd_args);
98 static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_args);
99 static bool determine_subgame(GameParams *game_params);
100
101 static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args);
102 static bool migrate_database(const GameParams &game_params, const Settings &cmd_args);
103
104 /**********************************************************************/
105
106 /*
107         gettime.h implementation
108 */
109
110 #ifdef SERVER
111
112 u32 getTimeMs()
113 {
114         /* Use imprecise system calls directly (from porting.h) */
115         return porting::getTime(PRECISION_MILLI);
116 }
117
118 u32 getTime(TimePrecision prec)
119 {
120         return porting::getTime(prec);
121 }
122
123 #endif
124
125 class StderrLogOutput: public ILogOutput
126 {
127 public:
128         /* line: Full line with timestamp, level and thread */
129         void printLog(const std::string &line)
130         {
131                 std::cerr << line << std::endl;
132         }
133 } main_stderr_log_out;
134
135 class DstreamNoStderrLogOutput: public ILogOutput
136 {
137 public:
138         /* line: Full line with timestamp, level and thread */
139         void printLog(const std::string &line)
140         {
141                 dstream_no_stderr << line << std::endl;
142         }
143 } main_dstream_no_stderr_log_out;
144
145 static OptionList allowed_options;
146
147 int main(int argc, char *argv[])
148 {
149         int retval;
150
151         debug_set_exception_handler();
152
153         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
154         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
155
156         log_register_thread("main");
157
158         Settings cmd_args;
159         bool cmd_args_ok = get_cmdline_opts(argc, argv, &cmd_args);
160         if (!cmd_args_ok
161                         || cmd_args.getFlag("help")
162                         || cmd_args.exists("nonopt1")) {
163                 print_help(allowed_options);
164                 return cmd_args_ok ? 0 : 1;
165         }
166
167         if (cmd_args.getFlag("version")) {
168                 print_version();
169                 return 0;
170         }
171
172         setup_log_params(cmd_args);
173
174         porting::signal_handler_init();
175         porting::initializePaths();
176
177         if (!create_userdata_path()) {
178                 errorstream << "Cannot create user data directory" << std::endl;
179                 return 1;
180         }
181
182         // Initialize debug stacks
183         debug_stacks_init();
184         DSTACK(__FUNCTION_NAME);
185
186         // Debug handler
187         BEGIN_DEBUG_EXCEPTION_HANDLER
188
189         // List gameids if requested
190         if (cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") {
191                 list_game_ids();
192                 return 0;
193         }
194
195         // List worlds if requested
196         if (cmd_args.exists("world") && cmd_args.get("world") == "list") {
197                 list_worlds();
198                 return 0;
199         }
200
201         GameParams game_params;
202         if (!init_common(&game_params.log_level, cmd_args, argc, argv))
203                 return 1;
204
205 #ifndef __ANDROID__
206         // Run unit tests
207         if (cmd_args.getFlag("run-unittests")) {
208                 run_tests();
209                 return 0;
210         }
211 #endif
212
213 #ifdef SERVER
214         game_params.is_dedicated_server = true;
215 #else
216         game_params.is_dedicated_server = cmd_args.getFlag("server");
217 #endif
218
219         if (!game_configure(&game_params, cmd_args))
220                 return 1;
221
222         sanity_check(game_params.world_path != "");
223
224         infostream << "Using commanded world path ["
225                    << game_params.world_path << "]" << std::endl;
226
227         //Run dedicated server if asked to or no other option
228         g_settings->set("server_dedicated",
229                         game_params.is_dedicated_server ? "true" : "false");
230
231         if (game_params.is_dedicated_server)
232                 return run_dedicated_server(game_params, cmd_args) ? 0 : 1;
233
234 #ifndef SERVER
235         ClientLauncher launcher;
236         retval = launcher.run(game_params, cmd_args) ? 0 : 1;
237 #else
238         retval = 0;
239 #endif
240
241         // Update configuration file
242         if (g_settings_path != "")
243                 g_settings->updateConfigFile(g_settings_path.c_str());
244
245         print_modified_quicktune_values();
246
247         // Stop httpfetch thread (if started)
248         httpfetch_cleanup();
249
250         END_DEBUG_EXCEPTION_HANDLER(errorstream)
251
252         return retval;
253 }
254
255
256 /*****************************************************************************
257  * Startup / Init
258  *****************************************************************************/
259
260
261 static bool get_cmdline_opts(int argc, char *argv[], Settings *cmd_args)
262 {
263         set_allowed_options(&allowed_options);
264
265         return cmd_args->parseCommandLine(argc, argv, allowed_options);
266 }
267
268 static void set_allowed_options(OptionList *allowed_options)
269 {
270         allowed_options->clear();
271
272         allowed_options->insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
273                         _("Show allowed options"))));
274         allowed_options->insert(std::make_pair("version", ValueSpec(VALUETYPE_FLAG,
275                         _("Show version information"))));
276         allowed_options->insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
277                         _("Load configuration from specified file"))));
278         allowed_options->insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
279                         _("Set network port (UDP)"))));
280         allowed_options->insert(std::make_pair("run-unittests", ValueSpec(VALUETYPE_FLAG,
281                         _("Run the unit tests and exit"))));
282         allowed_options->insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
283                         _("Same as --world (deprecated)"))));
284         allowed_options->insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
285                         _("Set world path (implies local game) ('list' lists all)"))));
286         allowed_options->insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
287                         _("Set world by name (implies local game)"))));
288         allowed_options->insert(std::make_pair("quiet", ValueSpec(VALUETYPE_FLAG,
289                         _("Print to console errors only"))));
290         allowed_options->insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
291                         _("Print more information to console"))));
292         allowed_options->insert(std::make_pair("verbose",  ValueSpec(VALUETYPE_FLAG,
293                         _("Print even more information to console"))));
294         allowed_options->insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
295                         _("Print enormous amounts of information to log and console"))));
296         allowed_options->insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
297                         _("Set logfile path ('' = no logging)"))));
298         allowed_options->insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
299                         _("Set gameid (\"--gameid list\" prints available ones)"))));
300         allowed_options->insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING,
301                         _("Migrate from current map backend to another (Only works when using minetestserver or with --server)"))));
302 #ifndef SERVER
303         allowed_options->insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
304                         _("Show available video modes"))));
305         allowed_options->insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
306                         _("Run speed tests"))));
307         allowed_options->insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
308                         _("Address to connect to. ('' = local game)"))));
309         allowed_options->insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
310                         _("Enable random user input, for testing"))));
311         allowed_options->insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
312                         _("Run dedicated server"))));
313         allowed_options->insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
314                         _("Set player name"))));
315         allowed_options->insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
316                         _("Set password"))));
317         allowed_options->insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
318                         _("Disable main menu"))));
319 #endif
320
321 }
322
323 static void print_help(const OptionList &allowed_options)
324 {
325         dstream << _("Allowed options:") << std::endl;
326         print_allowed_options(allowed_options);
327 }
328
329 static void print_allowed_options(const OptionList &allowed_options)
330 {
331         for (OptionList::const_iterator i = allowed_options.begin();
332                         i != allowed_options.end(); ++i) {
333                 std::ostringstream os1(std::ios::binary);
334                 os1 << "  --" << i->first;
335                 if (i->second.type != VALUETYPE_FLAG)
336                         os1 << _(" <value>");
337
338                 dstream << padStringRight(os1.str(), 24);
339
340                 if (i->second.help != NULL)
341                         dstream << i->second.help;
342
343                 dstream << std::endl;
344         }
345 }
346
347 static void print_version()
348 {
349         dstream << PROJECT_NAME_C " " << g_version_hash << std::endl;
350 #ifndef SERVER
351         dstream << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
352 #endif
353         dstream << "Build info: " << g_build_info << std::endl;
354 }
355
356 static void list_game_ids()
357 {
358         std::set<std::string> gameids = getAvailableGameIds();
359         for (std::set<std::string>::const_iterator i = gameids.begin();
360                         i != gameids.end(); i++)
361                 dstream << (*i) <<std::endl;
362 }
363
364 static void list_worlds()
365 {
366         dstream << _("Available worlds:") << std::endl;
367         std::vector<WorldSpec> worldspecs = getAvailableWorlds();
368         print_worldspecs(worldspecs, dstream);
369 }
370
371 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
372                                                          std::ostream &os)
373 {
374         for (size_t i = 0; i < worldspecs.size(); i++) {
375                 std::string name = worldspecs[i].name;
376                 std::string path = worldspecs[i].path;
377                 if (name.find(" ") != std::string::npos)
378                         name = std::string("'") + name + "'";
379                 path = std::string("'") + path + "'";
380                 name = padStringRight(name, 14);
381                 os << "  " << name << " " << path << std::endl;
382         }
383 }
384
385 static void print_modified_quicktune_values()
386 {
387         bool header_printed = false;
388         std::vector<std::string> names = getQuicktuneNames();
389
390         for (u32 i = 0; i < names.size(); i++) {
391                 QuicktuneValue val = getQuicktuneValue(names[i]);
392                 if (!val.modified)
393                         continue;
394                 if (!header_printed) {
395                         dstream << "Modified quicktune values:" << std::endl;
396                         header_printed = true;
397                 }
398                 dstream << names[i] << " = " << val.getString() << std::endl;
399         }
400 }
401
402 static void setup_log_params(const Settings &cmd_args)
403 {
404         // Quiet mode, print errors only
405         if (cmd_args.getFlag("quiet")) {
406                 log_remove_output(&main_stderr_log_out);
407                 log_add_output_maxlev(&main_stderr_log_out, LMT_ERROR);
408         }
409
410         // If trace is enabled, enable logging of certain things
411         if (cmd_args.getFlag("trace")) {
412                 dstream << _("Enabling trace level debug output") << std::endl;
413                 log_trace_level_enabled = true;
414                 dout_con_ptr = &verbosestream; // this is somewhat old crap
415                 socket_enable_debug_output = true; // socket doesn't use log.h
416         }
417
418         // In certain cases, output info level on stderr
419         if (cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
420                         cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
421                 log_add_output(&main_stderr_log_out, LMT_INFO);
422
423         // In certain cases, output verbose level on stderr
424         if (cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
425                 log_add_output(&main_stderr_log_out, LMT_VERBOSE);
426 }
427
428 static bool create_userdata_path()
429 {
430         bool success;
431
432 #ifdef __ANDROID__
433         porting::initAndroid();
434
435         porting::setExternalStorageDir(porting::jnienv);
436         if (!fs::PathExists(porting::path_user)) {
437                 success = fs::CreateDir(porting::path_user);
438         } else {
439                 success = true;
440         }
441         porting::copyAssets();
442 #else
443         // Create user data directory
444         success = fs::CreateDir(porting::path_user);
445 #endif
446
447         infostream << "path_share = " << porting::path_share << std::endl;
448         infostream << "path_user  = " << porting::path_user << std::endl;
449
450         return success;
451 }
452
453 static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[])
454 {
455         startup_message();
456         set_default_settings(g_settings);
457
458         // Initialize sockets
459         sockets_init();
460         atexit(sockets_cleanup);
461
462         if (!read_config_file(cmd_args))
463                 return false;
464
465         init_debug_streams(log_level, cmd_args);
466
467         // Initialize random seed
468         srand(time(0));
469         mysrand(time(0));
470
471         // Initialize HTTP fetcher
472         httpfetch_init(g_settings->getS32("curl_parallel_limit"));
473
474 #ifdef _MSC_VER
475         init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
476                 g_settings->get("language"), argc, argv);
477 #else
478         init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
479                 g_settings->get("language"));
480 #endif
481
482         return true;
483 }
484
485 static void startup_message()
486 {
487         infostream << PROJECT_NAME << " " << _("with")
488                    << " SER_FMT_VER_HIGHEST_READ="
489                << (int)SER_FMT_VER_HIGHEST_READ << ", "
490                << g_build_info << std::endl;
491 }
492
493 static bool read_config_file(const Settings &cmd_args)
494 {
495         // Path of configuration file in use
496         sanity_check(g_settings_path == "");    // Sanity check
497
498         if (cmd_args.exists("config")) {
499                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
500                 if (!r) {
501                         errorstream << "Could not read configuration from \""
502                                     << cmd_args.get("config") << "\"" << std::endl;
503                         return false;
504                 }
505                 g_settings_path = cmd_args.get("config");
506         } else {
507                 std::vector<std::string> filenames;
508                 filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf");
509                 // Legacy configuration file location
510                 filenames.push_back(porting::path_user +
511                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
512
513 #if RUN_IN_PLACE
514                 // Try also from a lower level (to aid having the same configuration
515                 // for many RUN_IN_PLACE installs)
516                 filenames.push_back(porting::path_user +
517                                 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
518 #endif
519
520                 for (size_t i = 0; i < filenames.size(); i++) {
521                         bool r = g_settings->readConfigFile(filenames[i].c_str());
522                         if (r) {
523                                 g_settings_path = filenames[i];
524                                 break;
525                         }
526                 }
527
528                 // If no path found, use the first one (menu creates the file)
529                 if (g_settings_path == "")
530                         g_settings_path = filenames[0];
531         }
532
533         return true;
534 }
535
536 static void init_debug_streams(int *log_level, const Settings &cmd_args)
537 {
538 #if RUN_IN_PLACE
539         std::string logfile = DEBUGFILE;
540 #else
541         std::string logfile = porting::path_user + DIR_DELIM + DEBUGFILE;
542 #endif
543         if (cmd_args.exists("logfile"))
544                 logfile = cmd_args.get("logfile");
545
546         log_remove_output(&main_dstream_no_stderr_log_out);
547         *log_level = g_settings->getS32("debug_log_level");
548
549         if (*log_level == 0) //no logging
550                 logfile = "";
551         if (*log_level < 0) {
552                 dstream << "WARNING: Supplied debug_log_level < 0; Using 0" << std::endl;
553                 *log_level = 0;
554         } else if (*log_level > LMT_NUM_VALUES) {
555                 dstream << "WARNING: Supplied debug_log_level > " << LMT_NUM_VALUES
556                         << "; Using " << LMT_NUM_VALUES << std::endl;
557                 *log_level = LMT_NUM_VALUES;
558         }
559
560         log_add_output_maxlev(&main_dstream_no_stderr_log_out,
561                         (LogMessageLevel)(*log_level - 1));
562
563         debugstreams_init(false, logfile == "" ? NULL : logfile.c_str());
564
565         infostream << "logfile = " << logfile << std::endl;
566
567         atexit(debugstreams_deinit);
568 }
569
570 static bool game_configure(GameParams *game_params, const Settings &cmd_args)
571 {
572         game_configure_port(game_params, cmd_args);
573
574         if (!game_configure_world(game_params, cmd_args)) {
575                 errorstream << "No world path specified or found." << std::endl;
576                 return false;
577         }
578
579         game_configure_subgame(game_params, cmd_args);
580
581         return true;
582 }
583
584 static void game_configure_port(GameParams *game_params, const Settings &cmd_args)
585 {
586         if (cmd_args.exists("port"))
587                 game_params->socket_port = cmd_args.getU16("port");
588         else
589                 game_params->socket_port = g_settings->getU16("port");
590
591         if (game_params->socket_port == 0)
592                 game_params->socket_port = DEFAULT_SERVER_PORT;
593 }
594
595 static bool game_configure_world(GameParams *game_params, const Settings &cmd_args)
596 {
597         if (get_world_from_cmdline(game_params, cmd_args))
598                 return true;
599         if (get_world_from_config(game_params, cmd_args))
600                 return true;
601
602         return auto_select_world(game_params);
603 }
604
605 static bool get_world_from_cmdline(GameParams *game_params, const Settings &cmd_args)
606 {
607         std::string commanded_world = "";
608
609         // World name
610         std::string commanded_worldname = "";
611         if (cmd_args.exists("worldname"))
612                 commanded_worldname = cmd_args.get("worldname");
613
614         // If a world name was specified, convert it to a path
615         if (commanded_worldname != "") {
616                 // Get information about available worlds
617                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
618                 bool found = false;
619                 for (u32 i = 0; i < worldspecs.size(); i++) {
620                         std::string name = worldspecs[i].name;
621                         if (name == commanded_worldname) {
622                                 dstream << _("Using world specified by --worldname on the "
623                                         "command line") << std::endl;
624                                 commanded_world = worldspecs[i].path;
625                                 found = true;
626                                 break;
627                         }
628                 }
629                 if (!found) {
630                         dstream << _("World") << " '" << commanded_worldname
631                                 << _("' not available. Available worlds:") << std::endl;
632                         print_worldspecs(worldspecs, dstream);
633                         return false;
634                 }
635
636                 game_params->world_path = get_clean_world_path(commanded_world);
637                 return commanded_world != "";
638         }
639
640         if (cmd_args.exists("world"))
641                 commanded_world = cmd_args.get("world");
642         else if (cmd_args.exists("map-dir"))
643                 commanded_world = cmd_args.get("map-dir");
644         else if (cmd_args.exists("nonopt0")) // First nameless argument
645                 commanded_world = cmd_args.get("nonopt0");
646
647         game_params->world_path = get_clean_world_path(commanded_world);
648         return commanded_world != "";
649 }
650
651 static bool get_world_from_config(GameParams *game_params, const Settings &cmd_args)
652 {
653         // World directory
654         std::string commanded_world = "";
655
656         if (g_settings->exists("map-dir"))
657                 commanded_world = g_settings->get("map-dir");
658
659         game_params->world_path = get_clean_world_path(commanded_world);
660
661         return commanded_world != "";
662 }
663
664 static bool auto_select_world(GameParams *game_params)
665 {
666         // No world was specified; try to select it automatically
667         // Get information about available worlds
668
669         verbosestream << _("Determining world path") << std::endl;
670
671         std::vector<WorldSpec> worldspecs = getAvailableWorlds();
672         std::string world_path;
673
674         // If there is only a single world, use it
675         if (worldspecs.size() == 1) {
676                 world_path = worldspecs[0].path;
677                 dstream <<_("Automatically selecting world at") << " ["
678                         << world_path << "]" << std::endl;
679         // If there are multiple worlds, list them
680         } else if (worldspecs.size() > 1 && game_params->is_dedicated_server) {
681                 dstream << _("Multiple worlds are available.") << std::endl;
682                 dstream << _("Please select one using --worldname <name>"
683                                 " or --world <path>") << std::endl;
684                 print_worldspecs(worldspecs, dstream);
685                 return false;
686         // If there are no worlds, automatically create a new one
687         } else {
688                 // This is the ultimate default world path
689                 world_path = porting::path_user + DIR_DELIM + "worlds" +
690                                 DIR_DELIM + "world";
691                 infostream << "Creating default world at ["
692                            << world_path << "]" << std::endl;
693         }
694
695         assert(world_path != "");       // Post-condition
696         game_params->world_path = world_path;
697         return true;
698 }
699
700 static std::string get_clean_world_path(const std::string &path)
701 {
702         const std::string worldmt = "world.mt";
703         std::string clean_path;
704
705         if (path.size() > worldmt.size()
706                         && path.substr(path.size() - worldmt.size()) == worldmt) {
707                 dstream << _("Supplied world.mt file - stripping it off.") << std::endl;
708                 clean_path = path.substr(0, path.size() - worldmt.size());
709         } else {
710                 clean_path = path;
711         }
712         return path;
713 }
714
715
716 static bool game_configure_subgame(GameParams *game_params, const Settings &cmd_args)
717 {
718         bool success;
719
720         success = get_game_from_cmdline(game_params, cmd_args);
721         if (!success)
722                 success = determine_subgame(game_params);
723
724         return success;
725 }
726
727 static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_args)
728 {
729         SubgameSpec commanded_gamespec;
730
731         if (cmd_args.exists("gameid")) {
732                 std::string gameid = cmd_args.get("gameid");
733                 commanded_gamespec = findSubgame(gameid);
734                 if (!commanded_gamespec.isValid()) {
735                         errorstream << "Game \"" << gameid << "\" not found" << std::endl;
736                         return false;
737                 }
738                 dstream << _("Using game specified by --gameid on the command line")
739                         << std::endl;
740                 game_params->game_spec = commanded_gamespec;
741                 return true;
742         }
743
744         return false;
745 }
746
747 static bool determine_subgame(GameParams *game_params)
748 {
749         SubgameSpec gamespec;
750
751         assert(game_params->world_path != "");  // Pre-condition
752
753         verbosestream << _("Determining gameid/gamespec") << std::endl;
754         // If world doesn't exist
755         if (game_params->world_path != ""
756                         && !getWorldExists(game_params->world_path)) {
757                 // Try to take gamespec from command line
758                 if (game_params->game_spec.isValid()) {
759                         gamespec = game_params->game_spec;
760                         infostream << "Using commanded gameid [" << gamespec.id << "]" << std::endl;
761                 } else { // Otherwise we will be using "minetest"
762                         gamespec = findSubgame(g_settings->get("default_game"));
763                         infostream << "Using default gameid [" << gamespec.id << "]" << std::endl;
764                         if (!gamespec.isValid()) {
765                                 errorstream << "Subgame specified in default_game ["
766                                             << g_settings->get("default_game")
767                                             << "] is invalid." << std::endl;
768                                 return false;
769                         }
770                 }
771         } else { // World exists
772                 std::string world_gameid = getWorldGameId(game_params->world_path, false);
773                 // If commanded to use a gameid, do so
774                 if (game_params->game_spec.isValid()) {
775                         gamespec = game_params->game_spec;
776                         if (game_params->game_spec.id != world_gameid) {
777                                 errorstream << "WARNING: Using commanded gameid ["
778                                             << gamespec.id << "]" << " instead of world gameid ["
779                                             << world_gameid << "]" << std::endl;
780                         }
781                 } else {
782                         // If world contains an embedded game, use it;
783                         // Otherwise find world from local system.
784                         gamespec = findWorldSubgame(game_params->world_path);
785                         infostream << "Using world gameid [" << gamespec.id << "]" << std::endl;
786                 }
787         }
788
789         if (!gamespec.isValid()) {
790                 errorstream << "Subgame [" << gamespec.id << "] could not be found."
791                             << std::endl;
792                 return false;
793         }
794
795         game_params->game_spec = gamespec;
796         return true;
797 }
798
799
800 /*****************************************************************************
801  * Dedicated server
802  *****************************************************************************/
803 static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args)
804 {
805         DSTACK("Dedicated server branch");
806
807         verbosestream << _("Using world path") << " ["
808                       << game_params.world_path << "]" << std::endl;
809         verbosestream << _("Using gameid") << " ["
810                       << game_params.game_spec.id << "]" << std::endl;
811
812         // Bind address
813         std::string bind_str = g_settings->get("bind_address");
814         Address bind_addr(0, 0, 0, 0, game_params.socket_port);
815
816         if (g_settings->getBool("ipv6_server")) {
817                 bind_addr.setAddress((IPv6AddressBytes*) NULL);
818         }
819         try {
820                 bind_addr.Resolve(bind_str.c_str());
821         } catch (ResolveError &e) {
822                 infostream << "Resolving bind address \"" << bind_str
823                            << "\" failed: " << e.what()
824                            << " -- Listening on all addresses." << std::endl;
825         }
826         if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
827                 errorstream << "Unable to listen on "
828                             << bind_addr.serializeString()
829                             << L" because IPv6 is disabled" << std::endl;
830                 return false;
831         }
832
833         // Database migration
834         if (cmd_args.exists("migrate"))
835                 return migrate_database(game_params, cmd_args);
836
837         // Create server
838         Server server(game_params.world_path,
839                         game_params.game_spec, false, bind_addr.isIPv6());
840
841         server.start(bind_addr);
842
843         // Run server
844         bool &kill = *porting::signal_handler_killstatus();
845         dedicated_server_loop(server, kill);
846
847         return true;
848 }
849
850 static bool migrate_database(const GameParams &game_params, const Settings &cmd_args)
851 {
852         std::string migrate_to = cmd_args.get("migrate");
853         Settings world_mt;
854         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
855         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
856                 errorstream << "Cannot read world.mt!" << std::endl;
857                 return false;
858         }
859         if (!world_mt.exists("backend")) {
860                 errorstream << "Please specify your current backend in world.mt:"
861                         << std::endl
862                         << "    backend = {sqlite3|leveldb|redis|dummy}"
863                         << std::endl;
864                 return false;
865         }
866         std::string backend = world_mt.get("backend");
867         if (backend == migrate_to) {
868                 errorstream << "Cannot migrate: new backend is same"
869                         << " as the old one" << std::endl;
870                 return false;
871         }
872         Database *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt),
873                 *new_db = ServerMap::createDatabase(migrate_to, game_params.world_path, world_mt);
874
875         u32 count = 0;
876         time_t last_update_time = 0;
877         bool &kill = *porting::signal_handler_killstatus();
878
879         std::vector<v3s16> blocks;
880         old_db->listAllLoadableBlocks(blocks);
881         new_db->beginSave();
882         for (std::vector<v3s16>::const_iterator it = blocks.begin(); it != blocks.end(); ++it) {
883                 if (kill) return false;
884
885                 const std::string &data = old_db->loadBlock(*it);
886                 if (!data.empty()) {
887                         new_db->saveBlock(*it, data);
888                 } else {
889                         errorstream << "Failed to load block " << PP(*it) << ", skipping it." << std::endl;
890                 }
891                 if (++count % 0xFF == 0 && time(NULL) - last_update_time >= 1) {
892                         std::cerr << " Migrated " << count << " blocks, "
893                                 << (100.0 * count / blocks.size()) << "% completed.\r";
894                         new_db->endSave();
895                         new_db->beginSave();
896                         last_update_time = time(NULL);
897                 }
898         }
899         std::cerr << std::endl;
900         new_db->endSave();
901         delete old_db;
902         delete new_db;
903
904         actionstream << "Successfully migrated " << count << " blocks" << std::endl;
905         world_mt.set("backend", migrate_to);
906         if (!world_mt.updateConfigFile(world_mt_path.c_str()))
907                 errorstream << "Failed to update world.mt!" << std::endl;
908         else
909                 actionstream << "world.mt updated" << std::endl;
910
911         return true;
912 }
913