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