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