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"
54 #include "constants.h"
57 #include "guiMessageMenu.h"
60 #include "guiMainMenu.h"
65 #include "defaultsettings.h"
72 #include "xCGUITTFont.h"
74 #include "util/string.h"
76 #include "quicktune.h"
77 #include "serverlist.h"
81 These are loaded from the config file.
83 Settings main_settings;
84 Settings *g_settings = &main_settings;
87 Profiler main_profiler;
88 Profiler *g_profiler = &main_profiler;
95 std::ostream *dout_con_ptr = &dummyout;
96 std::ostream *derr_con_ptr = &verbosestream;
99 std::ostream *dout_server_ptr = &infostream;
100 std::ostream *derr_server_ptr = &errorstream;
103 std::ostream *dout_client_ptr = &infostream;
104 std::ostream *derr_client_ptr = &errorstream;
111 /* mainmenumanager.h */
113 gui::IGUIEnvironment* guienv = NULL;
114 gui::IGUIStaticText *guiroot = NULL;
115 MainMenuManager g_menumgr;
119 return (g_menumgr.menuCount() == 0);
122 // Passed to menus to allow disconnecting and exiting
123 MainGameCallback *g_gamecallback = NULL;
127 gettime.h implementation
134 /* Use imprecise system calls directly (from porting.h) */
135 return porting::getTimeMs();
140 // A small helper class
144 virtual u32 getTime() = 0;
147 // A precise irrlicht one
148 class IrrlichtTimeGetter: public TimeGetter
151 IrrlichtTimeGetter(IrrlichtDevice *device):
158 return m_device->getTimer()->getRealTime();
161 IrrlichtDevice *m_device;
163 // Not so precise one which works without irrlicht
164 class SimpleTimeGetter: public TimeGetter
169 return porting::getTimeMs();
173 // A pointer to a global instance of the time getter
175 TimeGetter *g_timegetter = NULL;
179 if(g_timegetter == NULL)
181 return g_timegetter->getTime();
186 class StderrLogOutput: public ILogOutput
189 /* line: Full line with timestamp, level and thread */
190 void printLog(const std::string &line)
192 std::cerr<<line<<std::endl;
194 } main_stderr_log_out;
196 class DstreamNoStderrLogOutput: public ILogOutput
199 /* line: Full line with timestamp, level and thread */
200 void printLog(const std::string &line)
202 dstream_no_stderr<<line<<std::endl;
204 } main_dstream_no_stderr_log_out;
209 Event handler for Irrlicht
211 NOTE: Everything possible should be moved out from here,
212 probably to InputHandler and the_game
215 class MyEventReceiver : public IEventReceiver
218 // This is the one method that we have to implement
219 virtual bool OnEvent(const SEvent& event)
222 React to nothing here if a menu is active
224 if(noMenuActive() == false)
229 // Remember whether each key is down or up
230 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
232 if(event.KeyInput.PressedDown) {
233 keyIsDown.set(event.KeyInput);
234 keyWasDown.set(event.KeyInput);
236 keyIsDown.unset(event.KeyInput);
240 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
242 if(noMenuActive() == false)
245 middle_active = false;
246 right_active = false;
250 left_active = event.MouseInput.isLeftPressed();
251 middle_active = event.MouseInput.isMiddlePressed();
252 right_active = event.MouseInput.isRightPressed();
254 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
258 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
262 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
266 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
268 rightreleased = true;
270 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
272 mouse_wheel += event.MouseInput.Wheel;
280 bool IsKeyDown(const KeyPress &keyCode) const
282 return keyIsDown[keyCode];
285 // Checks whether a key was down and resets the state
286 bool WasKeyDown(const KeyPress &keyCode)
288 bool b = keyWasDown[keyCode];
290 keyWasDown.unset(keyCode);
307 rightclicked = false;
308 leftreleased = false;
309 rightreleased = false;
312 middle_active = false;
313 right_active = false;
335 IrrlichtDevice *m_device;
337 // The current state of keys
339 // Whether a key has been pressed or not
344 Separated input handler
347 class RealInputHandler : public InputHandler
350 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
355 virtual bool isKeyDown(const KeyPress &keyCode)
357 return m_receiver->IsKeyDown(keyCode);
359 virtual bool wasKeyDown(const KeyPress &keyCode)
361 return m_receiver->WasKeyDown(keyCode);
363 virtual v2s32 getMousePos()
365 return m_device->getCursorControl()->getPosition();
367 virtual void setMousePos(s32 x, s32 y)
369 m_device->getCursorControl()->setPosition(x, y);
372 virtual bool getLeftState()
374 return m_receiver->left_active;
376 virtual bool getRightState()
378 return m_receiver->right_active;
381 virtual bool getLeftClicked()
383 return m_receiver->leftclicked;
385 virtual bool getRightClicked()
387 return m_receiver->rightclicked;
389 virtual void resetLeftClicked()
391 m_receiver->leftclicked = false;
393 virtual void resetRightClicked()
395 m_receiver->rightclicked = false;
398 virtual bool getLeftReleased()
400 return m_receiver->leftreleased;
402 virtual bool getRightReleased()
404 return m_receiver->rightreleased;
406 virtual void resetLeftReleased()
408 m_receiver->leftreleased = false;
410 virtual void resetRightReleased()
412 m_receiver->rightreleased = false;
415 virtual s32 getMouseWheel()
417 return m_receiver->getMouseWheel();
422 m_receiver->clearInput();
425 IrrlichtDevice *m_device;
426 MyEventReceiver *m_receiver;
429 class RandomInputHandler : public InputHandler
437 rightclicked = false;
438 leftreleased = false;
439 rightreleased = false;
442 virtual bool isKeyDown(const KeyPress &keyCode)
444 return keydown[keyCode];
446 virtual bool wasKeyDown(const KeyPress &keyCode)
450 virtual v2s32 getMousePos()
454 virtual void setMousePos(s32 x, s32 y)
456 mousepos = v2s32(x,y);
459 virtual bool getLeftState()
463 virtual bool getRightState()
468 virtual bool getLeftClicked()
472 virtual bool getRightClicked()
476 virtual void resetLeftClicked()
480 virtual void resetRightClicked()
482 rightclicked = false;
485 virtual bool getLeftReleased()
489 virtual bool getRightReleased()
491 return rightreleased;
493 virtual void resetLeftReleased()
495 leftreleased = false;
497 virtual void resetRightReleased()
499 rightreleased = false;
502 virtual s32 getMouseWheel()
507 virtual void step(float dtime)
510 static float counter1 = 0;
514 counter1 = 0.1*Rand(1, 40);
515 keydown.toggle(getKeySetting("keymap_jump"));
519 static float counter1 = 0;
523 counter1 = 0.1*Rand(1, 40);
524 keydown.toggle(getKeySetting("keymap_special1"));
528 static float counter1 = 0;
532 counter1 = 0.1*Rand(1, 40);
533 keydown.toggle(getKeySetting("keymap_forward"));
537 static float counter1 = 0;
541 counter1 = 0.1*Rand(1, 40);
542 keydown.toggle(getKeySetting("keymap_left"));
546 static float counter1 = 0;
550 counter1 = 0.1*Rand(1, 20);
551 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
555 static float counter1 = 0;
559 counter1 = 0.1*Rand(1, 30);
560 leftdown = !leftdown;
568 static float counter1 = 0;
572 counter1 = 0.1*Rand(1, 15);
573 rightdown = !rightdown;
577 rightreleased = true;
580 mousepos += mousespeed;
583 s32 Rand(s32 min, s32 max)
585 return (myrand()%(max-min+1))+min;
599 void drawMenuBackground(video::IVideoDriver* driver)
601 core::dimension2d<u32> screensize = driver->getScreenSize();
603 video::ITexture *bgtexture =
604 driver->getTexture(getTexturePath("menubg.png").c_str());
607 s32 scaledsize = 128;
609 // The important difference between destsize and screensize is
610 // that destsize is rounded to whole scaled pixels.
611 // These formulas use component-wise multiplication and division of v2u32.
612 v2u32 texturesize = bgtexture->getSize();
613 v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
614 v2u32 destsize = scaledsize * sourcesize / texturesize;
616 // Default texture wrapping mode in Irrlicht is ETC_REPEAT.
617 driver->draw2DImage(bgtexture,
618 core::rect<s32>(0, 0, destsize.X, destsize.Y),
619 core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
623 video::ITexture *logotexture =
624 driver->getTexture(getTexturePath("menulogo.png").c_str());
627 v2s32 logosize(logotexture->getOriginalSize().Width,
628 logotexture->getOriginalSize().Height);
631 video::SColor bgcolor(255,50,50,50);
632 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
633 screensize.Width, screensize.Height);
634 driver->draw2DRectangle(bgcolor, bgrect, NULL);
636 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
637 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
638 rect -= v2s32(logosize.X/2, 0);
639 driver->draw2DImage(logotexture, rect,
640 core::rect<s32>(core::position2d<s32>(0,0),
641 core::dimension2di(logotexture->getSize())),
648 // These are defined global so that they're not optimized too much.
649 // Can't change them to volatile.
654 std::string tempstring;
655 std::string tempstring2;
660 infostream<<"The following test should take around 20ms."<<std::endl;
661 TimeTaker timer("Testing std::string speed");
662 const u32 jj = 10000;
663 for(u32 j=0; j<jj; j++)
668 for(u32 i=0; i<ii; i++){
669 tempstring2 += "asd";
671 for(u32 i=0; i<ii+1; i++){
673 if(tempstring == tempstring2)
679 infostream<<"All of the following tests should take around 100ms each."
683 TimeTaker timer("Testing floating-point conversion speed");
685 for(u32 i=0; i<4000000; i++){
692 TimeTaker timer("Testing floating-point vector speed");
694 tempv3f1 = v3f(1,2,3);
695 tempv3f2 = v3f(4,5,6);
696 for(u32 i=0; i<10000000; i++){
697 tempf += tempv3f1.dotProduct(tempv3f2);
698 tempv3f2 += v3f(7,8,9);
703 TimeTaker timer("Testing core::map speed");
705 core::map<v2s16, f32> map1;
708 for(s16 y=0; y<ii; y++){
709 for(s16 x=0; x<ii; x++){
710 map1.insert(v2s16(x,y), tempf);
714 for(s16 y=ii-1; y>=0; y--){
715 for(s16 x=0; x<ii; x++){
716 tempf = map1[v2s16(x,y)];
722 infostream<<"Around 5000/ms should do well here."<<std::endl;
723 TimeTaker timer("Testing mutex speed");
737 while(timer.getTime() < 10);
739 u32 dtime = timer.stop();
740 u32 per_ms = n / dtime;
741 infostream<<"Done. "<<dtime<<"ms, "
742 <<per_ms<<"/ms"<<std::endl;
746 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
749 for(u32 i=0; i<worldspecs.size(); i++){
750 std::string name = worldspecs[i].name;
751 std::string path = worldspecs[i].path;
752 if(name.find(" ") != std::string::npos)
753 name = std::string("'") + name + "'";
754 path = std::string("'") + path + "'";
755 name = padStringRight(name, 14);
756 os<<" "<<name<<" "<<path<<std::endl;
760 int main(int argc, char *argv[])
768 log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
769 log_add_output_all_levs(&main_dstream_no_stderr_log_out);
771 log_register_thread("main");
773 // This enables internatonal characters input
774 if( setlocale(LC_ALL, "") == NULL )
776 fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] );
779 // Set locale. This is for forcing '.' as the decimal point.
781 std::locale::global(std::locale(std::locale(""), "C", std::locale::numeric));
782 setlocale(LC_NUMERIC, "C");
783 } catch (const std::exception& ex) {
784 errorstream<<"Could not set numeric locale to C"<<std::endl;
790 // List all allowed options
791 core::map<std::string, ValueSpec> allowed_options;
792 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG,
793 _("Show allowed options")));
794 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
795 _("Load configuration from specified file")));
796 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING,
797 _("Set network port (UDP)")));
798 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG,
799 _("Disable unit tests")));
800 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG,
801 _("Enable unit tests")));
802 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
803 _("Same as --world (deprecated)")));
804 allowed_options.insert("world", ValueSpec(VALUETYPE_STRING,
805 _("Set world path (implies local game) ('list' lists all)")));
806 allowed_options.insert("worldname", ValueSpec(VALUETYPE_STRING,
807 _("Set world by name (implies local game)")));
808 allowed_options.insert("info", ValueSpec(VALUETYPE_FLAG,
809 _("Print more information to console")));
810 allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG,
811 _("Print even more information to console")));
812 allowed_options.insert("trace", ValueSpec(VALUETYPE_FLAG,
813 _("Print enormous amounts of information to log and console")));
814 allowed_options.insert("logfile", ValueSpec(VALUETYPE_STRING,
815 _("Set logfile path ('' = no logging)")));
816 allowed_options.insert("gameid", ValueSpec(VALUETYPE_STRING,
817 _("Set gameid (\"--gameid list\" prints available ones)")));
819 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG,
820 _("Run speed tests")));
821 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING,
822 _("Address to connect to. ('' = local game)")));
823 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG,
824 _("Enable random user input, for testing")));
825 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
826 _("Run dedicated server")));
827 allowed_options.insert("name", ValueSpec(VALUETYPE_STRING,
828 _("Set player name")));
829 allowed_options.insert("password", ValueSpec(VALUETYPE_STRING,
831 allowed_options.insert("go", ValueSpec(VALUETYPE_FLAG,
832 _("Disable main menu")));
837 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
839 if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
841 dstream<<_("Allowed options:")<<std::endl;
842 for(core::map<std::string, ValueSpec>::Iterator
843 i = allowed_options.getIterator();
844 i.atEnd() == false; i++)
846 std::ostringstream os1(std::ios::binary);
847 os1<<" --"<<i.getNode()->getKey();
848 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
852 dstream<<padStringRight(os1.str(), 24);
854 if(i.getNode()->getValue().help != NULL)
855 dstream<<i.getNode()->getValue().help;
859 return cmd_args.getFlag("help") ? 0 : 1;
863 Low-level initialization
866 // If trace is enabled, enable logging of certain things
867 if(cmd_args.getFlag("trace")){
868 dstream<<_("Enabling trace level debug output")<<std::endl;
869 log_trace_level_enabled = true;
870 dout_con_ptr = &verbosestream; // this is somewhat old crap
871 socket_enable_debug_output = true; // socket doesn't use log.h
873 // In certain cases, output info level on stderr
874 if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
875 cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
876 log_add_output(&main_stderr_log_out, LMT_INFO);
877 // In certain cases, output verbose level on stderr
878 if(cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
879 log_add_output(&main_stderr_log_out, LMT_VERBOSE);
881 porting::signal_handler_init();
882 bool &kill = *porting::signal_handler_killstatus();
884 porting::initializePaths();
886 // Create user data directory
887 fs::CreateDir(porting::path_user);
889 init_gettext((porting::path_share + DIR_DELIM + "locale").c_str());
891 infostream<<"path_share = "<<porting::path_share<<std::endl;
892 infostream<<"path_user = "<<porting::path_user<<std::endl;
894 // Initialize debug stacks
896 DSTACK(__FUNCTION_NAME);
899 BEGIN_DEBUG_EXCEPTION_HANDLER
901 // List gameids if requested
902 if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
904 std::set<std::string> gameids = getAvailableGameIds();
905 for(std::set<std::string>::const_iterator i = gameids.begin();
906 i != gameids.end(); i++)
907 dstream<<(*i)<<std::endl;
911 // List worlds if requested
912 if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
913 dstream<<_("Available worlds:")<<std::endl;
914 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
915 print_worldspecs(worldspecs, dstream);
919 // Print startup message
920 infostream<<PROJECT_NAME<<
921 " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
929 // Initialize default settings
930 set_default_settings(g_settings);
932 // Initialize sockets
934 atexit(sockets_cleanup);
940 // Path of configuration file in use
941 std::string configpath = "";
943 if(cmd_args.exists("config"))
945 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
948 errorstream<<"Could not read configuration from \""
949 <<cmd_args.get("config")<<"\""<<std::endl;
952 configpath = cmd_args.get("config");
956 core::array<std::string> filenames;
957 filenames.push_back(porting::path_user +
958 DIR_DELIM + "minetest.conf");
959 // Legacy configuration file location
960 filenames.push_back(porting::path_user +
961 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
963 // Try also from a lower level (to aid having the same configuration
964 // for many RUN_IN_PLACE installs)
965 filenames.push_back(porting::path_user +
966 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
969 for(u32 i=0; i<filenames.size(); i++)
971 bool r = g_settings->readConfigFile(filenames[i].c_str());
974 configpath = filenames[i];
979 // If no path found, use the first one (menu creates the file)
981 configpath = filenames[0];
984 // Initialize debug streams
985 #define DEBUGFILE "debug.txt"
987 std::string logfile = DEBUGFILE;
989 std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
991 if(cmd_args.exists("logfile"))
992 logfile = cmd_args.get("logfile");
994 log_remove_output(&main_dstream_no_stderr_log_out);
995 int loglevel = g_settings->getS32("debug_log_level");
997 if (loglevel == 0) //no logging
999 else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
1000 log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1));
1003 debugstreams_init(false, logfile.c_str());
1005 debugstreams_init(false, NULL);
1007 infostream<<"logfile = "<<logfile<<std::endl;
1009 // Initialize random seed
1017 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1018 || cmd_args.getFlag("enable-unittests") == true)
1029 if(cmd_args.exists("port"))
1030 port = cmd_args.getU16("port");
1031 else if(g_settings->exists("port"))
1032 port = g_settings->getU16("port");
1037 std::string commanded_world = "";
1038 if(cmd_args.exists("world"))
1039 commanded_world = cmd_args.get("world");
1040 else if(cmd_args.exists("map-dir"))
1041 commanded_world = cmd_args.get("map-dir");
1042 else if(cmd_args.exists("nonopt0")) // First nameless argument
1043 commanded_world = cmd_args.get("nonopt0");
1044 else if(g_settings->exists("map-dir"))
1045 commanded_world = g_settings->get("map-dir");
1048 std::string commanded_worldname = "";
1049 if(cmd_args.exists("worldname"))
1050 commanded_worldname = cmd_args.get("worldname");
1052 // Strip world.mt from commanded_world
1054 std::string worldmt = "world.mt";
1055 if(commanded_world.size() > worldmt.size() &&
1056 commanded_world.substr(commanded_world.size()-worldmt.size())
1058 dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl;
1059 commanded_world = commanded_world.substr(
1060 0, commanded_world.size()-worldmt.size());
1064 // If a world name was specified, convert it to a path
1065 if(commanded_worldname != ""){
1066 // Get information about available worlds
1067 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1069 for(u32 i=0; i<worldspecs.size(); i++){
1070 std::string name = worldspecs[i].name;
1071 if(name == commanded_worldname){
1072 if(commanded_world != ""){
1073 dstream<<_("--worldname takes precedence over previously "
1074 "selected world.")<<std::endl;
1076 commanded_world = worldspecs[i].path;
1082 dstream<<_("World")<<" '"<<commanded_worldname<<_("' not "
1083 "available. Available worlds:")<<std::endl;
1084 print_worldspecs(worldspecs, dstream);
1090 SubgameSpec commanded_gamespec;
1091 if(cmd_args.exists("gameid")){
1092 std::string gameid = cmd_args.get("gameid");
1093 commanded_gamespec = findSubgame(gameid);
1094 if(!commanded_gamespec.isValid()){
1095 errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl;
1101 Run dedicated server if asked to or no other option
1104 bool run_dedicated_server = true;
1106 bool run_dedicated_server = cmd_args.getFlag("server");
1108 g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
1109 if(run_dedicated_server)
1111 DSTACK("Dedicated server branch");
1112 // Create time getter if built with Irrlicht
1114 g_timegetter = new SimpleTimeGetter();
1118 std::string world_path;
1119 verbosestream<<_("Determining world path")<<std::endl;
1120 bool is_legacy_world = false;
1121 // If a world was commanded, use it
1122 if(commanded_world != ""){
1123 world_path = commanded_world;
1124 infostream<<"Using commanded world path ["<<world_path<<"]"
1127 // No world was specified; try to select it automatically
1130 // Get information about available worlds
1131 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1132 // If a world name was specified, select it
1133 if(commanded_worldname != ""){
1135 for(u32 i=0; i<worldspecs.size(); i++){
1136 std::string name = worldspecs[i].name;
1137 if(name == commanded_worldname){
1138 world_path = worldspecs[i].path;
1142 if(world_path == ""){
1143 dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not "
1144 "available. Available worlds:")<<std::endl;
1145 print_worldspecs(worldspecs, dstream);
1149 // If there is only a single world, use it
1150 if(worldspecs.size() == 1){
1151 world_path = worldspecs[0].path;
1152 dstream<<_("Automatically selecting world at")<<" ["
1153 <<world_path<<"]"<<std::endl;
1154 // If there are multiple worlds, list them
1155 } else if(worldspecs.size() > 1){
1156 dstream<<_("Multiple worlds are available.")<<std::endl;
1157 dstream<<_("Please select one using --worldname <name>"
1158 " or --world <path>")<<std::endl;
1159 print_worldspecs(worldspecs, dstream);
1161 // If there are no worlds, automatically create a new one
1163 // This is the ultimate default world path
1164 world_path = porting::path_user + DIR_DELIM + "worlds" +
1165 DIR_DELIM + "world";
1166 infostream<<"Creating default world at ["
1167 <<world_path<<"]"<<std::endl;
1171 if(world_path == ""){
1172 errorstream<<"No world path specified or found."<<std::endl;
1175 verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl;
1177 // We need a gamespec.
1178 SubgameSpec gamespec;
1179 verbosestream<<_("Determining gameid/gamespec")<<std::endl;
1180 // If world doesn't exist
1181 if(!getWorldExists(world_path))
1183 // Try to take gamespec from command line
1184 if(commanded_gamespec.isValid()){
1185 gamespec = commanded_gamespec;
1186 infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl;
1188 // Otherwise we will be using "minetest"
1190 gamespec = findSubgame(g_settings->get("default_game"));
1191 infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl;
1197 std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
1198 // If commanded to use a gameid, do so
1199 if(commanded_gamespec.isValid()){
1200 gamespec = commanded_gamespec;
1201 if(commanded_gamespec.id != world_gameid){
1202 errorstream<<"WARNING: Using commanded gameid ["
1203 <<gamespec.id<<"]"<<" instead of world gameid ["
1204 <<world_gameid<<"]"<<std::endl;
1207 // If world contains an embedded game, use it;
1208 // Otherwise find world from local system.
1209 gamespec = findWorldSubgame(world_path);
1210 infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl;
1213 if(!gamespec.isValid()){
1214 errorstream<<"Subgame ["<<gamespec.id<<"] could not be found."
1218 verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl;
1221 Server server(world_path, configpath, gamespec, false);
1225 dedicated_server_loop(server, kill);
1230 #ifndef SERVER // Exclude from dedicated server build
1236 std::string address = g_settings->get("address");
1237 if(commanded_world != "")
1239 else if(cmd_args.exists("address"))
1240 address = cmd_args.get("address");
1242 std::string playername = g_settings->get("name");
1243 if(cmd_args.exists("name"))
1244 playername = cmd_args.get("name");
1246 bool skip_main_menu = cmd_args.getFlag("go");
1249 Device initialization
1252 // Resolution selection
1254 bool fullscreen = g_settings->getBool("fullscreen");
1255 u16 screenW = g_settings->getU16("screenW");
1256 u16 screenH = g_settings->getU16("screenH");
1260 bool vsync = g_settings->getBool("vsync");
1261 u16 bits = g_settings->getU16("fullscreen_bpp");
1262 u16 fsaa = g_settings->getU16("fsaa");
1266 video::E_DRIVER_TYPE driverType;
1268 std::string driverstring = g_settings->get("video_driver");
1270 if(driverstring == "null")
1271 driverType = video::EDT_NULL;
1272 else if(driverstring == "software")
1273 driverType = video::EDT_SOFTWARE;
1274 else if(driverstring == "burningsvideo")
1275 driverType = video::EDT_BURNINGSVIDEO;
1276 else if(driverstring == "direct3d8")
1277 driverType = video::EDT_DIRECT3D8;
1278 else if(driverstring == "direct3d9")
1279 driverType = video::EDT_DIRECT3D9;
1280 else if(driverstring == "opengl")
1281 driverType = video::EDT_OPENGL;
1284 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1285 "to opengl"<<std::endl;
1286 driverType = video::EDT_OPENGL;
1290 Create device and exit if creation failed
1293 MyEventReceiver receiver;
1295 IrrlichtDevice *device;
1297 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1298 params.DriverType = driverType;
1299 params.WindowSize = core::dimension2d<u32>(screenW, screenH);
1301 params.AntiAlias = fsaa;
1302 params.Fullscreen = fullscreen;
1303 params.Stencilbuffer = false;
1304 params.Vsync = vsync;
1305 params.EventReceiver = &receiver;
1307 device = createDeviceEx(params);
1310 return 1; // could not create selected driver.
1313 Continue initialization
1316 video::IVideoDriver* driver = device->getVideoDriver();
1319 This changes the minimum allowed number of vertices in a VBO.
1322 //driver->setMinHardwareBufferVertexCount(50);
1324 // Create time getter
1325 g_timegetter = new IrrlichtTimeGetter(device);
1327 // Create game callback for menus
1328 g_gamecallback = new MainGameCallback(device);
1331 Speed tests (done after irrlicht is loaded to get timer)
1333 if(cmd_args.getFlag("speedtests"))
1335 dstream<<"Running speed tests"<<std::endl;
1340 device->setResizable(true);
1342 bool random_input = g_settings->getBool("random_input")
1343 || cmd_args.getFlag("random-input");
1344 InputHandler *input = NULL;
1346 input = new RandomInputHandler();
1348 input = new RealInputHandler(device, &receiver);
1350 scene::ISceneManager* smgr = device->getSceneManager();
1352 guienv = device->getGUIEnvironment();
1353 gui::IGUISkin* skin = guienv->getSkin();
1355 std::string font_path = g_settings->get("font_path");
1356 u16 font_size = g_settings->getU16("font_size");
1357 gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size);
1359 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1362 skin->setFont(font);
1364 errorstream<<"WARNING: Font file was not found."
1365 " Using default font."<<std::endl;
1366 // If font was not found, this will get us one
1367 font = skin->getFont();
1370 u32 text_height = font->getDimension(L"Hello, world!").Height;
1371 infostream<<"text_height="<<text_height<<std::endl;
1373 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1374 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1375 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1376 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1377 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1378 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1379 skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1380 skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1382 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
1383 // Irrlicht 1.8 input colours
1384 skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128));
1385 skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
1392 ChatBackend chat_backend;
1395 If an error occurs, this is set to something and the
1396 menu-game loop is restarted. It is then displayed before
1399 std::wstring error_message = L"";
1401 // The password entered during the menu screen,
1402 std::string password;
1404 bool first_loop = true;
1409 while(device->run() && kill == false)
1411 // Set the window caption
1412 device->setWindowCaption((std::wstring(L"Minetest [")+wgettext("Main Menu")+L"]").c_str());
1414 // This is used for catching disconnects
1419 Clear everything from the GUIEnvironment
1424 We need some kind of a root node to be able to add
1425 custom gui elements directly on the screen.
1426 Otherwise they won't be automatically drawn.
1428 guiroot = guienv->addStaticText(L"",
1429 core::rect<s32>(0, 0, 10000, 10000));
1431 SubgameSpec gamespec;
1432 WorldSpec worldspec;
1433 bool simple_singleplayer_mode = false;
1435 // These are set up based on the menu and other things
1436 std::string current_playername = "inv£lid";
1437 std::string current_password = "";
1438 std::string current_address = "does-not-exist";
1439 int current_port = 0;
1442 Out-of-game menu loop.
1444 Loop quits when menu returns proper parameters.
1446 while(kill == false)
1448 // If skip_main_menu, only go through here once
1449 if(skip_main_menu && !first_loop){
1455 // Cursor can be non-visible when coming from the game
1456 device->getCursorControl()->setVisible(true);
1457 // Some stuff are left to scene manager when coming from the game
1461 // Initialize menu data
1462 MainMenuData menudata;
1463 if(g_settings->exists("selected_mainmenu_tab"))
1464 menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
1465 menudata.address = narrow_to_wide(address);
1466 menudata.name = narrow_to_wide(playername);
1467 menudata.port = narrow_to_wide(itos(port));
1468 if(cmd_args.exists("password"))
1469 menudata.password = narrow_to_wide(cmd_args.get("password"));
1470 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1471 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1472 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1473 menudata.opaque_water = g_settings->getBool("opaque_water");
1474 menudata.mip_map = g_settings->getBool("mip_map");
1475 menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
1476 menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
1477 menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
1478 menudata.enable_shaders = g_settings->getS32("enable_shaders");
1479 menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
1480 menudata.enable_particles = g_settings->getBool("enable_particles");
1481 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
1482 menudata.creative_mode = g_settings->getBool("creative_mode");
1483 menudata.enable_damage = g_settings->getBool("enable_damage");
1484 menudata.enable_public = g_settings->getBool("server_announce");
1485 // Default to selecting nothing
1486 menudata.selected_world = -1;
1487 // Get world listing for the menu
1488 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1489 // If there is only one world, select it
1490 if(worldspecs.size() == 1){
1491 menudata.selected_world = 0;
1493 // Otherwise try to select according to selected_world_path
1494 else if(g_settings->exists("selected_world_path")){
1495 std::string trypath = g_settings->get("selected_world_path");
1496 for(u32 i=0; i<worldspecs.size(); i++){
1497 if(worldspecs[i].path == trypath){
1498 menudata.selected_world = i;
1503 // If a world was commanded, append and select it
1504 if(commanded_world != ""){
1505 std::string gameid = getWorldGameId(commanded_world, true);
1506 std::string name = _("[--world parameter]");
1508 gameid = g_settings->get("default_game");
1511 WorldSpec spec(commanded_world, name, gameid);
1512 worldspecs.push_back(spec);
1513 menudata.selected_world = worldspecs.size()-1;
1515 // Copy worldspecs to menu
1516 menudata.worlds = worldspecs;
1518 if(skip_main_menu == false)
1520 video::IVideoDriver* driver = device->getVideoDriver();
1522 infostream<<"Waiting for other menus"<<std::endl;
1523 while(device->run() && kill == false)
1527 driver->beginScene(true, true,
1528 video::SColor(255,128,128,128));
1529 drawMenuBackground(driver);
1532 // On some computers framerate doesn't seem to be
1533 // automatically limited
1536 infostream<<"Waited for other menus"<<std::endl;
1539 new GUIMainMenu(guienv, guiroot, -1,
1540 &g_menumgr, &menudata, g_gamecallback);
1541 menu->allowFocusRemoval(true);
1543 if(error_message != L"")
1545 verbosestream<<"error_message = "
1546 <<wide_to_narrow(error_message)<<std::endl;
1548 GUIMessageMenu *menu2 =
1549 new GUIMessageMenu(guienv, guiroot, -1,
1550 &g_menumgr, error_message.c_str());
1552 error_message = L"";
1555 infostream<<"Created main menu"<<std::endl;
1557 while(device->run() && kill == false)
1559 if(menu->getStatus() == true)
1562 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1563 driver->beginScene(true, true, video::SColor(255,128,128,128));
1565 drawMenuBackground(driver);
1571 // On some computers framerate doesn't seem to be
1572 // automatically limited
1576 infostream<<"Dropping main menu"<<std::endl;
1581 playername = wide_to_narrow(menudata.name);
1582 password = translatePassword(playername, menudata.password);
1583 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1585 address = wide_to_narrow(menudata.address);
1586 int newport = stoi(wide_to_narrow(menudata.port));
1589 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1591 g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
1592 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1593 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1594 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1595 g_settings->set("opaque_water", itos(menudata.opaque_water));
1597 g_settings->set("mip_map", itos(menudata.mip_map));
1598 g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
1599 g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
1600 g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
1602 g_settings->setS32("enable_shaders", menudata.enable_shaders);
1603 g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
1604 g_settings->set("enable_particles", itos(menudata.enable_particles));
1606 g_settings->set("creative_mode", itos(menudata.creative_mode));
1607 g_settings->set("enable_damage", itos(menudata.enable_damage));
1608 g_settings->set("server_announce", itos(menudata.enable_public));
1609 g_settings->set("name", playername);
1610 g_settings->set("address", address);
1611 g_settings->set("port", itos(port));
1612 if(menudata.selected_world != -1)
1613 g_settings->set("selected_world_path",
1614 worldspecs[menudata.selected_world].path);
1616 // Break out of menu-game loop to shut down cleanly
1617 if(device->run() == false || kill == true)
1620 current_playername = playername;
1621 current_password = password;
1622 current_address = address;
1623 current_port = port;
1625 // If using simple singleplayer mode, override
1626 if(simple_singleplayer_mode){
1627 current_playername = "singleplayer";
1628 current_password = "";
1629 current_address = "";
1630 current_port = 30011;
1632 else if (address != "")
1634 ServerListSpec server;
1635 server["name"] = menudata.servername;
1636 server["address"] = wide_to_narrow(menudata.address);
1637 server["port"] = wide_to_narrow(menudata.port);
1638 server["description"] = menudata.serverdescription;
1639 ServerList::insert(server);
1642 // Set world path to selected one
1643 if(menudata.selected_world != -1){
1644 worldspec = worldspecs[menudata.selected_world];
1645 infostream<<"Selected world: "<<worldspec.name
1646 <<" ["<<worldspec.path<<"]"<<std::endl;
1649 // Only refresh if so requested
1650 if(menudata.only_refresh){
1651 infostream<<"Refreshing menu"<<std::endl;
1655 // Create new world if requested
1656 if(menudata.create_world_name != L"")
1658 std::string path = porting::path_user + DIR_DELIM
1659 "worlds" + DIR_DELIM
1660 + wide_to_narrow(menudata.create_world_name);
1661 // Create world if it doesn't exist
1662 if(!initializeWorld(path, menudata.create_world_gameid)){
1663 error_message = wgettext("Failed to initialize world");
1664 errorstream<<wide_to_narrow(error_message)<<std::endl;
1667 g_settings->set("selected_world_path", path);
1672 if(current_address == "")
1674 if(menudata.selected_world == -1){
1675 error_message = wgettext("No world selected and no address "
1676 "provided. Nothing to do.");
1677 errorstream<<wide_to_narrow(error_message)<<std::endl;
1680 // Load gamespec for required game
1681 gamespec = findWorldSubgame(worldspec.path);
1682 if(!gamespec.isValid() && !commanded_gamespec.isValid()){
1683 error_message = wgettext("Could not find or load game \"")
1684 + narrow_to_wide(worldspec.gameid) + L"\"";
1685 errorstream<<wide_to_narrow(error_message)<<std::endl;
1688 if(commanded_gamespec.isValid() &&
1689 commanded_gamespec.id != worldspec.gameid){
1690 errorstream<<"WARNING: Overriding gamespec from \""
1691 <<worldspec.gameid<<"\" to \""
1692 <<commanded_gamespec.id<<"\""<<std::endl;
1693 gamespec = commanded_gamespec;
1696 if(!gamespec.isValid()){
1697 error_message = wgettext("Invalid gamespec.");
1698 error_message += L" (world_gameid="
1699 +narrow_to_wide(worldspec.gameid)+L")";
1700 errorstream<<wide_to_narrow(error_message)<<std::endl;
1709 // Break out of menu-game loop to shut down cleanly
1710 if(device->run() == false || kill == true)
1731 simple_singleplayer_mode
1735 catch(con::PeerNotFoundException &e)
1737 error_message = wgettext("Connection error (timed out?)");
1738 errorstream<<wide_to_narrow(error_message)<<std::endl;
1740 catch(ServerError &e)
1742 error_message = narrow_to_wide(e.what());
1743 errorstream<<wide_to_narrow(error_message)<<std::endl;
1747 errorstream<<e.what()<<std::endl;
1748 error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
1751 catch(std::exception &e)
1753 std::string narrow_message = "Some exception: \"";
1754 narrow_message += e.what();
1755 narrow_message += "\"";
1756 errorstream<<narrow_message<<std::endl;
1757 error_message = narrow_to_wide(narrow_message);
1761 // If no main menu, show error and exit
1764 if(error_message != L""){
1765 verbosestream<<"error_message = "
1766 <<wide_to_narrow(error_message)<<std::endl;
1776 In the end, delete the Irrlicht device.
1782 // Update configuration file
1783 if(configpath != "")
1784 g_settings->updateConfigFile(configpath.c_str());
1786 // Print modified quicktune values
1788 bool header_printed = false;
1789 std::vector<std::string> names = getQuicktuneNames();
1790 for(u32 i=0; i<names.size(); i++){
1791 QuicktuneValue val = getQuicktuneValue(names[i]);
1794 if(!header_printed){
1795 dstream<<"Modified quicktune values:"<<std::endl;
1796 header_printed = true;
1798 dstream<<names[i]<<" = "<<val.getString()<<std::endl;
1802 END_DEBUG_EXCEPTION_HANDLER(errorstream)
1804 debugstreams_deinit();