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