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