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