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