]> git.lizzy.rs Git - minetest.git/blob - src/main.cpp
Menu tuning WIP
[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                 // Gamespec
1047                 std::string world_gameid = getWorldGameId(world_path, is_legacy_world);
1048                 SubgameSpec gamespec = findSubgame(world_gameid);
1049                 if(commanded_gamespec.isValid() &&
1050                                 commanded_gamespec.id != world_gameid){
1051                         errorstream<<"WARNING: Overriding gameid from \""
1052                                         <<world_gameid<<"\" to \""
1053                                         <<commanded_gamespec.id<<"\""<<std::endl;
1054                         gamespec = commanded_gamespec;
1055                 }
1056
1057                 if(!gamespec.isValid()){
1058                         errorstream<<"Invalid gamespec. (world_gameid="
1059                                         <<world_gameid<<")"<<std::endl;
1060                         return 1;
1061                 }
1062                 
1063                 infostream<<"Using gamespec \""<<gamespec.id<<"\""<<std::endl;
1064
1065                 // Create server
1066                 Server server(world_path, configpath, gamespec);
1067                 server.start(port);
1068                 
1069                 // Run server
1070                 dedicated_server_loop(server, kill);
1071
1072                 return 0;
1073         }
1074
1075 #ifndef SERVER // Exclude from dedicated server build
1076
1077         /*
1078                 More parameters
1079         */
1080         
1081         std::string address = g_settings->get("address");
1082         if(commanded_world != "")
1083                 address = "";
1084         else if(cmd_args.exists("address"))
1085                 address = cmd_args.get("address");
1086         else if(cmd_args.exists("world"))
1087                 address = "";
1088         
1089         std::string playername = g_settings->get("name");
1090         if(cmd_args.exists("name"))
1091                 playername = cmd_args.get("name");
1092         
1093         bool skip_main_menu = cmd_args.getFlag("go");
1094
1095         /*
1096                 Device initialization
1097         */
1098
1099         // Resolution selection
1100         
1101         bool fullscreen = false;
1102         u16 screenW = g_settings->getU16("screenW");
1103         u16 screenH = g_settings->getU16("screenH");
1104
1105         // Determine driver
1106
1107         video::E_DRIVER_TYPE driverType;
1108         
1109         std::string driverstring = g_settings->get("video_driver");
1110
1111         if(driverstring == "null")
1112                 driverType = video::EDT_NULL;
1113         else if(driverstring == "software")
1114                 driverType = video::EDT_SOFTWARE;
1115         else if(driverstring == "burningsvideo")
1116                 driverType = video::EDT_BURNINGSVIDEO;
1117         else if(driverstring == "direct3d8")
1118                 driverType = video::EDT_DIRECT3D8;
1119         else if(driverstring == "direct3d9")
1120                 driverType = video::EDT_DIRECT3D9;
1121         else if(driverstring == "opengl")
1122                 driverType = video::EDT_OPENGL;
1123         else
1124         {
1125                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1126                                 "to opengl"<<std::endl;
1127                 driverType = video::EDT_OPENGL;
1128         }
1129
1130         /*
1131                 Create device and exit if creation failed
1132         */
1133
1134         MyEventReceiver receiver;
1135
1136         IrrlichtDevice *device;
1137         device = createDevice(driverType,
1138                         core::dimension2d<u32>(screenW, screenH),
1139                         16, fullscreen, false, false, &receiver);
1140
1141         if (device == 0)
1142                 return 1; // could not create selected driver.
1143         
1144         /*
1145                 Continue initialization
1146         */
1147
1148         video::IVideoDriver* driver = device->getVideoDriver();
1149
1150         // Disable mipmaps (because some of them look ugly)
1151         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1152
1153         /*
1154                 This changes the minimum allowed number of vertices in a VBO.
1155                 Default is 500.
1156         */
1157         //driver->setMinHardwareBufferVertexCount(50);
1158
1159         // Create time getter
1160         g_timegetter = new IrrlichtTimeGetter(device);
1161         
1162         // Create game callback for menus
1163         g_gamecallback = new MainGameCallback(device);
1164         
1165         /*
1166                 Speed tests (done after irrlicht is loaded to get timer)
1167         */
1168         if(cmd_args.getFlag("speedtests"))
1169         {
1170                 dstream<<"Running speed tests"<<std::endl;
1171                 SpeedTests();
1172                 return 0;
1173         }
1174         
1175         device->setResizable(true);
1176
1177         bool random_input = g_settings->getBool("random_input")
1178                         || cmd_args.getFlag("random-input");
1179         InputHandler *input = NULL;
1180         if(random_input)
1181                 input = new RandomInputHandler();
1182         else
1183                 input = new RealInputHandler(device, &receiver);
1184         
1185         scene::ISceneManager* smgr = device->getSceneManager();
1186
1187         guienv = device->getGUIEnvironment();
1188         gui::IGUISkin* skin = guienv->getSkin();
1189         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1190         if(font)
1191                 skin->setFont(font);
1192         else
1193                 errorstream<<"WARNING: Font file was not found."
1194                                 " Using default font."<<std::endl;
1195         // If font was not found, this will get us one
1196         font = skin->getFont();
1197         assert(font);
1198         
1199         u32 text_height = font->getDimension(L"Hello, world!").Height;
1200         infostream<<"text_height="<<text_height<<std::endl;
1201
1202         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1203         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1204         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1205         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1206         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1207         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1208         skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1209         skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1210         
1211         /*
1212                 GUI stuff
1213         */
1214
1215         ChatBackend chat_backend;
1216
1217         /*
1218                 If an error occurs, this is set to something and the
1219                 menu-game loop is restarted. It is then displayed before
1220                 the menu.
1221         */
1222         std::wstring error_message = L"";
1223
1224         // The password entered during the menu screen,
1225         std::string password;
1226
1227         bool first_loop = true;
1228
1229         /*
1230                 Menu-game loop
1231         */
1232         while(device->run() && kill == false)
1233         {
1234                 // Set the window caption
1235                 device->setWindowCaption(L"Minetest [Main Menu]");
1236
1237                 // This is used for catching disconnects
1238                 try
1239                 {
1240
1241                         /*
1242                                 Clear everything from the GUIEnvironment
1243                         */
1244                         guienv->clear();
1245                         
1246                         /*
1247                                 We need some kind of a root node to be able to add
1248                                 custom gui elements directly on the screen.
1249                                 Otherwise they won't be automatically drawn.
1250                         */
1251                         guiroot = guienv->addStaticText(L"",
1252                                         core::rect<s32>(0, 0, 10000, 10000));
1253                         
1254                         SubgameSpec gamespec;
1255                         WorldSpec worldspec;
1256
1257                         /*
1258                                 Out-of-game menu loop.
1259
1260                                 Loop quits when menu returns proper parameters.
1261                         */
1262                         while(kill == false)
1263                         {
1264                                 // If skip_main_menu, only go through here once
1265                                 if(skip_main_menu && !first_loop){
1266                                         kill = true;
1267                                         break;
1268                                 }
1269                                 first_loop = false;
1270                                 
1271                                 // Cursor can be non-visible when coming from the game
1272                                 device->getCursorControl()->setVisible(true);
1273                                 // Some stuff are left to scene manager when coming from the game
1274                                 // (map at least?)
1275                                 smgr->clear();
1276                                 
1277                                 // Initialize menu data
1278                                 MainMenuData menudata;
1279                                 menudata.address = narrow_to_wide(address);
1280                                 menudata.name = narrow_to_wide(playername);
1281                                 menudata.port = narrow_to_wide(itos(port));
1282                                 if(cmd_args.exists("password"))
1283                                         menudata.password = narrow_to_wide(cmd_args.get("password"));
1284                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1285                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1286                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1287                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1288                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1289                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1290                                 // Default to selecting nothing
1291                                 menudata.selected_world = -1;
1292                                 // Get world listing for the menu
1293                                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1294                                 // If there is only one world, select it
1295                                 if(worldspecs.size() == 1){
1296                                         menudata.selected_world = 0;
1297                                 }
1298                                 // Otherwise try to select according to selected_world_path
1299                                 else if(g_settings->exists("selected_world_path")){
1300                                         std::string trypath = g_settings->get("selected_world_path");
1301                                         for(u32 i=0; i<worldspecs.size(); i++){
1302                                                 if(worldspecs[i].path == trypath){
1303                                                         menudata.selected_world = i;
1304                                                         break;
1305                                                 }
1306                                         }
1307                                 }
1308                                 // If a world was commanded, append and select it
1309                                 if(commanded_world != ""){
1310                                         std::string gameid = getWorldGameId(commanded_world, true);
1311                                         std::string name = "[--world parameter]";
1312                                         if(gameid == ""){
1313                                                 gameid = g_settings->get("default_game");
1314                                                 name += " [new]";
1315                                         }
1316                                         WorldSpec spec(commanded_world, name, gameid);
1317                                         worldspecs.push_back(spec);
1318                                         menudata.selected_world = worldspecs.size()-1;
1319                                 }
1320                                 // Copy worldspecs to menu
1321                                 menudata.worlds = worldspecs;
1322
1323                                 if(skip_main_menu == false)
1324                                 {
1325                                         GUIMainMenu *menu =
1326                                                         new GUIMainMenu(guienv, guiroot, -1, 
1327                                                                 &g_menumgr, &menudata, g_gamecallback);
1328                                         menu->allowFocusRemoval(true);
1329
1330                                         if(error_message != L"")
1331                                         {
1332                                                 verbosestream<<"error_message = "
1333                                                                 <<wide_to_narrow(error_message)<<std::endl;
1334
1335                                                 GUIMessageMenu *menu2 =
1336                                                                 new GUIMessageMenu(guienv, guiroot, -1, 
1337                                                                         &g_menumgr, error_message.c_str());
1338                                                 menu2->drop();
1339                                                 error_message = L"";
1340                                         }
1341
1342                                         video::IVideoDriver* driver = device->getVideoDriver();
1343                                         
1344                                         infostream<<"Created main menu"<<std::endl;
1345
1346                                         while(device->run() && kill == false)
1347                                         {
1348                                                 if(menu->getStatus() == true)
1349                                                         break;
1350
1351                                                 //driver->beginScene(true, true, video::SColor(255,0,0,0));
1352                                                 driver->beginScene(true, true, video::SColor(255,128,128,128));
1353
1354                                                 drawMenuBackground(driver);
1355
1356                                                 guienv->drawAll();
1357                                                 
1358                                                 driver->endScene();
1359                                                 
1360                                                 // On some computers framerate doesn't seem to be
1361                                                 // automatically limited
1362                                                 sleep_ms(25);
1363                                         }
1364                                         
1365                                         // Break out of menu-game loop to shut down cleanly
1366                                         if(device->run() == false || kill == true)
1367                                                 break;
1368                                         
1369                                         infostream<<"Dropping main menu"<<std::endl;
1370
1371                                         menu->drop();
1372                                 }
1373
1374                                 // Set world path to selected one
1375                                 if(menudata.selected_world != -1){
1376                                         worldspec = worldspecs[menudata.selected_world];
1377                                         infostream<<"Selected world: "<<worldspec.name
1378                                                         <<" ["<<worldspec.path<<"]"<<std::endl;
1379                                 }
1380                                 
1381                                 // Delete world if requested
1382                                 if(menudata.delete_world_spec.isValid())
1383                                 {
1384                                         bool r = fs::RecursiveDeleteContent(
1385                                                         menudata.delete_world_spec.path);
1386                                         if(r == false){
1387                                                 error_message = L"World delete failed";
1388                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1389                                         }
1390                                         continue;
1391                                 }
1392
1393                                 // Create new world if requested
1394                                 if(menudata.create_world_name != L"")
1395                                 {
1396                                         std::string path = porting::path_user + DIR_DELIM
1397                                                         + "server" + DIR_DELIM + "worlds" + DIR_DELIM
1398                                                         + wide_to_narrow(menudata.create_world_name);
1399                                         // Create world if it doesn't exist
1400                                         if(!initializeWorld(path, menudata.create_world_gameid)){
1401                                                 error_message = L"Failed to initialize world";
1402                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1403                                                 continue;
1404                                         }
1405                                         g_settings->set("selected_world_path", path);
1406                                         continue;
1407                                 }
1408
1409                                 playername = wide_to_narrow(menudata.name);
1410                                 password = translatePassword(playername, menudata.password);
1411                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1412
1413                                 address = wide_to_narrow(menudata.address);
1414                                 int newport = stoi(wide_to_narrow(menudata.port));
1415                                 if(newport != 0)
1416                                         port = newport;
1417                                 // Save settings
1418                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1419                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1420                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1421                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1422                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1423                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1424                                 g_settings->set("name", playername);
1425                                 g_settings->set("address", address);
1426                                 g_settings->set("port", itos(port));
1427                                 if(menudata.selected_world != -1)
1428                                         g_settings->set("selected_world_path",
1429                                                         worldspecs[menudata.selected_world].path);
1430                                 // Update configuration file
1431                                 if(configpath != "")
1432                                         g_settings->updateConfigFile(configpath.c_str());
1433                                 
1434                                 // If local game
1435                                 if(address == "")
1436                                 {
1437                                         if(menudata.selected_world == -1){
1438                                                 error_message = L"No world selected and no address "
1439                                                                 L"provided. Nothing to do.";
1440                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1441                                                 continue;
1442                                         }
1443                                         // Load gamespec for required game
1444                                         gamespec = findSubgame(worldspec.gameid);
1445                                         if(!gamespec.isValid() && !commanded_gamespec.isValid()){
1446                                                 error_message = L"Could not find or load game \""
1447                                                                 + narrow_to_wide(worldspec.gameid) + L"\"";
1448                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1449                                                 continue;
1450                                         }
1451                                         if(commanded_gamespec.isValid() &&
1452                                                         commanded_gamespec.id != worldspec.gameid){
1453                                                 errorstream<<"WARNING: Overriding gamespec from \""
1454                                                                 <<worldspec.gameid<<"\" to \""
1455                                                                 <<commanded_gamespec.id<<"\""<<std::endl;
1456                                                 gamespec = commanded_gamespec;
1457                                         }
1458
1459                                         if(!gamespec.isValid()){
1460                                                 error_message = L"Invalid gamespec. (world_gameid="
1461                                                                 +narrow_to_wide(worldspec.gameid)+L")";
1462                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1463                                                 continue;
1464                                         }
1465                                 }
1466
1467                                 // Continue to game
1468                                 break;
1469                         }
1470                         
1471                         // Break out of menu-game loop to shut down cleanly
1472                         if(device->run() == false || kill == true)
1473                                 break;
1474                         
1475                         /*
1476                                 Run game
1477                         */
1478                         the_game(
1479                                 kill,
1480                                 random_input,
1481                                 input,
1482                                 device,
1483                                 font,
1484                                 worldspec.path,
1485                                 playername,
1486                                 password,
1487                                 address,
1488                                 port,
1489                                 error_message,
1490                                 configpath,
1491                                 chat_backend,
1492                                 gamespec
1493                         );
1494
1495                 } //try
1496                 catch(con::PeerNotFoundException &e)
1497                 {
1498                         error_message = L"Connection error (timed out?)";
1499                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1500                 }
1501                 catch(ServerError &e)
1502                 {
1503                         error_message = narrow_to_wide(e.what());
1504                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1505                 }
1506                 catch(ModError &e)
1507                 {
1508                         errorstream<<e.what()<<std::endl;
1509                         error_message = narrow_to_wide(e.what()) + L"\nCheck debug.txt for details.";
1510                 }
1511 #ifdef NDEBUG
1512                 catch(std::exception &e)
1513                 {
1514                         std::string narrow_message = "Some exception, what()=\"";
1515                         narrow_message += e.what();
1516                         narrow_message += "\"";
1517                         errorstream<<narrow_message<<std::endl;
1518                         error_message = narrow_to_wide(narrow_message);
1519                 }
1520 #endif
1521
1522                 // If no main menu, show error and exit
1523                 if(skip_main_menu)
1524                 {
1525                         if(error_message != L""){
1526                                 verbosestream<<"error_message = "
1527                                                 <<wide_to_narrow(error_message)<<std::endl;
1528                                 retval = 1;
1529                         }
1530                         break;
1531                 }
1532         } // Menu-game loop
1533         
1534         delete input;
1535
1536         /*
1537                 In the end, delete the Irrlicht device.
1538         */
1539         device->drop();
1540
1541 #endif // !SERVER
1542         
1543         // Update configuration file
1544         if(configpath != "")
1545                 g_settings->updateConfigFile(configpath.c_str());
1546         
1547         // Print modified quicktune values
1548         {
1549                 bool header_printed = false;
1550                 std::vector<std::string> names = getQuicktuneNames();
1551                 for(u32 i=0; i<names.size(); i++){
1552                         QuicktuneValue val = getQuicktuneValue(names[i]);
1553                         if(!val.modified)
1554                                 continue;
1555                         if(!header_printed){
1556                                 dstream<<"Modified quicktune values:"<<std::endl;
1557                                 header_printed = true;
1558                         }
1559                         dstream<<names[i]<<" = "<<val.getString()<<std::endl;
1560                 }
1561         }
1562
1563         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1564         
1565         debugstreams_deinit();
1566         
1567         return retval;
1568 }
1569
1570 //END
1571