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