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