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