]> git.lizzy.rs Git - dragonfireclient.git/blob - src/main.cpp
Fix for commit 87dcee6 It uses the wrong variable and only covers some use cases...
[dragonfireclient.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 << std::endl;
348 #ifndef SERVER
349         std::cout << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
350 #endif
351         std::cout << "Build info: " << g_build_info << std::endl;
352 }
353
354 static void list_game_ids()
355 {
356         std::set<std::string> gameids = getAvailableGameIds();
357         for (std::set<std::string>::const_iterator i = gameids.begin();
358                         i != gameids.end(); ++i)
359                 std::cout << (*i) <<std::endl;
360 }
361
362 static void list_worlds()
363 {
364         std::cout << _("Available worlds:") << std::endl;
365         std::vector<WorldSpec> worldspecs = getAvailableWorlds();
366         print_worldspecs(worldspecs, std::cout);
367 }
368
369 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
370                                                          std::ostream &os)
371 {
372         for (size_t i = 0; i < worldspecs.size(); i++) {
373                 std::string name = worldspecs[i].name;
374                 std::string path = worldspecs[i].path;
375                 if (name.find(" ") != std::string::npos)
376                         name = std::string("'") + name + "'";
377                 path = std::string("'") + path + "'";
378                 name = padStringRight(name, 14);
379                 os << "  " << name << " " << path << std::endl;
380         }
381 }
382
383 static void print_modified_quicktune_values()
384 {
385         bool header_printed = false;
386         std::vector<std::string> names = getQuicktuneNames();
387
388         for (u32 i = 0; i < names.size(); i++) {
389                 QuicktuneValue val = getQuicktuneValue(names[i]);
390                 if (!val.modified)
391                         continue;
392                 if (!header_printed) {
393                         dstream << "Modified quicktune values:" << std::endl;
394                         header_printed = true;
395                 }
396                 dstream << names[i] << " = " << val.getString() << std::endl;
397         }
398 }
399
400 static void setup_log_params(const Settings &cmd_args)
401 {
402         // Quiet mode, print errors only
403         if (cmd_args.getFlag("quiet")) {
404                 g_logger.removeOutput(&stderr_output);
405                 g_logger.addOutputMaxLevel(&stderr_output, LL_ERROR);
406         }
407
408         // If trace is enabled, enable logging of certain things
409         if (cmd_args.getFlag("trace")) {
410                 dstream << _("Enabling trace level debug output") << std::endl;
411                 g_logger.setTraceEnabled(true);
412                 dout_con_ptr = &verbosestream; // This is somewhat old
413                 socket_enable_debug_output = true; // Sockets doesn't use log.h
414         }
415
416         // In certain cases, output info level on stderr
417         if (cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
418                         cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
419                 g_logger.addOutput(&stderr_output, LL_INFO);
420
421         // In certain cases, output verbose level on stderr
422         if (cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
423                 g_logger.addOutput(&stderr_output, LL_VERBOSE);
424 }
425
426 static bool create_userdata_path()
427 {
428         bool success;
429
430 #ifdef __ANDROID__
431         if (!fs::PathExists(porting::path_user)) {
432                 success = fs::CreateDir(porting::path_user);
433         } else {
434                 success = true;
435         }
436         porting::copyAssets();
437 #else
438         // Create user data directory
439         success = fs::CreateDir(porting::path_user);
440 #endif
441
442         return success;
443 }
444
445 static bool init_common(const Settings &cmd_args, int argc, char *argv[])
446 {
447         startup_message();
448         set_default_settings(g_settings);
449
450         // Initialize sockets
451         sockets_init();
452         atexit(sockets_cleanup);
453
454         if (!read_config_file(cmd_args))
455                 return false;
456
457         init_log_streams(cmd_args);
458
459         // Initialize random seed
460         srand(time(0));
461         mysrand(time(0));
462
463         // Initialize HTTP fetcher
464         httpfetch_init(g_settings->getS32("curl_parallel_limit"));
465
466         init_gettext(porting::path_locale.c_str(),
467                 g_settings->get("language"), argc, argv);
468
469         return true;
470 }
471
472 static void startup_message()
473 {
474         infostream << PROJECT_NAME << " " << _("with")
475                    << " SER_FMT_VER_HIGHEST_READ="
476                << (int)SER_FMT_VER_HIGHEST_READ << ", "
477                << g_build_info << std::endl;
478 }
479
480 static bool read_config_file(const Settings &cmd_args)
481 {
482         // Path of configuration file in use
483         sanity_check(g_settings_path == "");    // Sanity check
484
485         if (cmd_args.exists("config")) {
486                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
487                 if (!r) {
488                         errorstream << "Could not read configuration from \""
489                                     << cmd_args.get("config") << "\"" << std::endl;
490                         return false;
491                 }
492                 g_settings_path = cmd_args.get("config");
493         } else {
494                 std::vector<std::string> filenames;
495                 filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf");
496                 // Legacy configuration file location
497                 filenames.push_back(porting::path_user +
498                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
499
500 #if RUN_IN_PLACE
501                 // Try also from a lower level (to aid having the same configuration
502                 // for many RUN_IN_PLACE installs)
503                 filenames.push_back(porting::path_user +
504                                 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
505 #endif
506
507                 for (size_t i = 0; i < filenames.size(); i++) {
508                         bool r = g_settings->readConfigFile(filenames[i].c_str());
509                         if (r) {
510                                 g_settings_path = filenames[i];
511                                 break;
512                         }
513                 }
514
515                 // If no path found, use the first one (menu creates the file)
516                 if (g_settings_path == "")
517                         g_settings_path = filenames[0];
518         }
519
520         return true;
521 }
522
523 static void init_log_streams(const Settings &cmd_args)
524 {
525 #if RUN_IN_PLACE
526         std::string log_filename = DEBUGFILE;
527 #else
528         std::string log_filename = porting::path_user + DIR_DELIM + DEBUGFILE;
529 #endif
530         if (cmd_args.exists("logfile"))
531                 log_filename = cmd_args.get("logfile");
532
533         g_logger.removeOutput(&file_log_output);
534         std::string conf_loglev = g_settings->get("debug_log_level");
535
536         // Old integer format
537         if (std::isdigit(conf_loglev[0])) {
538                 warningstream << "Deprecated use of debug_log_level with an "
539                         "integer value; please update your configuration." << std::endl;
540                 static const char *lev_name[] =
541                         {"", "error", "action", "info", "verbose"};
542                 int lev_i = atoi(conf_loglev.c_str());
543                 if (lev_i < 0 || lev_i >= (int)ARRLEN(lev_name)) {
544                         warningstream << "Supplied invalid debug_log_level!"
545                                 "  Assuming action level." << std::endl;
546                         lev_i = 2;
547                 }
548                 conf_loglev = lev_name[lev_i];
549         }
550
551         if (log_filename.empty() || conf_loglev.empty())  // No logging
552                 return;
553
554         LogLevel log_level = Logger::stringToLevel(conf_loglev);
555         if (log_level == LL_MAX) {
556                 warningstream << "Supplied unrecognized debug_log_level; "
557                         "using maximum." << std::endl;
558         }
559
560         verbosestream << "log_filename = " << log_filename << std::endl;
561
562         file_log_output.open(log_filename.c_str());
563         g_logger.addOutputMaxLevel(&file_log_output, log_level);
564 }
565
566 static bool game_configure(GameParams *game_params, const Settings &cmd_args)
567 {
568         game_configure_port(game_params, cmd_args);
569
570         if (!game_configure_world(game_params, cmd_args)) {
571                 errorstream << "No world path specified or found." << std::endl;
572                 return false;
573         }
574
575         game_configure_subgame(game_params, cmd_args);
576
577         return true;
578 }
579
580 static void game_configure_port(GameParams *game_params, const Settings &cmd_args)
581 {
582         if (cmd_args.exists("port"))
583                 game_params->socket_port = cmd_args.getU16("port");
584         else
585                 game_params->socket_port = g_settings->getU16("port");
586
587         if (game_params->socket_port == 0)
588                 game_params->socket_port = DEFAULT_SERVER_PORT;
589 }
590
591 static bool game_configure_world(GameParams *game_params, const Settings &cmd_args)
592 {
593         if (get_world_from_cmdline(game_params, cmd_args))
594                 return true;
595         if (get_world_from_config(game_params, cmd_args))
596                 return true;
597
598         return auto_select_world(game_params);
599 }
600
601 static bool get_world_from_cmdline(GameParams *game_params, const Settings &cmd_args)
602 {
603         std::string commanded_world = "";
604
605         // World name
606         std::string commanded_worldname = "";
607         if (cmd_args.exists("worldname"))
608                 commanded_worldname = cmd_args.get("worldname");
609
610         // If a world name was specified, convert it to a path
611         if (commanded_worldname != "") {
612                 // Get information about available worlds
613                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
614                 bool found = false;
615                 for (u32 i = 0; i < worldspecs.size(); i++) {
616                         std::string name = worldspecs[i].name;
617                         if (name == commanded_worldname) {
618                                 dstream << _("Using world specified by --worldname on the "
619                                         "command line") << std::endl;
620                                 commanded_world = worldspecs[i].path;
621                                 found = true;
622                                 break;
623                         }
624                 }
625                 if (!found) {
626                         dstream << _("World") << " '" << commanded_worldname
627                                 << _("' not available. Available worlds:") << std::endl;
628                         print_worldspecs(worldspecs, dstream);
629                         return false;
630                 }
631
632                 game_params->world_path = get_clean_world_path(commanded_world);
633                 return commanded_world != "";
634         }
635
636         if (cmd_args.exists("world"))
637                 commanded_world = cmd_args.get("world");
638         else if (cmd_args.exists("map-dir"))
639                 commanded_world = cmd_args.get("map-dir");
640         else if (cmd_args.exists("nonopt0")) // First nameless argument
641                 commanded_world = cmd_args.get("nonopt0");
642
643         game_params->world_path = get_clean_world_path(commanded_world);
644         return commanded_world != "";
645 }
646
647 static bool get_world_from_config(GameParams *game_params, const Settings &cmd_args)
648 {
649         // World directory
650         std::string commanded_world = "";
651
652         if (g_settings->exists("map-dir"))
653                 commanded_world = g_settings->get("map-dir");
654
655         game_params->world_path = get_clean_world_path(commanded_world);
656
657         return commanded_world != "";
658 }
659
660 static bool auto_select_world(GameParams *game_params)
661 {
662         // No world was specified; try to select it automatically
663         // Get information about available worlds
664
665         verbosestream << _("Determining world path") << std::endl;
666
667         std::vector<WorldSpec> worldspecs = getAvailableWorlds();
668         std::string world_path;
669
670         // If there is only a single world, use it
671         if (worldspecs.size() == 1) {
672                 world_path = worldspecs[0].path;
673                 dstream <<_("Automatically selecting world at") << " ["
674                         << world_path << "]" << std::endl;
675         // If there are multiple worlds, list them
676         } else if (worldspecs.size() > 1 && game_params->is_dedicated_server) {
677                 std::cerr << _("Multiple worlds are available.") << std::endl;
678                 std::cerr << _("Please select one using --worldname <name>"
679                                 " or --world <path>") << std::endl;
680                 print_worldspecs(worldspecs, std::cerr);
681                 return false;
682         // If there are no worlds, automatically create a new one
683         } else {
684                 // This is the ultimate default world path
685                 world_path = porting::path_user + DIR_DELIM + "worlds" +
686                                 DIR_DELIM + "world";
687                 infostream << "Creating default world at ["
688                            << world_path << "]" << std::endl;
689         }
690
691         assert(world_path != "");       // Post-condition
692         game_params->world_path = world_path;
693         return true;
694 }
695
696 static std::string get_clean_world_path(const std::string &path)
697 {
698         const std::string worldmt = "world.mt";
699         std::string clean_path;
700
701         if (path.size() > worldmt.size()
702                         && path.substr(path.size() - worldmt.size()) == worldmt) {
703                 dstream << _("Supplied world.mt file - stripping it off.") << std::endl;
704                 clean_path = path.substr(0, path.size() - worldmt.size());
705         } else {
706                 clean_path = path;
707         }
708         return path;
709 }
710
711
712 static bool game_configure_subgame(GameParams *game_params, const Settings &cmd_args)
713 {
714         bool success;
715
716         success = get_game_from_cmdline(game_params, cmd_args);
717         if (!success)
718                 success = determine_subgame(game_params);
719
720         return success;
721 }
722
723 static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_args)
724 {
725         SubgameSpec commanded_gamespec;
726
727         if (cmd_args.exists("gameid")) {
728                 std::string gameid = cmd_args.get("gameid");
729                 commanded_gamespec = findSubgame(gameid);
730                 if (!commanded_gamespec.isValid()) {
731                         errorstream << "Game \"" << gameid << "\" not found" << std::endl;
732                         return false;
733                 }
734                 dstream << _("Using game specified by --gameid on the command line")
735                         << std::endl;
736                 game_params->game_spec = commanded_gamespec;
737                 return true;
738         }
739
740         return false;
741 }
742
743 static bool determine_subgame(GameParams *game_params)
744 {
745         SubgameSpec gamespec;
746
747         assert(game_params->world_path != "");  // Pre-condition
748
749         verbosestream << _("Determining gameid/gamespec") << std::endl;
750         // If world doesn't exist
751         if (game_params->world_path != ""
752                         && !getWorldExists(game_params->world_path)) {
753                 // Try to take gamespec from command line
754                 if (game_params->game_spec.isValid()) {
755                         gamespec = game_params->game_spec;
756                         infostream << "Using commanded gameid [" << gamespec.id << "]" << std::endl;
757                 } else { // Otherwise we will be using "minetest"
758                         gamespec = findSubgame(g_settings->get("default_game"));
759                         infostream << "Using default gameid [" << gamespec.id << "]" << std::endl;
760                         if (!gamespec.isValid()) {
761                                 errorstream << "Subgame specified in default_game ["
762                                             << g_settings->get("default_game")
763                                             << "] is invalid." << std::endl;
764                                 return false;
765                         }
766                 }
767         } else { // World exists
768                 std::string world_gameid = getWorldGameId(game_params->world_path, false);
769                 // If commanded to use a gameid, do so
770                 if (game_params->game_spec.isValid()) {
771                         gamespec = game_params->game_spec;
772                         if (game_params->game_spec.id != world_gameid) {
773                                 warningstream << "Using commanded gameid ["
774                                             << gamespec.id << "]" << " instead of world gameid ["
775                                             << world_gameid << "]" << std::endl;
776                         }
777                 } else {
778                         // If world contains an embedded game, use it;
779                         // Otherwise find world from local system.
780                         gamespec = findWorldSubgame(game_params->world_path);
781                         infostream << "Using world gameid [" << gamespec.id << "]" << std::endl;
782                 }
783         }
784
785         if (!gamespec.isValid()) {
786                 errorstream << "Subgame [" << gamespec.id << "] could not be found."
787                             << std::endl;
788                 return false;
789         }
790
791         game_params->game_spec = gamespec;
792         return true;
793 }
794
795
796 /*****************************************************************************
797  * Dedicated server
798  *****************************************************************************/
799 static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args)
800 {
801         DSTACK("Dedicated server branch");
802
803         verbosestream << _("Using world path") << " ["
804                       << game_params.world_path << "]" << std::endl;
805         verbosestream << _("Using gameid") << " ["
806                       << game_params.game_spec.id << "]" << std::endl;
807
808         // Bind address
809         std::string bind_str = g_settings->get("bind_address");
810         Address bind_addr(0, 0, 0, 0, game_params.socket_port);
811
812         if (g_settings->getBool("ipv6_server")) {
813                 bind_addr.setAddress((IPv6AddressBytes*) NULL);
814         }
815         try {
816                 bind_addr.Resolve(bind_str.c_str());
817         } catch (ResolveError &e) {
818                 infostream << "Resolving bind address \"" << bind_str
819                            << "\" failed: " << e.what()
820                            << " -- Listening on all addresses." << std::endl;
821         }
822         if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
823                 errorstream << "Unable to listen on "
824                             << bind_addr.serializeString()
825                             << L" because IPv6 is disabled" << std::endl;
826                 return false;
827         }
828
829         // Database migration
830         if (cmd_args.exists("migrate"))
831                 return migrate_database(game_params, cmd_args);
832
833         if (cmd_args.exists("terminal")) {
834 #if USE_CURSES
835                 bool name_ok = true;
836                 std::string admin_nick = g_settings->get("name");
837
838                 name_ok = name_ok && !admin_nick.empty();
839                 name_ok = name_ok && string_allowed(admin_nick, PLAYERNAME_ALLOWED_CHARS);
840
841                 if (!name_ok) {
842                         if (admin_nick.empty()) {
843                                 errorstream << "No name given for admin. "
844                                         << "Please check your minetest.conf that it "
845                                         << "contains a 'name = ' to your main admin account."
846                                         << std::endl;
847                         } else {
848                                 errorstream << "Name for admin '"
849                                         << admin_nick << "' is not valid. "
850                                         << "Please check that it only contains allowed characters. "
851                                         << "Valid characters are: " << PLAYERNAME_ALLOWED_CHARS_USER_EXPL
852                                         << std::endl;
853                         }
854                         return false;
855                 }
856                 ChatInterface iface;
857                 bool &kill = *porting::signal_handler_killstatus();
858
859                 try {
860                         // Create server
861                         Server server(game_params.world_path,
862                                 game_params.game_spec, false, bind_addr.isIPv6(), &iface);
863
864                         g_term_console.setup(&iface, &kill, admin_nick);
865
866                         g_term_console.start();
867
868                         server.start(bind_addr);
869                         // Run server
870                         dedicated_server_loop(server, kill);
871                 } catch (const ModError &e) {
872                         g_term_console.stopAndWaitforThread();
873                         errorstream << "ModError: " << e.what() << std::endl;
874                         return false;
875                 } catch (const ServerError &e) {
876                         g_term_console.stopAndWaitforThread();
877                         errorstream << "ServerError: " << e.what() << std::endl;
878                         return false;
879                 }
880
881                 // Tell the console to stop, and wait for it to finish,
882                 // only then leave context and free iface
883                 g_term_console.stop();
884                 g_term_console.wait();
885
886                 g_term_console.clearKillStatus();
887         } else {
888 #else
889                 errorstream << "Cmd arg --terminal passed, but "
890                         << "compiled without ncurses. Ignoring." << std::endl;
891         } {
892 #endif
893                 try {
894                         // Create server
895                         Server server(game_params.world_path, game_params.game_spec, false,
896                                 bind_addr.isIPv6());
897                         server.start(bind_addr);
898
899                         // Run server
900                         bool &kill = *porting::signal_handler_killstatus();
901                         dedicated_server_loop(server, kill);
902
903                 } catch (const ModError &e) {
904                         errorstream << "ModError: " << e.what() << std::endl;
905                         return false;
906                 } catch (const ServerError &e) {
907                         errorstream << "ServerError: " << e.what() << std::endl;
908                         return false;
909                 }
910         }
911
912         return true;
913 }
914
915 static bool migrate_database(const GameParams &game_params, const Settings &cmd_args)
916 {
917         std::string migrate_to = cmd_args.get("migrate");
918         Settings world_mt;
919         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
920         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
921                 errorstream << "Cannot read world.mt!" << std::endl;
922                 return false;
923         }
924         if (!world_mt.exists("backend")) {
925                 errorstream << "Please specify your current backend in world.mt:"
926                         << std::endl
927                         << "    backend = {sqlite3|leveldb|redis|dummy}"
928                         << std::endl;
929                 return false;
930         }
931         std::string backend = world_mt.get("backend");
932         if (backend == migrate_to) {
933                 errorstream << "Cannot migrate: new backend is same"
934                         << " as the old one" << std::endl;
935                 return false;
936         }
937         Database *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt),
938                 *new_db = ServerMap::createDatabase(migrate_to, game_params.world_path, world_mt);
939
940         u32 count = 0;
941         time_t last_update_time = 0;
942         bool &kill = *porting::signal_handler_killstatus();
943
944         std::vector<v3s16> blocks;
945         old_db->listAllLoadableBlocks(blocks);
946         new_db->beginSave();
947         for (std::vector<v3s16>::const_iterator it = blocks.begin(); it != blocks.end(); ++it) {
948                 if (kill) return false;
949
950                 const std::string &data = old_db->loadBlock(*it);
951                 if (!data.empty()) {
952                         new_db->saveBlock(*it, data);
953                 } else {
954                         errorstream << "Failed to load block " << PP(*it) << ", skipping it." << std::endl;
955                 }
956                 if (++count % 0xFF == 0 && time(NULL) - last_update_time >= 1) {
957                         std::cerr << " Migrated " << count << " blocks, "
958                                 << (100.0 * count / blocks.size()) << "% completed.\r";
959                         new_db->endSave();
960                         new_db->beginSave();
961                         last_update_time = time(NULL);
962                 }
963         }
964         std::cerr << std::endl;
965         new_db->endSave();
966         delete old_db;
967         delete new_db;
968
969         actionstream << "Successfully migrated " << count << " blocks" << std::endl;
970         world_mt.set("backend", migrate_to);
971         if (!world_mt.updateConfigFile(world_mt_path.c_str()))
972                 errorstream << "Failed to update world.mt!" << std::endl;
973         else
974                 actionstream << "world.mt updated" << std::endl;
975
976         return true;
977 }
978