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