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