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