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