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