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