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