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