3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
\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
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
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
21 =============================== NOTES ==============================
\r
22 NOTE: Things starting with TODO are sometimes only suggestions.
\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
28 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
29 NOTE: Global locale is now set at initialization
\r
31 Random suggeestions (AKA very old suggestions that haven't been done):
\r
32 ----------------------------------------------------------------------
\r
34 SUGG: Fix address to be ipv6 compatible
\r
36 NOTE: When a new sector is generated, it may change the ground level
\r
37 of it's and it's neighbors border that two blocks that are
\r
38 above and below each other and that are generated before and
\r
39 after the sector heightmap generation (order doesn't matter),
\r
40 can have a small gap between each other at the border.
\r
41 SUGG: Use same technique for sector heightmaps as what we're
\r
42 using for UnlimitedHeightmap? (getting all neighbors
\r
45 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
47 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
48 - This enables saving many packets and making a faster connection
\r
49 - This also enables server to check if client has received the
\r
50 most recent block sent, for example.
\r
51 SUGG: Add a sane bandwidth throttling system to Connection
\r
53 SUGG: More fine-grained control of client's dumping of blocks from
\r
55 - ...What does this mean in the first place?
\r
57 SUGG: A map editing mode (similar to dedicated server mode)
\r
59 SUGG: Transfer more blocks in a single packet
\r
60 SUGG: A blockdata combiner class, to which blocks are added and at
\r
61 destruction it sends all the stuff in as few packets as possible.
\r
62 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
63 it by sending more stuff in a single packet.
\r
64 - Add a packet queue to RemoteClient, from which packets will be
\r
65 combined with object data packets
\r
66 - This is not exactly trivial: the object data packets are
\r
67 sometimes very big by themselves
\r
68 - This might not give much network performance gain though.
\r
70 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
71 - This will allow saving ages of rats on disk but not sending
\r
73 - Not applicable. MapBlockObjects will be removed in the future.
\r
75 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
76 - This is not doable because it is currently hand-made and not
\r
77 based on some mathematical function.
\r
78 - Note: This has been changing lately
\r
80 SUGG: A version number to blocks, which increments when the block is
\r
81 modified (node add/remove, water update, lighting update)
\r
82 - This can then be used to make sure the most recent version of
\r
83 a block has been sent to client, for example
\r
85 SUGG: Make the amount of blocks sending to client and the total
\r
86 amount of blocks dynamically limited. Transferring blocks is the
\r
87 main network eater of this system, so it is the one that has
\r
88 to be throttled so that RTTs stay low.
\r
90 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
91 different directions and then only those drawn that need to be
\r
93 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
96 SUGG: Background music based on cellular automata?
\r
97 http://www.earslap.com/projectslab/otomata
\r
103 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
105 - The player could go faster by a crafting a boat, or riding an animal
\r
107 - Random NPC traders. what else?
\r
111 - When furnace is destroyed, move items to player's inventory
\r
112 - Add lots of stuff
\r
114 - Growing grass, decaying leaves
\r
115 - This can be done in the active blocks I guess.
\r
116 - Lots of stuff can be done in the active blocks.
\r
117 - Uh, is there an active block list somewhere? I think not. Add it.
\r
118 - Breaking weak structures
\r
119 - This can probably be accomplished in the same way as grass
\r
120 - Player health points
\r
121 - When player dies, throw items on map (needs better item-on-map
\r
123 - Cobble to get mossy if near water
\r
124 - More slots in furnace source list, so that multiple ingredients
\r
128 - The Treasure Guard; a big monster with a hammer
\r
129 - The hammer does great damage, shakes the ground and removes a block
\r
130 - You can drop on top of it, and have some time to attack there
\r
131 before he shakes you off
\r
133 - Maybe the difficulty could come from monsters getting tougher in
\r
134 far-away places, and the player starting to need something from
\r
135 there when time goes by.
\r
136 - The player would have some of that stuff at the beginning, and
\r
137 would need new supplies of it when it runs out
\r
140 - A spread-items-on-map routine for the bomb, and for dying players
\r
145 Build system / running:
\r
146 -----------------------
\r
148 Networking and serialization:
\r
149 -----------------------------
\r
151 TODO: Get rid of GotSplitPacketException
\r
156 TODO: Configuration menu, at least for keys
\r
161 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
163 - That is >500 vertices
\r
164 - This is not easy; all the MapBlocks close to the player would
\r
165 still need to be drawn separately and combining the blocks
\r
166 would have to happen in a background thread
\r
168 SUGG: Make fetching sector's blocks more efficient when rendering
\r
169 sectors that have very large amounts of blocks (on client)
\r
170 - Is this necessary at all?
\r
172 TODO: Flowing water animation
\r
174 SUGG: Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
176 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
177 animating them is easier.
\r
179 SUGG: Option for enabling proper alpha channel for textures
\r
181 TODO: Make all water not backside culled
\r
189 TODO: Untie client network operations from framerate
\r
190 - Needs some input queues or something
\r
191 - This won't give much performance boost because calculating block
\r
192 meshes takes so long
\r
194 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
196 TODO: Don't update all meshes always on single node changes, but
\r
197 check which ones should be updated
\r
198 - implement Map::updateNodeMeshes() and the usage of it
\r
199 - It will give almost always a 4x boost in mesh update performance.
\r
203 - Tool/weapon visualization
\r
208 SUGG: Make an option to the server to disable building and digging near
\r
209 the starting position
\r
211 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
213 * Fix the problem with the server constantly saving one or a few
\r
214 blocks? List the first saved block, maybe it explains.
\r
215 - It is probably caused by oscillating water
\r
216 * Make a small history check to transformLiquids to detect and log
\r
217 continuous oscillations, in such detail that they can be fixed.
\r
222 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
224 SUGG: MovingObject::move and Player::move are basically the same.
\r
226 - NOTE: Player::move is more up-to-date.
\r
231 TODO: Mineral and ground material properties
\r
232 - This way mineral ground toughness can be calculated with just
\r
233 some formula, as well as tool strengths
\r
235 TODO: Flowing water to actually contain flow direction information
\r
237 SUGG: Erosion simulation at map generation time
\r
238 - Simulate water flows, which would carve out dirt fast and
\r
239 then turn stone into gravel and sand and relocate it.
\r
240 - How about relocating minerals, too? Coal and gold in
\r
241 downstream sand and gravel would be kind of cool
\r
242 - This would need a better way of handling minerals, mainly
\r
243 to have mineral content as a separate field. the first
\r
244 parameter field is free for this.
\r
245 - Simulate rock falling from cliffs when water has removed
\r
246 enough solid rock from the bottom
\r
248 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
249 of low-res 3d noise and interpolate linearly.
\r
252 * Possibly add some kind of erosion and other stuff
\r
253 * Better water generation (spread it to underwater caverns but don't
\r
254 fill dungeons that don't touch big water masses)
\r
255 * When generating a chunk and the neighboring chunk doesn't have mud
\r
256 and stuff yet and the ground is fairly flat, the mud will flow to
\r
257 the other chunk making nasty straight walls when the other chunk
\r
258 is generated. Fix it. Maybe just a special case if the ground is
\r
263 * Make an "environment metafile" to store at least time of day
\r
264 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
265 - Or maybe move content_features to material.{h,cpp}?
\r
267 Make a system for pregenerating quick information for mapblocks, so
\r
268 that the client can show them as cubes before they are actually sent
\r
271 Making it more portable:
\r
272 ------------------------
\r
273 * Some MSVC: std::sto* are defined without a namespace and collide
\r
274 with the ones in utility.h
\r
276 ======================================================================
\r
281 Setting this to 1 enables a special camera mode that forces
\r
282 the renderers to think that the camera statically points from
\r
283 the starting place to a static direction.
\r
285 This allows one to move around with the player and see what
\r
286 is actually drawn behind solid things and behind the player.
\r
288 #define FIELD_OF_VIEW_TEST 0
\r
292 #pragma message ("Disabling unit tests")
\r
294 #warning "Disabling unit tests"
\r
296 // Disable unit tests
\r
297 #define ENABLE_TESTS 0
\r
299 // Enable unit tests
\r
300 #define ENABLE_TESTS 1
\r
304 #pragma comment(lib, "Irrlicht.lib")
\r
305 //#pragma comment(lib, "jthread.lib")
\r
306 #pragma comment(lib, "zlibwapi.lib")
\r
307 #pragma comment(lib, "Shell32.lib")
\r
308 // This would get rid of the console window
\r
309 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
312 #include <iostream>
\r
314 #include <jmutexautolock.h>
\r
315 #include <locale.h>
\r
317 #include "common_irrlicht.h"
\r
320 #include "player.h"
\r
322 //#include "environment.h"
\r
323 #include "server.h"
\r
324 #include "client.h"
\r
325 //#include "serialization.h"
\r
326 #include "constants.h"
\r
327 //#include "strfnd.h"
\r
328 #include "porting.h"
\r
329 #include "gettime.h"
\r
330 #include "porting.h"
\r
331 #include "guiPauseMenu.h"
\r
332 #include "guiInventoryMenu.h"
\r
333 #include "guiTextInputMenu.h"
\r
334 #include "materials.h"
\r
335 #include "guiMessageMenu.h"
\r
336 #include "filesys.h"
\r
337 #include "config.h"
\r
338 #include "guiMainMenu.h"
\r
339 #include "mineral.h"
\r
342 #include "guiFurnaceMenu.h"
\r
344 // This makes textures
\r
345 ITextureSource *g_texturesource = NULL;
\r
347 MapDrawControl draw_control;
\r
351 These are loaded from the config file.
\r
354 Settings g_settings;
\r
356 extern void set_default_settings();
\r
362 IrrlichtDevice *g_device = NULL;
\r
363 Client *g_client = NULL;
\r
365 /*const s16 quickinv_size = 40;
\r
366 const s16 quickinv_padding = 8;
\r
367 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
368 const s16 quickinv_outer_padding = 4;
\r
369 const s16 quickinv_itemcount = 8;*/
\r
371 const s32 hotbar_itemcount = 8;
\r
372 const s32 hotbar_imagesize = 36;
\r
378 gui::IGUIEnvironment* guienv = NULL;
\r
379 gui::IGUIStaticText *guiroot = NULL;
\r
381 class MainMenuManager : public IMenuManager
\r
384 virtual void createdMenu(GUIModalMenu *menu)
\r
386 for(core::list<GUIModalMenu*>::Iterator
\r
387 i = m_stack.begin();
\r
388 i != m_stack.end(); i++)
\r
390 assert(*i != menu);
\r
393 if(m_stack.size() != 0)
\r
394 (*m_stack.getLast())->setVisible(false);
\r
395 m_stack.push_back(menu);
\r
398 virtual void deletingMenu(GUIModalMenu *menu)
\r
400 // Remove all entries if there are duplicates
\r
401 bool removed_entry;
\r
403 removed_entry = false;
\r
404 for(core::list<GUIModalMenu*>::Iterator
\r
405 i = m_stack.begin();
\r
406 i != m_stack.end(); i++)
\r
411 removed_entry = true;
\r
415 }while(removed_entry);
\r
417 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
418 assert(*i == menu);
\r
419 m_stack.erase(i);*/
\r
421 if(m_stack.size() != 0)
\r
422 (*m_stack.getLast())->setVisible(true);
\r
427 return m_stack.size();
\r
430 core::list<GUIModalMenu*> m_stack;
\r
433 MainMenuManager g_menumgr;
\r
435 bool noMenuActive()
\r
437 return (g_menumgr.menuCount() == 0);
\r
440 bool g_disconnect_requested = false;
\r
442 class MainGameCallback : public IGameCallback
\r
445 virtual void exitToOS()
\r
447 g_device->closeDevice();
\r
450 virtual void disconnect()
\r
452 g_disconnect_requested = true;
\r
456 MainGameCallback g_gamecallback;
\r
458 // Inventory actions from the menu are buffered here before sending
\r
459 // TODO: Get rid of this
\r
460 Queue<InventoryAction*> inventory_action_queue;
\r
461 // This is a copy of the inventory that the client's environment has
\r
462 Inventory local_inventory;
\r
464 u16 g_selected_item = 0;
\r
466 /*bool g_show_map_plot = false;
\r
467 bool g_refresh_map_plot = false;*/
\r
474 std::ostream *dout_con_ptr = &dummyout;
\r
475 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
476 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
477 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
478 //std::ostream *dout_con_ptr = &dstream;
\r
479 //std::ostream *derr_con_ptr = &dstream;
\r
482 std::ostream *dout_server_ptr = &dstream;
\r
483 std::ostream *derr_server_ptr = &dstream;
\r
486 std::ostream *dout_client_ptr = &dstream;
\r
487 std::ostream *derr_client_ptr = &dstream;
\r
490 gettime.h implementation
\r
496 Use irrlicht because it is more precise than porting.h's
\r
499 if(g_device == NULL)
\r
501 return g_device->getTimer()->getRealTime();
\r
508 struct TextDestSign : public TextDest
\r
510 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
512 m_blockpos = blockpos;
\r
516 void gotText(std::wstring text)
\r
518 std::string ntext = wide_to_narrow(text);
\r
519 dstream<<"Changing text of a sign object: "
\r
520 <<ntext<<std::endl;
\r
521 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
529 struct TextDestChat : public TextDest
\r
531 TextDestChat(Client *client)
\r
535 void gotText(std::wstring text)
\r
537 // Discard empty line
\r
541 // Parse command (server command starts with "/#")
\r
542 if(text[0] == L'/' && text[1] != L'#')
\r
544 std::wstring reply = L"Local: ";
\r
546 reply += L"Local commands not yet supported. "
\r
547 L"Server prefix is \"/#\".";
\r
549 m_client->addChatMessage(reply);
\r
554 m_client->sendChatMessage(text);
\r
556 m_client->addChatMessage(text);
\r
562 struct TextDestSignNode : public TextDest
\r
564 TextDestSignNode(v3s16 p, Client *client)
\r
569 void gotText(std::wstring text)
\r
571 std::string ntext = wide_to_narrow(text);
\r
572 dstream<<"Changing text of a sign node: "
\r
573 <<ntext<<std::endl;
\r
574 m_client->sendSignNodeText(m_p, ntext);
\r
581 class MyEventReceiver : public IEventReceiver
\r
584 // This is the one method that we have to implement
\r
585 virtual bool OnEvent(const SEvent& event)
\r
588 React to nothing here if a menu is active
\r
590 if(noMenuActive() == false)
\r
596 // Remember whether each key is down or up
\r
597 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
599 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
601 if(event.KeyInput.PressedDown)
\r
603 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
604 /*if(g_show_map_plot)
\r
606 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
607 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
609 g_show_map_plot = false;
\r
618 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
620 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
622 dstream<<DTIME<<"MyEventReceiver: "
\r
623 <<"Launching pause menu"<<std::endl;
\r
624 // It will delete itself by itself
\r
625 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
626 &g_menumgr))->drop();
\r
629 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
631 dstream<<DTIME<<"MyEventReceiver: "
\r
632 <<"Launching inventory"<<std::endl;
\r
634 GUIInventoryMenu *menu =
\r
635 new GUIInventoryMenu(guienv, guiroot, -1,
\r
636 &g_menumgr, v2s16(8,7),
\r
637 g_client->getInventoryContext(),
\r
640 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
641 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
642 "list", "current_player", "main",
\r
643 v2s32(0, 3), v2s32(8, 4)));
\r
644 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
645 "list", "current_player", "craft",
\r
646 v2s32(3, 0), v2s32(3, 3)));
\r
647 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
648 "list", "current_player", "craftresult",
\r
649 v2s32(7, 1), v2s32(1, 1)));
\r
651 menu->setDrawSpec(draw_spec);
\r
657 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
659 TextDest *dest = new TextDestChat(g_client);
\r
661 (new GUITextInputMenu(guienv, guiroot, -1,
\r
668 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
669 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
671 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
672 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
674 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
675 g_selected_item = s1-1;
\r
676 dstream<<DTIME<<"Selected item: "
\r
677 <<g_selected_item<<std::endl;
\r
680 // Viewing range selection
\r
681 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
683 if(draw_control.range_all)
\r
685 draw_control.range_all = false;
\r
686 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
690 draw_control.range_all = true;
\r
691 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
695 // Print debug stacks
\r
696 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
698 dstream<<"-----------------------------------------"
\r
700 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
701 dstream<<"-----------------------------------------"
\r
703 debug_stacks_print();
\r
707 /*if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
709 dstream<<"Map plot requested"<<std::endl;
\r
710 g_show_map_plot = !g_show_map_plot;
\r
711 if(g_show_map_plot)
\r
712 g_refresh_map_plot = true;
\r
718 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
720 if(noMenuActive() == false)
\r
722 left_active = false;
\r
723 middle_active = false;
\r
724 right_active = false;
\r
728 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
729 left_active = event.MouseInput.isLeftPressed();
\r
730 middle_active = event.MouseInput.isMiddlePressed();
\r
731 right_active = event.MouseInput.isRightPressed();
\r
733 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
735 leftclicked = true;
\r
737 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
739 rightclicked = true;
\r
741 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
743 leftreleased = true;
\r
745 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
747 rightreleased = true;
\r
749 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
751 /*dstream<<"event.MouseInput.Wheel="
\r
752 <<event.MouseInput.Wheel<<std::endl;*/
\r
754 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
755 hotbar_itemcount-1);
\r
756 if(event.MouseInput.Wheel < 0)
\r
758 if(g_selected_item < max_item)
\r
761 g_selected_item = 0;
\r
763 else if(event.MouseInput.Wheel > 0)
\r
765 if(g_selected_item > 0)
\r
768 g_selected_item = max_item;
\r
777 // This is used to check whether a key is being held down
\r
778 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
780 return keyIsDown[keyCode];
\r
785 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
786 keyIsDown[i] = false;
\r
788 leftclicked = false;
\r
789 rightclicked = false;
\r
790 leftreleased = false;
\r
791 rightreleased = false;
\r
793 left_active = false;
\r
794 middle_active = false;
\r
795 right_active = false;
\r
806 bool rightreleased;
\r
809 bool middle_active;
\r
813 // We use this array to store the current state of each key
\r
814 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
817 IrrlichtDevice *m_device;
\r
826 virtual ~InputHandler()
\r
830 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
832 virtual v2s32 getMousePos() = 0;
\r
833 virtual void setMousePos(s32 x, s32 y) = 0;
\r
835 virtual bool getLeftState() = 0;
\r
836 virtual bool getRightState() = 0;
\r
838 virtual bool getLeftClicked() = 0;
\r
839 virtual bool getRightClicked() = 0;
\r
840 virtual void resetLeftClicked() = 0;
\r
841 virtual void resetRightClicked() = 0;
\r
843 virtual bool getLeftReleased() = 0;
\r
844 virtual bool getRightReleased() = 0;
\r
845 virtual void resetLeftReleased() = 0;
\r
846 virtual void resetRightReleased() = 0;
\r
848 virtual void step(float dtime) {};
\r
850 virtual void clear() {};
\r
853 InputHandler *g_input = NULL;
\r
855 class RealInputHandler : public InputHandler
\r
858 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
860 m_receiver(receiver)
\r
863 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
865 return m_receiver->IsKeyDown(keyCode);
\r
867 virtual v2s32 getMousePos()
\r
869 return m_device->getCursorControl()->getPosition();
\r
871 virtual void setMousePos(s32 x, s32 y)
\r
873 m_device->getCursorControl()->setPosition(x, y);
\r
876 virtual bool getLeftState()
\r
878 return m_receiver->left_active;
\r
880 virtual bool getRightState()
\r
882 return m_receiver->right_active;
\r
885 virtual bool getLeftClicked()
\r
887 return m_receiver->leftclicked;
\r
889 virtual bool getRightClicked()
\r
891 return m_receiver->rightclicked;
\r
893 virtual void resetLeftClicked()
\r
895 m_receiver->leftclicked = false;
\r
897 virtual void resetRightClicked()
\r
899 m_receiver->rightclicked = false;
\r
902 virtual bool getLeftReleased()
\r
904 return m_receiver->leftreleased;
\r
906 virtual bool getRightReleased()
\r
908 return m_receiver->rightreleased;
\r
910 virtual void resetLeftReleased()
\r
912 m_receiver->leftreleased = false;
\r
914 virtual void resetRightReleased()
\r
916 m_receiver->rightreleased = false;
\r
921 resetRightClicked();
\r
922 resetLeftClicked();
\r
925 IrrlichtDevice *m_device;
\r
926 MyEventReceiver *m_receiver;
\r
929 class RandomInputHandler : public InputHandler
\r
932 RandomInputHandler()
\r
936 leftclicked = false;
\r
937 rightclicked = false;
\r
938 leftreleased = false;
\r
939 rightreleased = false;
\r
940 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
941 keydown[i] = false;
\r
943 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
945 return keydown[keyCode];
\r
947 virtual v2s32 getMousePos()
\r
951 virtual void setMousePos(s32 x, s32 y)
\r
953 mousepos = v2s32(x,y);
\r
956 virtual bool getLeftState()
\r
960 virtual bool getRightState()
\r
965 virtual bool getLeftClicked()
\r
967 return leftclicked;
\r
969 virtual bool getRightClicked()
\r
971 return rightclicked;
\r
973 virtual void resetLeftClicked()
\r
975 leftclicked = false;
\r
977 virtual void resetRightClicked()
\r
979 rightclicked = false;
\r
982 virtual bool getLeftReleased()
\r
984 return leftreleased;
\r
986 virtual bool getRightReleased()
\r
988 return rightreleased;
\r
990 virtual void resetLeftReleased()
\r
992 leftreleased = false;
\r
994 virtual void resetRightReleased()
\r
996 rightreleased = false;
\r
999 virtual void step(float dtime)
\r
1002 static float counter1 = 0;
\r
1003 counter1 -= dtime;
\r
1004 if(counter1 < 0.0)
\r
1006 counter1 = 0.1*Rand(1, 40);
\r
1007 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
1011 static float counter1 = 0;
\r
1012 counter1 -= dtime;
\r
1013 if(counter1 < 0.0)
\r
1015 counter1 = 0.1*Rand(1, 40);
\r
1016 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1020 static float counter1 = 0;
\r
1021 counter1 -= dtime;
\r
1022 if(counter1 < 0.0)
\r
1024 counter1 = 0.1*Rand(1, 40);
\r
1025 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1029 static float counter1 = 0;
\r
1030 counter1 -= dtime;
\r
1031 if(counter1 < 0.0)
\r
1033 counter1 = 0.1*Rand(1, 40);
\r
1034 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1038 static float counter1 = 0;
\r
1039 counter1 -= dtime;
\r
1040 if(counter1 < 0.0)
\r
1042 counter1 = 0.1*Rand(1, 20);
\r
1043 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1047 static float counter1 = 0;
\r
1048 counter1 -= dtime;
\r
1049 if(counter1 < 0.0)
\r
1051 counter1 = 0.1*Rand(1, 30);
\r
1052 leftdown = !leftdown;
\r
1054 leftclicked = true;
\r
1056 leftreleased = true;
\r
1060 static float counter1 = 0;
\r
1061 counter1 -= dtime;
\r
1062 if(counter1 < 0.0)
\r
1064 counter1 = 0.1*Rand(1, 15);
\r
1065 rightdown = !rightdown;
\r
1067 rightclicked = true;
\r
1069 rightreleased = true;
\r
1072 mousepos += mousespeed;
\r
1075 s32 Rand(s32 min, s32 max)
\r
1077 return (myrand()%(max-min+1))+min;
\r
1080 bool keydown[KEY_KEY_CODES_COUNT];
\r
1086 bool rightclicked;
\r
1087 bool leftreleased;
\r
1088 bool rightreleased;
\r
1091 void updateViewingRange(f32 frametime_in, Client *client)
\r
1093 if(draw_control.range_all == true)
\r
1096 static f32 added_frametime = 0;
\r
1097 static s16 added_frames = 0;
\r
1099 added_frametime += frametime_in;
\r
1100 added_frames += 1;
\r
1102 // Actually this counter kind of sucks because frametime is busytime
\r
1103 static f32 counter = 0;
\r
1104 counter -= frametime_in;
\r
1110 /*dstream<<__FUNCTION_NAME
\r
1111 <<": Collected "<<added_frames<<" frames, total of "
\r
1112 <<added_frametime<<"s."<<std::endl;*/
\r
1114 /*dstream<<"draw_control.blocks_drawn="
\r
1115 <<draw_control.blocks_drawn
\r
1116 <<", draw_control.blocks_would_have_drawn="
\r
1117 <<draw_control.blocks_would_have_drawn
\r
1120 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1121 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1123 draw_control.wanted_min_range = range_min;
\r
1124 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1126 float block_draw_ratio = 1.0;
\r
1127 if(draw_control.blocks_would_have_drawn != 0)
\r
1129 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1130 / (float)draw_control.blocks_would_have_drawn;
\r
1133 // Calculate the average frametime in the case that all wanted
\r
1134 // blocks had been drawn
\r
1135 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1137 added_frametime = 0.0;
\r
1140 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1141 float wanted_frametime = 1.0 / wanted_fps;
\r
1143 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1144 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1146 // If needed frametime change is small, just return
\r
1147 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1149 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1153 float range = draw_control.wanted_range;
\r
1154 float new_range = range;
\r
1156 static s16 range_old = 0;
\r
1157 static f32 frametime_old = 0;
\r
1159 float d_range = range - range_old;
\r
1160 f32 d_frametime = frametime - frametime_old;
\r
1161 // A sane default of 30ms per 50 nodes of range
\r
1162 static f32 time_per_range = 30. / 50;
\r
1165 time_per_range = d_frametime / d_range;
\r
1168 // The minimum allowed calculated frametime-range derivative:
\r
1169 // Practically this sets the maximum speed of changing the range.
\r
1170 // The lower this value, the higher the maximum changing speed.
\r
1171 // A low value here results in wobbly range (0.001)
\r
1172 // A high value here results in slow changing range (0.0025)
\r
1173 // SUGG: This could be dynamically adjusted so that when
\r
1174 // the camera is turning, this is lower
\r
1175 //float min_time_per_range = 0.0015;
\r
1176 float min_time_per_range = 0.0010;
\r
1177 //float min_time_per_range = 0.05 / range;
\r
1178 if(time_per_range < min_time_per_range)
\r
1180 time_per_range = min_time_per_range;
\r
1181 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1185 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1188 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1189 // Dampen the change a bit to kill oscillations
\r
1190 //wanted_range_change *= 0.9;
\r
1191 //wanted_range_change *= 0.75;
\r
1192 wanted_range_change *= 0.5;
\r
1193 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1195 // If needed range change is very small, just return
\r
1196 if(fabs(wanted_range_change) < 0.001)
\r
1198 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1202 new_range += wanted_range_change;
\r
1203 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1205 //float new_range_unclamped = new_range;
\r
1206 if(new_range < range_min)
\r
1207 new_range = range_min;
\r
1208 if(new_range > range_max)
\r
1209 new_range = range_max;
\r
1211 /*if(new_range != new_range_unclamped)
\r
1212 dstream<<", clamped to "<<new_range<<std::endl;
\r
1214 dstream<<std::endl;*/
\r
1216 draw_control.wanted_range = new_range;
\r
1218 range_old = new_range;
\r
1219 frametime_old = frametime;
\r
1222 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1223 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1224 Inventory *inventory, s32 halfheartcount)
\r
1226 InventoryList *mainlist = inventory->getList("main");
\r
1227 if(mainlist == NULL)
\r
1229 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1233 s32 padding = imgsize/12;
\r
1234 //s32 height = imgsize + padding*2;
\r
1235 s32 width = itemcount*(imgsize+padding*2);
\r
1237 // Position of upper left corner of bar
\r
1238 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1240 // Draw background color
\r
1241 /*core::rect<s32> barrect(0,0,width,height);
\r
1243 video::SColor bgcolor(255,128,128,128);
\r
1244 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1246 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1248 for(s32 i=0; i<itemcount; i++)
\r
1250 InventoryItem *item = mainlist->getItem(i);
\r
1252 core::rect<s32> rect = imgrect + pos
\r
1253 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1255 if(g_selected_item == i)
\r
1257 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1258 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1259 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1264 video::SColor bgcolor2(128,0,0,0);
\r
1265 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1270 drawInventoryItem(driver, font, item, rect, NULL);
\r
1278 video::ITexture *heart_texture =
\r
1279 driver->getTexture(porting::getDataPath("heart.png").c_str());
\r
1280 v2s32 p = pos + v2s32(0, -20);
\r
1281 for(s32 i=0; i<halfheartcount/2; i++)
\r
1283 const video::SColor color(255,255,255,255);
\r
1284 const video::SColor colors[] = {color,color,color,color};
\r
1285 core::rect<s32> rect(0,0,16,16);
\r
1287 driver->draw2DImage(heart_texture, rect,
\r
1288 core::rect<s32>(core::position2d<s32>(0,0),
\r
1289 core::dimension2di(heart_texture->getOriginalSize())),
\r
1290 NULL, colors, true);
\r
1293 if(halfheartcount % 2 == 1)
\r
1295 const video::SColor color(255,255,255,255);
\r
1296 const video::SColor colors[] = {color,color,color,color};
\r
1297 core::rect<s32> rect(0,0,16/2,16);
\r
1299 core::dimension2di srcd(heart_texture->getOriginalSize());
\r
1301 driver->draw2DImage(heart_texture, rect,
\r
1302 core::rect<s32>(core::position2d<s32>(0,0), srcd),
\r
1303 NULL, colors, true);
\r
1310 video::ITexture *g_map_plot_texture = NULL;
\r
1311 float g_map_plot_texture_scale = 4;
\r
1313 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1319 core::dimension2d<u32> dim(640,480);
\r
1320 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1322 for(u32 y=0; y<dim.Height; y++)
\r
1323 for(u32 x=0; x<dim.Width; x++)
\r
1325 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1326 pf *= g_map_plot_texture_scale;
\r
1328 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1331 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1332 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1334 // Screen coordinates that are based on multiples of
\r
1335 // 1000/g_map_plot_texture_scale and never negative
\r
1336 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1337 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1338 // Offset to center of image
\r
1339 ux -= dim.Width/2;
\r
1340 uy -= dim.Height/2;
\r
1342 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1343 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1344 c.set(255, 255, 255, 255);
\r
1345 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1346 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1347 c.set(255, 160, 160, 160);
\r
1348 else if(h < WATER_LEVEL - 0.5) // Water
\r
1349 c.set(255, 50, 50, 255);
\r
1351 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1352 || (h < WATER_LEVEL + 2
\r
1353 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1357 h = 1.0 - exp(-h);
\r
1359 video::SColor c1(255,237,201,175);
\r
1360 //video::SColor c2(255,20,20,20);
\r
1361 video::SColor c2(255,150,0,0);
\r
1362 c = c2.getInterpolated(c1, h);
\r
1368 h = 1.0 - exp(-h);
\r
1370 video::SColor c1(255,110,185,90);
\r
1371 //video::SColor c2(255,20,20,20);
\r
1372 video::SColor c2(255,150,0,0);
\r
1373 c = c2.getInterpolated(c1, h);
\r
1378 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1382 h = 1.0 - exp(-h);
\r
1384 video::SColor c1(255,237,201,175);
\r
1385 //video::SColor c2(255,20,20,20);
\r
1386 video::SColor c2(255,150,0,0);
\r
1387 c = c2.getInterpolated(c1, h);
\r
1391 else if(h < WATER_LEVEL + 2
\r
1392 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1393 c.set(255, 237, 201, 175);
\r
1395 else if(h < WATER_LEVEL + 10)
\r
1396 c.set(255, 50, 150, 50); // Green
\r
1397 else if(h < WATER_LEVEL + 20)
\r
1398 c.set(255, 110, 185, 50); // Yellowish green
\r
1399 else if(h < WATER_LEVEL + 40)
\r
1400 c.set(255, 180, 210, 50); // Greenish yellow
\r
1401 else if(h < WATER_LEVEL + 60)
\r
1402 c.set(255, 220, 220, 50); // Yellow
\r
1403 else if(h < WATER_LEVEL + 80)
\r
1404 c.set(255, 200, 200, 110); // Yellowish white
\r
1405 else if(h < WATER_LEVEL + 100)
\r
1406 c.set(255, 190, 190, 190); // Grey
\r
1408 c.set(255, 255, 255, 255); // White
\r
1416 h = 1.0 - exp(-h);
\r
1418 video::SColor c1(255,200,200,50);
\r
1419 video::SColor c2(255,0,150,0);
\r
1420 c = c1.getInterpolated(c2, h);
\r
1422 /*u32 a = (u32)(h*255);
\r
1426 c.set(255, a, a, a);*/
\r
1430 if(h >= WATER_LEVEL - 0.5
\r
1431 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1433 video::SColor c1(255,237,201,175);
\r
1434 c = c.getInterpolated(c1, 0.5);
\r
1438 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1441 video::SColor c1(255,255,0,0);
\r
1442 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1445 img->setPixel(x, y, c);
\r
1447 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1449 assert(g_map_plot_texture);
\r
1460 ChatLine(const std::wstring &a_text):
\r
1466 std::wstring text;
\r
1469 // These are defined global so that they're not optimized too much.
\r
1470 // Can't change them to volatile.
\r
1475 std::string tempstring;
\r
1476 std::string tempstring2;
\r
1481 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1482 TimeTaker timer("Testing std::string speed");
\r
1483 const u32 jj = 10000;
\r
1484 for(u32 j=0; j<jj; j++)
\r
1488 const u32 ii = 10;
\r
1489 for(u32 i=0; i<ii; i++){
\r
1490 tempstring2 += "asd";
\r
1492 for(u32 i=0; i<ii+1; i++){
\r
1493 tempstring += "asd";
\r
1494 if(tempstring == tempstring2)
\r
1500 dstream<<"All of the following tests should take around 100ms each."
\r
1504 TimeTaker timer("Testing floating-point conversion speed");
\r
1506 for(u32 i=0; i<4000000; i++){
\r
1513 TimeTaker timer("Testing floating-point vector speed");
\r
1515 tempv3f1 = v3f(1,2,3);
\r
1516 tempv3f2 = v3f(4,5,6);
\r
1517 for(u32 i=0; i<10000000; i++){
\r
1518 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1519 tempv3f2 += v3f(7,8,9);
\r
1524 TimeTaker timer("Testing core::map speed");
\r
1526 core::map<v2s16, f32> map1;
\r
1529 for(s16 y=0; y<ii; y++){
\r
1530 for(s16 x=0; x<ii; x++){
\r
1531 map1.insert(v2s16(x,y), tempf);
\r
1535 for(s16 y=ii-1; y>=0; y--){
\r
1536 for(s16 x=0; x<ii; x++){
\r
1537 tempf = map1[v2s16(x,y)];
\r
1543 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1544 TimeTaker timer("Testing mutex speed");
\r
1557 // Do at least 10ms
\r
1558 while(timer.getTime() < 10);
\r
1560 u32 dtime = timer.stop();
\r
1561 u32 per_ms = n / dtime;
\r
1562 std::cout<<"Done. "<<dtime<<"ms, "
\r
1563 <<per_ms<<"/ms"<<std::endl;
\r
1567 void getPointedNode(v3f player_position,
\r
1568 v3f camera_direction, v3f camera_position,
\r
1569 bool &nodefound, core::line3d<f32> shootline,
\r
1570 v3s16 &nodepos, v3s16 &neighbourpos,
\r
1571 core::aabbox3d<f32> &nodehilightbox,
\r
1576 f32 mindistance = BS * 1001;
\r
1578 v3s16 pos_i = floatToInt(player_position, BS);
\r
1580 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1584 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1585 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1586 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1587 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1588 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1589 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1591 for(s16 y = ystart; y <= yend; y++)
\r
1592 for(s16 z = zstart; z <= zend; z++)
\r
1593 for(s16 x = xstart; x <= xend; x++)
\r
1598 n = g_client->getNode(v3s16(x,y,z));
\r
1599 if(content_pointable(n.d) == false)
\r
1602 catch(InvalidPositionException &e)
\r
1608 v3f npf = intToFloat(np, BS);
\r
1613 v3s16(0,0,1), // back
\r
1614 v3s16(0,1,0), // top
\r
1615 v3s16(1,0,0), // right
\r
1616 v3s16(0,0,-1), // front
\r
1617 v3s16(0,-1,0), // bottom
\r
1618 v3s16(-1,0,0), // left
\r
1624 if(n.d == CONTENT_TORCH)
\r
1626 v3s16 dir = unpackDir(n.dir);
\r
1627 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1628 dir_f *= BS/2 - BS/6 - BS/20;
\r
1629 v3f cpf = npf + dir_f;
\r
1630 f32 distance = (cpf - camera_position).getLength();
\r
1632 core::aabbox3d<f32> box;
\r
1635 if(dir == v3s16(0,-1,0))
\r
1637 box = core::aabbox3d<f32>(
\r
1638 npf - v3f(BS/6, BS/2, BS/6),
\r
1639 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
1643 else if(dir == v3s16(0,1,0))
\r
1645 box = core::aabbox3d<f32>(
\r
1646 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
1647 npf + v3f(BS/6, BS/2, BS/6)
\r
1653 box = core::aabbox3d<f32>(
\r
1654 cpf - v3f(BS/6, BS/3, BS/6),
\r
1655 cpf + v3f(BS/6, BS/3, BS/6)
\r
1659 if(distance < mindistance)
\r
1661 if(box.intersectsWithLine(shootline))
\r
1665 neighbourpos = np;
\r
1666 mindistance = distance;
\r
1667 nodehilightbox = box;
\r
1671 else if(n.d == CONTENT_SIGN_WALL)
\r
1673 v3s16 dir = unpackDir(n.dir);
\r
1674 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1675 dir_f *= BS/2 - BS/6 - BS/20;
\r
1676 v3f cpf = npf + dir_f;
\r
1677 f32 distance = (cpf - camera_position).getLength();
\r
1681 v3f(BS*0.42,-BS*0.35,-BS*0.4),
\r
1682 v3f(BS*0.49, BS*0.35, BS*0.4),
\r
1685 for(s32 i=0; i<2; i++)
\r
1687 if(dir == v3s16(1,0,0))
\r
1688 vertices[i].rotateXZBy(0);
\r
1689 if(dir == v3s16(-1,0,0))
\r
1690 vertices[i].rotateXZBy(180);
\r
1691 if(dir == v3s16(0,0,1))
\r
1692 vertices[i].rotateXZBy(90);
\r
1693 if(dir == v3s16(0,0,-1))
\r
1694 vertices[i].rotateXZBy(-90);
\r
1695 if(dir == v3s16(0,-1,0))
\r
1696 vertices[i].rotateXYBy(-90);
\r
1697 if(dir == v3s16(0,1,0))
\r
1698 vertices[i].rotateXYBy(90);
\r
1700 vertices[i] += npf;
\r
1703 core::aabbox3d<f32> box;
\r
1705 box = core::aabbox3d<f32>(vertices[0]);
\r
1706 box.addInternalPoint(vertices[1]);
\r
1708 if(distance < mindistance)
\r
1710 if(box.intersectsWithLine(shootline))
\r
1714 neighbourpos = np;
\r
1715 mindistance = distance;
\r
1716 nodehilightbox = box;
\r
1725 for(u16 i=0; i<6; i++)
\r
1727 v3f dir_f = v3f(dirs[i].X,
\r
1728 dirs[i].Y, dirs[i].Z);
\r
1729 v3f centerpoint = npf + dir_f * BS/2;
\r
1731 (centerpoint - camera_position).getLength();
\r
1733 if(distance < mindistance)
\r
1735 core::CMatrix4<f32> m;
\r
1736 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1738 // This is the back face
\r
1739 v3f corners[2] = {
\r
1740 v3f(BS/2, BS/2, BS/2),
\r
1741 v3f(-BS/2, -BS/2, BS/2+d)
\r
1744 for(u16 j=0; j<2; j++)
\r
1746 m.rotateVect(corners[j]);
\r
1747 corners[j] += npf;
\r
1750 core::aabbox3d<f32> facebox(corners[0]);
\r
1751 facebox.addInternalPoint(corners[1]);
\r
1753 if(facebox.intersectsWithLine(shootline))
\r
1757 neighbourpos = np + dirs[i];
\r
1758 mindistance = distance;
\r
1760 //nodehilightbox = facebox;
\r
1762 const float d = 0.502;
\r
1763 core::aabbox3d<f32> nodebox
\r
1764 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
1765 v3f nodepos_f = intToFloat(nodepos, BS);
\r
1766 nodebox.MinEdge += nodepos_f;
\r
1767 nodebox.MaxEdge += nodepos_f;
\r
1768 nodehilightbox = nodebox;
\r
1770 } // if distance < mindistance
\r
1772 } // regular block
\r
1776 int main(int argc, char *argv[])
\r
1779 Parse command line
\r
1782 // List all allowed options
\r
1783 core::map<std::string, ValueSpec> allowed_options;
\r
1784 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1785 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1786 "Run server directly"));
\r
1787 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1788 "Load configuration from specified file"));
\r
1789 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1790 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1791 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1792 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1793 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1794 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1796 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1798 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1800 Settings cmd_args;
\r
1802 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1804 if(ret == false || cmd_args.getFlag("help"))
\r
1806 dstream<<"Allowed options:"<<std::endl;
\r
1807 for(core::map<std::string, ValueSpec>::Iterator
\r
1808 i = allowed_options.getIterator();
\r
1809 i.atEnd() == false; i++)
\r
1811 dstream<<" --"<<i.getNode()->getKey();
\r
1812 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1817 dstream<<" <value>";
\r
1819 dstream<<std::endl;
\r
1821 if(i.getNode()->getValue().help != NULL)
\r
1823 dstream<<" "<<i.getNode()->getValue().help
\r
1828 return cmd_args.getFlag("help") ? 0 : 1;
\r
1832 Low-level initialization
\r
1835 bool disable_stderr = false;
\r
1837 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1838 disable_stderr = true;
\r
1841 // Initialize debug streams
\r
1842 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1843 // Initialize debug stacks
\r
1844 debug_stacks_init();
\r
1846 DSTACK(__FUNCTION_NAME);
\r
1848 porting::signal_handler_init();
\r
1849 bool &kill = *porting::signal_handler_killstatus();
\r
1851 porting::initializePaths();
\r
1852 // Create user data directory
\r
1853 fs::CreateDir(porting::path_userdata);
\r
1855 // C-style stuff initialization
\r
1856 initializeMaterialProperties();
\r
1859 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1861 // Print startup message
\r
1862 dstream<<DTIME<<"minetest-c55"
\r
1863 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1864 <<", "<<BUILD_INFO
\r
1868 Basic initialization
\r
1871 // Initialize default settings
\r
1872 set_default_settings();
\r
1874 // Set locale. This is for forcing '.' as the decimal point.
\r
1875 std::locale::global(std::locale("C"));
\r
1876 // This enables printing all characters in bitmap font
\r
1877 setlocale(LC_CTYPE, "en_US");
\r
1879 // Initialize sockets
\r
1881 atexit(sockets_cleanup);
\r
1891 // Path of configuration file in use
\r
1892 std::string configpath = "";
\r
1894 if(cmd_args.exists("config"))
\r
1896 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1899 dstream<<"Could not read configuration from \""
\r
1900 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1903 configpath = cmd_args.get("config");
\r
1907 core::array<std::string> filenames;
\r
1908 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1909 #ifdef RUN_IN_PLACE
\r
1910 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1913 for(u32 i=0; i<filenames.size(); i++)
\r
1915 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1918 configpath = filenames[i];
\r
1923 // If no path found, use the first one (menu creates the file)
\r
1924 if(configpath == "")
\r
1925 configpath = filenames[0];
\r
1928 // Initialize random seed
\r
1933 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1935 These are needed for unit tests at least.
\r
1938 // Initial call with g_texturesource not set.
\r
1945 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1946 || cmd_args.getFlag("enable-unittests") == true)
\r
1951 /*for(s16 y=-100; y<100; y++)
\r
1952 for(s16 x=-100; x<100; x++)
\r
1954 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1964 if(cmd_args.exists("port"))
\r
1965 port = cmd_args.getU16("port");
\r
1966 else if(g_settings.exists("port"))
\r
1967 port = g_settings.getU16("port");
\r
1970 std::string map_dir = porting::path_userdata+"/map";
\r
1971 if(cmd_args.exists("map-dir"))
\r
1972 map_dir = cmd_args.get("map-dir");
\r
1973 else if(g_settings.exists("map-dir"))
\r
1974 map_dir = g_settings.get("map-dir");
\r
1976 // Run dedicated server if asked to
\r
1977 if(cmd_args.getFlag("server"))
\r
1979 DSTACK("Dedicated server branch");
\r
1982 Server server(map_dir.c_str());
\r
1983 server.start(port);
\r
1986 dedicated_server_loop(server, kill);
\r
1995 // Address to connect to
\r
1996 std::string address = "";
\r
1998 if(cmd_args.exists("address"))
\r
2000 address = cmd_args.get("address");
\r
2004 address = g_settings.get("address");
\r
2007 std::string playername = g_settings.get("name");
\r
2009 // Resolution selection
\r
2011 bool fullscreen = false;
\r
2012 u16 screenW = g_settings.getU16("screenW");
\r
2013 u16 screenH = g_settings.getU16("screenH");
\r
2015 // Determine driver
\r
2017 video::E_DRIVER_TYPE driverType;
\r
2019 std::string driverstring = g_settings.get("video_driver");
\r
2021 if(driverstring == "null")
\r
2022 driverType = video::EDT_NULL;
\r
2023 else if(driverstring == "software")
\r
2024 driverType = video::EDT_SOFTWARE;
\r
2025 else if(driverstring == "burningsvideo")
\r
2026 driverType = video::EDT_BURNINGSVIDEO;
\r
2027 else if(driverstring == "direct3d8")
\r
2028 driverType = video::EDT_DIRECT3D8;
\r
2029 else if(driverstring == "direct3d9")
\r
2030 driverType = video::EDT_DIRECT3D9;
\r
2031 else if(driverstring == "opengl")
\r
2032 driverType = video::EDT_OPENGL;
\r
2035 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
2036 "to opengl"<<std::endl;
\r
2037 driverType = video::EDT_OPENGL;
\r
2040 // create device and exit if creation failed
\r
2042 MyEventReceiver receiver;
\r
2044 IrrlichtDevice *device;
\r
2045 device = createDevice(driverType,
\r
2046 core::dimension2d<u32>(screenW, screenH),
\r
2047 16, fullscreen, false, false, &receiver);
\r
2050 return 1; // could not create selected driver.
\r
2052 g_device = device;
\r
2053 TextureSource *texturesource = new TextureSource(device);
\r
2054 g_texturesource = texturesource;
\r
2057 Speed tests (done after irrlicht is loaded to get timer)
\r
2059 if(cmd_args.getFlag("speedtests"))
\r
2061 dstream<<"Running speed tests"<<std::endl;
\r
2066 device->setResizable(true);
\r
2068 bool random_input = g_settings.getBool("random_input")
\r
2069 || cmd_args.getFlag("random-input");
\r
2071 g_input = new RandomInputHandler();
\r
2073 g_input = new RealInputHandler(device, &receiver);
\r
2076 Continue initialization
\r
2079 video::IVideoDriver* driver = device->getVideoDriver();
\r
2082 This changes the minimum allowed number of vertices in a VBO.
\r
2085 //driver->setMinHardwareBufferVertexCount(50);
\r
2087 scene::ISceneManager* smgr = device->getSceneManager();
\r
2089 guienv = device->getGUIEnvironment();
\r
2090 gui::IGUISkin* skin = guienv->getSkin();
\r
2091 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
2093 skin->setFont(font);
\r
2095 dstream<<"WARNING: Font file was not found."
\r
2096 " Using default font."<<std::endl;
\r
2097 // If font was not found, this will get us one
\r
2098 font = skin->getFont();
\r
2101 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
2102 dstream<<"text_height="<<text_height<<std::endl;
\r
2104 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
2105 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
2106 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
2107 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
2108 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
2109 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
2112 Preload some textures and stuff
\r
2115 init_content_inventory_texture_paths();
\r
2116 init_mapnode(); // Second call with g_texturesource set
\r
2124 We need some kind of a root node to be able to add
\r
2125 custom gui elements directly on the screen.
\r
2126 Otherwise they won't be automatically drawn.
\r
2128 guiroot = guienv->addStaticText(L"",
\r
2129 core::rect<s32>(0, 0, 10000, 10000));
\r
2131 // First line of debug text
\r
2132 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
2134 core::rect<s32>(5, 5, 795, 5+text_height),
\r
2136 // Second line of debug text
\r
2137 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
2139 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
2142 // At the middle of the screen
\r
2143 // Object infos are shown in this
\r
2144 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
2146 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
2150 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
2152 core::rect<s32>(0,0,0,0),
\r
2153 false, false); // Disable word wrap as of now
\r
2155 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
2156 core::list<ChatLine> chat_lines;
\r
2159 If an error occurs, this is set to something and the
\r
2160 menu-game loop is restarted. It is then displayed before
\r
2163 std::wstring error_message = L"";
\r
2168 while(g_device->run() && kill == false)
\r
2171 // This is used for catching disconnects
\r
2176 Out-of-game menu loop.
\r
2178 Loop quits when menu returns proper parameters.
\r
2180 while(kill == false)
\r
2182 // Cursor can be non-visible when coming from the game
\r
2183 device->getCursorControl()->setVisible(true);
\r
2184 // Some stuff are left to scene manager when coming from the game
\r
2185 // (map at least?)
\r
2187 // Reset or hide the debug gui texts
\r
2188 guitext->setText(L"Minetest-c55");
\r
2189 guitext2->setVisible(false);
\r
2190 guitext_info->setVisible(false);
\r
2191 guitext_chat->setVisible(false);
\r
2193 // Initialize menu data
\r
2194 MainMenuData menudata;
\r
2195 menudata.address = narrow_to_wide(address);
\r
2196 menudata.name = narrow_to_wide(playername);
\r
2197 menudata.port = narrow_to_wide(itos(port));
\r
2198 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
2200 GUIMainMenu *menu =
\r
2201 new GUIMainMenu(guienv, guiroot, -1,
\r
2202 &g_menumgr, &menudata, &g_gamecallback);
\r
2203 menu->allowFocusRemoval(true);
\r
2205 if(error_message != L"")
\r
2207 GUIMessageMenu *menu2 =
\r
2208 new GUIMessageMenu(guienv, guiroot, -1,
\r
2209 &g_menumgr, error_message.c_str());
\r
2211 error_message = L"";
\r
2214 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2216 dstream<<"Created main menu"<<std::endl;
\r
2218 while(g_device->run() && kill == false)
\r
2220 if(menu->getStatus() == true)
\r
2223 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2224 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
2225 guienv->drawAll();
\r
2226 driver->endScene();
\r
2229 // Break out of menu-game loop to shut down cleanly
\r
2230 if(g_device->run() == false || kill == true)
\r
2233 dstream<<"Dropping main menu"<<std::endl;
\r
2237 // Delete map if requested
\r
2238 if(menudata.delete_map)
\r
2240 bool r = fs::RecursiveDeleteContent(map_dir);
\r
2242 error_message = L"Delete failed";
\r
2246 playername = wide_to_narrow(menudata.name);
\r
2247 address = wide_to_narrow(menudata.address);
\r
2248 int newport = stoi(wide_to_narrow(menudata.port));
\r
2251 //port = stoi(wide_to_narrow(menudata.port));
\r
2252 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
2254 // Check for valid parameters, restart menu if invalid.
\r
2255 if(playername == "")
\r
2257 error_message = L"Name required.";
\r
2262 g_settings.set("name", playername);
\r
2263 g_settings.set("address", address);
\r
2264 g_settings.set("port", itos(port));
\r
2265 // Update configuration file
\r
2266 if(configpath != "")
\r
2267 g_settings.updateConfigFile(configpath.c_str());
\r
2269 // Continue to game
\r
2273 // Break out of menu-game loop to shut down cleanly
\r
2274 if(g_device->run() == false)
\r
2278 Make a scope here so that the client and the server and other
\r
2279 stuff gets removed when disconnected or the irrlicht device
\r
2285 Draw "Loading" screen
\r
2287 const wchar_t *text = L"Loading and connecting...";
\r
2288 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2289 core::vector2d<s32> textsize(300, text_height);
\r
2290 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2292 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2293 text, textrect, false, false);
\r
2294 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2296 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2297 guienv->drawAll();
\r
2298 driver->endScene();
\r
2300 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2304 SharedPtr will delete it when it goes out of scope.
\r
2306 SharedPtr<Server> server;
\r
2307 if(address == ""){
\r
2308 server = new Server(map_dir);
\r
2309 server->start(port);
\r
2316 Client client(device, playername.c_str(), draw_control);
\r
2318 g_client = &client;
\r
2320 Address connect_address(0,0,0,0, port);
\r
2323 //connect_address.Resolve("localhost");
\r
2324 connect_address.setAddress(127,0,0,1);
\r
2326 connect_address.Resolve(address.c_str());
\r
2328 catch(ResolveError &e)
\r
2330 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2332 error_message = L"Couldn't resolve address";
\r
2333 gui_loadingtext->remove();
\r
2337 dstream<<DTIME<<"Connecting to server at ";
\r
2338 connect_address.print(&dstream);
\r
2339 dstream<<std::endl;
\r
2340 client.connect(connect_address);
\r
2343 while(client.connectedAndInitialized() == false)
\r
2346 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2347 guienv->drawAll();
\r
2348 driver->endScene();
\r
2350 // Update client and server
\r
2354 if(server != NULL)
\r
2355 server->step(0.1);
\r
2361 catch(con::PeerNotFoundException &e)
\r
2363 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2365 error_message = L"Connection timed out.";
\r
2366 gui_loadingtext->remove();
\r
2373 /*scene::ISceneNode* skybox;
\r
2374 skybox = smgr->addSkyBoxSceneNode(
\r
2375 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2376 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2377 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2378 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2379 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2380 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2383 Create the camera node
\r
2386 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2387 0, // Camera parent
\r
2388 v3f(BS*100, BS*2, BS*100), // Look from
\r
2389 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2393 if(camera == NULL)
\r
2396 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2397 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2398 //video::SColor skycolor = video::SColor(255,120,185,244);
\r
2399 video::SColor skycolor = video::SColor(255,140,186,250);
\r
2401 camera->setFOV(FOV_ANGLE);
\r
2403 // Just so big a value that everything rendered is visible
\r
2404 camera->setFarValue(100000*BS);
\r
2406 f32 camera_yaw = 0; // "right/left"
\r
2407 f32 camera_pitch = 0; // "up/down"
\r
2413 gui_loadingtext->remove();
\r
2416 Add some gui stuff
\r
2419 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2420 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2421 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2422 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2424 // Test the text input system
\r
2425 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2427 /*GUIMessageMenu *menu =
\r
2428 new GUIMessageMenu(guienv, guiroot, -1,
\r
2433 // Launch pause menu
\r
2434 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2435 &g_menumgr))->drop();
\r
2438 guitext2->setVisible(true);
\r
2439 guitext_info->setVisible(true);
\r
2440 guitext_chat->setVisible(true);
\r
2442 //s32 guitext_chat_pad_bottom = 70;
\r
2444 v2u32 screensize(0,0);
\r
2445 v2u32 last_screensize(0,0);
\r
2448 Some statistics are collected in these
\r
2451 u32 beginscenetime = 0;
\r
2452 u32 scenetime = 0;
\r
2453 u32 endscenetime = 0;
\r
2456 //throw con::PeerNotFoundException("lol");
\r
2458 core::list<float> frametime_log;
\r
2460 float damage_flash_timer = 0;
\r
2466 bool first_loop_after_window_activation = true;
\r
2468 // Time is in milliseconds
\r
2469 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2470 // NOTE: So we have to use getTime() and call run()s between them
\r
2471 u32 lasttime = device->getTimer()->getTime();
\r
2473 while(device->run() && kill == false)
\r
2475 if(g_disconnect_requested)
\r
2477 g_disconnect_requested = false;
\r
2482 Process TextureSource's queue
\r
2484 texturesource->processQueue();
\r
2487 Random calculations
\r
2489 last_screensize = screensize;
\r
2490 screensize = driver->getScreenSize();
\r
2491 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2492 //bool screensize_changed = screensize != last_screensize;
\r
2494 // Hilight boxes collected during the loop and displayed
\r
2495 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2498 std::wstring infotext;
\r
2500 // When screen size changes, update positions and sizes of stuff
\r
2501 /*if(screensize_changed)
\r
2503 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2504 quick_inventory->updatePosition(pos);
\r
2507 //TimeTaker //timer1("//timer1");
\r
2509 // Time of frame without fps limit
\r
2513 // not using getRealTime is necessary for wine
\r
2514 u32 time = device->getTimer()->getTime();
\r
2515 if(time > lasttime)
\r
2516 busytime_u32 = time - lasttime;
\r
2519 busytime = busytime_u32 / 1000.0;
\r
2522 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2524 // Necessary for device->getTimer()->getTime()
\r
2531 updateViewingRange(busytime, &client);
\r
2538 float fps_max = g_settings.getFloat("fps_max");
\r
2539 u32 frametime_min = 1000./fps_max;
\r
2541 if(busytime_u32 < frametime_min)
\r
2543 u32 sleeptime = frametime_min - busytime_u32;
\r
2544 device->sleep(sleeptime);
\r
2548 // Necessary for device->getTimer()->getTime()
\r
2552 Time difference calculation
\r
2554 f32 dtime; // in seconds
\r
2556 u32 time = device->getTimer()->getTime();
\r
2557 if(time > lasttime)
\r
2558 dtime = (time - lasttime) / 1000.0;
\r
2564 Log frametime for visualization
\r
2566 frametime_log.push_back(dtime);
\r
2567 if(frametime_log.size() > 100)
\r
2569 core::list<float>::Iterator i = frametime_log.begin();
\r
2570 frametime_log.erase(i);
\r
2574 Visualize frametime in terminal
\r
2576 /*for(u32 i=0; i<dtime*400; i++)
\r
2578 std::cout<<std::endl;*/
\r
2581 Time average and jitter calculation
\r
2584 static f32 dtime_avg1 = 0.0;
\r
2585 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2586 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2588 static f32 dtime_jitter1_max_sample = 0.0;
\r
2589 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2591 static f32 jitter1_max = 0.0;
\r
2592 static f32 counter = 0.0;
\r
2593 if(dtime_jitter1 > jitter1_max)
\r
2594 jitter1_max = dtime_jitter1;
\r
2599 dtime_jitter1_max_sample = jitter1_max;
\r
2600 dtime_jitter1_max_fraction
\r
2601 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2602 jitter1_max = 0.0;
\r
2607 Busytime average and jitter calculation
\r
2610 static f32 busytime_avg1 = 0.0;
\r
2611 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2612 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2614 static f32 busytime_jitter1_max_sample = 0.0;
\r
2615 static f32 busytime_jitter1_min_sample = 0.0;
\r
2617 static f32 jitter1_max = 0.0;
\r
2618 static f32 jitter1_min = 0.0;
\r
2619 static f32 counter = 0.0;
\r
2620 if(busytime_jitter1 > jitter1_max)
\r
2621 jitter1_max = busytime_jitter1;
\r
2622 if(busytime_jitter1 < jitter1_min)
\r
2623 jitter1_min = busytime_jitter1;
\r
2625 if(counter > 0.0){
\r
2627 busytime_jitter1_max_sample = jitter1_max;
\r
2628 busytime_jitter1_min_sample = jitter1_min;
\r
2629 jitter1_max = 0.0;
\r
2630 jitter1_min = 0.0;
\r
2635 Debug info for client
\r
2638 static float counter = 0.0;
\r
2643 client.printDebugInfo(std::cout);
\r
2648 Input handler step()
\r
2650 g_input->step(dtime);
\r
2657 Player speed control
\r
2666 bool a_superspeed,
\r
2670 PlayerControl control(
\r
2671 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2672 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2673 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2674 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2675 g_input->isKeyDown(irr::KEY_SPACE),
\r
2676 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2677 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2678 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2682 client.setPlayerControl(control);
\r
2689 if(server != NULL)
\r
2691 //TimeTaker timer("server->step(dtime)");
\r
2692 server->step(dtime);
\r
2696 Process environment
\r
2700 //TimeTaker timer("client.step(dtime)");
\r
2701 client.step(dtime);
\r
2702 //client.step(dtime_avg1);
\r
2705 // Read client events
\r
2708 ClientEvent event = client.getClientEvent();
\r
2709 if(event.type == CE_NONE)
\r
2713 else if(event.type == CE_PLAYER_DAMAGE)
\r
2715 //u16 damage = event.player_damage.amount;
\r
2716 //dstream<<"Player damage: "<<damage<<std::endl;
\r
2717 damage_flash_timer = 0.05;
\r
2719 else if(event.type == CE_PLAYER_FORCE_MOVE)
\r
2721 camera_yaw = event.player_force_move.yaw;
\r
2722 camera_pitch = event.player_force_move.pitch;
\r
2726 // Get player position
\r
2727 v3f player_position = client.getPlayerPosition();
\r
2729 //TimeTaker //timer2("//timer2");
\r
2732 Mouse and camera control
\r
2735 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2738 device->getCursorControl()->setVisible(false);
\r
2740 if(first_loop_after_window_activation){
\r
2741 //std::cout<<"window active, first loop"<<std::endl;
\r
2742 first_loop_after_window_activation = false;
\r
2745 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2746 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2747 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2748 camera_yaw -= dx*0.2;
\r
2749 camera_pitch += dy*0.2;
\r
2750 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2751 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2753 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2756 device->getCursorControl()->setVisible(true);
\r
2758 //std::cout<<"window inactive"<<std::endl;
\r
2759 first_loop_after_window_activation = true;
\r
2762 camera_yaw = wrapDegrees(camera_yaw);
\r
2763 camera_pitch = wrapDegrees(camera_pitch);
\r
2765 v3f camera_direction = v3f(0,0,1);
\r
2766 camera_direction.rotateYZBy(camera_pitch);
\r
2767 camera_direction.rotateXZBy(camera_yaw);
\r
2769 // This is at the height of the eyes of the current figure
\r
2770 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2771 // This is more like in minecraft
\r
2772 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2774 camera->setPosition(camera_position);
\r
2775 // *100.0 helps in large map coordinates
\r
2776 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2778 if(FIELD_OF_VIEW_TEST){
\r
2779 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2782 //TimeTaker timer("client.updateCamera");
\r
2783 client.updateCamera(camera_position, camera_direction);
\r
2787 //TimeTaker //timer3("//timer3");
\r
2790 Calculate what block is the crosshair pointing to
\r
2793 //u32 t1 = device->getTimer()->getRealTime();
\r
2795 //f32 d = 4; // max. distance
\r
2796 f32 d = 4; // max. distance
\r
2797 core::line3d<f32> shootline(camera_position,
\r
2798 camera_position + camera_direction * BS * (d+1));
\r
2800 MapBlockObject *selected_object = client.getSelectedObject
\r
2801 (d*BS, camera_position, shootline);
\r
2803 ClientActiveObject *selected_active_object
\r
2804 = client.getSelectedActiveObject
\r
2805 (d*BS, camera_position, shootline);
\r
2807 if(selected_object != NULL)
\r
2809 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2811 core::aabbox3d<f32> box_on_map
\r
2812 = selected_object->getSelectionBoxOnMap();
\r
2814 hilightboxes.push_back(box_on_map);
\r
2816 infotext = narrow_to_wide(selected_object->infoText());
\r
2818 if(g_input->getLeftClicked())
\r
2820 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2821 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2822 selected_object->getId(), g_selected_item);
\r
2824 else if(g_input->getRightClicked())
\r
2826 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2828 Check if we want to modify the object ourselves
\r
2830 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2832 dstream<<"Sign object right-clicked"<<std::endl;
\r
2834 if(random_input == false)
\r
2836 // Get a new text for it
\r
2838 TextDest *dest = new TextDestSign(
\r
2839 selected_object->getBlock()->getPos(),
\r
2840 selected_object->getId(),
\r
2843 SignObject *sign_object = (SignObject*)selected_object;
\r
2845 std::wstring wtext =
\r
2846 narrow_to_wide(sign_object->getText());
\r
2848 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2854 Otherwise pass the event to the server as-is
\r
2858 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2859 selected_object->getId(), g_selected_item);
\r
2863 else if(selected_active_object != NULL)
\r
2865 //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
\r
2867 core::aabbox3d<f32> *selection_box
\r
2868 = selected_active_object->getSelectionBox();
\r
2869 // Box should exist because object was returned in the
\r
2871 assert(selection_box);
\r
2873 v3f pos = selected_active_object->getPosition();
\r
2875 core::aabbox3d<f32> box_on_map(
\r
2876 selection_box->MinEdge + pos,
\r
2877 selection_box->MaxEdge + pos
\r
2880 hilightboxes.push_back(box_on_map);
\r
2882 //infotext = narrow_to_wide("A ClientActiveObject");
\r
2883 infotext = narrow_to_wide(selected_active_object->infoText());
\r
2885 if(g_input->getLeftClicked())
\r
2887 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2888 client.clickActiveObject(0,
\r
2889 selected_active_object->getId(), g_selected_item);
\r
2891 else if(g_input->getRightClicked())
\r
2893 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2896 else // selected_object == NULL
\r
2900 Find out which node we are pointing at
\r
2903 bool nodefound = false;
\r
2905 v3s16 neighbourpos;
\r
2906 core::aabbox3d<f32> nodehilightbox;
\r
2908 getPointedNode(player_position,
\r
2909 camera_direction, camera_position,
\r
2910 nodefound, shootline,
\r
2911 nodepos, neighbourpos,
\r
2912 nodehilightbox, d);
\r
2914 static float nodig_delay_counter = 0.0;
\r
2918 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2920 static float dig_time = 0.0;
\r
2921 static u16 dig_index = 0;
\r
2924 Visualize selection
\r
2927 hilightboxes.push_back(nodehilightbox);
\r
2930 Check information text of node
\r
2933 NodeMetadata *meta = client.getNodeMetadata(nodepos);
\r
2936 infotext = narrow_to_wide(meta->infoText());
\r
2939 //MapNode node = client.getNode(nodepos);
\r
2945 if(g_input->getLeftReleased())
\r
2947 client.clearTempMod(nodepos);
\r
2951 if(nodig_delay_counter > 0.0)
\r
2953 nodig_delay_counter -= dtime;
\r
2957 if(nodepos != nodepos_old)
\r
2959 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2960 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2962 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2964 client.clearTempMod(nodepos_old);
\r
2969 if(g_input->getLeftClicked() ||
\r
2970 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2972 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2973 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2975 if(g_input->getLeftClicked())
\r
2977 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2979 if(g_input->getLeftState())
\r
2981 MapNode n = client.getNode(nodepos);
\r
2983 // Get tool name. Default is "" = bare hands
\r
2984 std::string toolname = "";
\r
2985 InventoryList *mlist = local_inventory.getList("main");
\r
2988 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2989 if(item && (std::string)item->getName() == "ToolItem")
\r
2991 ToolItem *titem = (ToolItem*)item;
\r
2992 toolname = titem->getToolName();
\r
2996 // Get digging properties for material and tool
\r
2997 u8 material = n.d;
\r
2998 DiggingProperties prop =
\r
2999 getDiggingProperties(material, toolname);
\r
3001 float dig_time_complete = 0.0;
\r
3003 if(prop.diggable == false)
\r
3005 /*dstream<<"Material "<<(int)material
\r
3006 <<" not diggable with \""
\r
3007 <<toolname<<"\""<<std::endl;*/
\r
3008 // I guess nobody will wait for this long
\r
3009 dig_time_complete = 10000000.0;
\r
3013 dig_time_complete = prop.time;
\r
3016 if(dig_time_complete >= 0.001)
\r
3018 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
3019 * dig_time/dig_time_complete);
\r
3021 // This is for torches
\r
3024 dig_index = CRACK_ANIMATION_LENGTH;
\r
3027 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
3029 //TimeTaker timer("client.setTempMod");
\r
3030 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
3031 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
3035 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
3036 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
3037 client.clearTempMod(nodepos);
\r
3038 client.removeNode(nodepos);
\r
3042 nodig_delay_counter = dig_time_complete
\r
3043 / (float)CRACK_ANIMATION_LENGTH;
\r
3045 // We don't want a corresponding delay to
\r
3046 // very time consuming nodes
\r
3047 if(nodig_delay_counter > 0.5)
\r
3049 nodig_delay_counter = 0.5;
\r
3051 // We want a slight delay to very little
\r
3052 // time consuming nodes
\r
3053 float mindelay = 0.15;
\r
3054 if(nodig_delay_counter < mindelay)
\r
3056 nodig_delay_counter = mindelay;
\r
3060 dig_time += dtime;
\r
3064 if(g_input->getRightClicked())
\r
3066 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
3068 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
\r
3070 dstream<<"Sign node right-clicked"<<std::endl;
\r
3072 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
\r
3074 // Get a new text for it
\r
3076 TextDest *dest = new TextDestSignNode(nodepos, &client);
\r
3078 std::wstring wtext =
\r
3079 narrow_to_wide(signmeta->getText());
\r
3081 (new GUITextInputMenu(guienv, guiroot, -1,
\r
3085 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
\r
3087 dstream<<"Chest node right-clicked"<<std::endl;
\r
3089 //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
\r
3091 std::string chest_inv_id;
\r
3092 chest_inv_id += "nodemeta:";
\r
3093 chest_inv_id += itos(nodepos.X);
\r
3094 chest_inv_id += ",";
\r
3095 chest_inv_id += itos(nodepos.Y);
\r
3096 chest_inv_id += ",";
\r
3097 chest_inv_id += itos(nodepos.Z);
\r
3099 GUIInventoryMenu *menu =
\r
3100 new GUIInventoryMenu(guienv, guiroot, -1,
\r
3101 &g_menumgr, v2s16(8,9),
\r
3102 g_client->getInventoryContext(),
\r
3105 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
3107 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3108 "list", chest_inv_id, "0",
\r
3109 v2s32(0, 0), v2s32(8, 4)));
\r
3110 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3111 "list", "current_player", "main",
\r
3112 v2s32(0, 5), v2s32(8, 4)));
\r
3114 menu->setDrawSpec(draw_spec);
\r
3119 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
\r
3121 dstream<<"Furnace node right-clicked"<<std::endl;
\r
3123 GUIFurnaceMenu *menu =
\r
3124 new GUIFurnaceMenu(guienv, guiroot, -1,
\r
3125 &g_menumgr, nodepos, g_client);
\r
3132 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
3136 nodepos_old = nodepos;
\r
3141 } // selected_object == NULL
\r
3143 g_input->resetLeftClicked();
\r
3144 g_input->resetRightClicked();
\r
3146 if(g_input->getLeftReleased())
\r
3148 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
3150 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
3152 if(g_input->getRightReleased())
\r
3154 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
3158 g_input->resetLeftReleased();
\r
3159 g_input->resetRightReleased();
\r
3162 Calculate stuff for drawing
\r
3165 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
3167 u32 daynight_ratio = client.getDayNightRatio();
\r
3168 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
3169 video::SColor bgcolor = video::SColor(
\r
3171 skycolor.getRed() * l / 255,
\r
3172 skycolor.getGreen() * l / 255,
\r
3173 skycolor.getBlue() * l / 255);
\r
3179 if(g_settings.getBool("enable_fog") == true)
\r
3181 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
3182 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
3183 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
3184 if(draw_control.range_all)
\r
3185 range = 100000*BS;
\r
3189 video::EFT_FOG_LINEAR,
\r
3193 false, // pixel fog
\r
3194 false // range fog
\r
3201 video::EFT_FOG_LINEAR,
\r
3205 false, // pixel fog
\r
3206 false // range fog
\r
3212 Update gui stuff (0ms)
\r
3215 //TimeTaker guiupdatetimer("Gui updating");
\r
3218 static float drawtime_avg = 0;
\r
3219 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
3220 static float beginscenetime_avg = 0;
\r
3221 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
3222 static float scenetime_avg = 0;
\r
3223 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
3224 static float endscenetime_avg = 0;
\r
3225 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3227 char temptext[300];
\r
3228 snprintf(temptext, 300, "Minetest-c55 ("
\r
3230 ", R: range_all=%i"
\r
3232 " drawtime=%.0f, beginscenetime=%.0f"
\r
3233 ", scenetime=%.0f, endscenetime=%.0f",
\r
3235 draw_control.range_all,
\r
3237 beginscenetime_avg,
\r
3242 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3246 char temptext[300];
\r
3247 snprintf(temptext, 300,
\r
3248 "(% .1f, % .1f, % .1f)"
\r
3249 " (% .3f < btime_jitter < % .3f"
\r
3250 ", dtime_jitter = % .1f %%"
\r
3251 ", v_range = %.1f)",
\r
3252 player_position.X/BS,
\r
3253 player_position.Y/BS,
\r
3254 player_position.Z/BS,
\r
3255 busytime_jitter1_min_sample,
\r
3256 busytime_jitter1_max_sample,
\r
3257 dtime_jitter1_max_fraction * 100.0,
\r
3258 draw_control.wanted_range
\r
3261 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3265 guitext_info->setText(infotext.c_str());
\r
3269 Get chat messages from client
\r
3272 // Get new messages
\r
3273 std::wstring message;
\r
3274 while(client.getChatMessage(message))
\r
3276 chat_lines.push_back(ChatLine(message));
\r
3277 /*if(chat_lines.size() > 6)
\r
3279 core::list<ChatLine>::Iterator
\r
3280 i = chat_lines.begin();
\r
3281 chat_lines.erase(i);
\r
3284 // Append them to form the whole static text and throw
\r
3285 // it to the gui element
\r
3286 std::wstring whole;
\r
3287 // This will correspond to the line number counted from
\r
3288 // top to bottom, from size-1 to 0
\r
3289 s16 line_number = chat_lines.size();
\r
3290 // Count of messages to be removed from the top
\r
3291 u16 to_be_removed_count = 0;
\r
3292 for(core::list<ChatLine>::Iterator
\r
3293 i = chat_lines.begin();
\r
3294 i != chat_lines.end(); i++)
\r
3296 // After this, line number is valid for this loop
\r
3299 (*i).age += dtime;
\r
3301 This results in a maximum age of 60*6 to the
\r
3302 lowermost line and a maximum of 6 lines
\r
3304 float allowed_age = (6-line_number) * 60.0;
\r
3306 if((*i).age > allowed_age)
\r
3308 to_be_removed_count++;
\r
3311 whole += (*i).text + L'\n';
\r
3313 for(u16 i=0; i<to_be_removed_count; i++)
\r
3315 core::list<ChatLine>::Iterator
\r
3316 it = chat_lines.begin();
\r
3317 chat_lines.erase(it);
\r
3319 guitext_chat->setText(whole.c_str());
\r
3321 // Update gui element size and position
\r
3323 /*core::rect<s32> rect(
\r
3325 screensize.Y - guitext_chat_pad_bottom
\r
3326 - text_height*chat_lines.size(),
\r
3327 screensize.X - 10,
\r
3328 screensize.Y - guitext_chat_pad_bottom
\r
3330 core::rect<s32> rect(
\r
3333 screensize.X - 10,
\r
3334 50 + text_height*chat_lines.size()
\r
3337 guitext_chat->setRelativePosition(rect);
\r
3339 if(chat_lines.size() == 0)
\r
3340 guitext_chat->setVisible(false);
\r
3342 guitext_chat->setVisible(true);
\r
3349 static u16 old_selected_item = 65535;
\r
3350 if(client.getLocalInventoryUpdated()
\r
3351 || g_selected_item != old_selected_item)
\r
3353 old_selected_item = g_selected_item;
\r
3354 //std::cout<<"Updating local inventory"<<std::endl;
\r
3355 client.getLocalInventory(local_inventory);
\r
3359 Send actions returned by the inventory menu
\r
3361 while(inventory_action_queue.size() != 0)
\r
3363 InventoryAction *a = inventory_action_queue.pop_front();
\r
3365 client.sendInventoryAction(a);
\r
3374 TimeTaker drawtimer("Drawing");
\r
3378 TimeTaker timer("beginScene");
\r
3379 driver->beginScene(true, true, bgcolor);
\r
3380 //driver->beginScene(false, true, bgcolor);
\r
3381 beginscenetime = timer.stop(true);
\r
3386 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3389 TimeTaker timer("smgr");
\r
3391 scenetime = timer.stop(true);
\r
3395 //TimeTaker timer9("auxiliary drawings");
\r
3399 //TimeTaker //timer10("//timer10");
\r
3401 video::SMaterial m;
\r
3402 //m.Thickness = 10;
\r
3404 m.Lighting = false;
\r
3405 driver->setMaterial(m);
\r
3407 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3409 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3410 i != hilightboxes.end(); i++)
\r
3412 /*std::cout<<"hilightbox min="
\r
3413 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3415 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3417 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3423 if(g_settings.getBool("frametime_graph") == true)
\r
3426 for(core::list<float>::Iterator
\r
3427 i = frametime_log.begin();
\r
3428 i != frametime_log.end();
\r
3431 driver->draw2DLine(v2s32(x,50),
\r
3432 v2s32(x,50+(*i)*1000),
\r
3433 video::SColor(255,255,255,255));
\r
3441 if(g_show_map_plot && g_map_plot_texture)
\r
3443 core::dimension2d<u32> drawdim(640,480);
\r
3444 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3446 (screensize.X-drawdim.Width)/2,
\r
3447 (screensize.Y-drawdim.Height)/2
\r
3449 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3450 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3456 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3457 displaycenter + core::vector2d<s32>(10,0),
\r
3458 video::SColor(255,255,255,255));
\r
3459 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3460 displaycenter + core::vector2d<s32>(0,10),
\r
3461 video::SColor(255,255,255,255));
\r
3466 //TimeTaker //timer11("//timer11");
\r
3472 guienv->drawAll();
\r
3478 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3479 hotbar_imagesize, hotbar_itemcount, &local_inventory,
\r
3486 if(damage_flash_timer > 0.0)
\r
3488 damage_flash_timer -= dtime;
\r
3490 video::SColor color(128,255,0,0);
\r
3491 driver->draw2DRectangle(color,
\r
3492 core::rect<s32>(0,0,screensize.X,screensize.Y),
\r
3500 TimeTaker timer("endScene");
\r
3501 driver->endScene();
\r
3502 endscenetime = timer.stop(true);
\r
3505 drawtime = drawtimer.stop(true);
\r
3513 Refresh map plot if player has moved considerably
\r
3515 if(g_refresh_map_plot)
\r
3517 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3518 v3f p = client.getPlayerPosition() / BS;
\r
3519 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3521 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3522 old_player_pos = p;
\r
3524 g_refresh_map_plot = false;
\r
3528 static s16 lastFPS = 0;
\r
3529 //u16 fps = driver->getFPS();
\r
3530 u16 fps = (1.0/dtime_avg1);
\r
3532 if (lastFPS != fps)
\r
3534 core::stringw str = L"Minetest [";
\r
3535 str += driver->getName();
\r
3539 device->setWindowCaption(str.c_str());
\r
3544 } // client and server are deleted at this point
\r
3547 catch(con::PeerNotFoundException &e)
\r
3549 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3550 error_message = L"Connection timed out.";
\r
3553 } // Menu-game loop
\r
3558 In the end, delete the Irrlicht device.
\r
3563 Update configuration file
\r
3565 /*if(configpath != "")
\r
3567 g_settings.updateConfigFile(configpath.c_str());
\r
3570 END_DEBUG_EXCEPTION_HANDLER
\r
3572 debugstreams_deinit();
\r