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