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