]> git.lizzy.rs Git - dragonfireclient.git/blob - src/main.cpp
Mapgen is better now. Not a lot, but a bit!
[dragonfireclient.git] / src / main.cpp
1 /*\r
2 Minetest-c55\r
3 Copyright (C) 2010 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: VBO cannot be turned on for fast-changing stuff because there\r
25       is an apparanet memory leak in irrlicht when using it (not sure)\r
26           - It is not a memory leak but some kind of a buffer.\r
27 \r
28 NOTE: iostream.imbue(std::locale("C")) is very slow\r
29 NOTE: Global locale is now set at initialization\r
30 \r
31 Random suggeestions:\r
32 --------------------\r
33 \r
34 SUGG: Fix address to be ipv6 compatible\r
35 \r
36 NOTE: When a new sector is generated, it may change the ground level\r
37       of it's and it's neighbors border that two blocks that are\r
38           above and below each other and that are generated before and\r
39           after the sector heightmap generation (order doesn't matter),\r
40           can have a small gap between each other at the border.\r
41 SUGG: Use same technique for sector heightmaps as what we're\r
42       using for UnlimitedHeightmap? (getting all neighbors\r
43           when generating)\r
44 \r
45 SUGG: Transfer more blocks in a single packet\r
46 SUGG: A blockdata combiner class, to which blocks are added and at\r
47       destruction it sends all the stuff in as few packets as possible.\r
48 \r
49 SUGG: If player is on ground, mainly fetch ground-level blocks\r
50 SUGG: Fetch stuff mainly from the viewing direction\r
51 \r
52 SUGG: Expose Connection's seqnums and ACKs to server and client.\r
53       - This enables saving many packets and making a faster connection\r
54           - This also enables server to check if client has received the\r
55             most recent block sent, for example.\r
56 SUGG: Add a sane bandwidth throttling system to Connection\r
57 \r
58 SUGG: More fine-grained control of client's dumping of blocks from\r
59       memory\r
60           - ...What does this mean in the first place?\r
61 \r
62 SUGG: A map editing mode (similar to dedicated server mode)\r
63 \r
64 SUGG: Add a time value to the param of footstepped grass and check it\r
65       against a global timer when a block is accessed, to make old\r
66           steps fade away.\r
67 \r
68 SUGG: Make a copy of close-range environment on client for showing\r
69       on screen, with minimal mutexes to slow down the main loop\r
70 \r
71 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize\r
72       it by sending more stuff in a single packet.\r
73           - Add a packet queue to RemoteClient, from which packets will be\r
74             combined with object data packets\r
75                 - This is not exactly trivial: the object data packets are\r
76                   sometimes very big by themselves\r
77 \r
78 SUGG: Split MapBlockObject serialization to to-client and to-disk\r
79       - This will allow saving ages of rats on disk but not sending\r
80             them to clients\r
81 \r
82 SUGG: MovingObject::move and Player::move are basically the same.\r
83       combine them.\r
84           - NOTE: Player::move is more up-to-date.\r
85 \r
86 SUGG: Precalculate lighting translation table at runtime (at startup)\r
87       - This is not doable because it is currently hand-made and not\r
88             based on some mathematical function.\r
89                 - Note: This has been changing lately\r
90 \r
91 SUGG: A version number to blocks, which increments when the block is\r
92       modified (node add/remove, water update, lighting update)\r
93           - This can then be used to make sure the most recent version of\r
94             a block has been sent to client\r
95 \r
96 SUGG: Make the amount of blocks sending to client and the total\r
97           amount of blocks dynamically limited. Transferring blocks is the\r
98           main network eater of this system, so it is the one that has\r
99           to be throttled so that RTTs stay low.\r
100 \r
101 SUGG: Meshes of blocks could be split into 6 meshes facing into\r
102       different directions and then only those drawn that need to be\r
103 \r
104 SUGG: Calculate lighting per vertex to get a lighting effect like in\r
105       bartwe's game\r
106 \r
107 Gaming ideas:\r
108 -------------\r
109 \r
110 - Aim for something like controlling a single dwarf in Dwarf Fortress\r
111 \r
112 - The player could go faster by a crafting a boat, or riding an animal\r
113 \r
114 - Random NPC traders. what else?\r
115 \r
116 Documentation:\r
117 --------------\r
118 \r
119 Build system / running:\r
120 -----------------------\r
121 \r
122 Networking and serialization:\r
123 -----------------------------\r
124 \r
125 TODO: Get rid of GotSplitPacketException\r
126 \r
127 GUI:\r
128 ----\r
129 \r
130 TODO: Add gui option to remove map\r
131 \r
132 TODO: Configuration menu, at least for keys\r
133 \r
134 Graphics:\r
135 ---------\r
136 \r
137 TODO: Optimize day/night mesh updating somehow\r
138       - create copies of all textures for all lighting values and only\r
139             change texture for material?\r
140           - Umm... the collecting of the faces is the slow part\r
141             -> what about just changing the color values of the existing\r
142                    meshbuffers? It should go quite fast.\r
143                    - This is not easy; There'd need to be a buffer somewhere\r
144                      that would contain the night and day lighting values.\r
145                          - Actually if FastFaces would be stored, they could\r
146                            hold both values\r
147 \r
148 FEATURE: Combine MapBlock's face caches to so big pieces that VBO\r
149       gets used\r
150       - That is >500 vertices\r
151           - This is not easy; all the MapBlocks close to the player would\r
152             still need to be drawn separately and combining the blocks\r
153                 would have to happen in a background thread\r
154 \r
155 TODO: Make fetching sector's blocks more efficient when rendering\r
156       sectors that have very large amounts of blocks (on client)\r
157           - Is this necessary at all?\r
158 \r
159 TODO: Flowing water animation\r
160 \r
161 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC\r
162 \r
163 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that\r
164       animating them is easier.\r
165 \r
166 Configuration:\r
167 --------------\r
168 \r
169 Client:\r
170 -------\r
171 \r
172 TODO: Untie client network operations from framerate\r
173       - Needs some input queues or something\r
174 \r
175 SUGG: Make morning and evening transition more smooth and maybe shorter\r
176 \r
177 SUGG: Don't update all meshes always on single node changes, but\r
178       check which ones should be updated\r
179           - implement Map::updateNodeMeshes()\r
180 \r
181 TODO: Remove IrrlichtWrapper\r
182 \r
183 Server:\r
184 -------\r
185 \r
186 TODO: When player dies, throw items on map\r
187 \r
188 SUGG: Make an option to the server to disable building and digging near\r
189       the starting position\r
190 \r
191 TODO: Copy the text of the last picked sign to inventory in creative\r
192       mode\r
193 \r
194 TODO: Check what goes wrong with caching map to disk (Kray)\r
195       - Nothing?\r
196 \r
197 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop\r
198 \r
199 * Fix the problem with the server constantly saving one or a few\r
200   blocks? List the first saved block, maybe it explains.\r
201   - It is probably caused by oscillating water\r
202 * Make a small history check to transformLiquids to detect and log\r
203   continuous oscillations, in such detail that they can be fixed.\r
204 \r
205 Objects:\r
206 --------\r
207 \r
208 TODO: There has to be some better way to handle static objects than to\r
209       send them all the time. This affects signs and item objects.\r
210 SUGG: Signs could be done in the same way as torches. For this, blocks\r
211       need an additional metadata field for the texts\r
212           - This is also needed for item container chests\r
213 \r
214 Block object server side:\r
215       - A "near blocks" buffer, in which some nearby blocks are stored.\r
216           - For all blocks in the buffer, objects are stepped(). This\r
217             means they are active.\r
218           - TODO: A global active buffer is needed for the server\r
219           - TODO: A timestamp to blocks\r
220       - TODO: All blocks going in and out of the buffer are recorded.\r
221             - TODO: For outgoing blocks, timestamp is written.\r
222             - TODO: For incoming blocks, time difference is calculated and\r
223               objects are stepped according to it.\r
224 \r
225 - When an active object goes far from a player, either delete\r
226   it or store it statically.\r
227 - When a statically stored active object comes near a player,\r
228   recreate the active object\r
229 \r
230 * Continue making the scripting system:\r
231   * Make updateNodeMesh for a less verbose mesh update on add/removenode\r
232   * Switch to using a safe way for the self and env pointers\r
233   * Make some global environment hooks, like node placed and general\r
234     on_step()\r
235 * Add a global Lua spawn handler and such\r
236 * Get rid of MapBlockObjects\r
237 * Other players could be sent to clients as LuaCAOs\r
238 \r
239 Map:\r
240 ----\r
241 \r
242 TODO: Mineral and ground material properties\r
243       - This way mineral ground toughness can be calculated with just\r
244             some formula, as well as tool strengths\r
245 \r
246 TODO: Flowing water to actually contain flow direction information\r
247 \r
248 FEATURE: Create a system that allows a huge amount of different "map\r
249              generator modules/filters"\r
250 \r
251 FEATURE: Erosion simulation at map generation time\r
252                 - Simulate water flows, which would carve out dirt fast and\r
253                   then turn stone into gravel and sand and relocate it.\r
254                 - How about relocating minerals, too? Coal and gold in\r
255                   downstream sand and gravel would be kind of cool\r
256                   - This would need a better way of handling minerals, mainly\r
257                     to have mineral content as a separate field. the first\r
258                         parameter field is free for this.\r
259                 - Simulate rock falling from cliffs when water has removed\r
260                   enough solid rock from the bottom\r
261 \r
262 Mapgen v2 (not doing):\r
263 * only_from_disk might not work anymore - check and fix it.\r
264 * Make the generator to run in background and not blocking block\r
265   placement and transfer\r
266 * Add some kind of erosion and other stuff that now is possible\r
267 * Make client to fetch stuff asynchronously\r
268   - Needs method SyncProcessData\r
269 * Better water generation (spread it to underwater caverns but don't\r
270   fill dungeons that don't touch big water masses)\r
271 * When generating a chunk and the neighboring chunk doesn't have mud\r
272   and stuff yet and the ground is fairly flat, the mud will flow to\r
273   the other chunk making nasty straight walls when the other chunk\r
274   is generated. Fix it.\r
275 \r
276 Mapgen v4 (not doing):\r
277 * only_from_disk might not work anymore - check and fix it.\r
278 * Make the generator to run in background and not blocking block\r
279   placement and transfer\r
280 * Make chunks to be tiled vertically too\r
281 * MAKE IT FASTER\r
282 \r
283 Mapgen v3 (not doing):\r
284 * Generate trees better\r
285   - Add a "trees_added" flag to sector, or something\r
286 * How 'bout making turbulence controlled so that for a given 2d position\r
287   it can be completely turned off, and is usually so. This way generation\r
288   can be sped up a lot.\r
289 * Add a way to generate a block partly, so that trees are not added, like\r
290   the chunks in v2\r
291 * Add mud "discretely", not by guessing from the noise\r
292 \r
293 Mapgen v4:\r
294 * This will be the final way.\r
295 * Generate blocks in the same way as chunks, by copying a VoxelManipulator\r
296   from the map that is one block larger in all directions.\r
297 \r
298 Misc. stuff:\r
299 ------------\r
300 * Make an "environment metafile" to store at least time of day\r
301 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...\r
302   - Or maybe move content_features to material.{h,cpp}?\r
303 * Maybe:\r
304   Make a system for pregenerating quick information for mapblocks, so\r
305   that the client can show them as cubes before they are actually sent\r
306   or even generated.\r
307 * Optimize VoxelManipulator lighting implementation by using indices\r
308   in place of coordinates?\r
309 \r
310 Making it more portable:\r
311 ------------------------\r
312 * Some MSVC: std::sto* are defined without a namespace and collide\r
313   with the ones in utility.h\r
314 \r
315 ======================================================================\r
316 \r
317 */\r
318 \r
319 /*\r
320         Setting this to 1 enables a special camera mode that forces\r
321         the renderers to think that the camera statically points from\r
322         the starting place to a static direction.\r
323 \r
324         This allows one to move around with the player and see what\r
325         is actually drawn behind solid things and behind the player.\r
326 */\r
327 #define FIELD_OF_VIEW_TEST 0\r
328 \r
329 #ifdef NDEBUG\r
330         #ifdef _WIN32\r
331                 #pragma message ("Disabling unit tests")\r
332         #else\r
333                 #warning "Disabling unit tests"\r
334         #endif\r
335         // Disable unit tests\r
336         #define ENABLE_TESTS 0\r
337 #else\r
338         // Enable unit tests\r
339         #define ENABLE_TESTS 1\r
340 #endif\r
341 \r
342 #ifdef _MSC_VER\r
343         #pragma comment(lib, "Irrlicht.lib")\r
344         //#pragma comment(lib, "jthread.lib")\r
345         #pragma comment(lib, "zlibwapi.lib")\r
346         #pragma comment(lib, "Shell32.lib")\r
347         // This would get rid of the console window\r
348         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")\r
349 #endif\r
350 \r
351 #include <iostream>\r
352 #include <fstream>\r
353 #include <jmutexautolock.h>\r
354 #include <locale.h>\r
355 #include "main.h"\r
356 #include "common_irrlicht.h"\r
357 #include "debug.h"\r
358 #include "map.h"\r
359 #include "player.h"\r
360 #include "test.h"\r
361 //#include "environment.h"\r
362 #include "server.h"\r
363 #include "client.h"\r
364 //#include "serialization.h"\r
365 #include "constants.h"\r
366 //#include "strfnd.h"\r
367 #include "porting.h"\r
368 #include "irrlichtwrapper.h"\r
369 #include "gettime.h"\r
370 #include "porting.h"\r
371 #include "guiPauseMenu.h"\r
372 #include "guiInventoryMenu.h"\r
373 #include "guiTextInputMenu.h"\r
374 #include "materials.h"\r
375 #include "guiMessageMenu.h"\r
376 #include "filesys.h"\r
377 #include "config.h"\r
378 #include "guiMainMenu.h"\r
379 #include "mineral.h"\r
380 #include "noise.h"\r
381 #include "tile.h"\r
382 \r
383 // TODO: Remove this\r
384 IrrlichtWrapper *g_irrlicht = NULL;\r
385 \r
386 // This makes textures\r
387 ITextureSource *g_texturesource = NULL;\r
388 \r
389 MapDrawControl draw_control;\r
390 \r
391 /*\r
392         Settings.\r
393         These are loaded from the config file.\r
394 */\r
395 \r
396 Settings g_settings;\r
397 \r
398 extern void set_default_settings();\r
399 \r
400 /*\r
401         Random stuff\r
402 */\r
403 \r
404 IrrlichtDevice *g_device = NULL;\r
405 Client *g_client = NULL;\r
406 \r
407 /*const s16 quickinv_size = 40;\r
408 const s16 quickinv_padding = 8;\r
409 const s16 quickinv_spacing = quickinv_size + quickinv_padding;\r
410 const s16 quickinv_outer_padding = 4;\r
411 const s16 quickinv_itemcount = 8;*/\r
412 \r
413 const s32 hotbar_itemcount = 8;\r
414 const s32 hotbar_imagesize = 36;\r
415 \r
416 /*\r
417         GUI Stuff\r
418 */\r
419 \r
420 gui::IGUIEnvironment* guienv = NULL;\r
421 gui::IGUIStaticText *guiroot = NULL;\r
422 \r
423 class MainMenuManager : public IMenuManager\r
424 {\r
425 public:\r
426         virtual void createdMenu(GUIModalMenu *menu)\r
427         {\r
428                 for(core::list<GUIModalMenu*>::Iterator\r
429                                 i = m_stack.begin();\r
430                                 i != m_stack.end(); i++)\r
431                 {\r
432                         assert(*i != menu);\r
433                 }\r
434 \r
435                 if(m_stack.size() != 0)\r
436                         (*m_stack.getLast())->setVisible(false);\r
437                 m_stack.push_back(menu);\r
438         }\r
439 \r
440         virtual void deletingMenu(GUIModalMenu *menu)\r
441         {\r
442                 // Remove all entries if there are duplicates\r
443                 bool removed_entry;\r
444                 do{\r
445                         removed_entry = false;\r
446                         for(core::list<GUIModalMenu*>::Iterator\r
447                                         i = m_stack.begin();\r
448                                         i != m_stack.end(); i++)\r
449                         {\r
450                                 if(*i == menu)\r
451                                 {\r
452                                         m_stack.erase(i);\r
453                                         removed_entry = true;\r
454                                         break;\r
455                                 }\r
456                         }\r
457                 }while(removed_entry);\r
458 \r
459                 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();\r
460                 assert(*i == menu);\r
461                 m_stack.erase(i);*/\r
462                 \r
463                 if(m_stack.size() != 0)\r
464                         (*m_stack.getLast())->setVisible(true);\r
465         }\r
466 \r
467         u32 menuCount()\r
468         {\r
469                 return m_stack.size();\r
470         }\r
471 \r
472         core::list<GUIModalMenu*> m_stack;\r
473 };\r
474 \r
475 MainMenuManager g_menumgr;\r
476 \r
477 bool noMenuActive()\r
478 {\r
479         return (g_menumgr.menuCount() == 0);\r
480 }\r
481 \r
482 bool g_disconnect_requested = false;\r
483 \r
484 class MainGameCallback : public IGameCallback\r
485 {\r
486 public:\r
487         virtual void exitToOS()\r
488         {\r
489                 g_device->closeDevice();\r
490         }\r
491 \r
492         virtual void disconnect()\r
493         {\r
494                 g_disconnect_requested = true;\r
495         }\r
496 };\r
497 \r
498 MainGameCallback g_gamecallback;\r
499 \r
500 // Inventory actions from the menu are buffered here before sending\r
501 Queue<InventoryAction*> inventory_action_queue;\r
502 // This is a copy of the inventory that the client's environment has\r
503 Inventory local_inventory;\r
504 \r
505 u16 g_selected_item = 0;\r
506 \r
507 bool g_show_map_plot = false;\r
508 bool g_refresh_map_plot = false;\r
509 \r
510 /*\r
511         Debug streams\r
512 */\r
513 \r
514 // Connection\r
515 std::ostream *dout_con_ptr = &dummyout;\r
516 std::ostream *derr_con_ptr = &dstream_no_stderr;\r
517 //std::ostream *dout_con_ptr = &dstream_no_stderr;\r
518 //std::ostream *derr_con_ptr = &dstream_no_stderr;\r
519 //std::ostream *dout_con_ptr = &dstream;\r
520 //std::ostream *derr_con_ptr = &dstream;\r
521 \r
522 // Server\r
523 std::ostream *dout_server_ptr = &dstream;\r
524 std::ostream *derr_server_ptr = &dstream;\r
525 \r
526 // Client\r
527 std::ostream *dout_client_ptr = &dstream;\r
528 std::ostream *derr_client_ptr = &dstream;\r
529 \r
530 /*\r
531         gettime.h implementation\r
532 */\r
533 \r
534 u32 getTimeMs()\r
535 {\r
536         /*\r
537                 Use irrlicht because it is more precise than porting.h's\r
538                 getTimeMs()\r
539         */\r
540         if(g_irrlicht == NULL)\r
541                 return 0;\r
542         return g_irrlicht->getTime();\r
543 }\r
544 \r
545 /*\r
546         Text input system\r
547 */\r
548 \r
549 struct TextDestSign : public TextDest\r
550 {\r
551         TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
552         {\r
553                 m_blockpos = blockpos;\r
554                 m_id = id;\r
555                 m_client = client;\r
556         }\r
557         void gotText(std::wstring text)\r
558         {\r
559                 std::string ntext = wide_to_narrow(text);\r
560                 dstream<<"Changing text of a sign object: "\r
561                                 <<ntext<<std::endl;\r
562                 m_client->sendSignText(m_blockpos, m_id, ntext);\r
563         }\r
564 \r
565         v3s16 m_blockpos;\r
566         s16 m_id;\r
567         Client *m_client;\r
568 };\r
569 \r
570 struct TextDestChat : public TextDest\r
571 {\r
572         TextDestChat(Client *client)\r
573         {\r
574                 m_client = client;\r
575         }\r
576         void gotText(std::wstring text)\r
577         {\r
578                 // Discard empty line\r
579                 if(text == L"")\r
580                         return;\r
581                 \r
582                 // Parse command (server command starts with "/#")\r
583                 if(text[0] == L'/' && text[1] != L'#')\r
584                 {\r
585                         std::wstring reply = L"Local: ";\r
586 \r
587                         reply += L"Local commands not yet supported. "\r
588                                         L"Server prefix is \"/#\".";\r
589                         \r
590                         m_client->addChatMessage(reply);\r
591                         return;\r
592                 }\r
593 \r
594                 // Send to others\r
595                 m_client->sendChatMessage(text);\r
596                 // Show locally\r
597                 m_client->addChatMessage(text);\r
598         }\r
599 \r
600         Client *m_client;\r
601 };\r
602 \r
603 class MyEventReceiver : public IEventReceiver\r
604 {\r
605 public:\r
606         // This is the one method that we have to implement\r
607         virtual bool OnEvent(const SEvent& event)\r
608         {\r
609                 /*\r
610                         React to nothing here if a menu is active\r
611                 */\r
612                 if(noMenuActive() == false)\r
613                 {\r
614                         clearInput();\r
615                         return false;\r
616                 }\r
617 \r
618                 // Remember whether each key is down or up\r
619                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
620                 {\r
621                         keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;\r
622 \r
623                         if(event.KeyInput.PressedDown)\r
624                         {\r
625                                 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;\r
626                                 if(g_show_map_plot)\r
627                                 {\r
628                                         if(event.KeyInput.Key == irr::KEY_ESCAPE\r
629                                                 || event.KeyInput.Key == irr::KEY_KEY_M)\r
630                                         {\r
631                                                 g_show_map_plot = false;\r
632                                         }\r
633                                         return true;\r
634                                 }\r
635                                 \r
636                                 /*\r
637                                         Launch menus\r
638                                 */\r
639 \r
640                                 if(guienv != NULL && guiroot != NULL && g_device != NULL)\r
641                                 {\r
642                                         if(event.KeyInput.Key == irr::KEY_ESCAPE)\r
643                                         {\r
644                                                 dstream<<DTIME<<"MyEventReceiver: "\r
645                                                                 <<"Launching pause menu"<<std::endl;\r
646                                                 // It will delete itself by itself\r
647                                                 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,\r
648                                                                 &g_menumgr))->drop();\r
649                                                 return true;\r
650                                         }\r
651                                         if(event.KeyInput.Key == irr::KEY_KEY_I)\r
652                                         {\r
653                                                 dstream<<DTIME<<"MyEventReceiver: "\r
654                                                                 <<"Launching inventory"<<std::endl;\r
655                                                 (new GUIInventoryMenu(guienv, guiroot, -1,\r
656                                                                 &local_inventory, &inventory_action_queue,\r
657                                                                 &g_menumgr))->drop();\r
658                                                 return true;\r
659                                         }\r
660                                         if(event.KeyInput.Key == irr::KEY_KEY_T)\r
661                                         {\r
662                                                 TextDest *dest = new TextDestChat(g_client);\r
663 \r
664                                                 (new GUITextInputMenu(guienv, guiroot, -1,\r
665                                                                 &g_menumgr, dest,\r
666                                                                 L""))->drop();\r
667                                         }\r
668                                 }\r
669 \r
670                                 // Item selection\r
671                                 if(event.KeyInput.Key >= irr::KEY_KEY_0\r
672                                                 && event.KeyInput.Key <= irr::KEY_KEY_9)\r
673                                 {\r
674                                         u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;\r
675                                         if(event.KeyInput.Key == irr::KEY_KEY_0)\r
676                                                 s1 = 10;\r
677                                         if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)\r
678                                                 g_selected_item = s1-1;\r
679                                         dstream<<DTIME<<"Selected item: "\r
680                                                         <<g_selected_item<<std::endl;\r
681                                 }\r
682 \r
683                                 // Viewing range selection\r
684                                 if(event.KeyInput.Key == irr::KEY_KEY_R)\r
685                                 {\r
686                                         if(draw_control.range_all)\r
687                                         {\r
688                                                 draw_control.range_all = false;\r
689                                                 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;\r
690                                         }\r
691                                         else\r
692                                         {\r
693                                                 draw_control.range_all = true;\r
694                                                 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;\r
695                                         }\r
696                                 }\r
697 \r
698                                 // Print debug stacks\r
699                                 if(event.KeyInput.Key == irr::KEY_KEY_P)\r
700                                 {\r
701                                         dstream<<"-----------------------------------------"\r
702                                                         <<std::endl;\r
703                                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;\r
704                                         dstream<<"-----------------------------------------"\r
705                                                         <<std::endl;\r
706                                         debug_stacks_print();\r
707                                 }\r
708 \r
709                                 // Map plot\r
710                                 if(event.KeyInput.Key == irr::KEY_KEY_M)\r
711                                 {\r
712                                         dstream<<"Map plot requested"<<std::endl;\r
713                                         g_show_map_plot = !g_show_map_plot;\r
714                                         if(g_show_map_plot)\r
715                                                 g_refresh_map_plot = true;\r
716                                 }\r
717                                 \r
718                         }\r
719                 }\r
720 \r
721                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
722                 {\r
723                         if(noMenuActive() == false)\r
724                         {\r
725                                 left_active = false;\r
726                                 middle_active = false;\r
727                                 right_active = false;\r
728                         }\r
729                         else\r
730                         {\r
731                                 //dstream<<"MyEventReceiver: mouse input"<<std::endl;\r
732                                 left_active = event.MouseInput.isLeftPressed();\r
733                                 middle_active = event.MouseInput.isMiddlePressed();\r
734                                 right_active = event.MouseInput.isRightPressed();\r
735 \r
736                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
737                                 {\r
738                                         leftclicked = true;\r
739                                 }\r
740                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
741                                 {\r
742                                         rightclicked = true;\r
743                                 }\r
744                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
745                                 {\r
746                                         leftreleased = true;\r
747                                 }\r
748                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
749                                 {\r
750                                         rightreleased = true;\r
751                                 }\r
752                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
753                                 {\r
754                                         /*dstream<<"event.MouseInput.Wheel="\r
755                                                         <<event.MouseInput.Wheel<<std::endl;*/\r
756                                         \r
757                                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,\r
758                                                         hotbar_itemcount-1);\r
759                                         if(event.MouseInput.Wheel < 0)\r
760                                         {\r
761                                                 if(g_selected_item < max_item)\r
762                                                         g_selected_item++;\r
763                                                 else\r
764                                                         g_selected_item = 0;\r
765                                         }\r
766                                         else if(event.MouseInput.Wheel > 0)\r
767                                         {\r
768                                                 if(g_selected_item > 0)\r
769                                                         g_selected_item--;\r
770                                                 else\r
771                                                         g_selected_item = max_item;\r
772                                         }\r
773                                 }\r
774                         }\r
775                 }\r
776 \r
777                 return false;\r
778         }\r
779 \r
780         // This is used to check whether a key is being held down\r
781         virtual bool IsKeyDown(EKEY_CODE keyCode) const\r
782         {\r
783                 return keyIsDown[keyCode];\r
784         }\r
785 \r
786         void clearInput()\r
787         {\r
788                 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
789                                 keyIsDown[i] = false;\r
790                 \r
791                 leftclicked = false;\r
792                 rightclicked = false;\r
793                 leftreleased = false;\r
794                 rightreleased = false;\r
795 \r
796                 left_active = false;\r
797                 middle_active = false;\r
798                 right_active = false;\r
799         }\r
800 \r
801         MyEventReceiver()\r
802         {\r
803                 clearInput();\r
804         }\r
805 \r
806         bool leftclicked;\r
807         bool rightclicked;\r
808         bool leftreleased;\r
809         bool rightreleased;\r
810 \r
811         bool left_active;\r
812         bool middle_active;\r
813         bool right_active;\r
814 \r
815 private:\r
816         // We use this array to store the current state of each key\r
817         bool keyIsDown[KEY_KEY_CODES_COUNT];\r
818         //s32 mouseX;\r
819         //s32 mouseY;\r
820         IrrlichtDevice *m_device;\r
821 };\r
822 \r
823 class InputHandler\r
824 {\r
825 public:\r
826         InputHandler()\r
827         {\r
828         }\r
829         virtual ~InputHandler()\r
830         {\r
831         }\r
832 \r
833         virtual bool isKeyDown(EKEY_CODE keyCode) = 0;\r
834 \r
835         virtual v2s32 getMousePos() = 0;\r
836         virtual void setMousePos(s32 x, s32 y) = 0;\r
837 \r
838         virtual bool getLeftState() = 0;\r
839         virtual bool getRightState() = 0;\r
840 \r
841         virtual bool getLeftClicked() = 0;\r
842         virtual bool getRightClicked() = 0;\r
843         virtual void resetLeftClicked() = 0;\r
844         virtual void resetRightClicked() = 0;\r
845 \r
846         virtual bool getLeftReleased() = 0;\r
847         virtual bool getRightReleased() = 0;\r
848         virtual void resetLeftReleased() = 0;\r
849         virtual void resetRightReleased() = 0;\r
850         \r
851         virtual void step(float dtime) {};\r
852 \r
853         virtual void clear() {};\r
854 };\r
855 \r
856 InputHandler *g_input = NULL;\r
857 \r
858 class RealInputHandler : public InputHandler\r
859 {\r
860 public:\r
861         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):\r
862                 m_device(device),\r
863                 m_receiver(receiver)\r
864         {\r
865         }\r
866         virtual bool isKeyDown(EKEY_CODE keyCode)\r
867         {\r
868                 return m_receiver->IsKeyDown(keyCode);\r
869         }\r
870         virtual v2s32 getMousePos()\r
871         {\r
872                 return m_device->getCursorControl()->getPosition();\r
873         }\r
874         virtual void setMousePos(s32 x, s32 y)\r
875         {\r
876                 m_device->getCursorControl()->setPosition(x, y);\r
877         }\r
878 \r
879         virtual bool getLeftState()\r
880         {\r
881                 return m_receiver->left_active;\r
882         }\r
883         virtual bool getRightState()\r
884         {\r
885                 return m_receiver->right_active;\r
886         }\r
887         \r
888         virtual bool getLeftClicked()\r
889         {\r
890                 return m_receiver->leftclicked;\r
891         }\r
892         virtual bool getRightClicked()\r
893         {\r
894                 return m_receiver->rightclicked;\r
895         }\r
896         virtual void resetLeftClicked()\r
897         {\r
898                 m_receiver->leftclicked = false;\r
899         }\r
900         virtual void resetRightClicked()\r
901         {\r
902                 m_receiver->rightclicked = false;\r
903         }\r
904 \r
905         virtual bool getLeftReleased()\r
906         {\r
907                 return m_receiver->leftreleased;\r
908         }\r
909         virtual bool getRightReleased()\r
910         {\r
911                 return m_receiver->rightreleased;\r
912         }\r
913         virtual void resetLeftReleased()\r
914         {\r
915                 m_receiver->leftreleased = false;\r
916         }\r
917         virtual void resetRightReleased()\r
918         {\r
919                 m_receiver->rightreleased = false;\r
920         }\r
921 \r
922         void clear()\r
923         {\r
924                 resetRightClicked();\r
925                 resetLeftClicked();\r
926         }\r
927 private:\r
928         IrrlichtDevice *m_device;\r
929         MyEventReceiver *m_receiver;\r
930 };\r
931 \r
932 class RandomInputHandler : public InputHandler\r
933 {\r
934 public:\r
935         RandomInputHandler()\r
936         {\r
937                 leftdown = false;\r
938                 rightdown = false;\r
939                 leftclicked = false;\r
940                 rightclicked = false;\r
941                 leftreleased = false;\r
942                 rightreleased = false;\r
943                 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
944                         keydown[i] = false;\r
945         }\r
946         virtual bool isKeyDown(EKEY_CODE keyCode)\r
947         {\r
948                 return keydown[keyCode];\r
949         }\r
950         virtual v2s32 getMousePos()\r
951         {\r
952                 return mousepos;\r
953         }\r
954         virtual void setMousePos(s32 x, s32 y)\r
955         {\r
956                 mousepos = v2s32(x,y);\r
957         }\r
958 \r
959         virtual bool getLeftState()\r
960         {\r
961                 return leftdown;\r
962         }\r
963         virtual bool getRightState()\r
964         {\r
965                 return rightdown;\r
966         }\r
967 \r
968         virtual bool getLeftClicked()\r
969         {\r
970                 return leftclicked;\r
971         }\r
972         virtual bool getRightClicked()\r
973         {\r
974                 return rightclicked;\r
975         }\r
976         virtual void resetLeftClicked()\r
977         {\r
978                 leftclicked = false;\r
979         }\r
980         virtual void resetRightClicked()\r
981         {\r
982                 rightclicked = false;\r
983         }\r
984 \r
985         virtual bool getLeftReleased()\r
986         {\r
987                 return leftreleased;\r
988         }\r
989         virtual bool getRightReleased()\r
990         {\r
991                 return rightreleased;\r
992         }\r
993         virtual void resetLeftReleased()\r
994         {\r
995                 leftreleased = false;\r
996         }\r
997         virtual void resetRightReleased()\r
998         {\r
999                 rightreleased = false;\r
1000         }\r
1001 \r
1002         virtual void step(float dtime)\r
1003         {\r
1004                 {\r
1005                         static float counter1 = 0;\r
1006                         counter1 -= dtime;\r
1007                         if(counter1 < 0.0)\r
1008                         {\r
1009                                 counter1 = 0.1*Rand(1, 40);\r
1010                                 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];\r
1011                         }\r
1012                 }\r
1013                 {\r
1014                         static float counter1 = 0;\r
1015                         counter1 -= dtime;\r
1016                         if(counter1 < 0.0)\r
1017                         {\r
1018                                 counter1 = 0.1*Rand(1, 40);\r
1019                                 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];\r
1020                         }\r
1021                 }\r
1022                 {\r
1023                         static float counter1 = 0;\r
1024                         counter1 -= dtime;\r
1025                         if(counter1 < 0.0)\r
1026                         {\r
1027                                 counter1 = 0.1*Rand(1, 40);\r
1028                                 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];\r
1029                         }\r
1030                 }\r
1031                 {\r
1032                         static float counter1 = 0;\r
1033                         counter1 -= dtime;\r
1034                         if(counter1 < 0.0)\r
1035                         {\r
1036                                 counter1 = 0.1*Rand(1, 40);\r
1037                                 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];\r
1038                         }\r
1039                 }\r
1040                 {\r
1041                         static float counter1 = 0;\r
1042                         counter1 -= dtime;\r
1043                         if(counter1 < 0.0)\r
1044                         {\r
1045                                 counter1 = 0.1*Rand(1, 20);\r
1046                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));\r
1047                         }\r
1048                 }\r
1049                 {\r
1050                         static float counter1 = 0;\r
1051                         counter1 -= dtime;\r
1052                         if(counter1 < 0.0)\r
1053                         {\r
1054                                 counter1 = 0.1*Rand(1, 30);\r
1055                                 leftdown = !leftdown;\r
1056                                 if(leftdown)\r
1057                                         leftclicked = true;\r
1058                                 if(!leftdown)\r
1059                                         leftreleased = true;\r
1060                         }\r
1061                 }\r
1062                 {\r
1063                         static float counter1 = 0;\r
1064                         counter1 -= dtime;\r
1065                         if(counter1 < 0.0)\r
1066                         {\r
1067                                 counter1 = 0.1*Rand(1, 15);\r
1068                                 rightdown = !rightdown;\r
1069                                 if(rightdown)\r
1070                                         rightclicked = true;\r
1071                                 if(!rightdown)\r
1072                                         rightreleased = true;\r
1073                         }\r
1074                 }\r
1075                 mousepos += mousespeed;\r
1076         }\r
1077 \r
1078         s32 Rand(s32 min, s32 max)\r
1079         {\r
1080                 return (myrand()%(max-min+1))+min;\r
1081         }\r
1082 private:\r
1083         bool keydown[KEY_KEY_CODES_COUNT];\r
1084         v2s32 mousepos;\r
1085         v2s32 mousespeed;\r
1086         bool leftdown;\r
1087         bool rightdown;\r
1088         bool leftclicked;\r
1089         bool rightclicked;\r
1090         bool leftreleased;\r
1091         bool rightreleased;\r
1092 };\r
1093 \r
1094 void updateViewingRange(f32 frametime_in, Client *client)\r
1095 {\r
1096         if(draw_control.range_all == true)\r
1097                 return;\r
1098         \r
1099         static f32 added_frametime = 0;\r
1100         static s16 added_frames = 0;\r
1101 \r
1102         added_frametime += frametime_in;\r
1103         added_frames += 1;\r
1104 \r
1105         // Actually this counter kind of sucks because frametime is busytime\r
1106         static f32 counter = 0;\r
1107         counter -= frametime_in;\r
1108         if(counter > 0)\r
1109                 return;\r
1110         //counter = 0.1;\r
1111         counter = 0.2;\r
1112 \r
1113         /*dstream<<__FUNCTION_NAME\r
1114                         <<": Collected "<<added_frames<<" frames, total of "\r
1115                         <<added_frametime<<"s."<<std::endl;*/\r
1116         \r
1117         /*dstream<<"draw_control.blocks_drawn="\r
1118                         <<draw_control.blocks_drawn\r
1119                         <<", draw_control.blocks_would_have_drawn="\r
1120                         <<draw_control.blocks_would_have_drawn\r
1121                         <<std::endl;*/\r
1122         \r
1123         float range_min = g_settings.getS16("viewing_range_nodes_min");\r
1124         float range_max = g_settings.getS16("viewing_range_nodes_max");\r
1125         \r
1126         draw_control.wanted_min_range = range_min;\r
1127         draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;\r
1128         \r
1129         float block_draw_ratio = 1.0;\r
1130         if(draw_control.blocks_would_have_drawn != 0)\r
1131         {\r
1132                 block_draw_ratio = (float)draw_control.blocks_drawn\r
1133                         / (float)draw_control.blocks_would_have_drawn;\r
1134         }\r
1135 \r
1136         // Calculate the average frametime in the case that all wanted\r
1137         // blocks had been drawn\r
1138         f32 frametime = added_frametime / added_frames / block_draw_ratio;\r
1139         \r
1140         added_frametime = 0.0;\r
1141         added_frames = 0;\r
1142         \r
1143         float wanted_fps = g_settings.getFloat("wanted_fps");\r
1144         float wanted_frametime = 1.0 / wanted_fps;\r
1145         \r
1146         f32 wanted_frametime_change = wanted_frametime - frametime;\r
1147         //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;\r
1148         \r
1149         // If needed frametime change is small, just return\r
1150         if(fabs(wanted_frametime_change) < wanted_frametime*0.4)\r
1151         {\r
1152                 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;\r
1153                 return;\r
1154         }\r
1155 \r
1156         float range = draw_control.wanted_range;\r
1157         float new_range = range;\r
1158 \r
1159         static s16 range_old = 0;\r
1160         static f32 frametime_old = 0;\r
1161         \r
1162         float d_range = range - range_old;\r
1163         f32 d_frametime = frametime - frametime_old;\r
1164         // A sane default of 30ms per 50 nodes of range\r
1165         static f32 time_per_range = 30. / 50;\r
1166         if(d_range != 0)\r
1167         {\r
1168                 time_per_range = d_frametime / d_range;\r
1169         }\r
1170         \r
1171         // The minimum allowed calculated frametime-range derivative:\r
1172         // Practically this sets the maximum speed of changing the range.\r
1173         // The lower this value, the higher the maximum changing speed.\r
1174         // A low value here results in wobbly range (0.001)\r
1175         // A high value here results in slow changing range (0.0025)\r
1176         // SUGG: This could be dynamically adjusted so that when\r
1177         //       the camera is turning, this is lower\r
1178         //float min_time_per_range = 0.0015;\r
1179         float min_time_per_range = 0.0010;\r
1180         //float min_time_per_range = 0.05 / range;\r
1181         if(time_per_range < min_time_per_range)\r
1182         {\r
1183                 time_per_range = min_time_per_range;\r
1184                 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;\r
1185         }\r
1186         else\r
1187         {\r
1188                 //dstream<<"time_per_range="<<time_per_range<<std::endl;\r
1189         }\r
1190 \r
1191         f32 wanted_range_change = wanted_frametime_change / time_per_range;\r
1192         // Dampen the change a bit to kill oscillations\r
1193         //wanted_range_change *= 0.9;\r
1194         //wanted_range_change *= 0.75;\r
1195         wanted_range_change *= 0.5;\r
1196         //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;\r
1197 \r
1198         // If needed range change is very small, just return\r
1199         if(fabs(wanted_range_change) < 0.001)\r
1200         {\r
1201                 //dstream<<"ignoring small wanted_range_change"<<std::endl;\r
1202                 return;\r
1203         }\r
1204 \r
1205         new_range += wanted_range_change;\r
1206         //dstream<<"new_range="<<new_range/*<<std::endl*/;\r
1207         \r
1208         //float new_range_unclamped = new_range;\r
1209         if(new_range < range_min)\r
1210                 new_range = range_min;\r
1211         if(new_range > range_max)\r
1212                 new_range = range_max;\r
1213         \r
1214         /*if(new_range != new_range_unclamped)\r
1215                 dstream<<", clamped to "<<new_range<<std::endl;\r
1216         else\r
1217                 dstream<<std::endl;*/\r
1218 \r
1219         draw_control.wanted_range = new_range;\r
1220 \r
1221         range_old = new_range;\r
1222         frametime_old = frametime;\r
1223 }\r
1224 \r
1225 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,\r
1226                 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,\r
1227                 Inventory *inventory)\r
1228 {\r
1229         InventoryList *mainlist = inventory->getList("main");\r
1230         if(mainlist == NULL)\r
1231         {\r
1232                 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;\r
1233                 return;\r
1234         }\r
1235         \r
1236         s32 padding = imgsize/12;\r
1237         //s32 height = imgsize + padding*2;\r
1238         s32 width = itemcount*(imgsize+padding*2);\r
1239         \r
1240         // Position of upper left corner of bar\r
1241         v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);\r
1242         \r
1243         // Draw background color\r
1244         /*core::rect<s32> barrect(0,0,width,height);\r
1245         barrect += pos;\r
1246         video::SColor bgcolor(255,128,128,128);\r
1247         driver->draw2DRectangle(bgcolor, barrect, NULL);*/\r
1248 \r
1249         core::rect<s32> imgrect(0,0,imgsize,imgsize);\r
1250 \r
1251         for(s32 i=0; i<itemcount; i++)\r
1252         {\r
1253                 InventoryItem *item = mainlist->getItem(i);\r
1254                 \r
1255                 core::rect<s32> rect = imgrect + pos\r
1256                                 + v2s32(padding+i*(imgsize+padding*2), padding);\r
1257                 \r
1258                 if(g_selected_item == i)\r
1259                 {\r
1260                         driver->draw2DRectangle(video::SColor(255,255,0,0),\r
1261                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,\r
1262                                                         rect.LowerRightCorner + v2s32(1,1)*padding),\r
1263                                         NULL);\r
1264                 }\r
1265                 else\r
1266                 {\r
1267                         video::SColor bgcolor2(128,0,0,0);\r
1268                         driver->draw2DRectangle(bgcolor2, rect, NULL);\r
1269                 }\r
1270 \r
1271                 if(item != NULL)\r
1272                 {\r
1273                         drawInventoryItem(driver, font, item, rect, NULL);\r
1274                 }\r
1275         }\r
1276 }\r
1277 \r
1278 video::ITexture *g_map_plot_texture = NULL;\r
1279 float g_map_plot_texture_scale = 4;\r
1280 \r
1281 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,\r
1282                 Client *client)\r
1283 {\r
1284         assert(driver);\r
1285         assert(client);\r
1286 \r
1287         core::dimension2d<u32> dim(640,480);\r
1288         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);\r
1289         assert(img);\r
1290         for(u32 y=0; y<dim.Height; y++)\r
1291         for(u32 x=0; x<dim.Width; x++)\r
1292         {\r
1293                 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;\r
1294                 pf *= g_map_plot_texture_scale;\r
1295                 pf += centerpos;\r
1296                 double h = base_rock_level_2d(client->getMapSeed(), pf);\r
1297                 video::SColor c;\r
1298                 //double d1 = 50;\r
1299                 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;\r
1300                 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/\r
1301                         \r
1302                 // Screen coordinates that are based on multiples of\r
1303                 // 1000/g_map_plot_texture_scale and never negative\r
1304                 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;\r
1305                 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;\r
1306                 // Offset to center of image\r
1307                 ux -= dim.Width/2;\r
1308                 uy -= dim.Height/2;\r
1309 \r
1310                 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0\r
1311                                 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)\r
1312                         c.set(255, 255, 255, 255);\r
1313                 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0\r
1314                                 || ux % (u32)(100/g_map_plot_texture_scale) == 0)\r
1315                         c.set(255, 160, 160, 160);\r
1316                 else if(h < WATER_LEVEL - 0.5) // Water\r
1317                         c.set(255, 50, 50, 255);\r
1318 #if 0\r
1319                 else if(get_have_sand_ground(client->getMapSeed(), pf)\r
1320                                 || (h < WATER_LEVEL + 2\r
1321                                 && get_have_sand_coast(client->getMapSeed(), pf)))\r
1322                 {\r
1323                         h -= WATER_LEVEL;\r
1324                         h /= 50.0;\r
1325                         h = 1.0 - exp(-h);\r
1326 \r
1327                         video::SColor c1(255,237,201,175);\r
1328                         //video::SColor c2(255,20,20,20);\r
1329                         video::SColor c2(255,150,0,0);\r
1330                         c = c2.getInterpolated(c1, h);\r
1331                 }\r
1332                 else\r
1333                 {\r
1334                         h -= WATER_LEVEL;\r
1335                         h /= 50.0;\r
1336                         h = 1.0 - exp(-h);\r
1337 \r
1338                         video::SColor c1(255,110,185,90);\r
1339                         //video::SColor c2(255,20,20,20);\r
1340                         video::SColor c2(255,150,0,0);\r
1341                         c = c2.getInterpolated(c1, h);\r
1342                 }\r
1343 #endif\r
1344 #if 1\r
1345 #if 0\r
1346                 else if(get_have_sand_ground(client->getMapSeed(), pf))\r
1347                 {\r
1348                         h -= WATER_LEVEL;\r
1349                         h /= 20.0;\r
1350                         h = 1.0 - exp(-h);\r
1351 \r
1352                         video::SColor c1(255,237,201,175);\r
1353                         //video::SColor c2(255,20,20,20);\r
1354                         video::SColor c2(255,150,0,0);\r
1355                         c = c2.getInterpolated(c1, h);\r
1356                 }\r
1357 #endif\r
1358                 // Sand\r
1359                 else if(h < WATER_LEVEL + 2\r
1360                                 && get_have_sand_coast(client->getMapSeed(), pf))\r
1361                         c.set(255, 237, 201, 175);\r
1362 #if 1\r
1363                 else if(h < WATER_LEVEL + 10)\r
1364                         c.set(255, 50, 150, 50); // Green\r
1365                 else if(h < WATER_LEVEL + 20)\r
1366                         c.set(255, 110, 185, 50); // Yellowish green\r
1367                 else if(h < WATER_LEVEL + 40)\r
1368                         c.set(255, 180, 210, 50); // Greenish yellow\r
1369                 else if(h < WATER_LEVEL + 60)\r
1370                         c.set(255, 220, 220, 50); // Yellow\r
1371                 else if(h < WATER_LEVEL + 80)\r
1372                         c.set(255, 200, 200, 110); // Yellowish white\r
1373                 else if(h < WATER_LEVEL + 100)\r
1374                         c.set(255, 190, 190, 190); // Grey\r
1375                 else\r
1376                         c.set(255, 255, 255, 255); // White\r
1377 #endif\r
1378 #endif\r
1379 #if 0\r
1380                 else\r
1381                 {\r
1382                         h -= WATER_LEVEL;\r
1383                         h /= 20.0;\r
1384                         h = 1.0 - exp(-h);\r
1385 \r
1386                         video::SColor c1(255,200,200,50);\r
1387                         video::SColor c2(255,0,150,0);\r
1388                         c = c1.getInterpolated(c2, h);\r
1389 \r
1390                         /*u32 a = (u32)(h*255);\r
1391                         if(a > 255)\r
1392                                 a = 255;\r
1393                         a = 255-a;\r
1394                         c.set(255, a, a, a);*/\r
1395                 }\r
1396 #endif\r
1397 #if 1\r
1398                 if(h >= WATER_LEVEL - 0.5\r
1399                         && get_have_sand_ground(client->getMapSeed(), pf))\r
1400                 {\r
1401                         video::SColor c1(255,237,201,175);\r
1402                         c = c.getInterpolated(c1, 0.5);\r
1403                 }\r
1404 #endif\r
1405 #if 1\r
1406                 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);\r
1407                 if(tf > 0.001)\r
1408                 {\r
1409                         video::SColor c1(255,255,0,0);\r
1410                         c = c.getInterpolated(c1, 1.0-(0.5*tf));\r
1411                 }\r
1412 #endif\r
1413                 img->setPixel(x, y, c);\r
1414         }\r
1415         g_map_plot_texture = driver->addTexture("map_plot", img);\r
1416         img->drop();\r
1417         assert(g_map_plot_texture);\r
1418 }\r
1419 \r
1420 // Chat data\r
1421 struct ChatLine\r
1422 {\r
1423         ChatLine():\r
1424                 age(0.0)\r
1425         {\r
1426         }\r
1427         ChatLine(const std::wstring &a_text):\r
1428                 age(0.0),\r
1429                 text(a_text)\r
1430         {\r
1431         }\r
1432         float age;\r
1433         std::wstring text;\r
1434 };\r
1435 \r
1436 // These are defined global so that they're not optimized too much.\r
1437 // Can't change them to volatile.\r
1438 s16 temp16;\r
1439 f32 tempf;\r
1440 v3f tempv3f1;\r
1441 v3f tempv3f2;\r
1442 std::string tempstring;\r
1443 std::string tempstring2;\r
1444 \r
1445 void SpeedTests()\r
1446 {\r
1447         {\r
1448                 dstream<<"The following test should take around 20ms."<<std::endl;\r
1449                 TimeTaker timer("Testing std::string speed");\r
1450                 const u32 jj = 10000;\r
1451                 for(u32 j=0; j<jj; j++)\r
1452                 {\r
1453                         tempstring = "";\r
1454                         tempstring2 = "";\r
1455                         const u32 ii = 10;\r
1456                         for(u32 i=0; i<ii; i++){\r
1457                                 tempstring2 += "asd";\r
1458                         }\r
1459                         for(u32 i=0; i<ii+1; i++){\r
1460                                 tempstring += "asd";\r
1461                                 if(tempstring == tempstring2)\r
1462                                         break;\r
1463                         }\r
1464                 }\r
1465         }\r
1466         \r
1467         dstream<<"All of the following tests should take around 100ms each."\r
1468                         <<std::endl;\r
1469 \r
1470         {\r
1471                 TimeTaker timer("Testing floating-point conversion speed");\r
1472                 tempf = 0.001;\r
1473                 for(u32 i=0; i<4000000; i++){\r
1474                         temp16 += tempf;\r
1475                         tempf += 0.001;\r
1476                 }\r
1477         }\r
1478         \r
1479         {\r
1480                 TimeTaker timer("Testing floating-point vector speed");\r
1481 \r
1482                 tempv3f1 = v3f(1,2,3);\r
1483                 tempv3f2 = v3f(4,5,6);\r
1484                 for(u32 i=0; i<10000000; i++){\r
1485                         tempf += tempv3f1.dotProduct(tempv3f2);\r
1486                         tempv3f2 += v3f(7,8,9);\r
1487                 }\r
1488         }\r
1489 \r
1490         {\r
1491                 TimeTaker timer("Testing core::map speed");\r
1492                 \r
1493                 core::map<v2s16, f32> map1;\r
1494                 tempf = -324;\r
1495                 const s16 ii=300;\r
1496                 for(s16 y=0; y<ii; y++){\r
1497                         for(s16 x=0; x<ii; x++){\r
1498                                 map1.insert(v2s16(x,y), tempf);\r
1499                                 tempf += 1;\r
1500                         }\r
1501                 }\r
1502                 for(s16 y=ii-1; y>=0; y--){\r
1503                         for(s16 x=0; x<ii; x++){\r
1504                                 tempf = map1[v2s16(x,y)];\r
1505                         }\r
1506                 }\r
1507         }\r
1508 \r
1509         {\r
1510                 dstream<<"Around 5000/ms should do well here."<<std::endl;\r
1511                 TimeTaker timer("Testing mutex speed");\r
1512                 \r
1513                 JMutex m;\r
1514                 m.Init();\r
1515                 u32 n = 0;\r
1516                 u32 i = 0;\r
1517                 do{\r
1518                         n += 10000;\r
1519                         for(; i<n; i++){\r
1520                                 m.Lock();\r
1521                                 m.Unlock();\r
1522                         }\r
1523                 }\r
1524                 // Do at least 10ms\r
1525                 while(timer.getTime() < 10);\r
1526 \r
1527                 u32 dtime = timer.stop();\r
1528                 u32 per_ms = n / dtime;\r
1529                 std::cout<<"Done. "<<dtime<<"ms, "\r
1530                                 <<per_ms<<"/ms"<<std::endl;\r
1531         }\r
1532 }\r
1533 \r
1534 int main(int argc, char *argv[])\r
1535 {\r
1536         /*\r
1537                 Parse command line\r
1538         */\r
1539         \r
1540         // List all allowed options\r
1541         core::map<std::string, ValueSpec> allowed_options;\r
1542         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));\r
1543         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,\r
1544                         "Run server directly"));\r
1545         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,\r
1546                         "Load configuration from specified file"));\r
1547         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));\r
1548         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));\r
1549         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));\r
1550         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1551         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1552         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));\r
1553 #ifdef _WIN32\r
1554         allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));\r
1555 #endif\r
1556         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));\r
1557 \r
1558         Settings cmd_args;\r
1559         \r
1560         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);\r
1561 \r
1562         if(ret == false || cmd_args.getFlag("help"))\r
1563         {\r
1564                 dstream<<"Allowed options:"<<std::endl;\r
1565                 for(core::map<std::string, ValueSpec>::Iterator\r
1566                                 i = allowed_options.getIterator();\r
1567                                 i.atEnd() == false; i++)\r
1568                 {\r
1569                         dstream<<"  --"<<i.getNode()->getKey();\r
1570                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)\r
1571                         {\r
1572                         }\r
1573                         else\r
1574                         {\r
1575                                 dstream<<" <value>";\r
1576                         }\r
1577                         dstream<<std::endl;\r
1578 \r
1579                         if(i.getNode()->getValue().help != NULL)\r
1580                         {\r
1581                                 dstream<<"      "<<i.getNode()->getValue().help\r
1582                                                 <<std::endl;\r
1583                         }\r
1584                 }\r
1585 \r
1586                 return cmd_args.getFlag("help") ? 0 : 1;\r
1587         }\r
1588         \r
1589         /*\r
1590                 Low-level initialization\r
1591         */\r
1592 \r
1593         bool disable_stderr = false;\r
1594 #ifdef _WIN32\r
1595         if(cmd_args.getFlag("dstream-on-stderr") == false)\r
1596                 disable_stderr = true;\r
1597 #endif\r
1598 \r
1599         // Initialize debug streams\r
1600         debugstreams_init(disable_stderr, DEBUGFILE);\r
1601         // Initialize debug stacks\r
1602         debug_stacks_init();\r
1603 \r
1604         DSTACK(__FUNCTION_NAME);\r
1605 \r
1606         porting::signal_handler_init();\r
1607         bool &kill = *porting::signal_handler_killstatus();\r
1608         \r
1609         porting::initializePaths();\r
1610         // Create user data directory\r
1611         fs::CreateDir(porting::path_userdata);\r
1612         \r
1613         // C-style stuff initialization\r
1614         initializeMaterialProperties();\r
1615 \r
1616         // Debug handler\r
1617         BEGIN_DEBUG_EXCEPTION_HANDLER\r
1618 \r
1619         // Print startup message\r
1620         dstream<<DTIME<<"minetest-c55"\r
1621                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST\r
1622                         <<", "<<BUILD_INFO\r
1623                         <<std::endl;\r
1624         \r
1625         /*\r
1626                 Basic initialization\r
1627         */\r
1628 \r
1629         // Initialize default settings\r
1630         set_default_settings();\r
1631         \r
1632         // Set locale. This is for forcing '.' as the decimal point.\r
1633         std::locale::global(std::locale("C"));\r
1634         // This enables printing all characters in bitmap font\r
1635         setlocale(LC_CTYPE, "en_US");\r
1636 \r
1637         // Initialize sockets\r
1638         sockets_init();\r
1639         atexit(sockets_cleanup);\r
1640         \r
1641         /*\r
1642                 Initialization\r
1643         */\r
1644 \r
1645         /*\r
1646                 Read config file\r
1647         */\r
1648         \r
1649         // Path of configuration file in use\r
1650         std::string configpath = "";\r
1651         \r
1652         if(cmd_args.exists("config"))\r
1653         {\r
1654                 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());\r
1655                 if(r == false)\r
1656                 {\r
1657                         dstream<<"Could not read configuration from \""\r
1658                                         <<cmd_args.get("config")<<"\""<<std::endl;\r
1659                         return 1;\r
1660                 }\r
1661                 configpath = cmd_args.get("config");\r
1662         }\r
1663         else\r
1664         {\r
1665                 core::array<std::string> filenames;\r
1666                 filenames.push_back(porting::path_userdata + "/minetest.conf");\r
1667 #ifdef RUN_IN_PLACE\r
1668                 filenames.push_back(porting::path_userdata + "/../minetest.conf");\r
1669 #endif\r
1670 \r
1671                 for(u32 i=0; i<filenames.size(); i++)\r
1672                 {\r
1673                         bool r = g_settings.readConfigFile(filenames[i].c_str());\r
1674                         if(r)\r
1675                         {\r
1676                                 configpath = filenames[i];\r
1677                                 break;\r
1678                         }\r
1679                 }\r
1680                 \r
1681                 // If no path found, use the first one (menu creates the file)\r
1682                 if(configpath == "")\r
1683                         configpath = filenames[0];\r
1684         }\r
1685 \r
1686         // Initialize random seed\r
1687         srand(time(0));\r
1688         mysrand(time(0));\r
1689 \r
1690         /*\r
1691                 Pre-initialize some stuff with a dummy irrlicht wrapper.\r
1692 \r
1693                 These are needed for unit tests at least.\r
1694         */\r
1695         \r
1696         // Initial call with g_texturesource not set.\r
1697         init_mapnode();\r
1698 \r
1699         /*\r
1700                 Run unit tests\r
1701         */\r
1702 \r
1703         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)\r
1704                         || cmd_args.getFlag("enable-unittests") == true)\r
1705         {\r
1706                 run_tests();\r
1707         }\r
1708         \r
1709         /*for(s16 y=-100; y<100; y++)\r
1710         for(s16 x=-100; x<100; x++)\r
1711         {\r
1712                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;\r
1713         }\r
1714         return 0;*/\r
1715         \r
1716         /*\r
1717                 Some parameters\r
1718         */\r
1719 \r
1720         // Port\r
1721         u16 port = 30000;\r
1722         if(cmd_args.exists("port"))\r
1723                 port = cmd_args.getU16("port");\r
1724         else if(cmd_args.exists("port"))\r
1725                 port = g_settings.getU16("port");\r
1726         \r
1727         // Map directory\r
1728         std::string map_dir = porting::path_userdata+"/map";\r
1729         if(cmd_args.exists("map-dir"))\r
1730                 map_dir = cmd_args.get("map-dir");\r
1731         else if(g_settings.exists("map-dir"))\r
1732                 map_dir = g_settings.get("map-dir");\r
1733         \r
1734         // Run dedicated server if asked to\r
1735         if(cmd_args.getFlag("server"))\r
1736         {\r
1737                 DSTACK("Dedicated server branch");\r
1738 \r
1739                 // Create server\r
1740                 Server server(map_dir.c_str());\r
1741                 server.start(port);\r
1742                 \r
1743                 // Run server\r
1744                 dedicated_server_loop(server, kill);\r
1745 \r
1746                 return 0;\r
1747         }\r
1748 \r
1749         /*\r
1750                 More parameters\r
1751         */\r
1752         \r
1753         // Address to connect to\r
1754         std::string address = "";\r
1755         \r
1756         if(cmd_args.exists("address"))\r
1757         {\r
1758                 address = cmd_args.get("address");\r
1759         }\r
1760         else\r
1761         {\r
1762                 address = g_settings.get("address");\r
1763         }\r
1764         \r
1765         std::string playername = g_settings.get("name");\r
1766 \r
1767         // Resolution selection\r
1768         \r
1769         bool fullscreen = false;\r
1770         u16 screenW = g_settings.getU16("screenW");\r
1771         u16 screenH = g_settings.getU16("screenH");\r
1772 \r
1773         // Determine driver\r
1774 \r
1775         video::E_DRIVER_TYPE driverType;\r
1776         \r
1777         std::string driverstring = g_settings.get("video_driver");\r
1778 \r
1779         if(driverstring == "null")\r
1780                 driverType = video::EDT_NULL;\r
1781         else if(driverstring == "software")\r
1782                 driverType = video::EDT_SOFTWARE;\r
1783         else if(driverstring == "burningsvideo")\r
1784                 driverType = video::EDT_BURNINGSVIDEO;\r
1785         else if(driverstring == "direct3d8")\r
1786                 driverType = video::EDT_DIRECT3D8;\r
1787         else if(driverstring == "direct3d9")\r
1788                 driverType = video::EDT_DIRECT3D9;\r
1789         else if(driverstring == "opengl")\r
1790                 driverType = video::EDT_OPENGL;\r
1791         else\r
1792         {\r
1793                 dstream<<"WARNING: Invalid video_driver specified; defaulting "\r
1794                                 "to opengl"<<std::endl;\r
1795                 driverType = video::EDT_OPENGL;\r
1796         }\r
1797 \r
1798         // create device and exit if creation failed\r
1799 \r
1800         MyEventReceiver receiver;\r
1801 \r
1802         IrrlichtDevice *device;\r
1803         device = createDevice(driverType,\r
1804                         core::dimension2d<u32>(screenW, screenH),\r
1805                         16, fullscreen, false, false, &receiver);\r
1806 \r
1807         if (device == 0)\r
1808                 return 1; // could not create selected driver.\r
1809         \r
1810         g_device = device;\r
1811         g_irrlicht = new IrrlichtWrapper(device);\r
1812         TextureSource *texturesource = new TextureSource(device);\r
1813         g_texturesource = texturesource;\r
1814 \r
1815         /*\r
1816                 Speed tests (done after irrlicht is loaded to get timer)\r
1817         */\r
1818         if(cmd_args.getFlag("speedtests"))\r
1819         {\r
1820                 dstream<<"Running speed tests"<<std::endl;\r
1821                 SpeedTests();\r
1822                 return 0;\r
1823         }\r
1824         \r
1825         device->setResizable(true);\r
1826 \r
1827         bool random_input = g_settings.getBool("random_input")\r
1828                         || cmd_args.getFlag("random-input");\r
1829         if(random_input)\r
1830                 g_input = new RandomInputHandler();\r
1831         else\r
1832                 g_input = new RealInputHandler(device, &receiver);\r
1833         \r
1834         /*\r
1835                 Continue initialization\r
1836         */\r
1837 \r
1838         video::IVideoDriver* driver = device->getVideoDriver();\r
1839 \r
1840         /*\r
1841                 This changes the minimum allowed number of vertices in a VBO.\r
1842                 Default is 500.\r
1843         */\r
1844         //driver->setMinHardwareBufferVertexCount(50);\r
1845 \r
1846         scene::ISceneManager* smgr = device->getSceneManager();\r
1847 \r
1848         guienv = device->getGUIEnvironment();\r
1849         gui::IGUISkin* skin = guienv->getSkin();\r
1850         gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());\r
1851         if(font)\r
1852                 skin->setFont(font);\r
1853         else\r
1854                 dstream<<"WARNING: Font file was not found."\r
1855                                 " Using default font."<<std::endl;\r
1856         // If font was not found, this will get us one\r
1857         font = skin->getFont();\r
1858         assert(font);\r
1859 \r
1860         u32 text_height = font->getDimension(L"Hello, world!").Height;\r
1861         dstream<<"text_height="<<text_height<<std::endl;\r
1862 \r
1863         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
1864         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
1865         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
1866         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));\r
1867         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));\r
1868         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));\r
1869         \r
1870         /*\r
1871                 Preload some textures and stuff\r
1872         */\r
1873 \r
1874         init_content_inventory_texture_paths();\r
1875         init_mapnode(); // Second call with g_texturesource set\r
1876         init_mineral();\r
1877 \r
1878         /*\r
1879                 GUI stuff\r
1880         */\r
1881 \r
1882         /*\r
1883                 We need some kind of a root node to be able to add\r
1884                 custom gui elements directly on the screen.\r
1885                 Otherwise they won't be automatically drawn.\r
1886         */\r
1887         guiroot = guienv->addStaticText(L"",\r
1888                         core::rect<s32>(0, 0, 10000, 10000));\r
1889         \r
1890         // First line of debug text\r
1891         gui::IGUIStaticText *guitext = guienv->addStaticText(\r
1892                         L"",\r
1893                         core::rect<s32>(5, 5, 795, 5+text_height),\r
1894                         false, false);\r
1895         // Second line of debug text\r
1896         gui::IGUIStaticText *guitext2 = guienv->addStaticText(\r
1897                         L"",\r
1898                         core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),\r
1899                         false, false);\r
1900         \r
1901         // At the middle of the screen\r
1902         // Object infos are shown in this\r
1903         gui::IGUIStaticText *guitext_info = guienv->addStaticText(\r
1904                         L"",\r
1905                         core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),\r
1906                         false, false);\r
1907         \r
1908         // Chat text\r
1909         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(\r
1910                         L"",\r
1911                         core::rect<s32>(0,0,0,0),\r
1912                         false, false); // Disable word wrap as of now\r
1913                         //false, true);\r
1914         //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));\r
1915         core::list<ChatLine> chat_lines;\r
1916         \r
1917         /*\r
1918                 If an error occurs, this is set to something and the\r
1919                 menu-game loop is restarted. It is then displayed before\r
1920                 the menu.\r
1921         */\r
1922         std::wstring error_message = L"";\r
1923         \r
1924         /*\r
1925                 Menu-game loop\r
1926         */\r
1927         while(g_device->run() && kill == false)\r
1928         {\r
1929         \r
1930         // This is used for catching disconnects\r
1931         try\r
1932         {\r
1933         \r
1934         /*\r
1935                 Out-of-game menu loop.\r
1936 \r
1937                 Loop quits when menu returns proper parameters.\r
1938         */\r
1939         while(kill == false)\r
1940         {\r
1941                 // Cursor can be non-visible when coming from the game\r
1942                 device->getCursorControl()->setVisible(true);\r
1943                 // Some stuff are left to scene manager when coming from the game\r
1944                 // (map at least?)\r
1945                 smgr->clear();\r
1946                 // Reset or hide the debug gui texts\r
1947                 guitext->setText(L"Minetest-c55");\r
1948                 guitext2->setVisible(false);\r
1949                 guitext_info->setVisible(false);\r
1950                 guitext_chat->setVisible(false);\r
1951                 \r
1952                 // Initialize menu data\r
1953                 MainMenuData menudata;\r
1954                 menudata.address = narrow_to_wide(address);\r
1955                 menudata.name = narrow_to_wide(playername);\r
1956                 menudata.port = narrow_to_wide(itos(port));\r
1957                 menudata.creative_mode = g_settings.getBool("creative_mode");\r
1958 \r
1959                 GUIMainMenu *menu =\r
1960                                 new GUIMainMenu(guienv, guiroot, -1, \r
1961                                         &g_menumgr, &menudata, &g_gamecallback);\r
1962                 menu->allowFocusRemoval(true);\r
1963 \r
1964                 if(error_message != L"")\r
1965                 {\r
1966                         GUIMessageMenu *menu2 =\r
1967                                         new GUIMessageMenu(guienv, guiroot, -1, \r
1968                                                 &g_menumgr, error_message.c_str());\r
1969                         menu2->drop();\r
1970                         error_message = L"";\r
1971                 }\r
1972 \r
1973                 video::IVideoDriver* driver = g_device->getVideoDriver();\r
1974                 \r
1975                 dstream<<"Created main menu"<<std::endl;\r
1976 \r
1977                 while(g_device->run() && kill == false)\r
1978                 {\r
1979                         // Run global IrrlichtWrapper's main thread processing stuff\r
1980                         g_irrlicht->Run();\r
1981                         \r
1982                         if(menu->getStatus() == true)\r
1983                                 break;\r
1984 \r
1985                         //driver->beginScene(true, true, video::SColor(255,0,0,0));\r
1986                         driver->beginScene(true, true, video::SColor(255,128,128,128));\r
1987                         guienv->drawAll();\r
1988                         driver->endScene();\r
1989                 }\r
1990                 \r
1991                 // Break out of menu-game loop to shut down cleanly\r
1992                 if(g_device->run() == false || kill == true)\r
1993                         break;\r
1994                 \r
1995                 dstream<<"Dropping main menu"<<std::endl;\r
1996 \r
1997                 menu->drop();\r
1998                 \r
1999                 // Delete map if requested\r
2000                 if(menudata.delete_map)\r
2001                 {\r
2002                         bool r = fs::RecursiveDeleteContent(map_dir);\r
2003                         if(r == false)\r
2004                                 error_message = L"Delete failed";\r
2005                         continue;\r
2006                 }\r
2007 \r
2008                 playername = wide_to_narrow(menudata.name);\r
2009                 address = wide_to_narrow(menudata.address);\r
2010                 port = stoi(wide_to_narrow(menudata.port));\r
2011                 g_settings.set("creative_mode", itos(menudata.creative_mode));\r
2012                 \r
2013                 // Check for valid parameters, restart menu if invalid.\r
2014                 if(playername == "")\r
2015                 {\r
2016                         error_message = L"Name required.";\r
2017                         continue;\r
2018                 }\r
2019                 \r
2020                 // Save settings\r
2021                 g_settings.set("name", playername);\r
2022                 g_settings.set("address", address);\r
2023                 g_settings.set("port", itos(port));\r
2024                 // Update configuration file\r
2025                 if(configpath != "")\r
2026                         g_settings.updateConfigFile(configpath.c_str());\r
2027         \r
2028                 // Continue to game\r
2029                 break;\r
2030         }\r
2031         \r
2032         // Break out of menu-game loop to shut down cleanly\r
2033         if(g_device->run() == false)\r
2034                 break;\r
2035 \r
2036         /*\r
2037                 Make a scope here so that the client and the server and other\r
2038                 stuff gets removed when disconnected or the irrlicht device\r
2039                 is removed.\r
2040         */\r
2041         {\r
2042 \r
2043         // This is set to true at the end of the scope\r
2044         g_irrlicht->Shutdown(false);\r
2045 \r
2046         /*\r
2047                 Draw "Loading" screen\r
2048         */\r
2049         const wchar_t *text = L"Loading and connecting...";\r
2050         core::vector2d<s32> center(screenW/2, screenH/2);\r
2051         core::vector2d<s32> textsize(300, text_height);\r
2052         core::rect<s32> textrect(center - textsize/2, center + textsize/2);\r
2053 \r
2054         gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(\r
2055                         text, textrect, false, false);\r
2056         gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);\r
2057 \r
2058         driver->beginScene(true, true, video::SColor(255,0,0,0));\r
2059         guienv->drawAll();\r
2060         driver->endScene();\r
2061 \r
2062         std::cout<<DTIME<<"Creating server and client"<<std::endl;\r
2063         \r
2064         /*\r
2065                 Create server.\r
2066                 SharedPtr will delete it when it goes out of scope.\r
2067         */\r
2068         SharedPtr<Server> server;\r
2069         if(address == ""){\r
2070                 server = new Server(map_dir);\r
2071                 server->start(port);\r
2072         }\r
2073         \r
2074         /*\r
2075                 Create client\r
2076         */\r
2077 \r
2078         Client client(device, playername.c_str(), draw_control);\r
2079                         \r
2080         g_client = &client;\r
2081         \r
2082         Address connect_address(0,0,0,0, port);\r
2083         try{\r
2084                 if(address == "")\r
2085                         //connect_address.Resolve("localhost");\r
2086                         connect_address.setAddress(127,0,0,1);\r
2087                 else\r
2088                         connect_address.Resolve(address.c_str());\r
2089         }\r
2090         catch(ResolveError &e)\r
2091         {\r
2092                 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;\r
2093                 //return 0;\r
2094                 error_message = L"Couldn't resolve address";\r
2095                 gui_loadingtext->remove();\r
2096                 continue;\r
2097         }\r
2098         \r
2099         dstream<<DTIME<<"Connecting to server at ";\r
2100         connect_address.print(&dstream);\r
2101         dstream<<std::endl;\r
2102         client.connect(connect_address);\r
2103         \r
2104         try{\r
2105                 while(client.connectedAndInitialized() == false)\r
2106                 {\r
2107                         // Update screen\r
2108                         driver->beginScene(true, true, video::SColor(255,0,0,0));\r
2109                         guienv->drawAll();\r
2110                         driver->endScene();\r
2111 \r
2112                         // Update client and server\r
2113 \r
2114                         client.step(0.1);\r
2115 \r
2116                         if(server != NULL)\r
2117                                 server->step(0.1);\r
2118                         \r
2119                         // Delay a bit\r
2120                         sleep_ms(100);\r
2121                 }\r
2122         }\r
2123         catch(con::PeerNotFoundException &e)\r
2124         {\r
2125                 std::cout<<DTIME<<"Timed out."<<std::endl;\r
2126                 //return 0;\r
2127                 error_message = L"Connection timed out.";\r
2128                 gui_loadingtext->remove();\r
2129                 continue;\r
2130         }\r
2131 \r
2132         /*\r
2133                 Create skybox\r
2134         */\r
2135         /*scene::ISceneNode* skybox;\r
2136         skybox = smgr->addSkyBoxSceneNode(\r
2137                 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),\r
2138                 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),\r
2139                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
2140                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
2141                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
2142                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/\r
2143         \r
2144         /*\r
2145                 Create the camera node\r
2146         */\r
2147 \r
2148         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(\r
2149                 0, // Camera parent\r
2150                 v3f(BS*100, BS*2, BS*100), // Look from\r
2151                 v3f(BS*100+1, BS*2, BS*100), // Look to\r
2152                 -1 // Camera ID\r
2153         );\r
2154 \r
2155         if(camera == NULL)\r
2156                 return 1;\r
2157 \r
2158         //video::SColor skycolor = video::SColor(255,90,140,200);\r
2159         //video::SColor skycolor = video::SColor(255,166,202,244);\r
2160         video::SColor skycolor = video::SColor(255,120,185,244);\r
2161 \r
2162         camera->setFOV(FOV_ANGLE);\r
2163 \r
2164         // Just so big a value that everything rendered is visible\r
2165         camera->setFarValue(100000*BS);\r
2166         \r
2167         /*\r
2168                 Lighting test code. Doesn't quite work this way.\r
2169                 The CPU-computed lighting is good.\r
2170         */\r
2171 \r
2172         /*\r
2173         smgr->addLightSceneNode(NULL,\r
2174                 v3f(0, BS*1000000, 0),\r
2175                 video::SColorf(0.3,0.3,0.3),\r
2176                 BS*10000000);\r
2177 \r
2178         smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));\r
2179 \r
2180         scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,\r
2181                         v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);\r
2182         */\r
2183 \r
2184         f32 camera_yaw = 0; // "right/left"\r
2185         f32 camera_pitch = 0; // "up/down"\r
2186 \r
2187         /*\r
2188                 Move into game\r
2189         */\r
2190         \r
2191         gui_loadingtext->remove();\r
2192 \r
2193         /*\r
2194                 Add some gui stuff\r
2195         */\r
2196 \r
2197         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
2198                         (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/\r
2199         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
2200                         (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/\r
2201         \r
2202         // Test the text input system\r
2203         /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,\r
2204                         NULL))->drop();*/\r
2205         /*GUIMessageMenu *menu =\r
2206                         new GUIMessageMenu(guienv, guiroot, -1, \r
2207                                 &g_menumgr,\r
2208                                 L"Asd");\r
2209         menu->drop();*/\r
2210         \r
2211         // Launch pause menu\r
2212         (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,\r
2213                         &g_menumgr))->drop();\r
2214         \r
2215         // Enable texts\r
2216         guitext2->setVisible(true);\r
2217         guitext_info->setVisible(true);\r
2218         guitext_chat->setVisible(true);\r
2219 \r
2220         //s32 guitext_chat_pad_bottom = 70;\r
2221 \r
2222         v2u32 screensize(0,0);\r
2223         v2u32 last_screensize(0,0);\r
2224         \r
2225         /*\r
2226                 Some statistics are collected in these\r
2227         */\r
2228         u32 drawtime = 0;\r
2229         u32 beginscenetime = 0;\r
2230         u32 scenetime = 0;\r
2231         u32 endscenetime = 0;\r
2232         \r
2233         // A test\r
2234         //throw con::PeerNotFoundException("lol");\r
2235 \r
2236         core::list<float> frametime_log;\r
2237 \r
2238         /*\r
2239                 Main loop\r
2240         */\r
2241 \r
2242         bool first_loop_after_window_activation = true;\r
2243 \r
2244         // Time is in milliseconds\r
2245         // NOTE: getRealTime() causes strange problems in wine (imprecision?)\r
2246         // NOTE: So we have to use getTime() and call run()s between them\r
2247         u32 lasttime = device->getTimer()->getTime();\r
2248 \r
2249         while(device->run() && kill == false)\r
2250         {\r
2251                 if(g_disconnect_requested)\r
2252                 {\r
2253                         g_disconnect_requested = false;\r
2254                         break;\r
2255                 }\r
2256 \r
2257                 /*\r
2258                         Run global IrrlichtWrapper's main thread processing stuff\r
2259                 */\r
2260                 g_irrlicht->Run();\r
2261 \r
2262                 /*\r
2263                         Process TextureSource's queue\r
2264                 */\r
2265                 texturesource->processQueue();\r
2266 \r
2267                 /*\r
2268                         Random calculations\r
2269                 */\r
2270                 last_screensize = screensize;\r
2271                 screensize = driver->getScreenSize();\r
2272                 v2s32 displaycenter(screensize.X/2,screensize.Y/2);\r
2273                 //bool screensize_changed = screensize != last_screensize;\r
2274                 \r
2275                 // Hilight boxes collected during the loop and displayed\r
2276                 core::list< core::aabbox3d<f32> > hilightboxes;\r
2277                 \r
2278                 // Info text\r
2279                 std::wstring infotext;\r
2280 \r
2281                 // When screen size changes, update positions and sizes of stuff\r
2282                 /*if(screensize_changed)\r
2283                 {\r
2284                         v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);\r
2285                         quick_inventory->updatePosition(pos);\r
2286                 }*/\r
2287 \r
2288                 //TimeTaker //timer1("//timer1");\r
2289                 \r
2290                 // Time of frame without fps limit\r
2291                 float busytime;\r
2292                 u32 busytime_u32;\r
2293                 {\r
2294                         // not using getRealTime is necessary for wine\r
2295                         u32 time = device->getTimer()->getTime();\r
2296                         if(time > lasttime)\r
2297                                 busytime_u32 = time - lasttime;\r
2298                         else\r
2299                                 busytime_u32 = 0;\r
2300                         busytime = busytime_u32 / 1000.0;\r
2301                 }\r
2302 \r
2303                 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;\r
2304         \r
2305                 // Absolutelu necessary for wine!\r
2306                 device->run();\r
2307 \r
2308                 /*\r
2309                         Viewing range\r
2310                 */\r
2311                 \r
2312                 updateViewingRange(busytime, &client);\r
2313                 \r
2314                 /*\r
2315                         FPS limiter\r
2316                 */\r
2317 \r
2318                 {\r
2319                         float fps_max = g_settings.getFloat("fps_max");\r
2320                         u32 frametime_min = 1000./fps_max;\r
2321                         \r
2322                         if(busytime_u32 < frametime_min)\r
2323                         {\r
2324                                 u32 sleeptime = frametime_min - busytime_u32;\r
2325                                 device->sleep(sleeptime);\r
2326                         }\r
2327                 }\r
2328 \r
2329                 // Absolutelu necessary for wine!\r
2330                 device->run();\r
2331 \r
2332                 /*\r
2333                         Time difference calculation\r
2334                 */\r
2335                 f32 dtime; // in seconds\r
2336                 \r
2337                 u32 time = device->getTimer()->getTime();\r
2338                 if(time > lasttime)\r
2339                         dtime = (time - lasttime) / 1000.0;\r
2340                 else\r
2341                         dtime = 0;\r
2342                 lasttime = time;\r
2343 \r
2344                 /*\r
2345                         Log frametime for visualization\r
2346                 */\r
2347                 frametime_log.push_back(dtime);\r
2348                 if(frametime_log.size() > 100)\r
2349                 {\r
2350                         core::list<float>::Iterator i = frametime_log.begin();\r
2351                         frametime_log.erase(i);\r
2352                 }\r
2353 \r
2354                 /*\r
2355                         Visualize frametime in terminal\r
2356                 */\r
2357                 /*for(u32 i=0; i<dtime*400; i++)\r
2358                         std::cout<<"X";\r
2359                 std::cout<<std::endl;*/\r
2360 \r
2361                 /*\r
2362                         Time average and jitter calculation\r
2363                 */\r
2364 \r
2365                 static f32 dtime_avg1 = 0.0;\r
2366                 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;\r
2367                 f32 dtime_jitter1 = dtime - dtime_avg1;\r
2368 \r
2369                 static f32 dtime_jitter1_max_sample = 0.0;\r
2370                 static f32 dtime_jitter1_max_fraction = 0.0;\r
2371                 {\r
2372                         static f32 jitter1_max = 0.0;\r
2373                         static f32 counter = 0.0;\r
2374                         if(dtime_jitter1 > jitter1_max)\r
2375                                 jitter1_max = dtime_jitter1;\r
2376                         counter += dtime;\r
2377                         if(counter > 0.0)\r
2378                         {\r
2379                                 counter -= 3.0;\r
2380                                 dtime_jitter1_max_sample = jitter1_max;\r
2381                                 dtime_jitter1_max_fraction\r
2382                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);\r
2383                                 jitter1_max = 0.0;\r
2384                         }\r
2385                 }\r
2386                 \r
2387                 /*\r
2388                         Busytime average and jitter calculation\r
2389                 */\r
2390 \r
2391                 static f32 busytime_avg1 = 0.0;\r
2392                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;\r
2393                 f32 busytime_jitter1 = busytime - busytime_avg1;\r
2394                 \r
2395                 static f32 busytime_jitter1_max_sample = 0.0;\r
2396                 static f32 busytime_jitter1_min_sample = 0.0;\r
2397                 {\r
2398                         static f32 jitter1_max = 0.0;\r
2399                         static f32 jitter1_min = 0.0;\r
2400                         static f32 counter = 0.0;\r
2401                         if(busytime_jitter1 > jitter1_max)\r
2402                                 jitter1_max = busytime_jitter1;\r
2403                         if(busytime_jitter1 < jitter1_min)\r
2404                                 jitter1_min = busytime_jitter1;\r
2405                         counter += dtime;\r
2406                         if(counter > 0.0){\r
2407                                 counter -= 3.0;\r
2408                                 busytime_jitter1_max_sample = jitter1_max;\r
2409                                 busytime_jitter1_min_sample = jitter1_min;\r
2410                                 jitter1_max = 0.0;\r
2411                                 jitter1_min = 0.0;\r
2412                         }\r
2413                 }\r
2414                 \r
2415                 /*\r
2416                         Debug info for client\r
2417                 */\r
2418                 {\r
2419                         static float counter = 0.0;\r
2420                         counter -= dtime;\r
2421                         if(counter < 0)\r
2422                         {\r
2423                                 counter = 30.0;\r
2424                                 client.printDebugInfo(std::cout);\r
2425                         }\r
2426                 }\r
2427 \r
2428                 /*\r
2429                         Input handler step()\r
2430                 */\r
2431                 g_input->step(dtime);\r
2432 \r
2433                 /*\r
2434                         Misc. stuff\r
2435                 */\r
2436 \r
2437                 /*\r
2438                         Player speed control\r
2439                 */\r
2440                 \r
2441                 {\r
2442                         /*bool a_up,\r
2443                         bool a_down,\r
2444                         bool a_left,\r
2445                         bool a_right,\r
2446                         bool a_jump,\r
2447                         bool a_superspeed,\r
2448                         bool a_sneak,\r
2449                         float a_pitch,\r
2450                         float a_yaw*/\r
2451                         PlayerControl control(\r
2452                                 g_input->isKeyDown(irr::KEY_KEY_W),\r
2453                                 g_input->isKeyDown(irr::KEY_KEY_S),\r
2454                                 g_input->isKeyDown(irr::KEY_KEY_A),\r
2455                                 g_input->isKeyDown(irr::KEY_KEY_D),\r
2456                                 g_input->isKeyDown(irr::KEY_SPACE),\r
2457                                 g_input->isKeyDown(irr::KEY_KEY_E),\r
2458                                 g_input->isKeyDown(irr::KEY_LSHIFT)\r
2459                                                 || g_input->isKeyDown(irr::KEY_RSHIFT),\r
2460                                 camera_pitch,\r
2461                                 camera_yaw\r
2462                         );\r
2463                         client.setPlayerControl(control);\r
2464                 }\r
2465 \r
2466                 /*\r
2467                         Process environment\r
2468                 */\r
2469                 \r
2470                 {\r
2471                         //TimeTaker timer("client.step(dtime)");\r
2472                         client.step(dtime);\r
2473                         //client.step(dtime_avg1);\r
2474                 }\r
2475 \r
2476                 if(server != NULL)\r
2477                 {\r
2478                         //TimeTaker timer("server->step(dtime)");\r
2479                         server->step(dtime);\r
2480                 }\r
2481 \r
2482                 v3f player_position = client.getPlayerPosition();\r
2483                 \r
2484                 //TimeTaker //timer2("//timer2");\r
2485 \r
2486                 /*\r
2487                         Mouse and camera control\r
2488                 */\r
2489                 \r
2490                 if((device->isWindowActive() && noMenuActive()) || random_input)\r
2491                 {\r
2492                         if(!random_input)\r
2493                                 device->getCursorControl()->setVisible(false);\r
2494 \r
2495                         if(first_loop_after_window_activation){\r
2496                                 //std::cout<<"window active, first loop"<<std::endl;\r
2497                                 first_loop_after_window_activation = false;\r
2498                         }\r
2499                         else{\r
2500                                 s32 dx = g_input->getMousePos().X - displaycenter.X;\r
2501                                 s32 dy = g_input->getMousePos().Y - displaycenter.Y;\r
2502                                 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;\r
2503                                 camera_yaw -= dx*0.2;\r
2504                                 camera_pitch += dy*0.2;\r
2505                                 if(camera_pitch < -89.5) camera_pitch = -89.5;\r
2506                                 if(camera_pitch > 89.5) camera_pitch = 89.5;\r
2507                         }\r
2508                         g_input->setMousePos(displaycenter.X, displaycenter.Y);\r
2509                 }\r
2510                 else{\r
2511                         device->getCursorControl()->setVisible(true);\r
2512 \r
2513                         //std::cout<<"window inactive"<<std::endl;\r
2514                         first_loop_after_window_activation = true;\r
2515                 }\r
2516 \r
2517                 camera_yaw = wrapDegrees(camera_yaw);\r
2518                 camera_pitch = wrapDegrees(camera_pitch);\r
2519                 \r
2520                 v3f camera_direction = v3f(0,0,1);\r
2521                 camera_direction.rotateYZBy(camera_pitch);\r
2522                 camera_direction.rotateXZBy(camera_yaw);\r
2523                 \r
2524                 // This is at the height of the eyes of the current figure\r
2525                 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);\r
2526                 // This is more like in minecraft\r
2527                 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);\r
2528 \r
2529                 camera->setPosition(camera_position);\r
2530                 // *100.0 helps in large map coordinates\r
2531                 camera->setTarget(camera_position + camera_direction * 100.0);\r
2532 \r
2533                 if(FIELD_OF_VIEW_TEST){\r
2534                         client.updateCamera(v3f(0,0,0), v3f(0,0,1));\r
2535                 }\r
2536                 else{\r
2537                         //TimeTaker timer("client.updateCamera");\r
2538                         client.updateCamera(camera_position, camera_direction);\r
2539                 }\r
2540                 \r
2541                 //timer2.stop();\r
2542                 //TimeTaker //timer3("//timer3");\r
2543 \r
2544                 /*\r
2545                         Calculate what block is the crosshair pointing to\r
2546                 */\r
2547                 \r
2548                 //u32 t1 = device->getTimer()->getRealTime();\r
2549                 \r
2550                 //f32 d = 4; // max. distance\r
2551                 f32 d = 4; // max. distance\r
2552                 core::line3d<f32> shootline(camera_position,\r
2553                                 camera_position + camera_direction * BS * (d+1));\r
2554 \r
2555                 MapBlockObject *selected_object = client.getSelectedObject\r
2556                                 (d*BS, camera_position, shootline);\r
2557 \r
2558                 /*\r
2559                         If it's pointing to a MapBlockObject\r
2560                 */\r
2561 \r
2562                 if(selected_object != NULL)\r
2563                 {\r
2564                         //dstream<<"Client returned selected_object != NULL"<<std::endl;\r
2565 \r
2566                         core::aabbox3d<f32> box_on_map\r
2567                                         = selected_object->getSelectionBoxOnMap();\r
2568 \r
2569                         hilightboxes.push_back(box_on_map);\r
2570 \r
2571                         infotext = narrow_to_wide(selected_object->infoText());\r
2572 \r
2573                         if(g_input->getLeftClicked())\r
2574                         {\r
2575                                 std::cout<<DTIME<<"Left-clicked object"<<std::endl;\r
2576                                 client.clickObject(0, selected_object->getBlock()->getPos(),\r
2577                                                 selected_object->getId(), g_selected_item);\r
2578                         }\r
2579                         else if(g_input->getRightClicked())\r
2580                         {\r
2581                                 std::cout<<DTIME<<"Right-clicked object"<<std::endl;\r
2582                                 /*\r
2583                                         Check if we want to modify the object ourselves\r
2584                                 */\r
2585                                 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)\r
2586                                 {\r
2587                                         dstream<<"Sign object right-clicked"<<std::endl;\r
2588                                         \r
2589                                         if(random_input == false)\r
2590                                         {\r
2591                                                 // Get a new text for it\r
2592 \r
2593                                                 TextDest *dest = new TextDestSign(\r
2594                                                                 selected_object->getBlock()->getPos(),\r
2595                                                                 selected_object->getId(),\r
2596                                                                 &client);\r
2597 \r
2598                                                 SignObject *sign_object = (SignObject*)selected_object;\r
2599 \r
2600                                                 std::wstring wtext =\r
2601                                                                 narrow_to_wide(sign_object->getText());\r
2602 \r
2603                                                 (new GUITextInputMenu(guienv, guiroot, -1,\r
2604                                                                 &g_menumgr, dest,\r
2605                                                                 wtext))->drop();\r
2606                                         }\r
2607                                 }\r
2608                                 /*\r
2609                                         Otherwise pass the event to the server as-is\r
2610                                 */\r
2611                                 else\r
2612                                 {\r
2613                                         client.clickObject(1, selected_object->getBlock()->getPos(),\r
2614                                                         selected_object->getId(), g_selected_item);\r
2615                                 }\r
2616                         }\r
2617                 }\r
2618                 else // selected_object == NULL\r
2619                 {\r
2620 \r
2621                 /*\r
2622                         Find out which node we are pointing at\r
2623                 */\r
2624                 \r
2625                 bool nodefound = false;\r
2626                 v3s16 nodepos;\r
2627                 v3s16 neighbourpos;\r
2628                 core::aabbox3d<f32> nodehilightbox;\r
2629                 f32 mindistance = BS * 1001;\r
2630                 \r
2631                 v3s16 pos_i = floatToInt(player_position, BS);\r
2632 \r
2633                 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
2634                                 <<std::endl;*/\r
2635 \r
2636                 s16 a = d;\r
2637                 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);\r
2638                 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);\r
2639                 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);\r
2640                 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);\r
2641                 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);\r
2642                 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);\r
2643                 \r
2644                 for(s16 y = ystart; y <= yend; y++)\r
2645                 for(s16 z = zstart; z <= zend; z++)\r
2646                 for(s16 x = xstart; x <= xend; x++)\r
2647                 {\r
2648                         MapNode n;\r
2649                         try\r
2650                         {\r
2651                                 n = client.getNode(v3s16(x,y,z));\r
2652                                 if(content_pointable(n.d) == false)\r
2653                                         continue;\r
2654                         }\r
2655                         catch(InvalidPositionException &e)\r
2656                         {\r
2657                                 continue;\r
2658                         }\r
2659 \r
2660                         v3s16 np(x,y,z);\r
2661                         v3f npf = intToFloat(np, BS);\r
2662                         \r
2663                         f32 d = 0.01;\r
2664                         \r
2665                         v3s16 dirs[6] = {\r
2666                                 v3s16(0,0,1), // back\r
2667                                 v3s16(0,1,0), // top\r
2668                                 v3s16(1,0,0), // right\r
2669                                 v3s16(0,0,-1), // front\r
2670                                 v3s16(0,-1,0), // bottom\r
2671                                 v3s16(-1,0,0), // left\r
2672                         };\r
2673                         \r
2674                         /*\r
2675                                 Meta-objects\r
2676                         */\r
2677                         if(n.d == CONTENT_TORCH)\r
2678                         {\r
2679                                 v3s16 dir = unpackDir(n.dir);\r
2680                                 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
2681                                 dir_f *= BS/2 - BS/6 - BS/20;\r
2682                                 v3f cpf = npf + dir_f;\r
2683                                 f32 distance = (cpf - camera_position).getLength();\r
2684 \r
2685                                 core::aabbox3d<f32> box;\r
2686                                 \r
2687                                 // bottom\r
2688                                 if(dir == v3s16(0,-1,0))\r
2689                                 {\r
2690                                         box = core::aabbox3d<f32>(\r
2691                                                 npf - v3f(BS/6, BS/2, BS/6),\r
2692                                                 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)\r
2693                                         );\r
2694                                 }\r
2695                                 // top\r
2696                                 else if(dir == v3s16(0,1,0))\r
2697                                 {\r
2698                                         box = core::aabbox3d<f32>(\r
2699                                                 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),\r
2700                                                 npf + v3f(BS/6, BS/2, BS/6)\r
2701                                         );\r
2702                                 }\r
2703                                 // side\r
2704                                 else\r
2705                                 {\r
2706                                         box = core::aabbox3d<f32>(\r
2707                                                 cpf - v3f(BS/6, BS/3, BS/6),\r
2708                                                 cpf + v3f(BS/6, BS/3, BS/6)\r
2709                                         );\r
2710                                 }\r
2711 \r
2712                                 if(distance < mindistance)\r
2713                                 {\r
2714                                         if(box.intersectsWithLine(shootline))\r
2715                                         {\r
2716                                                 nodefound = true;\r
2717                                                 nodepos = np;\r
2718                                                 neighbourpos = np;\r
2719                                                 mindistance = distance;\r
2720                                                 nodehilightbox = box;\r
2721                                         }\r
2722                                 }\r
2723                         }\r
2724                         /*\r
2725                                 Regular blocks\r
2726                         */\r
2727                         else\r
2728                         {\r
2729                                 for(u16 i=0; i<6; i++)\r
2730                                 {\r
2731                                         v3f dir_f = v3f(dirs[i].X,\r
2732                                                         dirs[i].Y, dirs[i].Z);\r
2733                                         v3f centerpoint = npf + dir_f * BS/2;\r
2734                                         f32 distance =\r
2735                                                         (centerpoint - camera_position).getLength();\r
2736                                         \r
2737                                         if(distance < mindistance)\r
2738                                         {\r
2739                                                 core::CMatrix4<f32> m;\r
2740                                                 m.buildRotateFromTo(v3f(0,0,1), dir_f);\r
2741 \r
2742                                                 // This is the back face\r
2743                                                 v3f corners[2] = {\r
2744                                                         v3f(BS/2, BS/2, BS/2),\r
2745                                                         v3f(-BS/2, -BS/2, BS/2+d)\r
2746                                                 };\r
2747                                                 \r
2748                                                 for(u16 j=0; j<2; j++)\r
2749                                                 {\r
2750                                                         m.rotateVect(corners[j]);\r
2751                                                         corners[j] += npf;\r
2752                                                 }\r
2753 \r
2754                                                 core::aabbox3d<f32> facebox(corners[0]);\r
2755                                                 facebox.addInternalPoint(corners[1]);\r
2756 \r
2757                                                 if(facebox.intersectsWithLine(shootline))\r
2758                                                 {\r
2759                                                         nodefound = true;\r
2760                                                         nodepos = np;\r
2761                                                         neighbourpos = np + dirs[i];\r
2762                                                         mindistance = distance;\r
2763 \r
2764                                                         //nodehilightbox = facebox;\r
2765 \r
2766                                                         const float d = 0.502;\r
2767                                                         core::aabbox3d<f32> nodebox\r
2768                                                                         (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
2769                                                         v3f nodepos_f = intToFloat(nodepos, BS);\r
2770                                                         nodebox.MinEdge += nodepos_f;\r
2771                                                         nodebox.MaxEdge += nodepos_f;\r
2772                                                         nodehilightbox = nodebox;\r
2773                                                 }\r
2774                                         } // if distance < mindistance\r
2775                                 } // for dirs\r
2776                         } // regular block\r
2777                 } // for coords\r
2778 \r
2779                 static float nodig_delay_counter = 0.0;\r
2780 \r
2781                 if(nodefound)\r
2782                 {\r
2783                         static v3s16 nodepos_old(-32768,-32768,-32768);\r
2784 \r
2785                         static float dig_time = 0.0;\r
2786                         static u16 dig_index = 0;\r
2787                         \r
2788                         // Visualize selection\r
2789 \r
2790                         hilightboxes.push_back(nodehilightbox);\r
2791 \r
2792                         // Handle digging\r
2793                         \r
2794                         if(g_input->getLeftReleased())\r
2795                         {\r
2796                                 client.clearTempMod(nodepos);\r
2797                                 dig_time = 0.0;\r
2798                         }\r
2799                         \r
2800                         if(nodig_delay_counter > 0.0)\r
2801                         {\r
2802                                 nodig_delay_counter -= dtime;\r
2803                         }\r
2804                         else\r
2805                         {\r
2806                                 if(nodepos != nodepos_old)\r
2807                                 {\r
2808                                         std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","\r
2809                                                         <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
2810 \r
2811                                         if(nodepos_old != v3s16(-32768,-32768,-32768))\r
2812                                         {\r
2813                                                 client.clearTempMod(nodepos_old);\r
2814                                                 dig_time = 0.0;\r
2815                                         }\r
2816                                 }\r
2817 \r
2818                                 if(g_input->getLeftClicked() ||\r
2819                                                 (g_input->getLeftState() && nodepos != nodepos_old))\r
2820                                 {\r
2821                                         dstream<<DTIME<<"Started digging"<<std::endl;\r
2822                                         client.groundAction(0, nodepos, neighbourpos, g_selected_item);\r
2823                                 }\r
2824                                 if(g_input->getLeftClicked())\r
2825                                 {\r
2826                                         client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));\r
2827                                 }\r
2828                                 if(g_input->getLeftState())\r
2829                                 {\r
2830                                         MapNode n = client.getNode(nodepos);\r
2831                                 \r
2832                                         // Get tool name. Default is "" = bare hands\r
2833                                         std::string toolname = "";\r
2834                                         InventoryList *mlist = local_inventory.getList("main");\r
2835                                         if(mlist != NULL)\r
2836                                         {\r
2837                                                 InventoryItem *item = mlist->getItem(g_selected_item);\r
2838                                                 if(item && (std::string)item->getName() == "ToolItem")\r
2839                                                 {\r
2840                                                         ToolItem *titem = (ToolItem*)item;\r
2841                                                         toolname = titem->getToolName();\r
2842                                                 }\r
2843                                         }\r
2844 \r
2845                                         // Get digging properties for material and tool\r
2846                                         u8 material = n.d;\r
2847                                         DiggingProperties prop =\r
2848                                                         getDiggingProperties(material, toolname);\r
2849                                         \r
2850                                         float dig_time_complete = 0.0;\r
2851 \r
2852                                         if(prop.diggable == false)\r
2853                                         {\r
2854                                                 /*dstream<<"Material "<<(int)material\r
2855                                                                 <<" not diggable with \""\r
2856                                                                 <<toolname<<"\""<<std::endl;*/\r
2857                                                 // I guess nobody will wait for this long\r
2858                                                 dig_time_complete = 10000000.0;\r
2859                                         }\r
2860                                         else\r
2861                                         {\r
2862                                                 dig_time_complete = prop.time;\r
2863                                         }\r
2864                                         \r
2865                                         if(dig_time_complete >= 0.001)\r
2866                                         {\r
2867                                                 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH\r
2868                                                                 * dig_time/dig_time_complete);\r
2869                                         }\r
2870                                         // This is for torches\r
2871                                         else\r
2872                                         {\r
2873                                                 dig_index = CRACK_ANIMATION_LENGTH;\r
2874                                         }\r
2875 \r
2876                                         if(dig_index < CRACK_ANIMATION_LENGTH)\r
2877                                         {\r
2878                                                 //TimeTaker timer("client.setTempMod");\r
2879                                                 //dstream<<"dig_index="<<dig_index<<std::endl;\r
2880                                                 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));\r
2881                                         }\r
2882                                         else\r
2883                                         {\r
2884                                                 dstream<<DTIME<<"Digging completed"<<std::endl;\r
2885                                                 client.groundAction(3, nodepos, neighbourpos, g_selected_item);\r
2886                                                 client.clearTempMod(nodepos);\r
2887                                                 client.removeNode(nodepos);\r
2888 \r
2889                                                 dig_time = 0;\r
2890 \r
2891                                                 nodig_delay_counter = dig_time_complete\r
2892                                                                 / (float)CRACK_ANIMATION_LENGTH;\r
2893 \r
2894                                                 // We don't want a corresponding delay to\r
2895                                                 // very time consuming nodes\r
2896                                                 if(nodig_delay_counter > 0.5)\r
2897                                                 {\r
2898                                                         nodig_delay_counter = 0.5;\r
2899                                                 }\r
2900                                                 // We want a slight delay to very little\r
2901                                                 // time consuming nodes\r
2902                                                 float mindelay = 0.15;\r
2903                                                 if(nodig_delay_counter < mindelay)\r
2904                                                 {\r
2905                                                         nodig_delay_counter = mindelay;\r
2906                                                 }\r
2907                                         }\r
2908 \r
2909                                         dig_time += dtime;\r
2910                                 }\r
2911                         }\r
2912                         \r
2913                         if(g_input->getRightClicked())\r
2914                         {\r
2915                                 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;\r
2916                                 client.groundAction(1, nodepos, neighbourpos, g_selected_item);\r
2917                         }\r
2918                         \r
2919                         nodepos_old = nodepos;\r
2920                 }\r
2921                 else{\r
2922                 }\r
2923 \r
2924                 } // selected_object == NULL\r
2925                 \r
2926                 g_input->resetLeftClicked();\r
2927                 g_input->resetRightClicked();\r
2928                 \r
2929                 if(g_input->getLeftReleased())\r
2930                 {\r
2931                         std::cout<<DTIME<<"Left button released (stopped digging)"\r
2932                                         <<std::endl;\r
2933                         client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);\r
2934                 }\r
2935                 if(g_input->getRightReleased())\r
2936                 {\r
2937                         //std::cout<<DTIME<<"Right released"<<std::endl;\r
2938                         // Nothing here\r
2939                 }\r
2940                 \r
2941                 g_input->resetLeftReleased();\r
2942                 g_input->resetRightReleased();\r
2943                 \r
2944                 /*\r
2945                         Calculate stuff for drawing\r
2946                 */\r
2947 \r
2948                 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);\r
2949                 \r
2950                 u32 daynight_ratio = client.getDayNightRatio();\r
2951                 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);\r
2952                 video::SColor bgcolor = video::SColor(\r
2953                                 255,\r
2954                                 skycolor.getRed() * l / 255,\r
2955                                 skycolor.getGreen() * l / 255,\r
2956                                 skycolor.getBlue() * l / 255);\r
2957 \r
2958                 /*\r
2959                         Fog\r
2960                 */\r
2961                 \r
2962                 if(g_settings.getBool("enable_fog") == true)\r
2963                 {\r
2964                         //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;\r
2965                         f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;\r
2966                         //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;\r
2967                         if(draw_control.range_all)\r
2968                                 range = 100000*BS;\r
2969 \r
2970                         driver->setFog(\r
2971                                 bgcolor,\r
2972                                 video::EFT_FOG_LINEAR,\r
2973                                 range*0.4,\r
2974                                 range*1.0,\r
2975                                 0.01,\r
2976                                 false, // pixel fog\r
2977                                 false // range fog\r
2978                         );\r
2979                 }\r
2980                 else\r
2981                 {\r
2982                         driver->setFog(\r
2983                                 bgcolor,\r
2984                                 video::EFT_FOG_LINEAR,\r
2985                                 100000*BS,\r
2986                                 110000*BS,\r
2987                                 0.01,\r
2988                                 false, // pixel fog\r
2989                                 false // range fog\r
2990                         );\r
2991                 }\r
2992 \r
2993 \r
2994                 /*\r
2995                         Update gui stuff (0ms)\r
2996                 */\r
2997 \r
2998                 //TimeTaker guiupdatetimer("Gui updating");\r
2999                 \r
3000                 {\r
3001                         static float drawtime_avg = 0;\r
3002                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;\r
3003                         static float beginscenetime_avg = 0;\r
3004                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;\r
3005                         static float scenetime_avg = 0;\r
3006                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;\r
3007                         static float endscenetime_avg = 0;\r
3008                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;\r
3009                         \r
3010                         char temptext[300];\r
3011                         snprintf(temptext, 300, "Minetest-c55 ("\r
3012                                         "F: item=%i"\r
3013                                         ", R: range_all=%i"\r
3014                                         ")"\r
3015                                         " drawtime=%.0f, beginscenetime=%.0f"\r
3016                                         ", scenetime=%.0f, endscenetime=%.0f",\r
3017                                         g_selected_item,\r
3018                                         draw_control.range_all,\r
3019                                         drawtime_avg,\r
3020                                         beginscenetime_avg,\r
3021                                         scenetime_avg,\r
3022                                         endscenetime_avg\r
3023                                         );\r
3024                         \r
3025                         guitext->setText(narrow_to_wide(temptext).c_str());\r
3026                 }\r
3027                 \r
3028                 {\r
3029                         char temptext[300];\r
3030                         snprintf(temptext, 300,\r
3031                                         "(% .1f, % .1f, % .1f)"\r
3032                                         " (% .3f < btime_jitter < % .3f"\r
3033                                         ", dtime_jitter = % .1f %%"\r
3034                                         ", v_range = %.1f)",\r
3035                                         player_position.X/BS,\r
3036                                         player_position.Y/BS,\r
3037                                         player_position.Z/BS,\r
3038                                         busytime_jitter1_min_sample,\r
3039                                         busytime_jitter1_max_sample,\r
3040                                         dtime_jitter1_max_fraction * 100.0,\r
3041                                         draw_control.wanted_range\r
3042                                         );\r
3043 \r
3044                         guitext2->setText(narrow_to_wide(temptext).c_str());\r
3045                 }\r
3046                 \r
3047                 {\r
3048                         guitext_info->setText(infotext.c_str());\r
3049                 }\r
3050                 \r
3051                 /*\r
3052                         Get chat messages from client\r
3053                 */\r
3054                 {\r
3055                         // Get new messages\r
3056                         std::wstring message;\r
3057                         while(client.getChatMessage(message))\r
3058                         {\r
3059                                 chat_lines.push_back(ChatLine(message));\r
3060                                 /*if(chat_lines.size() > 6)\r
3061                                 {\r
3062                                         core::list<ChatLine>::Iterator\r
3063                                                         i = chat_lines.begin();\r
3064                                         chat_lines.erase(i);\r
3065                                 }*/\r
3066                         }\r
3067                         // Append them to form the whole static text and throw\r
3068                         // it to the gui element\r
3069                         std::wstring whole;\r
3070                         // This will correspond to the line number counted from\r
3071                         // top to bottom, from size-1 to 0\r
3072                         s16 line_number = chat_lines.size();\r
3073                         // Count of messages to be removed from the top\r
3074                         u16 to_be_removed_count = 0;\r
3075                         for(core::list<ChatLine>::Iterator\r
3076                                         i = chat_lines.begin();\r
3077                                         i != chat_lines.end(); i++)\r
3078                         {\r
3079                                 // After this, line number is valid for this loop\r
3080                                 line_number--;\r
3081                                 // Increment age\r
3082                                 (*i).age += dtime;\r
3083                                 /*\r
3084                                         This results in a maximum age of 60*6 to the\r
3085                                         lowermost line and a maximum of 6 lines\r
3086                                 */\r
3087                                 float allowed_age = (6-line_number) * 60.0;\r
3088 \r
3089                                 if((*i).age > allowed_age)\r
3090                                 {\r
3091                                         to_be_removed_count++;\r
3092                                         continue;\r
3093                                 }\r
3094                                 whole += (*i).text + L'\n';\r
3095                         }\r
3096                         for(u16 i=0; i<to_be_removed_count; i++)\r
3097                         {\r
3098                                 core::list<ChatLine>::Iterator\r
3099                                                 it = chat_lines.begin();\r
3100                                 chat_lines.erase(it);\r
3101                         }\r
3102                         guitext_chat->setText(whole.c_str());\r
3103 \r
3104                         // Update gui element size and position\r
3105 \r
3106                         /*core::rect<s32> rect(\r
3107                                         10,\r
3108                                         screensize.Y - guitext_chat_pad_bottom\r
3109                                                         - text_height*chat_lines.size(),\r
3110                                         screensize.X - 10,\r
3111                                         screensize.Y - guitext_chat_pad_bottom\r
3112                         );*/\r
3113                         core::rect<s32> rect(\r
3114                                         10,\r
3115                                         50,\r
3116                                         screensize.X - 10,\r
3117                                         50 + text_height*chat_lines.size()\r
3118                         );\r
3119 \r
3120                         guitext_chat->setRelativePosition(rect);\r
3121 \r
3122                         if(chat_lines.size() == 0)\r
3123                                 guitext_chat->setVisible(false);\r
3124                         else\r
3125                                 guitext_chat->setVisible(true);\r
3126                 }\r
3127 \r
3128                 /*\r
3129                         Inventory\r
3130                 */\r
3131                 \r
3132                 static u16 old_selected_item = 65535;\r
3133                 if(client.getLocalInventoryUpdated()\r
3134                                 || g_selected_item != old_selected_item)\r
3135                 {\r
3136                         old_selected_item = g_selected_item;\r
3137                         //std::cout<<"Updating local inventory"<<std::endl;\r
3138                         client.getLocalInventory(local_inventory);\r
3139                         /*quick_inventory->setSelection(g_selected_item);\r
3140                         quick_inventory->update();*/\r
3141                 }\r
3142                 \r
3143                 /*\r
3144                         Send actions returned by the inventory menu\r
3145                 */\r
3146                 while(inventory_action_queue.size() != 0)\r
3147                 {\r
3148                         InventoryAction *a = inventory_action_queue.pop_front();\r
3149 \r
3150                         client.sendInventoryAction(a);\r
3151                         // Eat it\r
3152                         delete a;\r
3153                 }\r
3154 \r
3155                 /*\r
3156                         Drawing begins\r
3157                 */\r
3158 \r
3159                 TimeTaker drawtimer("Drawing");\r
3160 \r
3161                 \r
3162                 {\r
3163                         TimeTaker timer("beginScene");\r
3164                         driver->beginScene(true, true, bgcolor);\r
3165                         //driver->beginScene(false, true, bgcolor);\r
3166                         beginscenetime = timer.stop(true);\r
3167                 }\r
3168 \r
3169                 //timer3.stop();\r
3170                 \r
3171                 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;\r
3172                 \r
3173                 {\r
3174                         TimeTaker timer("smgr");\r
3175                         smgr->drawAll();\r
3176                         scenetime = timer.stop(true);\r
3177                 }\r
3178                 \r
3179                 {\r
3180                 //TimeTaker timer9("auxiliary drawings");\r
3181                 // 0ms\r
3182                 \r
3183                 //timer9.stop();\r
3184                 //TimeTaker //timer10("//timer10");\r
3185                 \r
3186                 video::SMaterial m;\r
3187                 //m.Thickness = 10;\r
3188                 m.Thickness = 3;\r
3189                 m.Lighting = false;\r
3190                 driver->setMaterial(m);\r
3191 \r
3192                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);\r
3193 \r
3194                 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();\r
3195                                 i != hilightboxes.end(); i++)\r
3196                 {\r
3197                         /*std::cout<<"hilightbox min="\r
3198                                         <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"\r
3199                                         <<" max="\r
3200                                         <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"\r
3201                                         <<std::endl;*/\r
3202                         driver->draw3DBox(*i, video::SColor(255,0,0,0));\r
3203                 }\r
3204 \r
3205                 /*\r
3206                         Frametime log\r
3207                 */\r
3208                 if(g_settings.getBool("frametime_graph") == true)\r
3209                 {\r
3210                         s32 x = 10;\r
3211                         for(core::list<float>::Iterator\r
3212                                         i = frametime_log.begin();\r
3213                                         i != frametime_log.end();\r
3214                                         i++)\r
3215                         {\r
3216                                 driver->draw2DLine(v2s32(x,50),\r
3217                                                 v2s32(x,50+(*i)*1000),\r
3218                                                 video::SColor(255,255,255,255));\r
3219                                 x++;\r
3220                         }\r
3221                 }\r
3222 \r
3223                 /*\r
3224                         Draw map plot\r
3225                 */\r
3226                 if(g_show_map_plot && g_map_plot_texture)\r
3227                 {\r
3228                         core::dimension2d<u32> drawdim(640,480);\r
3229                         core::rect<s32> dest(v2s32(0,0), drawdim);\r
3230                         dest += v2s32(\r
3231                                 (screensize.X-drawdim.Width)/2,\r
3232                                 (screensize.Y-drawdim.Height)/2\r
3233                         );\r
3234                         core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());\r
3235                         driver->draw2DImage(g_map_plot_texture, dest, source);\r
3236                 }\r
3237 \r
3238                 /*\r
3239                         Draw crosshair\r
3240                 */\r
3241                 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),\r
3242                                 displaycenter + core::vector2d<s32>(10,0),\r
3243                                 video::SColor(255,255,255,255));\r
3244                 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),\r
3245                                 displaycenter + core::vector2d<s32>(0,10),\r
3246                                 video::SColor(255,255,255,255));\r
3247 \r
3248                 } // timer\r
3249 \r
3250                 //timer10.stop();\r
3251                 //TimeTaker //timer11("//timer11");\r
3252 \r
3253                 /*\r
3254                         Draw gui\r
3255                 */\r
3256                 // 0-1ms\r
3257                 guienv->drawAll();\r
3258 \r
3259                 /*\r
3260                         Draw hotbar\r
3261                 */\r
3262                 {\r
3263                         draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),\r
3264                                         hotbar_imagesize, hotbar_itemcount, &local_inventory);\r
3265                 }\r
3266                 \r
3267                 // End drawing\r
3268                 {\r
3269                         TimeTaker timer("endScene");\r
3270                         driver->endScene();\r
3271                         endscenetime = timer.stop(true);\r
3272                 }\r
3273 \r
3274                 drawtime = drawtimer.stop(true);\r
3275 \r
3276                 /*\r
3277                         End of drawing\r
3278                 */\r
3279 \r
3280                 /*\r
3281                         Refresh map plot if player has moved considerably\r
3282                 */\r
3283                 if(g_refresh_map_plot)\r
3284                 {\r
3285                         static v3f old_player_pos = v3f(1,1,1) * 10000000;\r
3286                         v3f p = client.getPlayerPosition() / BS;\r
3287                         if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)\r
3288                         {\r
3289                                 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);\r
3290                                 old_player_pos = p;\r
3291                         }\r
3292                         g_refresh_map_plot = false;\r
3293                 }\r
3294                 \r
3295                 static s16 lastFPS = 0;\r
3296                 //u16 fps = driver->getFPS();\r
3297                 u16 fps = (1.0/dtime_avg1);\r
3298 \r
3299                 if (lastFPS != fps)\r
3300                 {\r
3301                         core::stringw str = L"Minetest [";\r
3302                         str += driver->getName();\r
3303                         str += "] FPS:";\r
3304                         str += fps;\r
3305 \r
3306                         device->setWindowCaption(str.c_str());\r
3307                         lastFPS = fps;\r
3308                 }\r
3309                 \r
3310                 /*}\r
3311                 else\r
3312                         device->yield();*/\r
3313         }\r
3314 \r
3315         //delete quick_inventory;\r
3316 \r
3317         /*\r
3318                 Disable texture fetches and other stuff that is queued\r
3319                 to be processed by the main loop.\r
3320 \r
3321                 This has to be done before client goes out of scope.\r
3322         */\r
3323         g_irrlicht->Shutdown(true);\r
3324 \r
3325         } // client and server are deleted at this point\r
3326 \r
3327         } //try\r
3328         catch(con::PeerNotFoundException &e)\r
3329         {\r
3330                 dstream<<DTIME<<"Connection timed out."<<std::endl;\r
3331                 error_message = L"Connection timed out.";\r
3332         }\r
3333 \r
3334         } // Menu-game loop\r
3335         \r
3336         delete g_input;\r
3337 \r
3338         /*\r
3339                 In the end, delete the Irrlicht device.\r
3340         */\r
3341         device->drop();\r
3342         \r
3343         /*\r
3344                 Update configuration file\r
3345         */\r
3346         /*if(configpath != "")\r
3347         {\r
3348                 g_settings.updateConfigFile(configpath.c_str());\r
3349         }*/\r
3350 \r
3351         END_DEBUG_EXCEPTION_HANDLER\r
3352         \r
3353         debugstreams_deinit();\r
3354         \r
3355         return 0;\r
3356 }\r
3357 \r
3358 //END\r