0c936ab63396936e7fc9531d8d630facb915ea98
[oweals/minetest.git] / src / main.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
51 #include "debug.h"
52 #include "test.h"
53 #include "server.h"
54 #include "constants.h"
55 #include "porting.h"
56 #include "gettime.h"
57 #include "guiMessageMenu.h"
58 #include "filesys.h"
59 #include "config.h"
60 #include "guiMainMenu.h"
61 #include "game.h"
62 #include "keycode.h"
63 #include "tile.h"
64 #include "chat.h"
65 #include "defaultsettings.h"
66 #include "gettext.h"
67 #include "settings.h"
68 #include "profiler.h"
69 #include "log.h"
70 #include "mods.h"
71
72 /*
73         Settings.
74         These are loaded from the config file.
75 */
76 Settings main_settings;
77 Settings *g_settings = &main_settings;
78
79 // Global profiler
80 Profiler main_profiler;
81 Profiler *g_profiler = &main_profiler;
82
83 /*
84         Debug streams
85 */
86
87 // Connection
88 std::ostream *dout_con_ptr = &dummyout;
89 std::ostream *derr_con_ptr = &verbosestream;
90 //std::ostream *dout_con_ptr = &infostream;
91 //std::ostream *derr_con_ptr = &errorstream;
92
93 // Server
94 std::ostream *dout_server_ptr = &infostream;
95 std::ostream *derr_server_ptr = &errorstream;
96
97 // Client
98 std::ostream *dout_client_ptr = &infostream;
99 std::ostream *derr_client_ptr = &errorstream;
100
101 #ifndef SERVER
102 /*
103         Random stuff
104 */
105
106 /* mainmenumanager.h */
107
108 gui::IGUIEnvironment* guienv = NULL;
109 gui::IGUIStaticText *guiroot = NULL;
110 MainMenuManager g_menumgr;
111
112 bool noMenuActive()
113 {
114         return (g_menumgr.menuCount() == 0);
115 }
116
117 // Passed to menus to allow disconnecting and exiting
118 MainGameCallback *g_gamecallback = NULL;
119 #endif
120
121 /*
122         gettime.h implementation
123 */
124
125 #ifdef SERVER
126
127 u32 getTimeMs()
128 {
129         /* Use imprecise system calls directly (from porting.h) */
130         return porting::getTimeMs();
131 }
132
133 #else
134
135 // A small helper class
136 class TimeGetter
137 {
138 public:
139         virtual u32 getTime() = 0;
140 };
141
142 // A precise irrlicht one
143 class IrrlichtTimeGetter: public TimeGetter
144 {
145 public:
146         IrrlichtTimeGetter(IrrlichtDevice *device):
147                 m_device(device)
148         {}
149         u32 getTime()
150         {
151                 if(m_device == NULL)
152                         return 0;
153                 return m_device->getTimer()->getRealTime();
154         }
155 private:
156         IrrlichtDevice *m_device;
157 };
158 // Not so precise one which works without irrlicht
159 class SimpleTimeGetter: public TimeGetter
160 {
161 public:
162         u32 getTime()
163         {
164                 return porting::getTimeMs();
165         }
166 };
167
168 // A pointer to a global instance of the time getter
169 // TODO: why?
170 TimeGetter *g_timegetter = NULL;
171
172 u32 getTimeMs()
173 {
174         if(g_timegetter == NULL)
175                 return 0;
176         return g_timegetter->getTime();
177 }
178
179 #endif
180
181 class StderrLogOutput: public ILogOutput
182 {
183 public:
184         /* line: Full line with timestamp, level and thread */
185         void printLog(const std::string &line)
186         {
187                 std::cerr<<line<<std::endl;
188         }
189 } main_stderr_log_out;
190
191 class DstreamNoStderrLogOutput: public ILogOutput
192 {
193 public:
194         /* line: Full line with timestamp, level and thread */
195         void printLog(const std::string &line)
196         {
197                 dstream_no_stderr<<line<<std::endl;
198         }
199 } main_dstream_no_stderr_log_out;
200
201 #ifndef SERVER
202
203 /*
204         Event handler for Irrlicht
205
206         NOTE: Everything possible should be moved out from here,
207               probably to InputHandler and the_game
208 */
209
210 class MyEventReceiver : public IEventReceiver
211 {
212 public:
213         // This is the one method that we have to implement
214         virtual bool OnEvent(const SEvent& event)
215         {
216                 /*
217                         React to nothing here if a menu is active
218                 */
219                 if(noMenuActive() == false)
220                 {
221                         return false;
222                 }
223
224                 // Remember whether each key is down or up
225                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
226                 {
227                         if(event.KeyInput.PressedDown) {
228                                 keyIsDown.set(event.KeyInput);
229                                 keyWasDown.set(event.KeyInput);
230                         } else {
231                                 keyIsDown.unset(event.KeyInput);
232                         }
233                 }
234
235                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
236                 {
237                         if(noMenuActive() == false)
238                         {
239                                 left_active = false;
240                                 middle_active = false;
241                                 right_active = false;
242                         }
243                         else
244                         {
245                                 left_active = event.MouseInput.isLeftPressed();
246                                 middle_active = event.MouseInput.isMiddlePressed();
247                                 right_active = event.MouseInput.isRightPressed();
248
249                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
250                                 {
251                                         leftclicked = true;
252                                 }
253                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
254                                 {
255                                         rightclicked = true;
256                                 }
257                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
258                                 {
259                                         leftreleased = true;
260                                 }
261                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
262                                 {
263                                         rightreleased = true;
264                                 }
265                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
266                                 {
267                                         mouse_wheel += event.MouseInput.Wheel;
268                                 }
269                         }
270                 }
271
272                 return false;
273         }
274
275         bool IsKeyDown(const KeyPress &keyCode) const
276         {
277                 return keyIsDown[keyCode];
278         }
279         
280         // Checks whether a key was down and resets the state
281         bool WasKeyDown(const KeyPress &keyCode)
282         {
283                 bool b = keyWasDown[keyCode];
284                 if (b)
285                         keyWasDown.unset(keyCode);
286                 return b;
287         }
288
289         s32 getMouseWheel()
290         {
291                 s32 a = mouse_wheel;
292                 mouse_wheel = 0;
293                 return a;
294         }
295
296         void clearInput()
297         {
298                 keyIsDown.clear();
299                 keyWasDown.clear();
300
301                 leftclicked = false;
302                 rightclicked = false;
303                 leftreleased = false;
304                 rightreleased = false;
305
306                 left_active = false;
307                 middle_active = false;
308                 right_active = false;
309
310                 mouse_wheel = 0;
311         }
312
313         MyEventReceiver()
314         {
315                 clearInput();
316         }
317
318         bool leftclicked;
319         bool rightclicked;
320         bool leftreleased;
321         bool rightreleased;
322
323         bool left_active;
324         bool middle_active;
325         bool right_active;
326
327         s32 mouse_wheel;
328
329 private:
330         IrrlichtDevice *m_device;
331         
332         // The current state of keys
333         KeyList keyIsDown;
334         // Whether a key has been pressed or not
335         KeyList keyWasDown;
336 };
337
338 /*
339         Separated input handler
340 */
341
342 class RealInputHandler : public InputHandler
343 {
344 public:
345         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
346                 m_device(device),
347                 m_receiver(receiver)
348         {
349         }
350         virtual bool isKeyDown(const KeyPress &keyCode)
351         {
352                 return m_receiver->IsKeyDown(keyCode);
353         }
354         virtual bool wasKeyDown(const KeyPress &keyCode)
355         {
356                 return m_receiver->WasKeyDown(keyCode);
357         }
358         virtual v2s32 getMousePos()
359         {
360                 return m_device->getCursorControl()->getPosition();
361         }
362         virtual void setMousePos(s32 x, s32 y)
363         {
364                 m_device->getCursorControl()->setPosition(x, y);
365         }
366
367         virtual bool getLeftState()
368         {
369                 return m_receiver->left_active;
370         }
371         virtual bool getRightState()
372         {
373                 return m_receiver->right_active;
374         }
375         
376         virtual bool getLeftClicked()
377         {
378                 return m_receiver->leftclicked;
379         }
380         virtual bool getRightClicked()
381         {
382                 return m_receiver->rightclicked;
383         }
384         virtual void resetLeftClicked()
385         {
386                 m_receiver->leftclicked = false;
387         }
388         virtual void resetRightClicked()
389         {
390                 m_receiver->rightclicked = false;
391         }
392
393         virtual bool getLeftReleased()
394         {
395                 return m_receiver->leftreleased;
396         }
397         virtual bool getRightReleased()
398         {
399                 return m_receiver->rightreleased;
400         }
401         virtual void resetLeftReleased()
402         {
403                 m_receiver->leftreleased = false;
404         }
405         virtual void resetRightReleased()
406         {
407                 m_receiver->rightreleased = false;
408         }
409
410         virtual s32 getMouseWheel()
411         {
412                 return m_receiver->getMouseWheel();
413         }
414
415         void clear()
416         {
417                 m_receiver->clearInput();
418         }
419 private:
420         IrrlichtDevice *m_device;
421         MyEventReceiver *m_receiver;
422 };
423
424 class RandomInputHandler : public InputHandler
425 {
426 public:
427         RandomInputHandler()
428         {
429                 leftdown = false;
430                 rightdown = false;
431                 leftclicked = false;
432                 rightclicked = false;
433                 leftreleased = false;
434                 rightreleased = false;
435                 keydown.clear();
436         }
437         virtual bool isKeyDown(const KeyPress &keyCode)
438         {
439                 return keydown[keyCode];
440         }
441         virtual bool wasKeyDown(const KeyPress &keyCode)
442         {
443                 return false;
444         }
445         virtual v2s32 getMousePos()
446         {
447                 return mousepos;
448         }
449         virtual void setMousePos(s32 x, s32 y)
450         {
451                 mousepos = v2s32(x,y);
452         }
453
454         virtual bool getLeftState()
455         {
456                 return leftdown;
457         }
458         virtual bool getRightState()
459         {
460                 return rightdown;
461         }
462
463         virtual bool getLeftClicked()
464         {
465                 return leftclicked;
466         }
467         virtual bool getRightClicked()
468         {
469                 return rightclicked;
470         }
471         virtual void resetLeftClicked()
472         {
473                 leftclicked = false;
474         }
475         virtual void resetRightClicked()
476         {
477                 rightclicked = false;
478         }
479
480         virtual bool getLeftReleased()
481         {
482                 return leftreleased;
483         }
484         virtual bool getRightReleased()
485         {
486                 return rightreleased;
487         }
488         virtual void resetLeftReleased()
489         {
490                 leftreleased = false;
491         }
492         virtual void resetRightReleased()
493         {
494                 rightreleased = false;
495         }
496
497         virtual s32 getMouseWheel()
498         {
499                 return 0;
500         }
501
502         virtual void step(float dtime)
503         {
504                 {
505                         static float counter1 = 0;
506                         counter1 -= dtime;
507                         if(counter1 < 0.0)
508                         {
509                                 counter1 = 0.1*Rand(1, 40);
510                                 keydown.toggle(getKeySetting("keymap_jump"));
511                         }
512                 }
513                 {
514                         static float counter1 = 0;
515                         counter1 -= dtime;
516                         if(counter1 < 0.0)
517                         {
518                                 counter1 = 0.1*Rand(1, 40);
519                                 keydown.toggle(getKeySetting("keymap_special1"));
520                         }
521                 }
522                 {
523                         static float counter1 = 0;
524                         counter1 -= dtime;
525                         if(counter1 < 0.0)
526                         {
527                                 counter1 = 0.1*Rand(1, 40);
528                                 keydown.toggle(getKeySetting("keymap_forward"));
529                         }
530                 }
531                 {
532                         static float counter1 = 0;
533                         counter1 -= dtime;
534                         if(counter1 < 0.0)
535                         {
536                                 counter1 = 0.1*Rand(1, 40);
537                                 keydown.toggle(getKeySetting("keymap_left"));
538                         }
539                 }
540                 {
541                         static float counter1 = 0;
542                         counter1 -= dtime;
543                         if(counter1 < 0.0)
544                         {
545                                 counter1 = 0.1*Rand(1, 20);
546                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
547                         }
548                 }
549                 {
550                         static float counter1 = 0;
551                         counter1 -= dtime;
552                         if(counter1 < 0.0)
553                         {
554                                 counter1 = 0.1*Rand(1, 30);
555                                 leftdown = !leftdown;
556                                 if(leftdown)
557                                         leftclicked = true;
558                                 if(!leftdown)
559                                         leftreleased = true;
560                         }
561                 }
562                 {
563                         static float counter1 = 0;
564                         counter1 -= dtime;
565                         if(counter1 < 0.0)
566                         {
567                                 counter1 = 0.1*Rand(1, 15);
568                                 rightdown = !rightdown;
569                                 if(rightdown)
570                                         rightclicked = true;
571                                 if(!rightdown)
572                                         rightreleased = true;
573                         }
574                 }
575                 mousepos += mousespeed;
576         }
577
578         s32 Rand(s32 min, s32 max)
579         {
580                 return (myrand()%(max-min+1))+min;
581         }
582 private:
583         KeyList keydown;
584         v2s32 mousepos;
585         v2s32 mousespeed;
586         bool leftdown;
587         bool rightdown;
588         bool leftclicked;
589         bool rightclicked;
590         bool leftreleased;
591         bool rightreleased;
592 };
593
594 void drawMenuBackground(video::IVideoDriver* driver)
595 {
596         core::dimension2d<u32> screensize = driver->getScreenSize();
597                 
598         video::ITexture *bgtexture =
599                         driver->getTexture(getTexturePath("menubg.png").c_str());
600         if(bgtexture)
601         {
602                 s32 scaledsize = 128;
603                 
604                 // The important difference between destsize and screensize is
605                 // that destsize is rounded to whole scaled pixels.
606                 // These formulas use component-wise multiplication and division of v2u32.
607                 v2u32 texturesize = bgtexture->getSize();
608                 v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
609                 v2u32 destsize = scaledsize * sourcesize / texturesize;
610                 
611                 // Default texture wrapping mode in Irrlicht is ETC_REPEAT.
612                 driver->draw2DImage(bgtexture,
613                         core::rect<s32>(0, 0, destsize.X, destsize.Y),
614                         core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
615                         NULL, NULL, true);
616         }
617         
618         video::ITexture *logotexture =
619                         driver->getTexture(getTexturePath("menulogo.png").c_str());
620         if(logotexture)
621         {
622                 v2s32 logosize(logotexture->getOriginalSize().Width,
623                                 logotexture->getOriginalSize().Height);
624                 logosize *= 4;
625
626                 video::SColor bgcolor(255,50,50,50);
627                 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
628                                 screensize.Width, screensize.Height);
629                 driver->draw2DRectangle(bgcolor, bgrect, NULL);
630
631                 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
632                 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
633                 rect -= v2s32(logosize.X/2, 0);
634                 driver->draw2DImage(logotexture, rect,
635                         core::rect<s32>(core::position2d<s32>(0,0),
636                         core::dimension2di(logotexture->getSize())),
637                         NULL, NULL, true);
638         }
639 }
640
641 #endif
642
643 // These are defined global so that they're not optimized too much.
644 // Can't change them to volatile.
645 s16 temp16;
646 f32 tempf;
647 v3f tempv3f1;
648 v3f tempv3f2;
649 std::string tempstring;
650 std::string tempstring2;
651
652 void SpeedTests()
653 {
654         {
655                 infostream<<"The following test should take around 20ms."<<std::endl;
656                 TimeTaker timer("Testing std::string speed");
657                 const u32 jj = 10000;
658                 for(u32 j=0; j<jj; j++)
659                 {
660                         tempstring = "";
661                         tempstring2 = "";
662                         const u32 ii = 10;
663                         for(u32 i=0; i<ii; i++){
664                                 tempstring2 += "asd";
665                         }
666                         for(u32 i=0; i<ii+1; i++){
667                                 tempstring += "asd";
668                                 if(tempstring == tempstring2)
669                                         break;
670                         }
671                 }
672         }
673         
674         infostream<<"All of the following tests should take around 100ms each."
675                         <<std::endl;
676
677         {
678                 TimeTaker timer("Testing floating-point conversion speed");
679                 tempf = 0.001;
680                 for(u32 i=0; i<4000000; i++){
681                         temp16 += tempf;
682                         tempf += 0.001;
683                 }
684         }
685         
686         {
687                 TimeTaker timer("Testing floating-point vector speed");
688
689                 tempv3f1 = v3f(1,2,3);
690                 tempv3f2 = v3f(4,5,6);
691                 for(u32 i=0; i<10000000; i++){
692                         tempf += tempv3f1.dotProduct(tempv3f2);
693                         tempv3f2 += v3f(7,8,9);
694                 }
695         }
696
697         {
698                 TimeTaker timer("Testing core::map speed");
699                 
700                 core::map<v2s16, f32> map1;
701                 tempf = -324;
702                 const s16 ii=300;
703                 for(s16 y=0; y<ii; y++){
704                         for(s16 x=0; x<ii; x++){
705                                 map1.insert(v2s16(x,y), tempf);
706                                 tempf += 1;
707                         }
708                 }
709                 for(s16 y=ii-1; y>=0; y--){
710                         for(s16 x=0; x<ii; x++){
711                                 tempf = map1[v2s16(x,y)];
712                         }
713                 }
714         }
715
716         {
717                 infostream<<"Around 5000/ms should do well here."<<std::endl;
718                 TimeTaker timer("Testing mutex speed");
719                 
720                 JMutex m;
721                 m.Init();
722                 u32 n = 0;
723                 u32 i = 0;
724                 do{
725                         n += 10000;
726                         for(; i<n; i++){
727                                 m.Lock();
728                                 m.Unlock();
729                         }
730                 }
731                 // Do at least 10ms
732                 while(timer.getTime() < 10);
733
734                 u32 dtime = timer.stop();
735                 u32 per_ms = n / dtime;
736                 infostream<<"Done. "<<dtime<<"ms, "
737                                 <<per_ms<<"/ms"<<std::endl;
738         }
739 }
740
741 int main(int argc, char *argv[])
742 {
743         /*
744                 Initialization
745         */
746
747         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
748         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
749
750         log_register_thread("main");
751
752         // Set locale. This is for forcing '.' as the decimal point.
753         std::locale::global(std::locale("C"));
754         // This enables printing all characters in bitmap font
755         setlocale(LC_CTYPE, "en_US");
756
757         /*
758                 Parse command line
759         */
760         
761         // List all allowed options
762         core::map<std::string, ValueSpec> allowed_options;
763         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG,
764                         "Show allowed options"));
765         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
766                         "Load configuration from specified file"));
767         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING,
768                         "Set network port (UDP) to use"));
769         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG,
770                         "Disable unit tests"));
771         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG,
772                         "Enable unit tests"));
773         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING,
774                         "Map directory (where everything in the world is stored)"));
775         allowed_options.insert("info-on-stderr", ValueSpec(VALUETYPE_FLAG,
776                         "Print more information to console (deprecated; use --verbose)"));
777         allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG,
778                         "Print more information to console"));
779 #ifndef SERVER
780         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG,
781                         "Run speed tests"));
782         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING,
783                         "Address to connect to"));
784         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG,
785                         "Enable random user input, for testing"));
786         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
787                         "Run server directly"));
788 #endif
789
790         Settings cmd_args;
791         
792         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
793
794         if(ret == false || cmd_args.getFlag("help"))
795         {
796                 dstream<<"Allowed options:"<<std::endl;
797                 for(core::map<std::string, ValueSpec>::Iterator
798                                 i = allowed_options.getIterator();
799                                 i.atEnd() == false; i++)
800                 {
801                         dstream<<"  --"<<i.getNode()->getKey();
802                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)
803                         {
804                         }
805                         else
806                         {
807                                 dstream<<" <value>";
808                         }
809                         dstream<<std::endl;
810
811                         if(i.getNode()->getValue().help != NULL)
812                         {
813                                 dstream<<"      "<<i.getNode()->getValue().help
814                                                 <<std::endl;
815                         }
816                 }
817
818                 return cmd_args.getFlag("help") ? 0 : 1;
819         }
820         
821         /*
822                 Low-level initialization
823         */
824
825         if(cmd_args.getFlag("verbose") ||
826                         cmd_args.getFlag("info-on-stderr") ||
827                         cmd_args.getFlag("speedtests"))
828                 log_add_output(&main_stderr_log_out, LMT_INFO);
829
830         porting::signal_handler_init();
831         bool &kill = *porting::signal_handler_killstatus();
832         
833         porting::initializePaths();
834
835         // Create user data directory
836         fs::CreateDir(porting::path_user);
837
838         init_gettext((porting::path_share+DIR_DELIM+".."+DIR_DELIM+"locale").c_str());
839         
840         // Initialize debug streams
841 #ifdef RUN_IN_PLACE
842         std::string debugfile = DEBUGFILE;
843 #else
844         std::string debugfile = porting::path_user+DIR_DELIM+DEBUGFILE;
845 #endif
846         bool disable_stderr = false;
847         debugstreams_init(disable_stderr, debugfile.c_str());
848         // Initialize debug stacks
849         debug_stacks_init();
850
851         DSTACK(__FUNCTION_NAME);
852
853         dstream<<"path_share = "<<porting::path_share<<std::endl;
854         dstream<<"path_user  = "<<porting::path_user<<std::endl;
855
856         // Debug handler
857         BEGIN_DEBUG_EXCEPTION_HANDLER
858
859         // Print startup message
860         actionstream<<PROJECT_NAME<<
861                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
862                         <<", "<<BUILD_INFO
863                         <<std::endl;
864         
865         /*
866                 Basic initialization
867         */
868
869         // Initialize default settings
870         set_default_settings(g_settings);
871         
872         // Initialize sockets
873         sockets_init();
874         atexit(sockets_cleanup);
875         
876         /*
877                 Read config file
878         */
879         
880         // Path of configuration file in use
881         std::string configpath = "";
882         
883         if(cmd_args.exists("config"))
884         {
885                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
886                 if(r == false)
887                 {
888                         errorstream<<"Could not read configuration from \""
889                                         <<cmd_args.get("config")<<"\""<<std::endl;
890                         return 1;
891                 }
892                 configpath = cmd_args.get("config");
893         }
894         else
895         {
896                 core::array<std::string> filenames;
897                 filenames.push_back(porting::path_user +
898                                 DIR_DELIM + "minetest.conf");
899                 // Legacy configuration file location
900                 filenames.push_back(porting::path_user +
901                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
902 #ifdef RUN_IN_PLACE
903                 // Try also from a lower level (to aid having the same configuration
904                 // for many RUN_IN_PLACE installs)
905                 filenames.push_back(porting::path_user +
906                                 DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
907 #endif
908
909                 for(u32 i=0; i<filenames.size(); i++)
910                 {
911                         bool r = g_settings->readConfigFile(filenames[i].c_str());
912                         if(r)
913                         {
914                                 configpath = filenames[i];
915                                 break;
916                         }
917                 }
918                 
919                 // If no path found, use the first one (menu creates the file)
920                 if(configpath == "")
921                         configpath = filenames[0];
922         }
923
924         // Initialize random seed
925         srand(time(0));
926         mysrand(time(0));
927
928         /*
929                 Run unit tests
930         */
931
932         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
933                         || cmd_args.getFlag("enable-unittests") == true)
934         {
935                 run_tests();
936         }
937         
938         /*
939                 Game parameters
940         */
941
942         // Port
943         u16 port = 30000;
944         if(cmd_args.exists("port"))
945                 port = cmd_args.getU16("port");
946         else if(g_settings->exists("port"))
947                 port = g_settings->getU16("port");
948         if(port == 0)
949                 port = 30000;
950         
951         // Map directory
952         std::string map_dir = porting::path_user + DIR_DELIM + "server" + DIR_DELIM + "worlds" + DIR_DELIM + "world";
953         if(cmd_args.exists("map-dir"))
954                 map_dir = cmd_args.get("map-dir");
955         else if(g_settings->exists("map-dir"))
956                 map_dir = g_settings->get("map-dir");
957         else{
958                 // No map-dir option was specified.
959                 // Check if the world is found from the default directory, and if
960                 // not, see if the legacy world directory exists.
961                 std::string legacy_map_dir = porting::path_user+DIR_DELIM+".."+DIR_DELIM+"world";
962                 if(!fs::PathExists(map_dir) && fs::PathExists(legacy_map_dir)){
963                         errorstream<<"Warning: Using legacy world directory \""
964                                         <<legacy_map_dir<<"\""<<std::endl;
965                         map_dir = legacy_map_dir;
966                 }
967         }
968
969         // Run dedicated server if asked to or no other option
970 #ifdef SERVER
971         bool run_dedicated_server = true;
972 #else
973         bool run_dedicated_server = cmd_args.getFlag("server");
974 #endif
975         if(run_dedicated_server)
976         {
977                 DSTACK("Dedicated server branch");
978
979                 // Create time getter if built with Irrlicht
980 #ifndef SERVER
981                 g_timegetter = new SimpleTimeGetter();
982 #endif
983                 
984                 // Create server
985                 Server server(map_dir, configpath, "mesetint");
986                 server.start(port);
987                 
988                 // Run server
989                 dedicated_server_loop(server, kill);
990
991                 return 0;
992         }
993
994 #ifndef SERVER // Exclude from dedicated server build
995
996         /*
997                 More parameters
998         */
999         
1000         // Address to connect to
1001         std::string address = "";
1002         
1003         if(cmd_args.exists("address"))
1004         {
1005                 address = cmd_args.get("address");
1006         }
1007         else
1008         {
1009                 address = g_settings->get("address");
1010         }
1011         
1012         std::string playername = g_settings->get("name");
1013
1014         /*
1015                 Device initialization
1016         */
1017
1018         // Resolution selection
1019         
1020         bool fullscreen = false;
1021         u16 screenW = g_settings->getU16("screenW");
1022         u16 screenH = g_settings->getU16("screenH");
1023
1024         // Determine driver
1025
1026         video::E_DRIVER_TYPE driverType;
1027         
1028         std::string driverstring = g_settings->get("video_driver");
1029
1030         if(driverstring == "null")
1031                 driverType = video::EDT_NULL;
1032         else if(driverstring == "software")
1033                 driverType = video::EDT_SOFTWARE;
1034         else if(driverstring == "burningsvideo")
1035                 driverType = video::EDT_BURNINGSVIDEO;
1036         else if(driverstring == "direct3d8")
1037                 driverType = video::EDT_DIRECT3D8;
1038         else if(driverstring == "direct3d9")
1039                 driverType = video::EDT_DIRECT3D9;
1040         else if(driverstring == "opengl")
1041                 driverType = video::EDT_OPENGL;
1042         else
1043         {
1044                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1045                                 "to opengl"<<std::endl;
1046                 driverType = video::EDT_OPENGL;
1047         }
1048
1049         /*
1050                 Create device and exit if creation failed
1051         */
1052
1053         MyEventReceiver receiver;
1054
1055         IrrlichtDevice *device;
1056         device = createDevice(driverType,
1057                         core::dimension2d<u32>(screenW, screenH),
1058                         16, fullscreen, false, false, &receiver);
1059
1060         if (device == 0)
1061                 return 1; // could not create selected driver.
1062         
1063         /*
1064                 Continue initialization
1065         */
1066
1067         video::IVideoDriver* driver = device->getVideoDriver();
1068
1069         // Disable mipmaps (because some of them look ugly)
1070         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1071
1072         /*
1073                 This changes the minimum allowed number of vertices in a VBO.
1074                 Default is 500.
1075         */
1076         //driver->setMinHardwareBufferVertexCount(50);
1077
1078         // Set the window caption
1079         device->setWindowCaption(L"Minetest [Main Menu]");
1080         
1081         // Create time getter
1082         g_timegetter = new IrrlichtTimeGetter(device);
1083         
1084         // Create game callback for menus
1085         g_gamecallback = new MainGameCallback(device);
1086         
1087         /*
1088                 Speed tests (done after irrlicht is loaded to get timer)
1089         */
1090         if(cmd_args.getFlag("speedtests"))
1091         {
1092                 dstream<<"Running speed tests"<<std::endl;
1093                 SpeedTests();
1094                 return 0;
1095         }
1096         
1097         device->setResizable(true);
1098
1099         bool random_input = g_settings->getBool("random_input")
1100                         || cmd_args.getFlag("random-input");
1101         InputHandler *input = NULL;
1102         if(random_input)
1103                 input = new RandomInputHandler();
1104         else
1105                 input = new RealInputHandler(device, &receiver);
1106         
1107         scene::ISceneManager* smgr = device->getSceneManager();
1108
1109         guienv = device->getGUIEnvironment();
1110         gui::IGUISkin* skin = guienv->getSkin();
1111         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1112         if(font)
1113                 skin->setFont(font);
1114         else
1115                 errorstream<<"WARNING: Font file was not found."
1116                                 " Using default font."<<std::endl;
1117         // If font was not found, this will get us one
1118         font = skin->getFont();
1119         assert(font);
1120         
1121         u32 text_height = font->getDimension(L"Hello, world!").Height;
1122         infostream<<"text_height="<<text_height<<std::endl;
1123
1124         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1125         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1126         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1127         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1128         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1129         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1130         
1131         /*
1132                 GUI stuff
1133         */
1134
1135         ChatBackend chat_backend;
1136
1137         /*
1138                 If an error occurs, this is set to something and the
1139                 menu-game loop is restarted. It is then displayed before
1140                 the menu.
1141         */
1142         std::wstring error_message = L"";
1143
1144         // The password entered during the menu screen,
1145         std::string password;
1146
1147         /*
1148                 Menu-game loop
1149         */
1150         while(device->run() && kill == false)
1151         {
1152
1153                 // This is used for catching disconnects
1154                 try
1155                 {
1156
1157                         /*
1158                                 Clear everything from the GUIEnvironment
1159                         */
1160                         guienv->clear();
1161                         
1162                         /*
1163                                 We need some kind of a root node to be able to add
1164                                 custom gui elements directly on the screen.
1165                                 Otherwise they won't be automatically drawn.
1166                         */
1167                         guiroot = guienv->addStaticText(L"",
1168                                         core::rect<s32>(0, 0, 10000, 10000));
1169                         
1170                         /*
1171                                 Out-of-game menu loop.
1172
1173                                 Loop quits when menu returns proper parameters.
1174                         */
1175                         while(kill == false)
1176                         {
1177                                 // Cursor can be non-visible when coming from the game
1178                                 device->getCursorControl()->setVisible(true);
1179                                 // Some stuff are left to scene manager when coming from the game
1180                                 // (map at least?)
1181                                 smgr->clear();
1182                                 // Reset or hide the debug gui texts
1183                                 /*guitext->setText(L"Minetest-c55");
1184                                 guitext2->setVisible(false);
1185                                 guitext_info->setVisible(false);
1186                                 guitext_chat->setVisible(false);*/
1187                                 
1188                                 // Initialize menu data
1189                                 MainMenuData menudata;
1190                                 menudata.address = narrow_to_wide(address);
1191                                 menudata.name = narrow_to_wide(playername);
1192                                 menudata.port = narrow_to_wide(itos(port));
1193                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1194                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1195                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1196                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1197                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1198                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1199
1200                                 GUIMainMenu *menu =
1201                                                 new GUIMainMenu(guienv, guiroot, -1, 
1202                                                         &g_menumgr, &menudata, g_gamecallback);
1203                                 menu->allowFocusRemoval(true);
1204
1205                                 if(error_message != L"")
1206                                 {
1207                                         errorstream<<"error_message = "
1208                                                         <<wide_to_narrow(error_message)<<std::endl;
1209
1210                                         GUIMessageMenu *menu2 =
1211                                                         new GUIMessageMenu(guienv, guiroot, -1, 
1212                                                                 &g_menumgr, error_message.c_str());
1213                                         menu2->drop();
1214                                         error_message = L"";
1215                                 }
1216
1217                                 video::IVideoDriver* driver = device->getVideoDriver();
1218                                 
1219                                 infostream<<"Created main menu"<<std::endl;
1220
1221                                 while(device->run() && kill == false)
1222                                 {
1223                                         if(menu->getStatus() == true)
1224                                                 break;
1225
1226                                         //driver->beginScene(true, true, video::SColor(255,0,0,0));
1227                                         driver->beginScene(true, true, video::SColor(255,128,128,128));
1228
1229                                         drawMenuBackground(driver);
1230
1231                                         guienv->drawAll();
1232                                         
1233                                         driver->endScene();
1234                                         
1235                                         // On some computers framerate doesn't seem to be
1236                                         // automatically limited
1237                                         sleep_ms(25);
1238                                 }
1239                                 
1240                                 // Break out of menu-game loop to shut down cleanly
1241                                 if(device->run() == false || kill == true)
1242                                         break;
1243                                 
1244                                 infostream<<"Dropping main menu"<<std::endl;
1245
1246                                 menu->drop();
1247                                 
1248                                 // Delete map if requested
1249                                 if(menudata.delete_map)
1250                                 {
1251                                         bool r = fs::RecursiveDeleteContent(map_dir);
1252                                         if(r == false)
1253                                                 error_message = L"Delete failed";
1254                                         continue;
1255                                 }
1256
1257                                 playername = wide_to_narrow(menudata.name);
1258
1259                                 password = translatePassword(playername, menudata.password);
1260
1261                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1262
1263                                 address = wide_to_narrow(menudata.address);
1264                                 int newport = stoi(wide_to_narrow(menudata.port));
1265                                 if(newport != 0)
1266                                         port = newport;
1267                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1268                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1269                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1270                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1271                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1272                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1273                                 
1274                                 // NOTE: These are now checked server side; no need to do it
1275                                 //       here, so let's not do it here.
1276                                 /*// Check for valid parameters, restart menu if invalid.
1277                                 if(playername == "")
1278                                 {
1279                                         error_message = L"Name required.";
1280                                         continue;
1281                                 }
1282                                 // Check that name has only valid chars
1283                                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1284                                 {
1285                                         error_message = L"Characters allowed: "
1286                                                         +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
1287                                         continue;
1288                                 }*/
1289
1290                                 // Save settings
1291                                 g_settings->set("name", playername);
1292                                 g_settings->set("address", address);
1293                                 g_settings->set("port", itos(port));
1294                                 // Update configuration file
1295                                 if(configpath != "")
1296                                         g_settings->updateConfigFile(configpath.c_str());
1297                         
1298                                 // Continue to game
1299                                 break;
1300                         }
1301                         
1302                         // Break out of menu-game loop to shut down cleanly
1303                         if(device->run() == false || kill == true)
1304                                 break;
1305                         
1306                         /*
1307                                 Run game
1308                         */
1309                         the_game(
1310                                 kill,
1311                                 random_input,
1312                                 input,
1313                                 device,
1314                                 font,
1315                                 map_dir,
1316                                 playername,
1317                                 password,
1318                                 address,
1319                                 port,
1320                                 error_message,
1321                                 configpath,
1322                                 chat_backend
1323                         );
1324
1325                 } //try
1326                 catch(con::PeerNotFoundException &e)
1327                 {
1328                         errorstream<<"Connection error (timed out?)"<<std::endl;
1329                         error_message = L"Connection error (timed out?)";
1330                 }
1331                 catch(SocketException &e)
1332                 {
1333                         errorstream<<"Socket error (port already in use?)"<<std::endl;
1334                         error_message = L"Socket error (port already in use?)";
1335                 }
1336                 catch(ModError &e)
1337                 {
1338                         errorstream<<e.what()<<std::endl;
1339                         error_message = narrow_to_wide(e.what()) + L"\nCheck debug.txt for details.";
1340                 }
1341 #ifdef NDEBUG
1342                 catch(std::exception &e)
1343                 {
1344                         std::string narrow_message = "Some exception, what()=\"";
1345                         narrow_message += e.what();
1346                         narrow_message += "\"";
1347                         errorstream<<narrow_message<<std::endl;
1348                         error_message = narrow_to_wide(narrow_message);
1349                 }
1350 #endif
1351
1352         } // Menu-game loop
1353         
1354         delete input;
1355
1356         /*
1357                 In the end, delete the Irrlicht device.
1358         */
1359         device->drop();
1360
1361 #endif // !SERVER
1362         
1363         // Update configuration file
1364         if(configpath != "")
1365                 g_settings->updateConfigFile(configpath.c_str());
1366
1367         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1368         
1369         debugstreams_deinit();
1370         
1371         return 0;
1372 }
1373
1374 //END
1375