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