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"
50 #include "irrlichttypes_extrabloated.h"
55 #include "constants.h"
58 #include "guiMessageMenu.h"
61 #include "guiMainMenu.h"
66 #include "defaultsettings.h"
73 #include "xCGUITTFont.h"
75 #include "util/string.h"
77 #include "quicktune.h"
78 #include "serverlist.h"
82 These are loaded from the config file.
84 Settings main_settings;
85 Settings *g_settings = &main_settings;
88 Profiler main_profiler;
89 Profiler *g_profiler = &main_profiler;
96 std::ostream *dout_con_ptr = &dummyout;
97 std::ostream *derr_con_ptr = &verbosestream;
100 std::ostream *dout_server_ptr = &infostream;
101 std::ostream *derr_server_ptr = &errorstream;
104 std::ostream *dout_client_ptr = &infostream;
105 std::ostream *derr_client_ptr = &errorstream;
112 /* mainmenumanager.h */
114 gui::IGUIEnvironment* guienv = NULL;
115 gui::IGUIStaticText *guiroot = NULL;
116 MainMenuManager g_menumgr;
120 return (g_menumgr.menuCount() == 0);
123 // Passed to menus to allow disconnecting and exiting
124 MainGameCallback *g_gamecallback = NULL;
128 gettime.h implementation
135 /* Use imprecise system calls directly (from porting.h) */
136 return porting::getTime(PRECISION_MILLI);
139 u32 getTime(TimePrecision prec)
141 return porting::getTime(prec);
146 // A small helper class
150 virtual u32 getTime(TimePrecision prec) = 0;
153 // A precise irrlicht one
154 class IrrlichtTimeGetter: public TimeGetter
157 IrrlichtTimeGetter(IrrlichtDevice *device):
160 u32 getTime(TimePrecision prec)
162 if (prec == PRECISION_MILLI) {
165 return m_device->getTimer()->getRealTime();
167 return porting::getTime(prec);
171 IrrlichtDevice *m_device;
173 // Not so precise one which works without irrlicht
174 class SimpleTimeGetter: public TimeGetter
177 u32 getTime(TimePrecision prec)
179 return porting::getTime(prec);
183 // A pointer to a global instance of the time getter
185 TimeGetter *g_timegetter = NULL;
189 if(g_timegetter == NULL)
191 return g_timegetter->getTime(PRECISION_MILLI);
194 u32 getTime(TimePrecision prec) {
195 if (g_timegetter == NULL)
197 return g_timegetter->getTime(prec);
202 class StderrLogOutput: public ILogOutput
205 /* line: Full line with timestamp, level and thread */
206 void printLog(const std::string &line)
208 std::cerr<<line<<std::endl;
210 } main_stderr_log_out;
212 class DstreamNoStderrLogOutput: public ILogOutput
215 /* line: Full line with timestamp, level and thread */
216 void printLog(const std::string &line)
218 dstream_no_stderr<<line<<std::endl;
220 } main_dstream_no_stderr_log_out;
225 Event handler for Irrlicht
227 NOTE: Everything possible should be moved out from here,
228 probably to InputHandler and the_game
231 class MyEventReceiver : public IEventReceiver
234 // This is the one method that we have to implement
235 virtual bool OnEvent(const SEvent& event)
238 React to nothing here if a menu is active
240 if(noMenuActive() == false)
245 // Remember whether each key is down or up
246 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
248 if(event.KeyInput.PressedDown) {
249 keyIsDown.set(event.KeyInput);
250 keyWasDown.set(event.KeyInput);
252 keyIsDown.unset(event.KeyInput);
256 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
258 if(noMenuActive() == false)
261 middle_active = false;
262 right_active = false;
266 left_active = event.MouseInput.isLeftPressed();
267 middle_active = event.MouseInput.isMiddlePressed();
268 right_active = event.MouseInput.isRightPressed();
270 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
274 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
278 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
282 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
284 rightreleased = true;
286 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
288 mouse_wheel += event.MouseInput.Wheel;
296 bool IsKeyDown(const KeyPress &keyCode) const
298 return keyIsDown[keyCode];
301 // Checks whether a key was down and resets the state
302 bool WasKeyDown(const KeyPress &keyCode)
304 bool b = keyWasDown[keyCode];
306 keyWasDown.unset(keyCode);
323 rightclicked = false;
324 leftreleased = false;
325 rightreleased = false;
328 middle_active = false;
329 right_active = false;
351 IrrlichtDevice *m_device;
353 // The current state of keys
355 // Whether a key has been pressed or not
360 Separated input handler
363 class RealInputHandler : public InputHandler
366 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
371 virtual bool isKeyDown(const KeyPress &keyCode)
373 return m_receiver->IsKeyDown(keyCode);
375 virtual bool wasKeyDown(const KeyPress &keyCode)
377 return m_receiver->WasKeyDown(keyCode);
379 virtual v2s32 getMousePos()
381 return m_device->getCursorControl()->getPosition();
383 virtual void setMousePos(s32 x, s32 y)
385 m_device->getCursorControl()->setPosition(x, y);
388 virtual bool getLeftState()
390 return m_receiver->left_active;
392 virtual bool getRightState()
394 return m_receiver->right_active;
397 virtual bool getLeftClicked()
399 return m_receiver->leftclicked;
401 virtual bool getRightClicked()
403 return m_receiver->rightclicked;
405 virtual void resetLeftClicked()
407 m_receiver->leftclicked = false;
409 virtual void resetRightClicked()
411 m_receiver->rightclicked = false;
414 virtual bool getLeftReleased()
416 return m_receiver->leftreleased;
418 virtual bool getRightReleased()
420 return m_receiver->rightreleased;
422 virtual void resetLeftReleased()
424 m_receiver->leftreleased = false;
426 virtual void resetRightReleased()
428 m_receiver->rightreleased = false;
431 virtual s32 getMouseWheel()
433 return m_receiver->getMouseWheel();
438 m_receiver->clearInput();
441 IrrlichtDevice *m_device;
442 MyEventReceiver *m_receiver;
445 class RandomInputHandler : public InputHandler
453 rightclicked = false;
454 leftreleased = false;
455 rightreleased = false;
458 virtual bool isKeyDown(const KeyPress &keyCode)
460 return keydown[keyCode];
462 virtual bool wasKeyDown(const KeyPress &keyCode)
466 virtual v2s32 getMousePos()
470 virtual void setMousePos(s32 x, s32 y)
472 mousepos = v2s32(x,y);
475 virtual bool getLeftState()
479 virtual bool getRightState()
484 virtual bool getLeftClicked()
488 virtual bool getRightClicked()
492 virtual void resetLeftClicked()
496 virtual void resetRightClicked()
498 rightclicked = false;
501 virtual bool getLeftReleased()
505 virtual bool getRightReleased()
507 return rightreleased;
509 virtual void resetLeftReleased()
511 leftreleased = false;
513 virtual void resetRightReleased()
515 rightreleased = false;
518 virtual s32 getMouseWheel()
523 virtual void step(float dtime)
526 static float counter1 = 0;
530 counter1 = 0.1*Rand(1, 40);
531 keydown.toggle(getKeySetting("keymap_jump"));
535 static float counter1 = 0;
539 counter1 = 0.1*Rand(1, 40);
540 keydown.toggle(getKeySetting("keymap_special1"));
544 static float counter1 = 0;
548 counter1 = 0.1*Rand(1, 40);
549 keydown.toggle(getKeySetting("keymap_forward"));
553 static float counter1 = 0;
557 counter1 = 0.1*Rand(1, 40);
558 keydown.toggle(getKeySetting("keymap_left"));
562 static float counter1 = 0;
566 counter1 = 0.1*Rand(1, 20);
567 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
571 static float counter1 = 0;
575 counter1 = 0.1*Rand(1, 30);
576 leftdown = !leftdown;
584 static float counter1 = 0;
588 counter1 = 0.1*Rand(1, 15);
589 rightdown = !rightdown;
593 rightreleased = true;
596 mousepos += mousespeed;
599 s32 Rand(s32 min, s32 max)
601 return (myrand()%(max-min+1))+min;
615 //Draw the tiled menu background
616 void drawMenuBackground(video::IVideoDriver* driver) {
617 core::dimension2d<u32> screensize = driver->getScreenSize();
619 std::string path = getTexturePath("menubg.png");
621 static const video::ITexture *bgtexture =
622 driver->getTexture(path.c_str());
625 s32 scaledsize = 128;
627 // The important difference between destsize and screensize is
628 // that destsize is rounded to whole scaled pixels.
629 // These formulas use component-wise multiplication and division of v2u32.
630 v2u32 texturesize = bgtexture->getSize();
631 v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
632 v2u32 destsize = scaledsize * sourcesize / texturesize;
634 // Default texture wrapping mode in Irrlicht is ETC_REPEAT.
635 driver->draw2DImage(bgtexture,
636 core::rect<s32>(0, 0, destsize.X, destsize.Y),
637 core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
643 //Draw the footer at the bottom of the window
644 void drawMenuFooter(video::IVideoDriver* driver, bool clouds) {
645 core::dimension2d<u32> screensize = driver->getScreenSize();
646 std::string path = getTexturePath(clouds ?
647 "menufooter_clouds.png" : "menufooter.png");
649 static const video::ITexture *footertexture =
650 driver->getTexture(path.c_str());
653 f32 mult = (((f32)screensize.Width)) /
654 ((f32)footertexture->getOriginalSize().Width);
656 v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult,
657 ((f32)footertexture->getOriginalSize().Height) * mult);
659 // Don't draw the footer if there isn't enough room
660 s32 free_space = (((s32)screensize.Height)-320)/2;
661 if (free_space > footersize.Y) {
662 core::rect<s32> rect(0,0,footersize.X,footersize.Y);
663 rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
664 rect -= v2s32(footersize.X/2, 0);
666 driver->draw2DImage(footertexture, rect,
667 core::rect<s32>(core::position2d<s32>(0,0),
668 core::dimension2di(footertexture->getSize())),
675 // Draw the Header over the main menu
676 void drawMenuHeader(video::IVideoDriver* driver) {
677 core::dimension2d<u32> screensize = driver->getScreenSize();
679 std::string path = getTexturePath("menuheader.png");
681 static const video::ITexture *splashtexture =
682 driver->getTexture(path.c_str());
685 //v2s32 splashsize((splashtexture->getOriginalSize().Width*100)/
686 // splashtexture->getOriginalSize().Height, 80);
688 f32 mult = (((f32)screensize.Width / 2)) /
689 ((f32)splashtexture->getOriginalSize().Width);
691 v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult,
692 ((f32)splashtexture->getOriginalSize().Height) * mult);
694 // Don't draw the header is there isn't enough room
695 s32 free_space = (((s32)screensize.Height)-320)/2;
696 if (free_space > splashsize.Y) {
697 core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
698 splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
699 ((free_space/2)-splashsize.Y/2)+10);
701 video::SColor bgcolor(255,50,50,50);
703 driver->draw2DImage(splashtexture, splashrect,
704 core::rect<s32>(core::position2d<s32>(0,0),
705 core::dimension2di(splashtexture->getSize())),
712 // Draw the Splash over the clouds and under the main menu
713 void drawMenuSplash(video::IVideoDriver* driver) {
714 core::dimension2d<u32> screensize = driver->getScreenSize();
715 if (getTexturePath("menusplash.png") != "") {
716 static const video::ITexture *splashtexture =
717 driver->getTexture(getTexturePath("menusplash.png").c_str());
720 core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height);
722 video::SColor bgcolor(255,50,50,50);
724 driver->draw2DImage(splashtexture, splashrect,
725 core::rect<s32>(core::position2d<s32>(0,0),
726 core::dimension2di(splashtexture->getSize())),
734 // These are defined global so that they're not optimized too much.
735 // Can't change them to volatile.
740 std::string tempstring;
741 std::string tempstring2;
746 infostream<<"The following test should take around 20ms."<<std::endl;
747 TimeTaker timer("Testing std::string speed");
748 const u32 jj = 10000;
749 for(u32 j=0; j<jj; j++)
754 for(u32 i=0; i<ii; i++){
755 tempstring2 += "asd";
757 for(u32 i=0; i<ii+1; i++){
759 if(tempstring == tempstring2)
765 infostream<<"All of the following tests should take around 100ms each."
769 TimeTaker timer("Testing floating-point conversion speed");
771 for(u32 i=0; i<4000000; i++){
778 TimeTaker timer("Testing floating-point vector speed");
780 tempv3f1 = v3f(1,2,3);
781 tempv3f2 = v3f(4,5,6);
782 for(u32 i=0; i<10000000; i++){
783 tempf += tempv3f1.dotProduct(tempv3f2);
784 tempv3f2 += v3f(7,8,9);
789 TimeTaker timer("Testing std::map speed");
791 std::map<v2s16, f32> map1;
794 for(s16 y=0; y<ii; y++){
795 for(s16 x=0; x<ii; x++){
796 map1[v2s16(x,y)] = tempf;
800 for(s16 y=ii-1; y>=0; y--){
801 for(s16 x=0; x<ii; x++){
802 tempf = map1[v2s16(x,y)];
808 infostream<<"Around 5000/ms should do well here."<<std::endl;
809 TimeTaker timer("Testing mutex speed");
823 while(timer.getTimerTime() < 10);
825 u32 dtime = timer.stop();
826 u32 per_ms = n / dtime;
827 infostream<<"Done. "<<dtime<<"ms, "
828 <<per_ms<<"/ms"<<std::endl;
832 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
835 for(u32 i=0; i<worldspecs.size(); i++){
836 std::string name = worldspecs[i].name;
837 std::string path = worldspecs[i].path;
838 if(name.find(" ") != std::string::npos)
839 name = std::string("'") + name + "'";
840 path = std::string("'") + path + "'";
841 name = padStringRight(name, 14);
842 os<<" "<<name<<" "<<path<<std::endl;
846 int main(int argc, char *argv[])
854 log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
855 log_add_output_all_levs(&main_dstream_no_stderr_log_out);
857 log_register_thread("main");
859 // This enables internatonal characters input
860 if( setlocale(LC_ALL, "") == NULL )
862 fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] );
865 // Set locale. This is for forcing '.' as the decimal point.
867 std::locale::global(std::locale(std::locale(""), "C", std::locale::numeric));
868 setlocale(LC_NUMERIC, "C");
869 } catch (const std::exception& ex) {
870 errorstream<<"Could not set numeric locale to C"<<std::endl;
876 // List all allowed options
877 std::map<std::string, ValueSpec> allowed_options;
878 allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
879 _("Show allowed options"))));
880 allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
881 _("Load configuration from specified file"))));
882 allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
883 _("Set network port (UDP)"))));
884 allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
885 _("Disable unit tests"))));
886 allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
887 _("Enable unit tests"))));
888 allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
889 _("Same as --world (deprecated)"))));
890 allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
891 _("Set world path (implies local game) ('list' lists all)"))));
892 allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
893 _("Set world by name (implies local game)"))));
894 allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
895 _("Print more information to console"))));
896 allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG,
897 _("Print even more information to console"))));
898 allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
899 _("Print enormous amounts of information to log and console"))));
900 allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
901 _("Set logfile path ('' = no logging)"))));
902 allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
903 _("Set gameid (\"--gameid list\" prints available ones)"))));
905 allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
906 _("Run speed tests"))));
907 allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
908 _("Address to connect to. ('' = local game)"))));
909 allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
910 _("Enable random user input, for testing"))));
911 allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
912 _("Run dedicated server"))));
913 allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
914 _("Set player name"))));
915 allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
916 _("Set password"))));
917 allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
918 _("Disable main menu"))));
923 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
925 if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
927 dstream<<_("Allowed options:")<<std::endl;
928 for(std::map<std::string, ValueSpec>::iterator
929 i = allowed_options.begin();
930 i != allowed_options.end(); ++i)
932 std::ostringstream os1(std::ios::binary);
933 os1<<" --"<<i->first;
934 if(i->second.type == VALUETYPE_FLAG)
938 dstream<<padStringRight(os1.str(), 24);
940 if(i->second.help != NULL)
941 dstream<<i->second.help;
945 return cmd_args.getFlag("help") ? 0 : 1;
949 Low-level initialization
952 // If trace is enabled, enable logging of certain things
953 if(cmd_args.getFlag("trace")){
954 dstream<<_("Enabling trace level debug output")<<std::endl;
955 log_trace_level_enabled = true;
956 dout_con_ptr = &verbosestream; // this is somewhat old crap
957 socket_enable_debug_output = true; // socket doesn't use log.h
959 // In certain cases, output info level on stderr
960 if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
961 cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
962 log_add_output(&main_stderr_log_out, LMT_INFO);
963 // In certain cases, output verbose level on stderr
964 if(cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
965 log_add_output(&main_stderr_log_out, LMT_VERBOSE);
967 porting::signal_handler_init();
968 bool &kill = *porting::signal_handler_killstatus();
970 porting::initializePaths();
972 // Create user data directory
973 fs::CreateDir(porting::path_user);
975 init_gettext((porting::path_share + DIR_DELIM + "locale").c_str());
977 infostream<<"path_share = "<<porting::path_share<<std::endl;
978 infostream<<"path_user = "<<porting::path_user<<std::endl;
980 // Initialize debug stacks
982 DSTACK(__FUNCTION_NAME);
985 BEGIN_DEBUG_EXCEPTION_HANDLER
987 // List gameids if requested
988 if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
990 std::set<std::string> gameids = getAvailableGameIds();
991 for(std::set<std::string>::const_iterator i = gameids.begin();
992 i != gameids.end(); i++)
993 dstream<<(*i)<<std::endl;
997 // List worlds if requested
998 if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
999 dstream<<_("Available worlds:")<<std::endl;
1000 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1001 print_worldspecs(worldspecs, dstream);
1005 // Print startup message
1006 infostream<<PROJECT_NAME<<
1007 " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
1012 Basic initialization
1015 // Initialize default settings
1016 set_default_settings(g_settings);
1018 // Initialize sockets
1020 atexit(sockets_cleanup);
1026 // Path of configuration file in use
1027 std::string configpath = "";
1029 if(cmd_args.exists("config"))
1031 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
1034 errorstream<<"Could not read configuration from \""
1035 <<cmd_args.get("config")<<"\""<<std::endl;
1038 configpath = cmd_args.get("config");
1042 std::vector<std::string> filenames;
1043 filenames.push_back(porting::path_user +
1044 DIR_DELIM + "minetest.conf");
1045 // Legacy configuration file location
1046 filenames.push_back(porting::path_user +
1047 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1049 // Try also from a lower level (to aid having the same configuration
1050 // for many RUN_IN_PLACE installs)
1051 filenames.push_back(porting::path_user +
1052 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1055 for(u32 i=0; i<filenames.size(); i++)
1057 bool r = g_settings->readConfigFile(filenames[i].c_str());
1060 configpath = filenames[i];
1065 // If no path found, use the first one (menu creates the file)
1066 if(configpath == "")
1067 configpath = filenames[0];
1070 // Initialize debug streams
1071 #define DEBUGFILE "debug.txt"
1073 std::string logfile = DEBUGFILE;
1075 std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
1077 if(cmd_args.exists("logfile"))
1078 logfile = cmd_args.get("logfile");
1080 log_remove_output(&main_dstream_no_stderr_log_out);
1081 int loglevel = g_settings->getS32("debug_log_level");
1083 if (loglevel == 0) //no logging
1085 else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
1086 log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1));
1089 debugstreams_init(false, logfile.c_str());
1091 debugstreams_init(false, NULL);
1093 infostream<<"logfile = "<<logfile<<std::endl;
1095 // Initialize random seed
1103 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1104 || cmd_args.getFlag("enable-unittests") == true)
1115 if(cmd_args.exists("port"))
1116 port = cmd_args.getU16("port");
1117 else if(g_settings->exists("port"))
1118 port = g_settings->getU16("port");
1123 std::string commanded_world = "";
1124 if(cmd_args.exists("world"))
1125 commanded_world = cmd_args.get("world");
1126 else if(cmd_args.exists("map-dir"))
1127 commanded_world = cmd_args.get("map-dir");
1128 else if(cmd_args.exists("nonopt0")) // First nameless argument
1129 commanded_world = cmd_args.get("nonopt0");
1130 else if(g_settings->exists("map-dir"))
1131 commanded_world = g_settings->get("map-dir");
1134 std::string commanded_worldname = "";
1135 if(cmd_args.exists("worldname"))
1136 commanded_worldname = cmd_args.get("worldname");
1138 // Strip world.mt from commanded_world
1140 std::string worldmt = "world.mt";
1141 if(commanded_world.size() > worldmt.size() &&
1142 commanded_world.substr(commanded_world.size()-worldmt.size())
1144 dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl;
1145 commanded_world = commanded_world.substr(
1146 0, commanded_world.size()-worldmt.size());
1150 // If a world name was specified, convert it to a path
1151 if(commanded_worldname != ""){
1152 // Get information about available worlds
1153 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1155 for(u32 i=0; i<worldspecs.size(); i++){
1156 std::string name = worldspecs[i].name;
1157 if(name == commanded_worldname){
1158 if(commanded_world != ""){
1159 dstream<<_("--worldname takes precedence over previously "
1160 "selected world.")<<std::endl;
1162 commanded_world = worldspecs[i].path;
1168 dstream<<_("World")<<" '"<<commanded_worldname<<_("' not "
1169 "available. Available worlds:")<<std::endl;
1170 print_worldspecs(worldspecs, dstream);
1176 SubgameSpec commanded_gamespec;
1177 if(cmd_args.exists("gameid")){
1178 std::string gameid = cmd_args.get("gameid");
1179 commanded_gamespec = findSubgame(gameid);
1180 if(!commanded_gamespec.isValid()){
1181 errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl;
1187 Run dedicated server if asked to or no other option
1190 bool run_dedicated_server = true;
1192 bool run_dedicated_server = cmd_args.getFlag("server");
1194 g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
1195 if(run_dedicated_server)
1197 DSTACK("Dedicated server branch");
1198 // Create time getter if built with Irrlicht
1200 g_timegetter = new SimpleTimeGetter();
1204 std::string world_path;
1205 verbosestream<<_("Determining world path")<<std::endl;
1206 bool is_legacy_world = false;
1207 // If a world was commanded, use it
1208 if(commanded_world != ""){
1209 world_path = commanded_world;
1210 infostream<<"Using commanded world path ["<<world_path<<"]"
1213 // No world was specified; try to select it automatically
1216 // Get information about available worlds
1217 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1218 // If a world name was specified, select it
1219 if(commanded_worldname != ""){
1221 for(u32 i=0; i<worldspecs.size(); i++){
1222 std::string name = worldspecs[i].name;
1223 if(name == commanded_worldname){
1224 world_path = worldspecs[i].path;
1228 if(world_path == ""){
1229 dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not "
1230 "available. Available worlds:")<<std::endl;
1231 print_worldspecs(worldspecs, dstream);
1235 // If there is only a single world, use it
1236 if(worldspecs.size() == 1){
1237 world_path = worldspecs[0].path;
1238 dstream<<_("Automatically selecting world at")<<" ["
1239 <<world_path<<"]"<<std::endl;
1240 // If there are multiple worlds, list them
1241 } else if(worldspecs.size() > 1){
1242 dstream<<_("Multiple worlds are available.")<<std::endl;
1243 dstream<<_("Please select one using --worldname <name>"
1244 " or --world <path>")<<std::endl;
1245 print_worldspecs(worldspecs, dstream);
1247 // If there are no worlds, automatically create a new one
1249 // This is the ultimate default world path
1250 world_path = porting::path_user + DIR_DELIM + "worlds" +
1251 DIR_DELIM + "world";
1252 infostream<<"Creating default world at ["
1253 <<world_path<<"]"<<std::endl;
1257 if(world_path == ""){
1258 errorstream<<"No world path specified or found."<<std::endl;
1261 verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl;
1263 // We need a gamespec.
1264 SubgameSpec gamespec;
1265 verbosestream<<_("Determining gameid/gamespec")<<std::endl;
1266 // If world doesn't exist
1267 if(!getWorldExists(world_path))
1269 // Try to take gamespec from command line
1270 if(commanded_gamespec.isValid()){
1271 gamespec = commanded_gamespec;
1272 infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl;
1274 // Otherwise we will be using "minetest"
1276 gamespec = findSubgame(g_settings->get("default_game"));
1277 infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl;
1283 std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
1284 // If commanded to use a gameid, do so
1285 if(commanded_gamespec.isValid()){
1286 gamespec = commanded_gamespec;
1287 if(commanded_gamespec.id != world_gameid){
1288 errorstream<<"WARNING: Using commanded gameid ["
1289 <<gamespec.id<<"]"<<" instead of world gameid ["
1290 <<world_gameid<<"]"<<std::endl;
1293 // If world contains an embedded game, use it;
1294 // Otherwise find world from local system.
1295 gamespec = findWorldSubgame(world_path);
1296 infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl;
1299 if(!gamespec.isValid()){
1300 errorstream<<"Subgame ["<<gamespec.id<<"] could not be found."
1304 verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl;
1307 Server server(world_path, configpath, gamespec, false);
1311 dedicated_server_loop(server, kill);
1316 #ifndef SERVER // Exclude from dedicated server build
1322 std::string address = g_settings->get("address");
1323 if(commanded_world != "")
1325 else if(cmd_args.exists("address"))
1326 address = cmd_args.get("address");
1328 std::string playername = g_settings->get("name");
1329 if(cmd_args.exists("name"))
1330 playername = cmd_args.get("name");
1332 bool skip_main_menu = cmd_args.getFlag("go");
1335 Device initialization
1338 // Resolution selection
1340 bool fullscreen = g_settings->getBool("fullscreen");
1341 u16 screenW = g_settings->getU16("screenW");
1342 u16 screenH = g_settings->getU16("screenH");
1346 bool vsync = g_settings->getBool("vsync");
1347 u16 bits = g_settings->getU16("fullscreen_bpp");
1348 u16 fsaa = g_settings->getU16("fsaa");
1352 video::E_DRIVER_TYPE driverType;
1354 std::string driverstring = g_settings->get("video_driver");
1356 if(driverstring == "null")
1357 driverType = video::EDT_NULL;
1358 else if(driverstring == "software")
1359 driverType = video::EDT_SOFTWARE;
1360 else if(driverstring == "burningsvideo")
1361 driverType = video::EDT_BURNINGSVIDEO;
1362 else if(driverstring == "direct3d8")
1363 driverType = video::EDT_DIRECT3D8;
1364 else if(driverstring == "direct3d9")
1365 driverType = video::EDT_DIRECT3D9;
1366 else if(driverstring == "opengl")
1367 driverType = video::EDT_OPENGL;
1368 #ifdef _IRR_COMPILE_WITH_OGLES1_
1369 else if(driverstring == "ogles1")
1370 driverType = video::EDT_OGLES1;
1372 #ifdef _IRR_COMPILE_WITH_OGLES2_
1373 else if(driverstring == "ogles2")
1374 driverType = video::EDT_OGLES2;
1378 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1379 "to opengl"<<std::endl;
1380 driverType = video::EDT_OPENGL;
1384 Create device and exit if creation failed
1387 MyEventReceiver receiver;
1389 IrrlichtDevice *device;
1391 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1392 params.DriverType = driverType;
1393 params.WindowSize = core::dimension2d<u32>(screenW, screenH);
1395 params.AntiAlias = fsaa;
1396 params.Fullscreen = fullscreen;
1397 params.Stencilbuffer = false;
1398 params.Vsync = vsync;
1399 params.EventReceiver = &receiver;
1401 device = createDeviceEx(params);
1404 return 1; // could not create selected driver.
1407 Continue initialization
1410 video::IVideoDriver* driver = device->getVideoDriver();
1413 This changes the minimum allowed number of vertices in a VBO.
1416 //driver->setMinHardwareBufferVertexCount(50);
1418 // Create time getter
1419 g_timegetter = new IrrlichtTimeGetter(device);
1421 // Create game callback for menus
1422 g_gamecallback = new MainGameCallback(device);
1425 Speed tests (done after irrlicht is loaded to get timer)
1427 if(cmd_args.getFlag("speedtests"))
1429 dstream<<"Running speed tests"<<std::endl;
1434 device->setResizable(true);
1436 bool random_input = g_settings->getBool("random_input")
1437 || cmd_args.getFlag("random-input");
1438 InputHandler *input = NULL;
1440 input = new RandomInputHandler();
1442 input = new RealInputHandler(device, &receiver);
1444 scene::ISceneManager* smgr = device->getSceneManager();
1446 guienv = device->getGUIEnvironment();
1447 gui::IGUISkin* skin = guienv->getSkin();
1449 std::string font_path = g_settings->get("font_path");
1450 u16 font_size = g_settings->getU16("font_size");
1451 gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size);
1453 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1456 skin->setFont(font);
1458 errorstream<<"WARNING: Font file was not found."
1459 " Using default font."<<std::endl;
1460 // If font was not found, this will get us one
1461 font = skin->getFont();
1464 u32 text_height = font->getDimension(L"Hello, world!").Height;
1465 infostream<<"text_height="<<text_height<<std::endl;
1467 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1468 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1469 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1470 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1471 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1472 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1473 skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1474 skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1476 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
1477 // Irrlicht 1.8 input colours
1478 skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128));
1479 skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
1486 ChatBackend chat_backend;
1489 If an error occurs, this is set to something and the
1490 menu-game loop is restarted. It is then displayed before
1493 std::wstring error_message = L"";
1495 // The password entered during the menu screen,
1496 std::string password;
1498 bool first_loop = true;
1503 while(device->run() && kill == false)
1505 // Set the window caption
1506 device->setWindowCaption((std::wstring(L"Minetest [")+wgettext("Main Menu")+L"]").c_str());
1508 // This is used for catching disconnects
1513 Clear everything from the GUIEnvironment
1518 We need some kind of a root node to be able to add
1519 custom gui elements directly on the screen.
1520 Otherwise they won't be automatically drawn.
1522 guiroot = guienv->addStaticText(L"",
1523 core::rect<s32>(0, 0, 10000, 10000));
1525 SubgameSpec gamespec;
1526 WorldSpec worldspec;
1527 bool simple_singleplayer_mode = false;
1529 // These are set up based on the menu and other things
1530 std::string current_playername = "inv£lid";
1531 std::string current_password = "";
1532 std::string current_address = "does-not-exist";
1533 int current_port = 0;
1536 Out-of-game menu loop.
1538 Loop quits when menu returns proper parameters.
1540 while(kill == false)
1542 // If skip_main_menu, only go through here once
1543 if(skip_main_menu && !first_loop){
1549 // Cursor can be non-visible when coming from the game
1550 device->getCursorControl()->setVisible(true);
1551 // Some stuff are left to scene manager when coming from the game
1555 // Initialize menu data
1556 MainMenuData menudata;
1557 if(g_settings->exists("selected_mainmenu_tab"))
1558 menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
1559 if(g_settings->exists("selected_serverlist"))
1560 menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
1561 menudata.address = narrow_to_wide(address);
1562 menudata.name = narrow_to_wide(playername);
1563 menudata.port = narrow_to_wide(itos(port));
1564 if(cmd_args.exists("password"))
1565 menudata.password = narrow_to_wide(cmd_args.get("password"));
1566 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1567 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1568 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1569 menudata.opaque_water = g_settings->getBool("opaque_water");
1570 menudata.mip_map = g_settings->getBool("mip_map");
1571 menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
1572 menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
1573 menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
1574 menudata.enable_shaders = g_settings->getS32("enable_shaders");
1575 menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
1576 menudata.enable_particles = g_settings->getBool("enable_particles");
1577 menudata.liquid_finite = g_settings->getBool("liquid_finite");
1578 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
1579 menudata.creative_mode = g_settings->getBool("creative_mode");
1580 menudata.enable_damage = g_settings->getBool("enable_damage");
1581 menudata.enable_public = g_settings->getBool("server_announce");
1582 // Default to selecting nothing
1583 menudata.selected_world = -1;
1584 // Get world listing for the menu
1585 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1586 // If there is only one world, select it
1587 if(worldspecs.size() == 1){
1588 menudata.selected_world = 0;
1590 // Otherwise try to select according to selected_world_path
1591 else if(g_settings->exists("selected_world_path")){
1592 std::string trypath = g_settings->get("selected_world_path");
1593 for(u32 i=0; i<worldspecs.size(); i++){
1594 if(worldspecs[i].path == trypath){
1595 menudata.selected_world = i;
1600 // If a world was commanded, append and select it
1601 if(commanded_world != ""){
1602 std::string gameid = getWorldGameId(commanded_world, true);
1603 std::string name = _("[--world parameter]");
1605 gameid = g_settings->get("default_game");
1608 WorldSpec spec(commanded_world, name, gameid);
1609 worldspecs.push_back(spec);
1610 menudata.selected_world = worldspecs.size()-1;
1612 // Copy worldspecs to menu
1613 menudata.worlds = worldspecs;
1615 if(skip_main_menu == false)
1617 video::IVideoDriver* driver = device->getVideoDriver();
1618 float fps_max = g_settings->getFloat("fps_max");
1619 infostream<<"Waiting for other menus"<<std::endl;
1620 while(device->run() && kill == false)
1624 driver->beginScene(true, true,
1625 video::SColor(255,128,128,128));
1626 drawMenuBackground(driver);
1629 // On some computers framerate doesn't seem to be
1630 // automatically limited
1633 infostream<<"Waited for other menus"<<std::endl;
1636 new GUIMainMenu(guienv, guiroot, -1,
1637 &g_menumgr, &menudata, g_gamecallback);
1638 menu->allowFocusRemoval(true);
1640 // Clouds for the main menu
1641 bool cloud_menu_background = false;
1642 Clouds *clouds = NULL;
1643 if (g_settings->getBool("menu_clouds")) {
1644 cloud_menu_background = true;
1645 clouds = new Clouds(smgr->getRootSceneNode(),
1646 smgr, -1, rand(), 100);
1647 clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
1649 // A camera to see the clouds
1650 scene::ICameraSceneNode* camera;
1651 camera = smgr->addCameraSceneNode(0,
1652 v3f(0,0,0), v3f(0, 60, 100));
1653 camera->setFarValue(10000);
1656 if(error_message != L"")
1658 verbosestream<<"error_message = "
1659 <<wide_to_narrow(error_message)<<std::endl;
1661 GUIMessageMenu *menu2 =
1662 new GUIMessageMenu(guienv, guiroot, -1,
1663 &g_menumgr, error_message.c_str());
1665 error_message = L"";
1668 // Time is in milliseconds, for clouds
1669 u32 lasttime = device->getTimer()->getTime();
1671 infostream<<"Created main menu"<<std::endl;
1673 while(device->run() && kill == false)
1675 if(menu->getStatus() == true)
1678 // Time calc for the clouds
1679 f32 dtime; // in seconds
1680 if (cloud_menu_background) {
1681 u32 time = device->getTimer()->getTime();
1683 dtime = (time - lasttime) / 1000.0;
1689 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1690 driver->beginScene(true, true, video::SColor(255,140,186,250));
1692 if (cloud_menu_background) {
1693 // *3 otherwise the clouds would move very slowly
1694 clouds->step(dtime*3);
1697 drawMenuSplash(driver);
1698 drawMenuFooter(driver, true);
1699 drawMenuHeader(driver);
1701 drawMenuBackground(driver);
1702 drawMenuFooter(driver, false);
1709 // On some computers framerate doesn't seem to be
1710 // automatically limited
1711 if (cloud_menu_background) {
1712 // Time of frame without fps limit
1715 // not using getRealTime is necessary for wine
1716 u32 time = device->getTimer()->getTime();
1718 busytime_u32 = time - lasttime;
1721 busytime = busytime_u32 / 1000.0;
1724 u32 frametime_min = 1000./fps_max;
1726 if(busytime_u32 < frametime_min) {
1727 u32 sleeptime = frametime_min - busytime_u32;
1728 device->sleep(sleeptime);
1735 infostream<<"Dropping main menu"<<std::endl;
1738 if (cloud_menu_background) {
1744 playername = wide_to_narrow(menudata.name);
1745 if (playername == "")
1746 playername = std::string("Guest") + itos(myrand_range(1000,9999));
1747 password = translatePassword(playername, menudata.password);
1748 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1750 address = wide_to_narrow(menudata.address);
1751 int newport = stoi(wide_to_narrow(menudata.port));
1754 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1756 g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
1757 g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
1758 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1759 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1760 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1761 g_settings->set("opaque_water", itos(menudata.opaque_water));
1763 g_settings->set("mip_map", itos(menudata.mip_map));
1764 g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
1765 g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
1766 g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
1768 g_settings->setS32("enable_shaders", menudata.enable_shaders);
1769 g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
1770 g_settings->set("enable_particles", itos(menudata.enable_particles));
1771 g_settings->set("liquid_finite", itos(menudata.liquid_finite));
1773 g_settings->set("creative_mode", itos(menudata.creative_mode));
1774 g_settings->set("enable_damage", itos(menudata.enable_damage));
1775 g_settings->set("server_announce", itos(menudata.enable_public));
1776 g_settings->set("name", playername);
1777 g_settings->set("address", address);
1778 g_settings->set("port", itos(port));
1779 if(menudata.selected_world != -1)
1780 g_settings->set("selected_world_path",
1781 worldspecs[menudata.selected_world].path);
1783 // Break out of menu-game loop to shut down cleanly
1784 if(device->run() == false || kill == true)
1787 current_playername = playername;
1788 current_password = password;
1789 current_address = address;
1790 current_port = port;
1792 // If using simple singleplayer mode, override
1793 if(simple_singleplayer_mode){
1794 current_playername = "singleplayer";
1795 current_password = "";
1796 current_address = "";
1797 current_port = 30011;
1799 else if (address != "")
1801 ServerListSpec server;
1802 server["name"] = menudata.servername;
1803 server["address"] = wide_to_narrow(menudata.address);
1804 server["port"] = wide_to_narrow(menudata.port);
1805 server["description"] = menudata.serverdescription;
1806 ServerList::insert(server);
1809 // Set world path to selected one
1810 if(menudata.selected_world != -1){
1811 worldspec = worldspecs[menudata.selected_world];
1812 infostream<<"Selected world: "<<worldspec.name
1813 <<" ["<<worldspec.path<<"]"<<std::endl;
1816 // Only refresh if so requested
1817 if(menudata.only_refresh){
1818 infostream<<"Refreshing menu"<<std::endl;
1822 // Create new world if requested
1823 if(menudata.create_world_name != L"")
1825 std::string path = porting::path_user + DIR_DELIM
1826 "worlds" + DIR_DELIM
1827 + wide_to_narrow(menudata.create_world_name);
1828 // Create world if it doesn't exist
1829 if(!initializeWorld(path, menudata.create_world_gameid)){
1830 error_message = wgettext("Failed to initialize world");
1831 errorstream<<wide_to_narrow(error_message)<<std::endl;
1834 g_settings->set("selected_world_path", path);
1839 if(current_address == "")
1841 if(menudata.selected_world == -1){
1842 error_message = wgettext("No world selected and no address "
1843 "provided. Nothing to do.");
1844 errorstream<<wide_to_narrow(error_message)<<std::endl;
1847 // Load gamespec for required game
1848 gamespec = findWorldSubgame(worldspec.path);
1849 if(!gamespec.isValid() && !commanded_gamespec.isValid()){
1850 error_message = wgettext("Could not find or load game \"")
1851 + narrow_to_wide(worldspec.gameid) + L"\"";
1852 errorstream<<wide_to_narrow(error_message)<<std::endl;
1855 if(commanded_gamespec.isValid() &&
1856 commanded_gamespec.id != worldspec.gameid){
1857 errorstream<<"WARNING: Overriding gamespec from \""
1858 <<worldspec.gameid<<"\" to \""
1859 <<commanded_gamespec.id<<"\""<<std::endl;
1860 gamespec = commanded_gamespec;
1863 if(!gamespec.isValid()){
1864 error_message = wgettext("Invalid gamespec.");
1865 error_message += L" (world_gameid="
1866 +narrow_to_wide(worldspec.gameid)+L")";
1867 errorstream<<wide_to_narrow(error_message)<<std::endl;
1876 // Break out of menu-game loop to shut down cleanly
1877 if(device->run() == false || kill == true)
1898 simple_singleplayer_mode
1902 catch(con::PeerNotFoundException &e)
1904 error_message = wgettext("Connection error (timed out?)");
1905 errorstream<<wide_to_narrow(error_message)<<std::endl;
1907 catch(ServerError &e)
1909 error_message = narrow_to_wide(e.what());
1910 errorstream<<wide_to_narrow(error_message)<<std::endl;
1914 errorstream<<e.what()<<std::endl;
1915 error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
1918 catch(std::exception &e)
1920 std::string narrow_message = "Some exception: \"";
1921 narrow_message += e.what();
1922 narrow_message += "\"";
1923 errorstream<<narrow_message<<std::endl;
1924 error_message = narrow_to_wide(narrow_message);
1928 // If no main menu, show error and exit
1931 if(error_message != L""){
1932 verbosestream<<"error_message = "
1933 <<wide_to_narrow(error_message)<<std::endl;
1943 In the end, delete the Irrlicht device.
1949 // Update configuration file
1950 if(configpath != "")
1951 g_settings->updateConfigFile(configpath.c_str());
1953 // Print modified quicktune values
1955 bool header_printed = false;
1956 std::vector<std::string> names = getQuicktuneNames();
1957 for(u32 i=0; i<names.size(); i++){
1958 QuicktuneValue val = getQuicktuneValue(names[i]);
1961 if(!header_printed){
1962 dstream<<"Modified quicktune values:"<<std::endl;
1963 header_printed = true;
1965 dstream<<names[i]<<" = "<<val.getString()<<std::endl;
1969 END_DEBUG_EXCEPTION_HANDLER(errorstream)
1971 debugstreams_deinit();