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