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