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 - Breaking weak structures
\r
115 - This can probably be accomplished in the same way as grass
\r
116 - Player health points
\r
117 - When player dies, throw items on map (needs better item-on-map
\r
119 - Cobble to get mossy if near water
\r
120 - More slots in furnace source list, so that multiple ingredients
\r
127 Build system / running:
\r
128 -----------------------
\r
130 Networking and serialization:
\r
131 -----------------------------
\r
133 TODO: Get rid of GotSplitPacketException
\r
138 TODO: Configuration menu, at least for keys
\r
143 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
145 - That is >500 vertices
\r
146 - This is not easy; all the MapBlocks close to the player would
\r
147 still need to be drawn separately and combining the blocks
\r
148 would have to happen in a background thread
\r
150 SUGG: Make fetching sector's blocks more efficient when rendering
\r
151 sectors that have very large amounts of blocks (on client)
\r
152 - Is this necessary at all?
\r
154 TODO: Flowing water animation
\r
156 SUGG: Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
\r
158 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
159 animating them is easier.
\r
161 SUGG: Option for enabling proper alpha channel for textures
\r
169 TODO: Remove IrrlichtWrapper
\r
171 TODO: Untie client network operations from framerate
\r
172 - Needs some input queues or something
\r
173 - This won't give much performance boost because calculating block
\r
174 meshes takes so long
\r
176 SUGG: Make morning and evening transition more smooth and maybe shorter
\r
178 TODO: Don't update all meshes always on single node changes, but
\r
179 check which ones should be updated
\r
180 - implement Map::updateNodeMeshes() and the usage of it
\r
181 - It will give almost always a 4x boost in mesh update performance.
\r
186 SUGG: Make an option to the server to disable building and digging near
\r
187 the starting position
\r
189 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
191 * Fix the problem with the server constantly saving one or a few
\r
192 blocks? List the first saved block, maybe it explains.
\r
193 - It is probably caused by oscillating water
\r
194 * Make a small history check to transformLiquids to detect and log
\r
195 continuous oscillations, in such detail that they can be fixed.
\r
197 FIXME: If something is removed from craftresult with a right click,
\r
198 it is only possible to get one item from it should give 4
\r
203 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
205 SUGG: MovingObject::move and Player::move are basically the same.
\r
207 - NOTE: Player::move is more up-to-date.
\r
212 TODO: Mineral and ground material properties
\r
213 - This way mineral ground toughness can be calculated with just
\r
214 some formula, as well as tool strengths
\r
216 TODO: Flowing water to actually contain flow direction information
\r
218 SUGG: Erosion simulation at map generation time
\r
219 - Simulate water flows, which would carve out dirt fast and
\r
220 then turn stone into gravel and sand and relocate it.
\r
221 - How about relocating minerals, too? Coal and gold in
\r
222 downstream sand and gravel would be kind of cool
\r
223 - This would need a better way of handling minerals, mainly
\r
224 to have mineral content as a separate field. the first
\r
225 parameter field is free for this.
\r
226 - Simulate rock falling from cliffs when water has removed
\r
227 enough solid rock from the bottom
\r
230 * only_from_disk might not work anymore - check and fix it.
\r
231 * Make the generator to run in background and not blocking block
\r
232 placement and transfer
\r
233 * Possibly add some kind of erosion and other stuff
\r
234 * Better water generation (spread it to underwater caverns but don't
\r
235 fill dungeons that don't touch big water masses)
\r
236 * When generating a chunk and the neighboring chunk doesn't have mud
\r
237 and stuff yet and the ground is fairly flat, the mud will flow to
\r
238 the other chunk making nasty straight walls when the other chunk
\r
239 is generated. Fix it.
\r
243 * Make an "environment metafile" to store at least time of day
\r
244 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
245 - Or maybe move content_features to material.{h,cpp}?
\r
247 Make a system for pregenerating quick information for mapblocks, so
\r
248 that the client can show them as cubes before they are actually sent
\r
251 Making it more portable:
\r
252 ------------------------
\r
253 * Some MSVC: std::sto* are defined without a namespace and collide
\r
254 with the ones in utility.h
\r
256 ======================================================================
\r
261 Setting this to 1 enables a special camera mode that forces
\r
262 the renderers to think that the camera statically points from
\r
263 the starting place to a static direction.
\r
265 This allows one to move around with the player and see what
\r
266 is actually drawn behind solid things and behind the player.
\r
268 #define FIELD_OF_VIEW_TEST 0
\r
272 #pragma message ("Disabling unit tests")
\r
274 #warning "Disabling unit tests"
\r
276 // Disable unit tests
\r
277 #define ENABLE_TESTS 0
\r
279 // Enable unit tests
\r
280 #define ENABLE_TESTS 1
\r
284 #pragma comment(lib, "Irrlicht.lib")
\r
285 //#pragma comment(lib, "jthread.lib")
\r
286 #pragma comment(lib, "zlibwapi.lib")
\r
287 #pragma comment(lib, "Shell32.lib")
\r
288 // This would get rid of the console window
\r
289 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
292 #include <iostream>
\r
294 #include <jmutexautolock.h>
\r
295 #include <locale.h>
\r
297 #include "common_irrlicht.h"
\r
300 #include "player.h"
\r
302 //#include "environment.h"
\r
303 #include "server.h"
\r
304 #include "client.h"
\r
305 //#include "serialization.h"
\r
306 #include "constants.h"
\r
307 //#include "strfnd.h"
\r
308 #include "porting.h"
\r
309 #include "irrlichtwrapper.h"
\r
310 #include "gettime.h"
\r
311 #include "porting.h"
\r
312 #include "guiPauseMenu.h"
\r
313 #include "guiInventoryMenu.h"
\r
314 #include "guiTextInputMenu.h"
\r
315 #include "materials.h"
\r
316 #include "guiMessageMenu.h"
\r
317 #include "filesys.h"
\r
318 #include "config.h"
\r
319 #include "guiMainMenu.h"
\r
320 #include "mineral.h"
\r
323 #include "guiFurnaceMenu.h"
\r
325 // TODO: Remove this
\r
326 IrrlichtWrapper *g_irrlicht = NULL;
\r
328 // This makes textures
\r
329 ITextureSource *g_texturesource = NULL;
\r
331 MapDrawControl draw_control;
\r
335 These are loaded from the config file.
\r
338 Settings g_settings;
\r
340 extern void set_default_settings();
\r
346 IrrlichtDevice *g_device = NULL;
\r
347 Client *g_client = NULL;
\r
349 /*const s16 quickinv_size = 40;
\r
350 const s16 quickinv_padding = 8;
\r
351 const s16 quickinv_spacing = quickinv_size + quickinv_padding;
\r
352 const s16 quickinv_outer_padding = 4;
\r
353 const s16 quickinv_itemcount = 8;*/
\r
355 const s32 hotbar_itemcount = 8;
\r
356 const s32 hotbar_imagesize = 36;
\r
362 gui::IGUIEnvironment* guienv = NULL;
\r
363 gui::IGUIStaticText *guiroot = NULL;
\r
365 class MainMenuManager : public IMenuManager
\r
368 virtual void createdMenu(GUIModalMenu *menu)
\r
370 for(core::list<GUIModalMenu*>::Iterator
\r
371 i = m_stack.begin();
\r
372 i != m_stack.end(); i++)
\r
374 assert(*i != menu);
\r
377 if(m_stack.size() != 0)
\r
378 (*m_stack.getLast())->setVisible(false);
\r
379 m_stack.push_back(menu);
\r
382 virtual void deletingMenu(GUIModalMenu *menu)
\r
384 // Remove all entries if there are duplicates
\r
385 bool removed_entry;
\r
387 removed_entry = false;
\r
388 for(core::list<GUIModalMenu*>::Iterator
\r
389 i = m_stack.begin();
\r
390 i != m_stack.end(); i++)
\r
395 removed_entry = true;
\r
399 }while(removed_entry);
\r
401 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
402 assert(*i == menu);
\r
403 m_stack.erase(i);*/
\r
405 if(m_stack.size() != 0)
\r
406 (*m_stack.getLast())->setVisible(true);
\r
411 return m_stack.size();
\r
414 core::list<GUIModalMenu*> m_stack;
\r
417 MainMenuManager g_menumgr;
\r
419 bool noMenuActive()
\r
421 return (g_menumgr.menuCount() == 0);
\r
424 bool g_disconnect_requested = false;
\r
426 class MainGameCallback : public IGameCallback
\r
429 virtual void exitToOS()
\r
431 g_device->closeDevice();
\r
434 virtual void disconnect()
\r
436 g_disconnect_requested = true;
\r
440 MainGameCallback g_gamecallback;
\r
442 // Inventory actions from the menu are buffered here before sending
\r
443 // TODO: Get rid of this
\r
444 Queue<InventoryAction*> inventory_action_queue;
\r
445 // This is a copy of the inventory that the client's environment has
\r
446 Inventory local_inventory;
\r
448 u16 g_selected_item = 0;
\r
450 /*bool g_show_map_plot = false;
\r
451 bool g_refresh_map_plot = false;*/
\r
458 std::ostream *dout_con_ptr = &dummyout;
\r
459 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
460 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
461 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
462 //std::ostream *dout_con_ptr = &dstream;
\r
463 //std::ostream *derr_con_ptr = &dstream;
\r
466 std::ostream *dout_server_ptr = &dstream;
\r
467 std::ostream *derr_server_ptr = &dstream;
\r
470 std::ostream *dout_client_ptr = &dstream;
\r
471 std::ostream *derr_client_ptr = &dstream;
\r
474 gettime.h implementation
\r
480 Use irrlicht because it is more precise than porting.h's
\r
483 if(g_irrlicht == NULL)
\r
485 return g_irrlicht->getTime();
\r
492 struct TextDestSign : public TextDest
\r
494 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
496 m_blockpos = blockpos;
\r
500 void gotText(std::wstring text)
\r
502 std::string ntext = wide_to_narrow(text);
\r
503 dstream<<"Changing text of a sign object: "
\r
504 <<ntext<<std::endl;
\r
505 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
513 struct TextDestChat : public TextDest
\r
515 TextDestChat(Client *client)
\r
519 void gotText(std::wstring text)
\r
521 // Discard empty line
\r
525 // Parse command (server command starts with "/#")
\r
526 if(text[0] == L'/' && text[1] != L'#')
\r
528 std::wstring reply = L"Local: ";
\r
530 reply += L"Local commands not yet supported. "
\r
531 L"Server prefix is \"/#\".";
\r
533 m_client->addChatMessage(reply);
\r
538 m_client->sendChatMessage(text);
\r
540 m_client->addChatMessage(text);
\r
546 struct TextDestSignNode : public TextDest
\r
548 TextDestSignNode(v3s16 p, Client *client)
\r
553 void gotText(std::wstring text)
\r
555 std::string ntext = wide_to_narrow(text);
\r
556 dstream<<"Changing text of a sign node: "
\r
557 <<ntext<<std::endl;
\r
558 m_client->sendSignNodeText(m_p, ntext);
\r
565 class MyEventReceiver : public IEventReceiver
\r
568 // This is the one method that we have to implement
\r
569 virtual bool OnEvent(const SEvent& event)
\r
572 React to nothing here if a menu is active
\r
574 if(noMenuActive() == false)
\r
580 // Remember whether each key is down or up
\r
581 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
583 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
585 if(event.KeyInput.PressedDown)
\r
587 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
588 /*if(g_show_map_plot)
\r
590 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
591 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
593 g_show_map_plot = false;
\r
602 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
604 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
606 dstream<<DTIME<<"MyEventReceiver: "
\r
607 <<"Launching pause menu"<<std::endl;
\r
608 // It will delete itself by itself
\r
609 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
610 &g_menumgr))->drop();
\r
613 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
615 dstream<<DTIME<<"MyEventReceiver: "
\r
616 <<"Launching inventory"<<std::endl;
\r
618 GUIInventoryMenu *menu =
\r
619 new GUIInventoryMenu(guienv, guiroot, -1,
\r
620 &g_menumgr, v2s16(8,7),
\r
621 g_client->getInventoryContext(),
\r
624 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
625 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
626 "list", "current_player", "main",
\r
627 v2s32(0, 3), v2s32(8, 4)));
\r
628 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
629 "list", "current_player", "craft",
\r
630 v2s32(3, 0), v2s32(3, 3)));
\r
631 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
632 "list", "current_player", "craftresult",
\r
633 v2s32(7, 1), v2s32(1, 1)));
\r
635 menu->setDrawSpec(draw_spec);
\r
641 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
643 TextDest *dest = new TextDestChat(g_client);
\r
645 (new GUITextInputMenu(guienv, guiroot, -1,
\r
652 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
653 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
655 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
656 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
658 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
659 g_selected_item = s1-1;
\r
660 dstream<<DTIME<<"Selected item: "
\r
661 <<g_selected_item<<std::endl;
\r
664 // Viewing range selection
\r
665 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
667 if(draw_control.range_all)
\r
669 draw_control.range_all = false;
\r
670 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
674 draw_control.range_all = true;
\r
675 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
679 // Print debug stacks
\r
680 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
682 dstream<<"-----------------------------------------"
\r
684 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
685 dstream<<"-----------------------------------------"
\r
687 debug_stacks_print();
\r
691 /*if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
693 dstream<<"Map plot requested"<<std::endl;
\r
694 g_show_map_plot = !g_show_map_plot;
\r
695 if(g_show_map_plot)
\r
696 g_refresh_map_plot = true;
\r
702 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
704 if(noMenuActive() == false)
\r
706 left_active = false;
\r
707 middle_active = false;
\r
708 right_active = false;
\r
712 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
713 left_active = event.MouseInput.isLeftPressed();
\r
714 middle_active = event.MouseInput.isMiddlePressed();
\r
715 right_active = event.MouseInput.isRightPressed();
\r
717 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
719 leftclicked = true;
\r
721 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
723 rightclicked = true;
\r
725 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
727 leftreleased = true;
\r
729 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
731 rightreleased = true;
\r
733 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
735 /*dstream<<"event.MouseInput.Wheel="
\r
736 <<event.MouseInput.Wheel<<std::endl;*/
\r
738 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
739 hotbar_itemcount-1);
\r
740 if(event.MouseInput.Wheel < 0)
\r
742 if(g_selected_item < max_item)
\r
745 g_selected_item = 0;
\r
747 else if(event.MouseInput.Wheel > 0)
\r
749 if(g_selected_item > 0)
\r
752 g_selected_item = max_item;
\r
761 // This is used to check whether a key is being held down
\r
762 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
764 return keyIsDown[keyCode];
\r
769 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
770 keyIsDown[i] = false;
\r
772 leftclicked = false;
\r
773 rightclicked = false;
\r
774 leftreleased = false;
\r
775 rightreleased = false;
\r
777 left_active = false;
\r
778 middle_active = false;
\r
779 right_active = false;
\r
790 bool rightreleased;
\r
793 bool middle_active;
\r
797 // We use this array to store the current state of each key
\r
798 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
801 IrrlichtDevice *m_device;
\r
810 virtual ~InputHandler()
\r
814 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
816 virtual v2s32 getMousePos() = 0;
\r
817 virtual void setMousePos(s32 x, s32 y) = 0;
\r
819 virtual bool getLeftState() = 0;
\r
820 virtual bool getRightState() = 0;
\r
822 virtual bool getLeftClicked() = 0;
\r
823 virtual bool getRightClicked() = 0;
\r
824 virtual void resetLeftClicked() = 0;
\r
825 virtual void resetRightClicked() = 0;
\r
827 virtual bool getLeftReleased() = 0;
\r
828 virtual bool getRightReleased() = 0;
\r
829 virtual void resetLeftReleased() = 0;
\r
830 virtual void resetRightReleased() = 0;
\r
832 virtual void step(float dtime) {};
\r
834 virtual void clear() {};
\r
837 InputHandler *g_input = NULL;
\r
839 class RealInputHandler : public InputHandler
\r
842 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
844 m_receiver(receiver)
\r
847 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
849 return m_receiver->IsKeyDown(keyCode);
\r
851 virtual v2s32 getMousePos()
\r
853 return m_device->getCursorControl()->getPosition();
\r
855 virtual void setMousePos(s32 x, s32 y)
\r
857 m_device->getCursorControl()->setPosition(x, y);
\r
860 virtual bool getLeftState()
\r
862 return m_receiver->left_active;
\r
864 virtual bool getRightState()
\r
866 return m_receiver->right_active;
\r
869 virtual bool getLeftClicked()
\r
871 return m_receiver->leftclicked;
\r
873 virtual bool getRightClicked()
\r
875 return m_receiver->rightclicked;
\r
877 virtual void resetLeftClicked()
\r
879 m_receiver->leftclicked = false;
\r
881 virtual void resetRightClicked()
\r
883 m_receiver->rightclicked = false;
\r
886 virtual bool getLeftReleased()
\r
888 return m_receiver->leftreleased;
\r
890 virtual bool getRightReleased()
\r
892 return m_receiver->rightreleased;
\r
894 virtual void resetLeftReleased()
\r
896 m_receiver->leftreleased = false;
\r
898 virtual void resetRightReleased()
\r
900 m_receiver->rightreleased = false;
\r
905 resetRightClicked();
\r
906 resetLeftClicked();
\r
909 IrrlichtDevice *m_device;
\r
910 MyEventReceiver *m_receiver;
\r
913 class RandomInputHandler : public InputHandler
\r
916 RandomInputHandler()
\r
920 leftclicked = false;
\r
921 rightclicked = false;
\r
922 leftreleased = false;
\r
923 rightreleased = false;
\r
924 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
925 keydown[i] = false;
\r
927 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
929 return keydown[keyCode];
\r
931 virtual v2s32 getMousePos()
\r
935 virtual void setMousePos(s32 x, s32 y)
\r
937 mousepos = v2s32(x,y);
\r
940 virtual bool getLeftState()
\r
944 virtual bool getRightState()
\r
949 virtual bool getLeftClicked()
\r
951 return leftclicked;
\r
953 virtual bool getRightClicked()
\r
955 return rightclicked;
\r
957 virtual void resetLeftClicked()
\r
959 leftclicked = false;
\r
961 virtual void resetRightClicked()
\r
963 rightclicked = false;
\r
966 virtual bool getLeftReleased()
\r
968 return leftreleased;
\r
970 virtual bool getRightReleased()
\r
972 return rightreleased;
\r
974 virtual void resetLeftReleased()
\r
976 leftreleased = false;
\r
978 virtual void resetRightReleased()
\r
980 rightreleased = false;
\r
983 virtual void step(float dtime)
\r
986 static float counter1 = 0;
\r
990 counter1 = 0.1*Rand(1, 40);
\r
991 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
995 static float counter1 = 0;
\r
999 counter1 = 0.1*Rand(1, 40);
\r
1000 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1004 static float counter1 = 0;
\r
1005 counter1 -= dtime;
\r
1006 if(counter1 < 0.0)
\r
1008 counter1 = 0.1*Rand(1, 40);
\r
1009 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1013 static float counter1 = 0;
\r
1014 counter1 -= dtime;
\r
1015 if(counter1 < 0.0)
\r
1017 counter1 = 0.1*Rand(1, 40);
\r
1018 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1022 static float counter1 = 0;
\r
1023 counter1 -= dtime;
\r
1024 if(counter1 < 0.0)
\r
1026 counter1 = 0.1*Rand(1, 20);
\r
1027 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1031 static float counter1 = 0;
\r
1032 counter1 -= dtime;
\r
1033 if(counter1 < 0.0)
\r
1035 counter1 = 0.1*Rand(1, 30);
\r
1036 leftdown = !leftdown;
\r
1038 leftclicked = true;
\r
1040 leftreleased = true;
\r
1044 static float counter1 = 0;
\r
1045 counter1 -= dtime;
\r
1046 if(counter1 < 0.0)
\r
1048 counter1 = 0.1*Rand(1, 15);
\r
1049 rightdown = !rightdown;
\r
1051 rightclicked = true;
\r
1053 rightreleased = true;
\r
1056 mousepos += mousespeed;
\r
1059 s32 Rand(s32 min, s32 max)
\r
1061 return (myrand()%(max-min+1))+min;
\r
1064 bool keydown[KEY_KEY_CODES_COUNT];
\r
1070 bool rightclicked;
\r
1071 bool leftreleased;
\r
1072 bool rightreleased;
\r
1075 void updateViewingRange(f32 frametime_in, Client *client)
\r
1077 if(draw_control.range_all == true)
\r
1080 static f32 added_frametime = 0;
\r
1081 static s16 added_frames = 0;
\r
1083 added_frametime += frametime_in;
\r
1084 added_frames += 1;
\r
1086 // Actually this counter kind of sucks because frametime is busytime
\r
1087 static f32 counter = 0;
\r
1088 counter -= frametime_in;
\r
1094 /*dstream<<__FUNCTION_NAME
\r
1095 <<": Collected "<<added_frames<<" frames, total of "
\r
1096 <<added_frametime<<"s."<<std::endl;*/
\r
1098 /*dstream<<"draw_control.blocks_drawn="
\r
1099 <<draw_control.blocks_drawn
\r
1100 <<", draw_control.blocks_would_have_drawn="
\r
1101 <<draw_control.blocks_would_have_drawn
\r
1104 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1105 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1107 draw_control.wanted_min_range = range_min;
\r
1108 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1110 float block_draw_ratio = 1.0;
\r
1111 if(draw_control.blocks_would_have_drawn != 0)
\r
1113 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1114 / (float)draw_control.blocks_would_have_drawn;
\r
1117 // Calculate the average frametime in the case that all wanted
\r
1118 // blocks had been drawn
\r
1119 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1121 added_frametime = 0.0;
\r
1124 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1125 float wanted_frametime = 1.0 / wanted_fps;
\r
1127 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1128 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1130 // If needed frametime change is small, just return
\r
1131 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1133 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1137 float range = draw_control.wanted_range;
\r
1138 float new_range = range;
\r
1140 static s16 range_old = 0;
\r
1141 static f32 frametime_old = 0;
\r
1143 float d_range = range - range_old;
\r
1144 f32 d_frametime = frametime - frametime_old;
\r
1145 // A sane default of 30ms per 50 nodes of range
\r
1146 static f32 time_per_range = 30. / 50;
\r
1149 time_per_range = d_frametime / d_range;
\r
1152 // The minimum allowed calculated frametime-range derivative:
\r
1153 // Practically this sets the maximum speed of changing the range.
\r
1154 // The lower this value, the higher the maximum changing speed.
\r
1155 // A low value here results in wobbly range (0.001)
\r
1156 // A high value here results in slow changing range (0.0025)
\r
1157 // SUGG: This could be dynamically adjusted so that when
\r
1158 // the camera is turning, this is lower
\r
1159 //float min_time_per_range = 0.0015;
\r
1160 float min_time_per_range = 0.0010;
\r
1161 //float min_time_per_range = 0.05 / range;
\r
1162 if(time_per_range < min_time_per_range)
\r
1164 time_per_range = min_time_per_range;
\r
1165 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1169 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1172 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1173 // Dampen the change a bit to kill oscillations
\r
1174 //wanted_range_change *= 0.9;
\r
1175 //wanted_range_change *= 0.75;
\r
1176 wanted_range_change *= 0.5;
\r
1177 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1179 // If needed range change is very small, just return
\r
1180 if(fabs(wanted_range_change) < 0.001)
\r
1182 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1186 new_range += wanted_range_change;
\r
1187 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1189 //float new_range_unclamped = new_range;
\r
1190 if(new_range < range_min)
\r
1191 new_range = range_min;
\r
1192 if(new_range > range_max)
\r
1193 new_range = range_max;
\r
1195 /*if(new_range != new_range_unclamped)
\r
1196 dstream<<", clamped to "<<new_range<<std::endl;
\r
1198 dstream<<std::endl;*/
\r
1200 draw_control.wanted_range = new_range;
\r
1202 range_old = new_range;
\r
1203 frametime_old = frametime;
\r
1206 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1207 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1208 Inventory *inventory)
\r
1210 InventoryList *mainlist = inventory->getList("main");
\r
1211 if(mainlist == NULL)
\r
1213 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1217 s32 padding = imgsize/12;
\r
1218 //s32 height = imgsize + padding*2;
\r
1219 s32 width = itemcount*(imgsize+padding*2);
\r
1221 // Position of upper left corner of bar
\r
1222 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1224 // Draw background color
\r
1225 /*core::rect<s32> barrect(0,0,width,height);
\r
1227 video::SColor bgcolor(255,128,128,128);
\r
1228 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1230 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1232 for(s32 i=0; i<itemcount; i++)
\r
1234 InventoryItem *item = mainlist->getItem(i);
\r
1236 core::rect<s32> rect = imgrect + pos
\r
1237 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1239 if(g_selected_item == i)
\r
1241 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1242 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1243 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1248 video::SColor bgcolor2(128,0,0,0);
\r
1249 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1254 drawInventoryItem(driver, font, item, rect, NULL);
\r
1260 video::ITexture *g_map_plot_texture = NULL;
\r
1261 float g_map_plot_texture_scale = 4;
\r
1263 void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
\r
1269 core::dimension2d<u32> dim(640,480);
\r
1270 video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
\r
1272 for(u32 y=0; y<dim.Height; y++)
\r
1273 for(u32 x=0; x<dim.Width; x++)
\r
1275 v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
\r
1276 pf *= g_map_plot_texture_scale;
\r
1278 double h = base_rock_level_2d(client->getMapSeed(), pf);
\r
1281 /*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
\r
1282 s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
\r
1284 // Screen coordinates that are based on multiples of
\r
1285 // 1000/g_map_plot_texture_scale and never negative
\r
1286 u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1287 u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
\r
1288 // Offset to center of image
\r
1289 ux -= dim.Width/2;
\r
1290 uy -= dim.Height/2;
\r
1292 if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
\r
1293 || ux % (u32)(1000/g_map_plot_texture_scale) == 0)
\r
1294 c.set(255, 255, 255, 255);
\r
1295 else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
\r
1296 || ux % (u32)(100/g_map_plot_texture_scale) == 0)
\r
1297 c.set(255, 160, 160, 160);
\r
1298 else if(h < WATER_LEVEL - 0.5) // Water
\r
1299 c.set(255, 50, 50, 255);
\r
1301 else if(get_have_sand_ground(client->getMapSeed(), pf)
\r
1302 || (h < WATER_LEVEL + 2
\r
1303 && get_have_sand_coast(client->getMapSeed(), pf)))
\r
1307 h = 1.0 - exp(-h);
\r
1309 video::SColor c1(255,237,201,175);
\r
1310 //video::SColor c2(255,20,20,20);
\r
1311 video::SColor c2(255,150,0,0);
\r
1312 c = c2.getInterpolated(c1, h);
\r
1318 h = 1.0 - exp(-h);
\r
1320 video::SColor c1(255,110,185,90);
\r
1321 //video::SColor c2(255,20,20,20);
\r
1322 video::SColor c2(255,150,0,0);
\r
1323 c = c2.getInterpolated(c1, h);
\r
1328 else if(get_have_sand_ground(client->getMapSeed(), pf))
\r
1332 h = 1.0 - exp(-h);
\r
1334 video::SColor c1(255,237,201,175);
\r
1335 //video::SColor c2(255,20,20,20);
\r
1336 video::SColor c2(255,150,0,0);
\r
1337 c = c2.getInterpolated(c1, h);
\r
1341 else if(h < WATER_LEVEL + 2
\r
1342 && get_have_sand_coast(client->getMapSeed(), pf))
\r
1343 c.set(255, 237, 201, 175);
\r
1345 else if(h < WATER_LEVEL + 10)
\r
1346 c.set(255, 50, 150, 50); // Green
\r
1347 else if(h < WATER_LEVEL + 20)
\r
1348 c.set(255, 110, 185, 50); // Yellowish green
\r
1349 else if(h < WATER_LEVEL + 40)
\r
1350 c.set(255, 180, 210, 50); // Greenish yellow
\r
1351 else if(h < WATER_LEVEL + 60)
\r
1352 c.set(255, 220, 220, 50); // Yellow
\r
1353 else if(h < WATER_LEVEL + 80)
\r
1354 c.set(255, 200, 200, 110); // Yellowish white
\r
1355 else if(h < WATER_LEVEL + 100)
\r
1356 c.set(255, 190, 190, 190); // Grey
\r
1358 c.set(255, 255, 255, 255); // White
\r
1366 h = 1.0 - exp(-h);
\r
1368 video::SColor c1(255,200,200,50);
\r
1369 video::SColor c2(255,0,150,0);
\r
1370 c = c1.getInterpolated(c2, h);
\r
1372 /*u32 a = (u32)(h*255);
\r
1376 c.set(255, a, a, a);*/
\r
1380 if(h >= WATER_LEVEL - 0.5
\r
1381 && get_have_sand_ground(client->getMapSeed(), pf))
\r
1383 video::SColor c1(255,237,201,175);
\r
1384 c = c.getInterpolated(c1, 0.5);
\r
1388 double tf = get_turbulence_factor_2d(client->getMapSeed(), pf);
\r
1391 video::SColor c1(255,255,0,0);
\r
1392 c = c.getInterpolated(c1, 1.0-(0.5*tf));
\r
1395 img->setPixel(x, y, c);
\r
1397 g_map_plot_texture = driver->addTexture("map_plot", img);
\r
1399 assert(g_map_plot_texture);
\r
1410 ChatLine(const std::wstring &a_text):
\r
1416 std::wstring text;
\r
1419 // These are defined global so that they're not optimized too much.
\r
1420 // Can't change them to volatile.
\r
1425 std::string tempstring;
\r
1426 std::string tempstring2;
\r
1431 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1432 TimeTaker timer("Testing std::string speed");
\r
1433 const u32 jj = 10000;
\r
1434 for(u32 j=0; j<jj; j++)
\r
1438 const u32 ii = 10;
\r
1439 for(u32 i=0; i<ii; i++){
\r
1440 tempstring2 += "asd";
\r
1442 for(u32 i=0; i<ii+1; i++){
\r
1443 tempstring += "asd";
\r
1444 if(tempstring == tempstring2)
\r
1450 dstream<<"All of the following tests should take around 100ms each."
\r
1454 TimeTaker timer("Testing floating-point conversion speed");
\r
1456 for(u32 i=0; i<4000000; i++){
\r
1463 TimeTaker timer("Testing floating-point vector speed");
\r
1465 tempv3f1 = v3f(1,2,3);
\r
1466 tempv3f2 = v3f(4,5,6);
\r
1467 for(u32 i=0; i<10000000; i++){
\r
1468 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1469 tempv3f2 += v3f(7,8,9);
\r
1474 TimeTaker timer("Testing core::map speed");
\r
1476 core::map<v2s16, f32> map1;
\r
1479 for(s16 y=0; y<ii; y++){
\r
1480 for(s16 x=0; x<ii; x++){
\r
1481 map1.insert(v2s16(x,y), tempf);
\r
1485 for(s16 y=ii-1; y>=0; y--){
\r
1486 for(s16 x=0; x<ii; x++){
\r
1487 tempf = map1[v2s16(x,y)];
\r
1493 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1494 TimeTaker timer("Testing mutex speed");
\r
1507 // Do at least 10ms
\r
1508 while(timer.getTime() < 10);
\r
1510 u32 dtime = timer.stop();
\r
1511 u32 per_ms = n / dtime;
\r
1512 std::cout<<"Done. "<<dtime<<"ms, "
\r
1513 <<per_ms<<"/ms"<<std::endl;
\r
1517 int main(int argc, char *argv[])
\r
1520 Parse command line
\r
1523 // List all allowed options
\r
1524 core::map<std::string, ValueSpec> allowed_options;
\r
1525 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1526 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1527 "Run server directly"));
\r
1528 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1529 "Load configuration from specified file"));
\r
1530 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1531 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1532 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1533 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1534 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1535 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1537 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1539 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1541 Settings cmd_args;
\r
1543 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1545 if(ret == false || cmd_args.getFlag("help"))
\r
1547 dstream<<"Allowed options:"<<std::endl;
\r
1548 for(core::map<std::string, ValueSpec>::Iterator
\r
1549 i = allowed_options.getIterator();
\r
1550 i.atEnd() == false; i++)
\r
1552 dstream<<" --"<<i.getNode()->getKey();
\r
1553 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1558 dstream<<" <value>";
\r
1560 dstream<<std::endl;
\r
1562 if(i.getNode()->getValue().help != NULL)
\r
1564 dstream<<" "<<i.getNode()->getValue().help
\r
1569 return cmd_args.getFlag("help") ? 0 : 1;
\r
1573 Low-level initialization
\r
1576 bool disable_stderr = false;
\r
1578 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1579 disable_stderr = true;
\r
1582 // Initialize debug streams
\r
1583 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1584 // Initialize debug stacks
\r
1585 debug_stacks_init();
\r
1587 DSTACK(__FUNCTION_NAME);
\r
1589 porting::signal_handler_init();
\r
1590 bool &kill = *porting::signal_handler_killstatus();
\r
1592 porting::initializePaths();
\r
1593 // Create user data directory
\r
1594 fs::CreateDir(porting::path_userdata);
\r
1596 // C-style stuff initialization
\r
1597 initializeMaterialProperties();
\r
1600 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1602 // Print startup message
\r
1603 dstream<<DTIME<<"minetest-c55"
\r
1604 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1605 <<", "<<BUILD_INFO
\r
1609 Basic initialization
\r
1612 // Initialize default settings
\r
1613 set_default_settings();
\r
1615 // Set locale. This is for forcing '.' as the decimal point.
\r
1616 std::locale::global(std::locale("C"));
\r
1617 // This enables printing all characters in bitmap font
\r
1618 setlocale(LC_CTYPE, "en_US");
\r
1620 // Initialize sockets
\r
1622 atexit(sockets_cleanup);
\r
1632 // Path of configuration file in use
\r
1633 std::string configpath = "";
\r
1635 if(cmd_args.exists("config"))
\r
1637 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1640 dstream<<"Could not read configuration from \""
\r
1641 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1644 configpath = cmd_args.get("config");
\r
1648 core::array<std::string> filenames;
\r
1649 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1650 #ifdef RUN_IN_PLACE
\r
1651 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1654 for(u32 i=0; i<filenames.size(); i++)
\r
1656 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1659 configpath = filenames[i];
\r
1664 // If no path found, use the first one (menu creates the file)
\r
1665 if(configpath == "")
\r
1666 configpath = filenames[0];
\r
1669 // Initialize random seed
\r
1674 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1676 These are needed for unit tests at least.
\r
1679 // Initial call with g_texturesource not set.
\r
1686 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1687 || cmd_args.getFlag("enable-unittests") == true)
\r
1692 /*for(s16 y=-100; y<100; y++)
\r
1693 for(s16 x=-100; x<100; x++)
\r
1695 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1705 if(cmd_args.exists("port"))
\r
1706 port = cmd_args.getU16("port");
\r
1707 else if(cmd_args.exists("port"))
\r
1708 port = g_settings.getU16("port");
\r
1711 std::string map_dir = porting::path_userdata+"/map";
\r
1712 if(cmd_args.exists("map-dir"))
\r
1713 map_dir = cmd_args.get("map-dir");
\r
1714 else if(g_settings.exists("map-dir"))
\r
1715 map_dir = g_settings.get("map-dir");
\r
1717 // Run dedicated server if asked to
\r
1718 if(cmd_args.getFlag("server"))
\r
1720 DSTACK("Dedicated server branch");
\r
1723 Server server(map_dir.c_str());
\r
1724 server.start(port);
\r
1727 dedicated_server_loop(server, kill);
\r
1736 // Address to connect to
\r
1737 std::string address = "";
\r
1739 if(cmd_args.exists("address"))
\r
1741 address = cmd_args.get("address");
\r
1745 address = g_settings.get("address");
\r
1748 std::string playername = g_settings.get("name");
\r
1750 // Resolution selection
\r
1752 bool fullscreen = false;
\r
1753 u16 screenW = g_settings.getU16("screenW");
\r
1754 u16 screenH = g_settings.getU16("screenH");
\r
1756 // Determine driver
\r
1758 video::E_DRIVER_TYPE driverType;
\r
1760 std::string driverstring = g_settings.get("video_driver");
\r
1762 if(driverstring == "null")
\r
1763 driverType = video::EDT_NULL;
\r
1764 else if(driverstring == "software")
\r
1765 driverType = video::EDT_SOFTWARE;
\r
1766 else if(driverstring == "burningsvideo")
\r
1767 driverType = video::EDT_BURNINGSVIDEO;
\r
1768 else if(driverstring == "direct3d8")
\r
1769 driverType = video::EDT_DIRECT3D8;
\r
1770 else if(driverstring == "direct3d9")
\r
1771 driverType = video::EDT_DIRECT3D9;
\r
1772 else if(driverstring == "opengl")
\r
1773 driverType = video::EDT_OPENGL;
\r
1776 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1777 "to opengl"<<std::endl;
\r
1778 driverType = video::EDT_OPENGL;
\r
1781 // create device and exit if creation failed
\r
1783 MyEventReceiver receiver;
\r
1785 IrrlichtDevice *device;
\r
1786 device = createDevice(driverType,
\r
1787 core::dimension2d<u32>(screenW, screenH),
\r
1788 16, fullscreen, false, false, &receiver);
\r
1791 return 1; // could not create selected driver.
\r
1793 g_device = device;
\r
1794 g_irrlicht = new IrrlichtWrapper(device);
\r
1795 TextureSource *texturesource = new TextureSource(device);
\r
1796 g_texturesource = texturesource;
\r
1799 Speed tests (done after irrlicht is loaded to get timer)
\r
1801 if(cmd_args.getFlag("speedtests"))
\r
1803 dstream<<"Running speed tests"<<std::endl;
\r
1808 device->setResizable(true);
\r
1810 bool random_input = g_settings.getBool("random_input")
\r
1811 || cmd_args.getFlag("random-input");
\r
1813 g_input = new RandomInputHandler();
\r
1815 g_input = new RealInputHandler(device, &receiver);
\r
1818 Continue initialization
\r
1821 video::IVideoDriver* driver = device->getVideoDriver();
\r
1824 This changes the minimum allowed number of vertices in a VBO.
\r
1827 //driver->setMinHardwareBufferVertexCount(50);
\r
1829 scene::ISceneManager* smgr = device->getSceneManager();
\r
1831 guienv = device->getGUIEnvironment();
\r
1832 gui::IGUISkin* skin = guienv->getSkin();
\r
1833 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1835 skin->setFont(font);
\r
1837 dstream<<"WARNING: Font file was not found."
\r
1838 " Using default font."<<std::endl;
\r
1839 // If font was not found, this will get us one
\r
1840 font = skin->getFont();
\r
1843 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1844 dstream<<"text_height="<<text_height<<std::endl;
\r
1846 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1847 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1848 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1849 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1850 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1851 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1854 Preload some textures and stuff
\r
1857 init_content_inventory_texture_paths();
\r
1858 init_mapnode(); // Second call with g_texturesource set
\r
1866 We need some kind of a root node to be able to add
\r
1867 custom gui elements directly on the screen.
\r
1868 Otherwise they won't be automatically drawn.
\r
1870 guiroot = guienv->addStaticText(L"",
\r
1871 core::rect<s32>(0, 0, 10000, 10000));
\r
1873 // First line of debug text
\r
1874 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1876 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1878 // Second line of debug text
\r
1879 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1881 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1884 // At the middle of the screen
\r
1885 // Object infos are shown in this
\r
1886 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1888 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
1892 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1894 core::rect<s32>(0,0,0,0),
\r
1895 false, false); // Disable word wrap as of now
\r
1897 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1898 core::list<ChatLine> chat_lines;
\r
1901 If an error occurs, this is set to something and the
\r
1902 menu-game loop is restarted. It is then displayed before
\r
1905 std::wstring error_message = L"";
\r
1910 while(g_device->run() && kill == false)
\r
1913 // This is used for catching disconnects
\r
1918 Out-of-game menu loop.
\r
1920 Loop quits when menu returns proper parameters.
\r
1922 while(kill == false)
\r
1924 // Cursor can be non-visible when coming from the game
\r
1925 device->getCursorControl()->setVisible(true);
\r
1926 // Some stuff are left to scene manager when coming from the game
\r
1927 // (map at least?)
\r
1929 // Reset or hide the debug gui texts
\r
1930 guitext->setText(L"Minetest-c55");
\r
1931 guitext2->setVisible(false);
\r
1932 guitext_info->setVisible(false);
\r
1933 guitext_chat->setVisible(false);
\r
1935 // Initialize menu data
\r
1936 MainMenuData menudata;
\r
1937 menudata.address = narrow_to_wide(address);
\r
1938 menudata.name = narrow_to_wide(playername);
\r
1939 menudata.port = narrow_to_wide(itos(port));
\r
1940 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1942 GUIMainMenu *menu =
\r
1943 new GUIMainMenu(guienv, guiroot, -1,
\r
1944 &g_menumgr, &menudata, &g_gamecallback);
\r
1945 menu->allowFocusRemoval(true);
\r
1947 if(error_message != L"")
\r
1949 GUIMessageMenu *menu2 =
\r
1950 new GUIMessageMenu(guienv, guiroot, -1,
\r
1951 &g_menumgr, error_message.c_str());
\r
1953 error_message = L"";
\r
1956 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1958 dstream<<"Created main menu"<<std::endl;
\r
1960 while(g_device->run() && kill == false)
\r
1962 // Run global IrrlichtWrapper's main thread processing stuff
\r
1963 g_irrlicht->Run();
\r
1965 if(menu->getStatus() == true)
\r
1968 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1969 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1970 guienv->drawAll();
\r
1971 driver->endScene();
\r
1974 // Break out of menu-game loop to shut down cleanly
\r
1975 if(g_device->run() == false || kill == true)
\r
1978 dstream<<"Dropping main menu"<<std::endl;
\r
1982 // Delete map if requested
\r
1983 if(menudata.delete_map)
\r
1985 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1987 error_message = L"Delete failed";
\r
1991 playername = wide_to_narrow(menudata.name);
\r
1992 address = wide_to_narrow(menudata.address);
\r
1993 port = stoi(wide_to_narrow(menudata.port));
\r
1994 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1996 // Check for valid parameters, restart menu if invalid.
\r
1997 if(playername == "")
\r
1999 error_message = L"Name required.";
\r
2004 g_settings.set("name", playername);
\r
2005 g_settings.set("address", address);
\r
2006 g_settings.set("port", itos(port));
\r
2007 // Update configuration file
\r
2008 if(configpath != "")
\r
2009 g_settings.updateConfigFile(configpath.c_str());
\r
2011 // Continue to game
\r
2015 // Break out of menu-game loop to shut down cleanly
\r
2016 if(g_device->run() == false)
\r
2020 Make a scope here so that the client and the server and other
\r
2021 stuff gets removed when disconnected or the irrlicht device
\r
2026 // This is set to true at the end of the scope
\r
2027 g_irrlicht->Shutdown(false);
\r
2030 Draw "Loading" screen
\r
2032 const wchar_t *text = L"Loading and connecting...";
\r
2033 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2034 core::vector2d<s32> textsize(300, text_height);
\r
2035 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2037 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2038 text, textrect, false, false);
\r
2039 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2041 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2042 guienv->drawAll();
\r
2043 driver->endScene();
\r
2045 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2049 SharedPtr will delete it when it goes out of scope.
\r
2051 SharedPtr<Server> server;
\r
2052 if(address == ""){
\r
2053 server = new Server(map_dir);
\r
2054 server->start(port);
\r
2061 Client client(device, playername.c_str(), draw_control);
\r
2063 g_client = &client;
\r
2065 Address connect_address(0,0,0,0, port);
\r
2068 //connect_address.Resolve("localhost");
\r
2069 connect_address.setAddress(127,0,0,1);
\r
2071 connect_address.Resolve(address.c_str());
\r
2073 catch(ResolveError &e)
\r
2075 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2077 error_message = L"Couldn't resolve address";
\r
2078 gui_loadingtext->remove();
\r
2082 dstream<<DTIME<<"Connecting to server at ";
\r
2083 connect_address.print(&dstream);
\r
2084 dstream<<std::endl;
\r
2085 client.connect(connect_address);
\r
2088 while(client.connectedAndInitialized() == false)
\r
2091 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2092 guienv->drawAll();
\r
2093 driver->endScene();
\r
2095 // Update client and server
\r
2099 if(server != NULL)
\r
2100 server->step(0.1);
\r
2106 catch(con::PeerNotFoundException &e)
\r
2108 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2110 error_message = L"Connection timed out.";
\r
2111 gui_loadingtext->remove();
\r
2118 /*scene::ISceneNode* skybox;
\r
2119 skybox = smgr->addSkyBoxSceneNode(
\r
2120 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2121 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2122 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2123 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2124 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2125 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2128 Create the camera node
\r
2131 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2132 0, // Camera parent
\r
2133 v3f(BS*100, BS*2, BS*100), // Look from
\r
2134 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2138 if(camera == NULL)
\r
2141 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2142 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2143 video::SColor skycolor = video::SColor(255,120,185,244);
\r
2145 camera->setFOV(FOV_ANGLE);
\r
2147 // Just so big a value that everything rendered is visible
\r
2148 camera->setFarValue(100000*BS);
\r
2151 Lighting test code. Doesn't quite work this way.
\r
2152 The CPU-computed lighting is good.
\r
2156 smgr->addLightSceneNode(NULL,
\r
2157 v3f(0, BS*1000000, 0),
\r
2158 video::SColorf(0.3,0.3,0.3),
\r
2161 smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0));
\r
2163 scene::ILightSceneNode *light = smgr->addLightSceneNode(camera,
\r
2164 v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4);
\r
2167 f32 camera_yaw = 0; // "right/left"
\r
2168 f32 camera_pitch = 0; // "up/down"
\r
2174 gui_loadingtext->remove();
\r
2177 Add some gui stuff
\r
2180 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2181 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2182 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2183 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2185 // Test the text input system
\r
2186 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2188 /*GUIMessageMenu *menu =
\r
2189 new GUIMessageMenu(guienv, guiroot, -1,
\r
2194 // Launch pause menu
\r
2195 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2196 &g_menumgr))->drop();
\r
2199 guitext2->setVisible(true);
\r
2200 guitext_info->setVisible(true);
\r
2201 guitext_chat->setVisible(true);
\r
2203 //s32 guitext_chat_pad_bottom = 70;
\r
2205 v2u32 screensize(0,0);
\r
2206 v2u32 last_screensize(0,0);
\r
2209 Some statistics are collected in these
\r
2212 u32 beginscenetime = 0;
\r
2213 u32 scenetime = 0;
\r
2214 u32 endscenetime = 0;
\r
2217 //throw con::PeerNotFoundException("lol");
\r
2219 core::list<float> frametime_log;
\r
2225 bool first_loop_after_window_activation = true;
\r
2227 // Time is in milliseconds
\r
2228 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2229 // NOTE: So we have to use getTime() and call run()s between them
\r
2230 u32 lasttime = device->getTimer()->getTime();
\r
2232 while(device->run() && kill == false)
\r
2234 if(g_disconnect_requested)
\r
2236 g_disconnect_requested = false;
\r
2241 Run global IrrlichtWrapper's main thread processing stuff
\r
2243 g_irrlicht->Run();
\r
2246 Process TextureSource's queue
\r
2248 texturesource->processQueue();
\r
2251 Random calculations
\r
2253 last_screensize = screensize;
\r
2254 screensize = driver->getScreenSize();
\r
2255 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2256 //bool screensize_changed = screensize != last_screensize;
\r
2258 // Hilight boxes collected during the loop and displayed
\r
2259 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2262 std::wstring infotext;
\r
2264 // When screen size changes, update positions and sizes of stuff
\r
2265 /*if(screensize_changed)
\r
2267 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2268 quick_inventory->updatePosition(pos);
\r
2271 //TimeTaker //timer1("//timer1");
\r
2273 // Time of frame without fps limit
\r
2277 // not using getRealTime is necessary for wine
\r
2278 u32 time = device->getTimer()->getTime();
\r
2279 if(time > lasttime)
\r
2280 busytime_u32 = time - lasttime;
\r
2283 busytime = busytime_u32 / 1000.0;
\r
2286 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2288 // Necessary for device->getTimer()->getTime()
\r
2295 updateViewingRange(busytime, &client);
\r
2302 float fps_max = g_settings.getFloat("fps_max");
\r
2303 u32 frametime_min = 1000./fps_max;
\r
2305 if(busytime_u32 < frametime_min)
\r
2307 u32 sleeptime = frametime_min - busytime_u32;
\r
2308 device->sleep(sleeptime);
\r
2312 // Necessary for device->getTimer()->getTime()
\r
2316 Time difference calculation
\r
2318 f32 dtime; // in seconds
\r
2320 u32 time = device->getTimer()->getTime();
\r
2321 if(time > lasttime)
\r
2322 dtime = (time - lasttime) / 1000.0;
\r
2328 Log frametime for visualization
\r
2330 frametime_log.push_back(dtime);
\r
2331 if(frametime_log.size() > 100)
\r
2333 core::list<float>::Iterator i = frametime_log.begin();
\r
2334 frametime_log.erase(i);
\r
2338 Visualize frametime in terminal
\r
2340 /*for(u32 i=0; i<dtime*400; i++)
\r
2342 std::cout<<std::endl;*/
\r
2345 Time average and jitter calculation
\r
2348 static f32 dtime_avg1 = 0.0;
\r
2349 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2350 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2352 static f32 dtime_jitter1_max_sample = 0.0;
\r
2353 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2355 static f32 jitter1_max = 0.0;
\r
2356 static f32 counter = 0.0;
\r
2357 if(dtime_jitter1 > jitter1_max)
\r
2358 jitter1_max = dtime_jitter1;
\r
2363 dtime_jitter1_max_sample = jitter1_max;
\r
2364 dtime_jitter1_max_fraction
\r
2365 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2366 jitter1_max = 0.0;
\r
2371 Busytime average and jitter calculation
\r
2374 static f32 busytime_avg1 = 0.0;
\r
2375 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2376 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2378 static f32 busytime_jitter1_max_sample = 0.0;
\r
2379 static f32 busytime_jitter1_min_sample = 0.0;
\r
2381 static f32 jitter1_max = 0.0;
\r
2382 static f32 jitter1_min = 0.0;
\r
2383 static f32 counter = 0.0;
\r
2384 if(busytime_jitter1 > jitter1_max)
\r
2385 jitter1_max = busytime_jitter1;
\r
2386 if(busytime_jitter1 < jitter1_min)
\r
2387 jitter1_min = busytime_jitter1;
\r
2389 if(counter > 0.0){
\r
2391 busytime_jitter1_max_sample = jitter1_max;
\r
2392 busytime_jitter1_min_sample = jitter1_min;
\r
2393 jitter1_max = 0.0;
\r
2394 jitter1_min = 0.0;
\r
2399 Debug info for client
\r
2402 static float counter = 0.0;
\r
2407 client.printDebugInfo(std::cout);
\r
2412 Input handler step()
\r
2414 g_input->step(dtime);
\r
2421 Player speed control
\r
2430 bool a_superspeed,
\r
2434 PlayerControl control(
\r
2435 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2436 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2437 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2438 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2439 g_input->isKeyDown(irr::KEY_SPACE),
\r
2440 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2441 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2442 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2446 client.setPlayerControl(control);
\r
2450 Process environment
\r
2454 //TimeTaker timer("client.step(dtime)");
\r
2455 client.step(dtime);
\r
2456 //client.step(dtime_avg1);
\r
2459 if(server != NULL)
\r
2461 //TimeTaker timer("server->step(dtime)");
\r
2462 server->step(dtime);
\r
2465 v3f player_position = client.getPlayerPosition();
\r
2467 //TimeTaker //timer2("//timer2");
\r
2470 Mouse and camera control
\r
2473 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2476 device->getCursorControl()->setVisible(false);
\r
2478 if(first_loop_after_window_activation){
\r
2479 //std::cout<<"window active, first loop"<<std::endl;
\r
2480 first_loop_after_window_activation = false;
\r
2483 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2484 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2485 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2486 camera_yaw -= dx*0.2;
\r
2487 camera_pitch += dy*0.2;
\r
2488 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2489 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2491 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2494 device->getCursorControl()->setVisible(true);
\r
2496 //std::cout<<"window inactive"<<std::endl;
\r
2497 first_loop_after_window_activation = true;
\r
2500 camera_yaw = wrapDegrees(camera_yaw);
\r
2501 camera_pitch = wrapDegrees(camera_pitch);
\r
2503 v3f camera_direction = v3f(0,0,1);
\r
2504 camera_direction.rotateYZBy(camera_pitch);
\r
2505 camera_direction.rotateXZBy(camera_yaw);
\r
2507 // This is at the height of the eyes of the current figure
\r
2508 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2509 // This is more like in minecraft
\r
2510 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2512 camera->setPosition(camera_position);
\r
2513 // *100.0 helps in large map coordinates
\r
2514 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2516 if(FIELD_OF_VIEW_TEST){
\r
2517 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2520 //TimeTaker timer("client.updateCamera");
\r
2521 client.updateCamera(camera_position, camera_direction);
\r
2525 //TimeTaker //timer3("//timer3");
\r
2528 Calculate what block is the crosshair pointing to
\r
2531 //u32 t1 = device->getTimer()->getRealTime();
\r
2533 //f32 d = 4; // max. distance
\r
2534 f32 d = 4; // max. distance
\r
2535 core::line3d<f32> shootline(camera_position,
\r
2536 camera_position + camera_direction * BS * (d+1));
\r
2538 MapBlockObject *selected_object = client.getSelectedObject
\r
2539 (d*BS, camera_position, shootline);
\r
2541 ClientActiveObject *selected_active_object
\r
2542 = client.getSelectedActiveObject
\r
2543 (d*BS, camera_position, shootline);
\r
2545 if(selected_object != NULL)
\r
2547 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2549 core::aabbox3d<f32> box_on_map
\r
2550 = selected_object->getSelectionBoxOnMap();
\r
2552 hilightboxes.push_back(box_on_map);
\r
2554 infotext = narrow_to_wide(selected_object->infoText());
\r
2556 if(g_input->getLeftClicked())
\r
2558 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2559 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2560 selected_object->getId(), g_selected_item);
\r
2562 else if(g_input->getRightClicked())
\r
2564 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2566 Check if we want to modify the object ourselves
\r
2568 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2570 dstream<<"Sign object right-clicked"<<std::endl;
\r
2572 if(random_input == false)
\r
2574 // Get a new text for it
\r
2576 TextDest *dest = new TextDestSign(
\r
2577 selected_object->getBlock()->getPos(),
\r
2578 selected_object->getId(),
\r
2581 SignObject *sign_object = (SignObject*)selected_object;
\r
2583 std::wstring wtext =
\r
2584 narrow_to_wide(sign_object->getText());
\r
2586 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2592 Otherwise pass the event to the server as-is
\r
2596 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2597 selected_object->getId(), g_selected_item);
\r
2601 else if(selected_active_object != NULL)
\r
2603 //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
\r
2605 core::aabbox3d<f32> *selection_box
\r
2606 = selected_active_object->getSelectionBox();
\r
2607 // Box should exist because object was returned in the
\r
2609 assert(selection_box);
\r
2611 v3f pos = selected_active_object->getPosition();
\r
2613 core::aabbox3d<f32> box_on_map(
\r
2614 selection_box->MinEdge + pos,
\r
2615 selection_box->MaxEdge + pos
\r
2618 hilightboxes.push_back(box_on_map);
\r
2620 //infotext = narrow_to_wide("A ClientActiveObject");
\r
2621 infotext = narrow_to_wide(selected_active_object->infoText());
\r
2623 if(g_input->getLeftClicked())
\r
2625 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2626 client.clickActiveObject(0,
\r
2627 selected_active_object->getId(), g_selected_item);
\r
2629 else if(g_input->getRightClicked())
\r
2631 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2634 Check if we want to modify the object ourselves
\r
2636 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2640 Otherwise pass the event to the server as-is
\r
2644 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2645 selected_object->getId(), g_selected_item);
\r
2650 else // selected_object == NULL
\r
2654 Find out which node we are pointing at
\r
2657 bool nodefound = false;
\r
2659 v3s16 neighbourpos;
\r
2660 core::aabbox3d<f32> nodehilightbox;
\r
2661 f32 mindistance = BS * 1001;
\r
2663 v3s16 pos_i = floatToInt(player_position, BS);
\r
2665 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2669 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2670 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2671 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2672 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2673 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2674 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2676 for(s16 y = ystart; y <= yend; y++)
\r
2677 for(s16 z = zstart; z <= zend; z++)
\r
2678 for(s16 x = xstart; x <= xend; x++)
\r
2683 n = client.getNode(v3s16(x,y,z));
\r
2684 if(content_pointable(n.d) == false)
\r
2687 catch(InvalidPositionException &e)
\r
2693 v3f npf = intToFloat(np, BS);
\r
2698 v3s16(0,0,1), // back
\r
2699 v3s16(0,1,0), // top
\r
2700 v3s16(1,0,0), // right
\r
2701 v3s16(0,0,-1), // front
\r
2702 v3s16(0,-1,0), // bottom
\r
2703 v3s16(-1,0,0), // left
\r
2709 if(n.d == CONTENT_TORCH)
\r
2711 v3s16 dir = unpackDir(n.dir);
\r
2712 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2713 dir_f *= BS/2 - BS/6 - BS/20;
\r
2714 v3f cpf = npf + dir_f;
\r
2715 f32 distance = (cpf - camera_position).getLength();
\r
2717 core::aabbox3d<f32> box;
\r
2720 if(dir == v3s16(0,-1,0))
\r
2722 box = core::aabbox3d<f32>(
\r
2723 npf - v3f(BS/6, BS/2, BS/6),
\r
2724 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2728 else if(dir == v3s16(0,1,0))
\r
2730 box = core::aabbox3d<f32>(
\r
2731 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2732 npf + v3f(BS/6, BS/2, BS/6)
\r
2738 box = core::aabbox3d<f32>(
\r
2739 cpf - v3f(BS/6, BS/3, BS/6),
\r
2740 cpf + v3f(BS/6, BS/3, BS/6)
\r
2744 if(distance < mindistance)
\r
2746 if(box.intersectsWithLine(shootline))
\r
2750 neighbourpos = np;
\r
2751 mindistance = distance;
\r
2752 nodehilightbox = box;
\r
2756 else if(n.d == CONTENT_SIGN_WALL)
\r
2758 v3s16 dir = unpackDir(n.dir);
\r
2759 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2760 dir_f *= BS/2 - BS/6 - BS/20;
\r
2761 v3f cpf = npf + dir_f;
\r
2762 f32 distance = (cpf - camera_position).getLength();
\r
2766 v3f(BS*0.42,-BS*0.35,-BS*0.4),
\r
2767 v3f(BS*0.49, BS*0.35, BS*0.4),
\r
2770 for(s32 i=0; i<2; i++)
\r
2772 if(dir == v3s16(1,0,0))
\r
2773 vertices[i].rotateXZBy(0);
\r
2774 if(dir == v3s16(-1,0,0))
\r
2775 vertices[i].rotateXZBy(180);
\r
2776 if(dir == v3s16(0,0,1))
\r
2777 vertices[i].rotateXZBy(90);
\r
2778 if(dir == v3s16(0,0,-1))
\r
2779 vertices[i].rotateXZBy(-90);
\r
2780 if(dir == v3s16(0,-1,0))
\r
2781 vertices[i].rotateXYBy(-90);
\r
2782 if(dir == v3s16(0,1,0))
\r
2783 vertices[i].rotateXYBy(90);
\r
2785 vertices[i] += npf;
\r
2788 core::aabbox3d<f32> box;
\r
2790 box = core::aabbox3d<f32>(vertices[0]);
\r
2791 box.addInternalPoint(vertices[1]);
\r
2793 if(distance < mindistance)
\r
2795 if(box.intersectsWithLine(shootline))
\r
2799 neighbourpos = np;
\r
2800 mindistance = distance;
\r
2801 nodehilightbox = box;
\r
2810 for(u16 i=0; i<6; i++)
\r
2812 v3f dir_f = v3f(dirs[i].X,
\r
2813 dirs[i].Y, dirs[i].Z);
\r
2814 v3f centerpoint = npf + dir_f * BS/2;
\r
2816 (centerpoint - camera_position).getLength();
\r
2818 if(distance < mindistance)
\r
2820 core::CMatrix4<f32> m;
\r
2821 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2823 // This is the back face
\r
2824 v3f corners[2] = {
\r
2825 v3f(BS/2, BS/2, BS/2),
\r
2826 v3f(-BS/2, -BS/2, BS/2+d)
\r
2829 for(u16 j=0; j<2; j++)
\r
2831 m.rotateVect(corners[j]);
\r
2832 corners[j] += npf;
\r
2835 core::aabbox3d<f32> facebox(corners[0]);
\r
2836 facebox.addInternalPoint(corners[1]);
\r
2838 if(facebox.intersectsWithLine(shootline))
\r
2842 neighbourpos = np + dirs[i];
\r
2843 mindistance = distance;
\r
2845 //nodehilightbox = facebox;
\r
2847 const float d = 0.502;
\r
2848 core::aabbox3d<f32> nodebox
\r
2849 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2850 v3f nodepos_f = intToFloat(nodepos, BS);
\r
2851 nodebox.MinEdge += nodepos_f;
\r
2852 nodebox.MaxEdge += nodepos_f;
\r
2853 nodehilightbox = nodebox;
\r
2855 } // if distance < mindistance
\r
2857 } // regular block
\r
2860 static float nodig_delay_counter = 0.0;
\r
2864 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2866 static float dig_time = 0.0;
\r
2867 static u16 dig_index = 0;
\r
2870 Visualize selection
\r
2873 hilightboxes.push_back(nodehilightbox);
\r
2876 Check information text of node
\r
2879 NodeMetadata *meta = client.getNodeMetadata(nodepos);
\r
2882 infotext = narrow_to_wide(meta->infoText());
\r
2885 //MapNode node = client.getNode(nodepos);
\r
2891 if(g_input->getLeftReleased())
\r
2893 client.clearTempMod(nodepos);
\r
2897 if(nodig_delay_counter > 0.0)
\r
2899 nodig_delay_counter -= dtime;
\r
2903 if(nodepos != nodepos_old)
\r
2905 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2906 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2908 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2910 client.clearTempMod(nodepos_old);
\r
2915 if(g_input->getLeftClicked() ||
\r
2916 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2918 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2919 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2921 if(g_input->getLeftClicked())
\r
2923 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2925 if(g_input->getLeftState())
\r
2927 MapNode n = client.getNode(nodepos);
\r
2929 // Get tool name. Default is "" = bare hands
\r
2930 std::string toolname = "";
\r
2931 InventoryList *mlist = local_inventory.getList("main");
\r
2934 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2935 if(item && (std::string)item->getName() == "ToolItem")
\r
2937 ToolItem *titem = (ToolItem*)item;
\r
2938 toolname = titem->getToolName();
\r
2942 // Get digging properties for material and tool
\r
2943 u8 material = n.d;
\r
2944 DiggingProperties prop =
\r
2945 getDiggingProperties(material, toolname);
\r
2947 float dig_time_complete = 0.0;
\r
2949 if(prop.diggable == false)
\r
2951 /*dstream<<"Material "<<(int)material
\r
2952 <<" not diggable with \""
\r
2953 <<toolname<<"\""<<std::endl;*/
\r
2954 // I guess nobody will wait for this long
\r
2955 dig_time_complete = 10000000.0;
\r
2959 dig_time_complete = prop.time;
\r
2962 if(dig_time_complete >= 0.001)
\r
2964 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2965 * dig_time/dig_time_complete);
\r
2967 // This is for torches
\r
2970 dig_index = CRACK_ANIMATION_LENGTH;
\r
2973 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2975 //TimeTaker timer("client.setTempMod");
\r
2976 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2977 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2981 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2982 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2983 client.clearTempMod(nodepos);
\r
2984 client.removeNode(nodepos);
\r
2988 nodig_delay_counter = dig_time_complete
\r
2989 / (float)CRACK_ANIMATION_LENGTH;
\r
2991 // We don't want a corresponding delay to
\r
2992 // very time consuming nodes
\r
2993 if(nodig_delay_counter > 0.5)
\r
2995 nodig_delay_counter = 0.5;
\r
2997 // We want a slight delay to very little
\r
2998 // time consuming nodes
\r
2999 float mindelay = 0.15;
\r
3000 if(nodig_delay_counter < mindelay)
\r
3002 nodig_delay_counter = mindelay;
\r
3006 dig_time += dtime;
\r
3010 if(g_input->getRightClicked())
\r
3012 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
3014 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
\r
3016 dstream<<"Sign node right-clicked"<<std::endl;
\r
3018 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
\r
3020 // Get a new text for it
\r
3022 TextDest *dest = new TextDestSignNode(nodepos, &client);
\r
3024 std::wstring wtext =
\r
3025 narrow_to_wide(signmeta->getText());
\r
3027 (new GUITextInputMenu(guienv, guiroot, -1,
\r
3031 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
\r
3033 dstream<<"Chest node right-clicked"<<std::endl;
\r
3035 //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
\r
3037 std::string chest_inv_id;
\r
3038 chest_inv_id += "nodemeta:";
\r
3039 chest_inv_id += itos(nodepos.X);
\r
3040 chest_inv_id += ",";
\r
3041 chest_inv_id += itos(nodepos.Y);
\r
3042 chest_inv_id += ",";
\r
3043 chest_inv_id += itos(nodepos.Z);
\r
3045 GUIInventoryMenu *menu =
\r
3046 new GUIInventoryMenu(guienv, guiroot, -1,
\r
3047 &g_menumgr, v2s16(8,9),
\r
3048 g_client->getInventoryContext(),
\r
3051 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
3053 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3054 "list", chest_inv_id, "0",
\r
3055 v2s32(0, 0), v2s32(8, 4)));
\r
3056 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
3057 "list", "current_player", "main",
\r
3058 v2s32(0, 5), v2s32(8, 4)));
\r
3060 menu->setDrawSpec(draw_spec);
\r
3065 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
\r
3067 dstream<<"Furnace node right-clicked"<<std::endl;
\r
3069 GUIFurnaceMenu *menu =
\r
3070 new GUIFurnaceMenu(guienv, guiroot, -1,
\r
3071 &g_menumgr, nodepos, g_client);
\r
3078 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
3082 nodepos_old = nodepos;
\r
3087 } // selected_object == NULL
\r
3089 g_input->resetLeftClicked();
\r
3090 g_input->resetRightClicked();
\r
3092 if(g_input->getLeftReleased())
\r
3094 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
3096 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
3098 if(g_input->getRightReleased())
\r
3100 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
3104 g_input->resetLeftReleased();
\r
3105 g_input->resetRightReleased();
\r
3108 Calculate stuff for drawing
\r
3111 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
3113 u32 daynight_ratio = client.getDayNightRatio();
\r
3114 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
3115 video::SColor bgcolor = video::SColor(
\r
3117 skycolor.getRed() * l / 255,
\r
3118 skycolor.getGreen() * l / 255,
\r
3119 skycolor.getBlue() * l / 255);
\r
3125 if(g_settings.getBool("enable_fog") == true)
\r
3127 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
3128 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
3129 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
3130 if(draw_control.range_all)
\r
3131 range = 100000*BS;
\r
3135 video::EFT_FOG_LINEAR,
\r
3139 false, // pixel fog
\r
3140 false // range fog
\r
3147 video::EFT_FOG_LINEAR,
\r
3151 false, // pixel fog
\r
3152 false // range fog
\r
3158 Update gui stuff (0ms)
\r
3161 //TimeTaker guiupdatetimer("Gui updating");
\r
3164 static float drawtime_avg = 0;
\r
3165 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
3166 static float beginscenetime_avg = 0;
\r
3167 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
3168 static float scenetime_avg = 0;
\r
3169 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
3170 static float endscenetime_avg = 0;
\r
3171 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3173 char temptext[300];
\r
3174 snprintf(temptext, 300, "Minetest-c55 ("
\r
3176 ", R: range_all=%i"
\r
3178 " drawtime=%.0f, beginscenetime=%.0f"
\r
3179 ", scenetime=%.0f, endscenetime=%.0f",
\r
3181 draw_control.range_all,
\r
3183 beginscenetime_avg,
\r
3188 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3192 char temptext[300];
\r
3193 snprintf(temptext, 300,
\r
3194 "(% .1f, % .1f, % .1f)"
\r
3195 " (% .3f < btime_jitter < % .3f"
\r
3196 ", dtime_jitter = % .1f %%"
\r
3197 ", v_range = %.1f)",
\r
3198 player_position.X/BS,
\r
3199 player_position.Y/BS,
\r
3200 player_position.Z/BS,
\r
3201 busytime_jitter1_min_sample,
\r
3202 busytime_jitter1_max_sample,
\r
3203 dtime_jitter1_max_fraction * 100.0,
\r
3204 draw_control.wanted_range
\r
3207 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3211 guitext_info->setText(infotext.c_str());
\r
3215 Get chat messages from client
\r
3218 // Get new messages
\r
3219 std::wstring message;
\r
3220 while(client.getChatMessage(message))
\r
3222 chat_lines.push_back(ChatLine(message));
\r
3223 /*if(chat_lines.size() > 6)
\r
3225 core::list<ChatLine>::Iterator
\r
3226 i = chat_lines.begin();
\r
3227 chat_lines.erase(i);
\r
3230 // Append them to form the whole static text and throw
\r
3231 // it to the gui element
\r
3232 std::wstring whole;
\r
3233 // This will correspond to the line number counted from
\r
3234 // top to bottom, from size-1 to 0
\r
3235 s16 line_number = chat_lines.size();
\r
3236 // Count of messages to be removed from the top
\r
3237 u16 to_be_removed_count = 0;
\r
3238 for(core::list<ChatLine>::Iterator
\r
3239 i = chat_lines.begin();
\r
3240 i != chat_lines.end(); i++)
\r
3242 // After this, line number is valid for this loop
\r
3245 (*i).age += dtime;
\r
3247 This results in a maximum age of 60*6 to the
\r
3248 lowermost line and a maximum of 6 lines
\r
3250 float allowed_age = (6-line_number) * 60.0;
\r
3252 if((*i).age > allowed_age)
\r
3254 to_be_removed_count++;
\r
3257 whole += (*i).text + L'\n';
\r
3259 for(u16 i=0; i<to_be_removed_count; i++)
\r
3261 core::list<ChatLine>::Iterator
\r
3262 it = chat_lines.begin();
\r
3263 chat_lines.erase(it);
\r
3265 guitext_chat->setText(whole.c_str());
\r
3267 // Update gui element size and position
\r
3269 /*core::rect<s32> rect(
\r
3271 screensize.Y - guitext_chat_pad_bottom
\r
3272 - text_height*chat_lines.size(),
\r
3273 screensize.X - 10,
\r
3274 screensize.Y - guitext_chat_pad_bottom
\r
3276 core::rect<s32> rect(
\r
3279 screensize.X - 10,
\r
3280 50 + text_height*chat_lines.size()
\r
3283 guitext_chat->setRelativePosition(rect);
\r
3285 if(chat_lines.size() == 0)
\r
3286 guitext_chat->setVisible(false);
\r
3288 guitext_chat->setVisible(true);
\r
3295 static u16 old_selected_item = 65535;
\r
3296 if(client.getLocalInventoryUpdated()
\r
3297 || g_selected_item != old_selected_item)
\r
3299 old_selected_item = g_selected_item;
\r
3300 //std::cout<<"Updating local inventory"<<std::endl;
\r
3301 client.getLocalInventory(local_inventory);
\r
3305 Send actions returned by the inventory menu
\r
3307 while(inventory_action_queue.size() != 0)
\r
3309 InventoryAction *a = inventory_action_queue.pop_front();
\r
3311 client.sendInventoryAction(a);
\r
3320 TimeTaker drawtimer("Drawing");
\r
3324 TimeTaker timer("beginScene");
\r
3325 driver->beginScene(true, true, bgcolor);
\r
3326 //driver->beginScene(false, true, bgcolor);
\r
3327 beginscenetime = timer.stop(true);
\r
3332 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3335 TimeTaker timer("smgr");
\r
3337 scenetime = timer.stop(true);
\r
3341 //TimeTaker timer9("auxiliary drawings");
\r
3345 //TimeTaker //timer10("//timer10");
\r
3347 video::SMaterial m;
\r
3348 //m.Thickness = 10;
\r
3350 m.Lighting = false;
\r
3351 driver->setMaterial(m);
\r
3353 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3355 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3356 i != hilightboxes.end(); i++)
\r
3358 /*std::cout<<"hilightbox min="
\r
3359 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3361 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3363 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3369 if(g_settings.getBool("frametime_graph") == true)
\r
3372 for(core::list<float>::Iterator
\r
3373 i = frametime_log.begin();
\r
3374 i != frametime_log.end();
\r
3377 driver->draw2DLine(v2s32(x,50),
\r
3378 v2s32(x,50+(*i)*1000),
\r
3379 video::SColor(255,255,255,255));
\r
3387 if(g_show_map_plot && g_map_plot_texture)
\r
3389 core::dimension2d<u32> drawdim(640,480);
\r
3390 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3392 (screensize.X-drawdim.Width)/2,
\r
3393 (screensize.Y-drawdim.Height)/2
\r
3395 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3396 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3402 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3403 displaycenter + core::vector2d<s32>(10,0),
\r
3404 video::SColor(255,255,255,255));
\r
3405 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3406 displaycenter + core::vector2d<s32>(0,10),
\r
3407 video::SColor(255,255,255,255));
\r
3412 //TimeTaker //timer11("//timer11");
\r
3418 guienv->drawAll();
\r
3424 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3425 hotbar_imagesize, hotbar_itemcount, &local_inventory);
\r
3430 TimeTaker timer("endScene");
\r
3431 driver->endScene();
\r
3432 endscenetime = timer.stop(true);
\r
3435 drawtime = drawtimer.stop(true);
\r
3443 Refresh map plot if player has moved considerably
\r
3445 if(g_refresh_map_plot)
\r
3447 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3448 v3f p = client.getPlayerPosition() / BS;
\r
3449 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3451 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3452 old_player_pos = p;
\r
3454 g_refresh_map_plot = false;
\r
3458 static s16 lastFPS = 0;
\r
3459 //u16 fps = driver->getFPS();
\r
3460 u16 fps = (1.0/dtime_avg1);
\r
3462 if (lastFPS != fps)
\r
3464 core::stringw str = L"Minetest [";
\r
3465 str += driver->getName();
\r
3469 device->setWindowCaption(str.c_str());
\r
3475 device->yield();*/
\r
3478 //delete quick_inventory;
\r
3481 Disable texture fetches and other stuff that is queued
\r
3482 to be processed by the main loop.
\r
3484 This has to be done before client goes out of scope.
\r
3486 g_irrlicht->Shutdown(true);
\r
3488 } // client and server are deleted at this point
\r
3491 catch(con::PeerNotFoundException &e)
\r
3493 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3494 error_message = L"Connection timed out.";
\r
3497 } // Menu-game loop
\r
3502 In the end, delete the Irrlicht device.
\r
3507 Update configuration file
\r
3509 /*if(configpath != "")
\r
3511 g_settings.updateConfigFile(configpath.c_str());
\r
3514 END_DEBUG_EXCEPTION_HANDLER
\r
3516 debugstreams_deinit();
\r