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
99 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
101 - The player could go faster by a crafting a boat, or riding an animal
\r
103 - Random NPC traders. what else?
\r
107 - When furnace is destroyed, move items to player's inventory
\r
108 - Add lots of stuff
\r
110 - Growing grass, decaying leaves
\r
111 - This can be done in the active blocks I guess.
\r
112 - Lots of stuff can be done in the active blocks.
\r
113 - Uh, is there an active block list somewhere? I think not. Add it.
\r
114 - Player health points
\r
115 - When player dies, throw items on map (needs better item-on-map
\r
117 - Cobble to get mossy if near water
\r
118 - More slots in furnace source list, so that multiple ingredients
\r
125 Build system / running:
\r
126 -----------------------
\r
128 Networking and serialization:
\r
129 -----------------------------
\r
131 TODO: Get rid of GotSplitPacketException
\r
136 TODO: Configuration menu, at least for keys
\r
141 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
143 - That is >500 vertices
\r
144 - This is not easy; all the MapBlocks close to the player would
\r
145 still need to be drawn separately and combining the blocks
\r
146 would have to happen in a background thread
\r
148 SUGG: Make fetching sector's blocks more efficient when rendering
\r
149 sectors that have very large amounts of blocks (on client)
\r
150 - Is this necessary at all?
\r
152 TODO: Flowing water animation
\r
154 SUGG: Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
156 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
157 animating them is easier.
\r
159 SUGG: Option for enabling proper alpha channel for textures
\r
167 TODO: Remove IrrlichtWrapper
\r
169 TODO: Untie client network operations from framerate
\r
170 - Needs some input queues or something
\r
171 - This won't give much performance boost because calculating block
\r
172 meshes takes so long
\r
174 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
176 TODO: Don't update all meshes always on single node changes, but
\r
177 check which ones should be updated
\r
178 - implement Map::updateNodeMeshes() and the usage of it
\r
179 - It will give almost always a 4x boost in mesh update performance.
\r
184 SUGG: Make an option to the server to disable building and digging near
\r
185 the starting position
\r
187 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
189 * Fix the problem with the server constantly saving one or a few
\r
190 blocks? List the first saved block, maybe it explains.
\r
191 - It is probably caused by oscillating water
\r
192 * Make a small history check to transformLiquids to detect and log
\r
193 continuous oscillations, in such detail that they can be fixed.
\r
195 FIXME: If something is removed from craftresult with a right click,
\r
196 it is only possible to get one item from it should give 4
\r
201 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
203 SUGG: MovingObject::move and Player::move are basically the same.
\r
205 - NOTE: Player::move is more up-to-date.
\r
210 TODO: Mineral and ground material properties
\r
211 - This way mineral ground toughness can be calculated with just
\r
212 some formula, as well as tool strengths
\r
214 TODO: Flowing water to actually contain flow direction information
\r
216 SUGG: Erosion simulation at map generation time
\r
217 - Simulate water flows, which would carve out dirt fast and
\r
218 then turn stone into gravel and sand and relocate it.
\r
219 - How about relocating minerals, too? Coal and gold in
\r
220 downstream sand and gravel would be kind of cool
\r
221 - This would need a better way of handling minerals, mainly
\r
222 to have mineral content as a separate field. the first
\r
223 parameter field is free for this.
\r
224 - Simulate rock falling from cliffs when water has removed
\r
225 enough solid rock from the bottom
\r
228 * only_from_disk might not work anymore - check and fix it.
\r
229 * Make the generator to run in background and not blocking block
\r
230 placement and transfer
\r
231 * Possibly add some kind of erosion and other stuff
\r
232 * Better water generation (spread it to underwater caverns but don't
\r
233 fill dungeons that don't touch big water masses)
\r
234 * When generating a chunk and the neighboring chunk doesn't have mud
\r
235 and stuff yet and the ground is fairly flat, the mud will flow to
\r
236 the other chunk making nasty straight walls when the other chunk
\r
237 is generated. Fix it.
\r
241 * Make an "environment metafile" to store at least time of day
\r
242 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
243 - Or maybe move content_features to material.{h,cpp}?
\r
245 Make a system for pregenerating quick information for mapblocks, so
\r
246 that the client can show them as cubes before they are actually sent
\r
249 Making it more portable:
\r
250 ------------------------
\r
251 * Some MSVC: std::sto* are defined without a namespace and collide
\r
252 with the ones in utility.h
\r
254 ======================================================================
\r
259 Setting this to 1 enables a special camera mode that forces
\r
260 the renderers to think that the camera statically points from
\r
261 the starting place to a static direction.
\r
263 This allows one to move around with the player and see what
\r
264 is actually drawn behind solid things and behind the player.
\r
266 #define FIELD_OF_VIEW_TEST 0
\r
270 #pragma message ("Disabling unit tests")
\r
272 #warning "Disabling unit tests"
\r
274 // Disable unit tests
\r
275 #define ENABLE_TESTS 0
\r
277 // Enable unit tests
\r
278 #define ENABLE_TESTS 1
\r
282 #pragma comment(lib, "Irrlicht.lib")
\r
283 //#pragma comment(lib, "jthread.lib")
\r
284 #pragma comment(lib, "zlibwapi.lib")
\r
285 #pragma comment(lib, "Shell32.lib")
\r
286 // This would get rid of the console window
\r
287 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
290 #include <iostream>
\r
292 #include <jmutexautolock.h>
\r
293 #include <locale.h>
\r
295 #include "common_irrlicht.h"
\r
298 #include "player.h"
\r
300 //#include "environment.h"
\r
301 #include "server.h"
\r
302 #include "client.h"
\r
303 //#include "serialization.h"
\r
304 #include "constants.h"
\r
305 //#include "strfnd.h"
\r
306 #include "porting.h"
\r
307 #include "irrlichtwrapper.h"
\r
308 #include "gettime.h"
\r
309 #include "porting.h"
\r
310 #include "guiPauseMenu.h"
\r
311 #include "guiInventoryMenu.h"
\r
312 #include "guiTextInputMenu.h"
\r
313 #include "materials.h"
\r
314 #include "guiMessageMenu.h"
\r
315 #include "filesys.h"
\r
316 #include "config.h"
\r
317 #include "guiMainMenu.h"
\r
318 #include "mineral.h"
\r
321 #include "guiFurnaceMenu.h"
\r
323 // TODO: Remove this
\r
324 IrrlichtWrapper *g_irrlicht = NULL;
\r
326 // This makes textures
\r
327 ITextureSource *g_texturesource = NULL;
\r
329 MapDrawControl draw_control;
\r
333 These are loaded from the config file.
\r
336 Settings g_settings;
\r
338 extern void set_default_settings();
\r
344 IrrlichtDevice *g_device = NULL;
\r
345 Client *g_client = NULL;
\r
347 /*const s16 quickinv_size = 40;
\r
348 const s16 quickinv_padding = 8;
\r
349 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
350 const s16 quickinv_outer_padding = 4;
\r
351 const s16 quickinv_itemcount = 8;*/
\r
353 const s32 hotbar_itemcount = 8;
\r
354 const s32 hotbar_imagesize = 36;
\r
360 gui::IGUIEnvironment* guienv = NULL;
\r
361 gui::IGUIStaticText *guiroot = NULL;
\r
363 class MainMenuManager : public IMenuManager
\r
366 virtual void createdMenu(GUIModalMenu *menu)
\r
368 for(core::list<GUIModalMenu*>::Iterator
\r
369 i = m_stack.begin();
\r
370 i != m_stack.end(); i++)
\r
372 assert(*i != menu);
\r
375 if(m_stack.size() != 0)
\r
376 (*m_stack.getLast())->setVisible(false);
\r
377 m_stack.push_back(menu);
\r
380 virtual void deletingMenu(GUIModalMenu *menu)
\r
382 // Remove all entries if there are duplicates
\r
383 bool removed_entry;
\r
385 removed_entry = false;
\r
386 for(core::list<GUIModalMenu*>::Iterator
\r
387 i = m_stack.begin();
\r
388 i != m_stack.end(); i++)
\r
393 removed_entry = true;
\r
397 }while(removed_entry);
\r
399 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
400 assert(*i == menu);
\r
401 m_stack.erase(i);*/
\r
403 if(m_stack.size() != 0)
\r
404 (*m_stack.getLast())->setVisible(true);
\r
409 return m_stack.size();
\r
412 core::list<GUIModalMenu*> m_stack;
\r
415 MainMenuManager g_menumgr;
\r
417 bool noMenuActive()
\r
419 return (g_menumgr.menuCount() == 0);
\r
422 bool g_disconnect_requested = false;
\r
424 class MainGameCallback : public IGameCallback
\r
427 virtual void exitToOS()
\r
429 g_device->closeDevice();
\r
432 virtual void disconnect()
\r
434 g_disconnect_requested = true;
\r
438 MainGameCallback g_gamecallback;
\r
440 // Inventory actions from the menu are buffered here before sending
\r
441 // TODO: Get rid of this
\r
442 Queue<InventoryAction*> inventory_action_queue;
\r
443 // This is a copy of the inventory that the client's environment has
\r
444 Inventory local_inventory;
\r
446 u16 g_selected_item = 0;
\r
448 /*bool g_show_map_plot = false;
\r
449 bool g_refresh_map_plot = false;*/
\r
456 std::ostream *dout_con_ptr = &dummyout;
\r
457 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
458 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
459 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
460 //std::ostream *dout_con_ptr = &dstream;
\r
461 //std::ostream *derr_con_ptr = &dstream;
\r
464 std::ostream *dout_server_ptr = &dstream;
\r
465 std::ostream *derr_server_ptr = &dstream;
\r
468 std::ostream *dout_client_ptr = &dstream;
\r
469 std::ostream *derr_client_ptr = &dstream;
\r
472 gettime.h implementation
\r
478 Use irrlicht because it is more precise than porting.h's
\r
481 if(g_irrlicht == NULL)
\r
483 return g_irrlicht->getTime();
\r
490 struct TextDestSign : public TextDest
\r
492 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
494 m_blockpos = blockpos;
\r
498 void gotText(std::wstring text)
\r
500 std::string ntext = wide_to_narrow(text);
\r
501 dstream<<"Changing text of a sign object: "
\r
502 <<ntext<<std::endl;
\r
503 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
511 struct TextDestChat : public TextDest
\r
513 TextDestChat(Client *client)
\r
517 void gotText(std::wstring text)
\r
519 // Discard empty line
\r
523 // Parse command (server command starts with "/#")
\r
524 if(text[0] == L'/' && text[1] != L'#')
\r
526 std::wstring reply = L"Local: ";
\r
528 reply += L"Local commands not yet supported. "
\r
529 L"Server prefix is \"/#\".";
\r
531 m_client->addChatMessage(reply);
\r
536 m_client->sendChatMessage(text);
\r
538 m_client->addChatMessage(text);
\r
544 struct TextDestSignNode : public TextDest
\r
546 TextDestSignNode(v3s16 p, Client *client)
\r
551 void gotText(std::wstring text)
\r
553 std::string ntext = wide_to_narrow(text);
\r
554 dstream<<"Changing text of a sign node: "
\r
555 <<ntext<<std::endl;
\r
556 m_client->sendSignNodeText(m_p, ntext);
\r
563 class MyEventReceiver : public IEventReceiver
\r
566 // This is the one method that we have to implement
\r
567 virtual bool OnEvent(const SEvent& event)
\r
570 React to nothing here if a menu is active
\r
572 if(noMenuActive() == false)
\r
578 // Remember whether each key is down or up
\r
579 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
581 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
583 if(event.KeyInput.PressedDown)
\r
585 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
586 /*if(g_show_map_plot)
\r
588 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
589 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
591 g_show_map_plot = false;
\r
600 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
602 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
604 dstream<<DTIME<<"MyEventReceiver: "
\r
605 <<"Launching pause menu"<<std::endl;
\r
606 // It will delete itself by itself
\r
607 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
608 &g_menumgr))->drop();
\r
611 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
613 dstream<<DTIME<<"MyEventReceiver: "
\r
614 <<"Launching inventory"<<std::endl;
\r
616 GUIInventoryMenu *menu =
\r
617 new GUIInventoryMenu(guienv, guiroot, -1,
\r
618 &g_menumgr, v2s16(8,7),
\r
619 g_client->getInventoryContext(),
\r
622 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
623 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
624 "list", "current_player", "main",
\r
625 v2s32(0, 3), v2s32(8, 4)));
\r
626 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
627 "list", "current_player", "craft",
\r
628 v2s32(3, 0), v2s32(3, 3)));
\r
629 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
630 "list", "current_player", "craftresult",
\r
631 v2s32(7, 1), v2s32(1, 1)));
\r
633 menu->setDrawSpec(draw_spec);
\r
639 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
641 TextDest *dest = new TextDestChat(g_client);
\r
643 (new GUITextInputMenu(guienv, guiroot, -1,
\r
650 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
651 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
653 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
654 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
656 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
657 g_selected_item = s1-1;
\r
658 dstream<<DTIME<<"Selected item: "
\r
659 <<g_selected_item<<std::endl;
\r
662 // Viewing range selection
\r
663 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
665 if(draw_control.range_all)
\r
667 draw_control.range_all = false;
\r
668 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
672 draw_control.range_all = true;
\r
673 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
677 // Print debug stacks
\r
678 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
680 dstream<<"-----------------------------------------"
\r
682 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
683 dstream<<"-----------------------------------------"
\r
685 debug_stacks_print();
\r
689 /*if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
691 dstream<<"Map plot requested"<<std::endl;
\r
692 g_show_map_plot = !g_show_map_plot;
\r
693 if(g_show_map_plot)
\r
694 g_refresh_map_plot = true;
\r
700 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
702 if(noMenuActive() == false)
\r
704 left_active = false;
\r
705 middle_active = false;
\r
706 right_active = false;
\r
710 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
711 left_active = event.MouseInput.isLeftPressed();
\r
712 middle_active = event.MouseInput.isMiddlePressed();
\r
713 right_active = event.MouseInput.isRightPressed();
\r
715 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
717 leftclicked = true;
\r
719 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
721 rightclicked = true;
\r
723 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
725 leftreleased = true;
\r
727 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
729 rightreleased = true;
\r
731 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
733 /*dstream<<"event.MouseInput.Wheel="
\r
734 <<event.MouseInput.Wheel<<std::endl;*/
\r
736 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
737 hotbar_itemcount-1);
\r
738 if(event.MouseInput.Wheel < 0)
\r
740 if(g_selected_item < max_item)
\r
743 g_selected_item = 0;
\r
745 else if(event.MouseInput.Wheel > 0)
\r
747 if(g_selected_item > 0)
\r
750 g_selected_item = max_item;
\r
759 // This is used to check whether a key is being held down
\r
760 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
762 return keyIsDown[keyCode];
\r
767 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
768 keyIsDown[i] = false;
\r
770 leftclicked = false;
\r
771 rightclicked = false;
\r
772 leftreleased = false;
\r
773 rightreleased = false;
\r
775 left_active = false;
\r
776 middle_active = false;
\r
777 right_active = false;
\r
788 bool rightreleased;
\r
791 bool middle_active;
\r
795 // We use this array to store the current state of each key
\r
796 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
799 IrrlichtDevice *m_device;
\r
808 virtual ~InputHandler()
\r
812 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
814 virtual v2s32 getMousePos() = 0;
\r
815 virtual void setMousePos(s32 x, s32 y) = 0;
\r
817 virtual bool getLeftState() = 0;
\r
818 virtual bool getRightState() = 0;
\r
820 virtual bool getLeftClicked() = 0;
\r
821 virtual bool getRightClicked() = 0;
\r
822 virtual void resetLeftClicked() = 0;
\r
823 virtual void resetRightClicked() = 0;
\r
825 virtual bool getLeftReleased() = 0;
\r
826 virtual bool getRightReleased() = 0;
\r
827 virtual void resetLeftReleased() = 0;
\r
828 virtual void resetRightReleased() = 0;
\r
830 virtual void step(float dtime) {};
\r
832 virtual void clear() {};
\r
835 InputHandler *g_input = NULL;
\r
837 class RealInputHandler : public InputHandler
\r
840 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
842 m_receiver(receiver)
\r
845 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
847 return m_receiver->IsKeyDown(keyCode);
\r
849 virtual v2s32 getMousePos()
\r
851 return m_device->getCursorControl()->getPosition();
\r
853 virtual void setMousePos(s32 x, s32 y)
\r
855 m_device->getCursorControl()->setPosition(x, y);
\r
858 virtual bool getLeftState()
\r
860 return m_receiver->left_active;
\r
862 virtual bool getRightState()
\r
864 return m_receiver->right_active;
\r
867 virtual bool getLeftClicked()
\r
869 return m_receiver->leftclicked;
\r
871 virtual bool getRightClicked()
\r
873 return m_receiver->rightclicked;
\r
875 virtual void resetLeftClicked()
\r
877 m_receiver->leftclicked = false;
\r
879 virtual void resetRightClicked()
\r
881 m_receiver->rightclicked = false;
\r
884 virtual bool getLeftReleased()
\r
886 return m_receiver->leftreleased;
\r
888 virtual bool getRightReleased()
\r
890 return m_receiver->rightreleased;
\r
892 virtual void resetLeftReleased()
\r
894 m_receiver->leftreleased = false;
\r
896 virtual void resetRightReleased()
\r
898 m_receiver->rightreleased = false;
\r
903 resetRightClicked();
\r
904 resetLeftClicked();
\r
907 IrrlichtDevice *m_device;
\r
908 MyEventReceiver *m_receiver;
\r
911 class RandomInputHandler : public InputHandler
\r
914 RandomInputHandler()
\r
918 leftclicked = false;
\r
919 rightclicked = false;
\r
920 leftreleased = false;
\r
921 rightreleased = false;
\r
922 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
923 keydown[i] = false;
\r
925 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
927 return keydown[keyCode];
\r
929 virtual v2s32 getMousePos()
\r
933 virtual void setMousePos(s32 x, s32 y)
\r
935 mousepos = v2s32(x,y);
\r
938 virtual bool getLeftState()
\r
942 virtual bool getRightState()
\r
947 virtual bool getLeftClicked()
\r
949 return leftclicked;
\r
951 virtual bool getRightClicked()
\r
953 return rightclicked;
\r
955 virtual void resetLeftClicked()
\r
957 leftclicked = false;
\r
959 virtual void resetRightClicked()
\r
961 rightclicked = false;
\r
964 virtual bool getLeftReleased()
\r
966 return leftreleased;
\r
968 virtual bool getRightReleased()
\r
970 return rightreleased;
\r
972 virtual void resetLeftReleased()
\r
974 leftreleased = false;
\r
976 virtual void resetRightReleased()
\r
978 rightreleased = false;
\r
981 virtual void step(float dtime)
\r
984 static float counter1 = 0;
\r
988 counter1 = 0.1*Rand(1, 40);
\r
989 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
993 static float counter1 = 0;
\r
997 counter1 = 0.1*Rand(1, 40);
\r
998 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\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_KEY_W] = !keydown[irr::KEY_KEY_W];
\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_A] = !keydown[irr::KEY_KEY_A];
\r
1020 static float counter1 = 0;
\r
1021 counter1 -= dtime;
\r
1022 if(counter1 < 0.0)
\r
1024 counter1 = 0.1*Rand(1, 20);
\r
1025 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1029 static float counter1 = 0;
\r
1030 counter1 -= dtime;
\r
1031 if(counter1 < 0.0)
\r
1033 counter1 = 0.1*Rand(1, 30);
\r
1034 leftdown = !leftdown;
\r
1036 leftclicked = true;
\r
1038 leftreleased = true;
\r
1042 static float counter1 = 0;
\r
1043 counter1 -= dtime;
\r
1044 if(counter1 < 0.0)
\r
1046 counter1 = 0.1*Rand(1, 15);
\r
1047 rightdown = !rightdown;
\r
1049 rightclicked = true;
\r
1051 rightreleased = true;
\r
1054 mousepos += mousespeed;
\r
1057 s32 Rand(s32 min, s32 max)
\r
1059 return (myrand()%(max-min+1))+min;
\r
1062 bool keydown[KEY_KEY_CODES_COUNT];
\r
1068 bool rightclicked;
\r
1069 bool leftreleased;
\r
1070 bool rightreleased;
\r
1073 void updateViewingRange(f32 frametime_in, Client *client)
\r
1075 if(draw_control.range_all == true)
\r
1078 static f32 added_frametime = 0;
\r
1079 static s16 added_frames = 0;
\r
1081 added_frametime += frametime_in;
\r
1082 added_frames += 1;
\r
1084 // Actually this counter kind of sucks because frametime is busytime
\r
1085 static f32 counter = 0;
\r
1086 counter -= frametime_in;
\r
1092 /*dstream<<__FUNCTION_NAME
\r
1093 <<": Collected "<<added_frames<<" frames, total of "
\r
1094 <<added_frametime<<"s."<<std::endl;*/
\r
1096 /*dstream<<"draw_control.blocks_drawn="
\r
1097 <<draw_control.blocks_drawn
\r
1098 <<", draw_control.blocks_would_have_drawn="
\r
1099 <<draw_control.blocks_would_have_drawn
\r
1102 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1103 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1105 draw_control.wanted_min_range = range_min;
\r
1106 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1108 float block_draw_ratio = 1.0;
\r
1109 if(draw_control.blocks_would_have_drawn != 0)
\r
1111 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1112 / (float)draw_control.blocks_would_have_drawn;
\r
1115 // Calculate the average frametime in the case that all wanted
\r
1116 // blocks had been drawn
\r
1117 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1119 added_frametime = 0.0;
\r
1122 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1123 float wanted_frametime = 1.0 / wanted_fps;
\r
1125 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1126 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1128 // If needed frametime change is small, just return
\r
1129 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1131 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1135 float range = draw_control.wanted_range;
\r
1136 float new_range = range;
\r
1138 static s16 range_old = 0;
\r
1139 static f32 frametime_old = 0;
\r
1141 float d_range = range - range_old;
\r
1142 f32 d_frametime = frametime - frametime_old;
\r
1143 // A sane default of 30ms per 50 nodes of range
\r
1144 static f32 time_per_range = 30. / 50;
\r
1147 time_per_range = d_frametime / d_range;
\r
1150 // The minimum allowed calculated frametime-range derivative:
\r
1151 // Practically this sets the maximum speed of changing the range.
\r
1152 // The lower this value, the higher the maximum changing speed.
\r
1153 // A low value here results in wobbly range (0.001)
\r
1154 // A high value here results in slow changing range (0.0025)
\r
1155 // SUGG: This could be dynamically adjusted so that when
\r
1156 // the camera is turning, this is lower
\r
1157 //float min_time_per_range = 0.0015;
\r
1158 float min_time_per_range = 0.0010;
\r
1159 //float min_time_per_range = 0.05 / range;
\r
1160 if(time_per_range < min_time_per_range)
\r
1162 time_per_range = min_time_per_range;
\r
1163 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1167 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1170 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1171 // Dampen the change a bit to kill oscillations
\r
1172 //wanted_range_change *= 0.9;
\r
1173 //wanted_range_change *= 0.75;
\r
1174 wanted_range_change *= 0.5;
\r
1175 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1177 // If needed range change is very small, just return
\r
1178 if(fabs(wanted_range_change) < 0.001)
\r
1180 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1184 new_range += wanted_range_change;
\r
1185 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1187 //float new_range_unclamped = new_range;
\r
1188 if(new_range < range_min)
\r
1189 new_range = range_min;
\r
1190 if(new_range > range_max)
\r
1191 new_range = range_max;
\r
1193 /*if(new_range != new_range_unclamped)
\r
1194 dstream<<", clamped to "<<new_range<<std::endl;
\r
1196 dstream<<std::endl;*/
\r
1198 draw_control.wanted_range = new_range;
\r
1200 range_old = new_range;
\r
1201 frametime_old = frametime;
\r
1204 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1205 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1206 Inventory *inventory)
\r
1208 InventoryList *mainlist = inventory->getList("main");
\r
1209 if(mainlist == NULL)
\r
1211 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1215 s32 padding = imgsize/12;
\r
1216 //s32 height = imgsize + padding*2;
\r
1217 s32 width = itemcount*(imgsize+padding*2);
\r
1219 // Position of upper left corner of bar
\r
1220 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1222 // Draw background color
\r
1223 /*core::rect<s32> barrect(0,0,width,height);
\r
1225 video::SColor bgcolor(255,128,128,128);
\r
1226 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1228 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1230 for(s32 i=0; i<itemcount; i++)
\r
1232 InventoryItem *item = mainlist->getItem(i);
\r
1234 core::rect<s32> rect = imgrect + pos
\r
1235 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1237 if(g_selected_item == i)
\r
1239 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1240 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1241 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1246 video::SColor bgcolor2(128,0,0,0);
\r
1247 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1252 drawInventoryItem(driver, font, item, rect, NULL);
\r
1258 video::ITexture *g_map_plot_texture = NULL;
\r
1259 float g_map_plot_texture_scale = 4;
\r
1261 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1267 core::dimension2d<u32> dim(640,480);
\r
1268 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1270 for(u32 y=0; y<dim.Height; y++)
\r
1271 for(u32 x=0; x<dim.Width; x++)
\r
1273 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1274 pf *= g_map_plot_texture_scale;
\r
1276 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1279 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1280 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1282 // Screen coordinates that are based on multiples of
\r
1283 // 1000/g_map_plot_texture_scale and never negative
\r
1284 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1285 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1286 // Offset to center of image
\r
1287 ux -= dim.Width/2;
\r
1288 uy -= dim.Height/2;
\r
1290 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1291 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1292 c.set(255, 255, 255, 255);
\r
1293 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1294 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1295 c.set(255, 160, 160, 160);
\r
1296 else if(h < WATER_LEVEL - 0.5) // Water
\r
1297 c.set(255, 50, 50, 255);
\r
1299 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1300 || (h < WATER_LEVEL + 2
\r
1301 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1305 h = 1.0 - exp(-h);
\r
1307 video::SColor c1(255,237,201,175);
\r
1308 //video::SColor c2(255,20,20,20);
\r
1309 video::SColor c2(255,150,0,0);
\r
1310 c = c2.getInterpolated(c1, h);
\r
1316 h = 1.0 - exp(-h);
\r
1318 video::SColor c1(255,110,185,90);
\r
1319 //video::SColor c2(255,20,20,20);
\r
1320 video::SColor c2(255,150,0,0);
\r
1321 c = c2.getInterpolated(c1, h);
\r
1326 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1330 h = 1.0 - exp(-h);
\r
1332 video::SColor c1(255,237,201,175);
\r
1333 //video::SColor c2(255,20,20,20);
\r
1334 video::SColor c2(255,150,0,0);
\r
1335 c = c2.getInterpolated(c1, h);
\r
1339 else if(h < WATER_LEVEL + 2
\r
1340 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1341 c.set(255, 237, 201, 175);
\r
1343 else if(h < WATER_LEVEL + 10)
\r
1344 c.set(255, 50, 150, 50); // Green
\r
1345 else if(h < WATER_LEVEL + 20)
\r
1346 c.set(255, 110, 185, 50); // Yellowish green
\r
1347 else if(h < WATER_LEVEL + 40)
\r
1348 c.set(255, 180, 210, 50); // Greenish yellow
\r
1349 else if(h < WATER_LEVEL + 60)
\r
1350 c.set(255, 220, 220, 50); // Yellow
\r
1351 else if(h < WATER_LEVEL + 80)
\r
1352 c.set(255, 200, 200, 110); // Yellowish white
\r
1353 else if(h < WATER_LEVEL + 100)
\r
1354 c.set(255, 190, 190, 190); // Grey
\r
1356 c.set(255, 255, 255, 255); // White
\r
1364 h = 1.0 - exp(-h);
\r
1366 video::SColor c1(255,200,200,50);
\r
1367 video::SColor c2(255,0,150,0);
\r
1368 c = c1.getInterpolated(c2, h);
\r
1370 /*u32 a = (u32)(h*255);
\r
1374 c.set(255, a, a, a);*/
\r
1378 if(h >= WATER_LEVEL - 0.5
\r
1379 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1381 video::SColor c1(255,237,201,175);
\r
1382 c = c.getInterpolated(c1, 0.5);
\r
1386 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1389 video::SColor c1(255,255,0,0);
\r
1390 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1393 img->setPixel(x, y, c);
\r
1395 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1397 assert(g_map_plot_texture);
\r
1408 ChatLine(const std::wstring &a_text):
\r
1414 std::wstring text;
\r
1417 // These are defined global so that they're not optimized too much.
\r
1418 // Can't change them to volatile.
\r
1423 std::string tempstring;
\r
1424 std::string tempstring2;
\r
1429 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1430 TimeTaker timer("Testing std::string speed");
\r
1431 const u32 jj = 10000;
\r
1432 for(u32 j=0; j<jj; j++)
\r
1436 const u32 ii = 10;
\r
1437 for(u32 i=0; i<ii; i++){
\r
1438 tempstring2 += "asd";
\r
1440 for(u32 i=0; i<ii+1; i++){
\r
1441 tempstring += "asd";
\r
1442 if(tempstring == tempstring2)
\r
1448 dstream<<"All of the following tests should take around 100ms each."
\r
1452 TimeTaker timer("Testing floating-point conversion speed");
\r
1454 for(u32 i=0; i<4000000; i++){
\r
1461 TimeTaker timer("Testing floating-point vector speed");
\r
1463 tempv3f1 = v3f(1,2,3);
\r
1464 tempv3f2 = v3f(4,5,6);
\r
1465 for(u32 i=0; i<10000000; i++){
\r
1466 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1467 tempv3f2 += v3f(7,8,9);
\r
1472 TimeTaker timer("Testing core::map speed");
\r
1474 core::map<v2s16, f32> map1;
\r
1477 for(s16 y=0; y<ii; y++){
\r
1478 for(s16 x=0; x<ii; x++){
\r
1479 map1.insert(v2s16(x,y), tempf);
\r
1483 for(s16 y=ii-1; y>=0; y--){
\r
1484 for(s16 x=0; x<ii; x++){
\r
1485 tempf = map1[v2s16(x,y)];
\r
1491 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1492 TimeTaker timer("Testing mutex speed");
\r
1505 // Do at least 10ms
\r
1506 while(timer.getTime() < 10);
\r
1508 u32 dtime = timer.stop();
\r
1509 u32 per_ms = n / dtime;
\r
1510 std::cout<<"Done. "<<dtime<<"ms, "
\r
1511 <<per_ms<<"/ms"<<std::endl;
\r
1515 int main(int argc, char *argv[])
\r
1518 Parse command line
\r
1521 // List all allowed options
\r
1522 core::map<std::string, ValueSpec> allowed_options;
\r
1523 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1524 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1525 "Run server directly"));
\r
1526 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1527 "Load configuration from specified file"));
\r
1528 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1529 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1530 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1531 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1532 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1533 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1535 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1537 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1539 Settings cmd_args;
\r
1541 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1543 if(ret == false || cmd_args.getFlag("help"))
\r
1545 dstream<<"Allowed options:"<<std::endl;
\r
1546 for(core::map<std::string, ValueSpec>::Iterator
\r
1547 i = allowed_options.getIterator();
\r
1548 i.atEnd() == false; i++)
\r
1550 dstream<<" --"<<i.getNode()->getKey();
\r
1551 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1556 dstream<<" <value>";
\r
1558 dstream<<std::endl;
\r
1560 if(i.getNode()->getValue().help != NULL)
\r
1562 dstream<<" "<<i.getNode()->getValue().help
\r
1567 return cmd_args.getFlag("help") ? 0 : 1;
\r
1571 Low-level initialization
\r
1574 bool disable_stderr = false;
\r
1576 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1577 disable_stderr = true;
\r
1580 // Initialize debug streams
\r
1581 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1582 // Initialize debug stacks
\r
1583 debug_stacks_init();
\r
1585 DSTACK(__FUNCTION_NAME);
\r
1587 porting::signal_handler_init();
\r
1588 bool &kill = *porting::signal_handler_killstatus();
\r
1590 porting::initializePaths();
\r
1591 // Create user data directory
\r
1592 fs::CreateDir(porting::path_userdata);
\r
1594 // C-style stuff initialization
\r
1595 initializeMaterialProperties();
\r
1598 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1600 // Print startup message
\r
1601 dstream<<DTIME<<"minetest-c55"
\r
1602 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1603 <<", "<<BUILD_INFO
\r
1607 Basic initialization
\r
1610 // Initialize default settings
\r
1611 set_default_settings();
\r
1613 // Set locale. This is for forcing '.' as the decimal point.
\r
1614 std::locale::global(std::locale("C"));
\r
1615 // This enables printing all characters in bitmap font
\r
1616 setlocale(LC_CTYPE, "en_US");
\r
1618 // Initialize sockets
\r
1620 atexit(sockets_cleanup);
\r
1630 // Path of configuration file in use
\r
1631 std::string configpath = "";
\r
1633 if(cmd_args.exists("config"))
\r
1635 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1638 dstream<<"Could not read configuration from \""
\r
1639 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1642 configpath = cmd_args.get("config");
\r
1646 core::array<std::string> filenames;
\r
1647 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1648 #ifdef RUN_IN_PLACE
\r
1649 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1652 for(u32 i=0; i<filenames.size(); i++)
\r
1654 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1657 configpath = filenames[i];
\r
1662 // If no path found, use the first one (menu creates the file)
\r
1663 if(configpath == "")
\r
1664 configpath = filenames[0];
\r
1667 // Initialize random seed
\r
1672 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1674 These are needed for unit tests at least.
\r
1677 // Initial call with g_texturesource not set.
\r
1684 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1685 || cmd_args.getFlag("enable-unittests") == true)
\r
1690 /*for(s16 y=-100; y<100; y++)
\r
1691 for(s16 x=-100; x<100; x++)
\r
1693 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1703 if(cmd_args.exists("port"))
\r
1704 port = cmd_args.getU16("port");
\r
1705 else if(cmd_args.exists("port"))
\r
1706 port = g_settings.getU16("port");
\r
1709 std::string map_dir = porting::path_userdata+"/map";
\r
1710 if(cmd_args.exists("map-dir"))
\r
1711 map_dir = cmd_args.get("map-dir");
\r
1712 else if(g_settings.exists("map-dir"))
\r
1713 map_dir = g_settings.get("map-dir");
\r
1715 // Run dedicated server if asked to
\r
1716 if(cmd_args.getFlag("server"))
\r
1718 DSTACK("Dedicated server branch");
\r
1721 Server server(map_dir.c_str());
\r
1722 server.start(port);
\r
1725 dedicated_server_loop(server, kill);
\r
1734 // Address to connect to
\r
1735 std::string address = "";
\r
1737 if(cmd_args.exists("address"))
\r
1739 address = cmd_args.get("address");
\r
1743 address = g_settings.get("address");
\r
1746 std::string playername = g_settings.get("name");
\r
1748 // Resolution selection
\r
1750 bool fullscreen = false;
\r
1751 u16 screenW = g_settings.getU16("screenW");
\r
1752 u16 screenH = g_settings.getU16("screenH");
\r
1754 // Determine driver
\r
1756 video::E_DRIVER_TYPE driverType;
\r
1758 std::string driverstring = g_settings.get("video_driver");
\r
1760 if(driverstring == "null")
\r
1761 driverType = video::EDT_NULL;
\r
1762 else if(driverstring == "software")
\r
1763 driverType = video::EDT_SOFTWARE;
\r
1764 else if(driverstring == "burningsvideo")
\r
1765 driverType = video::EDT_BURNINGSVIDEO;
\r
1766 else if(driverstring == "direct3d8")
\r
1767 driverType = video::EDT_DIRECT3D8;
\r
1768 else if(driverstring == "direct3d9")
\r
1769 driverType = video::EDT_DIRECT3D9;
\r
1770 else if(driverstring == "opengl")
\r
1771 driverType = video::EDT_OPENGL;
\r
1774 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1775 "to opengl"<<std::endl;
\r
1776 driverType = video::EDT_OPENGL;
\r
1779 // create device and exit if creation failed
\r
1781 MyEventReceiver receiver;
\r
1783 IrrlichtDevice *device;
\r
1784 device = createDevice(driverType,
\r
1785 core::dimension2d<u32>(screenW, screenH),
\r
1786 16, fullscreen, false, false, &receiver);
\r
1789 return 1; // could not create selected driver.
\r
1791 g_device = device;
\r
1792 g_irrlicht = new IrrlichtWrapper(device);
\r
1793 TextureSource *texturesource = new TextureSource(device);
\r
1794 g_texturesource = texturesource;
\r
1797 Speed tests (done after irrlicht is loaded to get timer)
\r
1799 if(cmd_args.getFlag("speedtests"))
\r
1801 dstream<<"Running speed tests"<<std::endl;
\r
1806 device->setResizable(true);
\r
1808 bool random_input = g_settings.getBool("random_input")
\r
1809 || cmd_args.getFlag("random-input");
\r
1811 g_input = new RandomInputHandler();
\r
1813 g_input = new RealInputHandler(device, &receiver);
\r
1816 Continue initialization
\r
1819 video::IVideoDriver* driver = device->getVideoDriver();
\r
1822 This changes the minimum allowed number of vertices in a VBO.
\r
1825 //driver->setMinHardwareBufferVertexCount(50);
\r
1827 scene::ISceneManager* smgr = device->getSceneManager();
\r
1829 guienv = device->getGUIEnvironment();
\r
1830 gui::IGUISkin* skin = guienv->getSkin();
\r
1831 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1833 skin->setFont(font);
\r
1835 dstream<<"WARNING: Font file was not found."
\r
1836 " Using default font."<<std::endl;
\r
1837 // If font was not found, this will get us one
\r
1838 font = skin->getFont();
\r
1841 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1842 dstream<<"text_height="<<text_height<<std::endl;
\r
1844 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1845 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1846 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1847 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1848 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1849 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1852 Preload some textures and stuff
\r
1855 init_content_inventory_texture_paths();
\r
1856 init_mapnode(); // Second call with g_texturesource set
\r
1864 We need some kind of a root node to be able to add
\r
1865 custom gui elements directly on the screen.
\r
1866 Otherwise they won't be automatically drawn.
\r
1868 guiroot = guienv->addStaticText(L"",
\r
1869 core::rect<s32>(0, 0, 10000, 10000));
\r
1871 // First line of debug text
\r
1872 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1874 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1876 // Second line of debug text
\r
1877 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1879 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1882 // At the middle of the screen
\r
1883 // Object infos are shown in this
\r
1884 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1886 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1890 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1892 core::rect<s32>(0,0,0,0),
\r
1893 false, false); // Disable word wrap as of now
\r
1895 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1896 core::list<ChatLine> chat_lines;
\r
1899 If an error occurs, this is set to something and the
\r
1900 menu-game loop is restarted. It is then displayed before
\r
1903 std::wstring error_message = L"";
\r
1908 while(g_device->run() && kill == false)
\r
1911 // This is used for catching disconnects
\r
1916 Out-of-game menu loop.
\r
1918 Loop quits when menu returns proper parameters.
\r
1920 while(kill == false)
\r
1922 // Cursor can be non-visible when coming from the game
\r
1923 device->getCursorControl()->setVisible(true);
\r
1924 // Some stuff are left to scene manager when coming from the game
\r
1925 // (map at least?)
\r
1927 // Reset or hide the debug gui texts
\r
1928 guitext->setText(L"Minetest-c55");
\r
1929 guitext2->setVisible(false);
\r
1930 guitext_info->setVisible(false);
\r
1931 guitext_chat->setVisible(false);
\r
1933 // Initialize menu data
\r
1934 MainMenuData menudata;
\r
1935 menudata.address = narrow_to_wide(address);
\r
1936 menudata.name = narrow_to_wide(playername);
\r
1937 menudata.port = narrow_to_wide(itos(port));
\r
1938 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1940 GUIMainMenu *menu =
\r
1941 new GUIMainMenu(guienv, guiroot, -1,
\r
1942 &g_menumgr, &menudata, &g_gamecallback);
\r
1943 menu->allowFocusRemoval(true);
\r
1945 if(error_message != L"")
\r
1947 GUIMessageMenu *menu2 =
\r
1948 new GUIMessageMenu(guienv, guiroot, -1,
\r
1949 &g_menumgr, error_message.c_str());
\r
1951 error_message = L"";
\r
1954 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1956 dstream<<"Created main menu"<<std::endl;
\r
1958 while(g_device->run() && kill == false)
\r
1960 // Run global IrrlichtWrapper's main thread processing stuff
\r
1961 g_irrlicht->Run();
\r
1963 if(menu->getStatus() == true)
\r
1966 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1967 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1968 guienv->drawAll();
\r
1969 driver->endScene();
\r
1972 // Break out of menu-game loop to shut down cleanly
\r
1973 if(g_device->run() == false || kill == true)
\r
1976 dstream<<"Dropping main menu"<<std::endl;
\r
1980 // Delete map if requested
\r
1981 if(menudata.delete_map)
\r
1983 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1985 error_message = L"Delete failed";
\r
1989 playername = wide_to_narrow(menudata.name);
\r
1990 address = wide_to_narrow(menudata.address);
\r
1991 port = stoi(wide_to_narrow(menudata.port));
\r
1992 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1994 // Check for valid parameters, restart menu if invalid.
\r
1995 if(playername == "")
\r
1997 error_message = L"Name required.";
\r
2002 g_settings.set("name", playername);
\r
2003 g_settings.set("address", address);
\r
2004 g_settings.set("port", itos(port));
\r
2005 // Update configuration file
\r
2006 if(configpath != "")
\r
2007 g_settings.updateConfigFile(configpath.c_str());
\r
2009 // Continue to game
\r
2013 // Break out of menu-game loop to shut down cleanly
\r
2014 if(g_device->run() == false)
\r
2018 Make a scope here so that the client and the server and other
\r
2019 stuff gets removed when disconnected or the irrlicht device
\r
2024 // This is set to true at the end of the scope
\r
2025 g_irrlicht->Shutdown(false);
\r
2028 Draw "Loading" screen
\r
2030 const wchar_t *text = L"Loading and connecting...";
\r
2031 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2032 core::vector2d<s32> textsize(300, text_height);
\r
2033 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2035 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2036 text, textrect, false, false);
\r
2037 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2039 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2040 guienv->drawAll();
\r
2041 driver->endScene();
\r
2043 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2047 SharedPtr will delete it when it goes out of scope.
\r
2049 SharedPtr<Server> server;
\r
2050 if(address == ""){
\r
2051 server = new Server(map_dir);
\r
2052 server->start(port);
\r
2059 Client client(device, playername.c_str(), draw_control);
\r
2061 g_client = &client;
\r
2063 Address connect_address(0,0,0,0, port);
\r
2066 //connect_address.Resolve("localhost");
\r
2067 connect_address.setAddress(127,0,0,1);
\r
2069 connect_address.Resolve(address.c_str());
\r
2071 catch(ResolveError &e)
\r
2073 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2075 error_message = L"Couldn't resolve address";
\r
2076 gui_loadingtext->remove();
\r
2080 dstream<<DTIME<<"Connecting to server at ";
\r
2081 connect_address.print(&dstream);
\r
2082 dstream<<std::endl;
\r
2083 client.connect(connect_address);
\r
2086 while(client.connectedAndInitialized() == false)
\r
2089 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2090 guienv->drawAll();
\r
2091 driver->endScene();
\r
2093 // Update client and server
\r
2097 if(server != NULL)
\r
2098 server->step(0.1);
\r
2104 catch(con::PeerNotFoundException &e)
\r
2106 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2108 error_message = L"Connection timed out.";
\r
2109 gui_loadingtext->remove();
\r
2116 /*scene::ISceneNode* skybox;
\r
2117 skybox = smgr->addSkyBoxSceneNode(
\r
2118 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2119 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2120 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2121 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2122 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2123 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2126 Create the camera node
\r
2129 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2130 0, // Camera parent
\r
2131 v3f(BS*100, BS*2, BS*100), // Look from
\r
2132 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2136 if(camera == NULL)
\r
2139 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2140 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2141 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2143 camera->setFOV(FOV_ANGLE);
\r
2145 // Just so big a value that everything rendered is visible
\r
2146 camera->setFarValue(100000*BS);
\r
2149 Lighting test code. Doesn't quite work this way.
\r
2150 The CPU-computed lighting is good.
\r
2154 smgr->addLightSceneNode(NULL,
\r
2155 v3f(0, BS*1000000, 0),
\r
2156 video::SColorf(0.3,0.3,0.3),
\r
2159 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2161 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2162 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2165 f32 camera_yaw = 0; // "right/left"
\r
2166 f32 camera_pitch = 0; // "up/down"
\r
2172 gui_loadingtext->remove();
\r
2175 Add some gui stuff
\r
2178 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2179 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2180 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2181 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2183 // Test the text input system
\r
2184 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2186 /*GUIMessageMenu *menu =
\r
2187 new GUIMessageMenu(guienv, guiroot, -1,
\r
2192 // Launch pause menu
\r
2193 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2194 &g_menumgr))->drop();
\r
2197 guitext2->setVisible(true);
\r
2198 guitext_info->setVisible(true);
\r
2199 guitext_chat->setVisible(true);
\r
2201 //s32 guitext_chat_pad_bottom = 70;
\r
2203 v2u32 screensize(0,0);
\r
2204 v2u32 last_screensize(0,0);
\r
2207 Some statistics are collected in these
\r
2210 u32 beginscenetime = 0;
\r
2211 u32 scenetime = 0;
\r
2212 u32 endscenetime = 0;
\r
2215 //throw con::PeerNotFoundException("lol");
\r
2217 core::list<float> frametime_log;
\r
2223 bool first_loop_after_window_activation = true;
\r
2225 // Time is in milliseconds
\r
2226 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2227 // NOTE: So we have to use getTime() and call run()s between them
\r
2228 u32 lasttime = device->getTimer()->getTime();
\r
2230 while(device->run() && kill == false)
\r
2232 if(g_disconnect_requested)
\r
2234 g_disconnect_requested = false;
\r
2239 Run global IrrlichtWrapper's main thread processing stuff
\r
2241 g_irrlicht->Run();
\r
2244 Process TextureSource's queue
\r
2246 texturesource->processQueue();
\r
2249 Random calculations
\r
2251 last_screensize = screensize;
\r
2252 screensize = driver->getScreenSize();
\r
2253 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2254 //bool screensize_changed = screensize != last_screensize;
\r
2256 // Hilight boxes collected during the loop and displayed
\r
2257 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2260 std::wstring infotext;
\r
2262 // When screen size changes, update positions and sizes of stuff
\r
2263 /*if(screensize_changed)
\r
2265 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2266 quick_inventory->updatePosition(pos);
\r
2269 //TimeTaker //timer1("//timer1");
\r
2271 // Time of frame without fps limit
\r
2275 // not using getRealTime is necessary for wine
\r
2276 u32 time = device->getTimer()->getTime();
\r
2277 if(time > lasttime)
\r
2278 busytime_u32 = time - lasttime;
\r
2281 busytime = busytime_u32 / 1000.0;
\r
2284 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2286 // Necessary for device->getTimer()->getTime()
\r
2293 updateViewingRange(busytime, &client);
\r
2300 float fps_max = g_settings.getFloat("fps_max");
\r
2301 u32 frametime_min = 1000./fps_max;
\r
2303 if(busytime_u32 < frametime_min)
\r
2305 u32 sleeptime = frametime_min - busytime_u32;
\r
2306 device->sleep(sleeptime);
\r
2310 // Necessary for device->getTimer()->getTime()
\r
2314 Time difference calculation
\r
2316 f32 dtime; // in seconds
\r
2318 u32 time = device->getTimer()->getTime();
\r
2319 if(time > lasttime)
\r
2320 dtime = (time - lasttime) / 1000.0;
\r
2326 Log frametime for visualization
\r
2328 frametime_log.push_back(dtime);
\r
2329 if(frametime_log.size() > 100)
\r
2331 core::list<float>::Iterator i = frametime_log.begin();
\r
2332 frametime_log.erase(i);
\r
2336 Visualize frametime in terminal
\r
2338 /*for(u32 i=0; i<dtime*400; i++)
\r
2340 std::cout<<std::endl;*/
\r
2343 Time average and jitter calculation
\r
2346 static f32 dtime_avg1 = 0.0;
\r
2347 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2348 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2350 static f32 dtime_jitter1_max_sample = 0.0;
\r
2351 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2353 static f32 jitter1_max = 0.0;
\r
2354 static f32 counter = 0.0;
\r
2355 if(dtime_jitter1 > jitter1_max)
\r
2356 jitter1_max = dtime_jitter1;
\r
2361 dtime_jitter1_max_sample = jitter1_max;
\r
2362 dtime_jitter1_max_fraction
\r
2363 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2364 jitter1_max = 0.0;
\r
2369 Busytime average and jitter calculation
\r
2372 static f32 busytime_avg1 = 0.0;
\r
2373 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2374 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2376 static f32 busytime_jitter1_max_sample = 0.0;
\r
2377 static f32 busytime_jitter1_min_sample = 0.0;
\r
2379 static f32 jitter1_max = 0.0;
\r
2380 static f32 jitter1_min = 0.0;
\r
2381 static f32 counter = 0.0;
\r
2382 if(busytime_jitter1 > jitter1_max)
\r
2383 jitter1_max = busytime_jitter1;
\r
2384 if(busytime_jitter1 < jitter1_min)
\r
2385 jitter1_min = busytime_jitter1;
\r
2387 if(counter > 0.0){
\r
2389 busytime_jitter1_max_sample = jitter1_max;
\r
2390 busytime_jitter1_min_sample = jitter1_min;
\r
2391 jitter1_max = 0.0;
\r
2392 jitter1_min = 0.0;
\r
2397 Debug info for client
\r
2400 static float counter = 0.0;
\r
2405 client.printDebugInfo(std::cout);
\r
2410 Input handler step()
\r
2412 g_input->step(dtime);
\r
2419 Player speed control
\r
2428 bool a_superspeed,
\r
2432 PlayerControl control(
\r
2433 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2434 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2435 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2436 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2437 g_input->isKeyDown(irr::KEY_SPACE),
\r
2438 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2439 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2440 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2444 client.setPlayerControl(control);
\r
2448 Process environment
\r
2452 //TimeTaker timer("client.step(dtime)");
\r
2453 client.step(dtime);
\r
2454 //client.step(dtime_avg1);
\r
2457 if(server != NULL)
\r
2459 //TimeTaker timer("server->step(dtime)");
\r
2460 server->step(dtime);
\r
2463 v3f player_position = client.getPlayerPosition();
\r
2465 //TimeTaker //timer2("//timer2");
\r
2468 Mouse and camera control
\r
2471 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2474 device->getCursorControl()->setVisible(false);
\r
2476 if(first_loop_after_window_activation){
\r
2477 //std::cout<<"window active, first loop"<<std::endl;
\r
2478 first_loop_after_window_activation = false;
\r
2481 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2482 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2483 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2484 camera_yaw -= dx*0.2;
\r
2485 camera_pitch += dy*0.2;
\r
2486 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2487 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2489 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2492 device->getCursorControl()->setVisible(true);
\r
2494 //std::cout<<"window inactive"<<std::endl;
\r
2495 first_loop_after_window_activation = true;
\r
2498 camera_yaw = wrapDegrees(camera_yaw);
\r
2499 camera_pitch = wrapDegrees(camera_pitch);
\r
2501 v3f camera_direction = v3f(0,0,1);
\r
2502 camera_direction.rotateYZBy(camera_pitch);
\r
2503 camera_direction.rotateXZBy(camera_yaw);
\r
2505 // This is at the height of the eyes of the current figure
\r
2506 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2507 // This is more like in minecraft
\r
2508 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2510 camera->setPosition(camera_position);
\r
2511 // *100.0 helps in large map coordinates
\r
2512 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2514 if(FIELD_OF_VIEW_TEST){
\r
2515 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2518 //TimeTaker timer("client.updateCamera");
\r
2519 client.updateCamera(camera_position, camera_direction);
\r
2523 //TimeTaker //timer3("//timer3");
\r
2526 Calculate what block is the crosshair pointing to
\r
2529 //u32 t1 = device->getTimer()->getRealTime();
\r
2531 //f32 d = 4; // max. distance
\r
2532 f32 d = 4; // max. distance
\r
2533 core::line3d<f32> shootline(camera_position,
\r
2534 camera_position + camera_direction * BS * (d+1));
\r
2536 MapBlockObject *selected_object = client.getSelectedObject
\r
2537 (d*BS, camera_position, shootline);
\r
2539 ClientActiveObject *selected_active_object
\r
2540 = client.getSelectedActiveObject
\r
2541 (d*BS, camera_position, shootline);
\r
2543 if(selected_object != NULL)
\r
2545 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2547 core::aabbox3d<f32> box_on_map
\r
2548 = selected_object->getSelectionBoxOnMap();
\r
2550 hilightboxes.push_back(box_on_map);
\r
2552 infotext = narrow_to_wide(selected_object->infoText());
\r
2554 if(g_input->getLeftClicked())
\r
2556 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2557 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2558 selected_object->getId(), g_selected_item);
\r
2560 else if(g_input->getRightClicked())
\r
2562 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2564 Check if we want to modify the object ourselves
\r
2566 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2568 dstream<<"Sign object right-clicked"<<std::endl;
\r
2570 if(random_input == false)
\r
2572 // Get a new text for it
\r
2574 TextDest *dest = new TextDestSign(
\r
2575 selected_object->getBlock()->getPos(),
\r
2576 selected_object->getId(),
\r
2579 SignObject *sign_object = (SignObject*)selected_object;
\r
2581 std::wstring wtext =
\r
2582 narrow_to_wide(sign_object->getText());
\r
2584 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2590 Otherwise pass the event to the server as-is
\r
2594 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2595 selected_object->getId(), g_selected_item);
\r
2599 else if(selected_active_object != NULL)
\r
2601 //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
\r
2603 core::aabbox3d<f32> *selection_box
\r
2604 = selected_active_object->getSelectionBox();
\r
2605 // Box should exist because object was returned in the
\r
2607 assert(selection_box);
\r
2609 v3f pos = selected_active_object->getPosition();
\r
2611 core::aabbox3d<f32> box_on_map(
\r
2612 selection_box->MinEdge + pos,
\r
2613 selection_box->MaxEdge + pos
\r
2616 hilightboxes.push_back(box_on_map);
\r
2618 //infotext = narrow_to_wide("A ClientActiveObject");
\r
2619 infotext = narrow_to_wide(selected_active_object->infoText());
\r
2621 if(g_input->getLeftClicked())
\r
2623 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2624 client.clickActiveObject(0,
\r
2625 selected_active_object->getId(), g_selected_item);
\r
2627 else if(g_input->getRightClicked())
\r
2629 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2632 Check if we want to modify the object ourselves
\r
2634 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2638 Otherwise pass the event to the server as-is
\r
2642 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2643 selected_object->getId(), g_selected_item);
\r
2648 else // selected_object == NULL
\r
2652 Find out which node we are pointing at
\r
2655 bool nodefound = false;
\r
2657 v3s16 neighbourpos;
\r
2658 core::aabbox3d<f32> nodehilightbox;
\r
2659 f32 mindistance = BS * 1001;
\r
2661 v3s16 pos_i = floatToInt(player_position, BS);
\r
2663 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2667 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2668 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2669 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2670 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2671 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2672 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2674 for(s16 y = ystart; y <= yend; y++)
\r
2675 for(s16 z = zstart; z <= zend; z++)
\r
2676 for(s16 x = xstart; x <= xend; x++)
\r
2681 n = client.getNode(v3s16(x,y,z));
\r
2682 if(content_pointable(n.d) == false)
\r
2685 catch(InvalidPositionException &e)
\r
2691 v3f npf = intToFloat(np, BS);
\r
2696 v3s16(0,0,1), // back
\r
2697 v3s16(0,1,0), // top
\r
2698 v3s16(1,0,0), // right
\r
2699 v3s16(0,0,-1), // front
\r
2700 v3s16(0,-1,0), // bottom
\r
2701 v3s16(-1,0,0), // left
\r
2707 if(n.d == CONTENT_TORCH)
\r
2709 v3s16 dir = unpackDir(n.dir);
\r
2710 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2711 dir_f *= BS/2 - BS/6 - BS/20;
\r
2712 v3f cpf = npf + dir_f;
\r
2713 f32 distance = (cpf - camera_position).getLength();
\r
2715 core::aabbox3d<f32> box;
\r
2718 if(dir == v3s16(0,-1,0))
\r
2720 box = core::aabbox3d<f32>(
\r
2721 npf - v3f(BS/6, BS/2, BS/6),
\r
2722 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2726 else if(dir == v3s16(0,1,0))
\r
2728 box = core::aabbox3d<f32>(
\r
2729 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2730 npf + v3f(BS/6, BS/2, BS/6)
\r
2736 box = core::aabbox3d<f32>(
\r
2737 cpf - v3f(BS/6, BS/3, BS/6),
\r
2738 cpf + v3f(BS/6, BS/3, BS/6)
\r
2742 if(distance < mindistance)
\r
2744 if(box.intersectsWithLine(shootline))
\r
2748 neighbourpos = np;
\r
2749 mindistance = distance;
\r
2750 nodehilightbox = box;
\r
2754 else if(n.d == CONTENT_SIGN_WALL)
\r
2756 v3s16 dir = unpackDir(n.dir);
\r
2757 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2758 dir_f *= BS/2 - BS/6 - BS/20;
\r
2759 v3f cpf = npf + dir_f;
\r
2760 f32 distance = (cpf - camera_position).getLength();
\r
2764 v3f(BS*0.42,-BS*0.35,-BS*0.4),
\r
2765 v3f(BS*0.49, BS*0.35, BS*0.4),
\r
2768 for(s32 i=0; i<2; i++)
\r
2770 if(dir == v3s16(1,0,0))
\r
2771 vertices[i].rotateXZBy(0);
\r
2772 if(dir == v3s16(-1,0,0))
\r
2773 vertices[i].rotateXZBy(180);
\r
2774 if(dir == v3s16(0,0,1))
\r
2775 vertices[i].rotateXZBy(90);
\r
2776 if(dir == v3s16(0,0,-1))
\r
2777 vertices[i].rotateXZBy(-90);
\r
2778 if(dir == v3s16(0,-1,0))
\r
2779 vertices[i].rotateXYBy(-90);
\r
2780 if(dir == v3s16(0,1,0))
\r
2781 vertices[i].rotateXYBy(90);
\r
2783 vertices[i] += npf;
\r
2786 core::aabbox3d<f32> box;
\r
2788 box = core::aabbox3d<f32>(vertices[0]);
\r
2789 box.addInternalPoint(vertices[1]);
\r
2791 if(distance < mindistance)
\r
2793 if(box.intersectsWithLine(shootline))
\r
2797 neighbourpos = np;
\r
2798 mindistance = distance;
\r
2799 nodehilightbox = box;
\r
2808 for(u16 i=0; i<6; i++)
\r
2810 v3f dir_f = v3f(dirs[i].X,
\r
2811 dirs[i].Y, dirs[i].Z);
\r
2812 v3f centerpoint = npf + dir_f * BS/2;
\r
2814 (centerpoint - camera_position).getLength();
\r
2816 if(distance < mindistance)
\r
2818 core::CMatrix4<f32> m;
\r
2819 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2821 // This is the back face
\r
2822 v3f corners[2] = {
\r
2823 v3f(BS/2, BS/2, BS/2),
\r
2824 v3f(-BS/2, -BS/2, BS/2+d)
\r
2827 for(u16 j=0; j<2; j++)
\r
2829 m.rotateVect(corners[j]);
\r
2830 corners[j] += npf;
\r
2833 core::aabbox3d<f32> facebox(corners[0]);
\r
2834 facebox.addInternalPoint(corners[1]);
\r
2836 if(facebox.intersectsWithLine(shootline))
\r
2840 neighbourpos = np + dirs[i];
\r
2841 mindistance = distance;
\r
2843 //nodehilightbox = facebox;
\r
2845 const float d = 0.502;
\r
2846 core::aabbox3d<f32> nodebox
\r
2847 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2848 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2849 nodebox.MinEdge += nodepos_f;
\r
2850 nodebox.MaxEdge += nodepos_f;
\r
2851 nodehilightbox = nodebox;
\r
2853 } // if distance < mindistance
\r
2855 } // regular block
\r
2858 static float nodig_delay_counter = 0.0;
\r
2862 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2864 static float dig_time = 0.0;
\r
2865 static u16 dig_index = 0;
\r
2868 Visualize selection
\r
2871 hilightboxes.push_back(nodehilightbox);
\r
2874 Check information text of node
\r
2877 NodeMetadata *meta = client.getNodeMetadata(nodepos);
\r
2880 infotext = narrow_to_wide(meta->infoText());
\r
2883 //MapNode node = client.getNode(nodepos);
\r
2889 if(g_input->getLeftReleased())
\r
2891 client.clearTempMod(nodepos);
\r
2895 if(nodig_delay_counter > 0.0)
\r
2897 nodig_delay_counter -= dtime;
\r
2901 if(nodepos != nodepos_old)
\r
2903 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2904 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2906 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2908 client.clearTempMod(nodepos_old);
\r
2913 if(g_input->getLeftClicked() ||
\r
2914 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2916 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2917 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2919 if(g_input->getLeftClicked())
\r
2921 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2923 if(g_input->getLeftState())
\r
2925 MapNode n = client.getNode(nodepos);
\r
2927 // Get tool name. Default is "" = bare hands
\r
2928 std::string toolname = "";
\r
2929 InventoryList *mlist = local_inventory.getList("main");
\r
2932 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2933 if(item && (std::string)item->getName() == "ToolItem")
\r
2935 ToolItem *titem = (ToolItem*)item;
\r
2936 toolname = titem->getToolName();
\r
2940 // Get digging properties for material and tool
\r
2941 u8 material = n.d;
\r
2942 DiggingProperties prop =
\r
2943 getDiggingProperties(material, toolname);
\r
2945 float dig_time_complete = 0.0;
\r
2947 if(prop.diggable == false)
\r
2949 /*dstream<<"Material "<<(int)material
\r
2950 <<" not diggable with \""
\r
2951 <<toolname<<"\""<<std::endl;*/
\r
2952 // I guess nobody will wait for this long
\r
2953 dig_time_complete = 10000000.0;
\r
2957 dig_time_complete = prop.time;
\r
2960 if(dig_time_complete >= 0.001)
\r
2962 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2963 * dig_time/dig_time_complete);
\r
2965 // This is for torches
\r
2968 dig_index = CRACK_ANIMATION_LENGTH;
\r
2971 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2973 //TimeTaker timer("client.setTempMod");
\r
2974 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2975 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2979 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2980 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2981 client.clearTempMod(nodepos);
\r
2982 client.removeNode(nodepos);
\r
2986 nodig_delay_counter = dig_time_complete
\r
2987 / (float)CRACK_ANIMATION_LENGTH;
\r
2989 // We don't want a corresponding delay to
\r
2990 // very time consuming nodes
\r
2991 if(nodig_delay_counter > 0.5)
\r
2993 nodig_delay_counter = 0.5;
\r
2995 // We want a slight delay to very little
\r
2996 // time consuming nodes
\r
2997 float mindelay = 0.15;
\r
2998 if(nodig_delay_counter < mindelay)
\r
3000 nodig_delay_counter = mindelay;
\r
3004 dig_time += dtime;
\r
3008 if(g_input->getRightClicked())
\r
3010 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
3012 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
\r
3014 dstream<<"Sign node right-clicked"<<std::endl;
\r
3016 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
\r
3018 // Get a new text for it
\r
3020 TextDest *dest = new TextDestSignNode(nodepos, &client);
\r
3022 std::wstring wtext =
\r
3023 narrow_to_wide(signmeta->getText());
\r
3025 (new GUITextInputMenu(guienv, guiroot, -1,
\r
3029 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
\r
3031 dstream<<"Chest node right-clicked"<<std::endl;
\r
3033 //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
\r
3035 std::string chest_inv_id;
\r
3036 chest_inv_id += "nodemeta:";
\r
3037 chest_inv_id += itos(nodepos.X);
\r
3038 chest_inv_id += ",";
\r
3039 chest_inv_id += itos(nodepos.Y);
\r
3040 chest_inv_id += ",";
\r
3041 chest_inv_id += itos(nodepos.Z);
\r
3043 GUIInventoryMenu *menu =
\r
3044 new GUIInventoryMenu(guienv, guiroot, -1,
\r
3045 &g_menumgr, v2s16(8,9),
\r
3046 g_client->getInventoryContext(),
\r
3049 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
3051 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3052 "list", chest_inv_id, "0",
\r
3053 v2s32(0, 0), v2s32(8, 4)));
\r
3054 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3055 "list", "current_player", "main",
\r
3056 v2s32(0, 5), v2s32(8, 4)));
\r
3058 menu->setDrawSpec(draw_spec);
\r
3063 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
\r
3065 dstream<<"Furnace node right-clicked"<<std::endl;
\r
3067 GUIFurnaceMenu *menu =
\r
3068 new GUIFurnaceMenu(guienv, guiroot, -1,
\r
3069 &g_menumgr, nodepos, g_client);
\r
3076 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
3080 nodepos_old = nodepos;
\r
3085 } // selected_object == NULL
\r
3087 g_input->resetLeftClicked();
\r
3088 g_input->resetRightClicked();
\r
3090 if(g_input->getLeftReleased())
\r
3092 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
3094 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
3096 if(g_input->getRightReleased())
\r
3098 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
3102 g_input->resetLeftReleased();
\r
3103 g_input->resetRightReleased();
\r
3106 Calculate stuff for drawing
\r
3109 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
3111 u32 daynight_ratio = client.getDayNightRatio();
\r
3112 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
3113 video::SColor bgcolor = video::SColor(
\r
3115 skycolor.getRed() * l / 255,
\r
3116 skycolor.getGreen() * l / 255,
\r
3117 skycolor.getBlue() * l / 255);
\r
3123 if(g_settings.getBool("enable_fog") == true)
\r
3125 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
3126 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
3127 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
3128 if(draw_control.range_all)
\r
3129 range = 100000*BS;
\r
3133 video::EFT_FOG_LINEAR,
\r
3137 false, // pixel fog
\r
3138 false // range fog
\r
3145 video::EFT_FOG_LINEAR,
\r
3149 false, // pixel fog
\r
3150 false // range fog
\r
3156 Update gui stuff (0ms)
\r
3159 //TimeTaker guiupdatetimer("Gui updating");
\r
3162 static float drawtime_avg = 0;
\r
3163 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
3164 static float beginscenetime_avg = 0;
\r
3165 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
3166 static float scenetime_avg = 0;
\r
3167 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
3168 static float endscenetime_avg = 0;
\r
3169 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3171 char temptext[300];
\r
3172 snprintf(temptext, 300, "Minetest-c55 ("
\r
3174 ", R: range_all=%i"
\r
3176 " drawtime=%.0f, beginscenetime=%.0f"
\r
3177 ", scenetime=%.0f, endscenetime=%.0f",
\r
3179 draw_control.range_all,
\r
3181 beginscenetime_avg,
\r
3186 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3190 char temptext[300];
\r
3191 snprintf(temptext, 300,
\r
3192 "(% .1f, % .1f, % .1f)"
\r
3193 " (% .3f < btime_jitter < % .3f"
\r
3194 ", dtime_jitter = % .1f %%"
\r
3195 ", v_range = %.1f)",
\r
3196 player_position.X/BS,
\r
3197 player_position.Y/BS,
\r
3198 player_position.Z/BS,
\r
3199 busytime_jitter1_min_sample,
\r
3200 busytime_jitter1_max_sample,
\r
3201 dtime_jitter1_max_fraction * 100.0,
\r
3202 draw_control.wanted_range
\r
3205 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3209 guitext_info->setText(infotext.c_str());
\r
3213 Get chat messages from client
\r
3216 // Get new messages
\r
3217 std::wstring message;
\r
3218 while(client.getChatMessage(message))
\r
3220 chat_lines.push_back(ChatLine(message));
\r
3221 /*if(chat_lines.size() > 6)
\r
3223 core::list<ChatLine>::Iterator
\r
3224 i = chat_lines.begin();
\r
3225 chat_lines.erase(i);
\r
3228 // Append them to form the whole static text and throw
\r
3229 // it to the gui element
\r
3230 std::wstring whole;
\r
3231 // This will correspond to the line number counted from
\r
3232 // top to bottom, from size-1 to 0
\r
3233 s16 line_number = chat_lines.size();
\r
3234 // Count of messages to be removed from the top
\r
3235 u16 to_be_removed_count = 0;
\r
3236 for(core::list<ChatLine>::Iterator
\r
3237 i = chat_lines.begin();
\r
3238 i != chat_lines.end(); i++)
\r
3240 // After this, line number is valid for this loop
\r
3243 (*i).age += dtime;
\r
3245 This results in a maximum age of 60*6 to the
\r
3246 lowermost line and a maximum of 6 lines
\r
3248 float allowed_age = (6-line_number) * 60.0;
\r
3250 if((*i).age > allowed_age)
\r
3252 to_be_removed_count++;
\r
3255 whole += (*i).text + L'\n';
\r
3257 for(u16 i=0; i<to_be_removed_count; i++)
\r
3259 core::list<ChatLine>::Iterator
\r
3260 it = chat_lines.begin();
\r
3261 chat_lines.erase(it);
\r
3263 guitext_chat->setText(whole.c_str());
\r
3265 // Update gui element size and position
\r
3267 /*core::rect<s32> rect(
\r
3269 screensize.Y - guitext_chat_pad_bottom
\r
3270 - text_height*chat_lines.size(),
\r
3271 screensize.X - 10,
\r
3272 screensize.Y - guitext_chat_pad_bottom
\r
3274 core::rect<s32> rect(
\r
3277 screensize.X - 10,
\r
3278 50 + text_height*chat_lines.size()
\r
3281 guitext_chat->setRelativePosition(rect);
\r
3283 if(chat_lines.size() == 0)
\r
3284 guitext_chat->setVisible(false);
\r
3286 guitext_chat->setVisible(true);
\r
3293 static u16 old_selected_item = 65535;
\r
3294 if(client.getLocalInventoryUpdated()
\r
3295 || g_selected_item != old_selected_item)
\r
3297 old_selected_item = g_selected_item;
\r
3298 //std::cout<<"Updating local inventory"<<std::endl;
\r
3299 client.getLocalInventory(local_inventory);
\r
3303 Send actions returned by the inventory menu
\r
3305 while(inventory_action_queue.size() != 0)
\r
3307 InventoryAction *a = inventory_action_queue.pop_front();
\r
3309 client.sendInventoryAction(a);
\r
3318 TimeTaker drawtimer("Drawing");
\r
3322 TimeTaker timer("beginScene");
\r
3323 driver->beginScene(true, true, bgcolor);
\r
3324 //driver->beginScene(false, true, bgcolor);
\r
3325 beginscenetime = timer.stop(true);
\r
3330 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3333 TimeTaker timer("smgr");
\r
3335 scenetime = timer.stop(true);
\r
3339 //TimeTaker timer9("auxiliary drawings");
\r
3343 //TimeTaker //timer10("//timer10");
\r
3345 video::SMaterial m;
\r
3346 //m.Thickness = 10;
\r
3348 m.Lighting = false;
\r
3349 driver->setMaterial(m);
\r
3351 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3353 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3354 i != hilightboxes.end(); i++)
\r
3356 /*std::cout<<"hilightbox min="
\r
3357 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3359 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3361 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3367 if(g_settings.getBool("frametime_graph") == true)
\r
3370 for(core::list<float>::Iterator
\r
3371 i = frametime_log.begin();
\r
3372 i != frametime_log.end();
\r
3375 driver->draw2DLine(v2s32(x,50),
\r
3376 v2s32(x,50+(*i)*1000),
\r
3377 video::SColor(255,255,255,255));
\r
3385 if(g_show_map_plot && g_map_plot_texture)
\r
3387 core::dimension2d<u32> drawdim(640,480);
\r
3388 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3390 (screensize.X-drawdim.Width)/2,
\r
3391 (screensize.Y-drawdim.Height)/2
\r
3393 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3394 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3400 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3401 displaycenter + core::vector2d<s32>(10,0),
\r
3402 video::SColor(255,255,255,255));
\r
3403 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3404 displaycenter + core::vector2d<s32>(0,10),
\r
3405 video::SColor(255,255,255,255));
\r
3410 //TimeTaker //timer11("//timer11");
\r
3416 guienv->drawAll();
\r
3422 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3423 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3428 TimeTaker timer("endScene");
\r
3429 driver->endScene();
\r
3430 endscenetime = timer.stop(true);
\r
3433 drawtime = drawtimer.stop(true);
\r
3441 Refresh map plot if player has moved considerably
\r
3443 if(g_refresh_map_plot)
\r
3445 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3446 v3f p = client.getPlayerPosition() / BS;
\r
3447 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3449 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3450 old_player_pos = p;
\r
3452 g_refresh_map_plot = false;
\r
3456 static s16 lastFPS = 0;
\r
3457 //u16 fps = driver->getFPS();
\r
3458 u16 fps = (1.0/dtime_avg1);
\r
3460 if (lastFPS != fps)
\r
3462 core::stringw str = L"Minetest [";
\r
3463 str += driver->getName();
\r
3467 device->setWindowCaption(str.c_str());
\r
3473 device->yield();*/
\r
3476 //delete quick_inventory;
\r
3479 Disable texture fetches and other stuff that is queued
\r
3480 to be processed by the main loop.
\r
3482 This has to be done before client goes out of scope.
\r
3484 g_irrlicht->Shutdown(true);
\r
3486 } // client and server are deleted at this point
\r
3489 catch(con::PeerNotFoundException &e)
\r
3491 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3492 error_message = L"Connection timed out.";
\r
3495 } // Menu-game loop
\r
3500 In the end, delete the Irrlicht device.
\r
3505 Update configuration file
\r
3507 /*if(configpath != "")
\r
3509 g_settings.updateConfigFile(configpath.c_str());
\r
3512 END_DEBUG_EXCEPTION_HANDLER
\r
3514 debugstreams_deinit();
\r