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