]> git.lizzy.rs Git - minetest.git/blob - src/main.cpp
3d342e5960646e0dc8cc2196ee0c76eedd68325c
[minetest.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 TODO: Add a long step function to objects that is called with the time\r
264       difference when block activates\r
265 \r
266 Map:\r
267 ----\r
268 \r
269 TODO: Mineral and ground material properties\r
270       - This way mineral ground toughness can be calculated with just\r
271             some formula, as well as tool strengths\r
272 \r
273 TODO: Flowing water to actually contain flow direction information\r
274       - There is a space for this - it just has to be implemented.\r
275 \r
276 SUGG: Erosion simulation at map generation time\r
277         - Simulate water flows, which would carve out dirt fast and\r
278           then turn stone into gravel and sand and relocate it.\r
279         - How about relocating minerals, too? Coal and gold in\r
280           downstream sand and gravel would be kind of cool\r
281           - This would need a better way of handling minerals, mainly\r
282                 to have mineral content as a separate field. the first\r
283                 parameter field is free for this.\r
284         - Simulate rock falling from cliffs when water has removed\r
285           enough solid rock from the bottom\r
286 \r
287 SUGG: Try out the notch way of generating maps, that is, make bunches\r
288       of low-res 3d noise and interpolate linearly.\r
289 \r
290 Mapgen v2:\r
291 * Possibly add some kind of erosion and other stuff\r
292 * Better water generation (spread it to underwater caverns but don't\r
293   fill dungeons that don't touch big water masses)\r
294 * When generating a chunk and the neighboring chunk doesn't have mud\r
295   and stuff yet and the ground is fairly flat, the mud will flow to\r
296   the other chunk making nasty straight walls when the other chunk\r
297   is generated. Fix it. Maybe just a special case if the ground is\r
298   flat?\r
299 \r
300 Misc. stuff:\r
301 ------------\r
302 * Move digging property stuff from material.{h,cpp} to mapnode.cpp\r
303   - ...Or maybe move content_features to material.{h,cpp}?\r
304 \r
305 Making it more portable:\r
306 ------------------------\r
307  \r
308 Stuff to do before release:\r
309 ---------------------------\r
310 \r
311 Stuff to do after release:\r
312 ---------------------------\r
313 - Make sure server handles removing grass when a block is placed (etc)\r
314     - The client should not do it by itself\r
315 - Block cube placement around player's head\r
316 - Protocol version field\r
317 - Consider getting some textures from cisoun's texture pack\r
318         - Ask from Cisoun\r
319 - Make sure the fence implementation and data format is good\r
320         - Think about using same bits for material for fences and doors, for\r
321         example\r
322 - Finish the ActiveBlockModifier stuff and use it for something\r
323 - Move mineral to param2, increment map serialization version, add conversion\r
324 \r
325 ======================================================================\r
326 \r
327 */\r
328 \r
329 #ifdef NDEBUG\r
330         #ifdef _WIN32\r
331                 #pragma message ("Disabling unit tests")\r
332         #else\r
333                 #warning "Disabling unit tests"\r
334         #endif\r
335         // Disable unit tests\r
336         #define ENABLE_TESTS 0\r
337 #else\r
338         // Enable unit tests\r
339         #define ENABLE_TESTS 1\r
340 #endif\r
341 \r
342 #ifdef _MSC_VER\r
343         #pragma comment(lib, "Irrlicht.lib")\r
344         //#pragma comment(lib, "jthread.lib")\r
345         #pragma comment(lib, "zlibwapi.lib")\r
346         #pragma comment(lib, "Shell32.lib")\r
347         // This would get rid of the console window\r
348         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")\r
349 #endif\r
350 \r
351 #include <iostream>\r
352 #include <fstream>\r
353 //#include <jmutexautolock.h>\r
354 #include <locale.h>\r
355 #include "main.h"\r
356 #include "common_irrlicht.h"\r
357 #include "debug.h"\r
358 //#include "map.h"\r
359 //#include "player.h"\r
360 #include "test.h"\r
361 #include "server.h"\r
362 //#include "client.h"\r
363 #include "constants.h"\r
364 #include "porting.h"\r
365 #include "gettime.h"\r
366 #include "guiMessageMenu.h"\r
367 #include "filesys.h"\r
368 #include "config.h"\r
369 #include "guiMainMenu.h"\r
370 #include "mineral.h"\r
371 //#include "noise.h"\r
372 //#include "tile.h"\r
373 #include "materials.h"\r
374 #include "game.h"\r
375 #include "keycode.h"\r
376 \r
377 // This makes textures\r
378 ITextureSource *g_texturesource = NULL;\r
379 \r
380 /*\r
381         Settings.\r
382         These are loaded from the config file.\r
383 */\r
384 \r
385 Settings g_settings;\r
386 // This is located in defaultsettings.cpp\r
387 extern void set_default_settings();\r
388 \r
389 /*\r
390         Random stuff\r
391 */\r
392 \r
393 /*\r
394         GUI Stuff\r
395 */\r
396 \r
397 gui::IGUIEnvironment* guienv = NULL;\r
398 gui::IGUIStaticText *guiroot = NULL;\r
399 \r
400 MainMenuManager g_menumgr;\r
401 \r
402 bool noMenuActive()\r
403 {\r
404         return (g_menumgr.menuCount() == 0);\r
405 }\r
406 \r
407 // Passed to menus to allow disconnecting and exiting\r
408 \r
409 MainGameCallback *g_gamecallback = NULL;\r
410 \r
411 /*\r
412         Debug streams\r
413 */\r
414 \r
415 // Connection\r
416 std::ostream *dout_con_ptr = &dummyout;\r
417 std::ostream *derr_con_ptr = &dstream_no_stderr;\r
418 //std::ostream *dout_con_ptr = &dstream_no_stderr;\r
419 //std::ostream *derr_con_ptr = &dstream_no_stderr;\r
420 //std::ostream *dout_con_ptr = &dstream;\r
421 //std::ostream *derr_con_ptr = &dstream;\r
422 \r
423 // Server\r
424 std::ostream *dout_server_ptr = &dstream;\r
425 std::ostream *derr_server_ptr = &dstream;\r
426 \r
427 // Client\r
428 std::ostream *dout_client_ptr = &dstream;\r
429 std::ostream *derr_client_ptr = &dstream;\r
430 \r
431 /*\r
432         gettime.h implementation\r
433 */\r
434 \r
435 // A small helper class\r
436 class TimeGetter\r
437 {\r
438 public:\r
439         TimeGetter(IrrlichtDevice *device):\r
440                 m_device(device)\r
441         {}\r
442         u32 getTime()\r
443         {\r
444                 if(m_device == NULL)\r
445                         return 0;\r
446                 return m_device->getTimer()->getRealTime();\r
447         }\r
448 private:\r
449         IrrlichtDevice *m_device;\r
450 };\r
451 \r
452 // A pointer to a global instance of the time getter\r
453 TimeGetter *g_timegetter = NULL;\r
454 \r
455 u32 getTimeMs()\r
456 {\r
457         if(g_timegetter == NULL)\r
458                 return 0;\r
459         return g_timegetter->getTime();\r
460 }\r
461 \r
462 /*\r
463         Event handler for Irrlicht\r
464 \r
465         NOTE: Everything possible should be moved out from here,\r
466               probably to InputHandler and the_game\r
467 */\r
468 \r
469 class MyEventReceiver : public IEventReceiver\r
470 {\r
471 public:\r
472         // This is the one method that we have to implement\r
473         virtual bool OnEvent(const SEvent& event)\r
474         {\r
475                 /*\r
476                         React to nothing here if a menu is active\r
477                 */\r
478                 if(noMenuActive() == false)\r
479                 {\r
480                         return false;\r
481                 }\r
482 \r
483                 // Remember whether each key is down or up\r
484                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
485                 {\r
486                         keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;\r
487 \r
488                         if(event.KeyInput.PressedDown)\r
489                                 keyWasDown[event.KeyInput.Key] = true;\r
490                 }\r
491 \r
492                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
493                 {\r
494                         if(noMenuActive() == false)\r
495                         {\r
496                                 left_active = false;\r
497                                 middle_active = false;\r
498                                 right_active = false;\r
499                         }\r
500                         else\r
501                         {\r
502                                 //dstream<<"MyEventReceiver: mouse input"<<std::endl;\r
503                                 left_active = event.MouseInput.isLeftPressed();\r
504                                 middle_active = event.MouseInput.isMiddlePressed();\r
505                                 right_active = event.MouseInput.isRightPressed();\r
506 \r
507                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
508                                 {\r
509                                         leftclicked = true;\r
510                                 }\r
511                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
512                                 {\r
513                                         rightclicked = true;\r
514                                 }\r
515                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
516                                 {\r
517                                         leftreleased = true;\r
518                                 }\r
519                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
520                                 {\r
521                                         rightreleased = true;\r
522                                 }\r
523                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
524                                 {\r
525                                         mouse_wheel += event.MouseInput.Wheel;\r
526                                 }\r
527                         }\r
528                 }\r
529 \r
530                 return false;\r
531         }\r
532 \r
533         bool IsKeyDown(EKEY_CODE keyCode) const\r
534         {\r
535                 return keyIsDown[keyCode];\r
536         }\r
537         \r
538         // Checks whether a key was down and resets the state\r
539         bool WasKeyDown(EKEY_CODE keyCode)\r
540         {\r
541                 bool b = keyWasDown[keyCode];\r
542                 keyWasDown[keyCode] = false;\r
543                 return b;\r
544         }\r
545 \r
546         s32 getMouseWheel()\r
547         {\r
548                 s32 a = mouse_wheel;\r
549                 mouse_wheel = 0;\r
550                 return a;\r
551         }\r
552 \r
553         void clearInput()\r
554         {\r
555                 for(u32 i=0; i<KEY_KEY_CODES_COUNT; i++)\r
556                 {\r
557                         keyIsDown[i] = false;\r
558                         keyWasDown[i] = false;\r
559                 }\r
560                 \r
561                 leftclicked = false;\r
562                 rightclicked = false;\r
563                 leftreleased = false;\r
564                 rightreleased = false;\r
565 \r
566                 left_active = false;\r
567                 middle_active = false;\r
568                 right_active = false;\r
569 \r
570                 mouse_wheel = 0;\r
571         }\r
572 \r
573         MyEventReceiver()\r
574         {\r
575                 clearInput();\r
576         }\r
577 \r
578         bool leftclicked;\r
579         bool rightclicked;\r
580         bool leftreleased;\r
581         bool rightreleased;\r
582 \r
583         bool left_active;\r
584         bool middle_active;\r
585         bool right_active;\r
586 \r
587         s32 mouse_wheel;\r
588 \r
589 private:\r
590         IrrlichtDevice *m_device;\r
591         \r
592         // The current state of keys\r
593         bool keyIsDown[KEY_KEY_CODES_COUNT];\r
594         // Whether a key has been pressed or not\r
595         bool keyWasDown[KEY_KEY_CODES_COUNT];\r
596 };\r
597 \r
598 /*\r
599         Separated input handler\r
600 */\r
601 \r
602 class RealInputHandler : public InputHandler\r
603 {\r
604 public:\r
605         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):\r
606                 m_device(device),\r
607                 m_receiver(receiver)\r
608         {\r
609         }\r
610         virtual bool isKeyDown(EKEY_CODE keyCode)\r
611         {\r
612                 return m_receiver->IsKeyDown(keyCode);\r
613         }\r
614         virtual bool wasKeyDown(EKEY_CODE keyCode)\r
615         {\r
616                 return m_receiver->WasKeyDown(keyCode);\r
617         }\r
618         virtual v2s32 getMousePos()\r
619         {\r
620                 return m_device->getCursorControl()->getPosition();\r
621         }\r
622         virtual void setMousePos(s32 x, s32 y)\r
623         {\r
624                 m_device->getCursorControl()->setPosition(x, y);\r
625         }\r
626 \r
627         virtual bool getLeftState()\r
628         {\r
629                 return m_receiver->left_active;\r
630         }\r
631         virtual bool getRightState()\r
632         {\r
633                 return m_receiver->right_active;\r
634         }\r
635         \r
636         virtual bool getLeftClicked()\r
637         {\r
638                 return m_receiver->leftclicked;\r
639         }\r
640         virtual bool getRightClicked()\r
641         {\r
642                 return m_receiver->rightclicked;\r
643         }\r
644         virtual void resetLeftClicked()\r
645         {\r
646                 m_receiver->leftclicked = false;\r
647         }\r
648         virtual void resetRightClicked()\r
649         {\r
650                 m_receiver->rightclicked = false;\r
651         }\r
652 \r
653         virtual bool getLeftReleased()\r
654         {\r
655                 return m_receiver->leftreleased;\r
656         }\r
657         virtual bool getRightReleased()\r
658         {\r
659                 return m_receiver->rightreleased;\r
660         }\r
661         virtual void resetLeftReleased()\r
662         {\r
663                 m_receiver->leftreleased = false;\r
664         }\r
665         virtual void resetRightReleased()\r
666         {\r
667                 m_receiver->rightreleased = false;\r
668         }\r
669 \r
670         virtual s32 getMouseWheel()\r
671         {\r
672                 return m_receiver->getMouseWheel();\r
673         }\r
674 \r
675         void clear()\r
676         {\r
677                 m_receiver->clearInput();\r
678         }\r
679 private:\r
680         IrrlichtDevice *m_device;\r
681         MyEventReceiver *m_receiver;\r
682 };\r
683 \r
684 class RandomInputHandler : public InputHandler\r
685 {\r
686 public:\r
687         RandomInputHandler()\r
688         {\r
689                 leftdown = false;\r
690                 rightdown = false;\r
691                 leftclicked = false;\r
692                 rightclicked = false;\r
693                 leftreleased = false;\r
694                 rightreleased = false;\r
695                 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
696                         keydown[i] = false;\r
697         }\r
698         virtual bool isKeyDown(EKEY_CODE keyCode)\r
699         {\r
700                 return keydown[keyCode];\r
701         }\r
702         virtual bool wasKeyDown(EKEY_CODE keyCode)\r
703         {\r
704                 return false;\r
705         }\r
706         virtual v2s32 getMousePos()\r
707         {\r
708                 return mousepos;\r
709         }\r
710         virtual void setMousePos(s32 x, s32 y)\r
711         {\r
712                 mousepos = v2s32(x,y);\r
713         }\r
714 \r
715         virtual bool getLeftState()\r
716         {\r
717                 return leftdown;\r
718         }\r
719         virtual bool getRightState()\r
720         {\r
721                 return rightdown;\r
722         }\r
723 \r
724         virtual bool getLeftClicked()\r
725         {\r
726                 return leftclicked;\r
727         }\r
728         virtual bool getRightClicked()\r
729         {\r
730                 return rightclicked;\r
731         }\r
732         virtual void resetLeftClicked()\r
733         {\r
734                 leftclicked = false;\r
735         }\r
736         virtual void resetRightClicked()\r
737         {\r
738                 rightclicked = false;\r
739         }\r
740 \r
741         virtual bool getLeftReleased()\r
742         {\r
743                 return leftreleased;\r
744         }\r
745         virtual bool getRightReleased()\r
746         {\r
747                 return rightreleased;\r
748         }\r
749         virtual void resetLeftReleased()\r
750         {\r
751                 leftreleased = false;\r
752         }\r
753         virtual void resetRightReleased()\r
754         {\r
755                 rightreleased = false;\r
756         }\r
757 \r
758         virtual s32 getMouseWheel()\r
759         {\r
760                 return 0;\r
761         }\r
762 \r
763         virtual void step(float dtime)\r
764         {\r
765                 {\r
766                         static float counter1 = 0;\r
767                         counter1 -= dtime;\r
768                         if(counter1 < 0.0)\r
769                         {\r
770                                 counter1 = 0.1*Rand(1, 40);\r
771                                 keydown[getKeySetting("keymap_jump")] =\r
772                                                 !keydown[getKeySetting("keymap_jump")];\r
773                         }\r
774                 }\r
775                 {\r
776                         static float counter1 = 0;\r
777                         counter1 -= dtime;\r
778                         if(counter1 < 0.0)\r
779                         {\r
780                                 counter1 = 0.1*Rand(1, 40);\r
781                                 keydown[getKeySetting("keymap_special1")] =\r
782                                                 !keydown[getKeySetting("keymap_special1")];\r
783                         }\r
784                 }\r
785                 {\r
786                         static float counter1 = 0;\r
787                         counter1 -= dtime;\r
788                         if(counter1 < 0.0)\r
789                         {\r
790                                 counter1 = 0.1*Rand(1, 40);\r
791                                 keydown[getKeySetting("keymap_forward")] =\r
792                                                 !keydown[getKeySetting("keymap_forward")];\r
793                         }\r
794                 }\r
795                 {\r
796                         static float counter1 = 0;\r
797                         counter1 -= dtime;\r
798                         if(counter1 < 0.0)\r
799                         {\r
800                                 counter1 = 0.1*Rand(1, 40);\r
801                                 keydown[getKeySetting("keymap_left")] =\r
802                                                 !keydown[getKeySetting("keymap_left")];\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, 20);\r
811                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));\r
812                         }\r
813                 }\r
814                 {\r
815                         static float counter1 = 0;\r
816                         counter1 -= dtime;\r
817                         if(counter1 < 0.0)\r
818                         {\r
819                                 counter1 = 0.1*Rand(1, 30);\r
820                                 leftdown = !leftdown;\r
821                                 if(leftdown)\r
822                                         leftclicked = true;\r
823                                 if(!leftdown)\r
824                                         leftreleased = true;\r
825                         }\r
826                 }\r
827                 {\r
828                         static float counter1 = 0;\r
829                         counter1 -= dtime;\r
830                         if(counter1 < 0.0)\r
831                         {\r
832                                 counter1 = 0.1*Rand(1, 15);\r
833                                 rightdown = !rightdown;\r
834                                 if(rightdown)\r
835                                         rightclicked = true;\r
836                                 if(!rightdown)\r
837                                         rightreleased = true;\r
838                         }\r
839                 }\r
840                 mousepos += mousespeed;\r
841         }\r
842 \r
843         s32 Rand(s32 min, s32 max)\r
844         {\r
845                 return (myrand()%(max-min+1))+min;\r
846         }\r
847 private:\r
848         bool keydown[KEY_KEY_CODES_COUNT];\r
849         v2s32 mousepos;\r
850         v2s32 mousespeed;\r
851         bool leftdown;\r
852         bool rightdown;\r
853         bool leftclicked;\r
854         bool rightclicked;\r
855         bool leftreleased;\r
856         bool rightreleased;\r
857 };\r
858 \r
859 // These are defined global so that they're not optimized too much.\r
860 // Can't change them to volatile.\r
861 s16 temp16;\r
862 f32 tempf;\r
863 v3f tempv3f1;\r
864 v3f tempv3f2;\r
865 std::string tempstring;\r
866 std::string tempstring2;\r
867 \r
868 void SpeedTests()\r
869 {\r
870         {\r
871                 dstream<<"The following test should take around 20ms."<<std::endl;\r
872                 TimeTaker timer("Testing std::string speed");\r
873                 const u32 jj = 10000;\r
874                 for(u32 j=0; j<jj; j++)\r
875                 {\r
876                         tempstring = "";\r
877                         tempstring2 = "";\r
878                         const u32 ii = 10;\r
879                         for(u32 i=0; i<ii; i++){\r
880                                 tempstring2 += "asd";\r
881                         }\r
882                         for(u32 i=0; i<ii+1; i++){\r
883                                 tempstring += "asd";\r
884                                 if(tempstring == tempstring2)\r
885                                         break;\r
886                         }\r
887                 }\r
888         }\r
889         \r
890         dstream<<"All of the following tests should take around 100ms each."\r
891                         <<std::endl;\r
892 \r
893         {\r
894                 TimeTaker timer("Testing floating-point conversion speed");\r
895                 tempf = 0.001;\r
896                 for(u32 i=0; i<4000000; i++){\r
897                         temp16 += tempf;\r
898                         tempf += 0.001;\r
899                 }\r
900         }\r
901         \r
902         {\r
903                 TimeTaker timer("Testing floating-point vector speed");\r
904 \r
905                 tempv3f1 = v3f(1,2,3);\r
906                 tempv3f2 = v3f(4,5,6);\r
907                 for(u32 i=0; i<10000000; i++){\r
908                         tempf += tempv3f1.dotProduct(tempv3f2);\r
909                         tempv3f2 += v3f(7,8,9);\r
910                 }\r
911         }\r
912 \r
913         {\r
914                 TimeTaker timer("Testing core::map speed");\r
915                 \r
916                 core::map<v2s16, f32> map1;\r
917                 tempf = -324;\r
918                 const s16 ii=300;\r
919                 for(s16 y=0; y<ii; y++){\r
920                         for(s16 x=0; x<ii; x++){\r
921                                 map1.insert(v2s16(x,y), tempf);\r
922                                 tempf += 1;\r
923                         }\r
924                 }\r
925                 for(s16 y=ii-1; y>=0; y--){\r
926                         for(s16 x=0; x<ii; x++){\r
927                                 tempf = map1[v2s16(x,y)];\r
928                         }\r
929                 }\r
930         }\r
931 \r
932         {\r
933                 dstream<<"Around 5000/ms should do well here."<<std::endl;\r
934                 TimeTaker timer("Testing mutex speed");\r
935                 \r
936                 JMutex m;\r
937                 m.Init();\r
938                 u32 n = 0;\r
939                 u32 i = 0;\r
940                 do{\r
941                         n += 10000;\r
942                         for(; i<n; i++){\r
943                                 m.Lock();\r
944                                 m.Unlock();\r
945                         }\r
946                 }\r
947                 // Do at least 10ms\r
948                 while(timer.getTime() < 10);\r
949 \r
950                 u32 dtime = timer.stop();\r
951                 u32 per_ms = n / dtime;\r
952                 std::cout<<"Done. "<<dtime<<"ms, "\r
953                                 <<per_ms<<"/ms"<<std::endl;\r
954         }\r
955 }\r
956 \r
957 void drawMenuBackground(video::IVideoDriver* driver)\r
958 {\r
959         core::dimension2d<u32> screensize = driver->getScreenSize();\r
960                 \r
961         video::ITexture *bgtexture =\r
962                         driver->getTexture(getTexturePath("mud.png").c_str());\r
963         if(bgtexture)\r
964         {\r
965                 s32 texturesize = 128;\r
966                 s32 tiled_y = screensize.Height / texturesize + 1;\r
967                 s32 tiled_x = screensize.Width / texturesize + 1;\r
968                 \r
969                 for(s32 y=0; y<tiled_y; y++)\r
970                 for(s32 x=0; x<tiled_x; x++)\r
971                 {\r
972                         core::rect<s32> rect(0,0,texturesize,texturesize);\r
973                         rect += v2s32(x*texturesize, y*texturesize);\r
974                         driver->draw2DImage(bgtexture, rect,\r
975                                 core::rect<s32>(core::position2d<s32>(0,0),\r
976                                 core::dimension2di(bgtexture->getSize())),\r
977                                 NULL, NULL, true);\r
978                 }\r
979         }\r
980         \r
981         video::ITexture *logotexture =\r
982                         driver->getTexture(getTexturePath("menulogo.png").c_str());\r
983         if(logotexture)\r
984         {\r
985                 v2s32 logosize(logotexture->getOriginalSize().Width,\r
986                                 logotexture->getOriginalSize().Height);\r
987                 logosize *= 4;\r
988 \r
989                 video::SColor bgcolor(255,50,50,50);\r
990                 core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20,\r
991                                 screensize.Width, screensize.Height);\r
992                 driver->draw2DRectangle(bgcolor, bgrect, NULL);\r
993 \r
994                 core::rect<s32> rect(0,0,logosize.X,logosize.Y);\r
995                 rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y);\r
996                 rect -= v2s32(logosize.X/2, 0);\r
997                 driver->draw2DImage(logotexture, rect,\r
998                         core::rect<s32>(core::position2d<s32>(0,0),\r
999                         core::dimension2di(logotexture->getSize())),\r
1000                         NULL, NULL, true);\r
1001         }\r
1002 }\r
1003 \r
1004 int main(int argc, char *argv[])\r
1005 {\r
1006         /*\r
1007                 Parse command line\r
1008         */\r
1009         \r
1010         // List all allowed options\r
1011         core::map<std::string, ValueSpec> allowed_options;\r
1012         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));\r
1013         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,\r
1014                         "Run server directly"));\r
1015         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,\r
1016                         "Load configuration from specified file"));\r
1017         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));\r
1018         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));\r
1019         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));\r
1020         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1021         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1022         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));\r
1023 #ifdef _WIN32\r
1024         allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));\r
1025 #endif\r
1026         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));\r
1027 \r
1028         Settings cmd_args;\r
1029         \r
1030         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);\r
1031 \r
1032         if(ret == false || cmd_args.getFlag("help"))\r
1033         {\r
1034                 dstream<<"Allowed options:"<<std::endl;\r
1035                 for(core::map<std::string, ValueSpec>::Iterator\r
1036                                 i = allowed_options.getIterator();\r
1037                                 i.atEnd() == false; i++)\r
1038                 {\r
1039                         dstream<<"  --"<<i.getNode()->getKey();\r
1040                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)\r
1041                         {\r
1042                         }\r
1043                         else\r
1044                         {\r
1045                                 dstream<<" <value>";\r
1046                         }\r
1047                         dstream<<std::endl;\r
1048 \r
1049                         if(i.getNode()->getValue().help != NULL)\r
1050                         {\r
1051                                 dstream<<"      "<<i.getNode()->getValue().help\r
1052                                                 <<std::endl;\r
1053                         }\r
1054                 }\r
1055 \r
1056                 return cmd_args.getFlag("help") ? 0 : 1;\r
1057         }\r
1058         \r
1059         /*\r
1060                 Low-level initialization\r
1061         */\r
1062 \r
1063         bool disable_stderr = false;\r
1064 #ifdef _WIN32\r
1065         if(cmd_args.getFlag("dstream-on-stderr") == false)\r
1066                 disable_stderr = true;\r
1067 #endif\r
1068 \r
1069         // Initialize debug streams\r
1070         debugstreams_init(disable_stderr, DEBUGFILE);\r
1071         // Initialize debug stacks\r
1072         debug_stacks_init();\r
1073 \r
1074         DSTACK(__FUNCTION_NAME);\r
1075 \r
1076         porting::signal_handler_init();\r
1077         bool &kill = *porting::signal_handler_killstatus();\r
1078         \r
1079         porting::initializePaths();\r
1080         // Create user data directory\r
1081         fs::CreateDir(porting::path_userdata);\r
1082         \r
1083         // C-style stuff initialization\r
1084         initializeMaterialProperties();\r
1085 \r
1086         // Debug handler\r
1087         BEGIN_DEBUG_EXCEPTION_HANDLER\r
1088 \r
1089         // Print startup message\r
1090         dstream<<DTIME<<"minetest-c55"\r
1091                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST\r
1092                         <<", "<<BUILD_INFO\r
1093                         <<std::endl;\r
1094         \r
1095         /*\r
1096                 Basic initialization\r
1097         */\r
1098 \r
1099         // Initialize default settings\r
1100         set_default_settings();\r
1101         \r
1102         // Set locale. This is for forcing '.' as the decimal point.\r
1103         std::locale::global(std::locale("C"));\r
1104         // This enables printing all characters in bitmap font\r
1105         setlocale(LC_CTYPE, "en_US");\r
1106 \r
1107         // Initialize sockets\r
1108         sockets_init();\r
1109         atexit(sockets_cleanup);\r
1110         \r
1111         /*\r
1112                 Initialization\r
1113         */\r
1114 \r
1115         /*\r
1116                 Read config file\r
1117         */\r
1118         \r
1119         // Path of configuration file in use\r
1120         std::string configpath = "";\r
1121         \r
1122         if(cmd_args.exists("config"))\r
1123         {\r
1124                 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());\r
1125                 if(r == false)\r
1126                 {\r
1127                         dstream<<"Could not read configuration from \""\r
1128                                         <<cmd_args.get("config")<<"\""<<std::endl;\r
1129                         return 1;\r
1130                 }\r
1131                 configpath = cmd_args.get("config");\r
1132         }\r
1133         else\r
1134         {\r
1135                 core::array<std::string> filenames;\r
1136                 filenames.push_back(porting::path_userdata + "/minetest.conf");\r
1137 #ifdef RUN_IN_PLACE\r
1138                 filenames.push_back(porting::path_userdata + "/../minetest.conf");\r
1139 #endif\r
1140 \r
1141                 for(u32 i=0; i<filenames.size(); i++)\r
1142                 {\r
1143                         bool r = g_settings.readConfigFile(filenames[i].c_str());\r
1144                         if(r)\r
1145                         {\r
1146                                 configpath = filenames[i];\r
1147                                 break;\r
1148                         }\r
1149                 }\r
1150                 \r
1151                 // If no path found, use the first one (menu creates the file)\r
1152                 if(configpath == "")\r
1153                         configpath = filenames[0];\r
1154         }\r
1155 \r
1156         // Initialize random seed\r
1157         srand(time(0));\r
1158         mysrand(time(0));\r
1159 \r
1160         /*\r
1161                 Pre-initialize some stuff with a dummy irrlicht wrapper.\r
1162 \r
1163                 These are needed for unit tests at least.\r
1164         */\r
1165         \r
1166         // Initial call with g_texturesource not set.\r
1167         init_mapnode();\r
1168 \r
1169         /*\r
1170                 Run unit tests\r
1171         */\r
1172 \r
1173         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)\r
1174                         || cmd_args.getFlag("enable-unittests") == true)\r
1175         {\r
1176                 run_tests();\r
1177         }\r
1178         \r
1179         /*for(s16 y=-100; y<100; y++)\r
1180         for(s16 x=-100; x<100; x++)\r
1181         {\r
1182                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;\r
1183         }\r
1184         return 0;*/\r
1185         \r
1186         /*\r
1187                 Game parameters\r
1188         */\r
1189 \r
1190         // Port\r
1191         u16 port = 30000;\r
1192         if(cmd_args.exists("port"))\r
1193                 port = cmd_args.getU16("port");\r
1194         else if(g_settings.exists("port"))\r
1195                 port = g_settings.getU16("port");\r
1196         if(port == 0)\r
1197                 port = 30000;\r
1198         \r
1199         // Map directory\r
1200         std::string map_dir = porting::path_userdata+"/world";\r
1201         if(cmd_args.exists("map-dir"))\r
1202                 map_dir = cmd_args.get("map-dir");\r
1203         else if(g_settings.exists("map-dir"))\r
1204                 map_dir = g_settings.get("map-dir");\r
1205         \r
1206         // Run dedicated server if asked to\r
1207         if(cmd_args.getFlag("server"))\r
1208         {\r
1209                 DSTACK("Dedicated server branch");\r
1210 \r
1211                 // Create server\r
1212                 Server server(map_dir.c_str());\r
1213                 server.start(port);\r
1214                 \r
1215                 // Run server\r
1216                 dedicated_server_loop(server, kill);\r
1217 \r
1218                 return 0;\r
1219         }\r
1220 \r
1221 \r
1222         /*\r
1223                 More parameters\r
1224         */\r
1225         \r
1226         // Address to connect to\r
1227         std::string address = "";\r
1228         \r
1229         if(cmd_args.exists("address"))\r
1230         {\r
1231                 address = cmd_args.get("address");\r
1232         }\r
1233         else\r
1234         {\r
1235                 address = g_settings.get("address");\r
1236         }\r
1237         \r
1238         std::string playername = g_settings.get("name");\r
1239 \r
1240         /*\r
1241                 Device initialization\r
1242         */\r
1243 \r
1244         // Resolution selection\r
1245         \r
1246         bool fullscreen = false;\r
1247         u16 screenW = g_settings.getU16("screenW");\r
1248         u16 screenH = g_settings.getU16("screenH");\r
1249 \r
1250         // Determine driver\r
1251 \r
1252         video::E_DRIVER_TYPE driverType;\r
1253         \r
1254         std::string driverstring = g_settings.get("video_driver");\r
1255 \r
1256         if(driverstring == "null")\r
1257                 driverType = video::EDT_NULL;\r
1258         else if(driverstring == "software")\r
1259                 driverType = video::EDT_SOFTWARE;\r
1260         else if(driverstring == "burningsvideo")\r
1261                 driverType = video::EDT_BURNINGSVIDEO;\r
1262         else if(driverstring == "direct3d8")\r
1263                 driverType = video::EDT_DIRECT3D8;\r
1264         else if(driverstring == "direct3d9")\r
1265                 driverType = video::EDT_DIRECT3D9;\r
1266         else if(driverstring == "opengl")\r
1267                 driverType = video::EDT_OPENGL;\r
1268         else\r
1269         {\r
1270                 dstream<<"WARNING: Invalid video_driver specified; defaulting "\r
1271                                 "to opengl"<<std::endl;\r
1272                 driverType = video::EDT_OPENGL;\r
1273         }\r
1274 \r
1275         /*\r
1276                 Create device and exit if creation failed\r
1277         */\r
1278 \r
1279         MyEventReceiver receiver;\r
1280 \r
1281         IrrlichtDevice *device;\r
1282         device = createDevice(driverType,\r
1283                         core::dimension2d<u32>(screenW, screenH),\r
1284                         16, fullscreen, false, false, &receiver);\r
1285 \r
1286         if (device == 0)\r
1287                 return 1; // could not create selected driver.\r
1288         \r
1289         // Set device in game parameters\r
1290         device = device;\r
1291         \r
1292         // Create time getter\r
1293         g_timegetter = new TimeGetter(device);\r
1294         \r
1295         // Create game callback for menus\r
1296         g_gamecallback = new MainGameCallback(device);\r
1297         \r
1298         // Create texture source\r
1299         g_texturesource = new TextureSource(device);\r
1300 \r
1301         /*\r
1302                 Speed tests (done after irrlicht is loaded to get timer)\r
1303         */\r
1304         if(cmd_args.getFlag("speedtests"))\r
1305         {\r
1306                 dstream<<"Running speed tests"<<std::endl;\r
1307                 SpeedTests();\r
1308                 return 0;\r
1309         }\r
1310         \r
1311         device->setResizable(true);\r
1312 \r
1313         bool random_input = g_settings.getBool("random_input")\r
1314                         || cmd_args.getFlag("random-input");\r
1315         InputHandler *input = NULL;\r
1316         if(random_input)\r
1317                 input = new RandomInputHandler();\r
1318         else\r
1319                 input = new RealInputHandler(device, &receiver);\r
1320         \r
1321         /*\r
1322                 Continue initialization\r
1323         */\r
1324 \r
1325         //video::IVideoDriver* driver = device->getVideoDriver();\r
1326 \r
1327         /*\r
1328                 This changes the minimum allowed number of vertices in a VBO.\r
1329                 Default is 500.\r
1330         */\r
1331         //driver->setMinHardwareBufferVertexCount(50);\r
1332 \r
1333         scene::ISceneManager* smgr = device->getSceneManager();\r
1334 \r
1335         guienv = device->getGUIEnvironment();\r
1336         gui::IGUISkin* skin = guienv->getSkin();\r
1337         gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());\r
1338         if(font)\r
1339                 skin->setFont(font);\r
1340         else\r
1341                 dstream<<"WARNING: Font file was not found."\r
1342                                 " Using default font."<<std::endl;\r
1343         // If font was not found, this will get us one\r
1344         font = skin->getFont();\r
1345         assert(font);\r
1346         \r
1347         u32 text_height = font->getDimension(L"Hello, world!").Height;\r
1348         dstream<<"text_height="<<text_height<<std::endl;\r
1349 \r
1350         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
1351         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
1352         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
1353         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));\r
1354         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));\r
1355         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));\r
1356         \r
1357         /*\r
1358                 Preload some textures and stuff\r
1359         */\r
1360 \r
1361         init_content_inventory_texture_paths();\r
1362         init_mapnode(); // Second call with g_texturesource set\r
1363         init_mineral();\r
1364 \r
1365         /*\r
1366                 GUI stuff\r
1367         */\r
1368 \r
1369         /*\r
1370                 If an error occurs, this is set to something and the\r
1371                 menu-game loop is restarted. It is then displayed before\r
1372                 the menu.\r
1373         */\r
1374         std::wstring error_message = L"";\r
1375 \r
1376         // The password entered during the menu screen,\r
1377         std::string password;\r
1378 \r
1379         /*\r
1380                 Menu-game loop\r
1381         */\r
1382         while(device->run() && kill == false)\r
1383         {\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                                         // On some computers framerate doesn't seem to be\r
1466                                         // automatically limited\r
1467                                         sleep_ms(25);\r
1468                                 }\r
1469                                 \r
1470                                 // Break out of menu-game loop to shut down cleanly\r
1471                                 if(device->run() == false || kill == true)\r
1472                                         break;\r
1473                                 \r
1474                                 dstream<<"Dropping main menu"<<std::endl;\r
1475 \r
1476                                 menu->drop();\r
1477                                 \r
1478                                 // Delete map if requested\r
1479                                 if(menudata.delete_map)\r
1480                                 {\r
1481                                         bool r = fs::RecursiveDeleteContent(map_dir);\r
1482                                         if(r == false)\r
1483                                                 error_message = L"Delete failed";\r
1484                                         continue;\r
1485                                 }\r
1486 \r
1487                                 playername = wide_to_narrow(menudata.name);\r
1488 \r
1489                                 password = translatePassword(playername, menudata.password);\r
1490 \r
1491                                 address = wide_to_narrow(menudata.address);\r
1492                                 int newport = stoi(wide_to_narrow(menudata.port));\r
1493                                 if(newport != 0)\r
1494                                         port = newport;\r
1495                                 g_settings.set("new_style_leaves", itos(menudata.fancy_trees));\r
1496                                 g_settings.set("smooth_lighting", itos(menudata.smooth_lighting));\r
1497                                 g_settings.set("creative_mode", itos(menudata.creative_mode));\r
1498                                 g_settings.set("enable_damage", itos(menudata.enable_damage));\r
1499                                 \r
1500                                 /*// Check for valid parameters, restart menu if invalid.\r
1501                                 if(playername == "")\r
1502                                 {\r
1503                                         error_message = L"Name required.";\r
1504                                         continue;\r
1505                                 }\r
1506                                 // Check that name has only valid chars\r
1507                                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)\r
1508                                 {\r
1509                                         error_message = L"Characters allowed: "\r
1510                                                         +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);\r
1511                                         continue;\r
1512                                 }*/\r
1513 \r
1514                                 // Save settings\r
1515                                 g_settings.set("name", playername);\r
1516                                 g_settings.set("address", address);\r
1517                                 g_settings.set("port", itos(port));\r
1518                                 // Update configuration file\r
1519                                 if(configpath != "")\r
1520                                         g_settings.updateConfigFile(configpath.c_str());\r
1521                         \r
1522                                 // Continue to game\r
1523                                 break;\r
1524                         }\r
1525                         \r
1526                         // Break out of menu-game loop to shut down cleanly\r
1527                         if(device->run() == false)\r
1528                                 break;\r
1529                         \r
1530                         // Initialize mapnode again to enable changed graphics settings\r
1531                         init_mapnode();\r
1532 \r
1533                         /*\r
1534                                 Run game\r
1535                         */\r
1536                         the_game(\r
1537                                 kill,\r
1538                                 random_input,\r
1539                                 input,\r
1540                                 device,\r
1541                                 font,\r
1542                                 map_dir,\r
1543                                 playername,\r
1544                                 password,\r
1545                                 address,\r
1546                                 port,\r
1547                                 error_message\r
1548                         );\r
1549 \r
1550                 } //try\r
1551                 catch(con::PeerNotFoundException &e)\r
1552                 {\r
1553                         dstream<<DTIME<<"Connection error (timed out?)"<<std::endl;\r
1554                         error_message = L"Connection error (timed out?)";\r
1555                 }\r
1556                 catch(SocketException &e)\r
1557                 {\r
1558                         dstream<<DTIME<<"Socket error (port already in use?)"<<std::endl;\r
1559                         error_message = L"Socket error (port already in use?)";\r
1560                 }\r
1561 #ifdef NDEBUG\r
1562                 catch(std::exception &e)\r
1563                 {\r
1564                         std::string narrow_message = "Some exception, what()=\"";\r
1565                         narrow_message += e.what();\r
1566                         narrow_message += "\"";\r
1567                         dstream<<DTIME<<narrow_message<<std::endl;\r
1568                         error_message = narrow_to_wide(narrow_message);\r
1569                 }\r
1570 #endif\r
1571 \r
1572         } // Menu-game loop\r
1573         \r
1574         delete input;\r
1575 \r
1576         /*\r
1577                 In the end, delete the Irrlicht device.\r
1578         */\r
1579         device->drop();\r
1580         \r
1581         END_DEBUG_EXCEPTION_HANDLER\r
1582         \r
1583         debugstreams_deinit();\r
1584         \r
1585         return 0;\r
1586 }\r
1587 \r
1588 //END\r