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