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