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