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