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