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