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