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