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