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