ToolDefManager serialization
[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 "nodedef.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         // Must be called before texturesource is created
1276         // (for texture atlas making)
1277         init_mineral();
1278
1279         /*
1280                 Run unit tests
1281         */
1282
1283         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
1284                         || cmd_args.getFlag("enable-unittests") == true)
1285         {
1286                 run_tests();
1287         }
1288         
1289         /*for(s16 y=-100; y<100; y++)
1290         for(s16 x=-100; x<100; x++)
1291         {
1292                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
1293         }
1294         return 0;*/
1295         
1296         /*
1297                 Game parameters
1298         */
1299
1300         // Port
1301         u16 port = 30000;
1302         if(cmd_args.exists("port"))
1303                 port = cmd_args.getU16("port");
1304         else if(g_settings->exists("port"))
1305                 port = g_settings->getU16("port");
1306         if(port == 0)
1307                 port = 30000;
1308         
1309         // Map directory
1310         std::string map_dir = porting::path_userdata+DIR_DELIM+"world";
1311         if(cmd_args.exists("map-dir"))
1312                 map_dir = cmd_args.get("map-dir");
1313         else if(g_settings->exists("map-dir"))
1314                 map_dir = g_settings->get("map-dir");
1315         
1316         // Run dedicated server if asked to
1317         if(cmd_args.getFlag("server"))
1318         {
1319                 DSTACK("Dedicated server branch");
1320
1321                 // Create time getter
1322                 g_timegetter = new SimpleTimeGetter();
1323                 
1324                 // Create server
1325                 Server server(map_dir.c_str(), configpath);
1326                 server.start(port);
1327                 
1328                 // Run server
1329                 dedicated_server_loop(server, kill);
1330
1331                 return 0;
1332         }
1333
1334
1335         /*
1336                 More parameters
1337         */
1338         
1339         // Address to connect to
1340         std::string address = "";
1341         
1342         if(cmd_args.exists("address"))
1343         {
1344                 address = cmd_args.get("address");
1345         }
1346         else
1347         {
1348                 address = g_settings->get("address");
1349         }
1350         
1351         std::string playername = g_settings->get("name");
1352
1353         /*
1354                 Device initialization
1355         */
1356
1357         // Resolution selection
1358         
1359         bool fullscreen = false;
1360         u16 screenW = g_settings->getU16("screenW");
1361         u16 screenH = g_settings->getU16("screenH");
1362
1363         // Determine driver
1364
1365         video::E_DRIVER_TYPE driverType;
1366         
1367         std::string driverstring = g_settings->get("video_driver");
1368
1369         if(driverstring == "null")
1370                 driverType = video::EDT_NULL;
1371         else if(driverstring == "software")
1372                 driverType = video::EDT_SOFTWARE;
1373         else if(driverstring == "burningsvideo")
1374                 driverType = video::EDT_BURNINGSVIDEO;
1375         else if(driverstring == "direct3d8")
1376                 driverType = video::EDT_DIRECT3D8;
1377         else if(driverstring == "direct3d9")
1378                 driverType = video::EDT_DIRECT3D9;
1379         else if(driverstring == "opengl")
1380                 driverType = video::EDT_OPENGL;
1381         else
1382         {
1383                 errorstream<<"WARNING: Invalid video_driver specified; defaulting "
1384                                 "to opengl"<<std::endl;
1385                 driverType = video::EDT_OPENGL;
1386         }
1387
1388         /*
1389                 Create device and exit if creation failed
1390         */
1391
1392         MyEventReceiver receiver;
1393
1394         IrrlichtDevice *device;
1395         device = createDevice(driverType,
1396                         core::dimension2d<u32>(screenW, screenH),
1397                         16, fullscreen, false, false, &receiver);
1398
1399         if (device == 0)
1400                 return 1; // could not create selected driver.
1401         
1402         /*
1403                 Continue initialization
1404         */
1405
1406         video::IVideoDriver* driver = device->getVideoDriver();
1407
1408         // Disable mipmaps (because some of them look ugly)
1409         driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1410
1411         /*
1412                 This changes the minimum allowed number of vertices in a VBO.
1413                 Default is 500.
1414         */
1415         //driver->setMinHardwareBufferVertexCount(50);
1416
1417         // Set the window caption
1418         device->setWindowCaption(L"Minetest [Main Menu]");
1419         
1420         // Create time getter
1421         g_timegetter = new IrrlichtTimeGetter(device);
1422         
1423         // Create game callback for menus
1424         g_gamecallback = new MainGameCallback(device);
1425         
1426         /*
1427                 Speed tests (done after irrlicht is loaded to get timer)
1428         */
1429         if(cmd_args.getFlag("speedtests"))
1430         {
1431                 dstream<<"Running speed tests"<<std::endl;
1432                 SpeedTests();
1433                 return 0;
1434         }
1435         
1436         device->setResizable(true);
1437
1438         bool random_input = g_settings->getBool("random_input")
1439                         || cmd_args.getFlag("random-input");
1440         InputHandler *input = NULL;
1441         if(random_input)
1442                 input = new RandomInputHandler();
1443         else
1444                 input = new RealInputHandler(device, &receiver);
1445         
1446         scene::ISceneManager* smgr = device->getSceneManager();
1447
1448         guienv = device->getGUIEnvironment();
1449         gui::IGUISkin* skin = guienv->getSkin();
1450         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
1451         if(font)
1452                 skin->setFont(font);
1453         else
1454                 errorstream<<"WARNING: Font file was not found."
1455                                 " Using default font."<<std::endl;
1456         // If font was not found, this will get us one
1457         font = skin->getFont();
1458         assert(font);
1459         
1460         u32 text_height = font->getDimension(L"Hello, world!").Height;
1461         infostream<<"text_height="<<text_height<<std::endl;
1462
1463         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
1464         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
1465         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
1466         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
1467         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
1468         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
1469         
1470         /*
1471                 GUI stuff
1472         */
1473
1474         /*
1475                 If an error occurs, this is set to something and the
1476                 menu-game loop is restarted. It is then displayed before
1477                 the menu.
1478         */
1479         std::wstring error_message = L"";
1480
1481         // The password entered during the menu screen,
1482         std::string password;
1483
1484         /*
1485                 Menu-game loop
1486         */
1487         while(device->run() && kill == false)
1488         {
1489
1490                 // This is used for catching disconnects
1491                 try
1492                 {
1493
1494                         /*
1495                                 Clear everything from the GUIEnvironment
1496                         */
1497                         guienv->clear();
1498                         
1499                         /*
1500                                 We need some kind of a root node to be able to add
1501                                 custom gui elements directly on the screen.
1502                                 Otherwise they won't be automatically drawn.
1503                         */
1504                         guiroot = guienv->addStaticText(L"",
1505                                         core::rect<s32>(0, 0, 10000, 10000));
1506                         
1507                         /*
1508                                 Out-of-game menu loop.
1509
1510                                 Loop quits when menu returns proper parameters.
1511                         */
1512                         while(kill == false)
1513                         {
1514                                 // Cursor can be non-visible when coming from the game
1515                                 device->getCursorControl()->setVisible(true);
1516                                 // Some stuff are left to scene manager when coming from the game
1517                                 // (map at least?)
1518                                 smgr->clear();
1519                                 // Reset or hide the debug gui texts
1520                                 /*guitext->setText(L"Minetest-c55");
1521                                 guitext2->setVisible(false);
1522                                 guitext_info->setVisible(false);
1523                                 guitext_chat->setVisible(false);*/
1524                                 
1525                                 // Initialize menu data
1526                                 MainMenuData menudata;
1527                                 menudata.address = narrow_to_wide(address);
1528                                 menudata.name = narrow_to_wide(playername);
1529                                 menudata.port = narrow_to_wide(itos(port));
1530                                 menudata.fancy_trees = g_settings->getBool("new_style_leaves");
1531                                 menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
1532                                 menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
1533                                 menudata.opaque_water = g_settings->getBool("opaque_water");
1534                                 menudata.creative_mode = g_settings->getBool("creative_mode");
1535                                 menudata.enable_damage = g_settings->getBool("enable_damage");
1536
1537                                 GUIMainMenu *menu =
1538                                                 new GUIMainMenu(guienv, guiroot, -1, 
1539                                                         &g_menumgr, &menudata, g_gamecallback);
1540                                 menu->allowFocusRemoval(true);
1541
1542                                 if(error_message != L"")
1543                                 {
1544                                         errorstream<<"error_message = "
1545                                                         <<wide_to_narrow(error_message)<<std::endl;
1546
1547                                         GUIMessageMenu *menu2 =
1548                                                         new GUIMessageMenu(guienv, guiroot, -1, 
1549                                                                 &g_menumgr, error_message.c_str());
1550                                         menu2->drop();
1551                                         error_message = L"";
1552                                 }
1553
1554                                 video::IVideoDriver* driver = device->getVideoDriver();
1555                                 
1556                                 infostream<<"Created main menu"<<std::endl;
1557
1558                                 while(device->run() && kill == false)
1559                                 {
1560                                         if(menu->getStatus() == true)
1561                                                 break;
1562
1563                                         //driver->beginScene(true, true, video::SColor(255,0,0,0));
1564                                         driver->beginScene(true, true, video::SColor(255,128,128,128));
1565
1566                                         drawMenuBackground(driver);
1567
1568                                         guienv->drawAll();
1569                                         
1570                                         driver->endScene();
1571                                         
1572                                         // On some computers framerate doesn't seem to be
1573                                         // automatically limited
1574                                         sleep_ms(25);
1575                                 }
1576                                 
1577                                 // Break out of menu-game loop to shut down cleanly
1578                                 if(device->run() == false || kill == true)
1579                                         break;
1580                                 
1581                                 infostream<<"Dropping main menu"<<std::endl;
1582
1583                                 menu->drop();
1584                                 
1585                                 // Delete map if requested
1586                                 if(menudata.delete_map)
1587                                 {
1588                                         bool r = fs::RecursiveDeleteContent(map_dir);
1589                                         if(r == false)
1590                                                 error_message = L"Delete failed";
1591                                         continue;
1592                                 }
1593
1594                                 playername = wide_to_narrow(menudata.name);
1595
1596                                 password = translatePassword(playername, menudata.password);
1597
1598                                 //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
1599
1600                                 address = wide_to_narrow(menudata.address);
1601                                 int newport = stoi(wide_to_narrow(menudata.port));
1602                                 if(newport != 0)
1603                                         port = newport;
1604                                 g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
1605                                 g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
1606                                 g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
1607                                 g_settings->set("opaque_water", itos(menudata.opaque_water));
1608                                 g_settings->set("creative_mode", itos(menudata.creative_mode));
1609                                 g_settings->set("enable_damage", itos(menudata.enable_damage));
1610                                 
1611                                 // NOTE: These are now checked server side; no need to do it
1612                                 //       here, so let's not do it here.
1613                                 /*// Check for valid parameters, restart menu if invalid.
1614                                 if(playername == "")
1615                                 {
1616                                         error_message = L"Name required.";
1617                                         continue;
1618                                 }
1619                                 // Check that name has only valid chars
1620                                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1621                                 {
1622                                         error_message = L"Characters allowed: "
1623                                                         +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
1624                                         continue;
1625                                 }*/
1626
1627                                 // Save settings
1628                                 g_settings->set("name", playername);
1629                                 g_settings->set("address", address);
1630                                 g_settings->set("port", itos(port));
1631                                 // Update configuration file
1632                                 if(configpath != "")
1633                                         g_settings->updateConfigFile(configpath.c_str());
1634                         
1635                                 // Continue to game
1636                                 break;
1637                         }
1638                         
1639                         // Break out of menu-game loop to shut down cleanly
1640                         if(device->run() == false)
1641                                 break;
1642                         
1643                         /*
1644                                 Run game
1645                         */
1646                         the_game(
1647                                 kill,
1648                                 random_input,
1649                                 input,
1650                                 device,
1651                                 font,
1652                                 map_dir,
1653                                 playername,
1654                                 password,
1655                                 address,
1656                                 port,
1657                                 error_message,
1658                                 configpath
1659                         );
1660
1661                 } //try
1662                 catch(con::PeerNotFoundException &e)
1663                 {
1664                         errorstream<<"Connection error (timed out?)"<<std::endl;
1665                         error_message = L"Connection error (timed out?)";
1666                 }
1667                 catch(SocketException &e)
1668                 {
1669                         errorstream<<"Socket error (port already in use?)"<<std::endl;
1670                         error_message = L"Socket error (port already in use?)";
1671                 }
1672 #ifdef NDEBUG
1673                 catch(std::exception &e)
1674                 {
1675                         std::string narrow_message = "Some exception, what()=\"";
1676                         narrow_message += e.what();
1677                         narrow_message += "\"";
1678                         errorstream<<narrow_message<<std::endl;
1679                         error_message = narrow_to_wide(narrow_message);
1680                 }
1681 #endif
1682
1683         } // Menu-game loop
1684         
1685         delete input;
1686
1687         /*
1688                 In the end, delete the Irrlicht device.
1689         */
1690         device->drop();
1691         
1692         END_DEBUG_EXCEPTION_HANDLER(errorstream)
1693         
1694         debugstreams_deinit();
1695         
1696         return 0;
1697 }
1698
1699 //END
1700