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