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