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