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