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