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