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