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