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