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