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