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