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