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