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