Create main menu tab "Settings" for client settings
[oweals/minetest.git] / src / main.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
51 #include "debug.h"
52 #include "test.h"
53 #include "server.h"
54 #include "constants.h"
55 #include "porting.h"
56 #include "gettime.h"
57 #include "guiMessageMenu.h"
58 #include "filesys.h"
59 #include "config.h"
60 #include "guiMainMenu.h"
61 #include "game.h"
62 #include "keycode.h"
63 #include "tile.h"
64 #include "chat.h"
65 #include "defaultsettings.h"
66 #include "gettext.h"
67 #include "settings.h"
68 #include "profiler.h"
69 #include "log.h"
70 #include "mods.h"
71 #include "utility_string.h"
72 #include "subgame.h"
73 #include "quicktune.h"
74
75 /*
76         Settings.
77         These are loaded from the config file.
78 */
79 Settings main_settings;
80 Settings *g_settings = &main_settings;
81
82 // Global profiler
83 Profiler main_profiler;
84 Profiler *g_profiler = &main_profiler;
85
86 /*
87         Debug streams
88 */
89
90 // Connection
91 std::ostream *dout_con_ptr = &dummyout;
92 std::ostream *derr_con_ptr = &verbosestream;
93 //std::ostream *dout_con_ptr = &infostream;
94 //std::ostream *derr_con_ptr = &errorstream;
95
96 // Server
97 std::ostream *dout_server_ptr = &infostream;
98 std::ostream *derr_server_ptr = &errorstream;
99
100 // Client
101 std::ostream *dout_client_ptr = &infostream;
102 std::ostream *derr_client_ptr = &errorstream;
103
104 #ifndef SERVER
105 /*
106         Random stuff
107 */
108
109 /* mainmenumanager.h */
110
111 gui::IGUIEnvironment* guienv = NULL;
112 gui::IGUIStaticText *guiroot = NULL;
113 MainMenuManager g_menumgr;
114
115 bool noMenuActive()
116 {
117         return (g_menumgr.menuCount() == 0);
118 }
119
120 // Passed to menus to allow disconnecting and exiting
121 MainGameCallback *g_gamecallback = NULL;
122 #endif
123
124 /*
125         gettime.h implementation
126 */
127
128 #ifdef SERVER
129
130 u32 getTimeMs()
131 {
132         /* Use imprecise system calls directly (from porting.h) */
133         return porting::getTimeMs();
134 }
135
136 #else
137
138 // A small helper class
139 class TimeGetter
140 {
141 public:
142         virtual u32 getTime() = 0;
143 };
144
145 // A precise irrlicht one
146 class IrrlichtTimeGetter: public TimeGetter
147 {
148 public:
149         IrrlichtTimeGetter(IrrlichtDevice *device):
150                 m_device(device)
151         {}
152         u32 getTime()
153         {
154                 if(m_device == NULL)
155                         return 0;
156                 return m_device->getTimer()->getRealTime();
157         }
158 private:
159         IrrlichtDevice *m_device;
160 };
161 // Not so precise one which works without irrlicht
162 class SimpleTimeGetter: public TimeGetter
163 {
164 public:
165         u32 getTime()
166         {
167                 return porting::getTimeMs();
168         }
169 };
170
171 // A pointer to a global instance of the time getter
172 // TODO: why?
173 TimeGetter *g_timegetter = NULL;
174
175 u32 getTimeMs()
176 {
177         if(g_timegetter == NULL)
178                 return 0;
179         return g_timegetter->getTime();
180 }
181
182 #endif
183
184 class StderrLogOutput: public ILogOutput
185 {
186 public:
187         /* line: Full line with timestamp, level and thread */
188         void printLog(const std::string &line)
189         {
190                 std::cerr<<line<<std::endl;
191         }
192 } main_stderr_log_out;
193
194 class DstreamNoStderrLogOutput: public ILogOutput
195 {
196 public:
197         /* line: Full line with timestamp, level and thread */
198         void printLog(const std::string &line)
199         {
200                 dstream_no_stderr<<line<<std::endl;
201         }
202 } main_dstream_no_stderr_log_out;
203
204 #ifndef SERVER
205
206 /*
207         Event handler for Irrlicht
208
209         NOTE: Everything possible should be moved out from here,
210               probably to InputHandler and the_game
211 */
212
213 class MyEventReceiver : public IEventReceiver
214 {
215 public:
216         // This is the one method that we have to implement
217         virtual bool OnEvent(const SEvent& event)
218         {
219                 /*
220                         React to nothing here if a menu is active
221                 */
222                 if(noMenuActive() == false)
223                 {
224                         return false;
225                 }
226
227                 // Remember whether each key is down or up
228                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
229                 {
230                         if(event.KeyInput.PressedDown) {
231                                 keyIsDown.set(event.KeyInput);
232                                 keyWasDown.set(event.KeyInput);
233                         } else {
234                                 keyIsDown.unset(event.KeyInput);
235                         }
236                 }
237
238                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
239                 {
240                         if(noMenuActive() == false)
241                         {
242                                 left_active = false;
243                                 middle_active = false;
244                                 right_active = false;
245                         }
246                         else
247                         {
248                                 left_active = event.MouseInput.isLeftPressed();
249                                 middle_active = event.MouseInput.isMiddlePressed();
250                                 right_active = event.MouseInput.isRightPressed();
251
252                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
253                                 {
254                                         leftclicked = true;
255                                 }
256                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
257                                 {
258                                         rightclicked = true;
259                                 }
260                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
261                                 {
262                                         leftreleased = true;
263                                 }
264                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
265                                 {
266                                         rightreleased = true;
267                                 }
268                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
269                                 {
270                                         mouse_wheel += event.MouseInput.Wheel;
271                                 }
272                         }
273                 }
274
275                 return false;
276         }
277
278         bool IsKeyDown(const KeyPress &keyCode) const
279         {
280                 return keyIsDown[keyCode];
281         }
282         
283         // Checks whether a key was down and resets the state
284         bool WasKeyDown(const KeyPress &keyCode)
285         {
286                 bool b = keyWasDown[keyCode];
287                 if (b)
288                         keyWasDown.unset(keyCode);
289                 return b;
290         }
291
292         s32 getMouseWheel()
293         {
294                 s32 a = mouse_wheel;
295                 mouse_wheel = 0;
296                 return a;
297         }
298
299         void clearInput()
300         {
301                 keyIsDown.clear();
302                 keyWasDown.clear();
303
304                 leftclicked = false;
305                 rightclicked = false;
306                 leftreleased = false;
307                 rightreleased = false;
308
309                 left_active = false;
310                 middle_active = false;
311                 right_active = false;
312
313                 mouse_wheel = 0;
314         }
315
316         MyEventReceiver()
317         {
318                 clearInput();
319         }
320
321         bool leftclicked;
322         bool rightclicked;
323         bool leftreleased;
324         bool rightreleased;
325
326         bool left_active;
327         bool middle_active;
328         bool right_active;
329
330         s32 mouse_wheel;
331
332 private:
333         IrrlichtDevice *m_device;
334         
335         // The current state of keys
336         KeyList keyIsDown;
337         // Whether a key has been pressed or not
338         KeyList keyWasDown;
339 };
340
341 /*
342         Separated input handler
343 */
344
345 class RealInputHandler : public InputHandler
346 {
347 public:
348         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
349                 m_device(device),
350                 m_receiver(receiver)
351         {
352         }
353         virtual bool isKeyDown(const KeyPress &keyCode)
354         {
355                 return m_receiver->IsKeyDown(keyCode);
356         }
357         virtual bool wasKeyDown(const KeyPress &keyCode)
358         {
359                 return m_receiver->WasKeyDown(keyCode);
360         }
361         virtual v2s32 getMousePos()
362         {
363                 return m_device->getCursorControl()->getPosition();
364         }
365         virtual void setMousePos(s32 x, s32 y)
366         {
367                 m_device->getCursorControl()->setPosition(x, y);
368         }
369
370         virtual bool getLeftState()
371         {
372                 return m_receiver->left_active;
373         }
374         virtual bool getRightState()
375         {
376                 return m_receiver->right_active;
377         }
378         
379         virtual bool getLeftClicked()
380         {
381                 return m_receiver->leftclicked;
382         }
383         virtual bool getRightClicked()
384         {
385                 return m_receiver->rightclicked;
386         }
387         virtual void resetLeftClicked()
388         {
389                 m_receiver->leftclicked = false;
390         }
391         virtual void resetRightClicked()
392         {
393                 m_receiver->rightclicked = false;
394         }
395
396         virtual bool getLeftReleased()
397         {
398                 return m_receiver->leftreleased;
399         }
400         virtual bool getRightReleased()
401         {
402                 return m_receiver->rightreleased;
403         }
404         virtual void resetLeftReleased()
405         {
406                 m_receiver->leftreleased = false;
407         }
408         virtual void resetRightReleased()
409         {
410                 m_receiver->rightreleased = false;
411         }
412
413         virtual s32 getMouseWheel()
414         {
415                 return m_receiver->getMouseWheel();
416         }
417
418         void clear()
419         {
420                 m_receiver->clearInput();
421         }
422 private:
423         IrrlichtDevice *m_device;
424         MyEventReceiver *m_receiver;
425 };
426
427 class RandomInputHandler : public InputHandler
428 {
429 public:
430         RandomInputHandler()
431         {
432                 leftdown = false;
433                 rightdown = false;
434                 leftclicked = false;
435                 rightclicked = false;
436                 leftreleased = false;
437                 rightreleased = false;
438                 keydown.clear();
439         }
440         virtual bool isKeyDown(const KeyPress &keyCode)
441         {
442                 return keydown[keyCode];
443         }
444         virtual bool wasKeyDown(const KeyPress &keyCode)
445         {
446                 return false;
447         }
448         virtual v2s32 getMousePos()
449         {
450                 return mousepos;
451         }
452         virtual void setMousePos(s32 x, s32 y)
453         {
454                 mousepos = v2s32(x,y);
455         }
456
457         virtual bool getLeftState()
458         {
459                 return leftdown;
460         }
461         virtual bool getRightState()
462         {
463                 return rightdown;
464         }
465
466         virtual bool getLeftClicked()
467         {
468                 return leftclicked;
469         }
470         virtual bool getRightClicked()
471         {
472                 return rightclicked;
473         }
474         virtual void resetLeftClicked()
475         {
476                 leftclicked = false;
477         }
478         virtual void resetRightClicked()
479         {
480                 rightclicked = false;
481         }
482
483         virtual bool getLeftReleased()
484         {
485                 return leftreleased;
486         }
487         virtual bool getRightReleased()
488         {
489                 return rightreleased;
490         }
491         virtual void resetLeftReleased()
492         {
493                 leftreleased = false;
494         }
495         virtual void resetRightReleased()
496         {
497                 rightreleased = false;
498         }
499
500         virtual s32 getMouseWheel()
501         {
502                 return 0;
503         }
504
505         virtual void step(float dtime)
506         {
507                 {
508                         static float counter1 = 0;
509                         counter1 -= dtime;
510                         if(counter1 < 0.0)
511                         {
512                                 counter1 = 0.1*Rand(1, 40);
513                                 keydown.toggle(getKeySetting("keymap_jump"));
514                         }
515                 }
516                 {
517                         static float counter1 = 0;
518                         counter1 -= dtime;
519                         if(counter1 < 0.0)
520                         {
521                                 counter1 = 0.1*Rand(1, 40);
522                                 keydown.toggle(getKeySetting("keymap_special1"));
523                         }
524                 }
525                 {
526                         static float counter1 = 0;
527                         counter1 -= dtime;
528                         if(counter1 < 0.0)
529                         {
530                                 counter1 = 0.1*Rand(1, 40);
531                                 keydown.toggle(getKeySetting("keymap_forward"));
532                         }
533                 }
534                 {
535                         static float counter1 = 0;
536                         counter1 -= dtime;
537                         if(counter1 < 0.0)
538                         {
539                                 counter1 = 0.1*Rand(1, 40);
540                                 keydown.toggle(getKeySetting("keymap_left"));
541                         }
542                 }
543                 {
544                         static float counter1 = 0;
545                         counter1 -= dtime;
546                         if(counter1 < 0.0)
547                         {
548                                 counter1 = 0.1*Rand(1, 20);
549                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
550                         }
551                 }
552                 {
553                         static float counter1 = 0;
554                         counter1 -= dtime;
555                         if(counter1 < 0.0)
556                         {
557                                 counter1 = 0.1*Rand(1, 30);
558                                 leftdown = !leftdown;
559                                 if(leftdown)
560                                         leftclicked = true;
561                                 if(!leftdown)
562                                         leftreleased = true;
563                         }
564                 }
565                 {
566                         static float counter1 = 0;
567                         counter1 -= dtime;
568                         if(counter1 < 0.0)
569                         {
570                                 counter1 = 0.1*Rand(1, 15);
571                                 rightdown = !rightdown;
572                                 if(rightdown)
573                                         rightclicked = true;
574                                 if(!rightdown)
575                                         rightreleased = true;
576                         }
577                 }
578                 mousepos += mousespeed;
579         }
580
581         s32 Rand(s32 min, s32 max)
582         {
583                 return (myrand()%(max-min+1))+min;
584         }
585 private:
586         KeyList keydown;
587         v2s32 mousepos;
588         v2s32 mousespeed;
589         bool leftdown;
590         bool rightdown;
591         bool leftclicked;
592         bool rightclicked;
593         bool leftreleased;
594         bool rightreleased;
595 };
596
597 void drawMenuBackground(video::IVideoDriver* driver)
598 {
599         core::dimension2d<u32> screensize = driver->getScreenSize();
600                 
601         video::ITexture *bgtexture =
602                         driver->getTexture(getTexturePath("menubg.png").c_str());
603         if(bgtexture)
604         {
605                 s32 scaledsize = 128;
606                 
607                 // The important difference between destsize and screensize is
608                 // that destsize is rounded to whole scaled pixels.
609                 // These formulas use component-wise multiplication and division of v2u32.
610                 v2u32 texturesize = bgtexture->getSize();
611                 v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
612                 v2u32 destsize = scaledsize * sourcesize / texturesize;
613                 
614                 // Default texture wrapping mode in Irrlicht is ETC_REPEAT.
615                 driver->draw2DImage(bgtexture,
616                         core::rect<s32>(0, 0, destsize.X, destsize.Y),
617                         core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
618                         NULL, NULL, true);
619         }
620         
621         video::ITexture *logotexture =
622                         driver->getTexture(getTexturePath("menulogo.png").c_str());
623         if(logotexture)
624         {
625                 v2s32 logosize(logotexture->getOriginalSize().Width,
626                                 logotexture->getOriginalSize().Height);
627                 logosize *= 4;
628
629                 video::SColor bgcolor(255,50,50,50);
630                 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
631                                 screensize.Width, screensize.Height);
632                 driver->draw2DRectangle(bgcolor, bgrect, NULL);
633
634                 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
635                 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
636                 rect -= v2s32(logosize.X/2, 0);
637                 driver->draw2DImage(logotexture, rect,
638                         core::rect<s32>(core::position2d<s32>(0,0),
639                         core::dimension2di(logotexture->getSize())),
640                         NULL, NULL, true);
641         }
642 }
643
644 #endif
645
646 // These are defined global so that they're not optimized too much.
647 // Can't change them to volatile.
648 s16 temp16;
649 f32 tempf;
650 v3f tempv3f1;
651 v3f tempv3f2;
652 std::string tempstring;
653 std::string tempstring2;
654
655 void SpeedTests()
656 {
657         {
658                 infostream<<"The following test should take around 20ms."<<std::endl;
659                 TimeTaker timer("Testing std::string speed");
660                 const u32 jj = 10000;
661                 for(u32 j=0; j<jj; j++)
662                 {
663                         tempstring = "";
664                         tempstring2 = "";
665                         const u32 ii = 10;
666                         for(u32 i=0; i<ii; i++){
667                                 tempstring2 += "asd";
668                         }
669                         for(u32 i=0; i<ii+1; i++){
670                                 tempstring += "asd";
671                                 if(tempstring == tempstring2)
672                                         break;
673                         }
674                 }
675         }
676         
677         infostream<<"All of the following tests should take around 100ms each."
678                         <<std::endl;
679
680         {
681                 TimeTaker timer("Testing floating-point conversion speed");
682                 tempf = 0.001;
683                 for(u32 i=0; i<4000000; i++){
684                         temp16 += tempf;
685                         tempf += 0.001;
686                 }
687         }
688         
689         {
690                 TimeTaker timer("Testing floating-point vector speed");
691
692                 tempv3f1 = v3f(1,2,3);
693                 tempv3f2 = v3f(4,5,6);
694                 for(u32 i=0; i<10000000; i++){
695                         tempf += tempv3f1.dotProduct(tempv3f2);
696                         tempv3f2 += v3f(7,8,9);
697                 }
698         }
699
700         {
701                 TimeTaker timer("Testing core::map speed");
702                 
703                 core::map<v2s16, f32> map1;
704                 tempf = -324;
705                 const s16 ii=300;
706                 for(s16 y=0; y<ii; y++){
707                         for(s16 x=0; x<ii; x++){
708                                 map1.insert(v2s16(x,y), tempf);
709                                 tempf += 1;
710                         }
711                 }
712                 for(s16 y=ii-1; y>=0; y--){
713                         for(s16 x=0; x<ii; x++){
714                                 tempf = map1[v2s16(x,y)];
715                         }
716                 }
717         }
718
719         {
720                 infostream<<"Around 5000/ms should do well here."<<std::endl;
721                 TimeTaker timer("Testing mutex speed");
722                 
723                 JMutex m;
724                 m.Init();
725                 u32 n = 0;
726                 u32 i = 0;
727                 do{
728                         n += 10000;
729                         for(; i<n; i++){
730                                 m.Lock();
731                                 m.Unlock();
732                         }
733                 }
734                 // Do at least 10ms
735                 while(timer.getTime() < 10);
736
737                 u32 dtime = timer.stop();
738                 u32 per_ms = n / dtime;
739                 infostream<<"Done. "<<dtime<<"ms, "
740                                 <<per_ms<<"/ms"<<std::endl;
741         }
742 }
743
744 int main(int argc, char *argv[])
745 {
746         int retval = 0;
747
748         /*
749                 Initialization
750         */
751
752         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
753         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
754
755         log_register_thread("main");
756
757         // Set locale. This is for forcing '.' as the decimal point.
758         std::locale::global(std::locale("C"));
759         // This enables printing all characters in bitmap font
760         setlocale(LC_CTYPE, "en_US");
761
762         /*
763                 Parse command line
764         */
765         
766         // List all allowed options
767         core::map<std::string, ValueSpec> allowed_options;
768         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG,
769                         "Show allowed options"));
770         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
771                         "Load configuration from specified file"));
772         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING,
773                         "Set network port (UDP)"));
774         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG,
775                         "Disable unit tests"));
776         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG,
777                         "Enable unit tests"));
778         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
779                         "Same as --world (deprecated)"));
780         allowed_options.insert("world", ValueSpec(VALUETYPE_STRING,
781                         "Set world path (implies local game)"));
782         allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG,
783                         "Print more information to console"));
784         allowed_options.insert("logfile", ValueSpec(VALUETYPE_STRING,
785                         "Set logfile path ('' = no logging)"));
786         allowed_options.insert("gameid", ValueSpec(VALUETYPE_STRING,
787                         "Set gameid (\"--gameid list\" prints available ones)"));
788 #ifndef SERVER
789         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG,
790                         "Run speed tests"));
791         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING,
792                         "Address to connect to. ('' = local game)"));
793         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG,
794                         "Enable random user input, for testing"));
795         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
796                         "Run dedicated server"));
797         allowed_options.insert("name", ValueSpec(VALUETYPE_STRING,
798                         "Set player name"));
799         allowed_options.insert("password", ValueSpec(VALUETYPE_STRING,
800                         "Set password"));
801         allowed_options.insert("go", ValueSpec(VALUETYPE_FLAG,
802                         "Disable main menu"));
803 #endif
804
805         Settings cmd_args;
806         
807         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
808
809         if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1"))
810         {
811                 dstream<<"Allowed options:"<<std::endl;
812                 for(core::map<std::string, ValueSpec>::Iterator
813                                 i = allowed_options.getIterator();
814                                 i.atEnd() == false; i++)
815                 {
816                         std::ostringstream os1(std::ios::binary);
817                         os1<<"  --"<<i.getNode()->getKey();
818                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)
819                                 {}
820                         else
821                                 os1<<" <value>";
822                         dstream<<padStringRight(os1.str(), 24);
823
824                         if(i.getNode()->getValue().help != NULL)
825                                 dstream<<i.getNode()->getValue().help;
826                         dstream<<std::endl;
827                 }
828
829                 return cmd_args.getFlag("help") ? 0 : 1;
830         }
831         
832         /*
833                 Low-level initialization
834         */
835         
836         // In certain cases, output info level on stderr
837         if(cmd_args.getFlag("verbose") || cmd_args.getFlag("speedtests"))
838                 log_add_output(&main_stderr_log_out, LMT_INFO);
839
840         porting::signal_handler_init();
841         bool &kill = *porting::signal_handler_killstatus();
842         
843         porting::initializePaths();
844
845         // Create user data directory
846         fs::CreateDir(porting::path_user);
847
848         init_gettext((porting::path_share+DIR_DELIM+".."+DIR_DELIM+"locale").c_str());
849         
850         // Initialize debug streams
851 #ifdef RUN_IN_PLACE
852         std::string logfile = DEBUGFILE;
853 #else
854         std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
855 #endif
856         if(cmd_args.exists("logfile"))
857                 logfile = cmd_args.get("logfile");
858         if(logfile != "")
859                 debugstreams_init(false, logfile.c_str());
860         else
861                 debugstreams_init(false, NULL);
862
863         infostream<<"logfile    = "<<logfile<<std::endl;
864         infostream<<"path_share = "<<porting::path_share<<std::endl;
865         infostream<<"path_user  = "<<porting::path_user<<std::endl;
866
867         // Initialize debug stacks
868         debug_stacks_init();
869         DSTACK(__FUNCTION_NAME);
870
871         // Debug handler
872         BEGIN_DEBUG_EXCEPTION_HANDLER
873         
874         // List gameids if requested
875         if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list")
876         {
877                 std::set<std::string> gameids = getAvailableGameIds();
878                 for(std::set<std::string>::const_iterator i = gameids.begin();
879                                 i != gameids.end(); i++)
880                         dstream<<(*i)<<std::endl;
881                 return 0;
882         }
883         
884         // Print startup message
885         actionstream<<PROJECT_NAME<<
886                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
887                         <<", "<<BUILD_INFO
888                         <<std::endl;
889         
890         /*
891                 Basic initialization
892         */
893
894         // Initialize default settings
895         set_default_settings(g_settings);
896         
897         // Initialize sockets
898         sockets_init();
899         atexit(sockets_cleanup);
900         
901         /*
902                 Read config file
903         */
904         
905         // Path of configuration file in use
906         std::string configpath = "";
907         
908         if(cmd_args.exists("config"))
909         {
910                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
911                 if(r == false)
912                 {
913                         errorstream<<"Could not read configuration from \""
914                                         <<cmd_args.get("config")<<"\""<<std::endl;
915                         return 1;
916                 }
917                 configpath = cmd_args.get("config");
918         }
919         else
920         {
921                 core::array<std::string> filenames;
922                 filenames.push_back(porting::path_user +
923                                 DIR_DELIM + "minetest.conf");
924                 // Legacy configuration file location
925                 filenames.push_back(porting::path_user +
926                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
927 #ifdef RUN_IN_PLACE
928                 // Try also from a lower level (to aid having the same configuration
929                 // for many RUN_IN_PLACE installs)
930                 filenames.push_back(porting::path_user +
931                                 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
932 #endif
933
934                 for(u32 i=0; i<filenames.size(); i++)
935                 {
936                         bool r = g_settings->readConfigFile(filenames[i].c_str());
937                         if(r)
938                         {
939                                 configpath = filenames[i];
940                                 break;
941                         }
942                 }
943                 
944                 // If no path found, use the first one (menu creates the file)
945                 if(configpath == "")
946                         configpath = filenames[0];
947         }
948
949         // Initialize random seed
950         srand(time(0));
951         mysrand(time(0));
952
953         /*
954                 Run unit tests
955         */
956
957         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
958                         || cmd_args.getFlag("enable-unittests") == true)
959         {
960                 run_tests();
961         }
962         
963         /*
964                 Game parameters
965         */
966
967         // Port
968         u16 port = 30000;
969         if(cmd_args.exists("port"))
970                 port = cmd_args.getU16("port");
971         else if(g_settings->exists("port"))
972                 port = g_settings->getU16("port");
973         if(port == 0)
974                 port = 30000;
975         
976         // World directory
977         std::string commanded_world = "";
978         if(cmd_args.exists("world"))
979                 commanded_world = cmd_args.get("world");
980         else if(cmd_args.exists("map-dir"))
981                 commanded_world = cmd_args.get("map-dir");
982         else if(cmd_args.exists("nonopt0"))
983                 commanded_world = cmd_args.get("nonopt0");
984         else if(g_settings->exists("map-dir"))
985                 commanded_world = g_settings->get("map-dir");
986         
987         // Strip world.mt from commanded_world
988         {
989                 std::string worldmt = "world.mt";
990                 if(commanded_world.size() > worldmt.size() &&
991                                 commanded_world.substr(commanded_world.size()-worldmt.size())
992                                 == worldmt){
993                         dstream<<"Supplied world.mt file - stripping it off."<<std::endl;
994                         commanded_world = commanded_world.substr(
995                                         0, commanded_world.size()-worldmt.size());
996                 }
997         }
998         
999         // Gamespec
1000         SubgameSpec commanded_gamespec;
1001         if(cmd_args.exists("gameid")){
1002                 std::string gameid = cmd_args.get("gameid");
1003                 commanded_gamespec = findSubgame(gameid);
1004                 if(!commanded_gamespec.isValid()){
1005                         errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl;
1006                         return 1;
1007                 }
1008         }
1009
1010         /*
1011                 Run dedicated server if asked to or no other option
1012         */
1013 #ifdef SERVER
1014         bool run_dedicated_server = true;
1015 #else
1016         bool run_dedicated_server = cmd_args.getFlag("server");
1017 #endif
1018         if(run_dedicated_server)
1019         {
1020                 DSTACK("Dedicated server branch");
1021                 // Create time getter if built with Irrlicht
1022 #ifndef SERVER
1023                 g_timegetter = new SimpleTimeGetter();
1024 #endif
1025
1026                 // World directory
1027                 std::string world_path;
1028                 bool is_legacy_world = false;
1029                 if(commanded_world != ""){
1030                         world_path = commanded_world;
1031                 }
1032                 else{
1033                         // No specific world was commanded
1034                         // Check if the world is found from the default directory, and if
1035                         // not, see if the legacy world directory exists.
1036                         world_path = porting::path_user + DIR_DELIM + "server" + DIR_DELIM + "worlds" + DIR_DELIM + "world";
1037                         std::string legacy_world_path = porting::path_user+DIR_DELIM+".."+DIR_DELIM+"world";
1038                         if(!fs::PathExists(world_path) && fs::PathExists(legacy_world_path)){
1039                                 errorstream<<"Warning: Using legacy world directory \""
1040                                                 <<legacy_world_path<<"\""<<std::endl;
1041                                 world_path = legacy_world_path;
1042                                 is_legacy_world = true;
1043                         }
1044                 }
1045
1046                 if(world_path == ""){
1047                         errorstream<<"No world path specified or found."<<std::endl;
1048                         return 1;
1049                 }
1050
1051                 // Gamespec
1052                 std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
1053                 SubgameSpec gamespec = findSubgame(world_gameid);
1054                 if(commanded_gamespec.isValid() &&
1055                                 commanded_gamespec.id != world_gameid){
1056                         errorstream<<"WARNING: Overriding gameid from \""
1057                                         <<world_gameid<<"\" to \""
1058                                         <<commanded_gamespec.id<<"\""<<std::endl;
1059                         gamespec = commanded_gamespec;
1060                 }
1061
1062                 if(!gamespec.isValid()){
1063                         errorstream<<"Invalid gamespec. (world_gameid="
1064                                         <<world_gameid<<")"<<std::endl;
1065                         return 1;
1066                 }
1067                 
1068                 infostream<<"Using gamespec \""<<gamespec.id<<"\""<<std::endl;
1069
1070                 // Create server
1071                 Server server(world_path, configpath, gamespec, false);
1072                 server.start(port);
1073                 
1074                 // Run server
1075                 dedicated_server_loop(server, kill);
1076
1077                 return 0;
1078         }
1079
1080 #ifndef SERVER // Exclude from dedicated server build
1081
1082         /*
1083                 More parameters
1084         */
1085         
1086         std::string address = g_settings->get("address");
1087         if(commanded_world != "")
1088                 address = "";
1089         else if(cmd_args.exists("address"))
1090                 address = cmd_args.get("address");
1091         else if(cmd_args.exists("world"))
1092                 address = "";
1093         
1094         std::string playername = g_settings->get("name");
1095         if(cmd_args.exists("name"))
1096                 playername = cmd_args.get("name");
1097         
1098         bool skip_main_menu = cmd_args.getFlag("go");
1099
1100         /*
1101                 Device initialization
1102         */
1103
1104         // Resolution selection
1105         
1106         bool fullscreen = false;
1107         u16 screenW = g_settings->getU16("screenW");
1108         u16 screenH = g_settings->getU16("screenH");
1109
1110         // Determine driver
1111
1112         video::E_DRIVER_TYPE driverType;
1113         
1114         std::string driverstring = g_settings->get("video_driver");
1115
1116         if(driverstring == "null")
1117                 driverType = video::EDT_NULL;
1118         else if(driverstring == "software")
1119                 driverType = video::EDT_SOFTWARE;
1120         else if(driverstring == "burningsvideo")
1121                 driverType = video::EDT_BURNINGSVIDEO;
1122         else if(driverstring == "direct3d8")
1123                 driverType = video::EDT_DIRECT3D8;
1124         else if(driverstring == "direct3d9")
1125                 driverType = video::EDT_DIRECT3D9;
1126         else if(driverstring == "opengl")
1127                 driverType = video::EDT_OPENGL;
1128         else
1129         {
1130                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1131                                 "to opengl"<<std::endl;
1132                 driverType = video::EDT_OPENGL;
1133         }
1134
1135         /*
1136                 Create device and exit if creation failed
1137         */
1138
1139         MyEventReceiver receiver;
1140
1141         IrrlichtDevice *device;
1142         device = createDevice(driverType,
1143                         core::dimension2d<u32>(screenW, screenH),
1144                         16, fullscreen, false, false, &receiver);
1145
1146         if (device == 0)
1147                 return 1; // could not create selected driver.
1148         
1149         /*
1150                 Continue initialization
1151         */
1152
1153         video::IVideoDriver* driver = device->getVideoDriver();
1154
1155         // Disable mipmaps (because some of them look ugly)
1156         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1157
1158         /*
1159                 This changes the minimum allowed number of vertices in a VBO.
1160                 Default is 500.
1161         */
1162         //driver->setMinHardwareBufferVertexCount(50);
1163
1164         // Create time getter
1165         g_timegetter = new IrrlichtTimeGetter(device);
1166         
1167         // Create game callback for menus
1168         g_gamecallback = new MainGameCallback(device);
1169         
1170         /*
1171                 Speed tests (done after irrlicht is loaded to get timer)
1172         */
1173         if(cmd_args.getFlag("speedtests"))
1174         {
1175                 dstream<<"Running speed tests"<<std::endl;
1176                 SpeedTests();
1177                 return 0;
1178         }
1179         
1180         device->setResizable(true);
1181
1182         bool random_input = g_settings->getBool("random_input")
1183                         || cmd_args.getFlag("random-input");
1184         InputHandler *input = NULL;
1185         if(random_input)
1186                 input = new RandomInputHandler();
1187         else
1188                 input = new RealInputHandler(device, &receiver);
1189         
1190         scene::ISceneManager* smgr = device->getSceneManager();
1191
1192         guienv = device->getGUIEnvironment();
1193         gui::IGUISkin* skin = guienv->getSkin();
1194         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1195         if(font)
1196                 skin->setFont(font);
1197         else
1198                 errorstream<<"WARNING: Font file was not found."
1199                                 " Using default font."<<std::endl;
1200         // If font was not found, this will get us one
1201         font = skin->getFont();
1202         assert(font);
1203         
1204         u32 text_height = font->getDimension(L"Hello, world!").Height;
1205         infostream<<"text_height="<<text_height<<std::endl;
1206
1207         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1208         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1209         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1210         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1211         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1212         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1213         skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1214         skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1215         
1216         /*
1217                 GUI stuff
1218         */
1219
1220         ChatBackend chat_backend;
1221
1222         /*
1223                 If an error occurs, this is set to something and the
1224                 menu-game loop is restarted. It is then displayed before
1225                 the menu.
1226         */
1227         std::wstring error_message = L"";
1228
1229         // The password entered during the menu screen,
1230         std::string password;
1231
1232         bool first_loop = true;
1233
1234         /*
1235                 Menu-game loop
1236         */
1237         while(device->run() && kill == false)
1238         {
1239                 // Set the window caption
1240                 device->setWindowCaption(L"Minetest [Main Menu]");
1241
1242                 // This is used for catching disconnects
1243                 try
1244                 {
1245
1246                         /*
1247                                 Clear everything from the GUIEnvironment
1248                         */
1249                         guienv->clear();
1250                         
1251                         /*
1252                                 We need some kind of a root node to be able to add
1253                                 custom gui elements directly on the screen.
1254                                 Otherwise they won't be automatically drawn.
1255                         */
1256                         guiroot = guienv->addStaticText(L"",
1257                                         core::rect<s32>(0, 0, 10000, 10000));
1258                         
1259                         SubgameSpec gamespec;
1260                         WorldSpec worldspec;
1261                         bool simple_singleplayer_mode = false;
1262
1263                         // These are set up based on the menu and other things
1264                         std::string current_playername = "inv£lid";
1265                         std::string current_password = "";
1266                         std::string current_address = "does-not-exist";
1267                         int current_port = 0;
1268
1269                         /*
1270                                 Out-of-game menu loop.
1271
1272                                 Loop quits when menu returns proper parameters.
1273                         */
1274                         while(kill == false)
1275                         {
1276                                 // If skip_main_menu, only go through here once
1277                                 if(skip_main_menu && !first_loop){
1278                                         kill = true;
1279                                         break;
1280                                 }
1281                                 first_loop = false;
1282                                 
1283                                 // Cursor can be non-visible when coming from the game
1284                                 device->getCursorControl()->setVisible(true);
1285                                 // Some stuff are left to scene manager when coming from the game
1286                                 // (map at least?)
1287                                 smgr->clear();
1288                                 
1289                                 // Initialize menu data
1290                                 MainMenuData menudata;
1291                                 if(g_settings->exists("selected_mainmenu_tab"))
1292                                         menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
1293                                 menudata.address = narrow_to_wide(address);
1294                                 menudata.name = narrow_to_wide(playername);
1295                                 menudata.port = narrow_to_wide(itos(port));
1296                                 if(cmd_args.exists("password"))
1297                                         menudata.password = narrow_to_wide(cmd_args.get("password"));
1298                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1299                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1300                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1301                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1302                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1303                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1304                                 // Default to selecting nothing
1305                                 menudata.selected_world = -1;
1306                                 // Get world listing for the menu
1307                                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1308                                 // If there is only one world, select it
1309                                 if(worldspecs.size() == 1){
1310                                         menudata.selected_world = 0;
1311                                 }
1312                                 // Otherwise try to select according to selected_world_path
1313                                 else if(g_settings->exists("selected_world_path")){
1314                                         std::string trypath = g_settings->get("selected_world_path");
1315                                         for(u32 i=0; i<worldspecs.size(); i++){
1316                                                 if(worldspecs[i].path == trypath){
1317                                                         menudata.selected_world = i;
1318                                                         break;
1319                                                 }
1320                                         }
1321                                 }
1322                                 // If a world was commanded, append and select it
1323                                 if(commanded_world != ""){
1324                                         std::string gameid = getWorldGameId(commanded_world, true);
1325                                         std::string name = "[--world parameter]";
1326                                         if(gameid == ""){
1327                                                 gameid = g_settings->get("default_game");
1328                                                 name += " [new]";
1329                                         }
1330                                         WorldSpec spec(commanded_world, name, gameid);
1331                                         worldspecs.push_back(spec);
1332                                         menudata.selected_world = worldspecs.size()-1;
1333                                 }
1334                                 // Copy worldspecs to menu
1335                                 menudata.worlds = worldspecs;
1336
1337                                 if(skip_main_menu == false)
1338                                 {
1339                                         GUIMainMenu *menu =
1340                                                         new GUIMainMenu(guienv, guiroot, -1, 
1341                                                                 &g_menumgr, &menudata, g_gamecallback);
1342                                         menu->allowFocusRemoval(true);
1343
1344                                         if(error_message != L"")
1345                                         {
1346                                                 verbosestream<<"error_message = "
1347                                                                 <<wide_to_narrow(error_message)<<std::endl;
1348
1349                                                 GUIMessageMenu *menu2 =
1350                                                                 new GUIMessageMenu(guienv, guiroot, -1, 
1351                                                                         &g_menumgr, error_message.c_str());
1352                                                 menu2->drop();
1353                                                 error_message = L"";
1354                                         }
1355
1356                                         video::IVideoDriver* driver = device->getVideoDriver();
1357                                         
1358                                         infostream<<"Created main menu"<<std::endl;
1359
1360                                         while(device->run() && kill == false)
1361                                         {
1362                                                 if(menu->getStatus() == true)
1363                                                         break;
1364
1365                                                 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1366                                                 driver->beginScene(true, true, video::SColor(255,128,128,128));
1367
1368                                                 drawMenuBackground(driver);
1369
1370                                                 guienv->drawAll();
1371                                                 
1372                                                 driver->endScene();
1373                                                 
1374                                                 // On some computers framerate doesn't seem to be
1375                                                 // automatically limited
1376                                                 sleep_ms(25);
1377                                         }
1378                                         
1379                                         infostream<<"Dropping main menu"<<std::endl;
1380
1381                                         menu->drop();
1382                                 }
1383
1384                                 playername = wide_to_narrow(menudata.name);
1385                                 password = translatePassword(playername, menudata.password);
1386                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1387
1388                                 address = wide_to_narrow(menudata.address);
1389                                 int newport = stoi(wide_to_narrow(menudata.port));
1390                                 if(newport != 0)
1391                                         port = newport;
1392                                 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1393                                 // Save settings
1394                                 g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
1395                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1396                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1397                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1398                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1399                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1400                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1401                                 g_settings->set("name", playername);
1402                                 g_settings->set("address", address);
1403                                 g_settings->set("port", itos(port));
1404                                 if(menudata.selected_world != -1)
1405                                         g_settings->set("selected_world_path",
1406                                                         worldspecs[menudata.selected_world].path);
1407                                 
1408                                 // Break out of menu-game loop to shut down cleanly
1409                                 if(device->run() == false || kill == true)
1410                                         break;
1411                                 
1412                                 current_playername = playername;
1413                                 current_password = password;
1414                                 current_address = address;
1415                                 current_port = port;
1416
1417                                 // If using simple singleplayer mode, override
1418                                 if(simple_singleplayer_mode){
1419                                         current_playername = "singleplayer";
1420                                         current_password = "";
1421                                         current_address = "";
1422                                         current_port = 30011;
1423                                 }
1424                                 
1425                                 // Set world path to selected one
1426                                 if(menudata.selected_world != -1){
1427                                         worldspec = worldspecs[menudata.selected_world];
1428                                         infostream<<"Selected world: "<<worldspec.name
1429                                                         <<" ["<<worldspec.path<<"]"<<std::endl;
1430                                 }
1431                                 
1432                                 // Delete world if requested
1433                                 if(menudata.delete_world_spec.isValid())
1434                                 {
1435                                         bool r = fs::RecursiveDeleteContent(
1436                                                         menudata.delete_world_spec.path);
1437                                         if(r == false){
1438                                                 error_message = L"World delete failed";
1439                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1440                                         }
1441                                         continue;
1442                                 }
1443
1444                                 // Create new world if requested
1445                                 if(menudata.create_world_name != L"")
1446                                 {
1447                                         std::string path = porting::path_user + DIR_DELIM
1448                                                         + "server" + DIR_DELIM + "worlds" + DIR_DELIM
1449                                                         + wide_to_narrow(menudata.create_world_name);
1450                                         // Create world if it doesn't exist
1451                                         if(!initializeWorld(path, menudata.create_world_gameid)){
1452                                                 error_message = L"Failed to initialize world";
1453                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1454                                                 continue;
1455                                         }
1456                                         g_settings->set("selected_world_path", path);
1457                                         continue;
1458                                 }
1459
1460                                 // If local game
1461                                 if(current_address == "")
1462                                 {
1463                                         if(menudata.selected_world == -1){
1464                                                 error_message = L"No world selected and no address "
1465                                                                 L"provided. Nothing to do.";
1466                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1467                                                 continue;
1468                                         }
1469                                         // Load gamespec for required game
1470                                         gamespec = findSubgame(worldspec.gameid);
1471                                         if(!gamespec.isValid() && !commanded_gamespec.isValid()){
1472                                                 error_message = L"Could not find or load game \""
1473                                                                 + narrow_to_wide(worldspec.gameid) + L"\"";
1474                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1475                                                 continue;
1476                                         }
1477                                         if(commanded_gamespec.isValid() &&
1478                                                         commanded_gamespec.id != worldspec.gameid){
1479                                                 errorstream<<"WARNING: Overriding gamespec from \""
1480                                                                 <<worldspec.gameid<<"\" to \""
1481                                                                 <<commanded_gamespec.id<<"\""<<std::endl;
1482                                                 gamespec = commanded_gamespec;
1483                                         }
1484
1485                                         if(!gamespec.isValid()){
1486                                                 error_message = L"Invalid gamespec. (world_gameid="
1487                                                                 +narrow_to_wide(worldspec.gameid)+L")";
1488                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1489                                                 continue;
1490                                         }
1491                                 }
1492
1493                                 // Continue to game
1494                                 break;
1495                         }
1496                         
1497                         // Break out of menu-game loop to shut down cleanly
1498                         if(device->run() == false || kill == true)
1499                                 break;
1500
1501                         /*
1502                                 Run game
1503                         */
1504                         the_game(
1505                                 kill,
1506                                 random_input,
1507                                 input,
1508                                 device,
1509                                 font,
1510                                 worldspec.path,
1511                                 current_playername,
1512                                 current_password,
1513                                 current_address,
1514                                 current_port,
1515                                 error_message,
1516                                 configpath,
1517                                 chat_backend,
1518                                 gamespec,
1519                                 simple_singleplayer_mode
1520                         );
1521
1522                 } //try
1523                 catch(con::PeerNotFoundException &e)
1524                 {
1525                         error_message = L"Connection error (timed out?)";
1526                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1527                 }
1528                 catch(ServerError &e)
1529                 {
1530                         error_message = narrow_to_wide(e.what());
1531                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1532                 }
1533                 catch(ModError &e)
1534                 {
1535                         errorstream<<e.what()<<std::endl;
1536                         error_message = narrow_to_wide(e.what()) + L"\nCheck debug.txt for details.";
1537                 }
1538 #ifdef NDEBUG
1539                 catch(std::exception &e)
1540                 {
1541                         std::string narrow_message = "Some exception, what()=\"";
1542                         narrow_message += e.what();
1543                         narrow_message += "\"";
1544                         errorstream<<narrow_message<<std::endl;
1545                         error_message = narrow_to_wide(narrow_message);
1546                 }
1547 #endif
1548
1549                 // If no main menu, show error and exit
1550                 if(skip_main_menu)
1551                 {
1552                         if(error_message != L""){
1553                                 verbosestream<<"error_message = "
1554                                                 <<wide_to_narrow(error_message)<<std::endl;
1555                                 retval = 1;
1556                         }
1557                         break;
1558                 }
1559         } // Menu-game loop
1560         
1561         delete input;
1562
1563         /*
1564                 In the end, delete the Irrlicht device.
1565         */
1566         device->drop();
1567
1568 #endif // !SERVER
1569         
1570         // Update configuration file
1571         if(configpath != "")
1572                 g_settings->updateConfigFile(configpath.c_str());
1573         
1574         // Print modified quicktune values
1575         {
1576                 bool header_printed = false;
1577                 std::vector<std::string> names = getQuicktuneNames();
1578                 for(u32 i=0; i<names.size(); i++){
1579                         QuicktuneValue val = getQuicktuneValue(names[i]);
1580                         if(!val.modified)
1581                                 continue;
1582                         if(!header_printed){
1583                                 dstream<<"Modified quicktune values:"<<std::endl;
1584                                 header_printed = true;
1585                         }
1586                         dstream<<names[i]<<" = "<<val.getString()<<std::endl;
1587                 }
1588         }
1589
1590         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1591         
1592         debugstreams_deinit();
1593         
1594         return retval;
1595 }
1596
1597 //END
1598