b7c3ceffe3dfdd57409ba5e4a28a2ce464e43872
[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 /*
21 =============================== NOTES ==============================
22 NOTE: Things starting with TODO are sometimes only suggestions.
23
24 NOTE: iostream.imbue(std::locale("C")) is very slow
25 NOTE: Global locale is now set at initialization
26
27 NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
28       hardware buffer (it is not freed automatically)
29
30 NOTE: A random to-do list saved here as documentation:
31 A list of "active blocks" in which stuff happens. (+=done)
32         + Add a never-resetted game timer to the server
33         + Add a timestamp value to blocks
34         + The simple rule: All blocks near some player are "active"
35         - Do stuff in real time in active blocks
36                 + Handle objects
37                 - Grow grass, delete leaves without a tree
38                 - Spawn some mobs based on some rules
39                 - Transform cobble to mossy cobble near water
40                 - Run a custom script
41                 - ...And all kinds of other dynamic stuff
42         + Keep track of when a block becomes active and becomes inactive
43         + When a block goes inactive:
44                 + Store objects statically to block
45                 + Store timer value as the timestamp
46         + When a block goes active:
47                 + Create active objects out of static objects
48                 - Simulate the results of what would have happened if it would have
49                   been active for all the time
50                         - Grow a lot of grass and so on
51         + Initially it is fine to send information about every active object
52           to every player. Eventually it should be modified to only send info
53           about the nearest ones.
54                 + This was left to be done by the old system and it sends only the
55                   nearest ones.
56
57 Vim conversion regexpes for moving to extended content type storage:
58 %s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g
59 %s/content_features(\([^.]*\)\.d)/content_features(\1)/g
60 %s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g
61 %s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g
62 %s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g
63 %s/\.d;/.getContent();/g
64 %s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g
65 Other things to note:
66 - node.d = node.param0 (only in raw serialization; use getContent() otherwise)
67 - node.param = node.param1
68 - node.dir = node.param2
69 - content_walkable(node.d) etc should be changed to
70   content_features(node).walkable etc
71 - Also check for lines that store the result of getContent to a 8-bit
72   variable and fix them (result of getContent() must be stored in
73   content_t, which is 16-bit)
74
75 NOTE: Seeds in 1260:6c77e7dbfd29:
76 5721858502589302589:
77         Spawns you on a small sand island with a surface dungeon
78 2983455799928051958:
79         Enormous jungle + a surface dungeon at ~(250,0,0)
80
81 Old, wild and random suggestions that probably won't be done:
82 -------------------------------------------------------------
83
84 SUGG: If player is on ground, mainly fetch ground-level blocks
85
86 SUGG: Expose Connection's seqnums and ACKs to server and client.
87       - This enables saving many packets and making a faster connection
88           - This also enables server to check if client has received the
89             most recent block sent, for example.
90 SUGG: Add a sane bandwidth throttling system to Connection
91
92 SUGG: More fine-grained control of client's dumping of blocks from
93       memory
94           - ...What does this mean in the first place?
95
96 SUGG: A map editing mode (similar to dedicated server mode)
97
98 SUGG: Transfer more blocks in a single packet
99 SUGG: A blockdata combiner class, to which blocks are added and at
100       destruction it sends all the stuff in as few packets as possible.
101 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
102       it by sending more stuff in a single packet.
103           - Add a packet queue to RemoteClient, from which packets will be
104             combined with object data packets
105                 - This is not exactly trivial: the object data packets are
106                   sometimes very big by themselves
107           - This might not give much network performance gain though.
108
109 SUGG: Precalculate lighting translation table at runtime (at startup)
110       - This is not doable because it is currently hand-made and not
111             based on some mathematical function.
112                 - Note: This has been changing lately
113
114 SUGG: A version number to blocks, which increments when the block is
115       modified (node add/remove, water update, lighting update)
116           - This can then be used to make sure the most recent version of
117             a block has been sent to client, for example
118
119 SUGG: Make the amount of blocks sending to client and the total
120           amount of blocks dynamically limited. Transferring blocks is the
121           main network eater of this system, so it is the one that has
122           to be throttled so that RTTs stay low.
123
124 SUGG: Meshes of blocks could be split into 6 meshes facing into
125       different directions and then only those drawn that need to be
126
127 SUGG: Background music based on cellular automata?
128       http://www.earslap.com/projectslab/otomata
129
130 SUGG: Simple light color information to air
131
132 SUGG: Server-side objects could be moved based on nodes to enable very
133       lightweight operation and simple AI
134         - Not practical; client would still need to show smooth movement.
135
136 SUGG: Make a system for pregenerating quick information for mapblocks, so
137           that the client can show them as cubes before they are actually sent
138           or even generated.
139
140 SUGG: Erosion simulation at map generation time
141     - This might be plausible if larger areas of map were pregenerated
142           without lighting (which is slow)
143         - Simulate water flows, which would carve out dirt fast and
144           then turn stone into gravel and sand and relocate it.
145         - How about relocating minerals, too? Coal and gold in
146           downstream sand and gravel would be kind of cool
147           - This would need a better way of handling minerals, mainly
148                 to have mineral content as a separate field. the first
149                 parameter field is free for this.
150         - Simulate rock falling from cliffs when water has removed
151           enough solid rock from the bottom
152
153 SUGG: For non-mapgen FarMesh: Add a per-sector database to store surface
154       stuff as simple flags/values
155       - Light?
156           - A building?
157           And at some point make the server send this data to the client too,
158           instead of referring to the noise functions
159           - Ground height
160           - Surface ground type
161           - Trees?
162
163 Gaming ideas:
164 -------------
165
166 - Aim for something like controlling a single dwarf in Dwarf Fortress
167 - The player could go faster by a crafting a boat, or riding an animal
168 - Random NPC traders. what else?
169
170 Game content:
171 -------------
172
173 - When furnace is destroyed, move items to player's inventory
174 - Add lots of stuff
175 - Glass blocks
176 - Growing grass, decaying leaves
177         - This can be done in the active blocks I guess.
178         - Lots of stuff can be done in the active blocks.
179         - Uh, is there an active block list somewhere? I think not. Add it.
180 - Breaking weak structures
181         - This can probably be accomplished in the same way as grass
182 - Player health points
183         - When player dies, throw items on map (needs better item-on-map
184           implementation)
185 - Cobble to get mossy if near water
186 - More slots in furnace source list, so that multiple ingredients
187   are possible.
188 - Keys to chests?
189
190 - The Treasure Guard; a big monster with a hammer
191         - The hammer does great damage, shakes the ground and removes a block
192         - You can drop on top of it, and have some time to attack there
193           before he shakes you off
194
195 - Maybe the difficulty could come from monsters getting tougher in
196   far-away places, and the player starting to need something from
197   there when time goes by.
198   - The player would have some of that stuff at the beginning, and
199     would need new supplies of it when it runs out
200
201 - A bomb
202 - A spread-items-on-map routine for the bomb, and for dying players
203
204 - Fighting:
205   - Proper sword swing simulation
206   - Player should get damage from colliding to a wall at high speed
207
208 Documentation:
209 --------------
210
211 Build system / running:
212 -----------------------
213
214 Networking and serialization:
215 -----------------------------
216
217 SUGG: Fix address to be ipv6 compatible
218
219 User Interface:
220 ---------------
221
222 Graphics:
223 ---------
224
225 SUGG: Combine MapBlock's face caches to so big pieces that VBO
226       can be used
227       - That is >500 vertices
228           - This is not easy; all the MapBlocks close to the player would
229             still need to be drawn separately and combining the blocks
230                 would have to happen in a background thread
231
232 SUGG: Make fetching sector's blocks more efficient when rendering
233       sectors that have very large amounts of blocks (on client)
234           - Is this necessary at all?
235
236 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
237       animating them is easier.
238
239 SUGG: Option for enabling proper alpha channel for textures
240
241 TODO: Flowing water animation
242
243 TODO: A setting for enabling bilinear filtering for textures
244
245 TODO: Better control of draw_control.wanted_max_blocks
246
247 TODO: Further investigate the use of GPU lighting in addition to the
248       current one
249
250 TODO: Artificial (night) light could be more yellow colored than sunlight.
251       - This is technically doable.
252           - Also the actual colors of the textures could be made less colorful
253             in the dark but it's a bit more difficult.
254
255 SUGG: Somehow make the night less colorful
256
257 TODO: Occlusion culling
258       - At the same time, move some of the renderMap() block choosing code
259         to the same place as where the new culling happens.
260       - Shoot some rays per frame and when ready, make a new list of
261             blocks for usage of renderMap and give it a new pointer to it.
262
263 Configuration:
264 --------------
265
266 Client:
267 -------
268
269 TODO: Untie client network operations from framerate
270       - Needs some input queues or something
271           - This won't give much performance boost because calculating block
272             meshes takes so long
273
274 SUGG: Make morning and evening transition more smooth and maybe shorter
275
276 TODO: Don't update all meshes always on single node changes, but
277       check which ones should be updated
278           - implement Map::updateNodeMeshes() and the usage of it
279           - It will give almost always a 4x boost in mesh update performance.
280
281 - A weapon engine
282
283 - Tool/weapon visualization
284
285 FIXME: When disconnected to the menu, memory is not freed properly
286
287 TODO: Investigate how much the mesh generator thread gets used when
288       transferring map data
289
290 Server:
291 -------
292
293 SUGG: Make an option to the server to disable building and digging near
294       the starting position
295
296 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
297
298 * Fix the problem with the server constantly saving one or a few
299   blocks? List the first saved block, maybe it explains.
300   - It is probably caused by oscillating water
301   - TODO: Investigate if this still happens (this is a very old one)
302 * Make a small history check to transformLiquids to detect and log
303   continuous oscillations, in such detail that they can be fixed.
304
305 FIXME: The new optimized map sending doesn't sometimes send enough blocks
306        from big caves and such
307 FIXME: Block send distance configuration does not take effect for some reason
308
309 Environment:
310 ------------
311
312 TODO: Add proper hooks to when adding and removing active blocks
313
314 TODO: Finish the ActiveBlockModifier stuff and use it for something
315
316 Objects:
317 --------
318
319 TODO: Get rid of MapBlockObjects and use only ActiveObjects
320         - Skipping the MapBlockObject data is nasty - there is no "total
321           length" stored; have to make a SkipMBOs function which contains
322           enough of the current code to skip them properly.
323
324 SUGG: MovingObject::move and Player::move are basically the same.
325       combine them.
326         - NOTE: This is a bit tricky because player has the sneaking ability
327         - NOTE: Player::move is more up-to-date.
328         - NOTE: There is a simple move implementation now in collision.{h,cpp}
329         - NOTE: MovingObject will be deleted (MapBlockObject)
330
331 TODO: Add a long step function to objects that is called with the time
332       difference when block activates
333
334 Map:
335 ----
336
337 TODO: Flowing water to actually contain flow direction information
338       - There is a space for this - it just has to be implemented.
339
340 TODO: Consider smoothening cave floors after generating them
341
342 TODO: Fix make_tree, make_* to use seed-position-consistent pseudorandom
343           - delta also
344
345 Misc. stuff:
346 ------------
347 TODO: Make sure server handles removing grass when a block is placed (etc)
348       - The client should not do it by itself
349           - NOTE: I think nobody does it currently...
350 TODO: Block cube placement around player's head
351 TODO: Protocol version field
352 TODO: Think about using same bits for material for fences and doors, for
353           example
354 TODO: Move mineral to param2, increment map serialization version, add
355       conversion
356
357 SUGG: Restart irrlicht completely when coming back to main menu from game.
358         - This gets rid of everything that is stored in irrlicht's caches.
359         - This might be needed for texture pack selection in menu
360
361 TODO: Merge bahamada's audio stuff (clean patch available)
362
363 Making it more portable:
364 ------------------------
365  
366 Stuff to do before release:
367 ---------------------------
368
369 Fixes to the current release:
370 -----------------------------
371
372 Stuff to do after release:
373 ---------------------------
374
375 Doing currently:
376 ----------------
377
378 ======================================================================
379
380 */
381
382 #ifdef NDEBUG
383         /*#ifdef _WIN32
384                 #pragma message ("Disabling unit tests")
385         #else
386                 #warning "Disabling unit tests"
387         #endif*/
388         // Disable unit tests
389         #define ENABLE_TESTS 0
390 #else
391         // Enable unit tests
392         #define ENABLE_TESTS 1
393 #endif
394
395 #ifdef _MSC_VER
396         #pragma comment(lib, "Irrlicht.lib")
397         //#pragma comment(lib, "jthread.lib")
398         #pragma comment(lib, "zlibwapi.lib")
399         #pragma comment(lib, "Shell32.lib")
400         // This would get rid of the console window
401         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
402 #endif
403
404 #include "irrlicht.h" // createDevice
405
406 #include "main.h"
407 #include "mainmenumanager.h"
408 #include <iostream>
409 #include <fstream>
410 #include <locale.h>
411 #include "common_irrlicht.h"
412 #include "debug.h"
413 #include "test.h"
414 #include "server.h"
415 #include "constants.h"
416 #include "porting.h"
417 #include "gettime.h"
418 #include "guiMessageMenu.h"
419 #include "filesys.h"
420 #include "config.h"
421 #include "guiMainMenu.h"
422 #include "mineral.h"
423 #include "materials.h"
424 #include "game.h"
425 #include "keycode.h"
426 #include "tile.h"
427 #include "defaultsettings.h"
428 #include "gettext.h"
429 #include "settings.h"
430 #include "profiler.h"
431 #include "log.h"
432 #include "mods.h"
433
434 /*
435         Settings.
436         These are loaded from the config file.
437 */
438 Settings main_settings;
439 Settings *g_settings = &main_settings;
440
441 // Global profiler
442 Profiler main_profiler;
443 Profiler *g_profiler = &main_profiler;
444
445 /*
446         Random stuff
447 */
448
449 /*
450         mainmenumanager.h
451 */
452
453 gui::IGUIEnvironment* guienv = NULL;
454 gui::IGUIStaticText *guiroot = NULL;
455 MainMenuManager g_menumgr;
456
457 bool noMenuActive()
458 {
459         return (g_menumgr.menuCount() == 0);
460 }
461
462 // Passed to menus to allow disconnecting and exiting
463 MainGameCallback *g_gamecallback = NULL;
464
465 /*
466         Debug streams
467 */
468
469 // Connection
470 std::ostream *dout_con_ptr = &dummyout;
471 std::ostream *derr_con_ptr = &verbosestream;
472 //std::ostream *dout_con_ptr = &infostream;
473 //std::ostream *derr_con_ptr = &errorstream;
474
475 // Server
476 std::ostream *dout_server_ptr = &infostream;
477 std::ostream *derr_server_ptr = &errorstream;
478
479 // Client
480 std::ostream *dout_client_ptr = &infostream;
481 std::ostream *derr_client_ptr = &errorstream;
482
483 /*
484         gettime.h implementation
485 */
486
487 // A small helper class
488 class TimeGetter
489 {
490 public:
491         virtual u32 getTime() = 0;
492 };
493
494 // A precise irrlicht one
495 class IrrlichtTimeGetter: public TimeGetter
496 {
497 public:
498         IrrlichtTimeGetter(IrrlichtDevice *device):
499                 m_device(device)
500         {}
501         u32 getTime()
502         {
503                 if(m_device == NULL)
504                         return 0;
505                 return m_device->getTimer()->getRealTime();
506         }
507 private:
508         IrrlichtDevice *m_device;
509 };
510 // Not so precise one which works without irrlicht
511 class SimpleTimeGetter: public TimeGetter
512 {
513 public:
514         u32 getTime()
515         {
516                 return porting::getTimeMs();
517         }
518 };
519
520 // A pointer to a global instance of the time getter
521 // TODO: why?
522 TimeGetter *g_timegetter = NULL;
523
524 u32 getTimeMs()
525 {
526         if(g_timegetter == NULL)
527                 return 0;
528         return g_timegetter->getTime();
529 }
530
531 /*
532         Event handler for Irrlicht
533
534         NOTE: Everything possible should be moved out from here,
535               probably to InputHandler and the_game
536 */
537
538 class MyEventReceiver : public IEventReceiver
539 {
540 public:
541         // This is the one method that we have to implement
542         virtual bool OnEvent(const SEvent& event)
543         {
544                 /*
545                         React to nothing here if a menu is active
546                 */
547                 if(noMenuActive() == false)
548                 {
549                         return false;
550                 }
551
552                 // Remember whether each key is down or up
553                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
554                 {
555                         if(event.KeyInput.PressedDown) {
556                                 keyIsDown.set(event.KeyInput);
557                                 keyWasDown.set(event.KeyInput);
558                         } else {
559                                 keyIsDown.unset(event.KeyInput);
560                         }
561                 }
562
563                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
564                 {
565                         if(noMenuActive() == false)
566                         {
567                                 left_active = false;
568                                 middle_active = false;
569                                 right_active = false;
570                         }
571                         else
572                         {
573                                 left_active = event.MouseInput.isLeftPressed();
574                                 middle_active = event.MouseInput.isMiddlePressed();
575                                 right_active = event.MouseInput.isRightPressed();
576
577                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
578                                 {
579                                         leftclicked = true;
580                                 }
581                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
582                                 {
583                                         rightclicked = true;
584                                 }
585                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
586                                 {
587                                         leftreleased = true;
588                                 }
589                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
590                                 {
591                                         rightreleased = true;
592                                 }
593                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
594                                 {
595                                         mouse_wheel += event.MouseInput.Wheel;
596                                 }
597                         }
598                 }
599
600                 return false;
601         }
602
603         bool IsKeyDown(const KeyPress &keyCode) const
604         {
605                 return keyIsDown[keyCode];
606         }
607         
608         // Checks whether a key was down and resets the state
609         bool WasKeyDown(const KeyPress &keyCode)
610         {
611                 bool b = keyWasDown[keyCode];
612                 if (b)
613                         keyWasDown.unset(keyCode);
614                 return b;
615         }
616
617         s32 getMouseWheel()
618         {
619                 s32 a = mouse_wheel;
620                 mouse_wheel = 0;
621                 return a;
622         }
623
624         void clearInput()
625         {
626                 keyIsDown.clear();
627                 keyWasDown.clear();
628
629                 leftclicked = false;
630                 rightclicked = false;
631                 leftreleased = false;
632                 rightreleased = false;
633
634                 left_active = false;
635                 middle_active = false;
636                 right_active = false;
637
638                 mouse_wheel = 0;
639         }
640
641         MyEventReceiver()
642         {
643                 clearInput();
644         }
645
646         bool leftclicked;
647         bool rightclicked;
648         bool leftreleased;
649         bool rightreleased;
650
651         bool left_active;
652         bool middle_active;
653         bool right_active;
654
655         s32 mouse_wheel;
656
657 private:
658         IrrlichtDevice *m_device;
659         
660         // The current state of keys
661         KeyList keyIsDown;
662         // Whether a key has been pressed or not
663         KeyList keyWasDown;
664 };
665
666 /*
667         Separated input handler
668 */
669
670 class RealInputHandler : public InputHandler
671 {
672 public:
673         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
674                 m_device(device),
675                 m_receiver(receiver)
676         {
677         }
678         virtual bool isKeyDown(const KeyPress &keyCode)
679         {
680                 return m_receiver->IsKeyDown(keyCode);
681         }
682         virtual bool wasKeyDown(const KeyPress &keyCode)
683         {
684                 return m_receiver->WasKeyDown(keyCode);
685         }
686         virtual v2s32 getMousePos()
687         {
688                 return m_device->getCursorControl()->getPosition();
689         }
690         virtual void setMousePos(s32 x, s32 y)
691         {
692                 m_device->getCursorControl()->setPosition(x, y);
693         }
694
695         virtual bool getLeftState()
696         {
697                 return m_receiver->left_active;
698         }
699         virtual bool getRightState()
700         {
701                 return m_receiver->right_active;
702         }
703         
704         virtual bool getLeftClicked()
705         {
706                 return m_receiver->leftclicked;
707         }
708         virtual bool getRightClicked()
709         {
710                 return m_receiver->rightclicked;
711         }
712         virtual void resetLeftClicked()
713         {
714                 m_receiver->leftclicked = false;
715         }
716         virtual void resetRightClicked()
717         {
718                 m_receiver->rightclicked = false;
719         }
720
721         virtual bool getLeftReleased()
722         {
723                 return m_receiver->leftreleased;
724         }
725         virtual bool getRightReleased()
726         {
727                 return m_receiver->rightreleased;
728         }
729         virtual void resetLeftReleased()
730         {
731                 m_receiver->leftreleased = false;
732         }
733         virtual void resetRightReleased()
734         {
735                 m_receiver->rightreleased = false;
736         }
737
738         virtual s32 getMouseWheel()
739         {
740                 return m_receiver->getMouseWheel();
741         }
742
743         void clear()
744         {
745                 m_receiver->clearInput();
746         }
747 private:
748         IrrlichtDevice *m_device;
749         MyEventReceiver *m_receiver;
750 };
751
752 class RandomInputHandler : public InputHandler
753 {
754 public:
755         RandomInputHandler()
756         {
757                 leftdown = false;
758                 rightdown = false;
759                 leftclicked = false;
760                 rightclicked = false;
761                 leftreleased = false;
762                 rightreleased = false;
763                 keydown.clear();
764         }
765         virtual bool isKeyDown(const KeyPress &keyCode)
766         {
767                 return keydown[keyCode];
768         }
769         virtual bool wasKeyDown(const KeyPress &keyCode)
770         {
771                 return false;
772         }
773         virtual v2s32 getMousePos()
774         {
775                 return mousepos;
776         }
777         virtual void setMousePos(s32 x, s32 y)
778         {
779                 mousepos = v2s32(x,y);
780         }
781
782         virtual bool getLeftState()
783         {
784                 return leftdown;
785         }
786         virtual bool getRightState()
787         {
788                 return rightdown;
789         }
790
791         virtual bool getLeftClicked()
792         {
793                 return leftclicked;
794         }
795         virtual bool getRightClicked()
796         {
797                 return rightclicked;
798         }
799         virtual void resetLeftClicked()
800         {
801                 leftclicked = false;
802         }
803         virtual void resetRightClicked()
804         {
805                 rightclicked = false;
806         }
807
808         virtual bool getLeftReleased()
809         {
810                 return leftreleased;
811         }
812         virtual bool getRightReleased()
813         {
814                 return rightreleased;
815         }
816         virtual void resetLeftReleased()
817         {
818                 leftreleased = false;
819         }
820         virtual void resetRightReleased()
821         {
822                 rightreleased = false;
823         }
824
825         virtual s32 getMouseWheel()
826         {
827                 return 0;
828         }
829
830         virtual void step(float dtime)
831         {
832                 {
833                         static float counter1 = 0;
834                         counter1 -= dtime;
835                         if(counter1 < 0.0)
836                         {
837                                 counter1 = 0.1*Rand(1, 40);
838                                 keydown.toggle(getKeySetting("keymap_jump"));
839                         }
840                 }
841                 {
842                         static float counter1 = 0;
843                         counter1 -= dtime;
844                         if(counter1 < 0.0)
845                         {
846                                 counter1 = 0.1*Rand(1, 40);
847                                 keydown.toggle(getKeySetting("keymap_special1"));
848                         }
849                 }
850                 {
851                         static float counter1 = 0;
852                         counter1 -= dtime;
853                         if(counter1 < 0.0)
854                         {
855                                 counter1 = 0.1*Rand(1, 40);
856                                 keydown.toggle(getKeySetting("keymap_forward"));
857                         }
858                 }
859                 {
860                         static float counter1 = 0;
861                         counter1 -= dtime;
862                         if(counter1 < 0.0)
863                         {
864                                 counter1 = 0.1*Rand(1, 40);
865                                 keydown.toggle(getKeySetting("keymap_left"));
866                         }
867                 }
868                 {
869                         static float counter1 = 0;
870                         counter1 -= dtime;
871                         if(counter1 < 0.0)
872                         {
873                                 counter1 = 0.1*Rand(1, 20);
874                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
875                         }
876                 }
877                 {
878                         static float counter1 = 0;
879                         counter1 -= dtime;
880                         if(counter1 < 0.0)
881                         {
882                                 counter1 = 0.1*Rand(1, 30);
883                                 leftdown = !leftdown;
884                                 if(leftdown)
885                                         leftclicked = true;
886                                 if(!leftdown)
887                                         leftreleased = true;
888                         }
889                 }
890                 {
891                         static float counter1 = 0;
892                         counter1 -= dtime;
893                         if(counter1 < 0.0)
894                         {
895                                 counter1 = 0.1*Rand(1, 15);
896                                 rightdown = !rightdown;
897                                 if(rightdown)
898                                         rightclicked = true;
899                                 if(!rightdown)
900                                         rightreleased = true;
901                         }
902                 }
903                 mousepos += mousespeed;
904         }
905
906         s32 Rand(s32 min, s32 max)
907         {
908                 return (myrand()%(max-min+1))+min;
909         }
910 private:
911         KeyList keydown;
912         v2s32 mousepos;
913         v2s32 mousespeed;
914         bool leftdown;
915         bool rightdown;
916         bool leftclicked;
917         bool rightclicked;
918         bool leftreleased;
919         bool rightreleased;
920 };
921
922 // These are defined global so that they're not optimized too much.
923 // Can't change them to volatile.
924 s16 temp16;
925 f32 tempf;
926 v3f tempv3f1;
927 v3f tempv3f2;
928 std::string tempstring;
929 std::string tempstring2;
930
931 void SpeedTests()
932 {
933         {
934                 dstream<<"The following test should take around 20ms."<<std::endl;
935                 TimeTaker timer("Testing std::string speed");
936                 const u32 jj = 10000;
937                 for(u32 j=0; j<jj; j++)
938                 {
939                         tempstring = "";
940                         tempstring2 = "";
941                         const u32 ii = 10;
942                         for(u32 i=0; i<ii; i++){
943                                 tempstring2 += "asd";
944                         }
945                         for(u32 i=0; i<ii+1; i++){
946                                 tempstring += "asd";
947                                 if(tempstring == tempstring2)
948                                         break;
949                         }
950                 }
951         }
952         
953         dstream<<"All of the following tests should take around 100ms each."
954                         <<std::endl;
955
956         {
957                 TimeTaker timer("Testing floating-point conversion speed");
958                 tempf = 0.001;
959                 for(u32 i=0; i<4000000; i++){
960                         temp16 += tempf;
961                         tempf += 0.001;
962                 }
963         }
964         
965         {
966                 TimeTaker timer("Testing floating-point vector speed");
967
968                 tempv3f1 = v3f(1,2,3);
969                 tempv3f2 = v3f(4,5,6);
970                 for(u32 i=0; i<10000000; i++){
971                         tempf += tempv3f1.dotProduct(tempv3f2);
972                         tempv3f2 += v3f(7,8,9);
973                 }
974         }
975
976         {
977                 TimeTaker timer("Testing core::map speed");
978                 
979                 core::map<v2s16, f32> map1;
980                 tempf = -324;
981                 const s16 ii=300;
982                 for(s16 y=0; y<ii; y++){
983                         for(s16 x=0; x<ii; x++){
984                                 map1.insert(v2s16(x,y), tempf);
985                                 tempf += 1;
986                         }
987                 }
988                 for(s16 y=ii-1; y>=0; y--){
989                         for(s16 x=0; x<ii; x++){
990                                 tempf = map1[v2s16(x,y)];
991                         }
992                 }
993         }
994
995         {
996                 dstream<<"Around 5000/ms should do well here."<<std::endl;
997                 TimeTaker timer("Testing mutex speed");
998                 
999                 JMutex m;
1000                 m.Init();
1001                 u32 n = 0;
1002                 u32 i = 0;
1003                 do{
1004                         n += 10000;
1005                         for(; i<n; i++){
1006                                 m.Lock();
1007                                 m.Unlock();
1008                         }
1009                 }
1010                 // Do at least 10ms
1011                 while(timer.getTime() < 10);
1012
1013                 u32 dtime = timer.stop();
1014                 u32 per_ms = n / dtime;
1015                 dstream<<"Done. "<<dtime<<"ms, "
1016                                 <<per_ms<<"/ms"<<std::endl;
1017         }
1018 }
1019
1020 void drawMenuBackground(video::IVideoDriver* driver)
1021 {
1022         core::dimension2d<u32> screensize = driver->getScreenSize();
1023                 
1024         video::ITexture *bgtexture =
1025                         driver->getTexture(getTexturePath("menubg.png").c_str());
1026         if(bgtexture)
1027         {
1028                 s32 texturesize = 128;
1029                 s32 tiled_y = screensize.Height / texturesize + 1;
1030                 s32 tiled_x = screensize.Width / texturesize + 1;
1031                 
1032                 for(s32 y=0; y<tiled_y; y++)
1033                 for(s32 x=0; x<tiled_x; x++)
1034                 {
1035                         core::rect<s32> rect(0,0,texturesize,texturesize);
1036                         rect += v2s32(x*texturesize, y*texturesize);
1037                         driver->draw2DImage(bgtexture, rect,
1038                                 core::rect<s32>(core::position2d<s32>(0,0),
1039                                 core::dimension2di(bgtexture->getSize())),
1040                                 NULL, NULL, true);
1041                 }
1042         }
1043         
1044         video::ITexture *logotexture =
1045                         driver->getTexture(getTexturePath("menulogo.png").c_str());
1046         if(logotexture)
1047         {
1048                 v2s32 logosize(logotexture->getOriginalSize().Width,
1049                                 logotexture->getOriginalSize().Height);
1050                 logosize *= 4;
1051
1052                 video::SColor bgcolor(255,50,50,50);
1053                 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
1054                                 screensize.Width, screensize.Height);
1055                 driver->draw2DRectangle(bgcolor, bgrect, NULL);
1056
1057                 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
1058                 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
1059                 rect -= v2s32(logosize.X/2, 0);
1060                 driver->draw2DImage(logotexture, rect,
1061                         core::rect<s32>(core::position2d<s32>(0,0),
1062                         core::dimension2di(logotexture->getSize())),
1063                         NULL, NULL, true);
1064         }
1065 }
1066
1067 class StderrLogOutput: public ILogOutput
1068 {
1069 public:
1070         /* line: Full line with timestamp, level and thread */
1071         void printLog(const std::string &line)
1072         {
1073                 std::cerr<<line<<std::endl;
1074         }
1075 } main_stderr_log_out;
1076
1077 class DstreamNoStderrLogOutput: public ILogOutput
1078 {
1079 public:
1080         /* line: Full line with timestamp, level and thread */
1081         void printLog(const std::string &line)
1082         {
1083                 dstream_no_stderr<<line<<std::endl;
1084         }
1085 } main_dstream_no_stderr_log_out;
1086
1087 int main(int argc, char *argv[])
1088 {
1089         /*
1090                 Initialization
1091         */
1092
1093         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
1094         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
1095
1096         log_register_thread("main");
1097
1098         // Set locale. This is for forcing '.' as the decimal point.
1099         std::locale::global(std::locale("C"));
1100         // This enables printing all characters in bitmap font
1101         setlocale(LC_CTYPE, "en_US");
1102
1103         /*
1104                 Parse command line
1105         */
1106         
1107         // List all allowed options
1108         core::map<std::string, ValueSpec> allowed_options;
1109         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
1110         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
1111                         "Run server directly"));
1112         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
1113                         "Load configuration from specified file"));
1114         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
1115         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
1116         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
1117         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
1118         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
1119         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
1120 #ifdef _WIN32
1121         allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
1122 #endif
1123         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
1124         allowed_options.insert("info-on-stderr", ValueSpec(VALUETYPE_FLAG));
1125
1126         Settings cmd_args;
1127         
1128         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
1129
1130         if(ret == false || cmd_args.getFlag("help"))
1131         {
1132                 dstream<<"Allowed options:"<<std::endl;
1133                 for(core::map<std::string, ValueSpec>::Iterator
1134                                 i = allowed_options.getIterator();
1135                                 i.atEnd() == false; i++)
1136                 {
1137                         dstream<<"  --"<<i.getNode()->getKey();
1138                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)
1139                         {
1140                         }
1141                         else
1142                         {
1143                                 dstream<<" <value>";
1144                         }
1145                         dstream<<std::endl;
1146
1147                         if(i.getNode()->getValue().help != NULL)
1148                         {
1149                                 dstream<<"      "<<i.getNode()->getValue().help
1150                                                 <<std::endl;
1151                         }
1152                 }
1153
1154                 return cmd_args.getFlag("help") ? 0 : 1;
1155         }
1156         
1157         /*
1158                 Low-level initialization
1159         */
1160
1161         bool disable_stderr = false;
1162 #ifdef _WIN32
1163         if(cmd_args.getFlag("dstream-on-stderr") == false)
1164                 disable_stderr = true;
1165 #endif
1166         
1167         if(cmd_args.getFlag("info-on-stderr"))
1168                 log_add_output(&main_stderr_log_out, LMT_INFO);
1169
1170         porting::signal_handler_init();
1171         bool &kill = *porting::signal_handler_killstatus();
1172         
1173         // Initialize porting::path_data and porting::path_userdata
1174         porting::initializePaths();
1175
1176         // Create user data directory
1177         fs::CreateDir(porting::path_userdata);
1178
1179         init_gettext((porting::path_data+DIR_DELIM+".."+DIR_DELIM+"locale").c_str());
1180         
1181         // Initialize debug streams
1182 #ifdef RUN_IN_PLACE
1183         std::string debugfile = DEBUGFILE;
1184 #else
1185         std::string debugfile = porting::path_userdata+DIR_DELIM+DEBUGFILE;
1186 #endif
1187         debugstreams_init(disable_stderr, debugfile.c_str());
1188         // Initialize debug stacks
1189         debug_stacks_init();
1190
1191         DSTACK(__FUNCTION_NAME);
1192
1193         // Init material properties table
1194         //initializeMaterialProperties();
1195
1196         // Debug handler
1197         BEGIN_DEBUG_EXCEPTION_HANDLER
1198
1199         // Print startup message
1200         actionstream<<PROJECT_NAME<<
1201                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
1202                         <<", "<<BUILD_INFO
1203                         <<std::endl;
1204         
1205         /*
1206                 Basic initialization
1207         */
1208
1209         // Initialize default settings
1210         set_default_settings(g_settings);
1211         
1212         // Initialize sockets
1213         sockets_init();
1214         atexit(sockets_cleanup);
1215         
1216         /*
1217                 Read config file
1218         */
1219         
1220         // Path of configuration file in use
1221         std::string configpath = "";
1222         
1223         if(cmd_args.exists("config"))
1224         {
1225                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
1226                 if(r == false)
1227                 {
1228                         errorstream<<"Could not read configuration from \""
1229                                         <<cmd_args.get("config")<<"\""<<std::endl;
1230                         return 1;
1231                 }
1232                 configpath = cmd_args.get("config");
1233         }
1234         else
1235         {
1236                 core::array<std::string> filenames;
1237                 filenames.push_back(porting::path_userdata +
1238                                 DIR_DELIM + "minetest.conf");
1239 #ifdef RUN_IN_PLACE
1240                 filenames.push_back(porting::path_userdata +
1241                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1242 #endif
1243
1244                 for(u32 i=0; i<filenames.size(); i++)
1245                 {
1246                         bool r = g_settings->readConfigFile(filenames[i].c_str());
1247                         if(r)
1248                         {
1249                                 configpath = filenames[i];
1250                                 break;
1251                         }
1252                 }
1253                 
1254                 // If no path found, use the first one (menu creates the file)
1255                 if(configpath == "")
1256                         configpath = filenames[0];
1257         }
1258
1259         // Initialize random seed
1260         srand(time(0));
1261         mysrand(time(0));
1262
1263         /*
1264                 Pre-initialize some stuff with a dummy irrlicht wrapper.
1265
1266                 These are needed for unit tests at least.
1267         */
1268         
1269         // Must be called before texturesource is created
1270         // (for texture atlas making)
1271         init_mineral();
1272
1273         /*
1274                 Run unit tests
1275         */
1276
1277         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1278                         || cmd_args.getFlag("enable-unittests") == true)
1279         {
1280                 run_tests();
1281         }
1282         
1283         /*for(s16 y=-100; y<100; y++)
1284         for(s16 x=-100; x<100; x++)
1285         {
1286                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
1287         }
1288         return 0;*/
1289         
1290         /*
1291                 Game parameters
1292         */
1293
1294         // Port
1295         u16 port = 30000;
1296         if(cmd_args.exists("port"))
1297                 port = cmd_args.getU16("port");
1298         else if(g_settings->exists("port"))
1299                 port = g_settings->getU16("port");
1300         if(port == 0)
1301                 port = 30000;
1302         
1303         // Map directory
1304         std::string map_dir = porting::path_userdata+DIR_DELIM+"world";
1305         if(cmd_args.exists("map-dir"))
1306                 map_dir = cmd_args.get("map-dir");
1307         else if(g_settings->exists("map-dir"))
1308                 map_dir = g_settings->get("map-dir");
1309         
1310         // Run dedicated server if asked to
1311         if(cmd_args.getFlag("server"))
1312         {
1313                 DSTACK("Dedicated server branch");
1314
1315                 // Create time getter
1316                 g_timegetter = new SimpleTimeGetter();
1317                 
1318                 // Create server
1319                 Server server(map_dir.c_str(), configpath);
1320                 server.start(port);
1321                 
1322                 // Run server
1323                 dedicated_server_loop(server, kill);
1324
1325                 return 0;
1326         }
1327
1328
1329         /*
1330                 More parameters
1331         */
1332         
1333         // Address to connect to
1334         std::string address = "";
1335         
1336         if(cmd_args.exists("address"))
1337         {
1338                 address = cmd_args.get("address");
1339         }
1340         else
1341         {
1342                 address = g_settings->get("address");
1343         }
1344         
1345         std::string playername = g_settings->get("name");
1346
1347         /*
1348                 Device initialization
1349         */
1350
1351         // Resolution selection
1352         
1353         bool fullscreen = false;
1354         u16 screenW = g_settings->getU16("screenW");
1355         u16 screenH = g_settings->getU16("screenH");
1356
1357         // Determine driver
1358
1359         video::E_DRIVER_TYPE driverType;
1360         
1361         std::string driverstring = g_settings->get("video_driver");
1362
1363         if(driverstring == "null")
1364                 driverType = video::EDT_NULL;
1365         else if(driverstring == "software")
1366                 driverType = video::EDT_SOFTWARE;
1367         else if(driverstring == "burningsvideo")
1368                 driverType = video::EDT_BURNINGSVIDEO;
1369         else if(driverstring == "direct3d8")
1370                 driverType = video::EDT_DIRECT3D8;
1371         else if(driverstring == "direct3d9")
1372                 driverType = video::EDT_DIRECT3D9;
1373         else if(driverstring == "opengl")
1374                 driverType = video::EDT_OPENGL;
1375         else
1376         {
1377                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1378                                 "to opengl"<<std::endl;
1379                 driverType = video::EDT_OPENGL;
1380         }
1381
1382         /*
1383                 Create device and exit if creation failed
1384         */
1385
1386         MyEventReceiver receiver;
1387
1388         IrrlichtDevice *device;
1389         device = createDevice(driverType,
1390                         core::dimension2d<u32>(screenW, screenH),
1391                         16, fullscreen, false, false, &receiver);
1392
1393         if (device == 0)
1394                 return 1; // could not create selected driver.
1395         
1396         /*
1397                 Continue initialization
1398         */
1399
1400         video::IVideoDriver* driver = device->getVideoDriver();
1401
1402         // Disable mipmaps (because some of them look ugly)
1403         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1404
1405         /*
1406                 This changes the minimum allowed number of vertices in a VBO.
1407                 Default is 500.
1408         */
1409         //driver->setMinHardwareBufferVertexCount(50);
1410
1411         // Set the window caption
1412         device->setWindowCaption(L"Minetest [Main Menu]");
1413         
1414         // Create time getter
1415         g_timegetter = new IrrlichtTimeGetter(device);
1416         
1417         // Create game callback for menus
1418         g_gamecallback = new MainGameCallback(device);
1419         
1420         /*
1421                 Speed tests (done after irrlicht is loaded to get timer)
1422         */
1423         if(cmd_args.getFlag("speedtests"))
1424         {
1425                 dstream<<"Running speed tests"<<std::endl;
1426                 SpeedTests();
1427                 return 0;
1428         }
1429         
1430         device->setResizable(true);
1431
1432         bool random_input = g_settings->getBool("random_input")
1433                         || cmd_args.getFlag("random-input");
1434         InputHandler *input = NULL;
1435         if(random_input)
1436                 input = new RandomInputHandler();
1437         else
1438                 input = new RealInputHandler(device, &receiver);
1439         
1440         scene::ISceneManager* smgr = device->getSceneManager();
1441
1442         guienv = device->getGUIEnvironment();
1443         gui::IGUISkin* skin = guienv->getSkin();
1444         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1445         if(font)
1446                 skin->setFont(font);
1447         else
1448                 errorstream<<"WARNING: Font file was not found."
1449                                 " Using default font."<<std::endl;
1450         // If font was not found, this will get us one
1451         font = skin->getFont();
1452         assert(font);
1453         
1454         u32 text_height = font->getDimension(L"Hello, world!").Height;
1455         infostream<<"text_height="<<text_height<<std::endl;
1456
1457         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1458         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1459         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1460         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1461         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1462         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1463         
1464         /*
1465                 GUI stuff
1466         */
1467
1468         /*
1469                 If an error occurs, this is set to something and the
1470                 menu-game loop is restarted. It is then displayed before
1471                 the menu.
1472         */
1473         std::wstring error_message = L"";
1474
1475         // The password entered during the menu screen,
1476         std::string password;
1477
1478         /*
1479                 Menu-game loop
1480         */
1481         while(device->run() && kill == false)
1482         {
1483
1484                 // This is used for catching disconnects
1485                 try
1486                 {
1487
1488                         /*
1489                                 Clear everything from the GUIEnvironment
1490                         */
1491                         guienv->clear();
1492                         
1493                         /*
1494                                 We need some kind of a root node to be able to add
1495                                 custom gui elements directly on the screen.
1496                                 Otherwise they won't be automatically drawn.
1497                         */
1498                         guiroot = guienv->addStaticText(L"",
1499                                         core::rect<s32>(0, 0, 10000, 10000));
1500                         
1501                         /*
1502                                 Out-of-game menu loop.
1503
1504                                 Loop quits when menu returns proper parameters.
1505                         */
1506                         while(kill == false)
1507                         {
1508                                 // Cursor can be non-visible when coming from the game
1509                                 device->getCursorControl()->setVisible(true);
1510                                 // Some stuff are left to scene manager when coming from the game
1511                                 // (map at least?)
1512                                 smgr->clear();
1513                                 // Reset or hide the debug gui texts
1514                                 /*guitext->setText(L"Minetest-c55");
1515                                 guitext2->setVisible(false);
1516                                 guitext_info->setVisible(false);
1517                                 guitext_chat->setVisible(false);*/
1518                                 
1519                                 // Initialize menu data
1520                                 MainMenuData menudata;
1521                                 menudata.address = narrow_to_wide(address);
1522                                 menudata.name = narrow_to_wide(playername);
1523                                 menudata.port = narrow_to_wide(itos(port));
1524                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1525                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1526                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1527                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1528                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1529                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1530
1531                                 GUIMainMenu *menu =
1532                                                 new GUIMainMenu(guienv, guiroot, -1, 
1533                                                         &g_menumgr, &menudata, g_gamecallback);
1534                                 menu->allowFocusRemoval(true);
1535
1536                                 if(error_message != L"")
1537                                 {
1538                                         errorstream<<"error_message = "
1539                                                         <<wide_to_narrow(error_message)<<std::endl;
1540
1541                                         GUIMessageMenu *menu2 =
1542                                                         new GUIMessageMenu(guienv, guiroot, -1, 
1543                                                                 &g_menumgr, error_message.c_str());
1544                                         menu2->drop();
1545                                         error_message = L"";
1546                                 }
1547
1548                                 video::IVideoDriver* driver = device->getVideoDriver();
1549                                 
1550                                 infostream<<"Created main menu"<<std::endl;
1551
1552                                 while(device->run() && kill == false)
1553                                 {
1554                                         if(menu->getStatus() == true)
1555                                                 break;
1556
1557                                         //driver->beginScene(true, true, video::SColor(255,0,0,0));
1558                                         driver->beginScene(true, true, video::SColor(255,128,128,128));
1559
1560                                         drawMenuBackground(driver);
1561
1562                                         guienv->drawAll();
1563                                         
1564                                         driver->endScene();
1565                                         
1566                                         // On some computers framerate doesn't seem to be
1567                                         // automatically limited
1568                                         sleep_ms(25);
1569                                 }
1570                                 
1571                                 // Break out of menu-game loop to shut down cleanly
1572                                 if(device->run() == false || kill == true)
1573                                         break;
1574                                 
1575                                 infostream<<"Dropping main menu"<<std::endl;
1576
1577                                 menu->drop();
1578                                 
1579                                 // Delete map if requested
1580                                 if(menudata.delete_map)
1581                                 {
1582                                         bool r = fs::RecursiveDeleteContent(map_dir);
1583                                         if(r == false)
1584                                                 error_message = L"Delete failed";
1585                                         continue;
1586                                 }
1587
1588                                 playername = wide_to_narrow(menudata.name);
1589
1590                                 password = translatePassword(playername, menudata.password);
1591
1592                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1593
1594                                 address = wide_to_narrow(menudata.address);
1595                                 int newport = stoi(wide_to_narrow(menudata.port));
1596                                 if(newport != 0)
1597                                         port = newport;
1598                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1599                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1600                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1601                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1602                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1603                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1604                                 
1605                                 // NOTE: These are now checked server side; no need to do it
1606                                 //       here, so let's not do it here.
1607                                 /*// Check for valid parameters, restart menu if invalid.
1608                                 if(playername == "")
1609                                 {
1610                                         error_message = L"Name required.";
1611                                         continue;
1612                                 }
1613                                 // Check that name has only valid chars
1614                                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1615                                 {
1616                                         error_message = L"Characters allowed: "
1617                                                         +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
1618                                         continue;
1619                                 }*/
1620
1621                                 // Save settings
1622                                 g_settings->set("name", playername);
1623                                 g_settings->set("address", address);
1624                                 g_settings->set("port", itos(port));
1625                                 // Update configuration file
1626                                 if(configpath != "")
1627                                         g_settings->updateConfigFile(configpath.c_str());
1628                         
1629                                 // Continue to game
1630                                 break;
1631                         }
1632                         
1633                         // Break out of menu-game loop to shut down cleanly
1634                         if(device->run() == false || kill == true)
1635                                 break;
1636                         
1637                         /*
1638                                 Run game
1639                         */
1640                         the_game(
1641                                 kill,
1642                                 random_input,
1643                                 input,
1644                                 device,
1645                                 font,
1646                                 map_dir,
1647                                 playername,
1648                                 password,
1649                                 address,
1650                                 port,
1651                                 error_message,
1652                                 configpath
1653                         );
1654
1655                 } //try
1656                 catch(con::PeerNotFoundException &e)
1657                 {
1658                         errorstream<<"Connection error (timed out?)"<<std::endl;
1659                         error_message = L"Connection error (timed out?)";
1660                 }
1661                 catch(SocketException &e)
1662                 {
1663                         errorstream<<"Socket error (port already in use?)"<<std::endl;
1664                         error_message = L"Socket error (port already in use?)";
1665                 }
1666                 catch(ModError &e)
1667                 {
1668                         errorstream<<e.what()<<std::endl;
1669                         error_message = narrow_to_wide(e.what()) + L"\nCheck debug.txt for details.";
1670                 }
1671 #ifdef NDEBUG
1672                 catch(std::exception &e)
1673                 {
1674                         std::string narrow_message = "Some exception, what()=\"";
1675                         narrow_message += e.what();
1676                         narrow_message += "\"";
1677                         errorstream<<narrow_message<<std::endl;
1678                         error_message = narrow_to_wide(narrow_message);
1679                 }
1680 #endif
1681
1682         } // Menu-game loop
1683         
1684         delete input;
1685
1686         /*
1687                 In the end, delete the Irrlicht device.
1688         */
1689         device->drop();
1690         
1691         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1692         
1693         debugstreams_deinit();
1694         
1695         return 0;
1696 }
1697
1698 //END
1699