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