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