]> git.lizzy.rs Git - dragonfireclient.git/blob - src/main.cpp
Add redis database backend
[dragonfireclient.git] / src / main.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef NDEBUG
21         /*#ifdef _WIN32
22                 #pragma message ("Disabling unit tests")
23         #else
24                 #warning "Disabling unit tests"
25         #endif*/
26         // Disable unit tests
27         #define ENABLE_TESTS 0
28 #else
29         // Enable unit tests
30         #define ENABLE_TESTS 1
31 #endif
32
33 #ifdef _MSC_VER
34 #ifndef SERVER // Dedicated server isn't linked with Irrlicht
35         #pragma comment(lib, "Irrlicht.lib")
36         // This would get rid of the console window
37         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
38 #endif
39         #pragma comment(lib, "zlibwapi.lib")
40         #pragma comment(lib, "Shell32.lib")
41 #endif
42
43 #include "irrlicht.h" // createDevice
44
45 #include "main.h"
46 #include "mainmenumanager.h"
47 #include <iostream>
48 #include <fstream>
49 #include <locale.h>
50 #include "irrlichttypes_extrabloated.h"
51 #include "debug.h"
52 #include "test.h"
53 #include "clouds.h"
54 #include "server.h"
55 #include "constants.h"
56 #include "porting.h"
57 #include "gettime.h"
58 #include "filesys.h"
59 #include "config.h"
60 #include "version.h"
61 #include "guiMainMenu.h"
62 #include "game.h"
63 #include "keycode.h"
64 #include "tile.h"
65 #include "chat.h"
66 #include "defaultsettings.h"
67 #include "gettext.h"
68 #include "settings.h"
69 #include "profiler.h"
70 #include "log.h"
71 #include "mods.h"
72 #if USE_FREETYPE
73 #include "xCGUITTFont.h"
74 #endif
75 #include "util/string.h"
76 #include "subgame.h"
77 #include "quicktune.h"
78 #include "serverlist.h"
79 #include "httpfetch.h"
80 #include "guiEngine.h"
81 #include "mapsector.h"
82
83 #include "database-sqlite3.h"
84 #ifdef USE_LEVELDB
85 #include "database-leveldb.h"
86 #endif
87 #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
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         /*
1454                 Continue initialization
1455         */
1456
1457         video::IVideoDriver* driver = device->getVideoDriver();
1458
1459         /*
1460                 This changes the minimum allowed number of vertices in a VBO.
1461                 Default is 500.
1462         */
1463         //driver->setMinHardwareBufferVertexCount(50);
1464
1465         // Create time getter
1466         g_timegetter = new IrrlichtTimeGetter(device);
1467
1468         // Create game callback for menus
1469         g_gamecallback = new MainGameCallback(device);
1470
1471         /*
1472                 Speed tests (done after irrlicht is loaded to get timer)
1473         */
1474         if(cmd_args.getFlag("speedtests"))
1475         {
1476                 dstream<<"Running speed tests"<<std::endl;
1477                 SpeedTests();
1478                 device->drop();
1479                 return 0;
1480         }
1481
1482         device->setResizable(true);
1483
1484         bool random_input = g_settings->getBool("random_input")
1485                         || cmd_args.getFlag("random-input");
1486         InputHandler *input = NULL;
1487         if(random_input)
1488                 input = new RandomInputHandler();
1489         else
1490                 input = new RealInputHandler(device, &receiver);
1491
1492         scene::ISceneManager* smgr = device->getSceneManager();
1493
1494         guienv = device->getGUIEnvironment();
1495         gui::IGUISkin* skin = guienv->getSkin();
1496         std::string font_path = g_settings->get("font_path");
1497         gui::IGUIFont *font;
1498         #if USE_FREETYPE
1499         bool use_freetype = g_settings->getBool("freetype");
1500         if (use_freetype) {
1501                 std::string fallback;
1502                 if (is_yes(gettext("needs_fallback_font")))
1503                         fallback = "fallback_";
1504                 u16 font_size = g_settings->getU16(fallback + "font_size");
1505                 font_path = g_settings->get(fallback + "font_path");
1506                 u32 font_shadow = g_settings->getU16(fallback + "font_shadow");
1507                 u32 font_shadow_alpha = g_settings->getU16(fallback + "font_shadow_alpha");
1508                 font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size, true, true, font_shadow, font_shadow_alpha);
1509         } else {
1510                 font = guienv->getFont(font_path.c_str());
1511         }
1512         #else
1513         font = guienv->getFont(font_path.c_str());
1514         #endif
1515         if(font)
1516                 skin->setFont(font);
1517         else
1518                 errorstream<<"WARNING: Font file was not found."
1519                                 " Using default font."<<std::endl;
1520         // If font was not found, this will get us one
1521         font = skin->getFont();
1522         assert(font);
1523
1524         u32 text_height = font->getDimension(L"Hello, world!").Height;
1525         infostream<<"text_height="<<text_height<<std::endl;
1526
1527         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1528         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1529         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1530         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1531         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1532         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1533         skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50));
1534         skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255));
1535
1536 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
1537         // Irrlicht 1.8 input colours
1538         skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128));
1539         skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
1540 #endif
1541
1542
1543         // Create the menu clouds
1544         if (!g_menucloudsmgr)
1545                 g_menucloudsmgr = smgr->createNewSceneManager();
1546         if (!g_menuclouds)
1547                 g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
1548                         g_menucloudsmgr, -1, rand(), 100);
1549         g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255));
1550         scene::ICameraSceneNode* camera;
1551         camera = g_menucloudsmgr->addCameraSceneNode(0,
1552                                 v3f(0,0,0), v3f(0, 60, 100));
1553         camera->setFarValue(10000);
1554
1555         /*
1556                 GUI stuff
1557         */
1558
1559         ChatBackend chat_backend;
1560
1561         /*
1562                 If an error occurs, this is set to something and the
1563                 menu-game loop is restarted. It is then displayed before
1564                 the menu.
1565         */
1566         std::wstring error_message = L"";
1567
1568         // The password entered during the menu screen,
1569         std::string password;
1570
1571         bool first_loop = true;
1572
1573         /*
1574                 Menu-game loop
1575         */
1576         while(device->run() && kill == false)
1577         {
1578                 // Set the window caption
1579                 wchar_t* text = wgettext("Main Menu");
1580                 device->setWindowCaption((std::wstring(L"Minetest [")+text+L"]").c_str());
1581                 delete[] text;
1582
1583                 // This is used for catching disconnects
1584                 try
1585                 {
1586
1587                         /*
1588                                 Clear everything from the GUIEnvironment
1589                         */
1590                         guienv->clear();
1591
1592                         /*
1593                                 We need some kind of a root node to be able to add
1594                                 custom gui elements directly on the screen.
1595                                 Otherwise they won't be automatically drawn.
1596                         */
1597                         guiroot = guienv->addStaticText(L"",
1598                                         core::rect<s32>(0, 0, 10000, 10000));
1599
1600                         SubgameSpec gamespec;
1601                         WorldSpec worldspec;
1602                         bool simple_singleplayer_mode = false;
1603
1604                         // These are set up based on the menu and other things
1605                         std::string current_playername = "inv£lid";
1606                         std::string current_password = "";
1607                         std::string current_address = "does-not-exist";
1608                         int current_port = 0;
1609
1610                         /*
1611                                 Out-of-game menu loop.
1612
1613                                 Loop quits when menu returns proper parameters.
1614                         */
1615                         while(kill == false)
1616                         {
1617                                 // If skip_main_menu, only go through here once
1618                                 if(skip_main_menu && !first_loop){
1619                                         kill = true;
1620                                         break;
1621                                 }
1622                                 first_loop = false;
1623
1624                                 // Cursor can be non-visible when coming from the game
1625                                 device->getCursorControl()->setVisible(true);
1626                                 // Some stuff are left to scene manager when coming from the game
1627                                 // (map at least?)
1628                                 smgr->clear();
1629
1630                                 // Initialize menu data
1631                                 MainMenuData menudata;
1632                                 menudata.address = address;
1633                                 menudata.name = playername;
1634                                 menudata.port = itos(port);
1635                                 menudata.errormessage = wide_to_narrow(error_message);
1636                                 error_message = L"";
1637                                 if(cmd_args.exists("password"))
1638                                         menudata.password = cmd_args.get("password");
1639
1640                                 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
1641
1642                                 menudata.enable_public = g_settings->getBool("server_announce");
1643
1644                                 std::vector<WorldSpec> worldspecs = getAvailableWorlds();
1645
1646                                 // If a world was commanded, append and select it
1647                                 if(commanded_world != ""){
1648
1649                                         std::string gameid = getWorldGameId(commanded_world, true);
1650                                         std::string name = _("[--world parameter]");
1651                                         if(gameid == ""){
1652                                                 gameid = g_settings->get("default_game");
1653                                                 name += " [new]";
1654                                         }
1655                                         //TODO find within worldspecs and set config
1656                                 }
1657
1658                                 if(skip_main_menu == false)
1659                                 {
1660                                         video::IVideoDriver* driver = device->getVideoDriver();
1661
1662                                         infostream<<"Waiting for other menus"<<std::endl;
1663                                         while(device->run() && kill == false)
1664                                         {
1665                                                 if(noMenuActive())
1666                                                         break;
1667                                                 driver->beginScene(true, true,
1668                                                                 video::SColor(255,128,128,128));
1669                                                 guienv->drawAll();
1670                                                 driver->endScene();
1671                                                 // On some computers framerate doesn't seem to be
1672                                                 // automatically limited
1673                                                 sleep_ms(25);
1674                                         }
1675                                         infostream<<"Waited for other menus"<<std::endl;
1676
1677                                         GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata,kill);
1678
1679                                         delete temp;
1680                                         //once finished you'll never end up here
1681                                         smgr->clear();
1682                                 }
1683
1684                                 if(menudata.errormessage != ""){
1685                                         error_message = narrow_to_wide(menudata.errormessage);
1686                                         continue;
1687                                 }
1688
1689                                 //update worldspecs (necessary as new world may have been created)
1690                                 worldspecs = getAvailableWorlds();
1691
1692                                 if (menudata.name == "")
1693                                         menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
1694                                 else
1695                                         playername = menudata.name;
1696
1697                                 password = translatePassword(playername, narrow_to_wide(menudata.password));
1698                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1699
1700                                 address = menudata.address;
1701                                 int newport = stoi(menudata.port);
1702                                 if(newport != 0)
1703                                         port = newport;
1704
1705                                 simple_singleplayer_mode = menudata.simple_singleplayer_mode;
1706
1707                                 // Save settings
1708                                 g_settings->set("name", playername);
1709
1710                                 if((menudata.selected_world >= 0) &&
1711                                                 (menudata.selected_world < (int)worldspecs.size()))
1712                                         g_settings->set("selected_world_path",
1713                                                         worldspecs[menudata.selected_world].path);
1714
1715                                 // Break out of menu-game loop to shut down cleanly
1716                                 if(device->run() == false || kill == true)
1717                                         break;
1718
1719                                 current_playername = playername;
1720                                 current_password = password;
1721                                 current_address = address;
1722                                 current_port = port;
1723
1724                                 // If using simple singleplayer mode, override
1725                                 if(simple_singleplayer_mode){
1726                                         current_playername = "singleplayer";
1727                                         current_password = "";
1728                                         current_address = "";
1729                                         current_port = myrand_range(49152, 65535);
1730                                 }
1731                                 else if (address != "")
1732                                 {
1733                                         ServerListSpec server;
1734                                         server["name"] = menudata.servername;
1735                                         server["address"] = menudata.address;
1736                                         server["port"] = menudata.port;
1737                                         server["description"] = menudata.serverdescription;
1738                                         ServerList::insert(server);
1739                                 }
1740
1741                                 // Set world path to selected one
1742                                 if ((menudata.selected_world >= 0) &&
1743                                         (menudata.selected_world < (int)worldspecs.size())) {
1744                                         worldspec = worldspecs[menudata.selected_world];
1745                                         infostream<<"Selected world: "<<worldspec.name
1746                                                         <<" ["<<worldspec.path<<"]"<<std::endl;
1747                                 }
1748
1749                                 // If local game
1750                                 if(current_address == "")
1751                                 {
1752                                         if(menudata.selected_world == -1){
1753                                                 error_message = wgettext("No world selected and no address "
1754                                                                 "provided. Nothing to do.");
1755                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1756                                                 continue;
1757                                         }
1758                                         // Load gamespec for required game
1759                                         gamespec = findWorldSubgame(worldspec.path);
1760                                         if(!gamespec.isValid() && !commanded_gamespec.isValid()){
1761                                                 error_message = wgettext("Could not find or load game \"")
1762                                                                 + narrow_to_wide(worldspec.gameid) + L"\"";
1763                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1764                                                 continue;
1765                                         }
1766                                         if(commanded_gamespec.isValid() &&
1767                                                         commanded_gamespec.id != worldspec.gameid){
1768                                                 errorstream<<"WARNING: Overriding gamespec from \""
1769                                                                 <<worldspec.gameid<<"\" to \""
1770                                                                 <<commanded_gamespec.id<<"\""<<std::endl;
1771                                                 gamespec = commanded_gamespec;
1772                                         }
1773
1774                                         if(!gamespec.isValid()){
1775                                                 error_message = wgettext("Invalid gamespec.");
1776                                                 error_message += L" (world_gameid="
1777                                                                 +narrow_to_wide(worldspec.gameid)+L")";
1778                                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1779                                                 continue;
1780                                         }
1781                                 }
1782
1783                                 // Continue to game
1784                                 break;
1785                         }
1786
1787                         // Break out of menu-game loop to shut down cleanly
1788                         if(device->run() == false || kill == true) {
1789                                 if(g_settings_path != "") {
1790                                         g_settings->updateConfigFile(
1791                                                 g_settings_path.c_str());
1792                                 }
1793                                 break;
1794                         }
1795
1796                         /*
1797                                 Run game
1798                         */
1799                         the_game(
1800                                 kill,
1801                                 random_input,
1802                                 input,
1803                                 device,
1804                                 font,
1805                                 worldspec.path,
1806                                 current_playername,
1807                                 current_password,
1808                                 current_address,
1809                                 current_port,
1810                                 error_message,
1811                                 chat_backend,
1812                                 gamespec,
1813                                 simple_singleplayer_mode
1814                         );
1815                         smgr->clear();
1816
1817                 } //try
1818                 catch(con::PeerNotFoundException &e)
1819                 {
1820                         error_message = wgettext("Connection error (timed out?)");
1821                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1822                 }
1823 #ifdef NDEBUG
1824                 catch(std::exception &e)
1825                 {
1826                         std::string narrow_message = "Some exception: \"";
1827                         narrow_message += e.what();
1828                         narrow_message += "\"";
1829                         errorstream<<narrow_message<<std::endl;
1830                         error_message = narrow_to_wide(narrow_message);
1831                 }
1832 #endif
1833
1834                 // If no main menu, show error and exit
1835                 if(skip_main_menu)
1836                 {
1837                         if(error_message != L""){
1838                                 verbosestream<<"error_message = "
1839                                                 <<wide_to_narrow(error_message)<<std::endl;
1840                                 retval = 1;
1841                         }
1842                         break;
1843                 }
1844         } // Menu-game loop
1845
1846
1847         g_menuclouds->drop();
1848         g_menucloudsmgr->drop();
1849
1850         delete input;
1851
1852         /*
1853                 In the end, delete the Irrlicht device.
1854         */
1855         device->drop();
1856
1857 #if USE_FREETYPE
1858         if (use_freetype)
1859                 font->drop();
1860 #endif
1861
1862 #endif // !SERVER
1863
1864         // Update configuration file
1865         if(g_settings_path != "")
1866                 g_settings->updateConfigFile(g_settings_path.c_str());
1867
1868         // Print modified quicktune values
1869         {
1870                 bool header_printed = false;
1871                 std::vector<std::string> names = getQuicktuneNames();
1872                 for(u32 i=0; i<names.size(); i++){
1873                         QuicktuneValue val = getQuicktuneValue(names[i]);
1874                         if(!val.modified)
1875                                 continue;
1876                         if(!header_printed){
1877                                 dstream<<"Modified quicktune values:"<<std::endl;
1878                                 header_printed = true;
1879                         }
1880                         dstream<<names[i]<<" = "<<val.getString()<<std::endl;
1881                 }
1882         }
1883
1884         // Stop httpfetch thread (if started)
1885         httpfetch_cleanup();
1886
1887         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1888
1889         debugstreams_deinit();
1890
1891         return retval;
1892 }
1893
1894 //END
1895