]> git.lizzy.rs Git - minetest.git/blob - src/main.cpp
Rework texture generating code, add texture grouping via ( ... )
[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         // 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                                 if (!block) {
1300                                         errorstream << "Failed to load block " << PP(*i) << ", skipping it.";
1301                                 } else {
1302                                         old_map.saveBlock(block, new_db);
1303                                         MapSector *sector = old_map.getSectorNoGenerate(v2s16(i->X, i->Z));
1304                                         sector->deleteBlock(block);
1305                                 }
1306                                 ++count;
1307                                 if (count % 500 == 0)
1308                                         actionstream << "Migrated " << count << " blocks "
1309                                                 << (100.0 * count / blocks.size()) << "% completed" << std::endl;
1310                         }
1311                         new_db->endSave();
1312                         delete new_db;
1313
1314                         actionstream << "Successfully migrated " << count << " blocks" << std::endl;
1315                         world_mt.set("backend", migrate_to);
1316                         if (!world_mt.updateConfigFile((world_path + DIR_DELIM + "world.mt").c_str()))
1317                                 errorstream << "Failed to update world.mt!" << std::endl;
1318                         else
1319                                 actionstream << "world.mt updated" << std::endl;
1320
1321                         return 0;
1322                 }
1323
1324                 server.start(bind_addr);
1325
1326                 // Run server
1327                 dedicated_server_loop(server, kill);
1328
1329                 return 0;
1330         }
1331
1332 #ifndef SERVER // Exclude from dedicated server build
1333
1334         /*
1335                 More parameters
1336         */
1337
1338         std::string address = g_settings->get("address");
1339         if (commanded_world != "")
1340                 address = "";
1341         else if (cmd_args.exists("address"))
1342                 address = cmd_args.get("address");
1343
1344         std::string playername = g_settings->get("name");
1345         if (cmd_args.exists("name"))
1346                 playername = cmd_args.get("name");
1347
1348         bool skip_main_menu = cmd_args.getFlag("go");
1349
1350         /*
1351                 Device initialization
1352         */
1353
1354         // Resolution selection
1355
1356         bool fullscreen = g_settings->getBool("fullscreen");
1357         u16 screenW = g_settings->getU16("screenW");
1358         u16 screenH = g_settings->getU16("screenH");
1359
1360         // bpp, fsaa, vsync
1361
1362         bool vsync = g_settings->getBool("vsync");
1363         u16 bits = g_settings->getU16("fullscreen_bpp");
1364         u16 fsaa = g_settings->getU16("fsaa");
1365
1366         // Determine driver
1367
1368         video::E_DRIVER_TYPE driverType;
1369
1370         std::string driverstring = g_settings->get("video_driver");
1371
1372         if (driverstring == "null")
1373                 driverType = video::EDT_NULL;
1374         else if (driverstring == "software")
1375                 driverType = video::EDT_SOFTWARE;
1376         else if (driverstring == "burningsvideo")
1377                 driverType = video::EDT_BURNINGSVIDEO;
1378         else if (driverstring == "direct3d8")
1379                 driverType = video::EDT_DIRECT3D8;
1380         else if (driverstring == "direct3d9")
1381                 driverType = video::EDT_DIRECT3D9;
1382         else if (driverstring == "opengl")
1383                 driverType = video::EDT_OPENGL;
1384 #ifdef _IRR_COMPILE_WITH_OGLES1_
1385         else if (driverstring == "ogles1")
1386                 driverType = video::EDT_OGLES1;
1387 #endif
1388 #ifdef _IRR_COMPILE_WITH_OGLES2_
1389         else if (driverstring == "ogles2")
1390                 driverType = video::EDT_OGLES2;
1391 #endif
1392         else {
1393                 errorstream << "WARNING: Invalid video_driver specified; defaulting "
1394                         << "to opengl" << std::endl;
1395                 driverType = video::EDT_OPENGL;
1396         }
1397
1398         /*
1399                 List video modes if requested
1400         */
1401
1402         MyEventReceiver* receiver = new MyEventReceiver();
1403
1404         if (cmd_args.getFlag("videomodes")) {
1405                 IrrlichtDevice *nulldevice;
1406
1407                 SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1408                 params.DriverType    = video::EDT_NULL;
1409                 params.WindowSize    = core::dimension2d<u32>(640, 480);
1410                 params.Bits          = 24;
1411                 params.AntiAlias     = fsaa;
1412                 params.Fullscreen    = false;
1413                 params.Stencilbuffer = false;
1414                 params.Vsync         = vsync;
1415                 params.EventReceiver = receiver;
1416                 params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
1417
1418                 nulldevice = createDeviceEx(params);
1419
1420                 if (nulldevice == 0)
1421                         return 1;
1422
1423                 dstream << _("Available video modes (WxHxD):") << std::endl;
1424
1425                 video::IVideoModeList *videomode_list =
1426                                 nulldevice->getVideoModeList();
1427
1428                 if (videomode_list == 0) {
1429                         nulldevice->drop();
1430                         return 1;
1431                 }
1432
1433                 s32 videomode_count = videomode_list->getVideoModeCount();
1434                 core::dimension2d<u32> videomode_res;
1435                 s32 videomode_depth;
1436                 for (s32 i = 0; i < videomode_count; ++i) {
1437                         videomode_res = videomode_list->getVideoModeResolution(i);
1438                         videomode_depth = videomode_list->getVideoModeDepth(i);
1439                         dstream<<videomode_res.Width << "x" << videomode_res.Height
1440                                         << "x" << videomode_depth << std::endl;
1441                 }
1442
1443                 dstream << _("Active video mode (WxHxD):") << std::endl;
1444                 videomode_res = videomode_list->getDesktopResolution();
1445                 videomode_depth = videomode_list->getDesktopDepth();
1446                 dstream << videomode_res.Width << "x" << videomode_res.Height
1447                                 << "x" << videomode_depth << std::endl;
1448
1449                 nulldevice->drop();
1450
1451                 delete receiver;
1452                 return 0;
1453         }
1454
1455         /*
1456                 Create device and exit if creation failed
1457         */
1458         SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
1459         params.DriverType    = driverType;
1460         params.WindowSize    = core::dimension2d<u32>(screenW, screenH);
1461         params.Bits          = bits;
1462         params.AntiAlias     = fsaa;
1463         params.Fullscreen    = fullscreen;
1464         params.Stencilbuffer = false;
1465         params.Vsync         = vsync;
1466         params.EventReceiver = receiver;
1467         params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
1468 #ifdef __ANDROID__
1469         params.PrivateData = porting::app_global;
1470         params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
1471                         "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
1472 #endif
1473
1474         IrrlichtDevice * device = createDeviceEx(params);
1475
1476         if (device == 0) {
1477                 delete receiver;
1478                 return 1; // could not create selected driver.
1479         }
1480
1481         // Map our log level to irrlicht engine one.
1482         static const irr::ELOG_LEVEL irr_log_level[5] = {
1483                 ELL_NONE,
1484                 ELL_ERROR,
1485                 ELL_WARNING,
1486                 ELL_INFORMATION,
1487 #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
1488                 ELL_INFORMATION
1489 #else
1490                 ELL_DEBUG
1491 #endif
1492         };
1493
1494         ILogger* irr_logger = device->getLogger();
1495         irr_logger->setLogLevel(irr_log_level[loglevel]);
1496
1497         porting::initIrrlicht(device);
1498         late_init_default_settings(g_settings);
1499
1500         /*
1501                 Continue initialization
1502         */
1503
1504         video::IVideoDriver* driver = device->getVideoDriver();
1505
1506         /*
1507                 This changes the minimum allowed number of vertices in a VBO.
1508                 Default is 500.
1509         */
1510         //driver->setMinHardwareBufferVertexCount(50);
1511
1512         // Create time getter
1513         g_timegetter = new IrrlichtTimeGetter(device);
1514
1515         // Create game callback for menus
1516         g_gamecallback = new MainGameCallback(device);
1517
1518         /*
1519                 Speed tests (done after irrlicht is loaded to get timer)
1520         */
1521         if (cmd_args.getFlag("speedtests"))
1522         {
1523                 dstream << "Running speed tests" << std::endl;
1524                 SpeedTests();
1525                 device->drop();
1526                 return 0;
1527         }
1528
1529         device->setResizable(true);
1530
1531         bool random_input = g_settings->getBool("random_input")
1532                         || cmd_args.getFlag("random-input");
1533         InputHandler *input = NULL;
1534
1535         if (random_input) {
1536                 input = new RandomInputHandler();
1537         } else {
1538                 input = new RealInputHandler(device,receiver);
1539         }
1540
1541         scene::ISceneManager* smgr = device->getSceneManager();
1542
1543         guienv = device->getGUIEnvironment();
1544         gui::IGUISkin* skin = guienv->getSkin();
1545         std::string font_path = g_settings->get("font_path");
1546         gui::IGUIFont *font;
1547         #if USE_FREETYPE
1548         bool use_freetype = g_settings->getBool("freetype");
1549         if (use_freetype) {
1550                 std::string fallback;
1551                 if (is_yes(gettext("needs_fallback_font")))
1552                         fallback = "fallback_";
1553                 u16 font_size = g_settings->getU16(fallback + "font_size");
1554                 font_path = g_settings->get(fallback + "font_path");
1555                 u32 font_shadow = g_settings->getU16(fallback + "font_shadow");
1556                 u32 font_shadow_alpha = g_settings->getU16(fallback + "font_shadow_alpha");
1557                 font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size,
1558                         true, true, font_shadow, font_shadow_alpha);
1559         } else {
1560                 font = guienv->getFont(font_path.c_str());
1561         }
1562         #else
1563         font = guienv->getFont(font_path.c_str());
1564         #endif
1565         if (font)
1566                 skin->setFont(font);
1567         else
1568                 errorstream << "WARNING: Font file was not found."
1569                                 << " Using default font." << std::endl;
1570         // If font was not found, this will get us one
1571         font = skin->getFont();
1572         assert(font);
1573
1574         u32 text_height = font->getDimension(L"Hello, world!").Height;
1575         infostream << "text_height=" << text_height << std::endl;
1576
1577         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
1578         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 0, 0, 0));
1579         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
1580         skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 100, 50));
1581         skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
1582
1583 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
1584         // Irrlicht 1.8 input colours
1585         skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
1586         skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
1587 #endif
1588
1589
1590         // Create the menu clouds
1591         if (!g_menucloudsmgr)
1592                 g_menucloudsmgr = smgr->createNewSceneManager();
1593         if (!g_menuclouds)
1594                 g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
1595                         g_menucloudsmgr, -1, rand(), 100);
1596         g_menuclouds->update(v2f(0, 0), video::SColor(255, 200, 200, 255));
1597         scene::ICameraSceneNode* camera;
1598         camera = g_menucloudsmgr->addCameraSceneNode(0,
1599                                 v3f(0, 0, 0), v3f(0, 60, 100));
1600         camera->setFarValue(10000);
1601
1602         /*
1603                 GUI stuff
1604         */
1605
1606         ChatBackend chat_backend;
1607
1608         /*
1609                 If an error occurs, this is set to something and the
1610                 menu-game loop is restarted. It is then displayed before
1611                 the menu.
1612         */
1613         std::wstring error_message = L"";
1614
1615         // The password entered during the menu screen,
1616         std::string password;
1617
1618         bool first_loop = true;
1619
1620         /*
1621                 Menu-game loop
1622         */
1623         while (device->run() && (kill == false) &&
1624                         (g_gamecallback->shutdown_requested == false))
1625         {
1626                 // Set the window caption
1627                 wchar_t* text = wgettext("Main Menu");
1628                 device->setWindowCaption((std::wstring(L"Minetest [") + text + L"]").c_str());
1629                 delete[] text;
1630
1631                 // This is used for catching disconnects
1632                 try
1633                 {
1634
1635                         /*
1636                                 Clear everything from the GUIEnvironment
1637                         */
1638                         guienv->clear();
1639
1640                         /*
1641                                 We need some kind of a root node to be able to add
1642                                 custom gui elements directly on the screen.
1643                                 Otherwise they won't be automatically drawn.
1644                         */
1645                         guiroot = guienv->addStaticText(L"",
1646                                 core::rect<s32>(0, 0, 10000, 10000));
1647
1648                         SubgameSpec gamespec;
1649                         WorldSpec worldspec;
1650                         bool simple_singleplayer_mode = false;
1651
1652                         // These are set up based on the menu and other things
1653                         std::string current_playername = "inv£lid";
1654                         std::string current_password = "";
1655                         std::string current_address = "does-not-exist";
1656                         int current_port = 0;
1657
1658                         /*
1659                                 Out-of-game menu loop.
1660
1661                                 Loop quits when menu returns proper parameters.
1662                         */
1663                         while (kill == false) {
1664                                 // If skip_main_menu, only go through here once
1665                                 if (skip_main_menu && !first_loop) {
1666                                         kill = true;
1667                                         break;
1668                                 }
1669                                 first_loop = false;
1670
1671                                 // Cursor can be non-visible when coming from the game
1672                                 #ifndef ANDROID
1673                                 device->getCursorControl()->setVisible(true);
1674                                 #endif
1675                                 // Some stuff are left to scene manager when coming from the game
1676                                 // (map at least?)
1677                                 smgr->clear();
1678
1679                                 // Initialize menu data
1680                                 MainMenuData menudata;
1681                                 menudata.address = address;
1682                                 menudata.name = playername;
1683                                 menudata.port = itos(port);
1684                                 menudata.errormessage = wide_to_narrow(error_message);
1685                                 error_message = L"";
1686                                 if (cmd_args.exists("password"))
1687                                         menudata.password = cmd_args.get("password");
1688
1689                                 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
1690
1691                                 menudata.enable_public = g_settings->getBool("server_announce");
1692
1693                                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1694
1695                                 // If a world was commanded, append and select it
1696                                 if(commanded_world != "") {
1697                                         worldspec.gameid = getWorldGameId(commanded_world, true);
1698                                         worldspec.name = _("[--world parameter]");
1699                                         if(worldspec.gameid == "") {
1700                                                 worldspec.gameid = g_settings->get("default_game");
1701                                                 worldspec.name += " [new]";
1702                                         }
1703                                         worldspec.path = commanded_world;
1704                                 }
1705
1706                                 if (skip_main_menu == false) {
1707                                         video::IVideoDriver* driver = device->getVideoDriver();
1708
1709                                         infostream << "Waiting for other menus" << std::endl;
1710                                         while (device->run() && kill == false) {
1711                                                 if (noMenuActive())
1712                                                         break;
1713                                                 driver->beginScene(true, true,
1714                                                                 video::SColor(255, 128, 128, 128));
1715                                                 guienv->drawAll();
1716                                                 driver->endScene();
1717                                                 // On some computers framerate doesn't seem to be
1718                                                 // automatically limited
1719                                                 sleep_ms(25);
1720                                         }
1721                                         infostream << "Waited for other menus" << std::endl;
1722
1723                                         /* show main menu */
1724                                         GUIEngine mymenu(device, guiroot, &g_menumgr,smgr,&menudata,kill);
1725
1726                                         //once finished you'll never end up here
1727                                         smgr->clear();
1728                                 }
1729
1730                                 if (menudata.errormessage != "") {
1731                                         error_message = narrow_to_wide(menudata.errormessage);
1732                                         continue;
1733                                 }
1734
1735                                 //update worldspecs (necessary as new world may have been created)
1736                                 worldspecs = getAvailableWorlds();
1737
1738                                 if (menudata.name == "")
1739                                         menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
1740                                 else
1741                                         playername = menudata.name;
1742
1743                                 password = translatePassword(playername, narrow_to_wide(menudata.password));
1744                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1745
1746                                 address = menudata.address;
1747                                 int newport = stoi(menudata.port);
1748                                 if (newport != 0)
1749                                         port = newport;
1750
1751                                 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1752
1753                                 // Save settings
1754                                 g_settings->set("name", playername);
1755
1756                                 // Break out of menu-game loop to shut down cleanly
1757                                 if (device->run() == false || kill == true)
1758                                         break;
1759
1760                                 current_playername = playername;
1761                                 current_password = password;
1762                                 current_address = address;
1763                                 current_port = port;
1764
1765                                 // If using simple singleplayer mode, override
1766                                 if (simple_singleplayer_mode) {
1767                                         current_playername = "singleplayer";
1768                                         current_password = "";
1769                                         current_address = "";
1770                                         current_port = myrand_range(49152, 65535);
1771                                 } else if (address != "") {
1772                                         ServerListSpec server;
1773                                         server["name"] = menudata.servername;
1774                                         server["address"] = menudata.address;
1775                                         server["port"] = menudata.port;
1776                                         server["description"] = menudata.serverdescription;
1777                                         ServerList::insert(server);
1778                                 }
1779
1780                                 if ((!skip_main_menu) &&
1781                                                 (menudata.selected_world >= 0) &&
1782                                                 (menudata.selected_world < (int)worldspecs.size())) {
1783                                         g_settings->set("selected_world_path",
1784                                                         worldspecs[menudata.selected_world].path);
1785                                         worldspec = worldspecs[menudata.selected_world];
1786
1787                                 }
1788
1789                                 infostream <<"Selected world: " << worldspec.name
1790                                                         << " ["<<worldspec.path<<"]" <<std::endl;
1791
1792
1793                                 // If local game
1794                                 if (current_address == "") {
1795                                         if (worldspec.path == "") {
1796                                                 error_message = wgettext("No world selected and no address "
1797                                                                 "provided. Nothing to do.");
1798                                                 errorstream << wide_to_narrow(error_message) << std::endl;
1799                                                 continue;
1800                                         }
1801
1802                                         if (!fs::PathExists(worldspec.path)) {
1803                                                 error_message = wgettext("Provided world path doesn't exist: ")
1804                                                                 + narrow_to_wide(worldspec.path);
1805                                                 errorstream << wide_to_narrow(error_message) << std::endl;
1806                                                 continue;
1807                                         }
1808
1809                                         // Load gamespec for required game
1810                                         gamespec = findWorldSubgame(worldspec.path);
1811                                         if (!gamespec.isValid() && !commanded_gamespec.isValid()) {
1812                                                 error_message = wgettext("Could not find or load game \"")
1813                                                                 + narrow_to_wide(worldspec.gameid) + L"\"";
1814                                                 errorstream << wide_to_narrow(error_message) << std::endl;
1815                                                 continue;
1816                                         }
1817                                         if (commanded_gamespec.isValid() &&
1818                                                         commanded_gamespec.id != worldspec.gameid) {
1819                                                 errorstream<<"WARNING: Overriding gamespec from \""
1820                                                                 << worldspec.gameid << "\" to \""
1821                                                                 << commanded_gamespec.id << "\"" << std::endl;
1822                                                 gamespec = commanded_gamespec;
1823                                         }
1824
1825                                         if (!gamespec.isValid()) {
1826                                                 error_message = wgettext("Invalid gamespec.");
1827                                                 error_message += L" (world_gameid="
1828                                                                 + narrow_to_wide(worldspec.gameid) + L")";
1829                                                 errorstream << wide_to_narrow(error_message) << std::endl;
1830                                                 continue;
1831                                         }
1832                                 }
1833
1834                                 // Continue to game
1835                                 break;
1836                         }
1837
1838                         // Break out of menu-game loop to shut down cleanly
1839                         if (device->run() == false || kill == true) {
1840                                 if (g_settings_path != "") {
1841                                         g_settings->updateConfigFile(g_settings_path.c_str());
1842                                 }
1843                                 break;
1844                         }
1845
1846                         /*
1847                                 Run game
1848                         */
1849 #ifdef HAVE_TOUCHSCREENGUI
1850         receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
1851         g_touchscreengui = receiver->m_touchscreengui;
1852 #endif
1853                         the_game(
1854                                 kill,
1855                                 random_input,
1856                                 input,
1857                                 device,
1858                                 font,
1859                                 worldspec.path,
1860                                 current_playername,
1861                                 current_password,
1862                                 current_address,
1863                                 current_port,
1864                                 error_message,
1865                                 chat_backend,
1866                                 gamespec,
1867                                 simple_singleplayer_mode
1868                         );
1869                         smgr->clear();
1870 #ifdef HAVE_TOUCHSCREENGUI
1871         delete g_touchscreengui;
1872         g_touchscreengui = NULL;
1873         receiver->m_touchscreengui = NULL;
1874 #endif
1875
1876                 } //try
1877                 catch(con::PeerNotFoundException &e)
1878                 {
1879                         error_message = wgettext("Connection error (timed out?)");
1880                         errorstream << wide_to_narrow(error_message) << std::endl;
1881                 }
1882 #ifdef NDEBUG
1883                 catch(std::exception &e)
1884                 {
1885                         std::string narrow_message = "Some exception: \"";
1886                         narrow_message += e.what();
1887                         narrow_message += "\"";
1888                         errorstream << narrow_message << std::endl;
1889                         error_message = narrow_to_wide(narrow_message);
1890                 }
1891 #endif
1892
1893                 // If no main menu, show error and exit
1894                 if (skip_main_menu) {
1895                         if (error_message != L"") {
1896                                 verbosestream << "error_message = "
1897                                                 << wide_to_narrow(error_message) << std::endl;
1898                                 retval = 1;
1899                         }
1900                         break;
1901                 }
1902         } // Menu-game loop
1903
1904
1905         g_menuclouds->drop();
1906         g_menucloudsmgr->drop();
1907
1908         delete input;
1909
1910         /*
1911                 In the end, delete the Irrlicht device.
1912         */
1913         device->drop();
1914
1915 #if USE_FREETYPE
1916         if (use_freetype)
1917                 font->drop();
1918 #endif
1919         delete receiver;
1920 #endif // !SERVER
1921
1922         // Update configuration file
1923         if (g_settings_path != "")
1924                 g_settings->updateConfigFile(g_settings_path.c_str());
1925
1926         // Print modified quicktune values
1927         {
1928                 bool header_printed = false;
1929                 std::vector<std::string> names = getQuicktuneNames();
1930                 for(u32 i = 0; i < names.size(); i++) {
1931                         QuicktuneValue val = getQuicktuneValue(names[i]);
1932                         if (!val.modified)
1933                                 continue;
1934                         if (!header_printed) {
1935                                 dstream << "Modified quicktune values:" << std::endl;
1936                                 header_printed = true;
1937                         }
1938                         dstream<<names[i] << " = " << val.getString() << std::endl;
1939                 }
1940         }
1941
1942         // Stop httpfetch thread (if started)
1943         httpfetch_cleanup();
1944
1945         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1946
1947         debugstreams_deinit();
1948
1949
1950         return retval;
1951 }
1952
1953 //END