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