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