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