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