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