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