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