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