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