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