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