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