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