]> git.lizzy.rs Git - minetest.git/blob - src/main.cpp
tinkering around
[minetest.git] / src / main.cpp
1 /*\r
2 Minetest-c55\r
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>\r
4 \r
5 This program is free software; you can redistribute it and/or modify\r
6 it under the terms of the GNU General Public License as published by\r
7 the Free Software Foundation; either version 2 of the License, or\r
8 (at your option) any later version.\r
9 \r
10 This program is distributed in the hope that it will be useful,\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 GNU General Public License for more details.\r
14 \r
15 You should have received a copy of the GNU General Public License along\r
16 with this program; if not, write to the Free Software Foundation, Inc.,\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 */\r
19 \r
20 /*\r
21 =============================== NOTES ==============================\r
22 NOTE: Things starting with TODO are sometimes only suggestions.\r
23 \r
24 NOTE: VBO cannot be turned on for fast-changing stuff because there\r
25       is an apparanet memory leak in irrlicht when using it (not sure)\r
26 \r
27 NOTE: iostream.imbue(std::locale("C")) is very slow\r
28 NOTE: Global locale is now set at initialization\r
29 \r
30 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         g_settings.set("dedicated_server", "");\r
310 \r
311         // Client stuff\r
312         g_settings.set("wanted_fps", "30");\r
313         g_settings.set("fps_max", "60");\r
314         g_settings.set("viewing_range_nodes_max", "300");\r
315         g_settings.set("viewing_range_nodes_min", "35");\r
316         g_settings.set("screenW", "");\r
317         g_settings.set("screenH", "");\r
318         g_settings.set("host_game", "");\r
319         g_settings.set("port", "");\r
320         g_settings.set("address", "");\r
321         g_settings.set("name", "");\r
322         g_settings.set("random_input", "false");\r
323         g_settings.set("client_delete_unused_sectors_timeout", "1200");\r
324         g_settings.set("max_block_send_distance", "8");\r
325         g_settings.set("max_block_generate_distance", "6");\r
326 \r
327         // Server stuff\r
328         g_settings.set("creative_mode", "false");\r
329         g_settings.set("heightmap_blocksize", "32");\r
330         g_settings.set("height_randmax", "constant 50.0");\r
331         g_settings.set("height_randfactor", "constant 0.6");\r
332         g_settings.set("height_base", "linear 0 0 0");\r
333         g_settings.set("plants_amount", "1.0");\r
334         g_settings.set("ravines_amount", "1.0");\r
335         g_settings.set("objectdata_interval", "0.2");\r
336         g_settings.set("active_object_range", "2");\r
337         g_settings.set("max_simultaneous_block_sends_per_client", "1");\r
338         g_settings.set("max_simultaneous_block_sends_server_total", "4");\r
339         g_settings.set("disable_water_climb", "true");\r
340         g_settings.set("endless_water", "true");\r
341 }\r
342 \r
343 /*\r
344         Random stuff\r
345 */\r
346 \r
347 //u16 g_selected_material = 0;\r
348 u16 g_selected_item = 0;\r
349 \r
350 bool g_esc_pressed = false;\r
351 \r
352 std::wstring g_text_buffer;\r
353 bool g_text_buffer_accepted = false;\r
354 \r
355 // When true, the mouse and keyboard are grabbed\r
356 bool g_game_focused = true;\r
357 \r
358 /*\r
359         Debug streams\r
360 */\r
361 \r
362 // Connection\r
363 std::ostream *dout_con_ptr = &dummyout;\r
364 std::ostream *derr_con_ptr = &dstream_no_stderr;\r
365 //std::ostream *dout_con_ptr = &dstream_no_stderr;\r
366 //std::ostream *derr_con_ptr = &dstream_no_stderr;\r
367 //std::ostream *dout_con_ptr = &dstream;\r
368 //std::ostream *derr_con_ptr = &dstream;\r
369 \r
370 // Server\r
371 std::ostream *dout_server_ptr = &dstream;\r
372 std::ostream *derr_server_ptr = &dstream;\r
373 \r
374 // Client\r
375 std::ostream *dout_client_ptr = &dstream;\r
376 std::ostream *derr_client_ptr = &dstream;\r
377 \r
378 \r
379 /*\r
380         Timestamp stuff\r
381 */\r
382 \r
383 JMutex g_timestamp_mutex;\r
384 //std::string g_timestamp;\r
385 \r
386 std::string getTimestamp()\r
387 {\r
388         if(g_timestamp_mutex.IsInitialized()==false)\r
389                 return "";\r
390         JMutexAutoLock lock(g_timestamp_mutex);\r
391         //return g_timestamp;\r
392         time_t t = time(NULL);\r
393         struct tm *tm = localtime(&t);\r
394         char cs[20];\r
395         strftime(cs, 20, "%H:%M:%S", tm);\r
396         return cs;\r
397 }\r
398 \r
399 class MyEventReceiver : public IEventReceiver\r
400 {\r
401 public:\r
402         // This is the one method that we have to implement\r
403         virtual bool OnEvent(const SEvent& event)\r
404         {\r
405                 // Remember whether each key is down or up\r
406                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
407                 {\r
408                         keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;\r
409 \r
410                         if(event.KeyInput.PressedDown)\r
411                         {\r
412                                 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;\r
413                                 if(g_game_focused == false)\r
414                                 {\r
415                                         s16 key = event.KeyInput.Key;\r
416                                         if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)\r
417                                         {\r
418                                                 g_text_buffer_accepted = true;\r
419                                         }\r
420                                         else if(key == irr::KEY_BACK)\r
421                                         {\r
422                                                 if(g_text_buffer.size() > 0)\r
423                                                         g_text_buffer = g_text_buffer.substr\r
424                                                                         (0, g_text_buffer.size()-1);\r
425                                         }\r
426                                         else\r
427                                         {\r
428                                                 wchar_t wc = event.KeyInput.Char;\r
429                                                 if(wc != 0)\r
430                                                         g_text_buffer += wc;\r
431                                         }\r
432                                 }\r
433                                 \r
434                                 if(event.KeyInput.Key == irr::KEY_ESCAPE)\r
435                                 {\r
436                                         //TODO: Not used anymore?\r
437                                         if(g_game_focused == true)\r
438                                         {\r
439                                                 dstream<<DTIME<<"ESC pressed"<<std::endl;\r
440                                                 g_esc_pressed = true;\r
441                                         }\r
442                                 }\r
443 \r
444                                 // Material selection\r
445                                 if(event.KeyInput.Key == irr::KEY_KEY_F)\r
446                                 {\r
447                                         if(g_game_focused == true)\r
448                                         {\r
449                                                 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
450                                                         g_selected_item++;\r
451                                                 else\r
452                                                         g_selected_item = 0;\r
453                                                 dstream<<DTIME<<"Selected item: "\r
454                                                                 <<g_selected_item<<std::endl;\r
455                                         }\r
456                                 }\r
457 \r
458                                 // Viewing range selection\r
459                                 if(event.KeyInput.Key == irr::KEY_KEY_R\r
460                                                 && g_game_focused)\r
461                                 {\r
462                                         JMutexAutoLock lock(g_range_mutex);\r
463                                         if(g_viewing_range_all)\r
464                                         {\r
465                                                 g_viewing_range_all = false;\r
466                                                 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;\r
467                                         }\r
468                                         else\r
469                                         {\r
470                                                 g_viewing_range_all = true;\r
471                                                 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;\r
472                                         }\r
473                                 }\r
474 \r
475                                 // Print debug stacks\r
476                                 if(event.KeyInput.Key == irr::KEY_KEY_P\r
477                                                 && g_game_focused)\r
478                                 {\r
479                                         dstream<<"-----------------------------------------"\r
480                                                         <<std::endl;\r
481                                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;\r
482                                         dstream<<"-----------------------------------------"\r
483                                                         <<std::endl;\r
484                                         debug_stacks_print();\r
485                                 }\r
486                         }\r
487                 }\r
488 \r
489                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
490                 {\r
491                         if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
492                         {\r
493                                 leftclicked = true;\r
494                         }\r
495                         if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
496                         {\r
497                                 rightclicked = true;\r
498                         }\r
499                         if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
500                         {\r
501                                 /*dstream<<"event.MouseInput.Wheel="\r
502                                                 <<event.MouseInput.Wheel<<std::endl;*/\r
503                                 if(event.MouseInput.Wheel < 0)\r
504                                 {\r
505                                         if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
506                                                 g_selected_item++;\r
507                                         else\r
508                                                 g_selected_item = 0;\r
509                                 }\r
510                                 else if(event.MouseInput.Wheel > 0)\r
511                                 {\r
512                                         if(g_selected_item > 0)\r
513                                                 g_selected_item--;\r
514                                         else\r
515                                                 g_selected_item = PLAYER_INVENTORY_SIZE-1;\r
516                                 }\r
517                         }\r
518                 }\r
519 \r
520                 return false;\r
521         }\r
522 \r
523         // This is used to check whether a key is being held down\r
524         virtual bool IsKeyDown(EKEY_CODE keyCode) const\r
525         {\r
526                 return keyIsDown[keyCode];\r
527         }\r
528 \r
529         MyEventReceiver()\r
530         {\r
531                 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
532                                 keyIsDown[i] = false;\r
533                 leftclicked = false;\r
534                 rightclicked = false;\r
535         }\r
536 \r
537         bool leftclicked;\r
538         bool rightclicked;\r
539 private:\r
540         // We use this array to store the current state of each key\r
541         bool keyIsDown[KEY_KEY_CODES_COUNT];\r
542         //s32 mouseX;\r
543         //s32 mouseY;\r
544 };\r
545 \r
546 class InputHandler\r
547 {\r
548 public:\r
549         InputHandler()\r
550         {\r
551         }\r
552         virtual ~InputHandler()\r
553         {\r
554         }\r
555         virtual bool isKeyDown(EKEY_CODE keyCode) = 0;\r
556         virtual v2s32 getMousePos() = 0;\r
557         virtual void setMousePos(s32 x, s32 y) = 0;\r
558         virtual bool getLeftClicked() = 0;\r
559         virtual bool getRightClicked() = 0;\r
560         virtual void resetLeftClicked() = 0;\r
561         virtual void resetRightClicked() = 0;\r
562         \r
563         virtual void step(float dtime) {};\r
564 \r
565         virtual void clear() {};\r
566 };\r
567 \r
568 InputHandler *g_input = NULL;\r
569 \r
570 void focusGame()\r
571 {\r
572         g_input->clear();\r
573         g_game_focused = true;\r
574 }\r
575 \r
576 void unFocusGame()\r
577 {\r
578         g_game_focused = false;\r
579 }\r
580 \r
581 class RealInputHandler : public InputHandler\r
582 {\r
583 public:\r
584         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):\r
585                 m_device(device),\r
586                 m_receiver(receiver)\r
587         {\r
588         }\r
589         virtual bool isKeyDown(EKEY_CODE keyCode)\r
590         {\r
591                 return m_receiver->IsKeyDown(keyCode);\r
592         }\r
593         virtual v2s32 getMousePos()\r
594         {\r
595                 return m_device->getCursorControl()->getPosition();\r
596         }\r
597         virtual void setMousePos(s32 x, s32 y)\r
598         {\r
599                 m_device->getCursorControl()->setPosition(x, y);\r
600         }\r
601 \r
602         virtual bool getLeftClicked()\r
603         {\r
604                 if(g_game_focused == false)\r
605                         return false;\r
606                 return m_receiver->leftclicked;\r
607         }\r
608         virtual bool getRightClicked()\r
609         {\r
610                 if(g_game_focused == false)\r
611                         return false;\r
612                 return m_receiver->rightclicked;\r
613         }\r
614         virtual void resetLeftClicked()\r
615         {\r
616                 m_receiver->leftclicked = false;\r
617         }\r
618         virtual void resetRightClicked()\r
619         {\r
620                 m_receiver->rightclicked = false;\r
621         }\r
622 \r
623         void clear()\r
624         {\r
625                 resetRightClicked();\r
626                 resetLeftClicked();\r
627         }\r
628 private:\r
629         IrrlichtDevice *m_device;\r
630         MyEventReceiver *m_receiver;\r
631 };\r
632 \r
633 class RandomInputHandler : public InputHandler\r
634 {\r
635 public:\r
636         RandomInputHandler()\r
637         {\r
638                 leftclicked = false;\r
639                 rightclicked = false;\r
640                 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
641                         keydown[i] = false;\r
642         }\r
643         virtual bool isKeyDown(EKEY_CODE keyCode)\r
644         {\r
645                 return keydown[keyCode];\r
646         }\r
647         virtual v2s32 getMousePos()\r
648         {\r
649                 return mousepos;\r
650         }\r
651         virtual void setMousePos(s32 x, s32 y)\r
652         {\r
653                 mousepos = v2s32(x,y);\r
654         }\r
655 \r
656         virtual bool getLeftClicked()\r
657         {\r
658                 return leftclicked;\r
659         }\r
660         virtual bool getRightClicked()\r
661         {\r
662                 return rightclicked;\r
663         }\r
664         virtual void resetLeftClicked()\r
665         {\r
666                 leftclicked = false;\r
667         }\r
668         virtual void resetRightClicked()\r
669         {\r
670                 rightclicked = false;\r
671         }\r
672 \r
673         virtual void step(float dtime)\r
674         {\r
675                 {\r
676                         static float counter1 = 0;\r
677                         counter1 -= dtime;\r
678                         if(counter1 < 0.0)\r
679                         {\r
680                                 counter1 = 0.1*Rand(1,10);\r
681                                 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)\r
682                                         g_selected_material++;\r
683                                 else\r
684                                         g_selected_material = 0;*/\r
685                                 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)\r
686                                         g_selected_item++;\r
687                                 else\r
688                                         g_selected_item = 0;\r
689                         }\r
690                 }\r
691                 {\r
692                         static float counter1 = 0;\r
693                         counter1 -= dtime;\r
694                         if(counter1 < 0.0)\r
695                         {\r
696                                 counter1 = 0.1*Rand(1, 40);\r
697                                 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];\r
698                         }\r
699                 }\r
700                 {\r
701                         static float counter1 = 0;\r
702                         counter1 -= dtime;\r
703                         if(counter1 < 0.0)\r
704                         {\r
705                                 counter1 = 0.1*Rand(1, 40);\r
706                                 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];\r
707                         }\r
708                 }\r
709                 {\r
710                         static float counter1 = 0;\r
711                         counter1 -= dtime;\r
712                         if(counter1 < 0.0)\r
713                         {\r
714                                 counter1 = 0.1*Rand(1, 40);\r
715                                 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];\r
716                         }\r
717                 }\r
718                 {\r
719                         static float counter1 = 0;\r
720                         counter1 -= dtime;\r
721                         if(counter1 < 0.0)\r
722                         {\r
723                                 counter1 = 0.1*Rand(1, 40);\r
724                                 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];\r
725                         }\r
726                 }\r
727                 {\r
728                         static float counter1 = 0;\r
729                         counter1 -= dtime;\r
730                         if(counter1 < 0.0)\r
731                         {\r
732                                 counter1 = 0.1*Rand(1, 20);\r
733                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));\r
734                         }\r
735                 }\r
736                 {\r
737                         static float counter1 = 0;\r
738                         counter1 -= dtime;\r
739                         if(counter1 < 0.0)\r
740                         {\r
741                                 counter1 = 0.1*Rand(1, 30);\r
742                                 leftclicked = true;\r
743                         }\r
744                 }\r
745                 {\r
746                         static float counter1 = 0;\r
747                         counter1 -= dtime;\r
748                         if(counter1 < 0.0)\r
749                         {\r
750                                 counter1 = 0.1*Rand(1, 20);\r
751                                 rightclicked = true;\r
752                         }\r
753                 }\r
754                 mousepos += mousespeed;\r
755         }\r
756 \r
757         s32 Rand(s32 min, s32 max)\r
758         {\r
759                 return (rand()%(max-min+1))+min;\r
760         }\r
761 private:\r
762         bool keydown[KEY_KEY_CODES_COUNT];\r
763         v2s32 mousepos;\r
764         v2s32 mousespeed;\r
765         bool leftclicked;\r
766         bool rightclicked;\r
767 };\r
768 \r
769 void updateViewingRange(f32 frametime, Client *client)\r
770 {\r
771         // Range_all messes up frametime_avg\r
772         if(g_viewing_range_all == true)\r
773                 return;\r
774 \r
775         float wanted_fps = g_settings.getFloat("wanted_fps");\r
776         \r
777         // Initialize to the target value\r
778         static float frametime_avg = 1.0/wanted_fps;\r
779         frametime_avg = frametime_avg * 0.9 + frametime * 0.1;\r
780 \r
781         static f32 counter = 0;\r
782         if(counter > 0){\r
783                 counter -= frametime;\r
784                 return;\r
785         }\r
786         //counter = 1.0; //seconds\r
787         counter = 0.5; //seconds\r
788 \r
789         //float freetime_ratio = 0.2;\r
790         //float freetime_ratio = 0.4;\r
791         float freetime_ratio = FREETIME_RATIO;\r
792 \r
793         float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));\r
794 \r
795         float fraction = sqrt(frametime_avg / frametime_wanted);\r
796 \r
797         static bool fraction_is_good = false;\r
798         \r
799         float fraction_good_threshold = 0.1;\r
800         //float fraction_bad_threshold = 0.25;\r
801         float fraction_bad_threshold = 0.1;\r
802         float fraction_limit;\r
803         // Use high limit if fraction is good AND the fraction would\r
804         // lower the range. We want to keep the range fairly high.\r
805         if(fraction_is_good && fraction > 1.0)\r
806                 fraction_limit = fraction_bad_threshold;\r
807         else\r
808                 fraction_limit = fraction_good_threshold;\r
809 \r
810         if(fabs(fraction - 1.0) < fraction_limit)\r
811         {\r
812                 fraction_is_good = true;\r
813                 return;\r
814         }\r
815         else\r
816         {\r
817                 fraction_is_good = false;\r
818         }\r
819 \r
820         //dstream<<"frametime_avg="<<frametime_avg<<std::endl;\r
821         //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;\r
822         /*dstream<<"fetching="<<client->isFetchingBlocks()\r
823                         <<" faction = "<<fraction<<std::endl;*/\r
824 \r
825         JMutexAutoLock lock(g_range_mutex);\r
826         \r
827         s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");\r
828         s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");\r
829 \r
830         s16 n = (float)g_viewing_range_nodes / fraction;\r
831         if(n < viewing_range_nodes_min)\r
832                 n = viewing_range_nodes_min;\r
833         if(n > viewing_range_nodes_max)\r
834                 n = viewing_range_nodes_max;\r
835 \r
836         bool can_change = true;\r
837 \r
838         if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)\r
839                 can_change = false;\r
840         \r
841         if(can_change)\r
842                 g_viewing_range_nodes = n;\r
843 \r
844         /*dstream<<"g_viewing_range_nodes = "\r
845                         <<g_viewing_range_nodes<<std::endl;*/\r
846 }\r
847 \r
848 class GUIQuickInventory : public IEventReceiver\r
849 {\r
850 public:\r
851         GUIQuickInventory(\r
852                         gui::IGUIEnvironment* env,\r
853                         gui::IGUIElement* parent,\r
854                         v2s32 pos,\r
855                         s32 itemcount,\r
856                         Inventory *inventory):\r
857                 m_itemcount(itemcount),\r
858                 m_inventory(inventory)\r
859         {\r
860                 core::rect<s32> imgsize(0,0,48,48);\r
861                 core::rect<s32> textsize(0,0,48,16);\r
862                 v2s32 spacing(0, 64);\r
863                 for(s32 i=0; i<m_itemcount; i++)\r
864                 {\r
865                         m_images.push_back(env->addImage(\r
866                                 imgsize + pos + spacing*i\r
867                         ));\r
868                         m_images[i]->setScaleImage(true);\r
869                         m_texts.push_back(env->addStaticText(\r
870                                 L"",\r
871                                 textsize + pos + spacing*i,\r
872                                 false, false\r
873                         ));\r
874                         m_texts[i]->setBackgroundColor(\r
875                                         video::SColor(128,0,0,0));\r
876                         m_texts[i]->setTextAlignment(\r
877                                         gui::EGUIA_CENTER,\r
878                                         gui::EGUIA_UPPERLEFT);\r
879                 }\r
880         }\r
881 \r
882         virtual bool OnEvent(const SEvent& event)\r
883         {\r
884                 return false;\r
885         }\r
886 \r
887         void setSelection(s32 i)\r
888         {\r
889                 m_selection = i;\r
890         }\r
891 \r
892         void update()\r
893         {\r
894                 s32 start = 0;\r
895 \r
896                 start = m_selection - m_itemcount / 2;\r
897 \r
898                 for(s32 i=0; i<m_itemcount; i++)\r
899                 {\r
900                         s32 j = i + start;\r
901 \r
902                         if(j > (s32)m_inventory->getSize() - 1)\r
903                                 j -= m_inventory->getSize();\r
904                         if(j < 0)\r
905                                 j += m_inventory->getSize();\r
906                         \r
907                         InventoryItem *item = m_inventory->getItem(j);\r
908                         // Null items\r
909                         if(item == NULL)\r
910                         {\r
911                                 m_images[i]->setImage(NULL);\r
912 \r
913                                 wchar_t t[10];\r
914                                 if(m_selection == j)\r
915                                         swprintf(t, 10, L"<-");\r
916                                 else\r
917                                         swprintf(t, 10, L"");\r
918                                 m_texts[i]->setText(t);\r
919 \r
920                                 // The next ifs will segfault with a NULL pointer\r
921                                 continue;\r
922                         }\r
923                         \r
924                         \r
925                         m_images[i]->setImage(item->getImage());\r
926                         \r
927                         wchar_t t[10];\r
928                         if(m_selection == j)\r
929                                 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());\r
930                         else\r
931                                 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());\r
932                         m_texts[i]->setText(t);\r
933                 }\r
934         }\r
935 \r
936 private:\r
937         s32 m_itemcount;\r
938         core::array<gui::IGUIStaticText*> m_texts;\r
939         core::array<gui::IGUIImage*> m_images;\r
940         Inventory *m_inventory;\r
941         s32 m_selection;\r
942 };\r
943 \r
944 int main(int argc, char *argv[])\r
945 {\r
946         /*\r
947                 Low-level initialization\r
948         */\r
949 \r
950         bool disable_stderr = false;\r
951 #ifdef _WIN32\r
952         disable_stderr = true;\r
953 #endif\r
954 \r
955         // Initialize debug streams\r
956         debugstreams_init(disable_stderr, DEBUGFILE);\r
957         // Initialize debug stacks\r
958         debug_stacks_init();\r
959 \r
960         DSTACK(__FUNCTION_NAME);\r
961 \r
962         try\r
963         {\r
964         \r
965         /*\r
966                 Basic initialization\r
967         */\r
968 \r
969         // Initialize default settings\r
970         set_default_settings();\r
971         \r
972         // Print startup message\r
973         dstream<<DTIME<<"minetest-c55"\r
974                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST\r
975                         <<", ENABLE_TESTS="<<ENABLE_TESTS\r
976                         <<std::endl;\r
977         \r
978         // Set locale. This is for forcing '.' as the decimal point.\r
979         std::locale::global(std::locale("C"));\r
980         // This enables printing all characters in bitmap font\r
981         setlocale(LC_CTYPE, "en_US");\r
982 \r
983         // Initialize sockets\r
984         sockets_init();\r
985         atexit(sockets_cleanup);\r
986         \r
987         // Initialize timestamp mutex\r
988         g_timestamp_mutex.Init();\r
989 \r
990         /*\r
991                 Run unit tests\r
992         */\r
993         if(ENABLE_TESTS)\r
994         {\r
995                 run_tests();\r
996         }\r
997         \r
998         /*\r
999                 Initialization\r
1000         */\r
1001 \r
1002         // Read config file\r
1003         \r
1004         if(argc >= 2)\r
1005         {\r
1006                 g_settings.readConfigFile(argv[1]);\r
1007         }\r
1008         else\r
1009         {\r
1010                 const char *filenames[2] =\r
1011                 {\r
1012                         "../minetest.conf",\r
1013                         "../../minetest.conf"\r
1014                 };\r
1015 \r
1016                 for(u32 i=0; i<2; i++)\r
1017                 {\r
1018                         bool r = g_settings.readConfigFile(filenames[i]);\r
1019                         if(r)\r
1020                                 break;\r
1021                 }\r
1022         }\r
1023 \r
1024         // Initialize random seed\r
1025         srand(time(0));\r
1026 \r
1027         g_range_mutex.Init();\r
1028         assert(g_range_mutex.IsInitialized());\r
1029 \r
1030         // Read map parameters from settings\r
1031 \r
1032         HMParams hm_params;\r
1033         hm_params.blocksize = g_settings.getU16("heightmap_blocksize");\r
1034         hm_params.randmax = g_settings.get("height_randmax");\r
1035         hm_params.randfactor = g_settings.get("height_randfactor");\r
1036         hm_params.base = g_settings.get("height_base");\r
1037 \r
1038         MapParams map_params;\r
1039         map_params.plants_amount = g_settings.getFloat("plants_amount");\r
1040         map_params.ravines_amount = g_settings.getFloat("ravines_amount");\r
1041 \r
1042         /*\r
1043                 Ask some stuff\r
1044         */\r
1045 \r
1046         std::cout<<std::endl<<std::endl;\r
1047         \r
1048         std::cout\r
1049         <<"        .__               __                   __   "<<std::endl\r
1050         <<"  _____ |__| ____   _____/  |_  ____   _______/  |_ "<<std::endl\r
1051         <<" /     \\|  |/    \\_/ __ \\   __\\/ __ \\ /  ___/\\   __\\"<<std::endl\r
1052         <<"|  Y Y  \\  |   |  \\  ___/|  | \\  ___/ \\___ \\  |  |  "<<std::endl\r
1053         <<"|__|_|  /__|___|  /\\___  >__|  \\___  >____  > |__|  "<<std::endl\r
1054         <<"      \\/        \\/     \\/          \\/     \\/        "<<std::endl\r
1055         <<std::endl\r
1056         <<"Now with more waterish water!"\r
1057         <<std::endl;\r
1058 \r
1059         std::cout<<std::endl;\r
1060         char templine[100];\r
1061         \r
1062         // Dedicated?\r
1063         bool dedicated = g_settings.getBoolAsk\r
1064                         ("dedicated_server", "Dedicated server?", false);\r
1065         std::cout<<"dedicated = "<<dedicated<<std::endl;\r
1066         \r
1067         // Port?\r
1068         u16 port = g_settings.getU16Ask("port", "Port", 30000);\r
1069         std::cout<<"-> "<<port<<std::endl;\r
1070         \r
1071         if(dedicated)\r
1072         {\r
1073                 DSTACK("Dedicated server branch");\r
1074                 \r
1075                 std::cout<<std::endl;\r
1076                 std::cout<<"========================"<<std::endl;\r
1077                 std::cout<<"Running dedicated server"<<std::endl;\r
1078                 std::cout<<"========================"<<std::endl;\r
1079                 std::cout<<std::endl;\r
1080 \r
1081                 Server server("../map", hm_params, map_params);\r
1082                 server.start(port);\r
1083         \r
1084                 for(;;)\r
1085                 {\r
1086                         // This is kind of a hack but can be done like this\r
1087                         // because server.step() is very light\r
1088                         sleep_ms(30);\r
1089                         server.step(0.030);\r
1090 \r
1091                         static int counter = 0;\r
1092                         counter--;\r
1093                         if(counter <= 0)\r
1094                         {\r
1095                                 counter = 10;\r
1096 \r
1097                                 core::list<PlayerInfo> list = server.getPlayerInfo();\r
1098                                 core::list<PlayerInfo>::Iterator i;\r
1099                                 static u32 sum_old = 0;\r
1100                                 u32 sum = PIChecksum(list);\r
1101                                 if(sum != sum_old)\r
1102                                 {\r
1103                                         std::cout<<DTIME<<"Player info:"<<std::endl;\r
1104                                         for(i=list.begin(); i!=list.end(); i++)\r
1105                                         {\r
1106                                                 i->PrintLine(&std::cout);\r
1107                                         }\r
1108                                 }\r
1109                                 sum_old = sum;\r
1110                         }\r
1111                 }\r
1112 \r
1113                 return 0;\r
1114         }\r
1115 \r
1116         bool hosting = false;\r
1117         char connect_name[100] = "";\r
1118 \r
1119         std::cout<<"Address to connect to [empty = host a game]: ";\r
1120         if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)\r
1121         {\r
1122                 std::cout<<g_settings.get("address")<<std::endl;\r
1123                 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());\r
1124         }\r
1125         else\r
1126         {\r
1127                 std::cin.getline(connect_name, 100);\r
1128         }\r
1129         \r
1130         if(connect_name[0] == 0){\r
1131                 snprintf(connect_name, 100, "127.0.0.1");\r
1132                 hosting = true;\r
1133         }\r
1134         \r
1135         if(hosting)\r
1136                 std::cout<<"-> hosting"<<std::endl;\r
1137         else\r
1138                 std::cout<<"-> "<<connect_name<<std::endl;\r
1139         \r
1140         char playername[PLAYERNAME_SIZE] = "";\r
1141         if(g_settings.get("name") != "")\r
1142         {\r
1143                 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());\r
1144         }\r
1145         else\r
1146         {\r
1147                 std::cout<<"Name of player: ";\r
1148                 std::cin.getline(playername, PLAYERNAME_SIZE);\r
1149         }\r
1150         std::cout<<"-> \""<<playername<<"\""<<std::endl;\r
1151 \r
1152         /*\r
1153                 Resolution selection\r
1154         */\r
1155 \r
1156         u16 screenW;\r
1157         u16 screenH;\r
1158         bool fullscreen = false;\r
1159         \r
1160         if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")\r
1161         {\r
1162                 screenW = atoi(g_settings.get("screenW").c_str());\r
1163                 screenH = atoi(g_settings.get("screenH").c_str());\r
1164         }\r
1165         else\r
1166         {\r
1167                 u16 resolutions[][3] = {\r
1168                         //W, H, fullscreen\r
1169                         {640,480, 0},\r
1170                         {800,600, 0},\r
1171                         {1024,768, 0},\r
1172                         {1280,1024, 0},\r
1173                         /*{640,480, 1},\r
1174                         {800,600, 1},\r
1175                         {1024,768, 1},\r
1176                         {1280,1024, 1},*/\r
1177                 };\r
1178 \r
1179                 u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);\r
1180                 \r
1181                 for(u16 i=0; i<res_count; i++)\r
1182                 {\r
1183                         std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"\r
1184                                         <<resolutions[i][1];\r
1185                         if(resolutions[i][2])\r
1186                                 std::cout<<" fullscreen"<<std::endl;\r
1187                         else\r
1188                                 std::cout<<" windowed"<<std::endl;\r
1189                 }\r
1190                 std::cout<<"Select a window resolution number [empty = 2]: ";\r
1191                 std::cin.getline(templine, 100);\r
1192 \r
1193                 u16 r0;\r
1194                 if(templine[0] == 0)\r
1195                         r0 = 2;\r
1196                 else\r
1197                         r0 = atoi(templine);\r
1198 \r
1199                 if(r0 > res_count || r0 == 0)\r
1200                         r0 = 2;\r
1201                 \r
1202                 {\r
1203                         u16 i = r0-1;\r
1204                         std::cout<<"-> ";\r
1205                         std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"\r
1206                                         <<resolutions[i][1];\r
1207                         if(resolutions[i][2])\r
1208                                 std::cout<<" fullscreen"<<std::endl;\r
1209                         else\r
1210                                 std::cout<<" windowed"<<std::endl;\r
1211                 }\r
1212 \r
1213                 screenW = resolutions[r0-1][0];\r
1214                 screenH = resolutions[r0-1][1];\r
1215                 fullscreen = resolutions[r0-1][2];\r
1216         }\r
1217 \r
1218         //\r
1219 \r
1220         MyEventReceiver receiver;\r
1221 \r
1222         video::E_DRIVER_TYPE driverType;\r
1223 \r
1224 #ifdef _WIN32\r
1225         //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work\r
1226         driverType = video::EDT_OPENGL;\r
1227 #else\r
1228         driverType = video::EDT_OPENGL;\r
1229 #endif\r
1230 \r
1231         // create device and exit if creation failed\r
1232 \r
1233         IrrlichtDevice *device;\r
1234         device = createDevice(driverType,\r
1235                         core::dimension2d<u32>(screenW, screenH),\r
1236                         16, fullscreen, false, false, &receiver);\r
1237         // With vsync\r
1238         /*device = createDevice(driverType,\r
1239                         core::dimension2d<u32>(screenW, screenH),\r
1240                         16, fullscreen, false, true, &receiver);*/\r
1241 \r
1242         if (device == 0)\r
1243                 return 1; // could not create selected driver.\r
1244 \r
1245         g_device = device;\r
1246         \r
1247         device->setResizable(true);\r
1248 \r
1249         if(g_settings.getBool("random_input"))\r
1250                 g_input = new RandomInputHandler();\r
1251         else\r
1252                 g_input = new RealInputHandler(device, &receiver);\r
1253         \r
1254         /*\r
1255                 Continue initialization\r
1256         */\r
1257 \r
1258         video::IVideoDriver* driver = device->getVideoDriver();\r
1259         // These make the textures not to show at all\r
1260         //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);\r
1261         //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );\r
1262 \r
1263         //driver->setMinHardwareBufferVertexCount(1);\r
1264 \r
1265         scene::ISceneManager* smgr = device->getSceneManager();\r
1266         \r
1267         // Pause menu\r
1268         guiPauseMenu pauseMenu(device, &receiver);\r
1269 \r
1270         gui::IGUIEnvironment* guienv = device->getGUIEnvironment();\r
1271         gui::IGUISkin* skin = guienv->getSkin();\r
1272         gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");\r
1273         if(font)\r
1274                 skin->setFont(font);\r
1275         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
1276         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
1277         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
1278         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));\r
1279         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));\r
1280         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));\r
1281         \r
1282         const wchar_t *text = L"Loading and connecting...";\r
1283         core::vector2d<s32> center(screenW/2, screenH/2);\r
1284         core::dimension2d<u32> textd = font->getDimension(text);\r
1285         std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;\r
1286         // Have to add a bit to disable the text from word wrapping\r
1287         //core::vector2d<s32> textsize(textd.Width+4, textd.Height);\r
1288         core::vector2d<s32> textsize(300, textd.Height);\r
1289         core::rect<s32> textrect(center - textsize/2, center + textsize/2);\r
1290 \r
1291         gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(\r
1292                         text, textrect, false, false);\r
1293         gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);\r
1294 \r
1295         driver->beginScene(true, true, video::SColor(255,0,0,0));\r
1296         guienv->drawAll();\r
1297         driver->endScene();\r
1298 \r
1299         /*\r
1300                 Preload some random textures that are used in threads\r
1301         */\r
1302         \r
1303         g_texturecache.set("torch", driver->getTexture("../data/torch.png"));\r
1304         g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));\r
1305         g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));\r
1306         \r
1307         /*\r
1308                 Load tile textures\r
1309         */\r
1310         for(s32 i=0; i<TILES_COUNT; i++)\r
1311         {\r
1312                 if(g_tile_texture_names[i] == NULL)\r
1313                         continue;\r
1314                 std::string name = g_tile_texture_names[i];\r
1315                 std::string filename;\r
1316                 filename += "../data/";\r
1317                 filename += name;\r
1318                 filename += ".png";\r
1319                 g_texturecache.set(name, driver->getTexture(filename.c_str()));\r
1320         }\r
1321 \r
1322         tile_materials_preload(g_texturecache);\r
1323 \r
1324         /*\r
1325                 Make a scope here for the client so that it gets removed\r
1326                 before the irrlicht device\r
1327         */\r
1328         {\r
1329 \r
1330         std::cout<<DTIME<<"Creating server and client"<<std::endl;\r
1331         \r
1332         /*\r
1333                 Create server\r
1334         */\r
1335         SharedPtr<Server> server;\r
1336         if(hosting){\r
1337                 server = new Server("../map", hm_params, map_params);\r
1338                 server->start(port);\r
1339         }\r
1340         \r
1341         /*\r
1342                 Create client\r
1343         */\r
1344 \r
1345         Client client(device, playername);\r
1346         \r
1347         Address connect_address(0,0,0,0, port);\r
1348         try{\r
1349                 connect_address.Resolve(connect_name);\r
1350         }\r
1351         catch(ResolveError &e)\r
1352         {\r
1353                 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;\r
1354                 return 0;\r
1355         }\r
1356         \r
1357         std::cout<<DTIME<<"Connecting to server..."<<std::endl;\r
1358         client.connect(connect_address);\r
1359         \r
1360         try{\r
1361                 while(client.connectedAndInitialized() == false)\r
1362                 {\r
1363                         client.step(0.1);\r
1364                         if(server != NULL){\r
1365                                 server->step(0.1);\r
1366                         }\r
1367                         sleep_ms(100);\r
1368                 }\r
1369         }\r
1370         catch(con::PeerNotFoundException &e)\r
1371         {\r
1372                 std::cout<<DTIME<<"Timed out."<<std::endl;\r
1373                 return 0;\r
1374         }\r
1375 \r
1376         /*\r
1377                 Create skybox\r
1378         */\r
1379         scene::ISceneNode* skybox;\r
1380         skybox = smgr->addSkyBoxSceneNode(\r
1381                 driver->getTexture("../data/skybox2.png"),\r
1382                 driver->getTexture("../data/skybox3.png"),\r
1383                 driver->getTexture("../data/skybox1.png"),\r
1384                 driver->getTexture("../data/skybox1.png"),\r
1385                 driver->getTexture("../data/skybox1.png"),\r
1386                 driver->getTexture("../data/skybox1.png"));\r
1387         \r
1388         /*\r
1389                 Create the camera node\r
1390         */\r
1391 \r
1392         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(\r
1393                 0, // Camera parent\r
1394                 v3f(BS*100, BS*2, BS*100), // Look from\r
1395                 v3f(BS*100+1, BS*2, BS*100), // Look to\r
1396                 -1 // Camera ID\r
1397         );\r
1398 \r
1399         if(camera == NULL)\r
1400                 return 1;\r
1401         \r
1402         video::SColor skycolor = video::SColor(255,90,140,200);\r
1403 \r
1404         camera->setFOV(FOV_ANGLE);\r
1405 \r
1406         // Just so big a value that everything rendered is visible\r
1407         camera->setFarValue(100000*BS);\r
1408 \r
1409         /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;\r
1410         f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;\r
1411         \r
1412         camera->setFarValue(range);\r
1413         \r
1414         driver->setFog(\r
1415                 skycolor,\r
1416                 video::EFT_FOG_LINEAR,\r
1417                 range*0.8,\r
1418                 range,\r
1419                 0.01,\r
1420                 false,\r
1421                 false\r
1422                 );*/\r
1423         \r
1424         f32 camera_yaw = 0; // "right/left"\r
1425         f32 camera_pitch = 0; // "up/down"\r
1426 \r
1427         /*\r
1428                 Move into game\r
1429         */\r
1430         \r
1431         gui_loadingtext->remove();\r
1432 \r
1433         pauseMenu.setVisible(true);\r
1434 \r
1435         /*\r
1436                 Add some gui stuff\r
1437         */\r
1438         \r
1439         // First line of debug text\r
1440         gui::IGUIStaticText *guitext = guienv->addStaticText(\r
1441                         L"Minetest-c55",\r
1442                         core::rect<s32>(5, 5, 795, 5+textsize.Y),\r
1443                         false, false);\r
1444         // Second line of debug text\r
1445         gui::IGUIStaticText *guitext2 = guienv->addStaticText(\r
1446                         L"",\r
1447                         core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),\r
1448                         false, false);\r
1449         \r
1450         // At the middle of the screen\r
1451         // Object infos are shown in this\r
1452         gui::IGUIStaticText *guitext_info = guienv->addStaticText(\r
1453                         L"test",\r
1454                         core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),\r
1455                         false, false);\r
1456         \r
1457         // This is a copy of the inventory that the client's environment has\r
1458         Inventory local_inventory(PLAYER_INVENTORY_SIZE);\r
1459         \r
1460         GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
1461                         (guienv, NULL, v2s32(10, 70), 5, &local_inventory);\r
1462         \r
1463         /*\r
1464                 Some statistics are collected in these\r
1465         */\r
1466         u32 drawtime = 0;\r
1467         u32 beginscenetime = 0;\r
1468         u32 scenetime = 0;\r
1469         u32 endscenetime = 0;\r
1470 \r
1471         /*\r
1472                 Text input system\r
1473         */\r
1474         \r
1475         struct TextDest\r
1476         {\r
1477                 virtual void sendText(std::string text) = 0;\r
1478         };\r
1479         \r
1480         struct TextDestSign : public TextDest\r
1481         {\r
1482                 TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
1483                 {\r
1484                         m_blockpos = blockpos;\r
1485                         m_id = id;\r
1486                         m_client = client;\r
1487                 }\r
1488                 void sendText(std::string text)\r
1489                 {\r
1490                         dstream<<"Changing text of a sign object: "\r
1491                                         <<text<<std::endl;\r
1492                         m_client->sendSignText(m_blockpos, m_id, text);\r
1493                 }\r
1494 \r
1495                 v3s16 m_blockpos;\r
1496                 s16 m_id;\r
1497                 Client *m_client;\r
1498         };\r
1499 \r
1500         TextDest *textbuf_dest = NULL;\r
1501         \r
1502         //gui::IGUIWindow* input_window = NULL;\r
1503         gui::IGUIStaticText* input_guitext = NULL;\r
1504 \r
1505         /*\r
1506                 Main loop\r
1507         */\r
1508 \r
1509         bool first_loop_after_window_activation = true;\r
1510 \r
1511         // Time is in milliseconds\r
1512         // NOTE: getRealTime() without run()s causes strange problems in wine\r
1513         // NOTE: Have to call run() between calls of this to update the timer\r
1514         u32 lasttime = device->getTimer()->getTime();\r
1515 \r
1516         while(device->run())\r
1517         {\r
1518                 /*\r
1519                         Random calculations\r
1520                 */\r
1521                 v2u32 screensize = driver->getScreenSize();\r
1522                 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);\r
1523 \r
1524                 // Hilight boxes collected during the loop and displayed\r
1525                 core::list< core::aabbox3d<f32> > hilightboxes;\r
1526                 \r
1527                 // Info text\r
1528                 std::wstring infotext;\r
1529 \r
1530                 //TimeTaker //timer1("//timer1", device);\r
1531                 \r
1532                 // Time of frame without fps limit\r
1533                 float busytime;\r
1534                 u32 busytime_u32;\r
1535                 {\r
1536                         // not using getRealTime is necessary for wine\r
1537                         u32 time = device->getTimer()->getTime();\r
1538                         if(time > lasttime)\r
1539                                 busytime_u32 = time - lasttime;\r
1540                         else\r
1541                                 busytime_u32 = 0;\r
1542                         busytime = busytime_u32 / 1000.0;\r
1543                 }\r
1544 \r
1545                 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;\r
1546         \r
1547                 // Absolutelu necessary for wine!\r
1548                 device->run();\r
1549 \r
1550                 /*\r
1551                         Viewing range\r
1552                 */\r
1553                 \r
1554                 //updateViewingRange(dtime, &client);\r
1555                 updateViewingRange(busytime, &client);\r
1556                 \r
1557                 /*\r
1558                         FPS limiter\r
1559                 */\r
1560 \r
1561                 {\r
1562                         float fps_max = g_settings.getFloat("fps_max");\r
1563                         u32 frametime_min = 1000./fps_max;\r
1564                         \r
1565                         if(busytime_u32 < frametime_min)\r
1566                         {\r
1567                                 u32 sleeptime = frametime_min - busytime_u32;\r
1568                                 device->sleep(sleeptime);\r
1569                         }\r
1570                 }\r
1571 \r
1572                 // Absolutelu necessary for wine!\r
1573                 device->run();\r
1574 \r
1575                 /*\r
1576                         Time difference calculation\r
1577                 */\r
1578                 f32 dtime; // in seconds\r
1579                 \r
1580                 u32 time = device->getTimer()->getTime();\r
1581                 if(time > lasttime)\r
1582                         dtime = (time - lasttime) / 1000.0;\r
1583                 else\r
1584                         dtime = 0;\r
1585                 lasttime = time;\r
1586 \r
1587                 /*\r
1588                         Time average and jitter calculation\r
1589                 */\r
1590 \r
1591                 static f32 dtime_avg1 = 0.0;\r
1592                 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;\r
1593                 f32 dtime_jitter1 = dtime - dtime_avg1;\r
1594 \r
1595                 static f32 dtime_jitter1_max_sample = 0.0;\r
1596                 static f32 dtime_jitter1_max_fraction = 0.0;\r
1597                 {\r
1598                         static f32 jitter1_max = 0.0;\r
1599                         static f32 counter = 0.0;\r
1600                         if(dtime_jitter1 > jitter1_max)\r
1601                                 jitter1_max = dtime_jitter1;\r
1602                         counter += dtime;\r
1603                         if(counter > 0.0)\r
1604                         {\r
1605                                 counter -= 3.0;\r
1606                                 dtime_jitter1_max_sample = jitter1_max;\r
1607                                 dtime_jitter1_max_fraction\r
1608                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);\r
1609                                 jitter1_max = 0.0;\r
1610                                 \r
1611                                 /*\r
1612                                         Control freetime ratio\r
1613                                 */\r
1614                                 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)\r
1615                                 {\r
1616                                         if(g_freetime_ratio < FREETIME_RATIO_MAX)\r
1617                                                 g_freetime_ratio += 0.01;\r
1618                                 }\r
1619                                 else\r
1620                                 {\r
1621                                         if(g_freetime_ratio > FREETIME_RATIO_MIN)\r
1622                                                 g_freetime_ratio -= 0.01;\r
1623                                 }*/\r
1624                         }\r
1625                 }\r
1626                 \r
1627                 /*\r
1628                         Busytime average and jitter calculation\r
1629                 */\r
1630 \r
1631                 static f32 busytime_avg1 = 0.0;\r
1632                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;\r
1633                 f32 busytime_jitter1 = busytime - busytime_avg1;\r
1634                 \r
1635                 static f32 busytime_jitter1_max_sample = 0.0;\r
1636                 static f32 busytime_jitter1_min_sample = 0.0;\r
1637                 {\r
1638                         static f32 jitter1_max = 0.0;\r
1639                         static f32 jitter1_min = 0.0;\r
1640                         static f32 counter = 0.0;\r
1641                         if(busytime_jitter1 > jitter1_max)\r
1642                                 jitter1_max = busytime_jitter1;\r
1643                         if(busytime_jitter1 < jitter1_min)\r
1644                                 jitter1_min = busytime_jitter1;\r
1645                         counter += dtime;\r
1646                         if(counter > 0.0){\r
1647                                 counter -= 3.0;\r
1648                                 busytime_jitter1_max_sample = jitter1_max;\r
1649                                 busytime_jitter1_min_sample = jitter1_min;\r
1650                                 jitter1_max = 0.0;\r
1651                                 jitter1_min = 0.0;\r
1652                         }\r
1653                 }\r
1654                 \r
1655                 /*\r
1656                         Debug info for client\r
1657                 */\r
1658                 {\r
1659                         static float counter = 0.0;\r
1660                         counter -= dtime;\r
1661                         if(counter < 0)\r
1662                         {\r
1663                                 counter = 30.0;\r
1664                                 client.printDebugInfo(std::cout);\r
1665                         }\r
1666                 }\r
1667 \r
1668                 /*\r
1669                         Input handler step()\r
1670                 */\r
1671                 g_input->step(dtime);\r
1672 \r
1673                 /*\r
1674                         Special keys\r
1675                 */\r
1676                 /*if(g_esc_pressed)\r
1677                 {\r
1678                         break;\r
1679                 }*/\r
1680 \r
1681                 /*\r
1682                         Player speed control\r
1683                 */\r
1684                 \r
1685                 if(g_game_focused)\r
1686                 {\r
1687                         /*bool a_up,\r
1688                         bool a_down,\r
1689                         bool a_left,\r
1690                         bool a_right,\r
1691                         bool a_jump,\r
1692                         bool a_superspeed,\r
1693                         float a_pitch,\r
1694                         float a_yaw*/\r
1695                         PlayerControl control(\r
1696                                 g_input->isKeyDown(irr::KEY_KEY_W),\r
1697                                 g_input->isKeyDown(irr::KEY_KEY_S),\r
1698                                 g_input->isKeyDown(irr::KEY_KEY_A),\r
1699                                 g_input->isKeyDown(irr::KEY_KEY_D),\r
1700                                 g_input->isKeyDown(irr::KEY_SPACE),\r
1701                                 g_input->isKeyDown(irr::KEY_KEY_2),\r
1702                                 camera_pitch,\r
1703                                 camera_yaw\r
1704                         );\r
1705                         client.setPlayerControl(control);\r
1706                 }\r
1707                 else\r
1708                 {\r
1709                         // Set every key to inactive\r
1710                         PlayerControl control;\r
1711                         client.setPlayerControl(control);\r
1712                 }\r
1713 \r
1714                 //timer1.stop();\r
1715                 /*\r
1716                         Process environment\r
1717                 */\r
1718                 \r
1719                 {\r
1720                         //TimeTaker timer("client.step(dtime)", device);\r
1721                         client.step(dtime);\r
1722                         //client.step(dtime_avg1);\r
1723                 }\r
1724 \r
1725                 if(server != NULL)\r
1726                 {\r
1727                         //TimeTaker timer("server->step(dtime)", device);\r
1728                         server->step(dtime);\r
1729                 }\r
1730 \r
1731                 v3f player_position = client.getPlayerPosition();\r
1732                 \r
1733                 //TimeTaker //timer2("//timer2", device);\r
1734 \r
1735                 /*\r
1736                         Mouse and camera control\r
1737                 */\r
1738                 \r
1739                 if(device->isWindowActive() && g_game_focused && !pauseMenu.isVisible())\r
1740                 {\r
1741                         device->getCursorControl()->setVisible(false);\r
1742 \r
1743                         if(first_loop_after_window_activation){\r
1744                                 //std::cout<<"window active, first loop"<<std::endl;\r
1745                                 first_loop_after_window_activation = false;\r
1746                         }\r
1747                         else{\r
1748                                 s32 dx = g_input->getMousePos().X - displaycenter.X;\r
1749                                 s32 dy = g_input->getMousePos().Y - displaycenter.Y;\r
1750                                 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;\r
1751                                 camera_yaw -= dx*0.2;\r
1752                                 camera_pitch += dy*0.2;\r
1753                                 if(camera_pitch < -89.5) camera_pitch = -89.5;\r
1754                                 if(camera_pitch > 89.5) camera_pitch = 89.5;\r
1755                         }\r
1756                         g_input->setMousePos(displaycenter.X, displaycenter.Y);\r
1757                 }\r
1758                 else{\r
1759                         device->getCursorControl()->setVisible(true);\r
1760 \r
1761                         //std::cout<<"window inactive"<<std::endl;\r
1762                         first_loop_after_window_activation = true;\r
1763                 }\r
1764 \r
1765                 camera_yaw = wrapDegrees(camera_yaw);\r
1766                 camera_pitch = wrapDegrees(camera_pitch);\r
1767                 \r
1768                 v3f camera_direction = v3f(0,0,1);\r
1769                 camera_direction.rotateYZBy(camera_pitch);\r
1770                 camera_direction.rotateXZBy(camera_yaw);\r
1771 \r
1772                 v3f camera_position =\r
1773                                 player_position + v3f(0, BS+BS/2, 0);\r
1774 \r
1775                 camera->setPosition(camera_position);\r
1776                 // *100.0 helps in large map coordinates\r
1777                 camera->setTarget(camera_position + camera_direction * 100.0);\r
1778 \r
1779                 if(FIELD_OF_VIEW_TEST){\r
1780                         //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));\r
1781                         client.updateCamera(v3f(0,0,0), v3f(0,0,1));\r
1782                 }\r
1783                 else{\r
1784                         //client.m_env.getMap().updateCamera(camera_position, camera_direction);\r
1785                         //TimeTaker timer("client.updateCamera", device);\r
1786                         client.updateCamera(camera_position, camera_direction);\r
1787                 }\r
1788                 \r
1789                 //timer2.stop();\r
1790                 //TimeTaker //timer3("//timer3", device);\r
1791 \r
1792                 /*\r
1793                         Calculate what block is the crosshair pointing to\r
1794                 */\r
1795                 \r
1796                 //u32 t1 = device->getTimer()->getRealTime();\r
1797                 \r
1798                 //f32 d = 4; // max. distance\r
1799                 f32 d = 4; // max. distance\r
1800                 core::line3d<f32> shootline(camera_position,\r
1801                                 camera_position + camera_direction * BS * (d+1));\r
1802 \r
1803                 MapBlockObject *selected_object = client.getSelectedObject\r
1804                                 (d*BS, camera_position, shootline);\r
1805 \r
1806                 if(selected_object != NULL)\r
1807                 {\r
1808                         //dstream<<"Client returned selected_object != NULL"<<std::endl;\r
1809 \r
1810                         core::aabbox3d<f32> box_on_map\r
1811                                         = selected_object->getSelectionBoxOnMap();\r
1812 \r
1813                         hilightboxes.push_back(box_on_map);\r
1814 \r
1815                         infotext = narrow_to_wide(selected_object->infoText());\r
1816 \r
1817                         if(g_input->getLeftClicked())\r
1818                         {\r
1819                                 std::cout<<DTIME<<"Left-clicked object"<<std::endl;\r
1820                                 client.clickObject(0, selected_object->getBlock()->getPos(),\r
1821                                                 selected_object->getId(), g_selected_item);\r
1822                         }\r
1823                         else if(g_input->getRightClicked())\r
1824                         {\r
1825                                 std::cout<<DTIME<<"Right-clicked object"<<std::endl;\r
1826                                 /*\r
1827                                         Check if we want to modify the object ourselves\r
1828                                 */\r
1829                                 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)\r
1830                                 {\r
1831                                         dstream<<"Sign object right-clicked"<<std::endl;\r
1832 \r
1833                                         unFocusGame();\r
1834 \r
1835                                         input_guitext = guienv->addStaticText(L"",\r
1836                                                         core::rect<s32>(150,100,350,120),\r
1837                                                         true, // border?\r
1838                                                         false, // wordwrap?\r
1839                                                         NULL);\r
1840 \r
1841                                         input_guitext->setDrawBackground(true);\r
1842 \r
1843                                         g_text_buffer = L"";\r
1844                                         g_text_buffer_accepted = false;\r
1845                                         textbuf_dest = new TextDestSign(\r
1846                                                         selected_object->getBlock()->getPos(),\r
1847                                                         selected_object->getId(),\r
1848                                                         &client);\r
1849                                 }\r
1850                                 /*\r
1851                                         Otherwise pass the event to the server as-is\r
1852                                 */\r
1853                                 else\r
1854                                 {\r
1855                                         client.clickObject(1, selected_object->getBlock()->getPos(),\r
1856                                                         selected_object->getId(), g_selected_item);\r
1857                                 }\r
1858                         }\r
1859                 }\r
1860                 else // selected_object == NULL\r
1861                 {\r
1862                 \r
1863                 bool nodefound = false;\r
1864                 v3s16 nodepos;\r
1865                 v3s16 neighbourpos;\r
1866                 core::aabbox3d<f32> nodefacebox;\r
1867                 f32 mindistance = BS * 1001;\r
1868                 \r
1869                 v3s16 pos_i = floatToInt(player_position);\r
1870 \r
1871                 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
1872                                 <<std::endl;*/\r
1873 \r
1874                 s16 a = d;\r
1875                 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);\r
1876                 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);\r
1877                 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);\r
1878                 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);\r
1879                 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);\r
1880                 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);\r
1881                 \r
1882                 for(s16 y = ystart; y <= yend; y++)\r
1883                 for(s16 z = zstart; z <= zend; z++)\r
1884                 for(s16 x = xstart; x <= xend; x++)\r
1885                 {\r
1886                         MapNode n;\r
1887                         try\r
1888                         {\r
1889                                 n = client.getNode(v3s16(x,y,z));\r
1890                                 if(content_pointable(n.d) == false)\r
1891                                         continue;\r
1892                         }\r
1893                         catch(InvalidPositionException &e)\r
1894                         {\r
1895                                 continue;\r
1896                         }\r
1897 \r
1898                         v3s16 np(x,y,z);\r
1899                         v3f npf = intToFloat(np);\r
1900                         \r
1901                         f32 d = 0.01;\r
1902                         \r
1903                         v3s16 dirs[6] = {\r
1904                                 v3s16(0,0,1), // back\r
1905                                 v3s16(0,1,0), // top\r
1906                                 v3s16(1,0,0), // right\r
1907                                 v3s16(0,0,-1), // front\r
1908                                 v3s16(0,-1,0), // bottom\r
1909                                 v3s16(-1,0,0), // left\r
1910                         };\r
1911                         \r
1912                         /*\r
1913                                 Meta-objects\r
1914                         */\r
1915                         if(n.d == CONTENT_TORCH)\r
1916                         {\r
1917                                 v3s16 dir = unpackDir(n.dir);\r
1918                                 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
1919                                 dir_f *= BS/2 - BS/6 - BS/20;\r
1920                                 v3f cpf = npf + dir_f;\r
1921                                 f32 distance = (cpf - camera_position).getLength();\r
1922 \r
1923                                 core::aabbox3d<f32> box;\r
1924                                 \r
1925                                 // bottom\r
1926                                 if(dir == v3s16(0,-1,0))\r
1927                                 {\r
1928                                         box = core::aabbox3d<f32>(\r
1929                                                 npf - v3f(BS/6, BS/2, BS/6),\r
1930                                                 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)\r
1931                                         );\r
1932                                 }\r
1933                                 // top\r
1934                                 else if(dir == v3s16(0,1,0))\r
1935                                 {\r
1936                                         box = core::aabbox3d<f32>(\r
1937                                                 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),\r
1938                                                 npf + v3f(BS/6, BS/2, BS/6)\r
1939                                         );\r
1940                                 }\r
1941                                 // side\r
1942                                 else\r
1943                                 {\r
1944                                         box = core::aabbox3d<f32>(\r
1945                                                 cpf - v3f(BS/6, BS/3, BS/6),\r
1946                                                 cpf + v3f(BS/6, BS/3, BS/6)\r
1947                                         );\r
1948                                 }\r
1949 \r
1950                                 if(distance < mindistance)\r
1951                                 {\r
1952                                         if(box.intersectsWithLine(shootline))\r
1953                                         {\r
1954                                                 nodefound = true;\r
1955                                                 nodepos = np;\r
1956                                                 neighbourpos = np;\r
1957                                                 mindistance = distance;\r
1958                                                 nodefacebox = box;\r
1959                                         }\r
1960                                 }\r
1961                         }\r
1962                         /*\r
1963                                 Regular blocks\r
1964                         */\r
1965                         else\r
1966                         {\r
1967                                 for(u16 i=0; i<6; i++)\r
1968                                 {\r
1969                                         v3f dir_f = v3f(dirs[i].X,\r
1970                                                         dirs[i].Y, dirs[i].Z);\r
1971                                         v3f centerpoint = npf + dir_f * BS/2;\r
1972                                         f32 distance =\r
1973                                                         (centerpoint - camera_position).getLength();\r
1974                                         \r
1975                                         if(distance < mindistance)\r
1976                                         {\r
1977                                                 core::CMatrix4<f32> m;\r
1978                                                 m.buildRotateFromTo(v3f(0,0,1), dir_f);\r
1979 \r
1980                                                 // This is the back face\r
1981                                                 v3f corners[2] = {\r
1982                                                         v3f(BS/2, BS/2, BS/2),\r
1983                                                         v3f(-BS/2, -BS/2, BS/2+d)\r
1984                                                 };\r
1985                                                 \r
1986                                                 for(u16 j=0; j<2; j++)\r
1987                                                 {\r
1988                                                         m.rotateVect(corners[j]);\r
1989                                                         corners[j] += npf;\r
1990                                                 }\r
1991 \r
1992                                                 core::aabbox3d<f32> facebox(corners[0]);\r
1993                                                 facebox.addInternalPoint(corners[1]);\r
1994 \r
1995                                                 if(facebox.intersectsWithLine(shootline))\r
1996                                                 {\r
1997                                                         nodefound = true;\r
1998                                                         nodepos = np;\r
1999                                                         neighbourpos = np + dirs[i];\r
2000                                                         mindistance = distance;\r
2001                                                         nodefacebox = facebox;\r
2002                                                 }\r
2003                                         } // if distance < mindistance\r
2004                                 } // for dirs\r
2005                         } // regular block\r
2006                 } // for coords\r
2007 \r
2008                 if(nodefound)\r
2009                 {\r
2010                         //std::cout<<DTIME<<"nodefound == true"<<std::endl;\r
2011                         //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
2012                         //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;\r
2013 \r
2014                         static v3s16 nodepos_old(-1,-1,-1);\r
2015                         if(nodepos != nodepos_old){\r
2016                                 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","\r
2017                                                 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
2018                                 nodepos_old = nodepos;\r
2019 \r
2020                                 /*wchar_t positiontext[20];\r
2021                                 swprintf(positiontext, 20, L"(%i,%i,%i)",\r
2022                                                 nodepos.X, nodepos.Y, nodepos.Z);\r
2023                                 positiontextgui->setText(positiontext);*/\r
2024                         }\r
2025 \r
2026                         hilightboxes.push_back(nodefacebox);\r
2027                         \r
2028                         if(g_input->getLeftClicked())\r
2029                         {\r
2030                                 //std::cout<<DTIME<<"Removing node"<<std::endl;\r
2031                                 //client.removeNode(nodepos);\r
2032                                 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;\r
2033                                 client.pressGround(0, nodepos, neighbourpos, g_selected_item);\r
2034                         }\r
2035                         if(g_input->getRightClicked())\r
2036                         {\r
2037                                 //std::cout<<DTIME<<"Placing node"<<std::endl;\r
2038                                 //client.addNodeFromInventory(neighbourpos, g_selected_item);\r
2039                                 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;\r
2040                                 client.pressGround(1, nodepos, neighbourpos, g_selected_item);\r
2041                         }\r
2042                 }\r
2043                 else{\r
2044                         //std::cout<<DTIME<<"nodefound == false"<<std::endl;\r
2045                         //positiontextgui->setText(L"");\r
2046                 }\r
2047 \r
2048                 } // selected_object == NULL\r
2049                 \r
2050                 g_input->resetLeftClicked();\r
2051                 g_input->resetRightClicked();\r
2052                 \r
2053                 /*\r
2054                         Calculate stuff for drawing\r
2055                 */\r
2056 \r
2057                 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);\r
2058 \r
2059                 /*f32 range = g_viewing_range_nodes * BS;\r
2060                 if(g_viewing_range_all)\r
2061                         range = 100000*BS;\r
2062 \r
2063                 driver->setFog(\r
2064                         skycolor,\r
2065                         video::EFT_FOG_LINEAR,\r
2066                         range*0.6,\r
2067                         range,\r
2068                         0.01,\r
2069                         false, // pixel fog\r
2070                         false // range fog\r
2071                         );*/\r
2072 \r
2073 \r
2074                 /*\r
2075                         Update gui stuff (0ms)\r
2076                 */\r
2077 \r
2078                 //TimeTaker guiupdatetimer("Gui updating", device);\r
2079                 \r
2080                 {\r
2081                         wchar_t temptext[150];\r
2082 \r
2083                         static float drawtime_avg = 0;\r
2084                         drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;\r
2085                         static float beginscenetime_avg = 0;\r
2086                         beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;\r
2087                         static float scenetime_avg = 0;\r
2088                         scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;\r
2089                         static float endscenetime_avg = 0;\r
2090                         endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;\r
2091                         \r
2092                         swprintf(temptext, 150, L"Minetest-c55 ("\r
2093                                         L"F: item=%i"\r
2094                                         L", R: range_all=%i"\r
2095                                         L")"\r
2096                                         L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",\r
2097                                         g_selected_item,\r
2098                                         g_viewing_range_all,\r
2099                                         drawtime_avg,\r
2100                                         beginscenetime_avg,\r
2101                                         scenetime_avg,\r
2102                                         endscenetime_avg\r
2103                                         );\r
2104                         \r
2105                         guitext->setText(temptext);\r
2106                 }\r
2107                 \r
2108                 {\r
2109                         wchar_t temptext[150];\r
2110                         swprintf(temptext, 150,\r
2111                                         L"(% .1f, % .1f, % .1f)"\r
2112                                         L" (% .3f < btime_jitter < % .3f"\r
2113                                         L", dtime_jitter = % .1f %%)",\r
2114                                         player_position.X/BS,\r
2115                                         player_position.Y/BS,\r
2116                                         player_position.Z/BS,\r
2117                                         busytime_jitter1_min_sample,\r
2118                                         busytime_jitter1_max_sample,\r
2119                                         dtime_jitter1_max_fraction * 100.0\r
2120                                         );\r
2121 \r
2122                         guitext2->setText(temptext);\r
2123                 }\r
2124                 \r
2125                 {\r
2126                         /*wchar_t temptext[100];\r
2127                         swprintf(temptext, 100,\r
2128                                         SWPRINTF_CHARSTRING,\r
2129                                         infotext.substr(0,99).c_str()\r
2130                                         );\r
2131 \r
2132                         guitext_info->setText(temptext);*/\r
2133 \r
2134                         guitext_info->setText(infotext.c_str());\r
2135                 }\r
2136 \r
2137                 /*\r
2138                         Inventory\r
2139                 */\r
2140                 \r
2141                 static u16 old_selected_item = 65535;\r
2142                 if(client.getLocalInventoryUpdated()\r
2143                                 || g_selected_item != old_selected_item)\r
2144                 {\r
2145                         old_selected_item = g_selected_item;\r
2146                         //std::cout<<"Updating local inventory"<<std::endl;\r
2147                         client.getLocalInventory(local_inventory);\r
2148                         quick_inventory->setSelection(g_selected_item);\r
2149                         quick_inventory->update();\r
2150                 }\r
2151 \r
2152                 if(input_guitext != NULL)\r
2153                 {\r
2154                         /*wchar_t temptext[100];\r
2155                         swprintf(temptext, 100,\r
2156                                         SWPRINTF_CHARSTRING,\r
2157                                         g_text_buffer.substr(0,99).c_str()\r
2158                                         );*/\r
2159                         input_guitext->setText(g_text_buffer.c_str());\r
2160                 }\r
2161 \r
2162                 /*\r
2163                         Text input stuff\r
2164                 */\r
2165                 if(input_guitext != NULL && g_text_buffer_accepted)\r
2166                 {\r
2167                         input_guitext->remove();\r
2168                         input_guitext = NULL;\r
2169                         \r
2170                         if(textbuf_dest != NULL)\r
2171                         {\r
2172                                 std::string text = wide_to_narrow(g_text_buffer);\r
2173                                 dstream<<"Sending text: "<<text<<std::endl;\r
2174                                 textbuf_dest->sendText(text);\r
2175                                 delete textbuf_dest;\r
2176                                 textbuf_dest = NULL;\r
2177                         }\r
2178 \r
2179                         focusGame();\r
2180                 }\r
2181 \r
2182                 //guiupdatetimer.stop();\r
2183 \r
2184                 /*\r
2185                         Drawing begins\r
2186                 */\r
2187 \r
2188                 TimeTaker drawtimer("Drawing", device);\r
2189 \r
2190                 /*\r
2191                         Background color is choosen based on whether the player is\r
2192                         much beyond the initial ground level\r
2193                 */\r
2194                 /*video::SColor bgcolor;\r
2195                 v3s16 p0 = Map::floatToInt(player_position);\r
2196                 // Does this make short random delays?\r
2197                 // NOTE: no need for this, sky doesn't show underground with\r
2198                 // enough range\r
2199                 bool is_underground = client.isNodeUnderground(p0);\r
2200                 //bool is_underground = false;\r
2201                 if(is_underground == false)\r
2202                         bgcolor = video::SColor(255,90,140,200);\r
2203                 else\r
2204                         bgcolor = video::SColor(255,0,0,0);*/\r
2205                         \r
2206                 //video::SColor bgcolor = video::SColor(255,90,140,200);\r
2207                 video::SColor bgcolor = skycolor;\r
2208                 \r
2209                 {\r
2210                 TimeTaker timer("beginScene", device);\r
2211                 driver->beginScene(true, true, bgcolor);\r
2212                 beginscenetime = timer.stop(true);\r
2213                 }\r
2214 \r
2215                 //timer3.stop();\r
2216                 \r
2217                 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;\r
2218                 \r
2219                 {\r
2220                 TimeTaker timer("smgr", device);\r
2221                 smgr->drawAll();\r
2222                 scenetime = timer.stop(true);\r
2223                 }\r
2224                 \r
2225                 {\r
2226                 //TimeTaker timer9("auxiliary drawings", device);\r
2227                 // 0ms\r
2228 \r
2229                 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),\r
2230                                 displaycenter + core::vector2d<s32>(10,0),\r
2231                                 video::SColor(255,255,255,255));\r
2232                 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),\r
2233                                 displaycenter + core::vector2d<s32>(0,10),\r
2234                                 video::SColor(255,255,255,255));\r
2235 \r
2236                 //timer9.stop();\r
2237                 //TimeTaker //timer10("//timer10", device);\r
2238                 \r
2239                 video::SMaterial m;\r
2240                 m.Thickness = 10;\r
2241                 m.Lighting = false;\r
2242                 driver->setMaterial(m);\r
2243 \r
2244                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);\r
2245 \r
2246                 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();\r
2247                                 i != hilightboxes.end(); i++)\r
2248                 {\r
2249                         /*std::cout<<"hilightbox min="\r
2250                                         <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"\r
2251                                         <<" max="\r
2252                                         <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"\r
2253                                         <<std::endl;*/\r
2254                         driver->draw3DBox(*i, video::SColor(255,0,0,0));\r
2255                 }\r
2256 \r
2257                 }\r
2258 \r
2259                 //timer10.stop();\r
2260                 //TimeTaker //timer11("//timer11", device);\r
2261 \r
2262                 /*\r
2263                         Draw gui\r
2264                 */\r
2265                 // 0-1ms\r
2266                 guienv->drawAll();\r
2267                 \r
2268                 // End drawing\r
2269                 {\r
2270                 TimeTaker timer("endScene", device);\r
2271                 driver->endScene();\r
2272                 endscenetime = timer.stop(true);\r
2273                 }\r
2274 \r
2275                 drawtime = drawtimer.stop(true);\r
2276 \r
2277                 /*\r
2278                         Drawing ends\r
2279                 */\r
2280                 \r
2281                 static s16 lastFPS = 0;\r
2282                 //u16 fps = driver->getFPS();\r
2283                 u16 fps = (1.0/dtime_avg1);\r
2284 \r
2285                 if (lastFPS != fps)\r
2286                 {\r
2287                         core::stringw str = L"Minetest [";\r
2288                         str += driver->getName();\r
2289                         str += "] FPS:";\r
2290                         str += fps;\r
2291 \r
2292                         device->setWindowCaption(str.c_str());\r
2293                         lastFPS = fps;\r
2294                 }\r
2295                 \r
2296                 /*}\r
2297                 else\r
2298                         device->yield();*/\r
2299         }\r
2300 \r
2301         } // client is deleted at this point\r
2302         \r
2303         delete g_input;\r
2304 \r
2305         /*\r
2306                 In the end, delete the Irrlicht device.\r
2307         */\r
2308         device->drop();\r
2309 \r
2310         } //try\r
2311         catch(con::PeerNotFoundException &e)\r
2312         {\r
2313                 dstream<<DTIME<<"Connection timed out."<<std::endl;\r
2314         }\r
2315 #if CATCH_UNHANDLED_EXCEPTIONS\r
2316         /*\r
2317                 This is what has to be done in every thread to get suitable debug info\r
2318         */\r
2319         catch(std::exception &e)\r
2320         {\r
2321                 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "\r
2322                                 <<e.what()<<std::endl;\r
2323                 assert(0);\r
2324         }\r
2325 #endif\r
2326 \r
2327         debugstreams_deinit();\r
2328         \r
2329         return 0;\r
2330 }\r
2331 \r
2332 //END\r