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