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