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"
80 #include "sound_openal.h"
84 These are loaded from the config file.
86 Settings main_settings;
87 Settings *g_settings = &main_settings;
90 Profiler main_profiler;
91 Profiler *g_profiler = &main_profiler;
93 // Menu clouds are created later
94 Clouds *g_menuclouds = 0;
95 irr::scene::ISceneManager *g_menucloudsmgr = 0;
102 std::ostream *dout_con_ptr = &dummyout;
103 std::ostream *derr_con_ptr = &verbosestream;
106 std::ostream *dout_server_ptr = &infostream;
107 std::ostream *derr_server_ptr = &errorstream;
110 std::ostream *dout_client_ptr = &infostream;
111 std::ostream *derr_client_ptr = &errorstream;
118 /* mainmenumanager.h */
120 gui::IGUIEnvironment* guienv = NULL;
121 gui::IGUIStaticText *guiroot = NULL;
122 MainMenuManager g_menumgr;
126 return (g_menumgr.menuCount() == 0);
129 // Passed to menus to allow disconnecting and exiting
130 MainGameCallback *g_gamecallback = NULL;
134 gettime.h implementation
141 /* Use imprecise system calls directly (from porting.h) */
142 return porting::getTime(PRECISION_MILLI);
145 u32 getTime(TimePrecision prec)
147 return porting::getTime(prec);
152 // A small helper class
156 virtual u32 getTime(TimePrecision prec) = 0;
159 // A precise irrlicht one
160 class IrrlichtTimeGetter: public TimeGetter
163 IrrlichtTimeGetter(IrrlichtDevice *device):
166 u32 getTime(TimePrecision prec)
168 if (prec == PRECISION_MILLI) {
171 return m_device->getTimer()->getRealTime();
173 return porting::getTime(prec);
177 IrrlichtDevice *m_device;
179 // Not so precise one which works without irrlicht
180 class SimpleTimeGetter: public TimeGetter
183 u32 getTime(TimePrecision prec)
185 return porting::getTime(prec);
189 // A pointer to a global instance of the time getter
191 TimeGetter *g_timegetter = NULL;
195 if(g_timegetter == NULL)
197 return g_timegetter->getTime(PRECISION_MILLI);
200 u32 getTime(TimePrecision prec) {
201 if (g_timegetter == NULL)
203 return g_timegetter->getTime(prec);
207 //Client side main menu music fetcher
209 class MenuMusicFetcher: public OnDemandSoundFetcher
211 std::set<std::string> m_fetched;
214 void fetchSounds(const std::string &name,
215 std::set<std::string> &dst_paths,
216 std::set<std::string> &dst_datas)
218 if(m_fetched.count(name))
220 m_fetched.insert(name);
222 base = porting::path_share + DIR_DELIM + "sounds";
223 dst_paths.insert(base + DIR_DELIM + name + ".ogg");
224 dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
225 dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
226 dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
227 dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
228 dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
229 dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
230 dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
231 dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
232 dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
233 dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
234 base = porting::path_user + DIR_DELIM + "sounds";
235 dst_paths.insert(base + DIR_DELIM + name + ".ogg");
236 dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
237 dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
238 dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
239 dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
240 dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
241 dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
242 dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
243 dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
244 dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
245 dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
250 class StderrLogOutput: public ILogOutput
253 /* line: Full line with timestamp, level and thread */
254 void printLog(const std::string &line)
256 std::cerr<<line<<std::endl;
258 } main_stderr_log_out;
260 class DstreamNoStderrLogOutput: public ILogOutput
263 /* line: Full line with timestamp, level and thread */
264 void printLog(const std::string &line)
266 dstream_no_stderr<<line<<std::endl;
268 } main_dstream_no_stderr_log_out;
273 Event handler for Irrlicht
275 NOTE: Everything possible should be moved out from here,
276 probably to InputHandler and the_game
279 class MyEventReceiver : public IEventReceiver
282 // This is the one method that we have to implement
283 virtual bool OnEvent(const SEvent& event)
286 React to nothing here if a menu is active
288 if(noMenuActive() == false)
293 // Remember whether each key is down or up
294 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
296 if(event.KeyInput.PressedDown) {
297 keyIsDown.set(event.KeyInput);
298 keyWasDown.set(event.KeyInput);
300 keyIsDown.unset(event.KeyInput);
304 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
306 if(noMenuActive() == false)
309 middle_active = false;
310 right_active = false;
314 left_active = event.MouseInput.isLeftPressed();
315 middle_active = event.MouseInput.isMiddlePressed();
316 right_active = event.MouseInput.isRightPressed();
318 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
322 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
326 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
330 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
332 rightreleased = true;
334 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
336 mouse_wheel += event.MouseInput.Wheel;
344 bool IsKeyDown(const KeyPress &keyCode) const
346 return keyIsDown[keyCode];
349 // Checks whether a key was down and resets the state
350 bool WasKeyDown(const KeyPress &keyCode)
352 bool b = keyWasDown[keyCode];
354 keyWasDown.unset(keyCode);
371 rightclicked = false;
372 leftreleased = false;
373 rightreleased = false;
376 middle_active = false;
377 right_active = false;
399 IrrlichtDevice *m_device;
401 // The current state of keys
403 // Whether a key has been pressed or not
408 Separated input handler
411 class RealInputHandler : public InputHandler
414 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
419 virtual bool isKeyDown(const KeyPress &keyCode)
421 return m_receiver->IsKeyDown(keyCode);
423 virtual bool wasKeyDown(const KeyPress &keyCode)
425 return m_receiver->WasKeyDown(keyCode);
427 virtual v2s32 getMousePos()
429 return m_device->getCursorControl()->getPosition();
431 virtual void setMousePos(s32 x, s32 y)
433 m_device->getCursorControl()->setPosition(x, y);
436 virtual bool getLeftState()
438 return m_receiver->left_active;
440 virtual bool getRightState()
442 return m_receiver->right_active;
445 virtual bool getLeftClicked()
447 return m_receiver->leftclicked;
449 virtual bool getRightClicked()
451 return m_receiver->rightclicked;
453 virtual void resetLeftClicked()
455 m_receiver->leftclicked = false;
457 virtual void resetRightClicked()
459 m_receiver->rightclicked = false;
462 virtual bool getLeftReleased()
464 return m_receiver->leftreleased;
466 virtual bool getRightReleased()
468 return m_receiver->rightreleased;
470 virtual void resetLeftReleased()
472 m_receiver->leftreleased = false;
474 virtual void resetRightReleased()
476 m_receiver->rightreleased = false;
479 virtual s32 getMouseWheel()
481 return m_receiver->getMouseWheel();
486 m_receiver->clearInput();
489 IrrlichtDevice *m_device;
490 MyEventReceiver *m_receiver;
493 class RandomInputHandler : public InputHandler
501 rightclicked = false;
502 leftreleased = false;
503 rightreleased = false;
506 virtual bool isKeyDown(const KeyPress &keyCode)
508 return keydown[keyCode];
510 virtual bool wasKeyDown(const KeyPress &keyCode)
514 virtual v2s32 getMousePos()
518 virtual void setMousePos(s32 x, s32 y)
520 mousepos = v2s32(x,y);
523 virtual bool getLeftState()
527 virtual bool getRightState()
532 virtual bool getLeftClicked()
536 virtual bool getRightClicked()
540 virtual void resetLeftClicked()
544 virtual void resetRightClicked()
546 rightclicked = false;
549 virtual bool getLeftReleased()
553 virtual bool getRightReleased()
555 return rightreleased;
557 virtual void resetLeftReleased()
559 leftreleased = false;
561 virtual void resetRightReleased()
563 rightreleased = false;
566 virtual s32 getMouseWheel()
571 virtual void step(float dtime)
574 static float counter1 = 0;
578 counter1 = 0.1*Rand(1, 40);
579 keydown.toggle(getKeySetting("keymap_jump"));
583 static float counter1 = 0;
587 counter1 = 0.1*Rand(1, 40);
588 keydown.toggle(getKeySetting("keymap_special1"));
592 static float counter1 = 0;
596 counter1 = 0.1*Rand(1, 40);
597 keydown.toggle(getKeySetting("keymap_forward"));
601 static float counter1 = 0;
605 counter1 = 0.1*Rand(1, 40);
606 keydown.toggle(getKeySetting("keymap_left"));
610 static float counter1 = 0;
614 counter1 = 0.1*Rand(1, 20);
615 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
619 static float counter1 = 0;
623 counter1 = 0.1*Rand(1, 30);
624 leftdown = !leftdown;
632 static float counter1 = 0;
636 counter1 = 0.1*Rand(1, 15);
637 rightdown = !rightdown;
641 rightreleased = true;
644 mousepos += mousespeed;
647 s32 Rand(s32 min, s32 max)
649 return (myrand()%(max-min+1))+min;
665 std::string current_gameid;
666 bool global_textures;
667 video::ITexture *background;
668 video::ITexture *overlay;
669 video::ITexture *header;
670 video::ITexture *footer;
673 global_textures(false),
680 static video::ITexture* getMenuTexture(const std::string &tname,
681 video::IVideoDriver* driver, const SubgameSpec *spec)
685 // eg. minetest_menu_background.png (for texture packs)
686 std::string pack_tname = spec->id + "_menu_" + tname + ".png";
687 path = getTexturePath(pack_tname);
689 return driver->getTexture(path.c_str());
690 // eg. games/minetest_game/menu/background.png
691 path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
693 return driver->getTexture(path.c_str());
696 // eg. menu_background.png
697 std::string pack_tname = "menu_" + tname + ".png";
698 path = getTexturePath(pack_tname);
700 return driver->getTexture(path.c_str());
705 void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
707 if(tab == TAB_SINGLEPLAYER){
708 if(spec->id == current_gameid)
710 current_gameid = spec->id;
711 global_textures = false;
712 background = getMenuTexture("background", driver, spec);
713 overlay = getMenuTexture("overlay", driver, spec);
714 header = getMenuTexture("header", driver, spec);
715 footer = getMenuTexture("footer", driver, spec);
720 global_textures = true;
721 background = getMenuTexture("background", driver, NULL);
722 overlay = getMenuTexture("overlay", driver, NULL);
723 header = getMenuTexture("header", driver, NULL);
724 footer = getMenuTexture("footer", driver, NULL);
729 void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
731 v2u32 screensize = driver->getScreenSize();
732 video::ITexture *texture = menutextures.background;
734 /* If no texture, draw background of solid color */
736 video::SColor color(255,80,58,37);
737 core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
738 driver->draw2DRectangle(color, rect, NULL);
742 /* Draw background texture */
743 v2u32 sourcesize = texture->getSize();
744 driver->draw2DImage(texture,
745 core::rect<s32>(0, 0, screensize.X, screensize.Y),
746 core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
750 void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
752 v2u32 screensize = driver->getScreenSize();
753 video::ITexture *texture = menutextures.overlay;
755 /* If no texture, draw nothing */
759 /* Draw overlay texture */
760 v2u32 sourcesize = texture->getSize();
761 driver->draw2DImage(texture,
762 core::rect<s32>(0, 0, screensize.X, screensize.Y),
763 core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
767 void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
769 core::dimension2d<u32> screensize = driver->getScreenSize();
770 video::ITexture *texture = menutextures.header;
772 /* If no texture, draw nothing */
776 f32 mult = (((f32)screensize.Width / 2)) /
777 ((f32)texture->getOriginalSize().Width);
779 v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
780 ((f32)texture->getOriginalSize().Height) * mult);
782 // Don't draw the header is there isn't enough room
783 s32 free_space = (((s32)screensize.Height)-320)/2;
784 if (free_space > splashsize.Y) {
785 core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
786 splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
787 ((free_space/2)-splashsize.Y/2)+10);
789 video::SColor bgcolor(255,50,50,50);
791 driver->draw2DImage(texture, splashrect,
792 core::rect<s32>(core::position2d<s32>(0,0),
793 core::dimension2di(texture->getSize())),
798 void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
800 core::dimension2d<u32> screensize = driver->getScreenSize();
801 video::ITexture *texture = menutextures.footer;
803 /* If no texture, draw nothing */
807 f32 mult = (((f32)screensize.Width)) /
808 ((f32)texture->getOriginalSize().Width);
810 v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
811 ((f32)texture->getOriginalSize().Height) * mult);
813 // Don't draw the footer if there isn't enough room
814 s32 free_space = (((s32)screensize.Height)-320)/2;
815 if (free_space > footersize.Y) {
816 core::rect<s32> rect(0,0,footersize.X,footersize.Y);
817 rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
818 rect -= v2s32(footersize.X/2, 0);
820 driver->draw2DImage(texture, rect,
821 core::rect<s32>(core::position2d<s32>(0,0),
822 core::dimension2di(texture->getSize())),
827 static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
829 for(size_t i=0; i<menudata.games.size(); i++){
830 if(menudata.games[i].id == menudata.selected_game){
831 return &menudata.games[i];
839 // These are defined global so that they're not optimized too much.
840 // Can't change them to volatile.
845 std::string tempstring;
846 std::string tempstring2;
851 infostream<<"The following test should take around 20ms."<<std::endl;
852 TimeTaker timer("Testing std::string speed");
853 const u32 jj = 10000;
854 for(u32 j=0; j<jj; j++)
859 for(u32 i=0; i<ii; i++){
860 tempstring2 += "asd";
862 for(u32 i=0; i<ii+1; i++){
864 if(tempstring == tempstring2)
870 infostream<<"All of the following tests should take around 100ms each."
874 TimeTaker timer("Testing floating-point conversion speed");
876 for(u32 i=0; i<4000000; i++){
883 TimeTaker timer("Testing floating-point vector speed");
885 tempv3f1 = v3f(1,2,3);
886 tempv3f2 = v3f(4,5,6);
887 for(u32 i=0; i<10000000; i++){
888 tempf += tempv3f1.dotProduct(tempv3f2);
889 tempv3f2 += v3f(7,8,9);
894 TimeTaker timer("Testing std::map speed");
896 std::map<v2s16, f32> map1;
899 for(s16 y=0; y<ii; y++){
900 for(s16 x=0; x<ii; x++){
901 map1[v2s16(x,y)] = tempf;
905 for(s16 y=ii-1; y>=0; y--){
906 for(s16 x=0; x<ii; x++){
907 tempf = map1[v2s16(x,y)];
913 infostream<<"Around 5000/ms should do well here."<<std::endl;
914 TimeTaker timer("Testing mutex speed");
928 while(timer.getTimerTime() < 10);
930 u32 dtime = timer.stop();
931 u32 per_ms = n / dtime;
932 infostream<<"Done. "<<dtime<<"ms, "
933 <<per_ms<<"/ms"<<std::endl;
937 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
940 for(u32 i=0; i<worldspecs.size(); i++){
941 std::string name = worldspecs[i].name;
942 std::string path = worldspecs[i].path;
943 if(name.find(" ") != std::string::npos)
944 name = std::string("'") + name + "'";
945 path = std::string("'") + path + "'";
946 name = padStringRight(name, 14);
947 os<<" "<<name<<" "<<path<<std::endl;
951 int main(int argc, char *argv[])
959 log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
960 log_add_output_all_levs(&main_dstream_no_stderr_log_out);
962 log_register_thread("main");
964 // This enables internatonal characters input
965 if( setlocale(LC_ALL, "") == NULL )
967 fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] );
970 // Set locale. This is for forcing '.' as the decimal point.
972 std::locale::global(std::locale(std::locale(""), "C", std::locale::numeric));
973 setlocale(LC_NUMERIC, "C");
974 } catch (const std::exception& ex) {
975 errorstream<<"Could not set numeric locale to C"<<std::endl;
981 // List all allowed options
982 std::map<std::string, ValueSpec> allowed_options;
983 allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
984 _("Show allowed options"))));
985 allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING,
986 _("Load configuration from specified file"))));
987 allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
988 _("Set network port (UDP)"))));
989 allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
990 _("Disable unit tests"))));
991 allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
992 _("Enable unit tests"))));
993 allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
994 _("Same as --world (deprecated)"))));
995 allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
996 _("Set world path (implies local game) ('list' lists all)"))));
997 allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING,
998 _("Set world by name (implies local game)"))));
999 allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG,
1000 _("Print more information to console"))));
1001 allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG,
1002 _("Print even more information to console"))));
1003 allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG,
1004 _("Print enormous amounts of information to log and console"))));
1005 allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING,
1006 _("Set logfile path ('' = no logging)"))));
1007 allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
1008 _("Set gameid (\"--gameid list\" prints available ones)"))));
1010 allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
1011 _("Show available video modes"))));
1012 allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
1013 _("Run speed tests"))));
1014 allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
1015 _("Address to connect to. ('' = local game)"))));
1016 allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
1017 _("Enable random user input, for testing"))));
1018 allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG,
1019 _("Run dedicated server"))));
1020 allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING,
1021 _("Set player name"))));
1022 allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING,
1023 _("Set password"))));
1024 allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG,
1025 _("Disable main menu"))));
1030 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
1032 if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
1034 dstream<<_("Allowed options:")<<std::endl;
1035 for(std::map<std::string, ValueSpec>::iterator
1036 i = allowed_options.begin();
1037 i != allowed_options.end(); ++i)
1039 std::ostringstream os1(std::ios::binary);
1040 os1<<" --"<<i->first;
1041 if(i->second.type == VALUETYPE_FLAG)
1045 dstream<<padStringRight(os1.str(), 24);
1047 if(i->second.help != NULL)
1048 dstream<<i->second.help;
1052 return cmd_args.getFlag("help") ? 0 : 1;
1056 Low-level initialization
1059 // If trace is enabled, enable logging of certain things
1060 if(cmd_args.getFlag("trace")){
1061 dstream<<_("Enabling trace level debug output")<<std::endl;
1062 log_trace_level_enabled = true;
1063 dout_con_ptr = &verbosestream; // this is somewhat old crap
1064 socket_enable_debug_output = true; // socket doesn't use log.h
1066 // In certain cases, output info level on stderr
1067 if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
1068 cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
1069 log_add_output(&main_stderr_log_out, LMT_INFO);
1070 // In certain cases, output verbose level on stderr
1071 if(cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
1072 log_add_output(&main_stderr_log_out, LMT_VERBOSE);
1074 porting::signal_handler_init();
1075 bool &kill = *porting::signal_handler_killstatus();
1077 porting::initializePaths();
1079 // Create user data directory
1080 fs::CreateDir(porting::path_user);
1082 init_gettext((porting::path_share + DIR_DELIM + "locale").c_str());
1084 infostream<<"path_share = "<<porting::path_share<<std::endl;
1085 infostream<<"path_user = "<<porting::path_user<<std::endl;
1087 // Initialize debug stacks
1088 debug_stacks_init();
1089 DSTACK(__FUNCTION_NAME);
1092 BEGIN_DEBUG_EXCEPTION_HANDLER
1094 // List gameids if requested
1095 if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
1097 std::set<std::string> gameids = getAvailableGameIds();
1098 for(std::set<std::string>::const_iterator i = gameids.begin();
1099 i != gameids.end(); i++)
1100 dstream<<(*i)<<std::endl;
1104 // List worlds if requested
1105 if(cmd_args.exists("world") && cmd_args.get("world") == "list"){
1106 dstream<<_("Available worlds:")<<std::endl;
1107 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1108 print_worldspecs(worldspecs, dstream);
1112 // Print startup message
1113 infostream<<PROJECT_NAME<<
1114 " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
1119 Basic initialization
1122 // Initialize default settings
1123 set_default_settings(g_settings);
1125 // Initialize sockets
1127 atexit(sockets_cleanup);
1133 // Path of configuration file in use
1134 std::string configpath = "";
1136 if(cmd_args.exists("config"))
1138 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
1141 errorstream<<"Could not read configuration from \""
1142 <<cmd_args.get("config")<<"\""<<std::endl;
1145 configpath = cmd_args.get("config");
1149 std::vector<std::string> filenames;
1150 filenames.push_back(porting::path_user +
1151 DIR_DELIM + "minetest.conf");
1152 // Legacy configuration file location
1153 filenames.push_back(porting::path_user +
1154 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1156 // Try also from a lower level (to aid having the same configuration
1157 // for many RUN_IN_PLACE installs)
1158 filenames.push_back(porting::path_user +
1159 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1162 for(u32 i=0; i<filenames.size(); i++)
1164 bool r = g_settings->readConfigFile(filenames[i].c_str());
1167 configpath = filenames[i];
1172 // If no path found, use the first one (menu creates the file)
1173 if(configpath == "")
1174 configpath = filenames[0];
1177 // Initialize debug streams
1178 #define DEBUGFILE "debug.txt"
1180 std::string logfile = DEBUGFILE;
1182 std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
1184 if(cmd_args.exists("logfile"))
1185 logfile = cmd_args.get("logfile");
1187 log_remove_output(&main_dstream_no_stderr_log_out);
1188 int loglevel = g_settings->getS32("debug_log_level");
1190 if (loglevel == 0) //no logging
1192 else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
1193 log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1));
1196 debugstreams_init(false, logfile.c_str());
1198 debugstreams_init(false, NULL);
1200 infostream<<"logfile = "<<logfile<<std::endl;
1202 // Initialize random seed
1210 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1211 || cmd_args.getFlag("enable-unittests") == true)
1222 if(cmd_args.exists("port"))
1223 port = cmd_args.getU16("port");
1224 else if(g_settings->exists("port"))
1225 port = g_settings->getU16("port");
1230 std::string commanded_world = "";
1231 if(cmd_args.exists("world"))
1232 commanded_world = cmd_args.get("world");
1233 else if(cmd_args.exists("map-dir"))
1234 commanded_world = cmd_args.get("map-dir");
1235 else if(cmd_args.exists("nonopt0")) // First nameless argument
1236 commanded_world = cmd_args.get("nonopt0");
1237 else if(g_settings->exists("map-dir"))
1238 commanded_world = g_settings->get("map-dir");
1241 std::string commanded_worldname = "";
1242 if(cmd_args.exists("worldname"))
1243 commanded_worldname = cmd_args.get("worldname");
1245 // Strip world.mt from commanded_world
1247 std::string worldmt = "world.mt";
1248 if(commanded_world.size() > worldmt.size() &&
1249 commanded_world.substr(commanded_world.size()-worldmt.size())
1251 dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl;
1252 commanded_world = commanded_world.substr(
1253 0, commanded_world.size()-worldmt.size());
1257 // If a world name was specified, convert it to a path
1258 if(commanded_worldname != ""){
1259 // Get information about available worlds
1260 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1262 for(u32 i=0; i<worldspecs.size(); i++){
1263 std::string name = worldspecs[i].name;
1264 if(name == commanded_worldname){
1265 if(commanded_world != ""){
1266 dstream<<_("--worldname takes precedence over previously "
1267 "selected world.")<<std::endl;
1269 commanded_world = worldspecs[i].path;
1275 dstream<<_("World")<<" '"<<commanded_worldname<<_("' not "
1276 "available. Available worlds:")<<std::endl;
1277 print_worldspecs(worldspecs, dstream);
1283 SubgameSpec commanded_gamespec;
1284 if(cmd_args.exists("gameid")){
1285 std::string gameid = cmd_args.get("gameid");
1286 commanded_gamespec = findSubgame(gameid);
1287 if(!commanded_gamespec.isValid()){
1288 errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl;
1294 Run dedicated server if asked to or no other option
1297 bool run_dedicated_server = true;
1299 bool run_dedicated_server = cmd_args.getFlag("server");
1301 g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
1302 if(run_dedicated_server)
1304 DSTACK("Dedicated server branch");
1305 // Create time getter if built with Irrlicht
1307 g_timegetter = new SimpleTimeGetter();
1311 std::string world_path;
1312 verbosestream<<_("Determining world path")<<std::endl;
1313 bool is_legacy_world = false;
1314 // If a world was commanded, use it
1315 if(commanded_world != ""){
1316 world_path = commanded_world;
1317 infostream<<"Using commanded world path ["<<world_path<<"]"
1320 // No world was specified; try to select it automatically
1323 // Get information about available worlds
1324 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1325 // If a world name was specified, select it
1326 if(commanded_worldname != ""){
1328 for(u32 i=0; i<worldspecs.size(); i++){
1329 std::string name = worldspecs[i].name;
1330 if(name == commanded_worldname){
1331 world_path = worldspecs[i].path;
1335 if(world_path == ""){
1336 dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not "
1337 "available. Available worlds:")<<std::endl;
1338 print_worldspecs(worldspecs, dstream);
1342 // If there is only a single world, use it
1343 if(worldspecs.size() == 1){
1344 world_path = worldspecs[0].path;
1345 dstream<<_("Automatically selecting world at")<<" ["
1346 <<world_path<<"]"<<std::endl;
1347 // If there are multiple worlds, list them
1348 } else if(worldspecs.size() > 1){
1349 dstream<<_("Multiple worlds are available.")<<std::endl;
1350 dstream<<_("Please select one using --worldname <name>"
1351 " or --world <path>")<<std::endl;
1352 print_worldspecs(worldspecs, dstream);
1354 // If there are no worlds, automatically create a new one
1356 // This is the ultimate default world path
1357 world_path = porting::path_user + DIR_DELIM + "worlds" +
1358 DIR_DELIM + "world";
1359 infostream<<"Creating default world at ["
1360 <<world_path<<"]"<<std::endl;
1364 if(world_path == ""){
1365 errorstream<<"No world path specified or found."<<std::endl;
1368 verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl;
1370 // We need a gamespec.
1371 SubgameSpec gamespec;
1372 verbosestream<<_("Determining gameid/gamespec")<<std::endl;
1373 // If world doesn't exist
1374 if(!getWorldExists(world_path))
1376 // Try to take gamespec from command line
1377 if(commanded_gamespec.isValid()){
1378 gamespec = commanded_gamespec;
1379 infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl;
1381 // Otherwise we will be using "minetest"
1383 gamespec = findSubgame(g_settings->get("default_game"));
1384 infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl;
1390 std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
1391 // If commanded to use a gameid, do so
1392 if(commanded_gamespec.isValid()){
1393 gamespec = commanded_gamespec;
1394 if(commanded_gamespec.id != world_gameid){
1395 errorstream<<"WARNING: Using commanded gameid ["
1396 <<gamespec.id<<"]"<<" instead of world gameid ["
1397 <<world_gameid<<"]"<<std::endl;
1400 // If world contains an embedded game, use it;
1401 // Otherwise find world from local system.
1402 gamespec = findWorldSubgame(world_path);
1403 infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl;
1406 if(!gamespec.isValid()){
1407 errorstream<<"Subgame ["<<gamespec.id<<"] could not be found."
1411 verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl;
1414 Server server(world_path, configpath, gamespec, false);
1418 dedicated_server_loop(server, kill);
1423 #ifndef SERVER // Exclude from dedicated server build
1429 std::string address = g_settings->get("address");
1430 if(commanded_world != "")
1432 else if(cmd_args.exists("address"))
1433 address = cmd_args.get("address");
1435 std::string playername = g_settings->get("name");
1436 if(cmd_args.exists("name"))
1437 playername = cmd_args.get("name");
1439 bool skip_main_menu = cmd_args.getFlag("go");
1442 Device initialization
1445 // Resolution selection
1447 bool fullscreen = g_settings->getBool("fullscreen");
1448 u16 screenW = g_settings->getU16("screenW");
1449 u16 screenH = g_settings->getU16("screenH");
1453 bool vsync = g_settings->getBool("vsync");
1454 u16 bits = g_settings->getU16("fullscreen_bpp");
1455 u16 fsaa = g_settings->getU16("fsaa");
1459 video::E_DRIVER_TYPE driverType;
1461 std::string driverstring = g_settings->get("video_driver");
1463 if(driverstring == "null")
1464 driverType = video::EDT_NULL;
1465 else if(driverstring == "software")
1466 driverType = video::EDT_SOFTWARE;
1467 else if(driverstring == "burningsvideo")
1468 driverType = video::EDT_BURNINGSVIDEO;
1469 else if(driverstring == "direct3d8")
1470 driverType = video::EDT_DIRECT3D8;
1471 else if(driverstring == "direct3d9")
1472 driverType = video::EDT_DIRECT3D9;
1473 else if(driverstring == "opengl")
1474 driverType = video::EDT_OPENGL;
1475 #ifdef _IRR_COMPILE_WITH_OGLES1_
1476 else if(driverstring == "ogles1")
1477 driverType = video::EDT_OGLES1;
1479 #ifdef _IRR_COMPILE_WITH_OGLES2_
1480 else if(driverstring == "ogles2")
1481 driverType = video::EDT_OGLES2;
1485 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1486 "to opengl"<<std::endl;
1487 driverType = video::EDT_OPENGL;
1491 List video modes if requested
1494 MyEventReceiver receiver;
1496 if(cmd_args.getFlag("videomodes")){
1497 IrrlichtDevice *nulldevice;
1499 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1500 params.DriverType = video::EDT_NULL;
1501 params.WindowSize = core::dimension2d<u32>(640, 480);
1503 params.AntiAlias = fsaa;
1504 params.Fullscreen = false;
1505 params.Stencilbuffer = false;
1506 params.Vsync = vsync;
1507 params.EventReceiver = &receiver;
1509 nulldevice = createDeviceEx(params);
1514 dstream<<_("Available video modes (WxHxD):")<<std::endl;
1516 video::IVideoModeList *videomode_list =
1517 nulldevice->getVideoModeList();
1519 if(videomode_list == 0){
1524 s32 videomode_count = videomode_list->getVideoModeCount();
1525 core::dimension2d<u32> videomode_res;
1526 s32 videomode_depth;
1527 for (s32 i = 0; i < videomode_count; ++i){
1528 videomode_res = videomode_list->getVideoModeResolution(i);
1529 videomode_depth = videomode_list->getVideoModeDepth(i);
1530 dstream<<videomode_res.Width<<"x"<<videomode_res.Height
1531 <<"x"<<videomode_depth<<std::endl;
1534 dstream<<_("Active video mode (WxHxD):")<<std::endl;
1535 videomode_res = videomode_list->getDesktopResolution();
1536 videomode_depth = videomode_list->getDesktopDepth();
1537 dstream<<videomode_res.Width<<"x"<<videomode_res.Height
1538 <<"x"<<videomode_depth<<std::endl;
1546 Create device and exit if creation failed
1549 IrrlichtDevice *device;
1551 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1552 params.DriverType = driverType;
1553 params.WindowSize = core::dimension2d<u32>(screenW, screenH);
1555 params.AntiAlias = fsaa;
1556 params.Fullscreen = fullscreen;
1557 params.Stencilbuffer = false;
1558 params.Vsync = vsync;
1559 params.EventReceiver = &receiver;
1561 device = createDeviceEx(params);
1564 return 1; // could not create selected driver.
1567 Continue initialization
1570 video::IVideoDriver* driver = device->getVideoDriver();
1573 This changes the minimum allowed number of vertices in a VBO.
1576 //driver->setMinHardwareBufferVertexCount(50);
1578 // Create time getter
1579 g_timegetter = new IrrlichtTimeGetter(device);
1581 // Create game callback for menus
1582 g_gamecallback = new MainGameCallback(device);
1585 Speed tests (done after irrlicht is loaded to get timer)
1587 if(cmd_args.getFlag("speedtests"))
1589 dstream<<"Running speed tests"<<std::endl;
1595 device->setResizable(true);
1597 bool random_input = g_settings->getBool("random_input")
1598 || cmd_args.getFlag("random-input");
1599 InputHandler *input = NULL;
1601 input = new RandomInputHandler();
1603 input = new RealInputHandler(device, &receiver);
1605 scene::ISceneManager* smgr = device->getSceneManager();
1607 guienv = device->getGUIEnvironment();
1608 gui::IGUISkin* skin = guienv->getSkin();
1610 std::string font_path = g_settings->get("font_path");
1611 u16 font_size = g_settings->getU16("font_size");
1612 gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size);
1614 gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1617 skin->setFont(font);
1619 errorstream<<"WARNING: Font file was not found."
1620 " Using default font."<<std::endl;
1621 // If font was not found, this will get us one
1622 font = skin->getFont();
1625 u32 text_height = font->getDimension(L"Hello, world!").Height;
1626 infostream<<"text_height="<<text_height<<std::endl;
1628 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1629 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1630 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1631 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1632 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1633 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1634 skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1635 skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1637 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
1638 // Irrlicht 1.8 input colours
1639 skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128));
1640 skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
1644 // Create the menu clouds
1645 if (!g_menucloudsmgr)
1646 g_menucloudsmgr = smgr->createNewSceneManager();
1648 g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
1649 g_menucloudsmgr, -1, rand(), 100);
1650 g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255));
1651 scene::ICameraSceneNode* camera;
1652 camera = g_menucloudsmgr->addCameraSceneNode(0,
1653 v3f(0,0,0), v3f(0, 60, 100));
1654 camera->setFarValue(10000);
1660 ChatBackend chat_backend;
1663 If an error occurs, this is set to something and the
1664 menu-game loop is restarted. It is then displayed before
1667 std::wstring error_message = L"";
1669 // The password entered during the menu screen,
1670 std::string password;
1672 bool first_loop = true;
1677 while(device->run() && kill == false)
1679 // Set the window caption
1680 wchar_t* text = wgettext("Main Menu");
1681 device->setWindowCaption((std::wstring(L"Minetest [")+text+L"]").c_str());
1684 // This is used for catching disconnects
1689 Clear everything from the GUIEnvironment
1694 We need some kind of a root node to be able to add
1695 custom gui elements directly on the screen.
1696 Otherwise they won't be automatically drawn.
1698 guiroot = guienv->addStaticText(L"",
1699 core::rect<s32>(0, 0, 10000, 10000));
1701 SubgameSpec gamespec;
1702 WorldSpec worldspec;
1703 bool simple_singleplayer_mode = false;
1705 // These are set up based on the menu and other things
1706 std::string current_playername = "inv£lid";
1707 std::string current_password = "";
1708 std::string current_address = "does-not-exist";
1709 int current_port = 0;
1712 Out-of-game menu loop.
1714 Loop quits when menu returns proper parameters.
1716 while(kill == false)
1718 // If skip_main_menu, only go through here once
1719 if(skip_main_menu && !first_loop){
1725 // Cursor can be non-visible when coming from the game
1726 device->getCursorControl()->setVisible(true);
1727 // Some stuff are left to scene manager when coming from the game
1731 // Initialize menu data
1732 MainMenuData menudata;
1733 if(g_settings->exists("selected_mainmenu_tab"))
1734 menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
1735 if(g_settings->exists("selected_serverlist"))
1736 menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
1737 if(g_settings->exists("selected_mainmenu_game")){
1738 menudata.selected_game = g_settings->get("selected_mainmenu_game");
1739 menudata.selected_game_name = findSubgame(menudata.selected_game).name;
1741 menudata.address = narrow_to_wide(address);
1742 menudata.name = narrow_to_wide(playername);
1743 menudata.port = narrow_to_wide(itos(port));
1744 if(cmd_args.exists("password"))
1745 menudata.password = narrow_to_wide(cmd_args.get("password"));
1746 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1747 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1748 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1749 menudata.opaque_water = g_settings->getBool("opaque_water");
1750 menudata.mip_map = g_settings->getBool("mip_map");
1751 menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
1752 menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
1753 menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
1754 menudata.enable_shaders = g_settings->getS32("enable_shaders");
1755 menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
1756 menudata.enable_particles = g_settings->getBool("enable_particles");
1757 menudata.liquid_finite = g_settings->getBool("liquid_finite");
1758 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
1759 menudata.creative_mode = g_settings->getBool("creative_mode");
1760 menudata.enable_damage = g_settings->getBool("enable_damage");
1761 menudata.enable_public = g_settings->getBool("server_announce");
1762 // Default to selecting nothing
1763 menudata.selected_world = -1;
1764 // Get world listing for the menu
1765 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1766 // If there is only one world, select it
1767 if(worldspecs.size() == 1){
1768 menudata.selected_world = 0;
1770 // Otherwise try to select according to selected_world_path
1771 else if(g_settings->exists("selected_world_path")){
1772 std::string trypath = g_settings->get("selected_world_path");
1773 for(u32 i=0; i<worldspecs.size(); i++){
1774 if(worldspecs[i].path == trypath){
1775 menudata.selected_world = i;
1780 // If a world was commanded, append and select it
1781 if(commanded_world != ""){
1782 std::string gameid = getWorldGameId(commanded_world, true);
1783 std::string name = _("[--world parameter]");
1785 gameid = g_settings->get("default_game");
1788 WorldSpec spec(commanded_world, name, gameid);
1789 worldspecs.push_back(spec);
1790 menudata.selected_world = worldspecs.size()-1;
1792 // Copy worldspecs to menu
1793 menudata.worlds = worldspecs;
1795 menudata.games = getAvailableGames();
1796 // If selected game doesn't exist, take first from list
1797 if(findSubgame(menudata.selected_game).id == "" &&
1798 !menudata.games.empty()){
1799 menudata.selected_game = menudata.games[0].id;
1801 const SubgameSpec *menugame = getMenuGame(menudata);
1803 MenuTextures menutextures;
1804 menutextures.update(driver, menugame, menudata.selected_tab);
1806 if(skip_main_menu == false)
1808 video::IVideoDriver* driver = device->getVideoDriver();
1809 float fps_max = g_settings->getFloat("fps_max");
1810 infostream<<"Waiting for other menus"<<std::endl;
1811 while(device->run() && kill == false)
1815 driver->beginScene(true, true,
1816 video::SColor(255,128,128,128));
1817 drawMenuBackground(driver, menutextures);
1820 // On some computers framerate doesn't seem to be
1821 // automatically limited
1824 infostream<<"Waited for other menus"<<std::endl;
1827 new GUIMainMenu(guienv, guiroot, -1,
1828 &g_menumgr, &menudata, g_gamecallback);
1829 menu->allowFocusRemoval(true);
1831 if(error_message != L"")
1833 verbosestream<<"error_message = "
1834 <<wide_to_narrow(error_message)<<std::endl;
1836 GUIMessageMenu *menu2 =
1837 new GUIMessageMenu(guienv, guiroot, -1,
1838 &g_menumgr, error_message.c_str());
1840 error_message = L"";
1843 // Time is in milliseconds, for clouds
1844 u32 lasttime = device->getTimer()->getTime();
1846 MenuMusicFetcher soundfetcher;
1847 ISoundManager *sound = NULL;
1849 sound = createOpenALSoundManager(&soundfetcher);
1852 sound = &dummySoundManager;
1853 SimpleSoundSpec spec;
1854 spec.name = "main_menu";
1856 s32 handle = sound->playSound(spec, true);
1858 infostream<<"Created main menu"<<std::endl;
1860 while(device->run() && kill == false)
1862 if(menu->getStatus() == true)
1865 // Game can be selected in the menu
1866 menugame = getMenuGame(menudata);
1867 menutextures.update(driver, menugame, menu->getTab());
1868 // Clouds for the main menu
1869 bool cloud_menu_background = g_settings->getBool("menu_clouds");
1871 // If game has regular background and no overlay, don't use clouds
1872 if(cloud_menu_background && menutextures.background &&
1873 !menutextures.overlay){
1874 cloud_menu_background = false;
1876 // If game game has overlay and no regular background, always draw clouds
1877 else if(menutextures.overlay && !menutextures.background){
1878 cloud_menu_background = true;
1882 // Time calc for the clouds
1883 f32 dtime=0; // in seconds
1884 if (cloud_menu_background) {
1885 u32 time = device->getTimer()->getTime();
1887 dtime = (time - lasttime) / 1000.0;
1893 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1894 driver->beginScene(true, true, video::SColor(255,140,186,250));
1896 if (cloud_menu_background) {
1897 // *3 otherwise the clouds would move very slowly
1898 g_menuclouds->step(dtime*3);
1899 g_menuclouds->render();
1900 g_menucloudsmgr->drawAll();
1901 drawMenuOverlay(driver, menutextures);
1902 drawMenuHeader(driver, menutextures);
1903 drawMenuFooter(driver, menutextures);
1905 drawMenuBackground(driver, menutextures);
1906 drawMenuHeader(driver, menutextures);
1907 drawMenuFooter(driver, menutextures);
1914 // On some computers framerate doesn't seem to be
1915 // automatically limited
1916 if (cloud_menu_background) {
1917 // Time of frame without fps limit
1920 // not using getRealTime is necessary for wine
1921 u32 time = device->getTimer()->getTime();
1923 busytime_u32 = time - lasttime;
1926 busytime = busytime_u32 / 1000.0;
1929 u32 frametime_min = 1000./fps_max;
1931 if(busytime_u32 < frametime_min) {
1932 u32 sleeptime = frametime_min - busytime_u32;
1933 device->sleep(sleeptime);
1939 sound->stopSound(handle);
1940 if(sound != &dummySoundManager){
1945 // Save controls status
1946 menu->readInput(&menudata);
1948 infostream<<"Dropping main menu"<<std::endl;
1953 playername = wide_to_narrow(menudata.name);
1954 if (playername == "")
1955 playername = std::string("Guest") + itos(myrand_range(1000,9999));
1956 password = translatePassword(playername, menudata.password);
1957 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1959 address = wide_to_narrow(menudata.address);
1960 int newport = stoi(wide_to_narrow(menudata.port));
1963 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1965 g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
1966 g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
1967 g_settings->set("selected_mainmenu_game", menudata.selected_game);
1968 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1969 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1970 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1971 g_settings->set("opaque_water", itos(menudata.opaque_water));
1973 g_settings->set("mip_map", itos(menudata.mip_map));
1974 g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
1975 g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
1976 g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
1978 g_settings->setS32("enable_shaders", menudata.enable_shaders);
1979 g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
1980 g_settings->set("enable_particles", itos(menudata.enable_particles));
1981 g_settings->set("liquid_finite", itos(menudata.liquid_finite));
1983 g_settings->set("creative_mode", itos(menudata.creative_mode));
1984 g_settings->set("enable_damage", itos(menudata.enable_damage));
1985 g_settings->set("server_announce", itos(menudata.enable_public));
1986 g_settings->set("name", playername);
1987 g_settings->set("address", address);
1988 g_settings->set("port", itos(port));
1989 if(menudata.selected_world != -1)
1990 g_settings->set("selected_world_path",
1991 worldspecs[menudata.selected_world].path);
1993 // Break out of menu-game loop to shut down cleanly
1994 if(device->run() == false || kill == true)
1997 current_playername = playername;
1998 current_password = password;
1999 current_address = address;
2000 current_port = port;
2002 // If using simple singleplayer mode, override
2003 if(simple_singleplayer_mode){
2004 current_playername = "singleplayer";
2005 current_password = "";
2006 current_address = "";
2007 current_port = 30011;
2009 else if (address != "")
2011 ServerListSpec server;
2012 server["name"] = menudata.servername;
2013 server["address"] = wide_to_narrow(menudata.address);
2014 server["port"] = wide_to_narrow(menudata.port);
2015 server["description"] = menudata.serverdescription;
2016 ServerList::insert(server);
2019 // Set world path to selected one
2020 if(menudata.selected_world != -1){
2021 worldspec = worldspecs[menudata.selected_world];
2022 infostream<<"Selected world: "<<worldspec.name
2023 <<" ["<<worldspec.path<<"]"<<std::endl;
2026 // Only refresh if so requested
2027 if(menudata.only_refresh){
2028 infostream<<"Refreshing menu"<<std::endl;
2032 // Create new world if requested
2033 if(menudata.create_world_name != L"")
2035 std::string path = porting::path_user + DIR_DELIM
2036 "worlds" + DIR_DELIM
2037 + wide_to_narrow(menudata.create_world_name);
2038 // Create world if it doesn't exist
2039 if(!initializeWorld(path, menudata.create_world_gameid)){
2040 error_message = wgettext("Failed to initialize world");
2041 errorstream<<wide_to_narrow(error_message)<<std::endl;
2044 g_settings->set("selected_world_path", path);
2045 g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
2050 if(current_address == "")
2052 if(menudata.selected_world == -1){
2053 error_message = wgettext("No world selected and no address "
2054 "provided. Nothing to do.");
2055 errorstream<<wide_to_narrow(error_message)<<std::endl;
2058 // Load gamespec for required game
2059 gamespec = findWorldSubgame(worldspec.path);
2060 if(!gamespec.isValid() && !commanded_gamespec.isValid()){
2061 error_message = wgettext("Could not find or load game \"")
2062 + narrow_to_wide(worldspec.gameid) + L"\"";
2063 errorstream<<wide_to_narrow(error_message)<<std::endl;
2066 if(commanded_gamespec.isValid() &&
2067 commanded_gamespec.id != worldspec.gameid){
2068 errorstream<<"WARNING: Overriding gamespec from \""
2069 <<worldspec.gameid<<"\" to \""
2070 <<commanded_gamespec.id<<"\""<<std::endl;
2071 gamespec = commanded_gamespec;
2074 if(!gamespec.isValid()){
2075 error_message = wgettext("Invalid gamespec.");
2076 error_message += L" (world_gameid="
2077 +narrow_to_wide(worldspec.gameid)+L")";
2078 errorstream<<wide_to_narrow(error_message)<<std::endl;
2087 // Break out of menu-game loop to shut down cleanly
2088 if(device->run() == false || kill == true)
2109 simple_singleplayer_mode
2114 catch(con::PeerNotFoundException &e)
2116 error_message = wgettext("Connection error (timed out?)");
2117 errorstream<<wide_to_narrow(error_message)<<std::endl;
2120 catch(std::exception &e)
2122 std::string narrow_message = "Some exception: \"";
2123 narrow_message += e.what();
2124 narrow_message += "\"";
2125 errorstream<<narrow_message<<std::endl;
2126 error_message = narrow_to_wide(narrow_message);
2130 // If no main menu, show error and exit
2133 if(error_message != L""){
2134 verbosestream<<"error_message = "
2135 <<wide_to_narrow(error_message)<<std::endl;
2143 g_menuclouds->drop();
2144 g_menucloudsmgr->drop();
2149 In the end, delete the Irrlicht device.
2159 // Update configuration file
2160 if(configpath != "")
2161 g_settings->updateConfigFile(configpath.c_str());
2163 // Print modified quicktune values
2165 bool header_printed = false;
2166 std::vector<std::string> names = getQuicktuneNames();
2167 for(u32 i=0; i<names.size(); i++){
2168 QuicktuneValue val = getQuicktuneValue(names[i]);
2171 if(!header_printed){
2172 dstream<<"Modified quicktune values:"<<std::endl;
2173 header_printed = true;
2175 dstream<<names[i]<<" = "<<val.getString()<<std::endl;
2179 END_DEBUG_EXCEPTION_HANDLER(errorstream)
2181 debugstreams_deinit();