63dc949559daa6057090251fc5fe42356523ce4c
[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: Mineral and ground material properties
338       - This way mineral ground toughness can be calculated with just
339             some formula, as well as tool strengths. Sounds too.
340           - There are TODOs in appropriate files: material.h, content_mapnode.h
341
342 TODO: Flowing water to actually contain flow direction information
343       - There is a space for this - it just has to be implemented.
344
345 TODO: Consider smoothening cave floors after generating them
346
347 TODO: Fix make_tree, make_* to use seed-position-consistent pseudorandom
348           - delta also
349
350 Misc. stuff:
351 ------------
352 TODO: Make sure server handles removing grass when a block is placed (etc)
353       - The client should not do it by itself
354           - NOTE: I think nobody does it currently...
355 TODO: Block cube placement around player's head
356 TODO: Protocol version field
357 TODO: Think about using same bits for material for fences and doors, for
358           example
359 TODO: Move mineral to param2, increment map serialization version, add
360       conversion
361
362 SUGG: Restart irrlicht completely when coming back to main menu from game.
363         - This gets rid of everything that is stored in irrlicht's caches.
364         - This might be needed for texture pack selection in menu
365
366 TODO: Merge bahamada's audio stuff (clean patch available)
367
368 Making it more portable:
369 ------------------------
370  
371 Stuff to do before release:
372 ---------------------------
373
374 Fixes to the current release:
375 -----------------------------
376
377 Stuff to do after release:
378 ---------------------------
379
380 Doing currently:
381 ----------------
382
383 ======================================================================
384
385 */
386
387 #ifdef NDEBUG
388         /*#ifdef _WIN32
389                 #pragma message ("Disabling unit tests")
390         #else
391                 #warning "Disabling unit tests"
392         #endif*/
393         // Disable unit tests
394         #define ENABLE_TESTS 0
395 #else
396         // Enable unit tests
397         #define ENABLE_TESTS 1
398 #endif
399
400 #ifdef _MSC_VER
401         #pragma comment(lib, "Irrlicht.lib")
402         //#pragma comment(lib, "jthread.lib")
403         #pragma comment(lib, "zlibwapi.lib")
404         #pragma comment(lib, "Shell32.lib")
405         // This would get rid of the console window
406         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
407 #endif
408
409 #include "irrlicht.h" // createDevice
410
411 #include "main.h"
412 #include "mainmenumanager.h"
413 #include <iostream>
414 #include <fstream>
415 #include <locale.h>
416 #include "common_irrlicht.h"
417 #include "debug.h"
418 #include "test.h"
419 #include "server.h"
420 #include "constants.h"
421 #include "porting.h"
422 #include "gettime.h"
423 #include "guiMessageMenu.h"
424 #include "filesys.h"
425 #include "config.h"
426 #include "guiMainMenu.h"
427 #include "mineral.h"
428 #include "materials.h"
429 #include "game.h"
430 #include "keycode.h"
431 #include "tile.h"
432 #include "defaultsettings.h"
433 #include "gettext.h"
434 #include "settings.h"
435 #include "profiler.h"
436 #include "log.h"
437 #include "mapnode_contentfeatures.h" // For init_contentfeatures
438 #include "content_mapnode.h" // For content_mapnode_init
439
440 /*
441         Settings.
442         These are loaded from the config file.
443 */
444 Settings main_settings;
445 Settings *g_settings = &main_settings;
446
447 // Global profiler
448 Profiler main_profiler;
449 Profiler *g_profiler = &main_profiler;
450
451 /*
452         Random stuff
453 */
454
455 /*
456         mainmenumanager.h
457 */
458
459 gui::IGUIEnvironment* guienv = NULL;
460 gui::IGUIStaticText *guiroot = NULL;
461 MainMenuManager g_menumgr;
462
463 bool noMenuActive()
464 {
465         return (g_menumgr.menuCount() == 0);
466 }
467
468 // Passed to menus to allow disconnecting and exiting
469 MainGameCallback *g_gamecallback = NULL;
470
471 /*
472         Debug streams
473 */
474
475 // Connection
476 std::ostream *dout_con_ptr = &dummyout;
477 std::ostream *derr_con_ptr = &verbosestream;
478 //std::ostream *dout_con_ptr = &infostream;
479 //std::ostream *derr_con_ptr = &errorstream;
480
481 // Server
482 std::ostream *dout_server_ptr = &infostream;
483 std::ostream *derr_server_ptr = &errorstream;
484
485 // Client
486 std::ostream *dout_client_ptr = &infostream;
487 std::ostream *derr_client_ptr = &errorstream;
488
489 /*
490         gettime.h implementation
491 */
492
493 // A small helper class
494 class TimeGetter
495 {
496 public:
497         virtual u32 getTime() = 0;
498 };
499
500 // A precise irrlicht one
501 class IrrlichtTimeGetter: public TimeGetter
502 {
503 public:
504         IrrlichtTimeGetter(IrrlichtDevice *device):
505                 m_device(device)
506         {}
507         u32 getTime()
508         {
509                 if(m_device == NULL)
510                         return 0;
511                 return m_device->getTimer()->getRealTime();
512         }
513 private:
514         IrrlichtDevice *m_device;
515 };
516 // Not so precise one which works without irrlicht
517 class SimpleTimeGetter: public TimeGetter
518 {
519 public:
520         u32 getTime()
521         {
522                 return porting::getTimeMs();
523         }
524 };
525
526 // A pointer to a global instance of the time getter
527 // TODO: why?
528 TimeGetter *g_timegetter = NULL;
529
530 u32 getTimeMs()
531 {
532         if(g_timegetter == NULL)
533                 return 0;
534         return g_timegetter->getTime();
535 }
536
537 /*
538         Event handler for Irrlicht
539
540         NOTE: Everything possible should be moved out from here,
541               probably to InputHandler and the_game
542 */
543
544 class MyEventReceiver : public IEventReceiver
545 {
546 public:
547         // This is the one method that we have to implement
548         virtual bool OnEvent(const SEvent& event)
549         {
550                 /*
551                         React to nothing here if a menu is active
552                 */
553                 if(noMenuActive() == false)
554                 {
555                         return false;
556                 }
557
558                 // Remember whether each key is down or up
559                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
560                 {
561                         if(event.KeyInput.PressedDown) {
562                                 keyIsDown.set(event.KeyInput);
563                                 keyWasDown.set(event.KeyInput);
564                         } else {
565                                 keyIsDown.unset(event.KeyInput);
566                         }
567                 }
568
569                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
570                 {
571                         if(noMenuActive() == false)
572                         {
573                                 left_active = false;
574                                 middle_active = false;
575                                 right_active = false;
576                         }
577                         else
578                         {
579                                 left_active = event.MouseInput.isLeftPressed();
580                                 middle_active = event.MouseInput.isMiddlePressed();
581                                 right_active = event.MouseInput.isRightPressed();
582
583                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
584                                 {
585                                         leftclicked = true;
586                                 }
587                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
588                                 {
589                                         rightclicked = true;
590                                 }
591                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
592                                 {
593                                         leftreleased = true;
594                                 }
595                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
596                                 {
597                                         rightreleased = true;
598                                 }
599                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
600                                 {
601                                         mouse_wheel += event.MouseInput.Wheel;
602                                 }
603                         }
604                 }
605
606                 return false;
607         }
608
609         bool IsKeyDown(const KeyPress &keyCode) const
610         {
611                 return keyIsDown[keyCode];
612         }
613         
614         // Checks whether a key was down and resets the state
615         bool WasKeyDown(const KeyPress &keyCode)
616         {
617                 bool b = keyWasDown[keyCode];
618                 if (b)
619                         keyWasDown.unset(keyCode);
620                 return b;
621         }
622
623         s32 getMouseWheel()
624         {
625                 s32 a = mouse_wheel;
626                 mouse_wheel = 0;
627                 return a;
628         }
629
630         void clearInput()
631         {
632                 keyIsDown.clear();
633                 keyWasDown.clear();
634
635                 leftclicked = false;
636                 rightclicked = false;
637                 leftreleased = false;
638                 rightreleased = false;
639
640                 left_active = false;
641                 middle_active = false;
642                 right_active = false;
643
644                 mouse_wheel = 0;
645         }
646
647         MyEventReceiver()
648         {
649                 clearInput();
650         }
651
652         bool leftclicked;
653         bool rightclicked;
654         bool leftreleased;
655         bool rightreleased;
656
657         bool left_active;
658         bool middle_active;
659         bool right_active;
660
661         s32 mouse_wheel;
662
663 private:
664         IrrlichtDevice *m_device;
665         
666         // The current state of keys
667         KeyList keyIsDown;
668         // Whether a key has been pressed or not
669         KeyList keyWasDown;
670 };
671
672 /*
673         Separated input handler
674 */
675
676 class RealInputHandler : public InputHandler
677 {
678 public:
679         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
680                 m_device(device),
681                 m_receiver(receiver)
682         {
683         }
684         virtual bool isKeyDown(const KeyPress &keyCode)
685         {
686                 return m_receiver->IsKeyDown(keyCode);
687         }
688         virtual bool wasKeyDown(const KeyPress &keyCode)
689         {
690                 return m_receiver->WasKeyDown(keyCode);
691         }
692         virtual v2s32 getMousePos()
693         {
694                 return m_device->getCursorControl()->getPosition();
695         }
696         virtual void setMousePos(s32 x, s32 y)
697         {
698                 m_device->getCursorControl()->setPosition(x, y);
699         }
700
701         virtual bool getLeftState()
702         {
703                 return m_receiver->left_active;
704         }
705         virtual bool getRightState()
706         {
707                 return m_receiver->right_active;
708         }
709         
710         virtual bool getLeftClicked()
711         {
712                 return m_receiver->leftclicked;
713         }
714         virtual bool getRightClicked()
715         {
716                 return m_receiver->rightclicked;
717         }
718         virtual void resetLeftClicked()
719         {
720                 m_receiver->leftclicked = false;
721         }
722         virtual void resetRightClicked()
723         {
724                 m_receiver->rightclicked = false;
725         }
726
727         virtual bool getLeftReleased()
728         {
729                 return m_receiver->leftreleased;
730         }
731         virtual bool getRightReleased()
732         {
733                 return m_receiver->rightreleased;
734         }
735         virtual void resetLeftReleased()
736         {
737                 m_receiver->leftreleased = false;
738         }
739         virtual void resetRightReleased()
740         {
741                 m_receiver->rightreleased = false;
742         }
743
744         virtual s32 getMouseWheel()
745         {
746                 return m_receiver->getMouseWheel();
747         }
748
749         void clear()
750         {
751                 m_receiver->clearInput();
752         }
753 private:
754         IrrlichtDevice *m_device;
755         MyEventReceiver *m_receiver;
756 };
757
758 class RandomInputHandler : public InputHandler
759 {
760 public:
761         RandomInputHandler()
762         {
763                 leftdown = false;
764                 rightdown = false;
765                 leftclicked = false;
766                 rightclicked = false;
767                 leftreleased = false;
768                 rightreleased = false;
769                 keydown.clear();
770         }
771         virtual bool isKeyDown(const KeyPress &keyCode)
772         {
773                 return keydown[keyCode];
774         }
775         virtual bool wasKeyDown(const KeyPress &keyCode)
776         {
777                 return false;
778         }
779         virtual v2s32 getMousePos()
780         {
781                 return mousepos;
782         }
783         virtual void setMousePos(s32 x, s32 y)
784         {
785                 mousepos = v2s32(x,y);
786         }
787
788         virtual bool getLeftState()
789         {
790                 return leftdown;
791         }
792         virtual bool getRightState()
793         {
794                 return rightdown;
795         }
796
797         virtual bool getLeftClicked()
798         {
799                 return leftclicked;
800         }
801         virtual bool getRightClicked()
802         {
803                 return rightclicked;
804         }
805         virtual void resetLeftClicked()
806         {
807                 leftclicked = false;
808         }
809         virtual void resetRightClicked()
810         {
811                 rightclicked = false;
812         }
813
814         virtual bool getLeftReleased()
815         {
816                 return leftreleased;
817         }
818         virtual bool getRightReleased()
819         {
820                 return rightreleased;
821         }
822         virtual void resetLeftReleased()
823         {
824                 leftreleased = false;
825         }
826         virtual void resetRightReleased()
827         {
828                 rightreleased = false;
829         }
830
831         virtual s32 getMouseWheel()
832         {
833                 return 0;
834         }
835
836         virtual void step(float dtime)
837         {
838                 {
839                         static float counter1 = 0;
840                         counter1 -= dtime;
841                         if(counter1 < 0.0)
842                         {
843                                 counter1 = 0.1*Rand(1, 40);
844                                 keydown.toggle(getKeySetting("keymap_jump"));
845                         }
846                 }
847                 {
848                         static float counter1 = 0;
849                         counter1 -= dtime;
850                         if(counter1 < 0.0)
851                         {
852                                 counter1 = 0.1*Rand(1, 40);
853                                 keydown.toggle(getKeySetting("keymap_special1"));
854                         }
855                 }
856                 {
857                         static float counter1 = 0;
858                         counter1 -= dtime;
859                         if(counter1 < 0.0)
860                         {
861                                 counter1 = 0.1*Rand(1, 40);
862                                 keydown.toggle(getKeySetting("keymap_forward"));
863                         }
864                 }
865                 {
866                         static float counter1 = 0;
867                         counter1 -= dtime;
868                         if(counter1 < 0.0)
869                         {
870                                 counter1 = 0.1*Rand(1, 40);
871                                 keydown.toggle(getKeySetting("keymap_left"));
872                         }
873                 }
874                 {
875                         static float counter1 = 0;
876                         counter1 -= dtime;
877                         if(counter1 < 0.0)
878                         {
879                                 counter1 = 0.1*Rand(1, 20);
880                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
881                         }
882                 }
883                 {
884                         static float counter1 = 0;
885                         counter1 -= dtime;
886                         if(counter1 < 0.0)
887                         {
888                                 counter1 = 0.1*Rand(1, 30);
889                                 leftdown = !leftdown;
890                                 if(leftdown)
891                                         leftclicked = true;
892                                 if(!leftdown)
893                                         leftreleased = true;
894                         }
895                 }
896                 {
897                         static float counter1 = 0;
898                         counter1 -= dtime;
899                         if(counter1 < 0.0)
900                         {
901                                 counter1 = 0.1*Rand(1, 15);
902                                 rightdown = !rightdown;
903                                 if(rightdown)
904                                         rightclicked = true;
905                                 if(!rightdown)
906                                         rightreleased = true;
907                         }
908                 }
909                 mousepos += mousespeed;
910         }
911
912         s32 Rand(s32 min, s32 max)
913         {
914                 return (myrand()%(max-min+1))+min;
915         }
916 private:
917         KeyList keydown;
918         v2s32 mousepos;
919         v2s32 mousespeed;
920         bool leftdown;
921         bool rightdown;
922         bool leftclicked;
923         bool rightclicked;
924         bool leftreleased;
925         bool rightreleased;
926 };
927
928 // These are defined global so that they're not optimized too much.
929 // Can't change them to volatile.
930 s16 temp16;
931 f32 tempf;
932 v3f tempv3f1;
933 v3f tempv3f2;
934 std::string tempstring;
935 std::string tempstring2;
936
937 void SpeedTests()
938 {
939         {
940                 dstream<<"The following test should take around 20ms."<<std::endl;
941                 TimeTaker timer("Testing std::string speed");
942                 const u32 jj = 10000;
943                 for(u32 j=0; j<jj; j++)
944                 {
945                         tempstring = "";
946                         tempstring2 = "";
947                         const u32 ii = 10;
948                         for(u32 i=0; i<ii; i++){
949                                 tempstring2 += "asd";
950                         }
951                         for(u32 i=0; i<ii+1; i++){
952                                 tempstring += "asd";
953                                 if(tempstring == tempstring2)
954                                         break;
955                         }
956                 }
957         }
958         
959         dstream<<"All of the following tests should take around 100ms each."
960                         <<std::endl;
961
962         {
963                 TimeTaker timer("Testing floating-point conversion speed");
964                 tempf = 0.001;
965                 for(u32 i=0; i<4000000; i++){
966                         temp16 += tempf;
967                         tempf += 0.001;
968                 }
969         }
970         
971         {
972                 TimeTaker timer("Testing floating-point vector speed");
973
974                 tempv3f1 = v3f(1,2,3);
975                 tempv3f2 = v3f(4,5,6);
976                 for(u32 i=0; i<10000000; i++){
977                         tempf += tempv3f1.dotProduct(tempv3f2);
978                         tempv3f2 += v3f(7,8,9);
979                 }
980         }
981
982         {
983                 TimeTaker timer("Testing core::map speed");
984                 
985                 core::map<v2s16, f32> map1;
986                 tempf = -324;
987                 const s16 ii=300;
988                 for(s16 y=0; y<ii; y++){
989                         for(s16 x=0; x<ii; x++){
990                                 map1.insert(v2s16(x,y), tempf);
991                                 tempf += 1;
992                         }
993                 }
994                 for(s16 y=ii-1; y>=0; y--){
995                         for(s16 x=0; x<ii; x++){
996                                 tempf = map1[v2s16(x,y)];
997                         }
998                 }
999         }
1000
1001         {
1002                 dstream<<"Around 5000/ms should do well here."<<std::endl;
1003                 TimeTaker timer("Testing mutex speed");
1004                 
1005                 JMutex m;
1006                 m.Init();
1007                 u32 n = 0;
1008                 u32 i = 0;
1009                 do{
1010                         n += 10000;
1011                         for(; i<n; i++){
1012                                 m.Lock();
1013                                 m.Unlock();
1014                         }
1015                 }
1016                 // Do at least 10ms
1017                 while(timer.getTime() < 10);
1018
1019                 u32 dtime = timer.stop();
1020                 u32 per_ms = n / dtime;
1021                 dstream<<"Done. "<<dtime<<"ms, "
1022                                 <<per_ms<<"/ms"<<std::endl;
1023         }
1024 }
1025
1026 void drawMenuBackground(video::IVideoDriver* driver)
1027 {
1028         core::dimension2d<u32> screensize = driver->getScreenSize();
1029                 
1030         video::ITexture *bgtexture =
1031                         driver->getTexture(getTexturePath("mud.png").c_str());
1032         if(bgtexture)
1033         {
1034                 s32 texturesize = 128;
1035                 s32 tiled_y = screensize.Height / texturesize + 1;
1036                 s32 tiled_x = screensize.Width / texturesize + 1;
1037                 
1038                 for(s32 y=0; y<tiled_y; y++)
1039                 for(s32 x=0; x<tiled_x; x++)
1040                 {
1041                         core::rect<s32> rect(0,0,texturesize,texturesize);
1042                         rect += v2s32(x*texturesize, y*texturesize);
1043                         driver->draw2DImage(bgtexture, rect,
1044                                 core::rect<s32>(core::position2d<s32>(0,0),
1045                                 core::dimension2di(bgtexture->getSize())),
1046                                 NULL, NULL, true);
1047                 }
1048         }
1049         
1050         video::ITexture *logotexture =
1051                         driver->getTexture(getTexturePath("menulogo.png").c_str());
1052         if(logotexture)
1053         {
1054                 v2s32 logosize(logotexture->getOriginalSize().Width,
1055                                 logotexture->getOriginalSize().Height);
1056                 logosize *= 4;
1057
1058                 video::SColor bgcolor(255,50,50,50);
1059                 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,
1060                                 screensize.Width, screensize.Height);
1061                 driver->draw2DRectangle(bgcolor, bgrect, NULL);
1062
1063                 core::rect<s32> rect(0,0,logosize.X,logosize.Y);
1064                 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);
1065                 rect -= v2s32(logosize.X/2, 0);
1066                 driver->draw2DImage(logotexture, rect,
1067                         core::rect<s32>(core::position2d<s32>(0,0),
1068                         core::dimension2di(logotexture->getSize())),
1069                         NULL, NULL, true);
1070         }
1071 }
1072
1073 class StderrLogOutput: public ILogOutput
1074 {
1075 public:
1076         /* line: Full line with timestamp, level and thread */
1077         void printLog(const std::string &line)
1078         {
1079                 std::cerr<<line<<std::endl;
1080         }
1081 } main_stderr_log_out;
1082
1083 class DstreamNoStderrLogOutput: public ILogOutput
1084 {
1085 public:
1086         /* line: Full line with timestamp, level and thread */
1087         void printLog(const std::string &line)
1088         {
1089                 dstream_no_stderr<<line<<std::endl;
1090         }
1091 } main_dstream_no_stderr_log_out;
1092
1093 int main(int argc, char *argv[])
1094 {
1095         /*
1096                 Initialization
1097         */
1098
1099         log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
1100         log_add_output_all_levs(&main_dstream_no_stderr_log_out);
1101
1102         log_register_thread("main");
1103
1104         // Set locale. This is for forcing '.' as the decimal point.
1105         std::locale::global(std::locale("C"));
1106         // This enables printing all characters in bitmap font
1107         setlocale(LC_CTYPE, "en_US");
1108
1109         /*
1110                 Parse command line
1111         */
1112         
1113         // List all allowed options
1114         core::map<std::string, ValueSpec> allowed_options;
1115         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
1116         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
1117                         "Run server directly"));
1118         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
1119                         "Load configuration from specified file"));
1120         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
1121         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
1122         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
1123         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
1124         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
1125         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
1126 #ifdef _WIN32
1127         allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
1128 #endif
1129         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
1130         allowed_options.insert("info-on-stderr", ValueSpec(VALUETYPE_FLAG));
1131
1132         Settings cmd_args;
1133         
1134         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
1135
1136         if(ret == false || cmd_args.getFlag("help"))
1137         {
1138                 dstream<<"Allowed options:"<<std::endl;
1139                 for(core::map<std::string, ValueSpec>::Iterator
1140                                 i = allowed_options.getIterator();
1141                                 i.atEnd() == false; i++)
1142                 {
1143                         dstream<<"  --"<<i.getNode()->getKey();
1144                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)
1145                         {
1146                         }
1147                         else
1148                         {
1149                                 dstream<<" <value>";
1150                         }
1151                         dstream<<std::endl;
1152
1153                         if(i.getNode()->getValue().help != NULL)
1154                         {
1155                                 dstream<<"      "<<i.getNode()->getValue().help
1156                                                 <<std::endl;
1157                         }
1158                 }
1159
1160                 return cmd_args.getFlag("help") ? 0 : 1;
1161         }
1162         
1163         /*
1164                 Low-level initialization
1165         */
1166
1167         bool disable_stderr = false;
1168 #ifdef _WIN32
1169         if(cmd_args.getFlag("dstream-on-stderr") == false)
1170                 disable_stderr = true;
1171 #endif
1172         
1173         if(cmd_args.getFlag("info-on-stderr"))
1174                 log_add_output(&main_stderr_log_out, LMT_INFO);
1175
1176         porting::signal_handler_init();
1177         bool &kill = *porting::signal_handler_killstatus();
1178         
1179         // Initialize porting::path_data and porting::path_userdata
1180         porting::initializePaths();
1181
1182         // Create user data directory
1183         fs::CreateDir(porting::path_userdata);
1184
1185         init_gettext((porting::path_data+DIR_DELIM+".."+DIR_DELIM+"locale").c_str());
1186         
1187         // Initialize debug streams
1188 #ifdef RUN_IN_PLACE
1189         std::string debugfile = DEBUGFILE;
1190 #else
1191         std::string debugfile = porting::path_userdata+DIR_DELIM+DEBUGFILE;
1192 #endif
1193         debugstreams_init(disable_stderr, debugfile.c_str());
1194         // Initialize debug stacks
1195         debug_stacks_init();
1196
1197         DSTACK(__FUNCTION_NAME);
1198
1199         // Init material properties table
1200         //initializeMaterialProperties();
1201
1202         // Debug handler
1203         BEGIN_DEBUG_EXCEPTION_HANDLER
1204
1205         // Print startup message
1206         actionstream<<PROJECT_NAME<<
1207                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
1208                         <<", "<<BUILD_INFO
1209                         <<std::endl;
1210         
1211         /*
1212                 Basic initialization
1213         */
1214
1215         // Initialize default settings
1216         set_default_settings(g_settings);
1217         
1218         // Initialize sockets
1219         sockets_init();
1220         atexit(sockets_cleanup);
1221         
1222         /*
1223                 Read config file
1224         */
1225         
1226         // Path of configuration file in use
1227         std::string configpath = "";
1228         
1229         if(cmd_args.exists("config"))
1230         {
1231                 bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
1232                 if(r == false)
1233                 {
1234                         errorstream<<"Could not read configuration from \""
1235                                         <<cmd_args.get("config")<<"\""<<std::endl;
1236                         return 1;
1237                 }
1238                 configpath = cmd_args.get("config");
1239         }
1240         else
1241         {
1242                 core::array<std::string> filenames;
1243                 filenames.push_back(porting::path_userdata +
1244                                 DIR_DELIM + "minetest.conf");
1245 #ifdef RUN_IN_PLACE
1246                 filenames.push_back(porting::path_userdata +
1247                                 DIR_DELIM + ".." + DIR_DELIM + "minetest.conf");
1248 #endif
1249
1250                 for(u32 i=0; i<filenames.size(); i++)
1251                 {
1252                         bool r = g_settings->readConfigFile(filenames[i].c_str());
1253                         if(r)
1254                         {
1255                                 configpath = filenames[i];
1256                                 break;
1257                         }
1258                 }
1259                 
1260                 // If no path found, use the first one (menu creates the file)
1261                 if(configpath == "")
1262                         configpath = filenames[0];
1263         }
1264
1265         // Initialize random seed
1266         srand(time(0));
1267         mysrand(time(0));
1268
1269         /*
1270                 Pre-initialize some stuff with a dummy irrlicht wrapper.
1271
1272                 These are needed for unit tests at least.
1273         */
1274         
1275         // Initialize content feature table without textures
1276         init_contentfeatures(NULL);
1277         // Initialize mapnode content without textures
1278         content_mapnode_init(NULL);
1279         // Must be called before texturesource is created
1280         // (for texture atlas making)
1281         init_mineral();
1282
1283         /*
1284                 Run unit tests
1285         */
1286
1287         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1288                         || cmd_args.getFlag("enable-unittests") == true)
1289         {
1290                 run_tests();
1291         }
1292         
1293         /*for(s16 y=-100; y<100; y++)
1294         for(s16 x=-100; x<100; x++)
1295         {
1296                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
1297         }
1298         return 0;*/
1299         
1300         /*
1301                 Game parameters
1302         */
1303
1304         // Port
1305         u16 port = 30000;
1306         if(cmd_args.exists("port"))
1307                 port = cmd_args.getU16("port");
1308         else if(g_settings->exists("port"))
1309                 port = g_settings->getU16("port");
1310         if(port == 0)
1311                 port = 30000;
1312         
1313         // Map directory
1314         std::string map_dir = porting::path_userdata+DIR_DELIM+"world";
1315         if(cmd_args.exists("map-dir"))
1316                 map_dir = cmd_args.get("map-dir");
1317         else if(g_settings->exists("map-dir"))
1318                 map_dir = g_settings->get("map-dir");
1319         
1320         // Run dedicated server if asked to
1321         if(cmd_args.getFlag("server"))
1322         {
1323                 DSTACK("Dedicated server branch");
1324
1325                 // Create time getter
1326                 g_timegetter = new SimpleTimeGetter();
1327                 
1328                 // Create server
1329                 Server server(map_dir.c_str(), configpath);
1330                 server.start(port);
1331                 
1332                 // Run server
1333                 dedicated_server_loop(server, kill);
1334
1335                 return 0;
1336         }
1337
1338
1339         /*
1340                 More parameters
1341         */
1342         
1343         // Address to connect to
1344         std::string address = "";
1345         
1346         if(cmd_args.exists("address"))
1347         {
1348                 address = cmd_args.get("address");
1349         }
1350         else
1351         {
1352                 address = g_settings->get("address");
1353         }
1354         
1355         std::string playername = g_settings->get("name");
1356
1357         /*
1358                 Device initialization
1359         */
1360
1361         // Resolution selection
1362         
1363         bool fullscreen = false;
1364         u16 screenW = g_settings->getU16("screenW");
1365         u16 screenH = g_settings->getU16("screenH");
1366
1367         // Determine driver
1368
1369         video::E_DRIVER_TYPE driverType;
1370         
1371         std::string driverstring = g_settings->get("video_driver");
1372
1373         if(driverstring == "null")
1374                 driverType = video::EDT_NULL;
1375         else if(driverstring == "software")
1376                 driverType = video::EDT_SOFTWARE;
1377         else if(driverstring == "burningsvideo")
1378                 driverType = video::EDT_BURNINGSVIDEO;
1379         else if(driverstring == "direct3d8")
1380                 driverType = video::EDT_DIRECT3D8;
1381         else if(driverstring == "direct3d9")
1382                 driverType = video::EDT_DIRECT3D9;
1383         else if(driverstring == "opengl")
1384                 driverType = video::EDT_OPENGL;
1385         else
1386         {
1387                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1388                                 "to opengl"<<std::endl;
1389                 driverType = video::EDT_OPENGL;
1390         }
1391
1392         /*
1393                 Create device and exit if creation failed
1394         */
1395
1396         MyEventReceiver receiver;
1397
1398         IrrlichtDevice *device;
1399         device = createDevice(driverType,
1400                         core::dimension2d<u32>(screenW, screenH),
1401                         16, fullscreen, false, false, &receiver);
1402
1403         if (device == 0)
1404                 return 1; // could not create selected driver.
1405         
1406         /*
1407                 Continue initialization
1408         */
1409
1410         video::IVideoDriver* driver = device->getVideoDriver();
1411
1412         // Disable mipmaps (because some of them look ugly)
1413         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1414
1415         /*
1416                 This changes the minimum allowed number of vertices in a VBO.
1417                 Default is 500.
1418         */
1419         //driver->setMinHardwareBufferVertexCount(50);
1420
1421         // Set the window caption
1422         device->setWindowCaption(L"Minetest [Main Menu]");
1423         
1424         // Create time getter
1425         g_timegetter = new IrrlichtTimeGetter(device);
1426         
1427         // Create game callback for menus
1428         g_gamecallback = new MainGameCallback(device);
1429         
1430         /*
1431                 Speed tests (done after irrlicht is loaded to get timer)
1432         */
1433         if(cmd_args.getFlag("speedtests"))
1434         {
1435                 dstream<<"Running speed tests"<<std::endl;
1436                 SpeedTests();
1437                 return 0;
1438         }
1439         
1440         device->setResizable(true);
1441
1442         bool random_input = g_settings->getBool("random_input")
1443                         || cmd_args.getFlag("random-input");
1444         InputHandler *input = NULL;
1445         if(random_input)
1446                 input = new RandomInputHandler();
1447         else
1448                 input = new RealInputHandler(device, &receiver);
1449         
1450         scene::ISceneManager* smgr = device->getSceneManager();
1451
1452         guienv = device->getGUIEnvironment();
1453         gui::IGUISkin* skin = guienv->getSkin();
1454         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1455         if(font)
1456                 skin->setFont(font);
1457         else
1458                 errorstream<<"WARNING: Font file was not found."
1459                                 " Using default font."<<std::endl;
1460         // If font was not found, this will get us one
1461         font = skin->getFont();
1462         assert(font);
1463         
1464         u32 text_height = font->getDimension(L"Hello, world!").Height;
1465         infostream<<"text_height="<<text_height<<std::endl;
1466
1467         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1468         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1469         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1470         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1471         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1472         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1473         
1474         /*
1475                 GUI stuff
1476         */
1477
1478         /*
1479                 If an error occurs, this is set to something and the
1480                 menu-game loop is restarted. It is then displayed before
1481                 the menu.
1482         */
1483         std::wstring error_message = L"";
1484
1485         // The password entered during the menu screen,
1486         std::string password;
1487
1488         /*
1489                 Menu-game loop
1490         */
1491         while(device->run() && kill == false)
1492         {
1493
1494                 // This is used for catching disconnects
1495                 try
1496                 {
1497
1498                         /*
1499                                 Clear everything from the GUIEnvironment
1500                         */
1501                         guienv->clear();
1502                         
1503                         /*
1504                                 We need some kind of a root node to be able to add
1505                                 custom gui elements directly on the screen.
1506                                 Otherwise they won't be automatically drawn.
1507                         */
1508                         guiroot = guienv->addStaticText(L"",
1509                                         core::rect<s32>(0, 0, 10000, 10000));
1510                         
1511                         /*
1512                                 Out-of-game menu loop.
1513
1514                                 Loop quits when menu returns proper parameters.
1515                         */
1516                         while(kill == false)
1517                         {
1518                                 // Cursor can be non-visible when coming from the game
1519                                 device->getCursorControl()->setVisible(true);
1520                                 // Some stuff are left to scene manager when coming from the game
1521                                 // (map at least?)
1522                                 smgr->clear();
1523                                 // Reset or hide the debug gui texts
1524                                 /*guitext->setText(L"Minetest-c55");
1525                                 guitext2->setVisible(false);
1526                                 guitext_info->setVisible(false);
1527                                 guitext_chat->setVisible(false);*/
1528                                 
1529                                 // Initialize menu data
1530                                 MainMenuData menudata;
1531                                 menudata.address = narrow_to_wide(address);
1532                                 menudata.name = narrow_to_wide(playername);
1533                                 menudata.port = narrow_to_wide(itos(port));
1534                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1535                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1536                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1537                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1538                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1539                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1540
1541                                 GUIMainMenu *menu =
1542                                                 new GUIMainMenu(guienv, guiroot, -1, 
1543                                                         &g_menumgr, &menudata, g_gamecallback);
1544                                 menu->allowFocusRemoval(true);
1545
1546                                 if(error_message != L"")
1547                                 {
1548                                         errorstream<<"error_message = "
1549                                                         <<wide_to_narrow(error_message)<<std::endl;
1550
1551                                         GUIMessageMenu *menu2 =
1552                                                         new GUIMessageMenu(guienv, guiroot, -1, 
1553                                                                 &g_menumgr, error_message.c_str());
1554                                         menu2->drop();
1555                                         error_message = L"";
1556                                 }
1557
1558                                 video::IVideoDriver* driver = device->getVideoDriver();
1559                                 
1560                                 infostream<<"Created main menu"<<std::endl;
1561
1562                                 while(device->run() && kill == false)
1563                                 {
1564                                         if(menu->getStatus() == true)
1565                                                 break;
1566
1567                                         //driver->beginScene(true, true, video::SColor(255,0,0,0));
1568                                         driver->beginScene(true, true, video::SColor(255,128,128,128));
1569
1570                                         drawMenuBackground(driver);
1571
1572                                         guienv->drawAll();
1573                                         
1574                                         driver->endScene();
1575                                         
1576                                         // On some computers framerate doesn't seem to be
1577                                         // automatically limited
1578                                         sleep_ms(25);
1579                                 }
1580                                 
1581                                 // Break out of menu-game loop to shut down cleanly
1582                                 if(device->run() == false || kill == true)
1583                                         break;
1584                                 
1585                                 infostream<<"Dropping main menu"<<std::endl;
1586
1587                                 menu->drop();
1588                                 
1589                                 // Delete map if requested
1590                                 if(menudata.delete_map)
1591                                 {
1592                                         bool r = fs::RecursiveDeleteContent(map_dir);
1593                                         if(r == false)
1594                                                 error_message = L"Delete failed";
1595                                         continue;
1596                                 }
1597
1598                                 playername = wide_to_narrow(menudata.name);
1599
1600                                 password = translatePassword(playername, menudata.password);
1601
1602                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1603
1604                                 address = wide_to_narrow(menudata.address);
1605                                 int newport = stoi(wide_to_narrow(menudata.port));
1606                                 if(newport != 0)
1607                                         port = newport;
1608                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1609                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1610                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1611                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1612                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1613                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1614                                 
1615                                 // NOTE: These are now checked server side; no need to do it
1616                                 //       here, so let's not do it here.
1617                                 /*// Check for valid parameters, restart menu if invalid.
1618                                 if(playername == "")
1619                                 {
1620                                         error_message = L"Name required.";
1621                                         continue;
1622                                 }
1623                                 // Check that name has only valid chars
1624                                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1625                                 {
1626                                         error_message = L"Characters allowed: "
1627                                                         +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
1628                                         continue;
1629                                 }*/
1630
1631                                 // Save settings
1632                                 g_settings->set("name", playername);
1633                                 g_settings->set("address", address);
1634                                 g_settings->set("port", itos(port));
1635                                 // Update configuration file
1636                                 if(configpath != "")
1637                                         g_settings->updateConfigFile(configpath.c_str());
1638                         
1639                                 // Continue to game
1640                                 break;
1641                         }
1642                         
1643                         // Break out of menu-game loop to shut down cleanly
1644                         if(device->run() == false)
1645                                 break;
1646                         
1647                         /*
1648                                 Run game
1649                         */
1650                         the_game(
1651                                 kill,
1652                                 random_input,
1653                                 input,
1654                                 device,
1655                                 font,
1656                                 map_dir,
1657                                 playername,
1658                                 password,
1659                                 address,
1660                                 port,
1661                                 error_message,
1662                                 configpath
1663                         );
1664
1665                 } //try
1666                 catch(con::PeerNotFoundException &e)
1667                 {
1668                         errorstream<<"Connection error (timed out?)"<<std::endl;
1669                         error_message = L"Connection error (timed out?)";
1670                 }
1671                 catch(SocketException &e)
1672                 {
1673                         errorstream<<"Socket error (port already in use?)"<<std::endl;
1674                         error_message = L"Socket error (port already in use?)";
1675                 }
1676 #ifdef NDEBUG
1677                 catch(std::exception &e)
1678                 {
1679                         std::string narrow_message = "Some exception, what()=\"";
1680                         narrow_message += e.what();
1681                         narrow_message += "\"";
1682                         errorstream<<narrow_message<<std::endl;
1683                         error_message = narrow_to_wide(narrow_message);
1684                 }
1685 #endif
1686
1687         } // Menu-game loop
1688         
1689         delete input;
1690
1691         /*
1692                 In the end, delete the Irrlicht device.
1693         */
1694         device->drop();
1695         
1696         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1697         
1698         debugstreams_deinit();
1699         
1700         return 0;
1701 }
1702
1703 //END
1704