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