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