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