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