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