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