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