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