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