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