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