2c059840ed2de0f5ef5ab5182e1b06687e2df7c2
[oweals/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           - It is not a memory leak but some kind of a buffer.\r
27 \r
28 NOTE: iostream.imbue(std::locale("C")) is very slow\r
29 NOTE: Global locale is now set at initialization\r
30 \r
31 SUGG: Fix address to be ipv6 compatible\r
32 \r
33 NOTE: When a new sector is generated, it may change the ground level\r
34       of it's and it's neighbors border that two blocks that are\r
35           above and below each other and that are generated before and\r
36           after the sector heightmap generation (order doesn't matter),\r
37           can have a small gap between each other at the border.\r
38 SUGG: Use same technique for sector heightmaps as what we're\r
39       using for UnlimitedHeightmap? (getting all neighbors\r
40           when generating)\r
41 \r
42 SUGG: Transfer more blocks in a single packet\r
43 SUGG: A blockdata combiner class, to which blocks are added and at\r
44       destruction it sends all the stuff in as few packets as possible.\r
45 \r
46 SUGG: If player is on ground, mainly fetch ground-level blocks\r
47 SUGG: Fetch stuff mainly from the viewing direction\r
48 \r
49 SUGG: Expose Connection's seqnums and ACKs to server and client.\r
50       - This enables saving many packets and making a faster connection\r
51           - This also enables server to check if client has received the\r
52             most recent block sent, for example.\r
53 SUGG: Add a sane bandwidth throttling system to Connection\r
54 \r
55 SUGG: More fine-grained control of client's dumping of blocks from\r
56       memory\r
57           - ...What does this mean in the first place?\r
58 \r
59 SUGG: A map editing mode (similar to dedicated server mode)\r
60 \r
61 SUGG: Add a time value to the param of footstepped grass and check it\r
62       against a global timer when a block is accessed, to make old\r
63           steps fade away.\r
64 \r
65 SUGG: Make a copy of close-range environment on client for showing\r
66       on screen, with minimal mutexes to slow down the main loop\r
67 \r
68 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize\r
69       it by sending more stuff in a single packet.\r
70           - Add a packet queue to RemoteClient, from which packets will be\r
71             combined with object data packets\r
72                 - This is not exactly trivial: the object data packets are\r
73                   sometimes very big by themselves\r
74 \r
75 SUGG: Split MapBlockObject serialization to to-client and to-disk\r
76       - This will allow saving ages of rats on disk but not sending\r
77             them to clients\r
78 \r
79 SUGG: MovingObject::move and Player::move are basically the same.\r
80       combine them.\r
81           - NOTE: Player::move is more up-to-date.\r
82 \r
83 SUGG: Precalculate lighting translation table at runtime (at startup)\r
84       - This is not doable because it is currently hand-made and not\r
85             based on some mathematical function.\r
86                 - Note: This has been changing lately\r
87 \r
88 SUGG: A version number to blocks, which increments when the block is\r
89       modified (node add/remove, water update, lighting update)\r
90           - This can then be used to make sure the most recent version of\r
91             a block has been sent to client\r
92 \r
93 SUGG: Make the amount of blocks sending to client and the total\r
94           amount of blocks dynamically limited. Transferring blocks is the\r
95           main network eater of this system, so it is the one that has\r
96           to be throttled so that RTTs stay low.\r
97 \r
98 SUGG: Meshes of blocks could be split into 6 meshes facing into\r
99       different directions and then only those drawn that need to be\r
100 \r
101 SUGG: Calculate lighting per vertex to get a lighting effect like in\r
102       bartwe's game\r
103 \r
104 Gaming ideas:\r
105 -------------\r
106 \r
107 - Aim for something like controlling a single dwarf in Dwarf Fortress\r
108 \r
109 - The player could go faster by a crafting a boat, or riding an animal\r
110 \r
111 - Random NPC traders. what else?\r
112 \r
113 Documentation:\r
114 --------------\r
115 \r
116 Build system / running:\r
117 -----------------------\r
118 \r
119 FIXME: Some network errors on Windows that cause local game to not work\r
120        - See siggjen's emails.\r
121            - Is this the famous "windows 7 problem"?\r
122        - Apparently there might be other errors too\r
123 \r
124 Networking and serialization:\r
125 -----------------------------\r
126 \r
127 TODO: Get rid of GotSplitPacketException\r
128 \r
129 GUI:\r
130 ----\r
131 \r
132 TODO: Add gui option to remove map\r
133 \r
134 TODO: Configuration menu, at least for keys\r
135 \r
136 Graphics:\r
137 ---------\r
138 \r
139 TODO: Optimize day/night mesh updating somehow\r
140       - create copies of all textures for all lighting values and only\r
141             change texture for material?\r
142           - Umm... the collecting of the faces is the slow part\r
143             -> what about just changing the color values of the existing\r
144                    meshbuffers? It should go quite fast.\r
145                    - This is not easy; There'd need to be a buffer somewhere\r
146                      that would contain the night and day lighting values.\r
147                          - Actually if FastFaces would be stored, they could\r
148                            hold both values\r
149 \r
150 FEATURE: Combine MapBlock's face caches to so big pieces that VBO\r
151       gets used\r
152       - That is >500 vertices\r
153           - This is not easy; all the MapBlocks close to the player would\r
154             still need to be drawn separately and combining the blocks\r
155                 would have to happen in a background thread\r
156 \r
157 TODO: Make fetching sector's blocks more efficient when rendering\r
158       sectors that have very large amounts of blocks (on client)\r
159           - Is this necessary at all?\r
160 \r
161 TODO: Flowing water animation\r
162 \r
163 Configuration:\r
164 --------------\r
165 \r
166 Client:\r
167 -------\r
168 \r
169 TODO: Untie client network operations from framerate\r
170       - Needs some input queues or something\r
171 \r
172 SUGG: Make morning and evening transition more smooth and maybe shorter\r
173 \r
174 SUGG: Don't update all meshes always on single node changes, but\r
175       check which ones should be updated\r
176           - implement Map::updateNodeMeshes()\r
177 \r
178 TODO: Remove IrrlichtWrapper\r
179 \r
180 Server:\r
181 -------\r
182 \r
183 TODO: When player dies, throw items on map\r
184 \r
185 TODO: Make an option to the server to disable building and digging near\r
186       the starting position\r
187 \r
188 TODO: Copy the text of the last picked sign to inventory in creative\r
189       mode\r
190 \r
191 TODO: Check what goes wrong with caching map to disk (Kray)\r
192       - Nothing?\r
193 \r
194 TODO: When server sees that client is removing an inexistent block in\r
195       an existent position, resend the MapBlock.\r
196 \r
197 FIXME: Server went into some infinite PeerNotFoundException loop\r
198 \r
199 Objects:\r
200 --------\r
201 \r
202 TODO: There has to be some better way to handle static objects than to\r
203       send them all the time. This affects signs and item objects.\r
204 SUGG: Signs could be done in the same way as torches. For this, blocks\r
205       need an additional metadata field for the texts\r
206           - This is also needed for item container chests\r
207 \r
208 Block object server side:\r
209       - A "near blocks" buffer, in which some nearby blocks are stored.\r
210           - For all blocks in the buffer, objects are stepped(). This\r
211             means they are active.\r
212           - TODO: A global active buffer is needed for the server\r
213           - TODO: A timestamp to blocks\r
214       - TODO: All blocks going in and out of the buffer are recorded.\r
215             - TODO: For outgoing blocks, timestamp is written.\r
216             - TODO: For incoming blocks, time difference is calculated and\r
217               objects are stepped according to it.\r
218 \r
219 - When an active object goes far from a player, either delete\r
220   it or store it statically.\r
221 - When a statically stored active object comes near a player,\r
222   recreate the active object\r
223 \r
224 Map:\r
225 ----\r
226 \r
227 TODO: Mineral and ground material properties\r
228       - This way mineral ground toughness can be calculated with just\r
229             some formula, as well as tool strengths\r
230 \r
231 TODO: Flowing water to actually contain flow direction information\r
232 \r
233 TODO: Remove duplicate lighting implementation from Map (leave\r
234       VoxelManipulator, which is faster)\r
235 \r
236 FEATURE: Create a system that allows a huge amount of different "map\r
237              generator modules/filters"\r
238 \r
239 FEATURE: Erosion simulation at map generation time\r
240                 - Simulate water flows, which would carve out dirt fast and\r
241                   then turn stone into gravel and sand and relocate it.\r
242                 - How about relocating minerals, too? Coal and gold in\r
243                   downstream sand and gravel would be kind of cool\r
244                   - This would need a better way of handling minerals, mainly\r
245                     to have mineral content as a separate field. the first\r
246                         parameter field is free for this.\r
247                 - Simulate rock falling from cliffs when water has removed\r
248                   enough solid rock from the bottom\r
249 \r
250 Doing now (most important at the top):\r
251 --------------------------------------\r
252 # maybe done\r
253 * not done\r
254 \r
255 === Next\r
256 * Continue making the scripting system:\r
257   * Make updateNodeMesh for a less verbose mesh update on add/removenode\r
258   * Switch to using a safe way for the self and env pointers\r
259   * Make some global environment hooks, like node placed and general\r
260     on_step()\r
261 \r
262 === Fixmes\r
263 * Check the fixmes in the list above\r
264 * Make server find the spawning place from the real map data, not from\r
265   the heightmap\r
266   - But the changing borders of chunk have to be avoided, because\r
267     there is time to generate only one chunk.\r
268 * Make the generator to run in background and not blocking block\r
269   placement and transfer\r
270 * only_from_disk might not work anymore - check and fix it.\r
271 \r
272 === Making it more portable\r
273 * Some MSVC: std::sto* are defined without a namespace and collide\r
274   with the ones in utility.h\r
275 \r
276 === Features\r
277 * Map should make the appropriate MapEditEvents\r
278 * Add a global Lua spawn handler and such\r
279 * Get rid of MapBlockObjects\r
280 * Other players could be sent to clients as LuaCAOs\r
281 * Add mud underground\r
282 * Make an "environment metafile" to store at least time of day\r
283 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...\r
284   - Or maybe move content_features to material.{h,cpp}?\r
285 * Add some kind of erosion and other stuff that now is possible\r
286 * Make client to fetch stuff asynchronously\r
287   - Needs method SyncProcessData\r
288 * Better water generation (spread it to underwater caverns but don't\r
289   fill dungeons that don't touch big water masses)\r
290 * When generating a chunk and the neighboring chunk doesn't have mud\r
291   and stuff yet and the ground is fairly flat, the mud will flow to\r
292   the other chunk making nasty straight walls when the other chunk\r
293   is generated. Fix it.\r
294 * Fix the problem with the server constantly saving one or a few\r
295   blocks? List the first saved block, maybe it explains.\r
296   - It is probably caused by oscillating water\r
297 * Make a small history check to transformLiquids to detect and log\r
298   continuous oscillations, in such detail that they can be fixed.\r
299 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC\r
300 \r
301 ======================================================================\r
302 \r
303 */\r
304 \r
305 /*\r
306         Setting this to 1 enables a special camera mode that forces\r
307         the renderers to think that the camera statically points from\r
308         the starting place to a static direction.\r
309 \r
310         This allows one to move around with the player and see what\r
311         is actually drawn behind solid things and behind the player.\r
312 */\r
313 #define FIELD_OF_VIEW_TEST 0\r
314 \r
315 #ifdef NDEBUG\r
316         #ifdef _WIN32\r
317                 #pragma message ("Disabling unit tests")\r
318         #else\r
319                 #warning "Disabling unit tests"\r
320         #endif\r
321         // Disable unit tests\r
322         #define ENABLE_TESTS 0\r
323 #else\r
324         // Enable unit tests\r
325         #define ENABLE_TESTS 1\r
326 #endif\r
327 \r
328 #ifdef _MSC_VER\r
329         #pragma comment(lib, "Irrlicht.lib")\r
330         //#pragma comment(lib, "jthread.lib")\r
331         #pragma comment(lib, "zlibwapi.lib")\r
332         #pragma comment(lib, "Shell32.lib")\r
333         // This would get rid of the console window\r
334         //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")\r
335 #endif\r
336 \r
337 #include <iostream>\r
338 #include <fstream>\r
339 #include <jmutexautolock.h>\r
340 #include <locale.h>\r
341 #include "main.h"\r
342 #include "common_irrlicht.h"\r
343 #include "debug.h"\r
344 #include "map.h"\r
345 #include "player.h"\r
346 #include "test.h"\r
347 //#include "environment.h"\r
348 #include "server.h"\r
349 #include "client.h"\r
350 //#include "serialization.h"\r
351 #include "constants.h"\r
352 //#include "strfnd.h"\r
353 #include "porting.h"\r
354 #include "irrlichtwrapper.h"\r
355 #include "gettime.h"\r
356 #include "porting.h"\r
357 #include "guiPauseMenu.h"\r
358 #include "guiInventoryMenu.h"\r
359 #include "guiTextInputMenu.h"\r
360 #include "materials.h"\r
361 #include "guiMessageMenu.h"\r
362 #include "filesys.h"\r
363 #include "config.h"\r
364 #include "guiMainMenu.h"\r
365 #include "mineral.h"\r
366 #include "noise.h"\r
367 #include "tile.h"\r
368 \r
369 // TODO: Remove this\r
370 IrrlichtWrapper *g_irrlicht = NULL;\r
371 \r
372 // This makes textures\r
373 ITextureSource *g_texturesource = NULL;\r
374 \r
375 MapDrawControl draw_control;\r
376 \r
377 /*\r
378         Settings.\r
379         These are loaded from the config file.\r
380 */\r
381 \r
382 Settings g_settings;\r
383 \r
384 extern void set_default_settings();\r
385 \r
386 /*\r
387         Random stuff\r
388 */\r
389 \r
390 IrrlichtDevice *g_device = NULL;\r
391 Client *g_client = NULL;\r
392 \r
393 /*const s16 quickinv_size = 40;\r
394 const s16 quickinv_padding = 8;\r
395 const s16 quickinv_spacing = quickinv_size + quickinv_padding;\r
396 const s16 quickinv_outer_padding = 4;\r
397 const s16 quickinv_itemcount = 8;*/\r
398 \r
399 const s32 hotbar_itemcount = 8;\r
400 const s32 hotbar_imagesize = 36;\r
401 \r
402 /*\r
403         GUI Stuff\r
404 */\r
405 \r
406 gui::IGUIEnvironment* guienv = NULL;\r
407 gui::IGUIStaticText *guiroot = NULL;\r
408 \r
409 class MainMenuManager : public IMenuManager\r
410 {\r
411 public:\r
412         virtual void createdMenu(GUIModalMenu *menu)\r
413         {\r
414                 for(core::list<GUIModalMenu*>::Iterator\r
415                                 i = m_stack.begin();\r
416                                 i != m_stack.end(); i++)\r
417                 {\r
418                         assert(*i != menu);\r
419                 }\r
420 \r
421                 if(m_stack.size() != 0)\r
422                         (*m_stack.getLast())->setVisible(false);\r
423                 m_stack.push_back(menu);\r
424         }\r
425 \r
426         virtual void deletingMenu(GUIModalMenu *menu)\r
427         {\r
428                 // Remove all entries if there are duplicates\r
429                 bool removed_entry;\r
430                 do{\r
431                         removed_entry = false;\r
432                         for(core::list<GUIModalMenu*>::Iterator\r
433                                         i = m_stack.begin();\r
434                                         i != m_stack.end(); i++)\r
435                         {\r
436                                 if(*i == menu)\r
437                                 {\r
438                                         m_stack.erase(i);\r
439                                         removed_entry = true;\r
440                                         break;\r
441                                 }\r
442                         }\r
443                 }while(removed_entry);\r
444 \r
445                 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();\r
446                 assert(*i == menu);\r
447                 m_stack.erase(i);*/\r
448                 \r
449                 if(m_stack.size() != 0)\r
450                         (*m_stack.getLast())->setVisible(true);\r
451         }\r
452 \r
453         u32 menuCount()\r
454         {\r
455                 return m_stack.size();\r
456         }\r
457 \r
458         core::list<GUIModalMenu*> m_stack;\r
459 };\r
460 \r
461 MainMenuManager g_menumgr;\r
462 \r
463 bool noMenuActive()\r
464 {\r
465         return (g_menumgr.menuCount() == 0);\r
466 }\r
467 \r
468 bool g_disconnect_requested = false;\r
469 \r
470 class MainGameCallback : public IGameCallback\r
471 {\r
472 public:\r
473         virtual void exitToOS()\r
474         {\r
475                 g_device->closeDevice();\r
476         }\r
477 \r
478         virtual void disconnect()\r
479         {\r
480                 g_disconnect_requested = true;\r
481         }\r
482 };\r
483 \r
484 MainGameCallback g_gamecallback;\r
485 \r
486 // Inventory actions from the menu are buffered here before sending\r
487 Queue<InventoryAction*> inventory_action_queue;\r
488 // This is a copy of the inventory that the client's environment has\r
489 Inventory local_inventory;\r
490 \r
491 u16 g_selected_item = 0;\r
492 \r
493 /*\r
494         Debug streams\r
495 */\r
496 \r
497 // Connection\r
498 std::ostream *dout_con_ptr = &dummyout;\r
499 std::ostream *derr_con_ptr = &dstream_no_stderr;\r
500 //std::ostream *dout_con_ptr = &dstream_no_stderr;\r
501 //std::ostream *derr_con_ptr = &dstream_no_stderr;\r
502 //std::ostream *dout_con_ptr = &dstream;\r
503 //std::ostream *derr_con_ptr = &dstream;\r
504 \r
505 // Server\r
506 std::ostream *dout_server_ptr = &dstream;\r
507 std::ostream *derr_server_ptr = &dstream;\r
508 \r
509 // Client\r
510 std::ostream *dout_client_ptr = &dstream;\r
511 std::ostream *derr_client_ptr = &dstream;\r
512 \r
513 /*\r
514         gettime.h implementation\r
515 */\r
516 \r
517 u32 getTimeMs()\r
518 {\r
519         /*\r
520                 Use irrlicht because it is more precise than porting.h's\r
521                 getTimeMs()\r
522         */\r
523         if(g_irrlicht == NULL)\r
524                 return 0;\r
525         return g_irrlicht->getTime();\r
526 }\r
527 \r
528 /*\r
529         Text input system\r
530 */\r
531 \r
532 struct TextDestSign : public TextDest\r
533 {\r
534         TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
535         {\r
536                 m_blockpos = blockpos;\r
537                 m_id = id;\r
538                 m_client = client;\r
539         }\r
540         void gotText(std::wstring text)\r
541         {\r
542                 std::string ntext = wide_to_narrow(text);\r
543                 dstream<<"Changing text of a sign object: "\r
544                                 <<ntext<<std::endl;\r
545                 m_client->sendSignText(m_blockpos, m_id, ntext);\r
546         }\r
547 \r
548         v3s16 m_blockpos;\r
549         s16 m_id;\r
550         Client *m_client;\r
551 };\r
552 \r
553 struct TextDestChat : public TextDest\r
554 {\r
555         TextDestChat(Client *client)\r
556         {\r
557                 m_client = client;\r
558         }\r
559         void gotText(std::wstring text)\r
560         {\r
561                 // Discard empty line\r
562                 if(text == L"")\r
563                         return;\r
564                 \r
565                 // Parse command (server command starts with "/#")\r
566                 if(text[0] == L'/' && text[1] != L'#')\r
567                 {\r
568                         std::wstring reply = L"Local: ";\r
569 \r
570                         reply += L"Local commands not yet supported. "\r
571                                         L"Server prefix is \"/#\".";\r
572                         \r
573                         m_client->addChatMessage(reply);\r
574                         return;\r
575                 }\r
576 \r
577                 // Send to others\r
578                 m_client->sendChatMessage(text);\r
579                 // Show locally\r
580                 m_client->addChatMessage(text);\r
581         }\r
582 \r
583         Client *m_client;\r
584 };\r
585 \r
586 class MyEventReceiver : public IEventReceiver\r
587 {\r
588 public:\r
589         // This is the one method that we have to implement\r
590         virtual bool OnEvent(const SEvent& event)\r
591         {\r
592                 /*\r
593                         React to nothing here if a menu is active\r
594                 */\r
595                 if(noMenuActive() == false)\r
596                 {\r
597                         clearInput();\r
598                         return false;\r
599                 }\r
600 \r
601                 // Remember whether each key is down or up\r
602                 if(event.EventType == irr::EET_KEY_INPUT_EVENT)\r
603                 {\r
604                         keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;\r
605 \r
606                         if(event.KeyInput.PressedDown)\r
607                         {\r
608                                 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;\r
609                                 \r
610                                 /*\r
611                                         Launch menus\r
612                                 */\r
613 \r
614                                 if(guienv != NULL && guiroot != NULL && g_device != NULL)\r
615                                 {\r
616                                         if(event.KeyInput.Key == irr::KEY_ESCAPE)\r
617                                         {\r
618                                                 dstream<<DTIME<<"MyEventReceiver: "\r
619                                                                 <<"Launching pause menu"<<std::endl;\r
620                                                 // It will delete itself by itself\r
621                                                 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,\r
622                                                                 &g_menumgr))->drop();\r
623                                                 return true;\r
624                                         }\r
625                                         if(event.KeyInput.Key == irr::KEY_KEY_I)\r
626                                         {\r
627                                                 dstream<<DTIME<<"MyEventReceiver: "\r
628                                                                 <<"Launching inventory"<<std::endl;\r
629                                                 (new GUIInventoryMenu(guienv, guiroot, -1,\r
630                                                                 &local_inventory, &inventory_action_queue,\r
631                                                                 &g_menumgr))->drop();\r
632                                                 return true;\r
633                                         }\r
634                                         if(event.KeyInput.Key == irr::KEY_KEY_T)\r
635                                         {\r
636                                                 TextDest *dest = new TextDestChat(g_client);\r
637 \r
638                                                 (new GUITextInputMenu(guienv, guiroot, -1,\r
639                                                                 &g_menumgr, dest,\r
640                                                                 L""))->drop();\r
641                                         }\r
642                                 }\r
643 \r
644                                 // Item selection\r
645                                 if(event.KeyInput.Key >= irr::KEY_KEY_0\r
646                                                 && event.KeyInput.Key <= irr::KEY_KEY_9)\r
647                                 {\r
648                                         u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;\r
649                                         if(event.KeyInput.Key == irr::KEY_KEY_0)\r
650                                                 s1 = 10;\r
651                                         if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)\r
652                                                 g_selected_item = s1-1;\r
653                                         dstream<<DTIME<<"Selected item: "\r
654                                                         <<g_selected_item<<std::endl;\r
655                                 }\r
656 \r
657                                 // Viewing range selection\r
658                                 if(event.KeyInput.Key == irr::KEY_KEY_R)\r
659                                 {\r
660                                         if(draw_control.range_all)\r
661                                         {\r
662                                                 draw_control.range_all = false;\r
663                                                 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;\r
664                                         }\r
665                                         else\r
666                                         {\r
667                                                 draw_control.range_all = true;\r
668                                                 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;\r
669                                         }\r
670                                 }\r
671 \r
672                                 // Print debug stacks\r
673                                 if(event.KeyInput.Key == irr::KEY_KEY_P)\r
674                                 {\r
675                                         dstream<<"-----------------------------------------"\r
676                                                         <<std::endl;\r
677                                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;\r
678                                         dstream<<"-----------------------------------------"\r
679                                                         <<std::endl;\r
680                                         debug_stacks_print();\r
681                                 }\r
682                         }\r
683                 }\r
684 \r
685                 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)\r
686                 {\r
687                         if(noMenuActive() == false)\r
688                         {\r
689                                 left_active = false;\r
690                                 middle_active = false;\r
691                                 right_active = false;\r
692                         }\r
693                         else\r
694                         {\r
695                                 //dstream<<"MyEventReceiver: mouse input"<<std::endl;\r
696                                 left_active = event.MouseInput.isLeftPressed();\r
697                                 middle_active = event.MouseInput.isMiddlePressed();\r
698                                 right_active = event.MouseInput.isRightPressed();\r
699 \r
700                                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)\r
701                                 {\r
702                                         leftclicked = true;\r
703                                 }\r
704                                 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)\r
705                                 {\r
706                                         rightclicked = true;\r
707                                 }\r
708                                 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)\r
709                                 {\r
710                                         leftreleased = true;\r
711                                 }\r
712                                 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)\r
713                                 {\r
714                                         rightreleased = true;\r
715                                 }\r
716                                 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)\r
717                                 {\r
718                                         /*dstream<<"event.MouseInput.Wheel="\r
719                                                         <<event.MouseInput.Wheel<<std::endl;*/\r
720                                         \r
721                                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,\r
722                                                         hotbar_itemcount-1);\r
723                                         if(event.MouseInput.Wheel < 0)\r
724                                         {\r
725                                                 if(g_selected_item < max_item)\r
726                                                         g_selected_item++;\r
727                                                 else\r
728                                                         g_selected_item = 0;\r
729                                         }\r
730                                         else if(event.MouseInput.Wheel > 0)\r
731                                         {\r
732                                                 if(g_selected_item > 0)\r
733                                                         g_selected_item--;\r
734                                                 else\r
735                                                         g_selected_item = max_item;\r
736                                         }\r
737                                 }\r
738                         }\r
739                 }\r
740 \r
741                 return false;\r
742         }\r
743 \r
744         // This is used to check whether a key is being held down\r
745         virtual bool IsKeyDown(EKEY_CODE keyCode) const\r
746         {\r
747                 return keyIsDown[keyCode];\r
748         }\r
749 \r
750         void clearInput()\r
751         {\r
752                 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
753                                 keyIsDown[i] = false;\r
754                 \r
755                 leftclicked = false;\r
756                 rightclicked = false;\r
757                 leftreleased = false;\r
758                 rightreleased = false;\r
759 \r
760                 left_active = false;\r
761                 middle_active = false;\r
762                 right_active = false;\r
763         }\r
764 \r
765         MyEventReceiver()\r
766         {\r
767                 clearInput();\r
768         }\r
769 \r
770         bool leftclicked;\r
771         bool rightclicked;\r
772         bool leftreleased;\r
773         bool rightreleased;\r
774 \r
775         bool left_active;\r
776         bool middle_active;\r
777         bool right_active;\r
778 \r
779 private:\r
780         // We use this array to store the current state of each key\r
781         bool keyIsDown[KEY_KEY_CODES_COUNT];\r
782         //s32 mouseX;\r
783         //s32 mouseY;\r
784         IrrlichtDevice *m_device;\r
785 };\r
786 \r
787 class InputHandler\r
788 {\r
789 public:\r
790         InputHandler()\r
791         {\r
792         }\r
793         virtual ~InputHandler()\r
794         {\r
795         }\r
796 \r
797         virtual bool isKeyDown(EKEY_CODE keyCode) = 0;\r
798 \r
799         virtual v2s32 getMousePos() = 0;\r
800         virtual void setMousePos(s32 x, s32 y) = 0;\r
801 \r
802         virtual bool getLeftState() = 0;\r
803         virtual bool getRightState() = 0;\r
804 \r
805         virtual bool getLeftClicked() = 0;\r
806         virtual bool getRightClicked() = 0;\r
807         virtual void resetLeftClicked() = 0;\r
808         virtual void resetRightClicked() = 0;\r
809 \r
810         virtual bool getLeftReleased() = 0;\r
811         virtual bool getRightReleased() = 0;\r
812         virtual void resetLeftReleased() = 0;\r
813         virtual void resetRightReleased() = 0;\r
814         \r
815         virtual void step(float dtime) {};\r
816 \r
817         virtual void clear() {};\r
818 };\r
819 \r
820 InputHandler *g_input = NULL;\r
821 \r
822 class RealInputHandler : public InputHandler\r
823 {\r
824 public:\r
825         RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):\r
826                 m_device(device),\r
827                 m_receiver(receiver)\r
828         {\r
829         }\r
830         virtual bool isKeyDown(EKEY_CODE keyCode)\r
831         {\r
832                 return m_receiver->IsKeyDown(keyCode);\r
833         }\r
834         virtual v2s32 getMousePos()\r
835         {\r
836                 return m_device->getCursorControl()->getPosition();\r
837         }\r
838         virtual void setMousePos(s32 x, s32 y)\r
839         {\r
840                 m_device->getCursorControl()->setPosition(x, y);\r
841         }\r
842 \r
843         virtual bool getLeftState()\r
844         {\r
845                 return m_receiver->left_active;\r
846         }\r
847         virtual bool getRightState()\r
848         {\r
849                 return m_receiver->right_active;\r
850         }\r
851         \r
852         virtual bool getLeftClicked()\r
853         {\r
854                 return m_receiver->leftclicked;\r
855         }\r
856         virtual bool getRightClicked()\r
857         {\r
858                 return m_receiver->rightclicked;\r
859         }\r
860         virtual void resetLeftClicked()\r
861         {\r
862                 m_receiver->leftclicked = false;\r
863         }\r
864         virtual void resetRightClicked()\r
865         {\r
866                 m_receiver->rightclicked = false;\r
867         }\r
868 \r
869         virtual bool getLeftReleased()\r
870         {\r
871                 return m_receiver->leftreleased;\r
872         }\r
873         virtual bool getRightReleased()\r
874         {\r
875                 return m_receiver->rightreleased;\r
876         }\r
877         virtual void resetLeftReleased()\r
878         {\r
879                 m_receiver->leftreleased = false;\r
880         }\r
881         virtual void resetRightReleased()\r
882         {\r
883                 m_receiver->rightreleased = false;\r
884         }\r
885 \r
886         void clear()\r
887         {\r
888                 resetRightClicked();\r
889                 resetLeftClicked();\r
890         }\r
891 private:\r
892         IrrlichtDevice *m_device;\r
893         MyEventReceiver *m_receiver;\r
894 };\r
895 \r
896 class RandomInputHandler : public InputHandler\r
897 {\r
898 public:\r
899         RandomInputHandler()\r
900         {\r
901                 leftdown = false;\r
902                 rightdown = false;\r
903                 leftclicked = false;\r
904                 rightclicked = false;\r
905                 leftreleased = false;\r
906                 rightreleased = false;\r
907                 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)\r
908                         keydown[i] = false;\r
909         }\r
910         virtual bool isKeyDown(EKEY_CODE keyCode)\r
911         {\r
912                 return keydown[keyCode];\r
913         }\r
914         virtual v2s32 getMousePos()\r
915         {\r
916                 return mousepos;\r
917         }\r
918         virtual void setMousePos(s32 x, s32 y)\r
919         {\r
920                 mousepos = v2s32(x,y);\r
921         }\r
922 \r
923         virtual bool getLeftState()\r
924         {\r
925                 return leftdown;\r
926         }\r
927         virtual bool getRightState()\r
928         {\r
929                 return rightdown;\r
930         }\r
931 \r
932         virtual bool getLeftClicked()\r
933         {\r
934                 return leftclicked;\r
935         }\r
936         virtual bool getRightClicked()\r
937         {\r
938                 return rightclicked;\r
939         }\r
940         virtual void resetLeftClicked()\r
941         {\r
942                 leftclicked = false;\r
943         }\r
944         virtual void resetRightClicked()\r
945         {\r
946                 rightclicked = false;\r
947         }\r
948 \r
949         virtual bool getLeftReleased()\r
950         {\r
951                 return leftreleased;\r
952         }\r
953         virtual bool getRightReleased()\r
954         {\r
955                 return rightreleased;\r
956         }\r
957         virtual void resetLeftReleased()\r
958         {\r
959                 leftreleased = false;\r
960         }\r
961         virtual void resetRightReleased()\r
962         {\r
963                 rightreleased = false;\r
964         }\r
965 \r
966         virtual void step(float dtime)\r
967         {\r
968                 {\r
969                         static float counter1 = 0;\r
970                         counter1 -= dtime;\r
971                         if(counter1 < 0.0)\r
972                         {\r
973                                 counter1 = 0.1*Rand(1, 40);\r
974                                 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];\r
975                         }\r
976                 }\r
977                 {\r
978                         static float counter1 = 0;\r
979                         counter1 -= dtime;\r
980                         if(counter1 < 0.0)\r
981                         {\r
982                                 counter1 = 0.1*Rand(1, 40);\r
983                                 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];\r
984                         }\r
985                 }\r
986                 {\r
987                         static float counter1 = 0;\r
988                         counter1 -= dtime;\r
989                         if(counter1 < 0.0)\r
990                         {\r
991                                 counter1 = 0.1*Rand(1, 40);\r
992                                 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];\r
993                         }\r
994                 }\r
995                 {\r
996                         static float counter1 = 0;\r
997                         counter1 -= dtime;\r
998                         if(counter1 < 0.0)\r
999                         {\r
1000                                 counter1 = 0.1*Rand(1, 40);\r
1001                                 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];\r
1002                         }\r
1003                 }\r
1004                 {\r
1005                         static float counter1 = 0;\r
1006                         counter1 -= dtime;\r
1007                         if(counter1 < 0.0)\r
1008                         {\r
1009                                 counter1 = 0.1*Rand(1, 20);\r
1010                                 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));\r
1011                         }\r
1012                 }\r
1013                 {\r
1014                         static float counter1 = 0;\r
1015                         counter1 -= dtime;\r
1016                         if(counter1 < 0.0)\r
1017                         {\r
1018                                 counter1 = 0.1*Rand(1, 30);\r
1019                                 leftdown = !leftdown;\r
1020                                 if(leftdown)\r
1021                                         leftclicked = true;\r
1022                                 if(!leftdown)\r
1023                                         leftreleased = true;\r
1024                         }\r
1025                 }\r
1026                 {\r
1027                         static float counter1 = 0;\r
1028                         counter1 -= dtime;\r
1029                         if(counter1 < 0.0)\r
1030                         {\r
1031                                 counter1 = 0.1*Rand(1, 15);\r
1032                                 rightdown = !rightdown;\r
1033                                 if(rightdown)\r
1034                                         rightclicked = true;\r
1035                                 if(!rightdown)\r
1036                                         rightreleased = true;\r
1037                         }\r
1038                 }\r
1039                 mousepos += mousespeed;\r
1040         }\r
1041 \r
1042         s32 Rand(s32 min, s32 max)\r
1043         {\r
1044                 return (myrand()%(max-min+1))+min;\r
1045         }\r
1046 private:\r
1047         bool keydown[KEY_KEY_CODES_COUNT];\r
1048         v2s32 mousepos;\r
1049         v2s32 mousespeed;\r
1050         bool leftdown;\r
1051         bool rightdown;\r
1052         bool leftclicked;\r
1053         bool rightclicked;\r
1054         bool leftreleased;\r
1055         bool rightreleased;\r
1056 };\r
1057 \r
1058 void updateViewingRange(f32 frametime_in, Client *client)\r
1059 {\r
1060         if(draw_control.range_all == true)\r
1061                 return;\r
1062         \r
1063         static f32 added_frametime = 0;\r
1064         static s16 added_frames = 0;\r
1065 \r
1066         added_frametime += frametime_in;\r
1067         added_frames += 1;\r
1068 \r
1069         // Actually this counter kind of sucks because frametime is busytime\r
1070         static f32 counter = 0;\r
1071         counter -= frametime_in;\r
1072         if(counter > 0)\r
1073                 return;\r
1074         //counter = 0.1;\r
1075         counter = 0.2;\r
1076 \r
1077         /*dstream<<__FUNCTION_NAME\r
1078                         <<": Collected "<<added_frames<<" frames, total of "\r
1079                         <<added_frametime<<"s."<<std::endl;*/\r
1080         \r
1081         /*dstream<<"draw_control.blocks_drawn="\r
1082                         <<draw_control.blocks_drawn\r
1083                         <<", draw_control.blocks_would_have_drawn="\r
1084                         <<draw_control.blocks_would_have_drawn\r
1085                         <<std::endl;*/\r
1086         \r
1087         float range_min = g_settings.getS16("viewing_range_nodes_min");\r
1088         float range_max = g_settings.getS16("viewing_range_nodes_max");\r
1089         \r
1090         draw_control.wanted_min_range = range_min;\r
1091         draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;\r
1092         \r
1093         float block_draw_ratio = 1.0;\r
1094         if(draw_control.blocks_would_have_drawn != 0)\r
1095         {\r
1096                 block_draw_ratio = (float)draw_control.blocks_drawn\r
1097                         / (float)draw_control.blocks_would_have_drawn;\r
1098         }\r
1099 \r
1100         // Calculate the average frametime in the case that all wanted\r
1101         // blocks had been drawn\r
1102         f32 frametime = added_frametime / added_frames / block_draw_ratio;\r
1103         \r
1104         added_frametime = 0.0;\r
1105         added_frames = 0;\r
1106         \r
1107         float wanted_fps = g_settings.getFloat("wanted_fps");\r
1108         float wanted_frametime = 1.0 / wanted_fps;\r
1109         \r
1110         f32 wanted_frametime_change = wanted_frametime - frametime;\r
1111         //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;\r
1112         \r
1113         // If needed frametime change is very small, just return\r
1114         if(fabs(wanted_frametime_change) < wanted_frametime*0.2)\r
1115         {\r
1116                 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;\r
1117                 return;\r
1118         }\r
1119 \r
1120         float range = draw_control.wanted_range;\r
1121         float new_range = range;\r
1122 \r
1123         static s16 range_old = 0;\r
1124         static f32 frametime_old = 0;\r
1125         \r
1126         float d_range = range - range_old;\r
1127         f32 d_frametime = frametime - frametime_old;\r
1128         // A sane default of 30ms per 50 nodes of range\r
1129         static f32 time_per_range = 30. / 50;\r
1130         if(d_range != 0)\r
1131         {\r
1132                 time_per_range = d_frametime / d_range;\r
1133         }\r
1134         \r
1135         // The minimum allowed calculated frametime-range derivative:\r
1136         // Practically this sets the maximum speed of changing the range.\r
1137         // The lower this value, the higher the maximum changing speed.\r
1138         // A low value here results in wobbly range (0.001)\r
1139         // A high value here results in slow changing range (0.0025)\r
1140         // SUGG: This could be dynamically adjusted so that when\r
1141         //       the camera is turning, this is lower\r
1142         //float min_time_per_range = 0.0015;\r
1143         float min_time_per_range = 0.0010;\r
1144         //float min_time_per_range = 0.05 / range;\r
1145         if(time_per_range < min_time_per_range)\r
1146         {\r
1147                 time_per_range = min_time_per_range;\r
1148                 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;\r
1149         }\r
1150         else\r
1151         {\r
1152                 //dstream<<"time_per_range="<<time_per_range<<std::endl;\r
1153         }\r
1154 \r
1155         f32 wanted_range_change = wanted_frametime_change / time_per_range;\r
1156         // Dampen the change a bit to kill oscillations\r
1157         //wanted_range_change *= 0.9;\r
1158         //wanted_range_change *= 0.75;\r
1159         wanted_range_change *= 0.5;\r
1160         //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;\r
1161 \r
1162         // If needed range change is very small, just return\r
1163         if(fabs(wanted_range_change) < 0.001)\r
1164         {\r
1165                 //dstream<<"ignoring small wanted_range_change"<<std::endl;\r
1166                 return;\r
1167         }\r
1168 \r
1169         new_range += wanted_range_change;\r
1170         //dstream<<"new_range="<<new_range/*<<std::endl*/;\r
1171         \r
1172         //float new_range_unclamped = new_range;\r
1173         if(new_range < range_min)\r
1174                 new_range = range_min;\r
1175         if(new_range > range_max)\r
1176                 new_range = range_max;\r
1177         \r
1178         /*if(new_range != new_range_unclamped)\r
1179                 dstream<<", clamped to "<<new_range<<std::endl;\r
1180         else\r
1181                 dstream<<std::endl;*/\r
1182 \r
1183         draw_control.wanted_range = new_range;\r
1184 \r
1185         range_old = new_range;\r
1186         frametime_old = frametime;\r
1187 }\r
1188 \r
1189 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,\r
1190                 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,\r
1191                 Inventory *inventory)\r
1192 {\r
1193         InventoryList *mainlist = inventory->getList("main");\r
1194         if(mainlist == NULL)\r
1195         {\r
1196                 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;\r
1197                 return;\r
1198         }\r
1199         \r
1200         s32 padding = imgsize/12;\r
1201         //s32 height = imgsize + padding*2;\r
1202         s32 width = itemcount*(imgsize+padding*2);\r
1203         \r
1204         // Position of upper left corner of bar\r
1205         v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);\r
1206         \r
1207         // Draw background color\r
1208         /*core::rect<s32> barrect(0,0,width,height);\r
1209         barrect += pos;\r
1210         video::SColor bgcolor(255,128,128,128);\r
1211         driver->draw2DRectangle(bgcolor, barrect, NULL);*/\r
1212 \r
1213         core::rect<s32> imgrect(0,0,imgsize,imgsize);\r
1214 \r
1215         for(s32 i=0; i<itemcount; i++)\r
1216         {\r
1217                 InventoryItem *item = mainlist->getItem(i);\r
1218                 \r
1219                 core::rect<s32> rect = imgrect + pos\r
1220                                 + v2s32(padding+i*(imgsize+padding*2), padding);\r
1221                 \r
1222                 if(g_selected_item == i)\r
1223                 {\r
1224                         driver->draw2DRectangle(video::SColor(255,255,0,0),\r
1225                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,\r
1226                                                         rect.LowerRightCorner + v2s32(1,1)*padding),\r
1227                                         NULL);\r
1228                 }\r
1229                 else\r
1230                 {\r
1231                         video::SColor bgcolor2(128,0,0,0);\r
1232                         driver->draw2DRectangle(bgcolor2, rect, NULL);\r
1233                 }\r
1234 \r
1235                 if(item != NULL)\r
1236                 {\r
1237                         drawInventoryItem(driver, font, item, rect, NULL);\r
1238                 }\r
1239         }\r
1240 }\r
1241 \r
1242 // Chat data\r
1243 struct ChatLine\r
1244 {\r
1245         ChatLine():\r
1246                 age(0.0)\r
1247         {\r
1248         }\r
1249         ChatLine(const std::wstring &a_text):\r
1250                 age(0.0),\r
1251                 text(a_text)\r
1252         {\r
1253         }\r
1254         float age;\r
1255         std::wstring text;\r
1256 };\r
1257 \r
1258 // These are defined global so that they're not optimized too much.\r
1259 // Can't change them to volatile.\r
1260 s16 temp16;\r
1261 f32 tempf;\r
1262 v3f tempv3f1;\r
1263 v3f tempv3f2;\r
1264 std::string tempstring;\r
1265 std::string tempstring2;\r
1266 \r
1267 void SpeedTests()\r
1268 {\r
1269         {\r
1270                 dstream<<"The following test should take around 20ms."<<std::endl;\r
1271                 TimeTaker timer("Testing std::string speed");\r
1272                 const u32 jj = 10000;\r
1273                 for(u32 j=0; j<jj; j++)\r
1274                 {\r
1275                         tempstring = "";\r
1276                         tempstring2 = "";\r
1277                         const u32 ii = 10;\r
1278                         for(u32 i=0; i<ii; i++){\r
1279                                 tempstring2 += "asd";\r
1280                         }\r
1281                         for(u32 i=0; i<ii+1; i++){\r
1282                                 tempstring += "asd";\r
1283                                 if(tempstring == tempstring2)\r
1284                                         break;\r
1285                         }\r
1286                 }\r
1287         }\r
1288         \r
1289         dstream<<"All of the following tests should take around 100ms each."\r
1290                         <<std::endl;\r
1291 \r
1292         {\r
1293                 TimeTaker timer("Testing floating-point conversion speed");\r
1294                 tempf = 0.001;\r
1295                 for(u32 i=0; i<4000000; i++){\r
1296                         temp16 += tempf;\r
1297                         tempf += 0.001;\r
1298                 }\r
1299         }\r
1300         \r
1301         {\r
1302                 TimeTaker timer("Testing floating-point vector speed");\r
1303 \r
1304                 tempv3f1 = v3f(1,2,3);\r
1305                 tempv3f2 = v3f(4,5,6);\r
1306                 for(u32 i=0; i<10000000; i++){\r
1307                         tempf += tempv3f1.dotProduct(tempv3f2);\r
1308                         tempv3f2 += v3f(7,8,9);\r
1309                 }\r
1310         }\r
1311 \r
1312         {\r
1313                 TimeTaker timer("Testing core::map speed");\r
1314                 \r
1315                 core::map<v2s16, f32> map1;\r
1316                 tempf = -324;\r
1317                 const s16 ii=300;\r
1318                 for(s16 y=0; y<ii; y++){\r
1319                         for(s16 x=0; x<ii; x++){\r
1320                                 map1.insert(v2s16(x,y), tempf);\r
1321                                 tempf += 1;\r
1322                         }\r
1323                 }\r
1324                 for(s16 y=ii-1; y>=0; y--){\r
1325                         for(s16 x=0; x<ii; x++){\r
1326                                 tempf = map1[v2s16(x,y)];\r
1327                         }\r
1328                 }\r
1329         }\r
1330 \r
1331         {\r
1332                 dstream<<"Around 5000/ms should do well here."<<std::endl;\r
1333                 TimeTaker timer("Testing mutex speed");\r
1334                 \r
1335                 JMutex m;\r
1336                 m.Init();\r
1337                 u32 n = 0;\r
1338                 u32 i = 0;\r
1339                 do{\r
1340                         n += 10000;\r
1341                         for(; i<n; i++){\r
1342                                 m.Lock();\r
1343                                 m.Unlock();\r
1344                         }\r
1345                 }\r
1346                 // Do at least 10ms\r
1347                 while(timer.getTime() < 10);\r
1348 \r
1349                 u32 dtime = timer.stop();\r
1350                 u32 per_ms = n / dtime;\r
1351                 std::cout<<"Done. "<<dtime<<"ms, "\r
1352                                 <<per_ms<<"/ms"<<std::endl;\r
1353         }\r
1354 }\r
1355 \r
1356 int main(int argc, char *argv[])\r
1357 {\r
1358         /*\r
1359                 Parse command line\r
1360         */\r
1361         \r
1362         // List all allowed options\r
1363         core::map<std::string, ValueSpec> allowed_options;\r
1364         allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));\r
1365         allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,\r
1366                         "Run server directly"));\r
1367         allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,\r
1368                         "Load configuration from specified file"));\r
1369         allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));\r
1370         allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));\r
1371         allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));\r
1372         allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1373         allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));\r
1374         allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));\r
1375 #ifdef _WIN32\r
1376         allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));\r
1377 #endif\r
1378         allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));\r
1379 \r
1380         Settings cmd_args;\r
1381         \r
1382         bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);\r
1383 \r
1384         if(ret == false || cmd_args.getFlag("help"))\r
1385         {\r
1386                 dstream<<"Allowed options:"<<std::endl;\r
1387                 for(core::map<std::string, ValueSpec>::Iterator\r
1388                                 i = allowed_options.getIterator();\r
1389                                 i.atEnd() == false; i++)\r
1390                 {\r
1391                         dstream<<"  --"<<i.getNode()->getKey();\r
1392                         if(i.getNode()->getValue().type == VALUETYPE_FLAG)\r
1393                         {\r
1394                         }\r
1395                         else\r
1396                         {\r
1397                                 dstream<<" <value>";\r
1398                         }\r
1399                         dstream<<std::endl;\r
1400 \r
1401                         if(i.getNode()->getValue().help != NULL)\r
1402                         {\r
1403                                 dstream<<"      "<<i.getNode()->getValue().help\r
1404                                                 <<std::endl;\r
1405                         }\r
1406                 }\r
1407 \r
1408                 return cmd_args.getFlag("help") ? 0 : 1;\r
1409         }\r
1410         \r
1411         /*\r
1412                 Low-level initialization\r
1413         */\r
1414 \r
1415         bool disable_stderr = false;\r
1416 #ifdef _WIN32\r
1417         if(cmd_args.getFlag("dstream-on-stderr") == false)\r
1418                 disable_stderr = true;\r
1419 #endif\r
1420 \r
1421         // Initialize debug streams\r
1422         debugstreams_init(disable_stderr, DEBUGFILE);\r
1423         // Initialize debug stacks\r
1424         debug_stacks_init();\r
1425 \r
1426         DSTACK(__FUNCTION_NAME);\r
1427 \r
1428         porting::signal_handler_init();\r
1429         bool &kill = *porting::signal_handler_killstatus();\r
1430         \r
1431         porting::initializePaths();\r
1432         // Create user data directory\r
1433         fs::CreateDir(porting::path_userdata);\r
1434         \r
1435         // C-style stuff initialization\r
1436         initializeMaterialProperties();\r
1437 \r
1438         // Debug handler\r
1439         BEGIN_DEBUG_EXCEPTION_HANDLER\r
1440 \r
1441         // Print startup message\r
1442         dstream<<DTIME<<"minetest-c55"\r
1443                         " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST\r
1444                         <<", "<<BUILD_INFO\r
1445                         <<std::endl;\r
1446         \r
1447         /*\r
1448                 Basic initialization\r
1449         */\r
1450 \r
1451         // Initialize default settings\r
1452         set_default_settings();\r
1453         \r
1454         // Set locale. This is for forcing '.' as the decimal point.\r
1455         std::locale::global(std::locale("C"));\r
1456         // This enables printing all characters in bitmap font\r
1457         setlocale(LC_CTYPE, "en_US");\r
1458 \r
1459         // Initialize sockets\r
1460         sockets_init();\r
1461         atexit(sockets_cleanup);\r
1462         \r
1463         /*\r
1464                 Initialization\r
1465         */\r
1466 \r
1467         /*\r
1468                 Read config file\r
1469         */\r
1470         \r
1471         // Path of configuration file in use\r
1472         std::string configpath = "";\r
1473         \r
1474         if(cmd_args.exists("config"))\r
1475         {\r
1476                 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());\r
1477                 if(r == false)\r
1478                 {\r
1479                         dstream<<"Could not read configuration from \""\r
1480                                         <<cmd_args.get("config")<<"\""<<std::endl;\r
1481                         return 1;\r
1482                 }\r
1483                 configpath = cmd_args.get("config");\r
1484         }\r
1485         else\r
1486         {\r
1487                 core::array<std::string> filenames;\r
1488                 filenames.push_back(porting::path_userdata + "/minetest.conf");\r
1489 #ifdef RUN_IN_PLACE\r
1490                 filenames.push_back(porting::path_userdata + "/../minetest.conf");\r
1491 #endif\r
1492 \r
1493                 for(u32 i=0; i<filenames.size(); i++)\r
1494                 {\r
1495                         bool r = g_settings.readConfigFile(filenames[i].c_str());\r
1496                         if(r)\r
1497                         {\r
1498                                 configpath = filenames[i];\r
1499                                 break;\r
1500                         }\r
1501                 }\r
1502                 \r
1503                 // If no path found, use the first one (menu creates the file)\r
1504                 if(configpath == "")\r
1505                         configpath = filenames[0];\r
1506         }\r
1507 \r
1508         // Initialize random seed\r
1509         srand(time(0));\r
1510         mysrand(time(0));\r
1511 \r
1512         /*\r
1513                 Pre-initialize some stuff with a dummy irrlicht wrapper.\r
1514 \r
1515                 These are needed for unit tests at least.\r
1516         */\r
1517         \r
1518         // Initial call with g_texturesource not set.\r
1519         init_mapnode();\r
1520 \r
1521         /*\r
1522                 Run unit tests\r
1523         */\r
1524 \r
1525         if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)\r
1526                         || cmd_args.getFlag("enable-unittests") == true)\r
1527         {\r
1528                 run_tests();\r
1529         }\r
1530         \r
1531         /*for(s16 y=-100; y<100; y++)\r
1532         for(s16 x=-100; x<100; x++)\r
1533         {\r
1534                 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;\r
1535         }\r
1536         return 0;*/\r
1537         \r
1538         /*\r
1539                 Some parameters\r
1540         */\r
1541 \r
1542         // Port\r
1543         u16 port = 30000;\r
1544         if(cmd_args.exists("port"))\r
1545                 port = cmd_args.getU16("port");\r
1546         else if(cmd_args.exists("port"))\r
1547                 port = g_settings.getU16("port");\r
1548         \r
1549         // Map directory\r
1550         std::string map_dir = porting::path_userdata+"/map";\r
1551         if(cmd_args.exists("map-dir"))\r
1552                 map_dir = cmd_args.get("map-dir");\r
1553         else if(g_settings.exists("map-dir"))\r
1554                 map_dir = g_settings.get("map-dir");\r
1555         \r
1556         // Run dedicated server if asked to\r
1557         if(cmd_args.getFlag("server"))\r
1558         {\r
1559                 DSTACK("Dedicated server branch");\r
1560 \r
1561                 // Create server\r
1562                 Server server(map_dir.c_str());\r
1563                 server.start(port);\r
1564                 \r
1565                 // Run server\r
1566                 dedicated_server_loop(server, kill);\r
1567 \r
1568                 return 0;\r
1569         }\r
1570 \r
1571         /*\r
1572                 More parameters\r
1573         */\r
1574         \r
1575         // Address to connect to\r
1576         std::string address = "";\r
1577         \r
1578         if(cmd_args.exists("address"))\r
1579         {\r
1580                 address = cmd_args.get("address");\r
1581         }\r
1582         else\r
1583         {\r
1584                 address = g_settings.get("address");\r
1585         }\r
1586         \r
1587         std::string playername = g_settings.get("name");\r
1588 \r
1589         // Resolution selection\r
1590         \r
1591         bool fullscreen = false;\r
1592         u16 screenW = g_settings.getU16("screenW");\r
1593         u16 screenH = g_settings.getU16("screenH");\r
1594 \r
1595         // Determine driver\r
1596 \r
1597         video::E_DRIVER_TYPE driverType;\r
1598         \r
1599         std::string driverstring = g_settings.get("video_driver");\r
1600 \r
1601         if(driverstring == "null")\r
1602                 driverType = video::EDT_NULL;\r
1603         else if(driverstring == "software")\r
1604                 driverType = video::EDT_SOFTWARE;\r
1605         else if(driverstring == "burningsvideo")\r
1606                 driverType = video::EDT_BURNINGSVIDEO;\r
1607         else if(driverstring == "direct3d8")\r
1608                 driverType = video::EDT_DIRECT3D8;\r
1609         else if(driverstring == "direct3d9")\r
1610                 driverType = video::EDT_DIRECT3D9;\r
1611         else if(driverstring == "opengl")\r
1612                 driverType = video::EDT_OPENGL;\r
1613         else\r
1614         {\r
1615                 dstream<<"WARNING: Invalid video_driver specified; defaulting "\r
1616                                 "to opengl"<<std::endl;\r
1617                 driverType = video::EDT_OPENGL;\r
1618         }\r
1619 \r
1620         // create device and exit if creation failed\r
1621 \r
1622         MyEventReceiver receiver;\r
1623 \r
1624         IrrlichtDevice *device;\r
1625         device = createDevice(driverType,\r
1626                         core::dimension2d<u32>(screenW, screenH),\r
1627                         16, fullscreen, false, false, &receiver);\r
1628 \r
1629         if (device == 0)\r
1630                 return 1; // could not create selected driver.\r
1631         \r
1632         g_device = device;\r
1633         g_irrlicht = new IrrlichtWrapper(device);\r
1634         TextureSource *texturesource = new TextureSource(device);\r
1635         g_texturesource = texturesource;\r
1636 \r
1637         /*\r
1638                 Speed tests (done after irrlicht is loaded to get timer)\r
1639         */\r
1640         if(cmd_args.getFlag("speedtests"))\r
1641         {\r
1642                 dstream<<"Running speed tests"<<std::endl;\r
1643                 SpeedTests();\r
1644                 return 0;\r
1645         }\r
1646         \r
1647         device->setResizable(true);\r
1648 \r
1649         bool random_input = g_settings.getBool("random_input")\r
1650                         || cmd_args.getFlag("random-input");\r
1651         if(random_input)\r
1652                 g_input = new RandomInputHandler();\r
1653         else\r
1654                 g_input = new RealInputHandler(device, &receiver);\r
1655         \r
1656         /*\r
1657                 Continue initialization\r
1658         */\r
1659 \r
1660         video::IVideoDriver* driver = device->getVideoDriver();\r
1661 \r
1662         /*\r
1663                 This changes the minimum allowed number of vertices in a VBO.\r
1664                 Default is 500.\r
1665         */\r
1666         //driver->setMinHardwareBufferVertexCount(50);\r
1667 \r
1668         scene::ISceneManager* smgr = device->getSceneManager();\r
1669 \r
1670         guienv = device->getGUIEnvironment();\r
1671         gui::IGUISkin* skin = guienv->getSkin();\r
1672         gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());\r
1673         if(font)\r
1674                 skin->setFont(font);\r
1675         else\r
1676                 dstream<<"WARNING: Font file was not found."\r
1677                                 " Using default font."<<std::endl;\r
1678         // If font was not found, this will get us one\r
1679         font = skin->getFont();\r
1680         assert(font);\r
1681 \r
1682         u32 text_height = font->getDimension(L"Hello, world!").Height;\r
1683         dstream<<"text_height="<<text_height<<std::endl;\r
1684 \r
1685         //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
1686         skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
1687         //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
1688         //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));\r
1689         skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));\r
1690         skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));\r
1691         \r
1692         /*\r
1693                 Preload some textures and stuff\r
1694         */\r
1695 \r
1696         init_content_inventory_texture_paths();\r
1697         init_mapnode(); // Second call with g_texturesource set\r
1698         init_mineral();\r
1699 \r
1700         /*\r
1701                 GUI stuff\r
1702         */\r
1703 \r
1704         /*\r
1705                 We need some kind of a root node to be able to add\r
1706                 custom gui elements directly on the screen.\r
1707                 Otherwise they won't be automatically drawn.\r
1708         */\r
1709         guiroot = guienv->addStaticText(L"",\r
1710                         core::rect<s32>(0, 0, 10000, 10000));\r
1711         \r
1712         // First line of debug text\r
1713         gui::IGUIStaticText *guitext = guienv->addStaticText(\r
1714                         L"",\r
1715                         core::rect<s32>(5, 5, 795, 5+text_height),\r
1716                         false, false);\r
1717         // Second line of debug text\r
1718         gui::IGUIStaticText *guitext2 = guienv->addStaticText(\r
1719                         L"",\r
1720                         core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),\r
1721                         false, false);\r
1722         \r
1723         // At the middle of the screen\r
1724         // Object infos are shown in this\r
1725         gui::IGUIStaticText *guitext_info = guienv->addStaticText(\r
1726                         L"",\r
1727                         core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),\r
1728                         false, false);\r
1729         \r
1730         // Chat text\r
1731         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(\r
1732                         L"",\r
1733                         core::rect<s32>(0,0,0,0),\r
1734                         false, false); // Disable word wrap as of now\r
1735                         //false, true);\r
1736         //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));\r
1737         core::list<ChatLine> chat_lines;\r
1738         \r
1739         /*\r
1740                 If an error occurs, this is set to something and the\r
1741                 menu-game loop is restarted. It is then displayed before\r
1742                 the menu.\r
1743         */\r
1744         std::wstring error_message = L"";\r
1745         \r
1746         /*\r
1747                 Menu-game loop\r
1748         */\r
1749         while(g_device->run() && kill == false)\r
1750         {\r
1751         \r
1752         // This is used for catching disconnects\r
1753         try\r
1754         {\r
1755         \r
1756         /*\r
1757                 Out-of-game menu loop.\r
1758 \r
1759                 Loop quits when menu returns proper parameters.\r
1760         */\r
1761         while(kill == false)\r
1762         {\r
1763                 // Cursor can be non-visible when coming from the game\r
1764                 device->getCursorControl()->setVisible(true);\r
1765                 // Some stuff are left to scene manager when coming from the game\r
1766                 // (map at least?)\r
1767                 smgr->clear();\r
1768                 // Reset or hide the debug gui texts\r
1769                 guitext->setText(L"Minetest-c55");\r
1770                 guitext2->setVisible(false);\r
1771                 guitext_info->setVisible(false);\r
1772                 guitext_chat->setVisible(false);\r
1773                 \r
1774                 // Initialize menu data\r
1775                 MainMenuData menudata;\r
1776                 menudata.address = narrow_to_wide(address);\r
1777                 menudata.name = narrow_to_wide(playername);\r
1778                 menudata.port = narrow_to_wide(itos(port));\r
1779                 menudata.creative_mode = g_settings.getBool("creative_mode");\r
1780 \r
1781                 GUIMainMenu *menu =\r
1782                                 new GUIMainMenu(guienv, guiroot, -1, \r
1783                                         &g_menumgr, &menudata, &g_gamecallback);\r
1784                 menu->allowFocusRemoval(true);\r
1785 \r
1786                 if(error_message != L"")\r
1787                 {\r
1788                         GUIMessageMenu *menu2 =\r
1789                                         new GUIMessageMenu(guienv, guiroot, -1, \r
1790                                                 &g_menumgr, error_message.c_str());\r
1791                         menu2->drop();\r
1792                         error_message = L"";\r
1793                 }\r
1794 \r
1795                 video::IVideoDriver* driver = g_device->getVideoDriver();\r
1796                 \r
1797                 dstream<<"Created main menu"<<std::endl;\r
1798 \r
1799                 while(g_device->run() && kill == false)\r
1800                 {\r
1801                         // Run global IrrlichtWrapper's main thread processing stuff\r
1802                         g_irrlicht->Run();\r
1803                         \r
1804                         if(menu->getStatus() == true)\r
1805                                 break;\r
1806 \r
1807                         //driver->beginScene(true, true, video::SColor(255,0,0,0));\r
1808                         driver->beginScene(true, true, video::SColor(255,128,128,128));\r
1809                         guienv->drawAll();\r
1810                         driver->endScene();\r
1811                 }\r
1812                 \r
1813                 // Break out of menu-game loop to shut down cleanly\r
1814                 if(g_device->run() == false || kill == true)\r
1815                         break;\r
1816                 \r
1817                 dstream<<"Dropping main menu"<<std::endl;\r
1818 \r
1819                 menu->drop();\r
1820                 \r
1821                 // Delete map if requested\r
1822                 if(menudata.delete_map)\r
1823                 {\r
1824                         bool r = fs::RecursiveDeleteContent(map_dir);\r
1825                         if(r == false)\r
1826                                 error_message = L"Delete failed";\r
1827                         continue;\r
1828                 }\r
1829 \r
1830                 playername = wide_to_narrow(menudata.name);\r
1831                 address = wide_to_narrow(menudata.address);\r
1832                 port = stoi(wide_to_narrow(menudata.port));\r
1833                 g_settings.set("creative_mode", itos(menudata.creative_mode));\r
1834                 \r
1835                 // Check for valid parameters, restart menu if invalid.\r
1836                 if(playername == "")\r
1837                 {\r
1838                         error_message = L"Name required.";\r
1839                         continue;\r
1840                 }\r
1841                 \r
1842                 // Save settings\r
1843                 g_settings.set("name", playername);\r
1844                 g_settings.set("address", address);\r
1845                 g_settings.set("port", itos(port));\r
1846                 // Update configuration file\r
1847                 if(configpath != "")\r
1848                         g_settings.updateConfigFile(configpath.c_str());\r
1849         \r
1850                 // Continue to game\r
1851                 break;\r
1852         }\r
1853         \r
1854         // Break out of menu-game loop to shut down cleanly\r
1855         if(g_device->run() == false)\r
1856                 break;\r
1857 \r
1858         /*\r
1859                 Make a scope here so that the client and the server and other\r
1860                 stuff gets removed when disconnected or the irrlicht device\r
1861                 is removed.\r
1862         */\r
1863         {\r
1864 \r
1865         // This is set to true at the end of the scope\r
1866         g_irrlicht->Shutdown(false);\r
1867 \r
1868         /*\r
1869                 Draw "Loading" screen\r
1870         */\r
1871         const wchar_t *text = L"Loading and connecting...";\r
1872         core::vector2d<s32> center(screenW/2, screenH/2);\r
1873         core::vector2d<s32> textsize(300, text_height);\r
1874         core::rect<s32> textrect(center - textsize/2, center + textsize/2);\r
1875 \r
1876         gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(\r
1877                         text, textrect, false, false);\r
1878         gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);\r
1879 \r
1880         driver->beginScene(true, true, video::SColor(255,0,0,0));\r
1881         guienv->drawAll();\r
1882         driver->endScene();\r
1883 \r
1884         std::cout<<DTIME<<"Creating server and client"<<std::endl;\r
1885         \r
1886         /*\r
1887                 Create server.\r
1888                 SharedPtr will delete it when it goes out of scope.\r
1889         */\r
1890         SharedPtr<Server> server;\r
1891         if(address == ""){\r
1892                 server = new Server(map_dir);\r
1893                 server->start(port);\r
1894         }\r
1895         \r
1896         /*\r
1897                 Create client\r
1898         */\r
1899 \r
1900         Client client(device, playername.c_str(), draw_control);\r
1901                         \r
1902         g_client = &client;\r
1903         \r
1904         Address connect_address(0,0,0,0, port);\r
1905         try{\r
1906                 if(address == "")\r
1907                         //connect_address.Resolve("localhost");\r
1908                         connect_address.setAddress(127,0,0,1);\r
1909                 else\r
1910                         connect_address.Resolve(address.c_str());\r
1911         }\r
1912         catch(ResolveError &e)\r
1913         {\r
1914                 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;\r
1915                 //return 0;\r
1916                 error_message = L"Couldn't resolve address";\r
1917                 gui_loadingtext->remove();\r
1918                 continue;\r
1919         }\r
1920         \r
1921         dstream<<DTIME<<"Connecting to server at ";\r
1922         connect_address.print(&dstream);\r
1923         dstream<<std::endl;\r
1924         client.connect(connect_address);\r
1925         \r
1926         try{\r
1927                 while(client.connectedAndInitialized() == false)\r
1928                 {\r
1929                         // Update screen\r
1930                         driver->beginScene(true, true, video::SColor(255,0,0,0));\r
1931                         guienv->drawAll();\r
1932                         driver->endScene();\r
1933 \r
1934                         // Update client and server\r
1935 \r
1936                         client.step(0.1);\r
1937 \r
1938                         if(server != NULL)\r
1939                                 server->step(0.1);\r
1940                         \r
1941                         // Delay a bit\r
1942                         sleep_ms(100);\r
1943                 }\r
1944         }\r
1945         catch(con::PeerNotFoundException &e)\r
1946         {\r
1947                 std::cout<<DTIME<<"Timed out."<<std::endl;\r
1948                 //return 0;\r
1949                 error_message = L"Connection timed out.";\r
1950                 gui_loadingtext->remove();\r
1951                 continue;\r
1952         }\r
1953 \r
1954         /*\r
1955                 Create skybox\r
1956         */\r
1957         /*scene::ISceneNode* skybox;\r
1958         skybox = smgr->addSkyBoxSceneNode(\r
1959                 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),\r
1960                 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),\r
1961                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
1962                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
1963                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),\r
1964                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/\r
1965         \r
1966         /*\r
1967                 Create the camera node\r
1968         */\r
1969 \r
1970         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(\r
1971                 0, // Camera parent\r
1972                 v3f(BS*100, BS*2, BS*100), // Look from\r
1973                 v3f(BS*100+1, BS*2, BS*100), // Look to\r
1974                 -1 // Camera ID\r
1975         );\r
1976 \r
1977         if(camera == NULL)\r
1978                 return 1;\r
1979 \r
1980         //video::SColor skycolor = video::SColor(255,90,140,200);\r
1981         //video::SColor skycolor = video::SColor(255,166,202,244);\r
1982         video::SColor skycolor = video::SColor(255,120,185,244);\r
1983 \r
1984         camera->setFOV(FOV_ANGLE);\r
1985 \r
1986         // Just so big a value that everything rendered is visible\r
1987         camera->setFarValue(100000*BS);\r
1988         \r
1989         /*\r
1990                 Lighting test code. Doesn't quite work this way.\r
1991                 The CPU-computed lighting is good.\r
1992         */\r
1993 \r
1994         /*\r
1995         smgr->addLightSceneNode(NULL,\r
1996                 v3f(0, BS*1000000, 0),\r
1997                 video::SColorf(0.3,0.3,0.3),\r
1998                 BS*10000000);\r
1999 \r
2000         smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));\r
2001 \r
2002         scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,\r
2003                         v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);\r
2004         */\r
2005 \r
2006         f32 camera_yaw = 0; // "right/left"\r
2007         f32 camera_pitch = 0; // "up/down"\r
2008 \r
2009         /*\r
2010                 Move into game\r
2011         */\r
2012         \r
2013         gui_loadingtext->remove();\r
2014 \r
2015         /*\r
2016                 Add some gui stuff\r
2017         */\r
2018 \r
2019         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
2020                         (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/\r
2021         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory\r
2022                         (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/\r
2023         \r
2024         // Test the text input system\r
2025         /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,\r
2026                         NULL))->drop();*/\r
2027         /*GUIMessageMenu *menu =\r
2028                         new GUIMessageMenu(guienv, guiroot, -1, \r
2029                                 &g_menumgr,\r
2030                                 L"Asd");\r
2031         menu->drop();*/\r
2032         \r
2033         // Launch pause menu\r
2034         (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,\r
2035                         &g_menumgr))->drop();\r
2036         \r
2037         // Enable texts\r
2038         guitext2->setVisible(true);\r
2039         guitext_info->setVisible(true);\r
2040         guitext_chat->setVisible(true);\r
2041 \r
2042         //s32 guitext_chat_pad_bottom = 70;\r
2043 \r
2044         v2u32 screensize(0,0);\r
2045         v2u32 last_screensize(0,0);\r
2046         \r
2047         /*\r
2048                 Some statistics are collected in these\r
2049         */\r
2050         u32 drawtime = 0;\r
2051         u32 beginscenetime = 0;\r
2052         u32 scenetime = 0;\r
2053         u32 endscenetime = 0;\r
2054         \r
2055         // A test\r
2056         //throw con::PeerNotFoundException("lol");\r
2057 \r
2058         core::list<float> frametime_log;\r
2059 \r
2060         /*\r
2061                 Main loop\r
2062         */\r
2063 \r
2064         bool first_loop_after_window_activation = true;\r
2065 \r
2066         // Time is in milliseconds\r
2067         // NOTE: getRealTime() causes strange problems in wine (imprecision?)\r
2068         // NOTE: So we have to use getTime() and call run()s between them\r
2069         u32 lasttime = device->getTimer()->getTime();\r
2070 \r
2071         while(device->run() && kill == false)\r
2072         {\r
2073                 if(g_disconnect_requested)\r
2074                 {\r
2075                         g_disconnect_requested = false;\r
2076                         break;\r
2077                 }\r
2078 \r
2079                 /*\r
2080                         Run global IrrlichtWrapper's main thread processing stuff\r
2081                 */\r
2082                 g_irrlicht->Run();\r
2083 \r
2084                 /*\r
2085                         Process TextureSource's queue\r
2086                 */\r
2087                 texturesource->processQueue();\r
2088 \r
2089                 /*\r
2090                         Random calculations\r
2091                 */\r
2092                 last_screensize = screensize;\r
2093                 screensize = driver->getScreenSize();\r
2094                 v2s32 displaycenter(screensize.X/2,screensize.Y/2);\r
2095                 //bool screensize_changed = screensize != last_screensize;\r
2096                 \r
2097                 // Hilight boxes collected during the loop and displayed\r
2098                 core::list< core::aabbox3d<f32> > hilightboxes;\r
2099                 \r
2100                 // Info text\r
2101                 std::wstring infotext;\r
2102 \r
2103                 // When screen size changes, update positions and sizes of stuff\r
2104                 /*if(screensize_changed)\r
2105                 {\r
2106                         v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);\r
2107                         quick_inventory->updatePosition(pos);\r
2108                 }*/\r
2109 \r
2110                 //TimeTaker //timer1("//timer1");\r
2111                 \r
2112                 // Time of frame without fps limit\r
2113                 float busytime;\r
2114                 u32 busytime_u32;\r
2115                 {\r
2116                         // not using getRealTime is necessary for wine\r
2117                         u32 time = device->getTimer()->getTime();\r
2118                         if(time > lasttime)\r
2119                                 busytime_u32 = time - lasttime;\r
2120                         else\r
2121                                 busytime_u32 = 0;\r
2122                         busytime = busytime_u32 / 1000.0;\r
2123                 }\r
2124 \r
2125                 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;\r
2126         \r
2127                 // Absolutelu necessary for wine!\r
2128                 device->run();\r
2129 \r
2130                 /*\r
2131                         Viewing range\r
2132                 */\r
2133                 \r
2134                 updateViewingRange(busytime, &client);\r
2135                 \r
2136                 /*\r
2137                         FPS limiter\r
2138                 */\r
2139 \r
2140                 {\r
2141                         float fps_max = g_settings.getFloat("fps_max");\r
2142                         u32 frametime_min = 1000./fps_max;\r
2143                         \r
2144                         if(busytime_u32 < frametime_min)\r
2145                         {\r
2146                                 u32 sleeptime = frametime_min - busytime_u32;\r
2147                                 device->sleep(sleeptime);\r
2148                         }\r
2149                 }\r
2150 \r
2151                 // Absolutelu necessary for wine!\r
2152                 device->run();\r
2153 \r
2154                 /*\r
2155                         Time difference calculation\r
2156                 */\r
2157                 f32 dtime; // in seconds\r
2158                 \r
2159                 u32 time = device->getTimer()->getTime();\r
2160                 if(time > lasttime)\r
2161                         dtime = (time - lasttime) / 1000.0;\r
2162                 else\r
2163                         dtime = 0;\r
2164                 lasttime = time;\r
2165 \r
2166                 /*\r
2167                         Log frametime for visualization\r
2168                 */\r
2169                 frametime_log.push_back(dtime);\r
2170                 if(frametime_log.size() > 100)\r
2171                 {\r
2172                         core::list<float>::Iterator i = frametime_log.begin();\r
2173                         frametime_log.erase(i);\r
2174                 }\r
2175 \r
2176                 /*\r
2177                         Visualize frametime in terminal\r
2178                 */\r
2179                 /*for(u32 i=0; i<dtime*400; i++)\r
2180                         std::cout<<"X";\r
2181                 std::cout<<std::endl;*/\r
2182 \r
2183                 /*\r
2184                         Time average and jitter calculation\r
2185                 */\r
2186 \r
2187                 static f32 dtime_avg1 = 0.0;\r
2188                 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;\r
2189                 f32 dtime_jitter1 = dtime - dtime_avg1;\r
2190 \r
2191                 static f32 dtime_jitter1_max_sample = 0.0;\r
2192                 static f32 dtime_jitter1_max_fraction = 0.0;\r
2193                 {\r
2194                         static f32 jitter1_max = 0.0;\r
2195                         static f32 counter = 0.0;\r
2196                         if(dtime_jitter1 > jitter1_max)\r
2197                                 jitter1_max = dtime_jitter1;\r
2198                         counter += dtime;\r
2199                         if(counter > 0.0)\r
2200                         {\r
2201                                 counter -= 3.0;\r
2202                                 dtime_jitter1_max_sample = jitter1_max;\r
2203                                 dtime_jitter1_max_fraction\r
2204                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);\r
2205                                 jitter1_max = 0.0;\r
2206                         }\r
2207                 }\r
2208                 \r
2209                 /*\r
2210                         Busytime average and jitter calculation\r
2211                 */\r
2212 \r
2213                 static f32 busytime_avg1 = 0.0;\r
2214                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;\r
2215                 f32 busytime_jitter1 = busytime - busytime_avg1;\r
2216                 \r
2217                 static f32 busytime_jitter1_max_sample = 0.0;\r
2218                 static f32 busytime_jitter1_min_sample = 0.0;\r
2219                 {\r
2220                         static f32 jitter1_max = 0.0;\r
2221                         static f32 jitter1_min = 0.0;\r
2222                         static f32 counter = 0.0;\r
2223                         if(busytime_jitter1 > jitter1_max)\r
2224                                 jitter1_max = busytime_jitter1;\r
2225                         if(busytime_jitter1 < jitter1_min)\r
2226                                 jitter1_min = busytime_jitter1;\r
2227                         counter += dtime;\r
2228                         if(counter > 0.0){\r
2229                                 counter -= 3.0;\r
2230                                 busytime_jitter1_max_sample = jitter1_max;\r
2231                                 busytime_jitter1_min_sample = jitter1_min;\r
2232                                 jitter1_max = 0.0;\r
2233                                 jitter1_min = 0.0;\r
2234                         }\r
2235                 }\r
2236                 \r
2237                 /*\r
2238                         Debug info for client\r
2239                 */\r
2240                 {\r
2241                         static float counter = 0.0;\r
2242                         counter -= dtime;\r
2243                         if(counter < 0)\r
2244                         {\r
2245                                 counter = 30.0;\r
2246                                 client.printDebugInfo(std::cout);\r
2247                         }\r
2248                 }\r
2249 \r
2250                 /*\r
2251                         Input handler step()\r
2252                 */\r
2253                 g_input->step(dtime);\r
2254 \r
2255                 /*\r
2256                         Player speed control\r
2257                 */\r
2258                 \r
2259                 {\r
2260                         /*bool a_up,\r
2261                         bool a_down,\r
2262                         bool a_left,\r
2263                         bool a_right,\r
2264                         bool a_jump,\r
2265                         bool a_superspeed,\r
2266                         bool a_sneak,\r
2267                         float a_pitch,\r
2268                         float a_yaw*/\r
2269                         PlayerControl control(\r
2270                                 g_input->isKeyDown(irr::KEY_KEY_W),\r
2271                                 g_input->isKeyDown(irr::KEY_KEY_S),\r
2272                                 g_input->isKeyDown(irr::KEY_KEY_A),\r
2273                                 g_input->isKeyDown(irr::KEY_KEY_D),\r
2274                                 g_input->isKeyDown(irr::KEY_SPACE),\r
2275                                 g_input->isKeyDown(irr::KEY_KEY_E),\r
2276                                 g_input->isKeyDown(irr::KEY_LSHIFT)\r
2277                                                 || g_input->isKeyDown(irr::KEY_RSHIFT),\r
2278                                 camera_pitch,\r
2279                                 camera_yaw\r
2280                         );\r
2281                         client.setPlayerControl(control);\r
2282                 }\r
2283 \r
2284                 /*\r
2285                         Process environment\r
2286                 */\r
2287                 \r
2288                 {\r
2289                         //TimeTaker timer("client.step(dtime)");\r
2290                         client.step(dtime);\r
2291                         //client.step(dtime_avg1);\r
2292                 }\r
2293 \r
2294                 if(server != NULL)\r
2295                 {\r
2296                         //TimeTaker timer("server->step(dtime)");\r
2297                         server->step(dtime);\r
2298                 }\r
2299 \r
2300                 v3f player_position = client.getPlayerPosition();\r
2301                 \r
2302                 //TimeTaker //timer2("//timer2");\r
2303 \r
2304                 /*\r
2305                         Mouse and camera control\r
2306                 */\r
2307                 \r
2308                 if((device->isWindowActive() && noMenuActive()) || random_input)\r
2309                 {\r
2310                         if(!random_input)\r
2311                                 device->getCursorControl()->setVisible(false);\r
2312 \r
2313                         if(first_loop_after_window_activation){\r
2314                                 //std::cout<<"window active, first loop"<<std::endl;\r
2315                                 first_loop_after_window_activation = false;\r
2316                         }\r
2317                         else{\r
2318                                 s32 dx = g_input->getMousePos().X - displaycenter.X;\r
2319                                 s32 dy = g_input->getMousePos().Y - displaycenter.Y;\r
2320                                 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;\r
2321                                 camera_yaw -= dx*0.2;\r
2322                                 camera_pitch += dy*0.2;\r
2323                                 if(camera_pitch < -89.5) camera_pitch = -89.5;\r
2324                                 if(camera_pitch > 89.5) camera_pitch = 89.5;\r
2325                         }\r
2326                         g_input->setMousePos(displaycenter.X, displaycenter.Y);\r
2327                 }\r
2328                 else{\r
2329                         device->getCursorControl()->setVisible(true);\r
2330 \r
2331                         //std::cout<<"window inactive"<<std::endl;\r
2332                         first_loop_after_window_activation = true;\r
2333                 }\r
2334 \r
2335                 camera_yaw = wrapDegrees(camera_yaw);\r
2336                 camera_pitch = wrapDegrees(camera_pitch);\r
2337                 \r
2338                 v3f camera_direction = v3f(0,0,1);\r
2339                 camera_direction.rotateYZBy(camera_pitch);\r
2340                 camera_direction.rotateXZBy(camera_yaw);\r
2341                 \r
2342                 // This is at the height of the eyes of the current figure\r
2343                 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);\r
2344                 // This is more like in minecraft\r
2345                 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);\r
2346 \r
2347                 camera->setPosition(camera_position);\r
2348                 // *100.0 helps in large map coordinates\r
2349                 camera->setTarget(camera_position + camera_direction * 100.0);\r
2350 \r
2351                 if(FIELD_OF_VIEW_TEST){\r
2352                         client.updateCamera(v3f(0,0,0), v3f(0,0,1));\r
2353                 }\r
2354                 else{\r
2355                         //TimeTaker timer("client.updateCamera");\r
2356                         client.updateCamera(camera_position, camera_direction);\r
2357                 }\r
2358                 \r
2359                 //timer2.stop();\r
2360                 //TimeTaker //timer3("//timer3");\r
2361 \r
2362                 /*\r
2363                         Calculate what block is the crosshair pointing to\r
2364                 */\r
2365                 \r
2366                 //u32 t1 = device->getTimer()->getRealTime();\r
2367                 \r
2368                 //f32 d = 4; // max. distance\r
2369                 f32 d = 4; // max. distance\r
2370                 core::line3d<f32> shootline(camera_position,\r
2371                                 camera_position + camera_direction * BS * (d+1));\r
2372 \r
2373                 MapBlockObject *selected_object = client.getSelectedObject\r
2374                                 (d*BS, camera_position, shootline);\r
2375 \r
2376                 /*\r
2377                         If it's pointing to a MapBlockObject\r
2378                 */\r
2379 \r
2380                 if(selected_object != NULL)\r
2381                 {\r
2382                         //dstream<<"Client returned selected_object != NULL"<<std::endl;\r
2383 \r
2384                         core::aabbox3d<f32> box_on_map\r
2385                                         = selected_object->getSelectionBoxOnMap();\r
2386 \r
2387                         hilightboxes.push_back(box_on_map);\r
2388 \r
2389                         infotext = narrow_to_wide(selected_object->infoText());\r
2390 \r
2391                         if(g_input->getLeftClicked())\r
2392                         {\r
2393                                 std::cout<<DTIME<<"Left-clicked object"<<std::endl;\r
2394                                 client.clickObject(0, selected_object->getBlock()->getPos(),\r
2395                                                 selected_object->getId(), g_selected_item);\r
2396                         }\r
2397                         else if(g_input->getRightClicked())\r
2398                         {\r
2399                                 std::cout<<DTIME<<"Right-clicked object"<<std::endl;\r
2400                                 /*\r
2401                                         Check if we want to modify the object ourselves\r
2402                                 */\r
2403                                 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)\r
2404                                 {\r
2405                                         dstream<<"Sign object right-clicked"<<std::endl;\r
2406                                         \r
2407                                         if(random_input == false)\r
2408                                         {\r
2409                                                 // Get a new text for it\r
2410 \r
2411                                                 TextDest *dest = new TextDestSign(\r
2412                                                                 selected_object->getBlock()->getPos(),\r
2413                                                                 selected_object->getId(),\r
2414                                                                 &client);\r
2415 \r
2416                                                 SignObject *sign_object = (SignObject*)selected_object;\r
2417 \r
2418                                                 std::wstring wtext =\r
2419                                                                 narrow_to_wide(sign_object->getText());\r
2420 \r
2421                                                 (new GUITextInputMenu(guienv, guiroot, -1,\r
2422                                                                 &g_menumgr, dest,\r
2423                                                                 wtext))->drop();\r
2424                                         }\r
2425                                 }\r
2426                                 /*\r
2427                                         Otherwise pass the event to the server as-is\r
2428                                 */\r
2429                                 else\r
2430                                 {\r
2431                                         client.clickObject(1, selected_object->getBlock()->getPos(),\r
2432                                                         selected_object->getId(), g_selected_item);\r
2433                                 }\r
2434                         }\r
2435                 }\r
2436                 else // selected_object == NULL\r
2437                 {\r
2438 \r
2439                 /*\r
2440                         Find out which node we are pointing at\r
2441                 */\r
2442                 \r
2443                 bool nodefound = false;\r
2444                 v3s16 nodepos;\r
2445                 v3s16 neighbourpos;\r
2446                 core::aabbox3d<f32> nodehilightbox;\r
2447                 f32 mindistance = BS * 1001;\r
2448                 \r
2449                 v3s16 pos_i = floatToInt(player_position, BS);\r
2450 \r
2451                 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"\r
2452                                 <<std::endl;*/\r
2453 \r
2454                 s16 a = d;\r
2455                 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);\r
2456                 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);\r
2457                 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);\r
2458                 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);\r
2459                 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);\r
2460                 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);\r
2461                 \r
2462                 for(s16 y = ystart; y <= yend; y++)\r
2463                 for(s16 z = zstart; z <= zend; z++)\r
2464                 for(s16 x = xstart; x <= xend; x++)\r
2465                 {\r
2466                         MapNode n;\r
2467                         try\r
2468                         {\r
2469                                 n = client.getNode(v3s16(x,y,z));\r
2470                                 if(content_pointable(n.d) == false)\r
2471                                         continue;\r
2472                         }\r
2473                         catch(InvalidPositionException &e)\r
2474                         {\r
2475                                 continue;\r
2476                         }\r
2477 \r
2478                         v3s16 np(x,y,z);\r
2479                         v3f npf = intToFloat(np, BS);\r
2480                         \r
2481                         f32 d = 0.01;\r
2482                         \r
2483                         v3s16 dirs[6] = {\r
2484                                 v3s16(0,0,1), // back\r
2485                                 v3s16(0,1,0), // top\r
2486                                 v3s16(1,0,0), // right\r
2487                                 v3s16(0,0,-1), // front\r
2488                                 v3s16(0,-1,0), // bottom\r
2489                                 v3s16(-1,0,0), // left\r
2490                         };\r
2491                         \r
2492                         /*\r
2493                                 Meta-objects\r
2494                         */\r
2495                         if(n.d == CONTENT_TORCH)\r
2496                         {\r
2497                                 v3s16 dir = unpackDir(n.dir);\r
2498                                 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);\r
2499                                 dir_f *= BS/2 - BS/6 - BS/20;\r
2500                                 v3f cpf = npf + dir_f;\r
2501                                 f32 distance = (cpf - camera_position).getLength();\r
2502 \r
2503                                 core::aabbox3d<f32> box;\r
2504                                 \r
2505                                 // bottom\r
2506                                 if(dir == v3s16(0,-1,0))\r
2507                                 {\r
2508                                         box = core::aabbox3d<f32>(\r
2509                                                 npf - v3f(BS/6, BS/2, BS/6),\r
2510                                                 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)\r
2511                                         );\r
2512                                 }\r
2513                                 // top\r
2514                                 else if(dir == v3s16(0,1,0))\r
2515                                 {\r
2516                                         box = core::aabbox3d<f32>(\r
2517                                                 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),\r
2518                                                 npf + v3f(BS/6, BS/2, BS/6)\r
2519                                         );\r
2520                                 }\r
2521                                 // side\r
2522                                 else\r
2523                                 {\r
2524                                         box = core::aabbox3d<f32>(\r
2525                                                 cpf - v3f(BS/6, BS/3, BS/6),\r
2526                                                 cpf + v3f(BS/6, BS/3, BS/6)\r
2527                                         );\r
2528                                 }\r
2529 \r
2530                                 if(distance < mindistance)\r
2531                                 {\r
2532                                         if(box.intersectsWithLine(shootline))\r
2533                                         {\r
2534                                                 nodefound = true;\r
2535                                                 nodepos = np;\r
2536                                                 neighbourpos = np;\r
2537                                                 mindistance = distance;\r
2538                                                 nodehilightbox = box;\r
2539                                         }\r
2540                                 }\r
2541                         }\r
2542                         /*\r
2543                                 Regular blocks\r
2544                         */\r
2545                         else\r
2546                         {\r
2547                                 for(u16 i=0; i<6; i++)\r
2548                                 {\r
2549                                         v3f dir_f = v3f(dirs[i].X,\r
2550                                                         dirs[i].Y, dirs[i].Z);\r
2551                                         v3f centerpoint = npf + dir_f * BS/2;\r
2552                                         f32 distance =\r
2553                                                         (centerpoint - camera_position).getLength();\r
2554                                         \r
2555                                         if(distance < mindistance)\r
2556                                         {\r
2557                                                 core::CMatrix4<f32> m;\r
2558                                                 m.buildRotateFromTo(v3f(0,0,1), dir_f);\r
2559 \r
2560                                                 // This is the back face\r
2561                                                 v3f corners[2] = {\r
2562                                                         v3f(BS/2, BS/2, BS/2),\r
2563                                                         v3f(-BS/2, -BS/2, BS/2+d)\r
2564                                                 };\r
2565                                                 \r
2566                                                 for(u16 j=0; j<2; j++)\r
2567                                                 {\r
2568                                                         m.rotateVect(corners[j]);\r
2569                                                         corners[j] += npf;\r
2570                                                 }\r
2571 \r
2572                                                 core::aabbox3d<f32> facebox(corners[0]);\r
2573                                                 facebox.addInternalPoint(corners[1]);\r
2574 \r
2575                                                 if(facebox.intersectsWithLine(shootline))\r
2576                                                 {\r
2577                                                         nodefound = true;\r
2578                                                         nodepos = np;\r
2579                                                         neighbourpos = np + dirs[i];\r
2580                                                         mindistance = distance;\r
2581 \r
2582                                                         //nodehilightbox = facebox;\r
2583 \r
2584                                                         const float d = 0.502;\r
2585                                                         core::aabbox3d<f32> nodebox\r
2586                                                                         (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
2587                                                         v3f nodepos_f = intToFloat(nodepos, BS);\r
2588                                                         nodebox.MinEdge += nodepos_f;\r
2589                                                         nodebox.MaxEdge += nodepos_f;\r
2590                                                         nodehilightbox = nodebox;\r
2591                                                 }\r
2592                                         } // if distance < mindistance\r
2593                                 } // for dirs\r
2594                         } // regular block\r
2595                 } // for coords\r
2596 \r
2597                 static float nodig_delay_counter = 0.0;\r
2598 \r
2599                 if(nodefound)\r
2600                 {\r
2601                         static v3s16 nodepos_old(-32768,-32768,-32768);\r
2602 \r
2603                         static float dig_time = 0.0;\r
2604                         static u16 dig_index = 0;\r
2605                         \r
2606                         // Visualize selection\r
2607 \r
2608                         hilightboxes.push_back(nodehilightbox);\r
2609 \r
2610                         // Handle digging\r
2611                         \r
2612                         if(g_input->getLeftReleased())\r
2613                         {\r
2614                                 client.clearTempMod(nodepos);\r
2615                                 dig_time = 0.0;\r
2616                         }\r
2617                         \r
2618                         if(nodig_delay_counter > 0.0)\r
2619                         {\r
2620                                 nodig_delay_counter -= dtime;\r
2621                         }\r
2622                         else\r
2623                         {\r
2624                                 if(nodepos != nodepos_old)\r
2625                                 {\r
2626                                         std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","\r
2627                                                         <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;\r
2628 \r
2629                                         if(nodepos_old != v3s16(-32768,-32768,-32768))\r
2630                                         {\r
2631                                                 client.clearTempMod(nodepos_old);\r
2632                                                 dig_time = 0.0;\r
2633                                         }\r
2634                                 }\r
2635 \r
2636                                 if(g_input->getLeftClicked() ||\r
2637                                                 (g_input->getLeftState() && nodepos != nodepos_old))\r
2638                                 {\r
2639                                         dstream<<DTIME<<"Started digging"<<std::endl;\r
2640                                         client.groundAction(0, nodepos, neighbourpos, g_selected_item);\r
2641                                 }\r
2642                                 if(g_input->getLeftClicked())\r
2643                                 {\r
2644                                         client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));\r
2645                                 }\r
2646                                 if(g_input->getLeftState())\r
2647                                 {\r
2648                                         MapNode n = client.getNode(nodepos);\r
2649                                 \r
2650                                         // Get tool name. Default is "" = bare hands\r
2651                                         std::string toolname = "";\r
2652                                         InventoryList *mlist = local_inventory.getList("main");\r
2653                                         if(mlist != NULL)\r
2654                                         {\r
2655                                                 InventoryItem *item = mlist->getItem(g_selected_item);\r
2656                                                 if(item && (std::string)item->getName() == "ToolItem")\r
2657                                                 {\r
2658                                                         ToolItem *titem = (ToolItem*)item;\r
2659                                                         toolname = titem->getToolName();\r
2660                                                 }\r
2661                                         }\r
2662 \r
2663                                         // Get digging properties for material and tool\r
2664                                         u8 material = n.d;\r
2665                                         DiggingProperties prop =\r
2666                                                         getDiggingProperties(material, toolname);\r
2667                                         \r
2668                                         float dig_time_complete = 0.0;\r
2669 \r
2670                                         if(prop.diggable == false)\r
2671                                         {\r
2672                                                 /*dstream<<"Material "<<(int)material\r
2673                                                                 <<" not diggable with \""\r
2674                                                                 <<toolname<<"\""<<std::endl;*/\r
2675                                                 // I guess nobody will wait for this long\r
2676                                                 dig_time_complete = 10000000.0;\r
2677                                         }\r
2678                                         else\r
2679                                         {\r
2680                                                 dig_time_complete = prop.time;\r
2681                                         }\r
2682                                         \r
2683                                         if(dig_time_complete >= 0.001)\r
2684                                         {\r
2685                                                 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH\r
2686                                                                 * dig_time/dig_time_complete);\r
2687                                         }\r
2688                                         // This is for torches\r
2689                                         else\r
2690                                         {\r
2691                                                 dig_index = CRACK_ANIMATION_LENGTH;\r
2692                                         }\r
2693 \r
2694                                         if(dig_index < CRACK_ANIMATION_LENGTH)\r
2695                                         {\r
2696                                                 //TimeTaker timer("client.setTempMod");\r
2697                                                 //dstream<<"dig_index="<<dig_index<<std::endl;\r
2698                                                 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));\r
2699                                         }\r
2700                                         else\r
2701                                         {\r
2702                                                 dstream<<DTIME<<"Digging completed"<<std::endl;\r
2703                                                 client.groundAction(3, nodepos, neighbourpos, g_selected_item);\r
2704                                                 client.clearTempMod(nodepos);\r
2705                                                 client.removeNode(nodepos);\r
2706 \r
2707                                                 dig_time = 0;\r
2708 \r
2709                                                 nodig_delay_counter = dig_time_complete\r
2710                                                                 / (float)CRACK_ANIMATION_LENGTH;\r
2711 \r
2712                                                 // We don't want a corresponding delay to\r
2713                                                 // very time consuming nodes\r
2714                                                 if(nodig_delay_counter > 0.5)\r
2715                                                 {\r
2716                                                         nodig_delay_counter = 0.5;\r
2717                                                 }\r
2718                                                 // We want a slight delay to very little\r
2719                                                 // time consuming nodes\r
2720                                                 float mindelay = 0.15;\r
2721                                                 if(nodig_delay_counter < mindelay)\r
2722                                                 {\r
2723                                                         nodig_delay_counter = mindelay;\r
2724                                                 }\r
2725                                         }\r
2726 \r
2727                                         dig_time += dtime;\r
2728                                 }\r
2729                         }\r
2730                         \r
2731                         if(g_input->getRightClicked())\r
2732                         {\r
2733                                 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;\r
2734                                 client.groundAction(1, nodepos, neighbourpos, g_selected_item);\r
2735                         }\r
2736                         \r
2737                         nodepos_old = nodepos;\r
2738                 }\r
2739                 else{\r
2740                 }\r
2741 \r
2742                 } // selected_object == NULL\r
2743                 \r
2744                 g_input->resetLeftClicked();\r
2745                 g_input->resetRightClicked();\r
2746                 \r
2747                 if(g_input->getLeftReleased())\r
2748                 {\r
2749                         std::cout<<DTIME<<"Left button released (stopped digging)"\r
2750                                         <<std::endl;\r
2751                         client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);\r
2752                 }\r
2753                 if(g_input->getRightReleased())\r
2754                 {\r
2755                         //std::cout<<DTIME<<"Right released"<<std::endl;\r
2756                         // Nothing here\r
2757                 }\r
2758                 \r
2759                 g_input->resetLeftReleased();\r
2760                 g_input->resetRightReleased();\r
2761                 \r
2762                 /*\r
2763                         Calculate stuff for drawing\r
2764                 */\r
2765 \r
2766                 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);\r
2767                 \r
2768                 u32 daynight_ratio = client.getDayNightRatio();\r
2769                 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);\r
2770                 video::SColor bgcolor = video::SColor(\r
2771                                 255,\r
2772                                 skycolor.getRed() * l / 255,\r
2773                                 skycolor.getGreen() * l / 255,\r
2774                                 skycolor.getBlue() * l / 255);\r
2775 \r
2776                 /*\r
2777                         Fog\r
2778                 */\r
2779                 \r
2780                 if(g_settings.getBool("enable_fog") == true)\r
2781                 {\r
2782                         //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;\r
2783                         f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;\r
2784                         //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;\r
2785                         if(draw_control.range_all)\r
2786                                 range = 100000*BS;\r
2787 \r
2788                         driver->setFog(\r
2789                                 bgcolor,\r
2790                                 video::EFT_FOG_LINEAR,\r
2791                                 range*0.6,\r
2792                                 range*1.0,\r
2793                                 0.01,\r
2794                                 false, // pixel fog\r
2795                                 false // range fog\r
2796                         );\r
2797                 }\r
2798                 else\r
2799                 {\r
2800                         driver->setFog(\r
2801                                 bgcolor,\r
2802                                 video::EFT_FOG_LINEAR,\r
2803                                 100000*BS,\r
2804                                 110000*BS,\r
2805                                 0.01,\r
2806                                 false, // pixel fog\r
2807                                 false // range fog\r
2808                         );\r
2809                 }\r
2810 \r
2811 \r
2812                 /*\r
2813                         Update gui stuff (0ms)\r
2814                 */\r
2815 \r
2816                 //TimeTaker guiupdatetimer("Gui updating");\r
2817                 \r
2818                 {\r
2819                         static float drawtime_avg = 0;\r
2820                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;\r
2821                         static float beginscenetime_avg = 0;\r
2822                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;\r
2823                         static float scenetime_avg = 0;\r
2824                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;\r
2825                         static float endscenetime_avg = 0;\r
2826                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;\r
2827                         \r
2828                         char temptext[300];\r
2829                         snprintf(temptext, 300, "Minetest-c55 ("\r
2830                                         "F: item=%i"\r
2831                                         ", R: range_all=%i"\r
2832                                         ")"\r
2833                                         " drawtime=%.0f, beginscenetime=%.0f"\r
2834                                         ", scenetime=%.0f, endscenetime=%.0f",\r
2835                                         g_selected_item,\r
2836                                         draw_control.range_all,\r
2837                                         drawtime_avg,\r
2838                                         beginscenetime_avg,\r
2839                                         scenetime_avg,\r
2840                                         endscenetime_avg\r
2841                                         );\r
2842                         \r
2843                         guitext->setText(narrow_to_wide(temptext).c_str());\r
2844                 }\r
2845                 \r
2846                 {\r
2847                         char temptext[300];\r
2848                         snprintf(temptext, 300,\r
2849                                         "(% .1f, % .1f, % .1f)"\r
2850                                         " (% .3f < btime_jitter < % .3f"\r
2851                                         ", dtime_jitter = % .1f %%"\r
2852                                         ", v_range = %.1f)",\r
2853                                         player_position.X/BS,\r
2854                                         player_position.Y/BS,\r
2855                                         player_position.Z/BS,\r
2856                                         busytime_jitter1_min_sample,\r
2857                                         busytime_jitter1_max_sample,\r
2858                                         dtime_jitter1_max_fraction * 100.0,\r
2859                                         draw_control.wanted_range\r
2860                                         );\r
2861 \r
2862                         guitext2->setText(narrow_to_wide(temptext).c_str());\r
2863                 }\r
2864                 \r
2865                 {\r
2866                         guitext_info->setText(infotext.c_str());\r
2867                 }\r
2868                 \r
2869                 /*\r
2870                         Get chat messages from client\r
2871                 */\r
2872                 {\r
2873                         // Get new messages\r
2874                         std::wstring message;\r
2875                         while(client.getChatMessage(message))\r
2876                         {\r
2877                                 chat_lines.push_back(ChatLine(message));\r
2878                                 /*if(chat_lines.size() > 6)\r
2879                                 {\r
2880                                         core::list<ChatLine>::Iterator\r
2881                                                         i = chat_lines.begin();\r
2882                                         chat_lines.erase(i);\r
2883                                 }*/\r
2884                         }\r
2885                         // Append them to form the whole static text and throw\r
2886                         // it to the gui element\r
2887                         std::wstring whole;\r
2888                         // This will correspond to the line number counted from\r
2889                         // top to bottom, from size-1 to 0\r
2890                         s16 line_number = chat_lines.size();\r
2891                         // Count of messages to be removed from the top\r
2892                         u16 to_be_removed_count = 0;\r
2893                         for(core::list<ChatLine>::Iterator\r
2894                                         i = chat_lines.begin();\r
2895                                         i != chat_lines.end(); i++)\r
2896                         {\r
2897                                 // After this, line number is valid for this loop\r
2898                                 line_number--;\r
2899                                 // Increment age\r
2900                                 (*i).age += dtime;\r
2901                                 /*\r
2902                                         This results in a maximum age of 60*6 to the\r
2903                                         lowermost line and a maximum of 6 lines\r
2904                                 */\r
2905                                 float allowed_age = (6-line_number) * 60.0;\r
2906 \r
2907                                 if((*i).age > allowed_age)\r
2908                                 {\r
2909                                         to_be_removed_count++;\r
2910                                         continue;\r
2911                                 }\r
2912                                 whole += (*i).text + L'\n';\r
2913                         }\r
2914                         for(u16 i=0; i<to_be_removed_count; i++)\r
2915                         {\r
2916                                 core::list<ChatLine>::Iterator\r
2917                                                 it = chat_lines.begin();\r
2918                                 chat_lines.erase(it);\r
2919                         }\r
2920                         guitext_chat->setText(whole.c_str());\r
2921 \r
2922                         // Update gui element size and position\r
2923 \r
2924                         /*core::rect<s32> rect(\r
2925                                         10,\r
2926                                         screensize.Y - guitext_chat_pad_bottom\r
2927                                                         - text_height*chat_lines.size(),\r
2928                                         screensize.X - 10,\r
2929                                         screensize.Y - guitext_chat_pad_bottom\r
2930                         );*/\r
2931                         core::rect<s32> rect(\r
2932                                         10,\r
2933                                         50,\r
2934                                         screensize.X - 10,\r
2935                                         50 + text_height*chat_lines.size()\r
2936                         );\r
2937 \r
2938                         guitext_chat->setRelativePosition(rect);\r
2939 \r
2940                         if(chat_lines.size() == 0)\r
2941                                 guitext_chat->setVisible(false);\r
2942                         else\r
2943                                 guitext_chat->setVisible(true);\r
2944                 }\r
2945 \r
2946                 /*\r
2947                         Inventory\r
2948                 */\r
2949                 \r
2950                 static u16 old_selected_item = 65535;\r
2951                 if(client.getLocalInventoryUpdated()\r
2952                                 || g_selected_item != old_selected_item)\r
2953                 {\r
2954                         old_selected_item = g_selected_item;\r
2955                         //std::cout<<"Updating local inventory"<<std::endl;\r
2956                         client.getLocalInventory(local_inventory);\r
2957                         /*quick_inventory->setSelection(g_selected_item);\r
2958                         quick_inventory->update();*/\r
2959                 }\r
2960                 \r
2961                 /*\r
2962                         Send actions returned by the inventory menu\r
2963                 */\r
2964                 while(inventory_action_queue.size() != 0)\r
2965                 {\r
2966                         InventoryAction *a = inventory_action_queue.pop_front();\r
2967 \r
2968                         client.sendInventoryAction(a);\r
2969                         // Eat it\r
2970                         delete a;\r
2971                 }\r
2972 \r
2973                 /*\r
2974                         Drawing begins\r
2975                 */\r
2976 \r
2977                 TimeTaker drawtimer("Drawing");\r
2978 \r
2979                 \r
2980                 {\r
2981                         TimeTaker timer("beginScene");\r
2982                         driver->beginScene(true, true, bgcolor);\r
2983                         //driver->beginScene(false, true, bgcolor);\r
2984                         beginscenetime = timer.stop(true);\r
2985                 }\r
2986 \r
2987                 //timer3.stop();\r
2988                 \r
2989                 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;\r
2990                 \r
2991                 {\r
2992                         TimeTaker timer("smgr");\r
2993                         smgr->drawAll();\r
2994                         scenetime = timer.stop(true);\r
2995                 }\r
2996                 \r
2997                 {\r
2998                 //TimeTaker timer9("auxiliary drawings");\r
2999                 // 0ms\r
3000                 \r
3001                 //timer9.stop();\r
3002                 //TimeTaker //timer10("//timer10");\r
3003                 \r
3004                 video::SMaterial m;\r
3005                 //m.Thickness = 10;\r
3006                 m.Thickness = 3;\r
3007                 m.Lighting = false;\r
3008                 driver->setMaterial(m);\r
3009 \r
3010                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);\r
3011 \r
3012                 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();\r
3013                                 i != hilightboxes.end(); i++)\r
3014                 {\r
3015                         /*std::cout<<"hilightbox min="\r
3016                                         <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"\r
3017                                         <<" max="\r
3018                                         <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"\r
3019                                         <<std::endl;*/\r
3020                         driver->draw3DBox(*i, video::SColor(255,0,0,0));\r
3021                 }\r
3022 \r
3023                 /*\r
3024                         Draw crosshair\r
3025                 */\r
3026                 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),\r
3027                                 displaycenter + core::vector2d<s32>(10,0),\r
3028                                 video::SColor(255,255,255,255));\r
3029                 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),\r
3030                                 displaycenter + core::vector2d<s32>(0,10),\r
3031                                 video::SColor(255,255,255,255));\r
3032 \r
3033                 /*\r
3034                         Frametime log\r
3035                 */\r
3036                 if(g_settings.getBool("frametime_graph") == true)\r
3037                 {\r
3038                         s32 x = 10;\r
3039                         for(core::list<float>::Iterator\r
3040                                         i = frametime_log.begin();\r
3041                                         i != frametime_log.end();\r
3042                                         i++)\r
3043                         {\r
3044                                 driver->draw2DLine(v2s32(x,50),\r
3045                                                 v2s32(x,50+(*i)*1000),\r
3046                                                 video::SColor(255,255,255,255));\r
3047                                 x++;\r
3048                         }\r
3049                 }\r
3050 \r
3051                 } // timer\r
3052 \r
3053                 //timer10.stop();\r
3054                 //TimeTaker //timer11("//timer11");\r
3055 \r
3056                 /*\r
3057                         Draw gui\r
3058                 */\r
3059                 // 0-1ms\r
3060                 guienv->drawAll();\r
3061 \r
3062                 /*\r
3063                         Draw hotbar\r
3064                 */\r
3065                 {\r
3066                         draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),\r
3067                                         hotbar_imagesize, hotbar_itemcount, &local_inventory);\r
3068                 }\r
3069                 \r
3070                 // End drawing\r
3071                 {\r
3072                         TimeTaker timer("endScene");\r
3073                         driver->endScene();\r
3074                         endscenetime = timer.stop(true);\r
3075                 }\r
3076 \r
3077                 drawtime = drawtimer.stop(true);\r
3078 \r
3079                 /*\r
3080                         Drawing ends\r
3081                 */\r
3082                 \r
3083                 static s16 lastFPS = 0;\r
3084                 //u16 fps = driver->getFPS();\r
3085                 u16 fps = (1.0/dtime_avg1);\r
3086 \r
3087                 if (lastFPS != fps)\r
3088                 {\r
3089                         core::stringw str = L"Minetest [";\r
3090                         str += driver->getName();\r
3091                         str += "] FPS:";\r
3092                         str += fps;\r
3093 \r
3094                         device->setWindowCaption(str.c_str());\r
3095                         lastFPS = fps;\r
3096                 }\r
3097                 \r
3098                 /*}\r
3099                 else\r
3100                         device->yield();*/\r
3101         }\r
3102 \r
3103         //delete quick_inventory;\r
3104 \r
3105         /*\r
3106                 Disable texture fetches and other stuff that is queued\r
3107                 to be processed by the main loop.\r
3108 \r
3109                 This has to be done before client goes out of scope.\r
3110         */\r
3111         g_irrlicht->Shutdown(true);\r
3112 \r
3113         } // client and server are deleted at this point\r
3114 \r
3115         } //try\r
3116         catch(con::PeerNotFoundException &e)\r
3117         {\r
3118                 dstream<<DTIME<<"Connection timed out."<<std::endl;\r
3119                 error_message = L"Connection timed out.";\r
3120         }\r
3121 \r
3122         } // Menu-game loop\r
3123         \r
3124         delete g_input;\r
3125 \r
3126         /*\r
3127                 In the end, delete the Irrlicht device.\r
3128         */\r
3129         device->drop();\r
3130         \r
3131         /*\r
3132                 Update configuration file\r
3133         */\r
3134         /*if(configpath != "")\r
3135         {\r
3136                 g_settings.updateConfigFile(configpath.c_str());\r
3137         }*/\r
3138 \r
3139         END_DEBUG_EXCEPTION_HANDLER\r
3140         \r
3141         debugstreams_deinit();\r
3142         \r
3143         return 0;\r
3144 }\r
3145 \r
3146 //END\r