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