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