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