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