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