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