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