Dont drop fonts with ENABLE_FREETYPE=0
[oweals/minetest.git] / src / main.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef NDEBUG
21         /*#ifdef _WIN32
22                 #pragma message ("Disabling unit tests")
23         #else
24                 #warning "Disabling unit tests"
25         #endif*/
26         // Disable unit tests
27         #define ENABLE_TESTS 0
28 #else
29         // Enable unit tests
30         #define ENABLE_TESTS 1
31 #endif
32
33 #ifdef _MSC_VER
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")
38 #endif
39         #pragma comment(lib, "zlibwapi.lib")
40         #pragma comment(lib, "Shell32.lib")
41 #endif
42
43 #include "irrlicht.h" // createDevice
44
45 #include "main.h"
46 #include "mainmenumanager.h"
47 #include <iostream>
48 #include <fstream>
49 #include <locale.h>
50 #include "irrlichttypes_extrabloated.h"
51 #include "debug.h"
52 #include "test.h"
53 #include "clouds.h"
54 #include "server.h"
55 #include "constants.h"
56 #include "porting.h"
57 #include "gettime.h"
58 #include "guiMessageMenu.h"
59 #include "filesys.h"
60 #include "config.h"
61 #include "guiMainMenu.h"
62 #include "game.h"
63 #include "keycode.h"
64 #include "tile.h"
65 #include "chat.h"
66 #include "defaultsettings.h"
67 #include "gettext.h"
68 #include "settings.h"
69 #include "profiler.h"
70 #include "log.h"
71 #include "mods.h"
72 #if USE_FREETYPE
73 #include "xCGUITTFont.h"
74 #endif
75 #include "util/string.h"
76 #include "subgame.h"
77 #include "quicktune.h"
78 #include "serverlist.h"
79 #include "sound.h"
80 #include "sound_openal.h"
81
82 /*
83         Settings.
84         These are loaded from the config file.
85 */
86 Settings main_settings;
87 Settings *g_settings = &main_settings;
88
89 // Global profiler
90 Profiler main_profiler;
91 Profiler *g_profiler = &main_profiler;
92
93 // Menu clouds are created later
94 Clouds *g_menuclouds = 0;
95 irr::scene::ISceneManager *g_menucloudsmgr = 0;
96
97 /*
98         Debug streams
99 */
100
101 // Connection
102 std::ostream *dout_con_ptr = &dummyout;
103 std::ostream *derr_con_ptr = &verbosestream;
104
105 // Server
106 std::ostream *dout_server_ptr = &infostream;
107 std::ostream *derr_server_ptr = &errorstream;
108
109 // Client
110 std::ostream *dout_client_ptr = &infostream;
111 std::ostream *derr_client_ptr = &errorstream;
112
113 #ifndef SERVER
114 /*
115         Random stuff
116 */
117
118 /* mainmenumanager.h */
119
120 gui::IGUIEnvironment* guienv = NULL;
121 gui::IGUIStaticText *guiroot = NULL;
122 MainMenuManager g_menumgr;
123
124 bool noMenuActive()
125 {
126         return (g_menumgr.menuCount() == 0);
127 }
128
129 // Passed to menus to allow disconnecting and exiting
130 MainGameCallback *g_gamecallback = NULL;
131 #endif
132
133 /*
134         gettime.h implementation
135 */
136
137 #ifdef SERVER
138
139 u32 getTimeMs()
140 {
141         /* Use imprecise system calls directly (from porting.h) */
142         return porting::getTime(PRECISION_MILLI);
143 }
144
145 u32 getTime(TimePrecision prec)
146 {
147         return porting::getTime(prec);
148 }
149
150 #else
151
152 // A small helper class
153 class TimeGetter
154 {
155 public:
156         virtual u32 getTime(TimePrecision prec) = 0;
157 };
158
159 // A precise irrlicht one
160 class IrrlichtTimeGetter: public TimeGetter
161 {
162 public:
163         IrrlichtTimeGetter(IrrlichtDevice *device):
164                 m_device(device)
165         {}
166         u32 getTime(TimePrecision prec)
167         {
168                 if (prec == PRECISION_MILLI) {
169                         if(m_device == NULL)
170                                 return 0;
171                         return m_device->getTimer()->getRealTime();
172                 } else {
173                         return porting::getTime(prec);
174                 }
175         }
176 private:
177         IrrlichtDevice *m_device;
178 };
179 // Not so precise one which works without irrlicht
180 class SimpleTimeGetter: public TimeGetter
181 {
182 public:
183         u32 getTime(TimePrecision prec)
184         {
185                 return porting::getTime(prec);
186         }
187 };
188
189 // A pointer to a global instance of the time getter
190 // TODO: why?
191 TimeGetter *g_timegetter = NULL;
192
193 u32 getTimeMs()
194 {
195         if(g_timegetter == NULL)
196                 return 0;
197         return g_timegetter->getTime(PRECISION_MILLI);
198 }
199
200 u32 getTime(TimePrecision prec) {
201         if (g_timegetter == NULL)
202                 return 0;
203         return g_timegetter->getTime(prec);
204 }
205 #endif
206
207 //Client side main menu music fetcher
208 #ifndef SERVER
209 class MenuMusicFetcher: public OnDemandSoundFetcher
210 {
211         std::set<std::string> m_fetched;
212 public:
213
214         void fetchSounds(const std::string &name,
215                         std::set<std::string> &dst_paths,
216                         std::set<std::string> &dst_datas)
217         {
218                 if(m_fetched.count(name))
219                         return;
220                 m_fetched.insert(name);
221                 std::string base;
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");
246                 }
247 };
248 #endif
249
250 class StderrLogOutput: public ILogOutput
251 {
252 public:
253         /* line: Full line with timestamp, level and thread */
254         void printLog(const std::string &line)
255         {
256                 std::cerr<<line<<std::endl;
257         }
258 } main_stderr_log_out;
259
260 class DstreamNoStderrLogOutput: public ILogOutput
261 {
262 public:
263         /* line: Full line with timestamp, level and thread */
264         void printLog(const std::string &line)
265         {
266                 dstream_no_stderr<<line<<std::endl;
267         }
268 } main_dstream_no_stderr_log_out;
269
270 #ifndef SERVER
271
272 /*
273         Event handler for Irrlicht
274
275         NOTE: Everything possible should be moved out from here,
276               probably to InputHandler and the_game
277 */
278
279 class MyEventReceiver : public IEventReceiver
280 {
281 public:
282         // This is the one method that we have to implement
283         virtual bool OnEvent(const SEvent& event)
284         {
285                 /*
286                         React to nothing here if a menu is active
287                 */
288                 if(noMenuActive() == false)
289                 {
290                         return false;
291                 }
292
293                 // Remember whether each key is down or up
294                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
295                 {
296                         if(event.KeyInput.PressedDown) {
297                                 keyIsDown.set(event.KeyInput);
298                                 keyWasDown.set(event.KeyInput);
299                         } else {
300                                 keyIsDown.unset(event.KeyInput);
301                         }
302                 }
303
304                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
305                 {
306                         if(noMenuActive() == false)
307                         {
308                                 left_active = false;
309                                 middle_active = false;
310                                 right_active = false;
311                         }
312                         else
313                         {
314                                 left_active = event.MouseInput.isLeftPressed();
315                                 middle_active = event.MouseInput.isMiddlePressed();
316                                 right_active = event.MouseInput.isRightPressed();
317
318                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
319                                 {
320                                         leftclicked = true;
321                                 }
322                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
323                                 {
324                                         rightclicked = true;
325                                 }
326                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
327                                 {
328                                         leftreleased = true;
329                                 }
330                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
331                                 {
332                                         rightreleased = true;
333                                 }
334                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
335                                 {
336                                         mouse_wheel += event.MouseInput.Wheel;
337                                 }
338                         }
339                 }
340
341                 return false;
342         }
343
344         bool IsKeyDown(const KeyPress &keyCode) const
345         {
346                 return keyIsDown[keyCode];
347         }
348         
349         // Checks whether a key was down and resets the state
350         bool WasKeyDown(const KeyPress &keyCode)
351         {
352                 bool b = keyWasDown[keyCode];
353                 if (b)
354                         keyWasDown.unset(keyCode);
355                 return b;
356         }
357
358         s32 getMouseWheel()
359         {
360                 s32 a = mouse_wheel;
361                 mouse_wheel = 0;
362                 return a;
363         }
364
365         void clearInput()
366         {
367                 keyIsDown.clear();
368                 keyWasDown.clear();
369
370                 leftclicked = false;
371                 rightclicked = false;
372                 leftreleased = false;
373                 rightreleased = false;
374
375                 left_active = false;
376                 middle_active = false;
377                 right_active = false;
378
379                 mouse_wheel = 0;
380         }
381
382         MyEventReceiver()
383         {
384                 clearInput();
385         }
386
387         bool leftclicked;
388         bool rightclicked;
389         bool leftreleased;
390         bool rightreleased;
391
392         bool left_active;
393         bool middle_active;
394         bool right_active;
395
396         s32 mouse_wheel;
397
398 private:
399         IrrlichtDevice *m_device;
400         
401         // The current state of keys
402         KeyList keyIsDown;
403         // Whether a key has been pressed or not
404         KeyList keyWasDown;
405 };
406
407 /*
408         Separated input handler
409 */
410
411 class RealInputHandler : public InputHandler
412 {
413 public:
414         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
415                 m_device(device),
416                 m_receiver(receiver)
417         {
418         }
419         virtual bool isKeyDown(const KeyPress &keyCode)
420         {
421                 return m_receiver->IsKeyDown(keyCode);
422         }
423         virtual bool wasKeyDown(const KeyPress &keyCode)
424         {
425                 return m_receiver->WasKeyDown(keyCode);
426         }
427         virtual v2s32 getMousePos()
428         {
429                 return m_device->getCursorControl()->getPosition();
430         }
431         virtual void setMousePos(s32 x, s32 y)
432         {
433                 m_device->getCursorControl()->setPosition(x, y);
434         }
435
436         virtual bool getLeftState()
437         {
438                 return m_receiver->left_active;
439         }
440         virtual bool getRightState()
441         {
442                 return m_receiver->right_active;
443         }
444         
445         virtual bool getLeftClicked()
446         {
447                 return m_receiver->leftclicked;
448         }
449         virtual bool getRightClicked()
450         {
451                 return m_receiver->rightclicked;
452         }
453         virtual void resetLeftClicked()
454         {
455                 m_receiver->leftclicked = false;
456         }
457         virtual void resetRightClicked()
458         {
459                 m_receiver->rightclicked = false;
460         }
461
462         virtual bool getLeftReleased()
463         {
464                 return m_receiver->leftreleased;
465         }
466         virtual bool getRightReleased()
467         {
468                 return m_receiver->rightreleased;
469         }
470         virtual void resetLeftReleased()
471         {
472                 m_receiver->leftreleased = false;
473         }
474         virtual void resetRightReleased()
475         {
476                 m_receiver->rightreleased = false;
477         }
478
479         virtual s32 getMouseWheel()
480         {
481                 return m_receiver->getMouseWheel();
482         }
483
484         void clear()
485         {
486                 m_receiver->clearInput();
487         }
488 private:
489         IrrlichtDevice *m_device;
490         MyEventReceiver *m_receiver;
491 };
492
493 class RandomInputHandler : public InputHandler
494 {
495 public:
496         RandomInputHandler()
497         {
498                 leftdown = false;
499                 rightdown = false;
500                 leftclicked = false;
501                 rightclicked = false;
502                 leftreleased = false;
503                 rightreleased = false;
504                 keydown.clear();
505         }
506         virtual bool isKeyDown(const KeyPress &keyCode)
507         {
508                 return keydown[keyCode];
509         }
510         virtual bool wasKeyDown(const KeyPress &keyCode)
511         {
512                 return false;
513         }
514         virtual v2s32 getMousePos()
515         {
516                 return mousepos;
517         }
518         virtual void setMousePos(s32 x, s32 y)
519         {
520                 mousepos = v2s32(x,y);
521         }
522
523         virtual bool getLeftState()
524         {
525                 return leftdown;
526         }
527         virtual bool getRightState()
528         {
529                 return rightdown;
530         }
531
532         virtual bool getLeftClicked()
533         {
534                 return leftclicked;
535         }
536         virtual bool getRightClicked()
537         {
538                 return rightclicked;
539         }
540         virtual void resetLeftClicked()
541         {
542                 leftclicked = false;
543         }
544         virtual void resetRightClicked()
545         {
546                 rightclicked = false;
547         }
548
549         virtual bool getLeftReleased()
550         {
551                 return leftreleased;
552         }
553         virtual bool getRightReleased()
554         {
555                 return rightreleased;
556         }
557         virtual void resetLeftReleased()
558         {
559                 leftreleased = false;
560         }
561         virtual void resetRightReleased()
562         {
563                 rightreleased = false;
564         }
565
566         virtual s32 getMouseWheel()
567         {
568                 return 0;
569         }
570
571         virtual void step(float dtime)
572         {
573                 {
574                         static float counter1 = 0;
575                         counter1 -= dtime;
576                         if(counter1 < 0.0)
577                         {
578                                 counter1 = 0.1*Rand(1, 40);
579                                 keydown.toggle(getKeySetting("keymap_jump"));
580                         }
581                 }
582                 {
583                         static float counter1 = 0;
584                         counter1 -= dtime;
585                         if(counter1 < 0.0)
586                         {
587                                 counter1 = 0.1*Rand(1, 40);
588                                 keydown.toggle(getKeySetting("keymap_special1"));
589                         }
590                 }
591                 {
592                         static float counter1 = 0;
593                         counter1 -= dtime;
594                         if(counter1 < 0.0)
595                         {
596                                 counter1 = 0.1*Rand(1, 40);
597                                 keydown.toggle(getKeySetting("keymap_forward"));
598                         }
599                 }
600                 {
601                         static float counter1 = 0;
602                         counter1 -= dtime;
603                         if(counter1 < 0.0)
604                         {
605                                 counter1 = 0.1*Rand(1, 40);
606                                 keydown.toggle(getKeySetting("keymap_left"));
607                         }
608                 }
609                 {
610                         static float counter1 = 0;
611                         counter1 -= dtime;
612                         if(counter1 < 0.0)
613                         {
614                                 counter1 = 0.1*Rand(1, 20);
615                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
616                         }
617                 }
618                 {
619                         static float counter1 = 0;
620                         counter1 -= dtime;
621                         if(counter1 < 0.0)
622                         {
623                                 counter1 = 0.1*Rand(1, 30);
624                                 leftdown = !leftdown;
625                                 if(leftdown)
626                                         leftclicked = true;
627                                 if(!leftdown)
628                                         leftreleased = true;
629                         }
630                 }
631                 {
632                         static float counter1 = 0;
633                         counter1 -= dtime;
634                         if(counter1 < 0.0)
635                         {
636                                 counter1 = 0.1*Rand(1, 15);
637                                 rightdown = !rightdown;
638                                 if(rightdown)
639                                         rightclicked = true;
640                                 if(!rightdown)
641                                         rightreleased = true;
642                         }
643                 }
644                 mousepos += mousespeed;
645         }
646
647         s32 Rand(s32 min, s32 max)
648         {
649                 return (myrand()%(max-min+1))+min;
650         }
651 private:
652         KeyList keydown;
653         v2s32 mousepos;
654         v2s32 mousespeed;
655         bool leftdown;
656         bool rightdown;
657         bool leftclicked;
658         bool rightclicked;
659         bool leftreleased;
660         bool rightreleased;
661 };
662
663 struct MenuTextures
664 {
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;
671
672         MenuTextures():
673                 global_textures(false),
674                 background(NULL),
675                 overlay(NULL),
676                 header(NULL),
677                 footer(NULL)
678         {}
679
680         static video::ITexture* getMenuTexture(const std::string &tname,
681                         video::IVideoDriver* driver, const SubgameSpec *spec)
682         {
683                 if(spec){
684                         std::string path;
685                         // eg. minetest_menu_background.png (for texture packs)
686                         std::string pack_tname = spec->id + "_menu_" + tname + ".png";
687                         path = getTexturePath(pack_tname);
688                         if(path != "")
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");
692                         if(path != "")
693                                 return driver->getTexture(path.c_str());
694                 } else {
695                         std::string path;
696                         // eg. menu_background.png
697                         std::string pack_tname = "menu_" + tname + ".png";
698                         path = getTexturePath(pack_tname);
699                         if(path != "")
700                                 return driver->getTexture(path.c_str());
701                 }
702                 return NULL;
703         }
704
705         void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
706         {
707                 if(tab == TAB_SINGLEPLAYER){
708                         if(spec->id == current_gameid)
709                                 return;
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);
716                 } else {
717                         if(global_textures)
718                                 return;
719                         current_gameid = "";
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);
725                 }
726         }
727 };
728
729 void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
730 {
731         v2u32 screensize = driver->getScreenSize();
732         video::ITexture *texture = menutextures.background;
733
734         /* If no texture, draw background of solid color */
735         if(!texture){
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);
739                 return;
740         }
741
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),
747                 NULL, NULL, true);
748 }
749
750 void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
751 {
752         v2u32 screensize = driver->getScreenSize();
753         video::ITexture *texture = menutextures.overlay;
754
755         /* If no texture, draw nothing */
756         if(!texture)
757                 return;
758
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),
764                 NULL, NULL, true);
765 }
766
767 void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
768 {
769         core::dimension2d<u32> screensize = driver->getScreenSize();
770         video::ITexture *texture = menutextures.header;
771
772         /* If no texture, draw nothing */
773         if(!texture)
774                 return;
775
776         f32 mult = (((f32)screensize.Width / 2)) /
777                 ((f32)texture->getOriginalSize().Width);
778
779         v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
780                         ((f32)texture->getOriginalSize().Height) * mult);
781
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);
788
789                 video::SColor bgcolor(255,50,50,50);
790
791                 driver->draw2DImage(texture, splashrect,
792                         core::rect<s32>(core::position2d<s32>(0,0),
793                         core::dimension2di(texture->getSize())),
794                         NULL, NULL, true);
795         }
796 }
797
798 void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
799 {
800         core::dimension2d<u32> screensize = driver->getScreenSize();
801         video::ITexture *texture = menutextures.footer;
802
803         /* If no texture, draw nothing */
804         if(!texture)
805                 return;
806
807         f32 mult = (((f32)screensize.Width)) /
808                 ((f32)texture->getOriginalSize().Width);
809
810         v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
811                         ((f32)texture->getOriginalSize().Height) * mult);
812
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);
819
820                 driver->draw2DImage(texture, rect,
821                         core::rect<s32>(core::position2d<s32>(0,0),
822                         core::dimension2di(texture->getSize())),
823                         NULL, NULL, true);
824         }
825 }
826
827 static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
828 {
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];
832                 }
833         }
834         return NULL;
835 }
836
837 #endif // !SERVER
838
839 // These are defined global so that they're not optimized too much.
840 // Can't change them to volatile.
841 s16 temp16;
842 f32 tempf;
843 v3f tempv3f1;
844 v3f tempv3f2;
845 std::string tempstring;
846 std::string tempstring2;
847
848 void SpeedTests()
849 {
850         {
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++)
855                 {
856                         tempstring = "";
857                         tempstring2 = "";
858                         const u32 ii = 10;
859                         for(u32 i=0; i<ii; i++){
860                                 tempstring2 += "asd";
861                         }
862                         for(u32 i=0; i<ii+1; i++){
863                                 tempstring += "asd";
864                                 if(tempstring == tempstring2)
865                                         break;
866                         }
867                 }
868         }
869         
870         infostream<<"All of the following tests should take around 100ms each."
871                         <<std::endl;
872
873         {
874                 TimeTaker timer("Testing floating-point conversion speed");
875                 tempf = 0.001;
876                 for(u32 i=0; i<4000000; i++){
877                         temp16 += tempf;
878                         tempf += 0.001;
879                 }
880         }
881         
882         {
883                 TimeTaker timer("Testing floating-point vector speed");
884
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);
890                 }
891         }
892
893         {
894                 TimeTaker timer("Testing std::map speed");
895                 
896                 std::map<v2s16, f32> map1;
897                 tempf = -324;
898                 const s16 ii=300;
899                 for(s16 y=0; y<ii; y++){
900                         for(s16 x=0; x<ii; x++){
901                                 map1[v2s16(x,y)] =  tempf;
902                                 tempf += 1;
903                         }
904                 }
905                 for(s16 y=ii-1; y>=0; y--){
906                         for(s16 x=0; x<ii; x++){
907                                 tempf = map1[v2s16(x,y)];
908                         }
909                 }
910         }
911
912         {
913                 infostream<<"Around 5000/ms should do well here."<<std::endl;
914                 TimeTaker timer("Testing mutex speed");
915                 
916                 JMutex m;
917                 m.Init();
918                 u32 n = 0;
919                 u32 i = 0;
920                 do{
921                         n += 10000;
922                         for(; i<n; i++){
923                                 m.Lock();
924                                 m.Unlock();
925                         }
926                 }
927                 // Do at least 10ms
928                 while(timer.getTimerTime() < 10);
929
930                 u32 dtime = timer.stop();
931                 u32 per_ms = n / dtime;
932                 infostream<<"Done. "<<dtime<<"ms, "
933                                 <<per_ms<<"/ms"<<std::endl;
934         }
935 }
936
937 static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
938                 std::ostream &os)
939 {
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;
948         }
949 }
950
951 int main(int argc, char *argv[])
952 {
953         int retval = 0;
954
955         /*
956                 Initialization
957         */
958
959         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
960         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
961
962         log_register_thread("main");
963
964         // This enables internatonal characters input
965         if( setlocale(LC_ALL, "") == NULL )
966         {
967                 fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] );
968         }
969
970         // Set locale. This is for forcing '.' as the decimal point.
971         try {
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;
976         }
977         /*
978                 Parse command line
979         */
980         
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)"))));
1009 #ifndef SERVER
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"))));
1026 #endif
1027
1028         Settings cmd_args;
1029         
1030         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
1031
1032         if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
1033         {
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)
1038                 {
1039                         std::ostringstream os1(std::ios::binary);
1040                         os1<<"  --"<<i->first;
1041                         if(i->second.type == VALUETYPE_FLAG)
1042                                 {}
1043                         else
1044                                 os1<<_(" <value>");
1045                         dstream<<padStringRight(os1.str(), 24);
1046
1047                         if(i->second.help != NULL)
1048                                 dstream<<i->second.help;
1049                         dstream<<std::endl;
1050                 }
1051
1052                 return cmd_args.getFlag("help") ? 0 : 1;
1053         }
1054         
1055         /*
1056                 Low-level initialization
1057         */
1058         
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
1065         }
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);
1073
1074         porting::signal_handler_init();
1075         bool &kill = *porting::signal_handler_killstatus();
1076         
1077         porting::initializePaths();
1078
1079         // Create user data directory
1080         fs::CreateDir(porting::path_user);
1081
1082         init_gettext((porting::path_share + DIR_DELIM + "locale").c_str());
1083
1084         infostream<<"path_share = "<<porting::path_share<<std::endl;
1085         infostream<<"path_user  = "<<porting::path_user<<std::endl;
1086
1087         // Initialize debug stacks
1088         debug_stacks_init();
1089         DSTACK(__FUNCTION_NAME);
1090
1091         // Debug handler
1092         BEGIN_DEBUG_EXCEPTION_HANDLER
1093         
1094         // List gameids if requested
1095         if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
1096         {
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;
1101                 return 0;
1102         }
1103         
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);
1109                 return 0;
1110         }
1111
1112         // Print startup message
1113         infostream<<PROJECT_NAME<<
1114                         " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
1115                         <<", "<<BUILD_INFO
1116                         <<std::endl;
1117         
1118         /*
1119                 Basic initialization
1120         */
1121
1122         // Initialize default settings
1123         set_default_settings(g_settings);
1124         
1125         // Initialize sockets
1126         sockets_init();
1127         atexit(sockets_cleanup);
1128         
1129         /*
1130                 Read config file
1131         */
1132         
1133         // Path of configuration file in use
1134         std::string configpath = "";
1135         
1136         if(cmd_args.exists("config"))
1137         {
1138                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
1139                 if(r == false)
1140                 {
1141                         errorstream<<"Could not read configuration from \""
1142                                         <<cmd_args.get("config")<<"\""<<std::endl;
1143                         return 1;
1144                 }
1145                 configpath = cmd_args.get("config");
1146         }
1147         else
1148         {
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");
1155 #if RUN_IN_PLACE
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");
1160 #endif
1161
1162                 for(u32 i=0; i<filenames.size(); i++)
1163                 {
1164                         bool r = g_settings->readConfigFile(filenames[i].c_str());
1165                         if(r)
1166                         {
1167                                 configpath = filenames[i];
1168                                 break;
1169                         }
1170                 }
1171                 
1172                 // If no path found, use the first one (menu creates the file)
1173                 if(configpath == "")
1174                         configpath = filenames[0];
1175         }
1176         
1177         // Initialize debug streams
1178 #define DEBUGFILE "debug.txt"
1179 #if RUN_IN_PLACE
1180         std::string logfile = DEBUGFILE;
1181 #else
1182         std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
1183 #endif
1184         if(cmd_args.exists("logfile"))
1185                 logfile = cmd_args.get("logfile");
1186         
1187         log_remove_output(&main_dstream_no_stderr_log_out);
1188         int loglevel = g_settings->getS32("debug_log_level");
1189
1190         if (loglevel == 0) //no logging
1191                 logfile = "";
1192         else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
1193                 log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1));
1194
1195         if(logfile != "")
1196                 debugstreams_init(false, logfile.c_str());
1197         else
1198                 debugstreams_init(false, NULL);
1199                 
1200         infostream<<"logfile    = "<<logfile<<std::endl;
1201
1202         // Initialize random seed
1203         srand(time(0));
1204         mysrand(time(0));
1205
1206         /*
1207                 Run unit tests
1208         */
1209
1210         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1211                         || cmd_args.getFlag("enable-unittests") == true)
1212         {
1213                 run_tests();
1214         }
1215         
1216         /*
1217                 Game parameters
1218         */
1219
1220         // Port
1221         u16 port = 30000;
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");
1226         if(port == 0)
1227                 port = 30000;
1228         
1229         // World directory
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");
1239         
1240         // World name
1241         std::string commanded_worldname = "";
1242         if(cmd_args.exists("worldname"))
1243                 commanded_worldname = cmd_args.get("worldname");
1244         
1245         // Strip world.mt from commanded_world
1246         {
1247                 std::string worldmt = "world.mt";
1248                 if(commanded_world.size() > worldmt.size() &&
1249                                 commanded_world.substr(commanded_world.size()-worldmt.size())
1250                                 == worldmt){
1251                         dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl;
1252                         commanded_world = commanded_world.substr(
1253                                         0, commanded_world.size()-worldmt.size());
1254                 }
1255         }
1256         
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();
1261                 bool found = false;
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;
1268                                 }
1269                                 commanded_world = worldspecs[i].path;
1270                                 found = true;
1271                                 break;
1272                         }
1273                 }
1274                 if(!found){
1275                         dstream<<_("World")<<" '"<<commanded_worldname<<_("' not "
1276                                         "available. Available worlds:")<<std::endl;
1277                         print_worldspecs(worldspecs, dstream);
1278                         return 1;
1279                 }
1280         }
1281
1282         // Gamespec
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;
1289                         return 1;
1290                 }
1291         }
1292
1293         /*
1294                 Run dedicated server if asked to or no other option
1295         */
1296 #ifdef SERVER
1297         bool run_dedicated_server = true;
1298 #else
1299         bool run_dedicated_server = cmd_args.getFlag("server");
1300 #endif
1301         g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
1302         if(run_dedicated_server)
1303         {
1304                 DSTACK("Dedicated server branch");
1305                 // Create time getter if built with Irrlicht
1306 #ifndef SERVER
1307                 g_timegetter = new SimpleTimeGetter();
1308 #endif
1309
1310                 // World directory
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<<"]"
1318                                         <<std::endl;
1319                 }
1320                 // No world was specified; try to select it automatically
1321                 else
1322                 {
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 != ""){
1327                                 world_path = "";
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;
1332                                                 break;
1333                                         }
1334                                 }
1335                                 if(world_path == ""){
1336                                         dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not "
1337                                                         "available. Available worlds:")<<std::endl;
1338                                         print_worldspecs(worldspecs, dstream);
1339                                         return 1;
1340                                 }
1341                         }
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);
1353                                 return 1;
1354                         // If there are no worlds, automatically create a new one
1355                         } else {
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;
1361                         }
1362                 }
1363
1364                 if(world_path == ""){
1365                         errorstream<<"No world path specified or found."<<std::endl;
1366                         return 1;
1367                 }
1368                 verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl;
1369
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))
1375                 {
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;
1380                         }
1381                         // Otherwise we will be using "minetest"
1382                         else{
1383                                 gamespec = findSubgame(g_settings->get("default_game"));
1384                                 infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl;
1385                         }
1386                 }
1387                 // World exists
1388                 else
1389                 {
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;
1398                                 }
1399                         } else{
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;
1404                         }
1405                 }
1406                 if(!gamespec.isValid()){
1407                         errorstream<<"Subgame ["<<gamespec.id<<"] could not be found."
1408                                         <<std::endl;
1409                         return 1;
1410                 }
1411                 verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl;
1412
1413                 // Create server
1414                 Server server(world_path, configpath, gamespec, false);
1415                 server.start(port);
1416                 
1417                 // Run server
1418                 dedicated_server_loop(server, kill);
1419
1420                 return 0;
1421         }
1422
1423 #ifndef SERVER // Exclude from dedicated server build
1424
1425         /*
1426                 More parameters
1427         */
1428         
1429         std::string address = g_settings->get("address");
1430         if(commanded_world != "")
1431                 address = "";
1432         else if(cmd_args.exists("address"))
1433                 address = cmd_args.get("address");
1434         
1435         std::string playername = g_settings->get("name");
1436         if(cmd_args.exists("name"))
1437                 playername = cmd_args.get("name");
1438         
1439         bool skip_main_menu = cmd_args.getFlag("go");
1440
1441         /*
1442                 Device initialization
1443         */
1444
1445         // Resolution selection
1446         
1447         bool fullscreen = g_settings->getBool("fullscreen");
1448         u16 screenW = g_settings->getU16("screenW");
1449         u16 screenH = g_settings->getU16("screenH");
1450
1451         // bpp, fsaa, vsync
1452
1453         bool vsync = g_settings->getBool("vsync");
1454         u16 bits = g_settings->getU16("fullscreen_bpp");
1455         u16 fsaa = g_settings->getU16("fsaa");
1456
1457         // Determine driver
1458
1459         video::E_DRIVER_TYPE driverType;
1460         
1461         std::string driverstring = g_settings->get("video_driver");
1462
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;
1478 #endif
1479 #ifdef _IRR_COMPILE_WITH_OGLES2_
1480         else if(driverstring == "ogles2")
1481                 driverType = video::EDT_OGLES2;
1482 #endif
1483         else
1484         {
1485                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1486                                 "to opengl"<<std::endl;
1487                 driverType = video::EDT_OPENGL;
1488         }
1489
1490         /*
1491                 List video modes if requested
1492         */
1493
1494         MyEventReceiver receiver;
1495
1496         if(cmd_args.getFlag("videomodes")){
1497                 IrrlichtDevice *nulldevice;
1498
1499                 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1500                 params.DriverType    = video::EDT_NULL;
1501                 params.WindowSize    = core::dimension2d<u32>(640, 480);
1502                 params.Bits          = 24;
1503                 params.AntiAlias     = fsaa;
1504                 params.Fullscreen    = false;
1505                 params.Stencilbuffer = false;
1506                 params.Vsync         = vsync;
1507                 params.EventReceiver = &receiver;
1508
1509                 nulldevice = createDeviceEx(params);
1510
1511                 if(nulldevice == 0)
1512                         return 1;
1513
1514                 dstream<<_("Available video modes (WxHxD):")<<std::endl;
1515
1516                 video::IVideoModeList *videomode_list =
1517                                 nulldevice->getVideoModeList();
1518
1519                 if(videomode_list == 0){
1520                         nulldevice->drop();
1521                         return 1;
1522                 }
1523
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;
1532                 }
1533
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;
1539
1540                 nulldevice->drop();
1541
1542                 return 0;
1543         }
1544
1545         /*
1546                 Create device and exit if creation failed
1547         */
1548
1549         IrrlichtDevice *device;
1550
1551         SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1552         params.DriverType    = driverType;
1553         params.WindowSize    = core::dimension2d<u32>(screenW, screenH);
1554         params.Bits          = bits;
1555         params.AntiAlias     = fsaa;
1556         params.Fullscreen    = fullscreen;
1557         params.Stencilbuffer = false;
1558         params.Vsync         = vsync;
1559         params.EventReceiver = &receiver;
1560
1561         device = createDeviceEx(params);
1562
1563         if (device == 0)
1564                 return 1; // could not create selected driver.
1565         
1566         /*
1567                 Continue initialization
1568         */
1569
1570         video::IVideoDriver* driver = device->getVideoDriver();
1571
1572         /*
1573                 This changes the minimum allowed number of vertices in a VBO.
1574                 Default is 500.
1575         */
1576         //driver->setMinHardwareBufferVertexCount(50);
1577
1578         // Create time getter
1579         g_timegetter = new IrrlichtTimeGetter(device);
1580         
1581         // Create game callback for menus
1582         g_gamecallback = new MainGameCallback(device);
1583         
1584         /*
1585                 Speed tests (done after irrlicht is loaded to get timer)
1586         */
1587         if(cmd_args.getFlag("speedtests"))
1588         {
1589                 dstream<<"Running speed tests"<<std::endl;
1590                 SpeedTests();
1591                 device->drop();
1592                 return 0;
1593         }
1594         
1595         device->setResizable(true);
1596
1597         bool random_input = g_settings->getBool("random_input")
1598                         || cmd_args.getFlag("random-input");
1599         InputHandler *input = NULL;
1600         if(random_input)
1601                 input = new RandomInputHandler();
1602         else
1603                 input = new RealInputHandler(device, &receiver);
1604         
1605         scene::ISceneManager* smgr = device->getSceneManager();
1606
1607         guienv = device->getGUIEnvironment();
1608         gui::IGUISkin* skin = guienv->getSkin();
1609         #if USE_FREETYPE
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);
1613         #else
1614         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1615         #endif
1616         if(font)
1617                 skin->setFont(font);
1618         else
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();
1623         assert(font);
1624         
1625         u32 text_height = font->getDimension(L"Hello, world!").Height;
1626         infostream<<"text_height="<<text_height<<std::endl;
1627
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));
1636
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));
1641 #endif
1642
1643
1644         // Create the menu clouds
1645         if (!g_menucloudsmgr)
1646                 g_menucloudsmgr = smgr->createNewSceneManager();
1647         if (!g_menuclouds)
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);
1655
1656         /*
1657                 GUI stuff
1658         */
1659
1660         ChatBackend chat_backend;
1661
1662         /*
1663                 If an error occurs, this is set to something and the
1664                 menu-game loop is restarted. It is then displayed before
1665                 the menu.
1666         */
1667         std::wstring error_message = L"";
1668
1669         // The password entered during the menu screen,
1670         std::string password;
1671
1672         bool first_loop = true;
1673
1674         /*
1675                 Menu-game loop
1676         */
1677         while(device->run() && kill == false)
1678         {
1679                 // Set the window caption
1680                 wchar_t* text = wgettext("Main Menu");
1681                 device->setWindowCaption((std::wstring(L"Minetest [")+text+L"]").c_str());
1682                 delete[] text;
1683
1684                 // This is used for catching disconnects
1685                 try
1686                 {
1687
1688                         /*
1689                                 Clear everything from the GUIEnvironment
1690                         */
1691                         guienv->clear();
1692                         
1693                         /*
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.
1697                         */
1698                         guiroot = guienv->addStaticText(L"",
1699                                         core::rect<s32>(0, 0, 10000, 10000));
1700                         
1701                         SubgameSpec gamespec;
1702                         WorldSpec worldspec;
1703                         bool simple_singleplayer_mode = false;
1704
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;
1710
1711                         /*
1712                                 Out-of-game menu loop.
1713
1714                                 Loop quits when menu returns proper parameters.
1715                         */
1716                         while(kill == false)
1717                         {
1718                                 // If skip_main_menu, only go through here once
1719                                 if(skip_main_menu && !first_loop){
1720                                         kill = true;
1721                                         break;
1722                                 }
1723                                 first_loop = false;
1724                                 
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
1728                                 // (map at least?)
1729                                 smgr->clear();
1730                                 
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;
1740                                 }
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;
1769                                 }
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;
1776                                                         break;
1777                                                 }
1778                                         }
1779                                 }
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]");
1784                                         if(gameid == ""){
1785                                                 gameid = g_settings->get("default_game");
1786                                                 name += " [new]";
1787                                         }
1788                                         WorldSpec spec(commanded_world, name, gameid);
1789                                         worldspecs.push_back(spec);
1790                                         menudata.selected_world = worldspecs.size()-1;
1791                                 }
1792                                 // Copy worldspecs to menu
1793                                 menudata.worlds = worldspecs;
1794                                 // Get game listing
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;
1800                                 }
1801                                 const SubgameSpec *menugame = getMenuGame(menudata);
1802
1803                                 MenuTextures menutextures;
1804                                 menutextures.update(driver, menugame, menudata.selected_tab);
1805
1806                                 if(skip_main_menu == false)
1807                                 {
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)
1812                                         {
1813                                                 if(noMenuActive())
1814                                                         break;
1815                                                 driver->beginScene(true, true,
1816                                                                 video::SColor(255,128,128,128));
1817                                                 drawMenuBackground(driver, menutextures);
1818                                                 guienv->drawAll();
1819                                                 driver->endScene();
1820                                                 // On some computers framerate doesn't seem to be
1821                                                 // automatically limited
1822                                                 sleep_ms(25);
1823                                         }
1824                                         infostream<<"Waited for other menus"<<std::endl;
1825
1826                                         GUIMainMenu *menu =
1827                                                         new GUIMainMenu(guienv, guiroot, -1, 
1828                                                                 &g_menumgr, &menudata, g_gamecallback);
1829                                         menu->allowFocusRemoval(true);
1830
1831                                         if(error_message != L"")
1832                                         {
1833                                                 verbosestream<<"error_message = "
1834                                                                 <<wide_to_narrow(error_message)<<std::endl;
1835
1836                                                 GUIMessageMenu *menu2 =
1837                                                                 new GUIMessageMenu(guienv, guiroot, -1, 
1838                                                                         &g_menumgr, error_message.c_str());
1839                                                 menu2->drop();
1840                                                 error_message = L"";
1841                                         }
1842
1843                                         // Time is in milliseconds, for clouds
1844                                         u32 lasttime = device->getTimer()->getTime();
1845
1846                                         MenuMusicFetcher soundfetcher;
1847                                         ISoundManager *sound = NULL;
1848                                         sound = createOpenALSoundManager(&soundfetcher);
1849                                         if(!sound)
1850                                                 sound = &dummySoundManager;
1851                                         SimpleSoundSpec spec;
1852                                         spec.name = "main_menu";
1853                                         spec.gain = 1;
1854                                         s32 handle = sound->playSound(spec, true);
1855
1856                                         infostream<<"Created main menu"<<std::endl;
1857
1858                                         while(device->run() && kill == false)
1859                                         {
1860                                                 if(menu->getStatus() == true)
1861                                                         break;
1862
1863                                                 // Game can be selected in the menu
1864                                                 menugame = getMenuGame(menudata);
1865                                                 menutextures.update(driver, menugame, menu->getTab());
1866                                                 // Clouds for the main menu
1867                                                 bool cloud_menu_background = g_settings->getBool("menu_clouds");
1868                                                 if(menugame){
1869                                                         // If game has regular background and no overlay, don't use clouds
1870                                                         if(cloud_menu_background && menutextures.background &&
1871                                                                         !menutextures.overlay){
1872                                                                 cloud_menu_background = false;
1873                                                         }
1874                                                         // If game game has overlay and no regular background, always draw clouds
1875                                                         else if(menutextures.overlay && !menutextures.background){
1876                                                                 cloud_menu_background = true;
1877                                                         }
1878                                                 }
1879
1880                                                 // Time calc for the clouds
1881                                                 f32 dtime=0; // in seconds
1882                                                 if (cloud_menu_background) {
1883                                                         u32 time = device->getTimer()->getTime();
1884                                                         if(time > lasttime)
1885                                                                 dtime = (time - lasttime) / 1000.0;
1886                                                         else
1887                                                                 dtime = 0;
1888                                                         lasttime = time;
1889                                                 }
1890
1891                                                 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1892                                                 driver->beginScene(true, true, video::SColor(255,140,186,250));
1893
1894                                                 if (cloud_menu_background) {
1895                                                         // *3 otherwise the clouds would move very slowly
1896                                                         g_menuclouds->step(dtime*3); 
1897                                                         g_menuclouds->render();
1898                                                         g_menucloudsmgr->drawAll();
1899                                                         drawMenuOverlay(driver, menutextures);
1900                                                         drawMenuHeader(driver, menutextures);
1901                                                         drawMenuFooter(driver, menutextures);
1902                                                 } else {
1903                                                         drawMenuBackground(driver, menutextures);
1904                                                         drawMenuHeader(driver, menutextures);
1905                                                         drawMenuFooter(driver, menutextures);
1906                                                 }
1907
1908                                                 guienv->drawAll();
1909
1910                                                 driver->endScene();
1911                                                 
1912                                                 // On some computers framerate doesn't seem to be
1913                                                 // automatically limited
1914                                                 if (cloud_menu_background) {
1915                                                         // Time of frame without fps limit
1916                                                         float busytime;
1917                                                         u32 busytime_u32;
1918                                                         // not using getRealTime is necessary for wine
1919                                                         u32 time = device->getTimer()->getTime();
1920                                                         if(time > lasttime)
1921                                                                 busytime_u32 = time - lasttime;
1922                                                         else
1923                                                                 busytime_u32 = 0;
1924                                                         busytime = busytime_u32 / 1000.0;
1925
1926                                                         // FPS limiter
1927                                                         u32 frametime_min = 1000./fps_max;
1928                         
1929                                                         if(busytime_u32 < frametime_min) {
1930                                                                 u32 sleeptime = frametime_min - busytime_u32;
1931                                                                 device->sleep(sleeptime);
1932                                                         }
1933                                                 } else {
1934                                                         sleep_ms(25);
1935                                                 }
1936                                         }
1937                                         sound->stopSound(handle);
1938                                         if(sound != &dummySoundManager){
1939                                                 delete sound;
1940                                                 sound = NULL;
1941                                         }
1942
1943                                         infostream<<"Dropping main menu"<<std::endl;
1944
1945                                         menu->drop();
1946                                 }
1947
1948                                 playername = wide_to_narrow(menudata.name);
1949                                 if (playername == "")
1950                                         playername = std::string("Guest") + itos(myrand_range(1000,9999));
1951                                 password = translatePassword(playername, menudata.password);
1952                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1953
1954                                 address = wide_to_narrow(menudata.address);
1955                                 int newport = stoi(wide_to_narrow(menudata.port));
1956                                 if(newport != 0)
1957                                         port = newport;
1958                                 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1959                                 // Save settings
1960                                 g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
1961                                 g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
1962                                 g_settings->set("selected_mainmenu_game", menudata.selected_game);
1963                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1964                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1965                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1966                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1967
1968                                 g_settings->set("mip_map", itos(menudata.mip_map));
1969                                 g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
1970                                 g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
1971                                 g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
1972
1973                                 g_settings->setS32("enable_shaders", menudata.enable_shaders);
1974                                 g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
1975                                 g_settings->set("enable_particles", itos(menudata.enable_particles));
1976                                 g_settings->set("liquid_finite", itos(menudata.liquid_finite));
1977
1978                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1979                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1980                                 g_settings->set("server_announce", itos(menudata.enable_public));
1981                                 g_settings->set("name", playername);
1982                                 g_settings->set("address", address);
1983                                 g_settings->set("port", itos(port));
1984                                 if(menudata.selected_world != -1)
1985                                         g_settings->set("selected_world_path",
1986                                                         worldspecs[menudata.selected_world].path);
1987
1988                                 // Break out of menu-game loop to shut down cleanly
1989                                 if(device->run() == false || kill == true)
1990                                         break;
1991                                 
1992                                 current_playername = playername;
1993                                 current_password = password;
1994                                 current_address = address;
1995                                 current_port = port;
1996
1997                                 // If using simple singleplayer mode, override
1998                                 if(simple_singleplayer_mode){
1999                                         current_playername = "singleplayer";
2000                                         current_password = "";
2001                                         current_address = "";
2002                                         current_port = 30011;
2003                                 }
2004                                 else if (address != "")
2005                                 {
2006                                         ServerListSpec server;
2007                                         server["name"] = menudata.servername;
2008                                         server["address"] = wide_to_narrow(menudata.address);
2009                                         server["port"] = wide_to_narrow(menudata.port);
2010                                         server["description"] = menudata.serverdescription;
2011                                         ServerList::insert(server);
2012                                 }
2013                                 
2014                                 // Set world path to selected one
2015                                 if(menudata.selected_world != -1){
2016                                         worldspec = worldspecs[menudata.selected_world];
2017                                         infostream<<"Selected world: "<<worldspec.name
2018                                                         <<" ["<<worldspec.path<<"]"<<std::endl;
2019                                 }
2020
2021                                 // Only refresh if so requested
2022                                 if(menudata.only_refresh){
2023                                         infostream<<"Refreshing menu"<<std::endl;
2024                                         continue;
2025                                 }
2026                                 
2027                                 // Create new world if requested
2028                                 if(menudata.create_world_name != L"")
2029                                 {
2030                                         std::string path = porting::path_user + DIR_DELIM
2031                                                         "worlds" + DIR_DELIM
2032                                                         + wide_to_narrow(menudata.create_world_name);
2033                                         // Create world if it doesn't exist
2034                                         if(!initializeWorld(path, menudata.create_world_gameid)){
2035                                                 error_message = wgettext("Failed to initialize world");
2036                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
2037                                                 continue;
2038                                         }
2039                                         g_settings->set("selected_world_path", path);
2040                                         g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
2041                                         continue;
2042                                 }
2043
2044                                 // If local game
2045                                 if(current_address == "")
2046                                 {
2047                                         if(menudata.selected_world == -1){
2048                                                 error_message = wgettext("No world selected and no address "
2049                                                                 "provided. Nothing to do.");
2050                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
2051                                                 continue;
2052                                         }
2053                                         // Load gamespec for required game
2054                                         gamespec = findWorldSubgame(worldspec.path);
2055                                         if(!gamespec.isValid() && !commanded_gamespec.isValid()){
2056                                                 error_message = wgettext("Could not find or load game \"")
2057                                                                 + narrow_to_wide(worldspec.gameid) + L"\"";
2058                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
2059                                                 continue;
2060                                         }
2061                                         if(commanded_gamespec.isValid() &&
2062                                                         commanded_gamespec.id != worldspec.gameid){
2063                                                 errorstream<<"WARNING: Overriding gamespec from \""
2064                                                                 <<worldspec.gameid<<"\" to \""
2065                                                                 <<commanded_gamespec.id<<"\""<<std::endl;
2066                                                 gamespec = commanded_gamespec;
2067                                         }
2068
2069                                         if(!gamespec.isValid()){
2070                                                 error_message = wgettext("Invalid gamespec.");
2071                                                 error_message += L" (world_gameid="
2072                                                                 +narrow_to_wide(worldspec.gameid)+L")";
2073                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
2074                                                 continue;
2075                                         }
2076                                 }
2077
2078                                 // Continue to game
2079                                 break;
2080                         }
2081                         
2082                         // Break out of menu-game loop to shut down cleanly
2083                         if(device->run() == false || kill == true)
2084                                 break;
2085
2086                         /*
2087                                 Run game
2088                         */
2089                         the_game(
2090                                 kill,
2091                                 random_input,
2092                                 input,
2093                                 device,
2094                                 font,
2095                                 worldspec.path,
2096                                 current_playername,
2097                                 current_password,
2098                                 current_address,
2099                                 current_port,
2100                                 error_message,
2101                                 configpath,
2102                                 chat_backend,
2103                                 gamespec,
2104                                 simple_singleplayer_mode
2105                         );
2106                         smgr->clear();
2107
2108                 } //try
2109                 catch(con::PeerNotFoundException &e)
2110                 {
2111                         error_message = wgettext("Connection error (timed out?)");
2112                         errorstream<<wide_to_narrow(error_message)<<std::endl;
2113                 }
2114 #ifdef NDEBUG
2115                 catch(std::exception &e)
2116                 {
2117                         std::string narrow_message = "Some exception: \"";
2118                         narrow_message += e.what();
2119                         narrow_message += "\"";
2120                         errorstream<<narrow_message<<std::endl;
2121                         error_message = narrow_to_wide(narrow_message);
2122                 }
2123 #endif
2124
2125                 // If no main menu, show error and exit
2126                 if(skip_main_menu)
2127                 {
2128                         if(error_message != L""){
2129                                 verbosestream<<"error_message = "
2130                                                 <<wide_to_narrow(error_message)<<std::endl;
2131                                 retval = 1;
2132                         }
2133                         break;
2134                 }
2135         } // Menu-game loop
2136         
2137         
2138         g_menuclouds->drop();
2139         g_menucloudsmgr->drop();
2140         
2141         delete input;
2142
2143         /*
2144                 In the end, delete the Irrlicht device.
2145         */
2146         device->drop();
2147
2148 #if USE_FREETYPE
2149         font->drop();
2150 #endif
2151
2152 #endif // !SERVER
2153         
2154         // Update configuration file
2155         if(configpath != "")
2156                 g_settings->updateConfigFile(configpath.c_str());
2157         
2158         // Print modified quicktune values
2159         {
2160                 bool header_printed = false;
2161                 std::vector<std::string> names = getQuicktuneNames();
2162                 for(u32 i=0; i<names.size(); i++){
2163                         QuicktuneValue val = getQuicktuneValue(names[i]);
2164                         if(!val.modified)
2165                                 continue;
2166                         if(!header_printed){
2167                                 dstream<<"Modified quicktune values:"<<std::endl;
2168                                 header_printed = true;
2169                         }
2170                         dstream<<names[i]<<" = "<<val.getString()<<std::endl;
2171                 }
2172         }
2173
2174         END_DEBUG_EXCEPTION_HANDLER(errorstream)
2175         
2176         debugstreams_deinit();
2177         
2178         return retval;
2179 }
2180
2181 //END
2182