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