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