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