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