3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
22 #pragma message ("Disabling unit tests")
24 #warning "Disabling unit tests"
27 #define ENABLE_TESTS 0
30 #define ENABLE_TESTS 1
34 #ifndef SERVER // Dedicated server isn't linked with Irrlicht
35 #pragma comment(lib, "Irrlicht.lib")
36 // This would get rid of the console window
37 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
39 #pragma comment(lib, "zlibwapi.lib")
40 #pragma comment(lib, "Shell32.lib")
43 #include "irrlicht.h" // createDevice
46 #include "mainmenumanager.h"
47 #include "irrlichttypes_extrabloated.h"
53 #include "guiMainMenu.h"
55 #include "defaultsettings.h"
59 #include "quicktune.h"
60 #include "httpfetch.h"
61 #include "guiEngine.h"
62 #include "mapsector.h"
63 #include "fontengine.h"
64 #include "gameparams.h"
66 #include "client/clientlauncher.h"
69 #include "database-sqlite3.h"
71 #include "database-leveldb.h"
75 #include "database-redis.h"
78 #ifdef HAVE_TOUCHSCREENGUI
79 #include "touchscreengui.h"
84 These are loaded from the config file.
86 static Settings main_settings;
87 Settings *g_settings = &main_settings;
88 std::string g_settings_path;
91 Profiler main_profiler;
92 Profiler *g_profiler = &main_profiler;
94 // Menu clouds are created later
95 Clouds *g_menuclouds = 0;
96 irr::scene::ISceneManager *g_menucloudsmgr = 0;
103 std::ostream *dout_con_ptr = &dummyout;
104 std::ostream *derr_con_ptr = &verbosestream;
107 std::ostream *dout_server_ptr = &infostream;
108 std::ostream *derr_server_ptr = &errorstream;
111 std::ostream *dout_client_ptr = &infostream;
112 std::ostream *derr_client_ptr = &errorstream;
114 #define DEBUGFILE "debug.txt"
115 #define DEFAULT_SERVER_PORT 30000
117 typedef std::map<std::string, ValueSpec> OptionList;
119 /**********************************************************************
121 **********************************************************************/
123 static bool get_cmdline_opts(int argc, char *argv[], Settings *cmd_args);
124 static void set_allowed_options(OptionList *allowed_options);
126 static void print_help(const OptionList &allowed_options);
127 static void print_allowed_options(const OptionList &allowed_options);
128 static void print_version();
129 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
131 static void print_modified_quicktune_values();
133 static void list_game_ids();
134 static void list_worlds();
135 static void setup_log_params(const Settings &cmd_args);
136 static bool create_userdata_path();
137 static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[]);
138 static void startup_message();
139 static bool read_config_file(const Settings &cmd_args);
140 static void init_debug_streams(int *log_level, const Settings &cmd_args);
142 static bool game_configure(GameParams *game_params, const Settings &cmd_args);
143 static void game_configure_port(GameParams *game_params, const Settings &cmd_args);
145 static bool game_configure_world(GameParams *game_params, const Settings &cmd_args);
146 static bool get_world_from_cmdline(GameParams *game_params, const Settings &cmd_args);
147 static bool get_world_from_config(GameParams *game_params, const Settings &cmd_args);
148 static bool auto_select_world(GameParams *game_params);
149 static std::string get_clean_world_path(const std::string &path);
151 static bool game_configure_subgame(GameParams *game_params, const Settings &cmd_args);
152 static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_args);
153 static bool determine_subgame(GameParams *game_params);
155 static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args);
156 static bool migrate_database(const GameParams &game_params, const Settings &cmd_args,
159 /**********************************************************************/
166 /* mainmenumanager.h */
168 gui::IGUIEnvironment* guienv = NULL;
169 gui::IGUIStaticText *guiroot = NULL;
170 MainMenuManager g_menumgr;
174 return (g_menumgr.menuCount() == 0);
177 // Passed to menus to allow disconnecting and exiting
178 MainGameCallback *g_gamecallback = NULL;
182 gettime.h implementation
189 /* Use imprecise system calls directly (from porting.h) */
190 return porting::getTime(PRECISION_MILLI);
193 u32 getTime(TimePrecision prec)
195 return porting::getTime(prec);
200 class StderrLogOutput: public ILogOutput
203 /* line: Full line with timestamp, level and thread */
204 void printLog(const std::string &line)
206 std::cerr << line << std::endl;
208 } main_stderr_log_out;
210 class DstreamNoStderrLogOutput: public ILogOutput
213 /* line: Full line with timestamp, level and thread */
214 void printLog(const std::string &line)
216 dstream_no_stderr << line << std::endl;
218 } main_dstream_no_stderr_log_out;
220 static OptionList allowed_options;
222 int main(int argc, char *argv[])
226 debug_set_exception_handler();
228 log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
229 log_add_output_all_levs(&main_dstream_no_stderr_log_out);
231 log_register_thread("main");
234 bool cmd_args_ok = get_cmdline_opts(argc, argv, &cmd_args);
236 || cmd_args.getFlag("help")
237 || cmd_args.exists("nonopt1")) {
238 print_help(allowed_options);
239 return cmd_args_ok ? 0 : 1;
242 if (cmd_args.getFlag("version")) {
247 setup_log_params(cmd_args);
249 porting::signal_handler_init();
250 porting::initializePaths();
252 if (!create_userdata_path()) {
253 errorstream << "Cannot create user data directory" << std::endl;
257 // Initialize debug stacks
259 DSTACK(__FUNCTION_NAME);
262 BEGIN_DEBUG_EXCEPTION_HANDLER
264 // List gameids if requested
265 if (cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") {
270 // List worlds if requested
271 if (cmd_args.exists("world") && cmd_args.get("world") == "list") {
276 GameParams game_params;
277 if (!init_common(&game_params.log_level, cmd_args, argc, argv))
282 if ((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
283 || cmd_args.getFlag("enable-unittests") == true) {
289 game_params.is_dedicated_server = true;
291 game_params.is_dedicated_server = cmd_args.getFlag("server");
294 if (!game_configure(&game_params, cmd_args))
297 assert(game_params.world_path != "");
299 infostream << "Using commanded world path ["
300 << game_params.world_path << "]" << std::endl;
302 //Run dedicated server if asked to or no other option
303 g_settings->set("server_dedicated",
304 game_params.is_dedicated_server ? "true" : "false");
306 if (game_params.is_dedicated_server)
307 return run_dedicated_server(game_params, cmd_args) ? 0 : 1;
310 ClientLauncher launcher;
311 retval = launcher.run(game_params, cmd_args) ? 0 : 1;
316 // Update configuration file
317 if (g_settings_path != "")
318 g_settings->updateConfigFile(g_settings_path.c_str());
320 print_modified_quicktune_values();
322 // Stop httpfetch thread (if started)
325 END_DEBUG_EXCEPTION_HANDLER(errorstream)
331 /*****************************************************************************
333 *****************************************************************************/
336 static bool get_cmdline_opts(int argc, char *argv[], Settings *cmd_args)
338 set_allowed_options(&allowed_options);
340 return cmd_args->parseCommandLine(argc, argv, allowed_options);
343 static void set_allowed_options(OptionList *allowed_options)
345 allowed_options->clear();
347 allowed_options->insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
348 _("Show allowed options"))));
349 allowed_options->insert(std::make_pair("version", ValueSpec(VALUETYPE_FLAG,
350 _("Show version information"))));
351 allowed_options->insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
352 _("Load configuration from specified file"))));
353 allowed_options->insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
354 _("Set network port (UDP)"))));
355 allowed_options->insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
356 _("Disable unit tests"))));
357 allowed_options->insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
358 _("Enable unit tests"))));
359 allowed_options->insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
360 _("Same as --world (deprecated)"))));
361 allowed_options->insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
362 _("Set world path (implies local game) ('list' lists all)"))));
363 allowed_options->insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
364 _("Set world by name (implies local game)"))));
365 allowed_options->insert(std::make_pair("quiet", ValueSpec(VALUETYPE_FLAG,
366 _("Print to console errors only"))));
367 allowed_options->insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
368 _("Print more information to console"))));
369 allowed_options->insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG,
370 _("Print even more information to console"))));
371 allowed_options->insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
372 _("Print enormous amounts of information to log and console"))));
373 allowed_options->insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
374 _("Set logfile path ('' = no logging)"))));
375 allowed_options->insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
376 _("Set gameid (\"--gameid list\" prints available ones)"))));
377 allowed_options->insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING,
378 _("Migrate from current map backend to another (Only works when using minetestserver or with --server)"))));
380 allowed_options->insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
381 _("Show available video modes"))));
382 allowed_options->insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
383 _("Run speed tests"))));
384 allowed_options->insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
385 _("Address to connect to. ('' = local game)"))));
386 allowed_options->insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
387 _("Enable random user input, for testing"))));
388 allowed_options->insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
389 _("Run dedicated server"))));
390 allowed_options->insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
391 _("Set player name"))));
392 allowed_options->insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
393 _("Set password"))));
394 allowed_options->insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
395 _("Disable main menu"))));
400 static void print_help(const OptionList &allowed_options)
402 dstream << _("Allowed options:") << std::endl;
403 print_allowed_options(allowed_options);
406 static void print_allowed_options(const OptionList &allowed_options)
408 for (OptionList::const_iterator i = allowed_options.begin();
409 i != allowed_options.end(); ++i) {
410 std::ostringstream os1(std::ios::binary);
411 os1 << " --" << i->first;
412 if (i->second.type != VALUETYPE_FLAG)
413 os1 << _(" <value>");
415 dstream << padStringRight(os1.str(), 24);
417 if (i->second.help != NULL)
418 dstream << i->second.help;
420 dstream << std::endl;
424 static void print_version()
427 dstream << "minetestserver " << minetest_version_hash << std::endl;
429 dstream << "Minetest " << minetest_version_hash << std::endl;
430 dstream << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
432 dstream << "Build info: " << minetest_build_info << std::endl;
435 static void list_game_ids()
437 std::set<std::string> gameids = getAvailableGameIds();
438 for (std::set<std::string>::const_iterator i = gameids.begin();
439 i != gameids.end(); i++)
440 dstream << (*i) <<std::endl;
443 static void list_worlds()
445 dstream << _("Available worlds:") << std::endl;
446 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
447 print_worldspecs(worldspecs, dstream);
450 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
453 for (size_t i = 0; i < worldspecs.size(); i++) {
454 std::string name = worldspecs[i].name;
455 std::string path = worldspecs[i].path;
456 if (name.find(" ") != std::string::npos)
457 name = std::string("'") + name + "'";
458 path = std::string("'") + path + "'";
459 name = padStringRight(name, 14);
460 os << " " << name << " " << path << std::endl;
464 static void print_modified_quicktune_values()
466 bool header_printed = false;
467 std::vector<std::string> names = getQuicktuneNames();
469 for (u32 i = 0; i < names.size(); i++) {
470 QuicktuneValue val = getQuicktuneValue(names[i]);
473 if (!header_printed) {
474 dstream << "Modified quicktune values:" << std::endl;
475 header_printed = true;
477 dstream << names[i] << " = " << val.getString() << std::endl;
481 static void setup_log_params(const Settings &cmd_args)
483 // Quiet mode, print errors only
484 if (cmd_args.getFlag("quiet")) {
485 log_remove_output(&main_stderr_log_out);
486 log_add_output_maxlev(&main_stderr_log_out, LMT_ERROR);
489 // If trace is enabled, enable logging of certain things
490 if (cmd_args.getFlag("trace")) {
491 dstream << _("Enabling trace level debug output") << std::endl;
492 log_trace_level_enabled = true;
493 dout_con_ptr = &verbosestream; // this is somewhat old crap
494 socket_enable_debug_output = true; // socket doesn't use log.h
497 // In certain cases, output info level on stderr
498 if (cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
499 cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
500 log_add_output(&main_stderr_log_out, LMT_INFO);
502 // In certain cases, output verbose level on stderr
503 if (cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
504 log_add_output(&main_stderr_log_out, LMT_VERBOSE);
507 static bool create_userdata_path()
512 porting::initAndroid();
514 porting::setExternalStorageDir(porting::jnienv);
515 if (!fs::PathExists(porting::path_user)) {
516 success = fs::CreateDir(porting::path_user);
520 porting::copyAssets();
522 // Create user data directory
523 success = fs::CreateDir(porting::path_user);
526 infostream << "path_share = " << porting::path_share << std::endl;
527 infostream << "path_user = " << porting::path_user << std::endl;
532 static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[])
535 set_default_settings(g_settings);
537 // Initialize sockets
539 atexit(sockets_cleanup);
541 if (!read_config_file(cmd_args))
544 init_debug_streams(log_level, cmd_args);
546 // Initialize random seed
550 // Initialize HTTP fetcher
551 httpfetch_init(g_settings->getS32("curl_parallel_limit"));
554 init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
555 g_settings->get("language"), argc, argv);
557 init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
558 g_settings->get("language"));
564 static void startup_message()
566 infostream << PROJECT_NAME << " " << _("with")
567 << " SER_FMT_VER_HIGHEST_READ="
568 << (int)SER_FMT_VER_HIGHEST_READ << ", "
569 << minetest_build_info << std::endl;
572 static bool read_config_file(const Settings &cmd_args)
574 // Path of configuration file in use
575 assert(g_settings_path == ""); // Sanity check
577 if (cmd_args.exists("config")) {
578 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
580 errorstream << "Could not read configuration from \""
581 << cmd_args.get("config") << "\"" << std::endl;
584 g_settings_path = cmd_args.get("config");
586 std::vector<std::string> filenames;
587 filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf");
588 // Legacy configuration file location
589 filenames.push_back(porting::path_user +
590 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
593 // Try also from a lower level (to aid having the same configuration
594 // for many RUN_IN_PLACE installs)
595 filenames.push_back(porting::path_user +
596 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
599 for (size_t i = 0; i < filenames.size(); i++) {
600 bool r = g_settings->readConfigFile(filenames[i].c_str());
602 g_settings_path = filenames[i];
607 // If no path found, use the first one (menu creates the file)
608 if (g_settings_path == "")
609 g_settings_path = filenames[0];
615 static void init_debug_streams(int *log_level, const Settings &cmd_args)
618 std::string logfile = DEBUGFILE;
620 std::string logfile = porting::path_user + DIR_DELIM + DEBUGFILE;
622 if (cmd_args.exists("logfile"))
623 logfile = cmd_args.get("logfile");
625 log_remove_output(&main_dstream_no_stderr_log_out);
626 *log_level = g_settings->getS32("debug_log_level");
628 if (*log_level == 0) //no logging
630 if (*log_level < 0) {
631 dstream << "WARNING: Supplied debug_log_level < 0; Using 0" << std::endl;
633 } else if (*log_level > LMT_NUM_VALUES) {
634 dstream << "WARNING: Supplied debug_log_level > " << LMT_NUM_VALUES
635 << "; Using " << LMT_NUM_VALUES << std::endl;
636 *log_level = LMT_NUM_VALUES;
639 log_add_output_maxlev(&main_dstream_no_stderr_log_out,
640 (LogMessageLevel)(*log_level - 1));
642 debugstreams_init(false, logfile == "" ? NULL : logfile.c_str());
644 infostream << "logfile = " << logfile << std::endl;
646 atexit(debugstreams_deinit);
649 static bool game_configure(GameParams *game_params, const Settings &cmd_args)
651 game_configure_port(game_params, cmd_args);
653 if (!game_configure_world(game_params, cmd_args)) {
654 errorstream << "No world path specified or found." << std::endl;
658 game_configure_subgame(game_params, cmd_args);
663 static void game_configure_port(GameParams *game_params, const Settings &cmd_args)
665 if (cmd_args.exists("port"))
666 game_params->socket_port = cmd_args.getU16("port");
668 game_params->socket_port = g_settings->getU16("port");
670 if (game_params->socket_port == 0)
671 game_params->socket_port = DEFAULT_SERVER_PORT;
674 static bool game_configure_world(GameParams *game_params, const Settings &cmd_args)
676 if (get_world_from_cmdline(game_params, cmd_args))
678 if (get_world_from_config(game_params, cmd_args))
681 return auto_select_world(game_params);
684 static bool get_world_from_cmdline(GameParams *game_params, const Settings &cmd_args)
686 std::string commanded_world = "";
689 std::string commanded_worldname = "";
690 if (cmd_args.exists("worldname"))
691 commanded_worldname = cmd_args.get("worldname");
693 // If a world name was specified, convert it to a path
694 if (commanded_worldname != "") {
695 // Get information about available worlds
696 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
698 for (u32 i = 0; i < worldspecs.size(); i++) {
699 std::string name = worldspecs[i].name;
700 if (name == commanded_worldname) {
701 dstream << _("Using world specified by --worldname on the "
702 "command line") << std::endl;
703 commanded_world = worldspecs[i].path;
709 dstream << _("World") << " '" << commanded_worldname
710 << _("' not available. Available worlds:") << std::endl;
711 print_worldspecs(worldspecs, dstream);
715 game_params->world_path = get_clean_world_path(commanded_world);
716 return commanded_world != "";
719 if (cmd_args.exists("world"))
720 commanded_world = cmd_args.get("world");
721 else if (cmd_args.exists("map-dir"))
722 commanded_world = cmd_args.get("map-dir");
723 else if (cmd_args.exists("nonopt0")) // First nameless argument
724 commanded_world = cmd_args.get("nonopt0");
726 game_params->world_path = get_clean_world_path(commanded_world);
727 return commanded_world != "";
730 static bool get_world_from_config(GameParams *game_params, const Settings &cmd_args)
733 std::string commanded_world = "";
735 if (g_settings->exists("map-dir"))
736 commanded_world = g_settings->get("map-dir");
738 game_params->world_path = get_clean_world_path(commanded_world);
740 return commanded_world != "";
743 static bool auto_select_world(GameParams *game_params)
745 // No world was specified; try to select it automatically
746 // Get information about available worlds
748 verbosestream << _("Determining world path") << std::endl;
750 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
751 std::string world_path;
753 // If there is only a single world, use it
754 if (worldspecs.size() == 1) {
755 world_path = worldspecs[0].path;
756 dstream <<_("Automatically selecting world at") << " ["
757 << world_path << "]" << std::endl;
758 // If there are multiple worlds, list them
759 } else if (worldspecs.size() > 1 && game_params->is_dedicated_server) {
760 dstream << _("Multiple worlds are available.") << std::endl;
761 dstream << _("Please select one using --worldname <name>"
762 " or --world <path>") << std::endl;
763 print_worldspecs(worldspecs, dstream);
765 // If there are no worlds, automatically create a new one
767 // This is the ultimate default world path
768 world_path = porting::path_user + DIR_DELIM + "worlds" +
770 infostream << "Creating default world at ["
771 << world_path << "]" << std::endl;
774 assert(world_path != "");
775 game_params->world_path = world_path;
779 static std::string get_clean_world_path(const std::string &path)
781 const std::string worldmt = "world.mt";
782 std::string clean_path;
784 if (path.size() > worldmt.size()
785 && path.substr(path.size() - worldmt.size()) == worldmt) {
786 dstream << _("Supplied world.mt file - stripping it off.") << std::endl;
787 clean_path = path.substr(0, path.size() - worldmt.size());
795 static bool game_configure_subgame(GameParams *game_params, const Settings &cmd_args)
799 success = get_game_from_cmdline(game_params, cmd_args);
801 success = determine_subgame(game_params);
806 static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_args)
808 SubgameSpec commanded_gamespec;
810 if (cmd_args.exists("gameid")) {
811 std::string gameid = cmd_args.get("gameid");
812 commanded_gamespec = findSubgame(gameid);
813 if (!commanded_gamespec.isValid()) {
814 errorstream << "Game \"" << gameid << "\" not found" << std::endl;
817 dstream << _("Using game specified by --gameid on the command line")
819 game_params->game_spec = commanded_gamespec;
826 static bool determine_subgame(GameParams *game_params)
828 SubgameSpec gamespec;
830 assert(game_params->world_path != ""); // pre-condition
832 verbosestream << _("Determining gameid/gamespec") << std::endl;
833 // If world doesn't exist
834 if (game_params->world_path != ""
835 && !getWorldExists(game_params->world_path)) {
836 // Try to take gamespec from command line
837 if (game_params->game_spec.isValid()) {
838 gamespec = game_params->game_spec;
839 infostream << "Using commanded gameid [" << gamespec.id << "]" << std::endl;
840 } else { // Otherwise we will be using "minetest"
841 gamespec = findSubgame(g_settings->get("default_game"));
842 infostream << "Using default gameid [" << gamespec.id << "]" << std::endl;
843 if (!gamespec.isValid()) {
844 errorstream << "Subgame specified in default_game ["
845 << g_settings->get("default_game")
846 << "] is invalid." << std::endl;
850 } else { // World exists
851 std::string world_gameid = getWorldGameId(game_params->world_path, false);
852 // If commanded to use a gameid, do so
853 if (game_params->game_spec.isValid()) {
854 gamespec = game_params->game_spec;
855 if (game_params->game_spec.id != world_gameid) {
856 errorstream << "WARNING: Using commanded gameid ["
857 << gamespec.id << "]" << " instead of world gameid ["
858 << world_gameid << "]" << std::endl;
861 // If world contains an embedded game, use it;
862 // Otherwise find world from local system.
863 gamespec = findWorldSubgame(game_params->world_path);
864 infostream << "Using world gameid [" << gamespec.id << "]" << std::endl;
868 if (!gamespec.isValid()) {
869 errorstream << "Subgame [" << gamespec.id << "] could not be found."
874 game_params->game_spec = gamespec;
879 /*****************************************************************************
881 *****************************************************************************/
882 static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args)
884 DSTACK("Dedicated server branch");
886 verbosestream << _("Using world path") << " ["
887 << game_params.world_path << "]" << std::endl;
888 verbosestream << _("Using gameid") << " ["
889 << game_params.game_spec.id << "]" << std::endl;
892 std::string bind_str = g_settings->get("bind_address");
893 Address bind_addr(0, 0, 0, 0, game_params.socket_port);
895 if (g_settings->getBool("ipv6_server")) {
896 bind_addr.setAddress((IPv6AddressBytes*) NULL);
899 bind_addr.Resolve(bind_str.c_str());
900 } catch (ResolveError &e) {
901 infostream << "Resolving bind address \"" << bind_str
902 << "\" failed: " << e.what()
903 << " -- Listening on all addresses." << std::endl;
905 if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
906 errorstream << "Unable to listen on "
907 << bind_addr.serializeString()
908 << L" because IPv6 is disabled" << std::endl;
913 Server server(game_params.world_path,
914 game_params.game_spec, false, bind_addr.isIPv6());
916 // Database migration
917 if (cmd_args.exists("migrate"))
918 return migrate_database(game_params, cmd_args, &server);
920 server.start(bind_addr);
923 bool &kill = *porting::signal_handler_killstatus();
924 dedicated_server_loop(server, kill);
929 static bool migrate_database(const GameParams &game_params, const Settings &cmd_args,
933 bool success = world_mt.readConfigFile((game_params.world_path
934 + DIR_DELIM + "world.mt").c_str());
936 errorstream << "Cannot read world.mt" << std::endl;
940 if (!world_mt.exists("backend")) {
941 errorstream << "Please specify your current backend in world.mt file:"
942 << std::endl << " backend = {sqlite3|leveldb|redis|dummy}"
947 std::string backend = world_mt.get("backend");
949 std::string migrate_to = cmd_args.get("migrate");
951 if (backend == migrate_to) {
952 errorstream << "Cannot migrate: new backend is same as the old one"
957 if (migrate_to == "sqlite3")
958 new_db = new Database_SQLite3(&(ServerMap&)server->getMap(),
959 game_params.world_path);
961 else if (migrate_to == "leveldb")
962 new_db = new Database_LevelDB(&(ServerMap&)server->getMap(),
963 game_params.world_path);
966 else if (migrate_to == "redis")
967 new_db = new Database_Redis(&(ServerMap&)server->getMap(),
968 game_params.world_path);
971 errorstream << "Migration to " << migrate_to << " is not supported"
976 std::list<v3s16> blocks;
977 ServerMap &old_map = ((ServerMap&)server->getMap());
978 old_map.listAllLoadableBlocks(blocks);
981 for (std::list<v3s16>::iterator i = blocks.begin(); i != blocks.end(); i++) {
982 MapBlock *block = old_map.loadBlock(*i);
984 errorstream << "Failed to load block " << PP(*i) << ", skipping it.";
986 old_map.saveBlock(block, new_db);
987 MapSector *sector = old_map.getSectorNoGenerate(v2s16(i->X, i->Z));
988 sector->deleteBlock(block);
991 if (count % 500 == 0)
992 actionstream << "Migrated " << count << " blocks "
993 << (100.0 * count / blocks.size()) << "% completed" << std::endl;
998 actionstream << "Successfully migrated " << count << " blocks" << std::endl;
999 world_mt.set("backend", migrate_to);
1000 if (!world_mt.updateConfigFile(
1001 (game_params.world_path+ DIR_DELIM + "world.mt").c_str()))
1002 errorstream << "Failed to update world.mt!" << std::endl;
1004 actionstream << "world.mt updated" << std::endl;