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