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