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