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: iostream.imbue(std::locale("C")) is very slow
\r
25 NOTE: Global locale is now set at initialization
\r
27 NOTE: If VBO (EHM_STATIC) is used, remember to explicitly free the
\r
28 hardware buffer (it is not freed automatically)
\r
30 Random suggeestions (AKA very old suggestions that haven't been done):
\r
31 ----------------------------------------------------------------------
\r
33 SUGG: Fix address to be ipv6 compatible
\r
35 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
37 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
38 - This enables saving many packets and making a faster connection
\r
39 - This also enables server to check if client has received the
\r
40 most recent block sent, for example.
\r
41 SUGG: Add a sane bandwidth throttling system to Connection
\r
43 SUGG: More fine-grained control of client's dumping of blocks from
\r
45 - ...What does this mean in the first place?
\r
47 SUGG: A map editing mode (similar to dedicated server mode)
\r
49 SUGG: Transfer more blocks in a single packet
\r
50 SUGG: A blockdata combiner class, to which blocks are added and at
\r
51 destruction it sends all the stuff in as few packets as possible.
\r
52 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
53 it by sending more stuff in a single packet.
\r
54 - Add a packet queue to RemoteClient, from which packets will be
\r
55 combined with object data packets
\r
56 - This is not exactly trivial: the object data packets are
\r
57 sometimes very big by themselves
\r
58 - This might not give much network performance gain though.
\r
60 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
61 - This is not doable because it is currently hand-made and not
\r
62 based on some mathematical function.
\r
63 - Note: This has been changing lately
\r
65 SUGG: A version number to blocks, which increments when the block is
\r
66 modified (node add/remove, water update, lighting update)
\r
67 - This can then be used to make sure the most recent version of
\r
68 a block has been sent to client, for example
\r
70 SUGG: Make the amount of blocks sending to client and the total
\r
71 amount of blocks dynamically limited. Transferring blocks is the
\r
72 main network eater of this system, so it is the one that has
\r
73 to be throttled so that RTTs stay low.
\r
75 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
76 different directions and then only those drawn that need to be
\r
78 SUGG: Calculate lighting per vertex to get a lighting effect like in
\r
81 SUGG: Background music based on cellular automata?
\r
82 http://www.earslap.com/projectslab/otomata
\r
88 - Aim for something like controlling a single dwarf in Dwarf Fortress
\r
90 - The player could go faster by a crafting a boat, or riding an animal
\r
92 - Random NPC traders. what else?
\r
96 - When furnace is destroyed, move items to player's inventory
\r
99 - Growing grass, decaying leaves
\r
100 - This can be done in the active blocks I guess.
\r
101 - Lots of stuff can be done in the active blocks.
\r
102 - Uh, is there an active block list somewhere? I think not. Add it.
\r
103 - Breaking weak structures
\r
104 - This can probably be accomplished in the same way as grass
\r
105 - Player health points
\r
106 - When player dies, throw items on map (needs better item-on-map
\r
108 - Cobble to get mossy if near water
\r
109 - More slots in furnace source list, so that multiple ingredients
\r
113 - The Treasure Guard; a big monster with a hammer
\r
114 - The hammer does great damage, shakes the ground and removes a block
\r
115 - You can drop on top of it, and have some time to attack there
\r
116 before he shakes you off
\r
118 - Maybe the difficulty could come from monsters getting tougher in
\r
119 far-away places, and the player starting to need something from
\r
120 there when time goes by.
\r
121 - The player would have some of that stuff at the beginning, and
\r
122 would need new supplies of it when it runs out
\r
125 - A spread-items-on-map routine for the bomb, and for dying players
\r
130 Build system / running:
\r
131 -----------------------
\r
133 Networking and serialization:
\r
134 -----------------------------
\r
136 TODO: Get rid of GotSplitPacketException
\r
141 TODO: Configuration menu, at least for keys
\r
146 SUGG: Combine MapBlock's face caches to so big pieces that VBO
\r
148 - That is >500 vertices
\r
149 - This is not easy; all the MapBlocks close to the player would
\r
150 still need to be drawn separately and combining the blocks
\r
151 would have to happen in a background thread
\r
153 SUGG: Make fetching sector's blocks more efficient when rendering
\r
154 sectors that have very large amounts of blocks (on client)
\r
155 - Is this necessary at all?
\r
157 TODO: Flowing water animation
\r
159 SUGG: Draw cubes in inventory directly with 3D drawing commands, so that
\r
160 animating them is easier.
\r
162 SUGG: Option for enabling proper alpha channel for textures
\r
163 TODO: A setting for enabling bilinear filtering for textures
\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
185 - Tool/weapon visualization
\r
190 SUGG: Make an option to the server to disable building and digging near
\r
191 the starting position
\r
193 FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
\r
195 * Fix the problem with the server constantly saving one or a few
\r
196 blocks? List the first saved block, maybe it explains.
\r
197 - It is probably caused by oscillating water
\r
198 * Make a small history check to transformLiquids to detect and log
\r
199 continuous oscillations, in such detail that they can be fixed.
\r
204 TODO: Get rid of MapBlockObjects and use ActiveObjects
\r
206 SUGG: MovingObject::move and Player::move are basically the same.
\r
208 - NOTE: Player::move is more up-to-date.
\r
209 - NOTE: There is a simple move implementation now in collision.{h,cpp}
\r
214 TODO: Mineral and ground material properties
\r
215 - This way mineral ground toughness can be calculated with just
\r
216 some formula, as well as tool strengths
\r
218 TODO: Flowing water to actually contain flow direction information
\r
219 - There is a space for this - it just has to be implemented.
\r
221 SUGG: Erosion simulation at map generation time
\r
222 - Simulate water flows, which would carve out dirt fast and
\r
223 then turn stone into gravel and sand and relocate it.
\r
224 - How about relocating minerals, too? Coal and gold in
\r
225 downstream sand and gravel would be kind of cool
\r
226 - This would need a better way of handling minerals, mainly
\r
227 to have mineral content as a separate field. the first
\r
228 parameter field is free for this.
\r
229 - Simulate rock falling from cliffs when water has removed
\r
230 enough solid rock from the bottom
\r
232 SUGG: Try out the notch way of generating maps, that is, make bunches
\r
233 of low-res 3d noise and interpolate linearly.
\r
236 * Possibly add some kind of erosion and other stuff
\r
237 * Better water generation (spread it to underwater caverns but don't
\r
238 fill dungeons that don't touch big water masses)
\r
239 * When generating a chunk and the neighboring chunk doesn't have mud
\r
240 and stuff yet and the ground is fairly flat, the mud will flow to
\r
241 the other chunk making nasty straight walls when the other chunk
\r
242 is generated. Fix it. Maybe just a special case if the ground is
\r
247 * Make an "environment metafile" to store at least time of day
\r
248 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...
\r
249 - Or maybe move content_features to material.{h,cpp}?
\r
251 Make a system for pregenerating quick information for mapblocks, so
\r
252 that the client can show them as cubes before they are actually sent
\r
255 Making it more portable:
\r
256 ------------------------
\r
257 * Some MSVC: std::sto* are defined without a namespace and collide
\r
258 with the ones in utility.h
\r
260 ======================================================================
\r
265 Setting this to 1 enables a special camera mode that forces
\r
266 the renderers to think that the camera statically points from
\r
267 the starting place to a static direction.
\r
269 This allows one to move around with the player and see what
\r
270 is actually drawn behind solid things and behind the player.
\r
272 #define FIELD_OF_VIEW_TEST 0
\r
276 #pragma message ("Disabling unit tests")
\r
278 #warning "Disabling unit tests"
\r
280 // Disable unit tests
\r
281 #define ENABLE_TESTS 0
\r
283 // Enable unit tests
\r
284 #define ENABLE_TESTS 1
\r
288 #pragma comment(lib, "Irrlicht.lib")
\r
289 //#pragma comment(lib, "jthread.lib")
\r
290 #pragma comment(lib, "zlibwapi.lib")
\r
291 #pragma comment(lib, "Shell32.lib")
\r
292 // This would get rid of the console window
\r
293 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
296 #include <iostream>
\r
298 #include <jmutexautolock.h>
\r
299 #include <locale.h>
\r
301 #include "common_irrlicht.h"
\r
304 #include "player.h"
\r
306 //#include "environment.h"
\r
307 #include "server.h"
\r
308 #include "client.h"
\r
309 //#include "serialization.h"
\r
310 #include "constants.h"
\r
311 //#include "strfnd.h"
\r
312 #include "porting.h"
\r
313 #include "gettime.h"
\r
314 #include "porting.h"
\r
315 #include "guiPauseMenu.h"
\r
316 #include "guiInventoryMenu.h"
\r
317 #include "guiTextInputMenu.h"
\r
318 #include "materials.h"
\r
319 #include "guiMessageMenu.h"
\r
320 #include "filesys.h"
\r
321 #include "config.h"
\r
322 #include "guiMainMenu.h"
\r
323 #include "mineral.h"
\r
326 #include "guiFurnaceMenu.h"
\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
339 // This is located in defaultsettings.cpp
\r
340 extern void set_default_settings();
\r
346 IrrlichtDevice *g_device = NULL;
\r
347 Client *g_client = NULL;
\r
349 const s32 hotbar_itemcount = 8;
\r
350 const s32 hotbar_imagesize = 36;
\r
356 gui::IGUIEnvironment* guienv = NULL;
\r
357 gui::IGUIStaticText *guiroot = NULL;
\r
359 // Handler for the modal menus
\r
361 class MainMenuManager : public IMenuManager
\r
364 virtual void createdMenu(GUIModalMenu *menu)
\r
366 for(core::list<GUIModalMenu*>::Iterator
\r
367 i = m_stack.begin();
\r
368 i != m_stack.end(); i++)
\r
370 assert(*i != menu);
\r
373 if(m_stack.size() != 0)
\r
374 (*m_stack.getLast())->setVisible(false);
\r
375 m_stack.push_back(menu);
\r
378 virtual void deletingMenu(GUIModalMenu *menu)
\r
380 // Remove all entries if there are duplicates
\r
381 bool removed_entry;
\r
383 removed_entry = false;
\r
384 for(core::list<GUIModalMenu*>::Iterator
\r
385 i = m_stack.begin();
\r
386 i != m_stack.end(); i++)
\r
391 removed_entry = true;
\r
395 }while(removed_entry);
\r
397 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
398 assert(*i == menu);
\r
399 m_stack.erase(i);*/
\r
401 if(m_stack.size() != 0)
\r
402 (*m_stack.getLast())->setVisible(true);
\r
407 return m_stack.size();
\r
410 core::list<GUIModalMenu*> m_stack;
\r
413 MainMenuManager g_menumgr;
\r
415 bool noMenuActive()
\r
417 return (g_menumgr.menuCount() == 0);
\r
420 // Passed to menus to allow disconnecting and exiting
\r
422 class MainGameCallback : public IGameCallback
\r
425 MainGameCallback():
\r
426 disconnect_requested(false)
\r
430 virtual void exitToOS()
\r
432 g_device->closeDevice();
\r
435 virtual void disconnect()
\r
437 disconnect_requested = true;
\r
440 bool disconnect_requested;
\r
443 MainGameCallback g_gamecallback;
\r
449 // Inventory actions from the menu are buffered here before sending
\r
450 Queue<InventoryAction*> inventory_action_queue;
\r
451 // This is a copy of the inventory that the client's environment has
\r
452 Inventory local_inventory;
\r
454 u16 g_selected_item = 0;
\r
461 std::ostream *dout_con_ptr = &dummyout;
\r
462 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
463 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
464 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
465 //std::ostream *dout_con_ptr = &dstream;
\r
466 //std::ostream *derr_con_ptr = &dstream;
\r
469 std::ostream *dout_server_ptr = &dstream;
\r
470 std::ostream *derr_server_ptr = &dstream;
\r
473 std::ostream *dout_client_ptr = &dstream;
\r
474 std::ostream *derr_client_ptr = &dstream;
\r
477 gettime.h implementation
\r
483 Use irrlicht because it is more precise than porting.h's
\r
486 if(g_device == NULL)
\r
488 return g_device->getTimer()->getRealTime();
\r
495 struct TextDestSign : public TextDest
\r
497 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
499 m_blockpos = blockpos;
\r
503 void gotText(std::wstring text)
\r
505 std::string ntext = wide_to_narrow(text);
\r
506 dstream<<"Changing text of a sign object: "
\r
507 <<ntext<<std::endl;
\r
508 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
516 struct TextDestChat : public TextDest
\r
518 TextDestChat(Client *client)
\r
522 void gotText(std::wstring text)
\r
524 // Discard empty line
\r
528 // Parse command (server command starts with "/#")
\r
529 if(text[0] == L'/' && text[1] != L'#')
\r
531 std::wstring reply = L"Local: ";
\r
533 reply += L"Local commands not yet supported. "
\r
534 L"Server prefix is \"/#\".";
\r
536 m_client->addChatMessage(reply);
\r
541 m_client->sendChatMessage(text);
\r
543 m_client->addChatMessage(text);
\r
549 struct TextDestSignNode : public TextDest
\r
551 TextDestSignNode(v3s16 p, Client *client)
\r
556 void gotText(std::wstring text)
\r
558 std::string ntext = wide_to_narrow(text);
\r
559 dstream<<"Changing text of a sign node: "
\r
560 <<ntext<<std::endl;
\r
561 m_client->sendSignNodeText(m_p, ntext);
\r
569 Event handler for Irrlicht
\r
572 class MyEventReceiver : public IEventReceiver
\r
575 // This is the one method that we have to implement
\r
576 virtual bool OnEvent(const SEvent& event)
\r
579 React to nothing here if a menu is active
\r
581 if(noMenuActive() == false)
\r
587 // Remember whether each key is down or up
\r
588 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
590 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
592 if(event.KeyInput.PressedDown)
\r
594 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
595 /*if(g_show_map_plot)
\r
597 if(event.KeyInput.Key == irr::KEY_ESCAPE
\r
598 || event.KeyInput.Key == irr::KEY_KEY_M)
\r
600 g_show_map_plot = false;
\r
609 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
611 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
613 dstream<<DTIME<<"MyEventReceiver: "
\r
614 <<"Launching pause menu"<<std::endl;
\r
615 // It will delete itself by itself
\r
616 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
617 &g_menumgr))->drop();
\r
620 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
622 dstream<<DTIME<<"MyEventReceiver: "
\r
623 <<"Launching inventory"<<std::endl;
\r
625 GUIInventoryMenu *menu =
\r
626 new GUIInventoryMenu(guienv, guiroot, -1,
\r
627 &g_menumgr, v2s16(8,7),
\r
628 g_client->getInventoryContext(),
\r
631 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
632 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
633 "list", "current_player", "main",
\r
634 v2s32(0, 3), v2s32(8, 4)));
\r
635 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
636 "list", "current_player", "craft",
\r
637 v2s32(3, 0), v2s32(3, 3)));
\r
638 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
639 "list", "current_player", "craftresult",
\r
640 v2s32(7, 1), v2s32(1, 1)));
\r
642 menu->setDrawSpec(draw_spec);
\r
648 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
650 TextDest *dest = new TextDestChat(g_client);
\r
652 (new GUITextInputMenu(guienv, guiroot, -1,
\r
659 if(event.KeyInput.Key >= irr::KEY_KEY_0
\r
660 && event.KeyInput.Key <= irr::KEY_KEY_9)
\r
662 u16 s1 = event.KeyInput.Key - irr::KEY_KEY_0;
\r
663 if(event.KeyInput.Key == irr::KEY_KEY_0)
\r
665 if(s1 < PLAYER_INVENTORY_SIZE && s1 < hotbar_itemcount)
\r
666 g_selected_item = s1-1;
\r
667 dstream<<DTIME<<"Selected item: "
\r
668 <<g_selected_item<<std::endl;
\r
671 // Viewing range selection
\r
672 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
674 if(draw_control.range_all)
\r
676 draw_control.range_all = false;
\r
677 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
681 draw_control.range_all = true;
\r
682 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
686 // Print debug stacks
\r
687 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
689 dstream<<"-----------------------------------------"
\r
691 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
692 dstream<<"-----------------------------------------"
\r
694 debug_stacks_print();
\r
698 /*if(event.KeyInput.Key == irr::KEY_KEY_M)
\r
700 dstream<<"Map plot requested"<<std::endl;
\r
701 g_show_map_plot = !g_show_map_plot;
\r
702 if(g_show_map_plot)
\r
703 g_refresh_map_plot = true;
\r
709 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
711 if(noMenuActive() == false)
\r
713 left_active = false;
\r
714 middle_active = false;
\r
715 right_active = false;
\r
719 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
720 left_active = event.MouseInput.isLeftPressed();
\r
721 middle_active = event.MouseInput.isMiddlePressed();
\r
722 right_active = event.MouseInput.isRightPressed();
\r
724 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
726 leftclicked = true;
\r
728 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
730 rightclicked = true;
\r
732 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
734 leftreleased = true;
\r
736 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
738 rightreleased = true;
\r
740 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
742 /*dstream<<"event.MouseInput.Wheel="
\r
743 <<event.MouseInput.Wheel<<std::endl;*/
\r
745 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
\r
746 hotbar_itemcount-1);
\r
747 if(event.MouseInput.Wheel < 0)
\r
749 if(g_selected_item < max_item)
\r
752 g_selected_item = 0;
\r
754 else if(event.MouseInput.Wheel > 0)
\r
756 if(g_selected_item > 0)
\r
759 g_selected_item = max_item;
\r
768 // This is used to check whether a key is being held down
\r
769 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
771 return keyIsDown[keyCode];
\r
776 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
777 keyIsDown[i] = false;
\r
779 leftclicked = false;
\r
780 rightclicked = false;
\r
781 leftreleased = false;
\r
782 rightreleased = false;
\r
784 left_active = false;
\r
785 middle_active = false;
\r
786 right_active = false;
\r
797 bool rightreleased;
\r
800 bool middle_active;
\r
804 // We use this array to store the current state of each key
\r
805 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
808 IrrlichtDevice *m_device;
\r
812 Separated input handler
\r
821 virtual ~InputHandler()
\r
825 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
827 virtual v2s32 getMousePos() = 0;
\r
828 virtual void setMousePos(s32 x, s32 y) = 0;
\r
830 virtual bool getLeftState() = 0;
\r
831 virtual bool getRightState() = 0;
\r
833 virtual bool getLeftClicked() = 0;
\r
834 virtual bool getRightClicked() = 0;
\r
835 virtual void resetLeftClicked() = 0;
\r
836 virtual void resetRightClicked() = 0;
\r
838 virtual bool getLeftReleased() = 0;
\r
839 virtual bool getRightReleased() = 0;
\r
840 virtual void resetLeftReleased() = 0;
\r
841 virtual void resetRightReleased() = 0;
\r
843 virtual void step(float dtime) {};
\r
845 virtual void clear() {};
\r
848 InputHandler *g_input = NULL;
\r
850 class RealInputHandler : public InputHandler
\r
853 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
855 m_receiver(receiver)
\r
858 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
860 return m_receiver->IsKeyDown(keyCode);
\r
862 virtual v2s32 getMousePos()
\r
864 return m_device->getCursorControl()->getPosition();
\r
866 virtual void setMousePos(s32 x, s32 y)
\r
868 m_device->getCursorControl()->setPosition(x, y);
\r
871 virtual bool getLeftState()
\r
873 return m_receiver->left_active;
\r
875 virtual bool getRightState()
\r
877 return m_receiver->right_active;
\r
880 virtual bool getLeftClicked()
\r
882 return m_receiver->leftclicked;
\r
884 virtual bool getRightClicked()
\r
886 return m_receiver->rightclicked;
\r
888 virtual void resetLeftClicked()
\r
890 m_receiver->leftclicked = false;
\r
892 virtual void resetRightClicked()
\r
894 m_receiver->rightclicked = false;
\r
897 virtual bool getLeftReleased()
\r
899 return m_receiver->leftreleased;
\r
901 virtual bool getRightReleased()
\r
903 return m_receiver->rightreleased;
\r
905 virtual void resetLeftReleased()
\r
907 m_receiver->leftreleased = false;
\r
909 virtual void resetRightReleased()
\r
911 m_receiver->rightreleased = false;
\r
916 resetRightClicked();
\r
917 resetLeftClicked();
\r
920 IrrlichtDevice *m_device;
\r
921 MyEventReceiver *m_receiver;
\r
924 class RandomInputHandler : public InputHandler
\r
927 RandomInputHandler()
\r
931 leftclicked = false;
\r
932 rightclicked = false;
\r
933 leftreleased = false;
\r
934 rightreleased = false;
\r
935 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
936 keydown[i] = false;
\r
938 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
940 return keydown[keyCode];
\r
942 virtual v2s32 getMousePos()
\r
946 virtual void setMousePos(s32 x, s32 y)
\r
948 mousepos = v2s32(x,y);
\r
951 virtual bool getLeftState()
\r
955 virtual bool getRightState()
\r
960 virtual bool getLeftClicked()
\r
962 return leftclicked;
\r
964 virtual bool getRightClicked()
\r
966 return rightclicked;
\r
968 virtual void resetLeftClicked()
\r
970 leftclicked = false;
\r
972 virtual void resetRightClicked()
\r
974 rightclicked = false;
\r
977 virtual bool getLeftReleased()
\r
979 return leftreleased;
\r
981 virtual bool getRightReleased()
\r
983 return rightreleased;
\r
985 virtual void resetLeftReleased()
\r
987 leftreleased = false;
\r
989 virtual void resetRightReleased()
\r
991 rightreleased = false;
\r
994 virtual void step(float dtime)
\r
997 static float counter1 = 0;
\r
1001 counter1 = 0.1*Rand(1, 40);
\r
1002 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
1006 static float counter1 = 0;
\r
1007 counter1 -= dtime;
\r
1008 if(counter1 < 0.0)
\r
1010 counter1 = 0.1*Rand(1, 40);
\r
1011 keydown[irr::KEY_KEY_E] = !keydown[irr::KEY_KEY_E];
\r
1015 static float counter1 = 0;
\r
1016 counter1 -= dtime;
\r
1017 if(counter1 < 0.0)
\r
1019 counter1 = 0.1*Rand(1, 40);
\r
1020 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
1024 static float counter1 = 0;
\r
1025 counter1 -= dtime;
\r
1026 if(counter1 < 0.0)
\r
1028 counter1 = 0.1*Rand(1, 40);
\r
1029 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
1033 static float counter1 = 0;
\r
1034 counter1 -= dtime;
\r
1035 if(counter1 < 0.0)
\r
1037 counter1 = 0.1*Rand(1, 20);
\r
1038 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
1042 static float counter1 = 0;
\r
1043 counter1 -= dtime;
\r
1044 if(counter1 < 0.0)
\r
1046 counter1 = 0.1*Rand(1, 30);
\r
1047 leftdown = !leftdown;
\r
1049 leftclicked = true;
\r
1051 leftreleased = true;
\r
1055 static float counter1 = 0;
\r
1056 counter1 -= dtime;
\r
1057 if(counter1 < 0.0)
\r
1059 counter1 = 0.1*Rand(1, 15);
\r
1060 rightdown = !rightdown;
\r
1062 rightclicked = true;
\r
1064 rightreleased = true;
\r
1067 mousepos += mousespeed;
\r
1070 s32 Rand(s32 min, s32 max)
\r
1072 return (myrand()%(max-min+1))+min;
\r
1075 bool keydown[KEY_KEY_CODES_COUNT];
\r
1081 bool rightclicked;
\r
1082 bool leftreleased;
\r
1083 bool rightreleased;
\r
1087 Render distance feedback loop
\r
1090 void updateViewingRange(f32 frametime_in, Client *client)
\r
1092 if(draw_control.range_all == true)
\r
1095 static f32 added_frametime = 0;
\r
1096 static s16 added_frames = 0;
\r
1098 added_frametime += frametime_in;
\r
1099 added_frames += 1;
\r
1101 // Actually this counter kind of sucks because frametime is busytime
\r
1102 static f32 counter = 0;
\r
1103 counter -= frametime_in;
\r
1109 /*dstream<<__FUNCTION_NAME
\r
1110 <<": Collected "<<added_frames<<" frames, total of "
\r
1111 <<added_frametime<<"s."<<std::endl;*/
\r
1113 /*dstream<<"draw_control.blocks_drawn="
\r
1114 <<draw_control.blocks_drawn
\r
1115 <<", draw_control.blocks_would_have_drawn="
\r
1116 <<draw_control.blocks_would_have_drawn
\r
1119 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1120 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1122 draw_control.wanted_min_range = range_min;
\r
1123 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1125 float block_draw_ratio = 1.0;
\r
1126 if(draw_control.blocks_would_have_drawn != 0)
\r
1128 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1129 / (float)draw_control.blocks_would_have_drawn;
\r
1132 // Calculate the average frametime in the case that all wanted
\r
1133 // blocks had been drawn
\r
1134 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1136 added_frametime = 0.0;
\r
1139 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1140 float wanted_frametime = 1.0 / wanted_fps;
\r
1142 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1143 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1145 // If needed frametime change is small, just return
\r
1146 if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
\r
1148 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1152 float range = draw_control.wanted_range;
\r
1153 float new_range = range;
\r
1155 static s16 range_old = 0;
\r
1156 static f32 frametime_old = 0;
\r
1158 float d_range = range - range_old;
\r
1159 f32 d_frametime = frametime - frametime_old;
\r
1160 // A sane default of 30ms per 50 nodes of range
\r
1161 static f32 time_per_range = 30. / 50;
\r
1164 time_per_range = d_frametime / d_range;
\r
1167 // The minimum allowed calculated frametime-range derivative:
\r
1168 // Practically this sets the maximum speed of changing the range.
\r
1169 // The lower this value, the higher the maximum changing speed.
\r
1170 // A low value here results in wobbly range (0.001)
\r
1171 // A high value here results in slow changing range (0.0025)
\r
1172 // SUGG: This could be dynamically adjusted so that when
\r
1173 // the camera is turning, this is lower
\r
1174 //float min_time_per_range = 0.0015;
\r
1175 float min_time_per_range = 0.0010;
\r
1176 //float min_time_per_range = 0.05 / range;
\r
1177 if(time_per_range < min_time_per_range)
\r
1179 time_per_range = min_time_per_range;
\r
1180 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1184 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1187 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1188 // Dampen the change a bit to kill oscillations
\r
1189 //wanted_range_change *= 0.9;
\r
1190 //wanted_range_change *= 0.75;
\r
1191 wanted_range_change *= 0.5;
\r
1192 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1194 // If needed range change is very small, just return
\r
1195 if(fabs(wanted_range_change) < 0.001)
\r
1197 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1201 new_range += wanted_range_change;
\r
1202 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1204 //float new_range_unclamped = new_range;
\r
1205 if(new_range < range_min)
\r
1206 new_range = range_min;
\r
1207 if(new_range > range_max)
\r
1208 new_range = range_max;
\r
1210 /*if(new_range != new_range_unclamped)
\r
1211 dstream<<", clamped to "<<new_range<<std::endl;
\r
1213 dstream<<std::endl;*/
\r
1215 draw_control.wanted_range = new_range;
\r
1217 range_old = new_range;
\r
1218 frametime_old = frametime;
\r
1222 Hotbar draw routine
\r
1225 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
\r
1226 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
\r
1227 Inventory *inventory, s32 halfheartcount)
\r
1229 InventoryList *mainlist = inventory->getList("main");
\r
1230 if(mainlist == NULL)
\r
1232 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
\r
1236 s32 padding = imgsize/12;
\r
1237 //s32 height = imgsize + padding*2;
\r
1238 s32 width = itemcount*(imgsize+padding*2);
\r
1240 // Position of upper left corner of bar
\r
1241 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
\r
1243 // Draw background color
\r
1244 /*core::rect<s32> barrect(0,0,width,height);
\r
1246 video::SColor bgcolor(255,128,128,128);
\r
1247 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
\r
1249 core::rect<s32> imgrect(0,0,imgsize,imgsize);
\r
1251 for(s32 i=0; i<itemcount; i++)
\r
1253 InventoryItem *item = mainlist->getItem(i);
\r
1255 core::rect<s32> rect = imgrect + pos
\r
1256 + v2s32(padding+i*(imgsize+padding*2), padding);
\r
1258 if(g_selected_item == i)
\r
1260 driver->draw2DRectangle(video::SColor(255,255,0,0),
\r
1261 core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
\r
1262 rect.LowerRightCorner + v2s32(1,1)*padding),
\r
1267 video::SColor bgcolor2(128,0,0,0);
\r
1268 driver->draw2DRectangle(bgcolor2, rect, NULL);
\r
1273 drawInventoryItem(driver, font, item, rect, NULL);
\r
1281 video::ITexture *heart_texture =
\r
1282 driver->getTexture(porting::getDataPath("heart.png").c_str());
\r
1283 v2s32 p = pos + v2s32(0, -20);
\r
1284 for(s32 i=0; i<halfheartcount/2; i++)
\r
1286 const video::SColor color(255,255,255,255);
\r
1287 const video::SColor colors[] = {color,color,color,color};
\r
1288 core::rect<s32> rect(0,0,16,16);
\r
1290 driver->draw2DImage(heart_texture, rect,
\r
1291 core::rect<s32>(core::position2d<s32>(0,0),
\r
1292 core::dimension2di(heart_texture->getOriginalSize())),
\r
1293 NULL, colors, true);
\r
1296 if(halfheartcount % 2 == 1)
\r
1298 const video::SColor color(255,255,255,255);
\r
1299 const video::SColor colors[] = {color,color,color,color};
\r
1300 core::rect<s32> rect(0,0,16/2,16);
\r
1302 core::dimension2di srcd(heart_texture->getOriginalSize());
\r
1304 driver->draw2DImage(heart_texture, rect,
\r
1305 core::rect<s32>(core::position2d<s32>(0,0), srcd),
\r
1306 NULL, colors, true);
\r
1319 ChatLine(const std::wstring &a_text):
\r
1325 std::wstring text;
\r
1328 // These are defined global so that they're not optimized too much.
\r
1329 // Can't change them to volatile.
\r
1334 std::string tempstring;
\r
1335 std::string tempstring2;
\r
1340 dstream<<"The following test should take around 20ms."<<std::endl;
\r
1341 TimeTaker timer("Testing std::string speed");
\r
1342 const u32 jj = 10000;
\r
1343 for(u32 j=0; j<jj; j++)
\r
1347 const u32 ii = 10;
\r
1348 for(u32 i=0; i<ii; i++){
\r
1349 tempstring2 += "asd";
\r
1351 for(u32 i=0; i<ii+1; i++){
\r
1352 tempstring += "asd";
\r
1353 if(tempstring == tempstring2)
\r
1359 dstream<<"All of the following tests should take around 100ms each."
\r
1363 TimeTaker timer("Testing floating-point conversion speed");
\r
1365 for(u32 i=0; i<4000000; i++){
\r
1372 TimeTaker timer("Testing floating-point vector speed");
\r
1374 tempv3f1 = v3f(1,2,3);
\r
1375 tempv3f2 = v3f(4,5,6);
\r
1376 for(u32 i=0; i<10000000; i++){
\r
1377 tempf += tempv3f1.dotProduct(tempv3f2);
\r
1378 tempv3f2 += v3f(7,8,9);
\r
1383 TimeTaker timer("Testing core::map speed");
\r
1385 core::map<v2s16, f32> map1;
\r
1388 for(s16 y=0; y<ii; y++){
\r
1389 for(s16 x=0; x<ii; x++){
\r
1390 map1.insert(v2s16(x,y), tempf);
\r
1394 for(s16 y=ii-1; y>=0; y--){
\r
1395 for(s16 x=0; x<ii; x++){
\r
1396 tempf = map1[v2s16(x,y)];
\r
1402 dstream<<"Around 5000/ms should do well here."<<std::endl;
\r
1403 TimeTaker timer("Testing mutex speed");
\r
1416 // Do at least 10ms
\r
1417 while(timer.getTime() < 10);
\r
1419 u32 dtime = timer.stop();
\r
1420 u32 per_ms = n / dtime;
\r
1421 std::cout<<"Done. "<<dtime<<"ms, "
\r
1422 <<per_ms<<"/ms"<<std::endl;
\r
1426 void getPointedNode(v3f player_position,
\r
1427 v3f camera_direction, v3f camera_position,
\r
1428 bool &nodefound, core::line3d<f32> shootline,
\r
1429 v3s16 &nodepos, v3s16 &neighbourpos,
\r
1430 core::aabbox3d<f32> &nodehilightbox,
\r
1435 f32 mindistance = BS * 1001;
\r
1437 v3s16 pos_i = floatToInt(player_position, BS);
\r
1439 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1443 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1444 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1445 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1446 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1447 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1448 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1450 for(s16 y = ystart; y <= yend; y++)
\r
1451 for(s16 z = zstart; z <= zend; z++)
\r
1452 for(s16 x = xstart; x <= xend; x++)
\r
1457 n = g_client->getNode(v3s16(x,y,z));
\r
1458 if(content_pointable(n.d) == false)
\r
1461 catch(InvalidPositionException &e)
\r
1467 v3f npf = intToFloat(np, BS);
\r
1472 v3s16(0,0,1), // back
\r
1473 v3s16(0,1,0), // top
\r
1474 v3s16(1,0,0), // right
\r
1475 v3s16(0,0,-1), // front
\r
1476 v3s16(0,-1,0), // bottom
\r
1477 v3s16(-1,0,0), // left
\r
1483 if(n.d == CONTENT_TORCH)
\r
1485 v3s16 dir = unpackDir(n.dir);
\r
1486 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1487 dir_f *= BS/2 - BS/6 - BS/20;
\r
1488 v3f cpf = npf + dir_f;
\r
1489 f32 distance = (cpf - camera_position).getLength();
\r
1491 core::aabbox3d<f32> box;
\r
1494 if(dir == v3s16(0,-1,0))
\r
1496 box = core::aabbox3d<f32>(
\r
1497 npf - v3f(BS/6, BS/2, BS/6),
\r
1498 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
1502 else if(dir == v3s16(0,1,0))
\r
1504 box = core::aabbox3d<f32>(
\r
1505 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
1506 npf + v3f(BS/6, BS/2, BS/6)
\r
1512 box = core::aabbox3d<f32>(
\r
1513 cpf - v3f(BS/6, BS/3, BS/6),
\r
1514 cpf + v3f(BS/6, BS/3, BS/6)
\r
1518 if(distance < mindistance)
\r
1520 if(box.intersectsWithLine(shootline))
\r
1524 neighbourpos = np;
\r
1525 mindistance = distance;
\r
1526 nodehilightbox = box;
\r
1530 else if(n.d == CONTENT_SIGN_WALL)
\r
1532 v3s16 dir = unpackDir(n.dir);
\r
1533 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1534 dir_f *= BS/2 - BS/6 - BS/20;
\r
1535 v3f cpf = npf + dir_f;
\r
1536 f32 distance = (cpf - camera_position).getLength();
\r
1540 v3f(BS*0.42,-BS*0.35,-BS*0.4),
\r
1541 v3f(BS*0.49, BS*0.35, BS*0.4),
\r
1544 for(s32 i=0; i<2; i++)
\r
1546 if(dir == v3s16(1,0,0))
\r
1547 vertices[i].rotateXZBy(0);
\r
1548 if(dir == v3s16(-1,0,0))
\r
1549 vertices[i].rotateXZBy(180);
\r
1550 if(dir == v3s16(0,0,1))
\r
1551 vertices[i].rotateXZBy(90);
\r
1552 if(dir == v3s16(0,0,-1))
\r
1553 vertices[i].rotateXZBy(-90);
\r
1554 if(dir == v3s16(0,-1,0))
\r
1555 vertices[i].rotateXYBy(-90);
\r
1556 if(dir == v3s16(0,1,0))
\r
1557 vertices[i].rotateXYBy(90);
\r
1559 vertices[i] += npf;
\r
1562 core::aabbox3d<f32> box;
\r
1564 box = core::aabbox3d<f32>(vertices[0]);
\r
1565 box.addInternalPoint(vertices[1]);
\r
1567 if(distance < mindistance)
\r
1569 if(box.intersectsWithLine(shootline))
\r
1573 neighbourpos = np;
\r
1574 mindistance = distance;
\r
1575 nodehilightbox = box;
\r
1584 for(u16 i=0; i<6; i++)
\r
1586 v3f dir_f = v3f(dirs[i].X,
\r
1587 dirs[i].Y, dirs[i].Z);
\r
1588 v3f centerpoint = npf + dir_f * BS/2;
\r
1590 (centerpoint - camera_position).getLength();
\r
1592 if(distance < mindistance)
\r
1594 core::CMatrix4<f32> m;
\r
1595 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1597 // This is the back face
\r
1598 v3f corners[2] = {
\r
1599 v3f(BS/2, BS/2, BS/2),
\r
1600 v3f(-BS/2, -BS/2, BS/2+d)
\r
1603 for(u16 j=0; j<2; j++)
\r
1605 m.rotateVect(corners[j]);
\r
1606 corners[j] += npf;
\r
1609 core::aabbox3d<f32> facebox(corners[0]);
\r
1610 facebox.addInternalPoint(corners[1]);
\r
1612 if(facebox.intersectsWithLine(shootline))
\r
1616 neighbourpos = np + dirs[i];
\r
1617 mindistance = distance;
\r
1619 //nodehilightbox = facebox;
\r
1621 const float d = 0.502;
\r
1622 core::aabbox3d<f32> nodebox
\r
1623 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
1624 v3f nodepos_f = intToFloat(nodepos, BS);
\r
1625 nodebox.MinEdge += nodepos_f;
\r
1626 nodebox.MaxEdge += nodepos_f;
\r
1627 nodehilightbox = nodebox;
\r
1629 } // if distance < mindistance
\r
1631 } // regular block
\r
1635 int main(int argc, char *argv[])
\r
1638 Parse command line
\r
1641 // List all allowed options
\r
1642 core::map<std::string, ValueSpec> allowed_options;
\r
1643 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1644 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1645 "Run server directly"));
\r
1646 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1647 "Load configuration from specified file"));
\r
1648 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1649 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1650 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1651 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1652 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1653 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1655 allowed_options.insert("dstream-on-stderr", ValueSpec(VALUETYPE_FLAG));
\r
1657 allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG));
\r
1659 Settings cmd_args;
\r
1661 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1663 if(ret == false || cmd_args.getFlag("help"))
\r
1665 dstream<<"Allowed options:"<<std::endl;
\r
1666 for(core::map<std::string, ValueSpec>::Iterator
\r
1667 i = allowed_options.getIterator();
\r
1668 i.atEnd() == false; i++)
\r
1670 dstream<<" --"<<i.getNode()->getKey();
\r
1671 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1676 dstream<<" <value>";
\r
1678 dstream<<std::endl;
\r
1680 if(i.getNode()->getValue().help != NULL)
\r
1682 dstream<<" "<<i.getNode()->getValue().help
\r
1687 return cmd_args.getFlag("help") ? 0 : 1;
\r
1691 Low-level initialization
\r
1694 bool disable_stderr = false;
\r
1696 if(cmd_args.getFlag("dstream-on-stderr") == false)
\r
1697 disable_stderr = true;
\r
1700 // Initialize debug streams
\r
1701 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1702 // Initialize debug stacks
\r
1703 debug_stacks_init();
\r
1705 DSTACK(__FUNCTION_NAME);
\r
1707 porting::signal_handler_init();
\r
1708 bool &kill = *porting::signal_handler_killstatus();
\r
1710 porting::initializePaths();
\r
1711 // Create user data directory
\r
1712 fs::CreateDir(porting::path_userdata);
\r
1714 // C-style stuff initialization
\r
1715 initializeMaterialProperties();
\r
1718 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1720 // Print startup message
\r
1721 dstream<<DTIME<<"minetest-c55"
\r
1722 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1723 <<", "<<BUILD_INFO
\r
1727 Basic initialization
\r
1730 // Initialize default settings
\r
1731 set_default_settings();
\r
1733 // Set locale. This is for forcing '.' as the decimal point.
\r
1734 std::locale::global(std::locale("C"));
\r
1735 // This enables printing all characters in bitmap font
\r
1736 setlocale(LC_CTYPE, "en_US");
\r
1738 // Initialize sockets
\r
1740 atexit(sockets_cleanup);
\r
1750 // Path of configuration file in use
\r
1751 std::string configpath = "";
\r
1753 if(cmd_args.exists("config"))
\r
1755 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1758 dstream<<"Could not read configuration from \""
\r
1759 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1762 configpath = cmd_args.get("config");
\r
1766 core::array<std::string> filenames;
\r
1767 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1768 #ifdef RUN_IN_PLACE
\r
1769 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1772 for(u32 i=0; i<filenames.size(); i++)
\r
1774 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1777 configpath = filenames[i];
\r
1782 // If no path found, use the first one (menu creates the file)
\r
1783 if(configpath == "")
\r
1784 configpath = filenames[0];
\r
1787 // Initialize random seed
\r
1792 Pre-initialize some stuff with a dummy irrlicht wrapper.
\r
1794 These are needed for unit tests at least.
\r
1797 // Initial call with g_texturesource not set.
\r
1804 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1805 || cmd_args.getFlag("enable-unittests") == true)
\r
1810 /*for(s16 y=-100; y<100; y++)
\r
1811 for(s16 x=-100; x<100; x++)
\r
1813 std::cout<<noise2d_gradient((double)x/10,(double)y/10, 32415)<<std::endl;
\r
1823 if(cmd_args.exists("port"))
\r
1824 port = cmd_args.getU16("port");
\r
1825 else if(g_settings.exists("port"))
\r
1826 port = g_settings.getU16("port");
\r
1829 std::string map_dir = porting::path_userdata+"/map";
\r
1830 if(cmd_args.exists("map-dir"))
\r
1831 map_dir = cmd_args.get("map-dir");
\r
1832 else if(g_settings.exists("map-dir"))
\r
1833 map_dir = g_settings.get("map-dir");
\r
1835 // Run dedicated server if asked to
\r
1836 if(cmd_args.getFlag("server"))
\r
1838 DSTACK("Dedicated server branch");
\r
1841 Server server(map_dir.c_str());
\r
1842 server.start(port);
\r
1845 dedicated_server_loop(server, kill);
\r
1854 // Address to connect to
\r
1855 std::string address = "";
\r
1857 if(cmd_args.exists("address"))
\r
1859 address = cmd_args.get("address");
\r
1863 address = g_settings.get("address");
\r
1866 std::string playername = g_settings.get("name");
\r
1868 // Resolution selection
\r
1870 bool fullscreen = false;
\r
1871 u16 screenW = g_settings.getU16("screenW");
\r
1872 u16 screenH = g_settings.getU16("screenH");
\r
1874 // Determine driver
\r
1876 video::E_DRIVER_TYPE driverType;
\r
1878 std::string driverstring = g_settings.get("video_driver");
\r
1880 if(driverstring == "null")
\r
1881 driverType = video::EDT_NULL;
\r
1882 else if(driverstring == "software")
\r
1883 driverType = video::EDT_SOFTWARE;
\r
1884 else if(driverstring == "burningsvideo")
\r
1885 driverType = video::EDT_BURNINGSVIDEO;
\r
1886 else if(driverstring == "direct3d8")
\r
1887 driverType = video::EDT_DIRECT3D8;
\r
1888 else if(driverstring == "direct3d9")
\r
1889 driverType = video::EDT_DIRECT3D9;
\r
1890 else if(driverstring == "opengl")
\r
1891 driverType = video::EDT_OPENGL;
\r
1894 dstream<<"WARNING: Invalid video_driver specified; defaulting "
\r
1895 "to opengl"<<std::endl;
\r
1896 driverType = video::EDT_OPENGL;
\r
1899 // create device and exit if creation failed
\r
1901 MyEventReceiver receiver;
\r
1903 IrrlichtDevice *device;
\r
1904 device = createDevice(driverType,
\r
1905 core::dimension2d<u32>(screenW, screenH),
\r
1906 16, fullscreen, false, false, &receiver);
\r
1909 return 1; // could not create selected driver.
\r
1911 g_device = device;
\r
1912 TextureSource *texturesource = new TextureSource(device);
\r
1913 g_texturesource = texturesource;
\r
1916 Speed tests (done after irrlicht is loaded to get timer)
\r
1918 if(cmd_args.getFlag("speedtests"))
\r
1920 dstream<<"Running speed tests"<<std::endl;
\r
1925 device->setResizable(true);
\r
1927 bool random_input = g_settings.getBool("random_input")
\r
1928 || cmd_args.getFlag("random-input");
\r
1930 g_input = new RandomInputHandler();
\r
1932 g_input = new RealInputHandler(device, &receiver);
\r
1935 Continue initialization
\r
1938 video::IVideoDriver* driver = device->getVideoDriver();
\r
1941 This changes the minimum allowed number of vertices in a VBO.
\r
1944 //driver->setMinHardwareBufferVertexCount(50);
\r
1946 scene::ISceneManager* smgr = device->getSceneManager();
\r
1948 guienv = device->getGUIEnvironment();
\r
1949 gui::IGUISkin* skin = guienv->getSkin();
\r
1950 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1952 skin->setFont(font);
\r
1954 dstream<<"WARNING: Font file was not found."
\r
1955 " Using default font."<<std::endl;
\r
1956 // If font was not found, this will get us one
\r
1957 font = skin->getFont();
\r
1960 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1961 dstream<<"text_height="<<text_height<<std::endl;
\r
1963 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1964 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1965 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1966 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1967 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1968 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1971 Preload some textures and stuff
\r
1974 init_content_inventory_texture_paths();
\r
1975 init_mapnode(); // Second call with g_texturesource set
\r
1983 We need some kind of a root node to be able to add
\r
1984 custom gui elements directly on the screen.
\r
1985 Otherwise they won't be automatically drawn.
\r
1987 guiroot = guienv->addStaticText(L"",
\r
1988 core::rect<s32>(0, 0, 10000, 10000));
\r
1990 // First line of debug text
\r
1991 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1993 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1995 // Second line of debug text
\r
1996 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1998 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
2001 // At the middle of the screen
\r
2002 // Object infos are shown in this
\r
2003 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
2005 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
\r
2009 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
2011 core::rect<s32>(0,0,0,0),
\r
2012 false, false); // Disable word wrap as of now
\r
2014 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
2015 core::list<ChatLine> chat_lines;
\r
2018 If an error occurs, this is set to something and the
\r
2019 menu-game loop is restarted. It is then displayed before
\r
2022 std::wstring error_message = L"";
\r
2027 while(g_device->run() && kill == false)
\r
2030 // This is used for catching disconnects
\r
2035 Out-of-game menu loop.
\r
2037 Loop quits when menu returns proper parameters.
\r
2039 while(kill == false)
\r
2041 // Cursor can be non-visible when coming from the game
\r
2042 device->getCursorControl()->setVisible(true);
\r
2043 // Some stuff are left to scene manager when coming from the game
\r
2044 // (map at least?)
\r
2046 // Reset or hide the debug gui texts
\r
2047 guitext->setText(L"Minetest-c55");
\r
2048 guitext2->setVisible(false);
\r
2049 guitext_info->setVisible(false);
\r
2050 guitext_chat->setVisible(false);
\r
2052 // Initialize menu data
\r
2053 MainMenuData menudata;
\r
2054 menudata.address = narrow_to_wide(address);
\r
2055 menudata.name = narrow_to_wide(playername);
\r
2056 menudata.port = narrow_to_wide(itos(port));
\r
2057 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
2059 GUIMainMenu *menu =
\r
2060 new GUIMainMenu(guienv, guiroot, -1,
\r
2061 &g_menumgr, &menudata, &g_gamecallback);
\r
2062 menu->allowFocusRemoval(true);
\r
2064 if(error_message != L"")
\r
2066 GUIMessageMenu *menu2 =
\r
2067 new GUIMessageMenu(guienv, guiroot, -1,
\r
2068 &g_menumgr, error_message.c_str());
\r
2070 error_message = L"";
\r
2073 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2075 dstream<<"Created main menu"<<std::endl;
\r
2077 while(g_device->run() && kill == false)
\r
2079 if(menu->getStatus() == true)
\r
2082 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2083 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
2084 guienv->drawAll();
\r
2085 driver->endScene();
\r
2088 // Break out of menu-game loop to shut down cleanly
\r
2089 if(g_device->run() == false || kill == true)
\r
2092 dstream<<"Dropping main menu"<<std::endl;
\r
2096 // Delete map if requested
\r
2097 if(menudata.delete_map)
\r
2099 bool r = fs::RecursiveDeleteContent(map_dir);
\r
2101 error_message = L"Delete failed";
\r
2105 playername = wide_to_narrow(menudata.name);
\r
2106 address = wide_to_narrow(menudata.address);
\r
2107 int newport = stoi(wide_to_narrow(menudata.port));
\r
2110 //port = stoi(wide_to_narrow(menudata.port));
\r
2111 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
2113 // Check for valid parameters, restart menu if invalid.
\r
2114 if(playername == "")
\r
2116 error_message = L"Name required.";
\r
2121 g_settings.set("name", playername);
\r
2122 g_settings.set("address", address);
\r
2123 g_settings.set("port", itos(port));
\r
2124 // Update configuration file
\r
2125 if(configpath != "")
\r
2126 g_settings.updateConfigFile(configpath.c_str());
\r
2128 // Continue to game
\r
2132 // Break out of menu-game loop to shut down cleanly
\r
2133 if(g_device->run() == false)
\r
2137 Make a scope here so that the client and the server and other
\r
2138 stuff gets removed when disconnected or the irrlicht device
\r
2144 Draw "Loading" screen
\r
2146 const wchar_t *text = L"Loading and connecting...";
\r
2147 core::vector2d<s32> center(screenW/2, screenH/2);
\r
2148 core::vector2d<s32> textsize(300, text_height);
\r
2149 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
2151 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
2152 text, textrect, false, false);
\r
2153 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
2155 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2156 guienv->drawAll();
\r
2157 driver->endScene();
\r
2159 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
2163 SharedPtr will delete it when it goes out of scope.
\r
2165 SharedPtr<Server> server;
\r
2166 if(address == ""){
\r
2167 server = new Server(map_dir);
\r
2168 server->start(port);
\r
2175 Client client(device, playername.c_str(), draw_control);
\r
2177 g_client = &client;
\r
2179 Address connect_address(0,0,0,0, port);
\r
2182 //connect_address.Resolve("localhost");
\r
2183 connect_address.setAddress(127,0,0,1);
\r
2185 connect_address.Resolve(address.c_str());
\r
2187 catch(ResolveError &e)
\r
2189 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
2191 error_message = L"Couldn't resolve address";
\r
2192 gui_loadingtext->remove();
\r
2196 dstream<<DTIME<<"Connecting to server at ";
\r
2197 connect_address.print(&dstream);
\r
2198 dstream<<std::endl;
\r
2199 client.connect(connect_address);
\r
2202 while(client.connectedAndInitialized() == false)
\r
2205 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2206 guienv->drawAll();
\r
2207 driver->endScene();
\r
2209 // Update client and server
\r
2213 if(server != NULL)
\r
2214 server->step(0.1);
\r
2220 catch(con::PeerNotFoundException &e)
\r
2222 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
2224 error_message = L"Connection timed out.";
\r
2225 gui_loadingtext->remove();
\r
2232 /*scene::ISceneNode* skybox;
\r
2233 skybox = smgr->addSkyBoxSceneNode(
\r
2234 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
2235 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
2236 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2237 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2238 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
2239 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
2242 Create the camera node
\r
2245 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
2246 0, // Camera parent
\r
2247 v3f(BS*100, BS*2, BS*100), // Look from
\r
2248 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
2252 if(camera == NULL)
\r
2255 //video::SColor skycolor = video::SColor(255,90,140,200);
\r
2256 //video::SColor skycolor = video::SColor(255,166,202,244);
\r
2257 //video::SColor skycolor = video::SColor(255,120,185,244);
\r
2258 video::SColor skycolor = video::SColor(255,140,186,250);
\r
2260 camera->setFOV(FOV_ANGLE);
\r
2262 // Just so big a value that everything rendered is visible
\r
2263 camera->setFarValue(100000*BS);
\r
2265 f32 camera_yaw = 0; // "right/left"
\r
2266 f32 camera_pitch = 0; // "up/down"
\r
2272 gui_loadingtext->remove();
\r
2275 Add some gui stuff
\r
2278 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2279 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
\r
2280 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
2281 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
\r
2283 // Test the text input system
\r
2284 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
2286 /*GUIMessageMenu *menu =
\r
2287 new GUIMessageMenu(guienv, guiroot, -1,
\r
2292 // Launch pause menu
\r
2293 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
2294 &g_menumgr))->drop();
\r
2297 guitext2->setVisible(true);
\r
2298 guitext_info->setVisible(true);
\r
2299 guitext_chat->setVisible(true);
\r
2301 //s32 guitext_chat_pad_bottom = 70;
\r
2303 v2u32 screensize(0,0);
\r
2304 v2u32 last_screensize(0,0);
\r
2307 Some statistics are collected in these
\r
2310 u32 beginscenetime = 0;
\r
2311 u32 scenetime = 0;
\r
2312 u32 endscenetime = 0;
\r
2315 //throw con::PeerNotFoundException("lol");
\r
2317 core::list<float> frametime_log;
\r
2319 float damage_flash_timer = 0;
\r
2325 bool first_loop_after_window_activation = true;
\r
2327 // Time is in milliseconds
\r
2328 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
2329 // NOTE: So we have to use getTime() and call run()s between them
\r
2330 u32 lasttime = device->getTimer()->getTime();
\r
2332 while(device->run() && kill == false)
\r
2334 if(g_gamecallback.disconnect_requested)
\r
2336 g_gamecallback.disconnect_requested = false;
\r
2341 Process TextureSource's queue
\r
2343 texturesource->processQueue();
\r
2346 Random calculations
\r
2348 last_screensize = screensize;
\r
2349 screensize = driver->getScreenSize();
\r
2350 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
\r
2351 //bool screensize_changed = screensize != last_screensize;
\r
2353 // Hilight boxes collected during the loop and displayed
\r
2354 core::list< core::aabbox3d<f32> > hilightboxes;
\r
2357 std::wstring infotext;
\r
2359 // When screen size changes, update positions and sizes of stuff
\r
2360 /*if(screensize_changed)
\r
2362 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
\r
2363 quick_inventory->updatePosition(pos);
\r
2366 //TimeTaker //timer1("//timer1");
\r
2368 // Time of frame without fps limit
\r
2372 // not using getRealTime is necessary for wine
\r
2373 u32 time = device->getTimer()->getTime();
\r
2374 if(time > lasttime)
\r
2375 busytime_u32 = time - lasttime;
\r
2378 busytime = busytime_u32 / 1000.0;
\r
2381 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
2383 // Necessary for device->getTimer()->getTime()
\r
2390 updateViewingRange(busytime, &client);
\r
2397 float fps_max = g_settings.getFloat("fps_max");
\r
2398 u32 frametime_min = 1000./fps_max;
\r
2400 if(busytime_u32 < frametime_min)
\r
2402 u32 sleeptime = frametime_min - busytime_u32;
\r
2403 device->sleep(sleeptime);
\r
2407 // Necessary for device->getTimer()->getTime()
\r
2411 Time difference calculation
\r
2413 f32 dtime; // in seconds
\r
2415 u32 time = device->getTimer()->getTime();
\r
2416 if(time > lasttime)
\r
2417 dtime = (time - lasttime) / 1000.0;
\r
2423 Log frametime for visualization
\r
2425 frametime_log.push_back(dtime);
\r
2426 if(frametime_log.size() > 100)
\r
2428 core::list<float>::Iterator i = frametime_log.begin();
\r
2429 frametime_log.erase(i);
\r
2433 Visualize frametime in terminal
\r
2435 /*for(u32 i=0; i<dtime*400; i++)
\r
2437 std::cout<<std::endl;*/
\r
2440 Time average and jitter calculation
\r
2443 static f32 dtime_avg1 = 0.0;
\r
2444 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
2445 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2447 static f32 dtime_jitter1_max_sample = 0.0;
\r
2448 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2450 static f32 jitter1_max = 0.0;
\r
2451 static f32 counter = 0.0;
\r
2452 if(dtime_jitter1 > jitter1_max)
\r
2453 jitter1_max = dtime_jitter1;
\r
2458 dtime_jitter1_max_sample = jitter1_max;
\r
2459 dtime_jitter1_max_fraction
\r
2460 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2461 jitter1_max = 0.0;
\r
2466 Busytime average and jitter calculation
\r
2469 static f32 busytime_avg1 = 0.0;
\r
2470 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2471 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2473 static f32 busytime_jitter1_max_sample = 0.0;
\r
2474 static f32 busytime_jitter1_min_sample = 0.0;
\r
2476 static f32 jitter1_max = 0.0;
\r
2477 static f32 jitter1_min = 0.0;
\r
2478 static f32 counter = 0.0;
\r
2479 if(busytime_jitter1 > jitter1_max)
\r
2480 jitter1_max = busytime_jitter1;
\r
2481 if(busytime_jitter1 < jitter1_min)
\r
2482 jitter1_min = busytime_jitter1;
\r
2484 if(counter > 0.0){
\r
2486 busytime_jitter1_max_sample = jitter1_max;
\r
2487 busytime_jitter1_min_sample = jitter1_min;
\r
2488 jitter1_max = 0.0;
\r
2489 jitter1_min = 0.0;
\r
2494 Debug info for client
\r
2497 static float counter = 0.0;
\r
2502 client.printDebugInfo(std::cout);
\r
2507 Input handler step()
\r
2509 g_input->step(dtime);
\r
2516 Player speed control
\r
2525 bool a_superspeed,
\r
2529 PlayerControl control(
\r
2530 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2531 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2532 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2533 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2534 g_input->isKeyDown(irr::KEY_SPACE),
\r
2535 g_input->isKeyDown(irr::KEY_KEY_E),
\r
2536 g_input->isKeyDown(irr::KEY_LSHIFT)
\r
2537 || g_input->isKeyDown(irr::KEY_RSHIFT),
\r
2541 client.setPlayerControl(control);
\r
2548 if(server != NULL)
\r
2550 //TimeTaker timer("server->step(dtime)");
\r
2551 server->step(dtime);
\r
2555 Process environment
\r
2559 //TimeTaker timer("client.step(dtime)");
\r
2560 client.step(dtime);
\r
2561 //client.step(dtime_avg1);
\r
2564 // Read client events
\r
2567 ClientEvent event = client.getClientEvent();
\r
2568 if(event.type == CE_NONE)
\r
2572 else if(event.type == CE_PLAYER_DAMAGE)
\r
2574 //u16 damage = event.player_damage.amount;
\r
2575 //dstream<<"Player damage: "<<damage<<std::endl;
\r
2576 damage_flash_timer = 0.05;
\r
2578 else if(event.type == CE_PLAYER_FORCE_MOVE)
\r
2580 camera_yaw = event.player_force_move.yaw;
\r
2581 camera_pitch = event.player_force_move.pitch;
\r
2585 // Get player position
\r
2586 v3f player_position = client.getPlayerPosition();
\r
2588 //TimeTaker //timer2("//timer2");
\r
2591 Mouse and camera control
\r
2594 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2597 device->getCursorControl()->setVisible(false);
\r
2599 if(first_loop_after_window_activation){
\r
2600 //std::cout<<"window active, first loop"<<std::endl;
\r
2601 first_loop_after_window_activation = false;
\r
2604 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2605 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2606 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2607 camera_yaw -= dx*0.2;
\r
2608 camera_pitch += dy*0.2;
\r
2609 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2610 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2612 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2615 device->getCursorControl()->setVisible(true);
\r
2617 //std::cout<<"window inactive"<<std::endl;
\r
2618 first_loop_after_window_activation = true;
\r
2621 camera_yaw = wrapDegrees(camera_yaw);
\r
2622 camera_pitch = wrapDegrees(camera_pitch);
\r
2624 v3f camera_direction = v3f(0,0,1);
\r
2625 camera_direction.rotateYZBy(camera_pitch);
\r
2626 camera_direction.rotateXZBy(camera_yaw);
\r
2628 // This is at the height of the eyes of the current figure
\r
2629 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2630 // This is more like in minecraft
\r
2631 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2633 camera->setPosition(camera_position);
\r
2634 // *100.0 helps in large map coordinates
\r
2635 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2637 if(FIELD_OF_VIEW_TEST){
\r
2638 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2641 //TimeTaker timer("client.updateCamera");
\r
2642 client.updateCamera(camera_position, camera_direction);
\r
2646 //TimeTaker //timer3("//timer3");
\r
2649 Calculate what block is the crosshair pointing to
\r
2652 //u32 t1 = device->getTimer()->getRealTime();
\r
2654 //f32 d = 4; // max. distance
\r
2655 f32 d = 4; // max. distance
\r
2656 core::line3d<f32> shootline(camera_position,
\r
2657 camera_position + camera_direction * BS * (d+1));
\r
2659 MapBlockObject *selected_object = client.getSelectedObject
\r
2660 (d*BS, camera_position, shootline);
\r
2662 ClientActiveObject *selected_active_object
\r
2663 = client.getSelectedActiveObject
\r
2664 (d*BS, camera_position, shootline);
\r
2666 if(selected_object != NULL)
\r
2668 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2670 core::aabbox3d<f32> box_on_map
\r
2671 = selected_object->getSelectionBoxOnMap();
\r
2673 hilightboxes.push_back(box_on_map);
\r
2675 infotext = narrow_to_wide(selected_object->infoText());
\r
2677 if(g_input->getLeftClicked())
\r
2679 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2680 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2681 selected_object->getId(), g_selected_item);
\r
2683 else if(g_input->getRightClicked())
\r
2685 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2687 Check if we want to modify the object ourselves
\r
2689 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2691 dstream<<"Sign object right-clicked"<<std::endl;
\r
2693 if(random_input == false)
\r
2695 // Get a new text for it
\r
2697 TextDest *dest = new TextDestSign(
\r
2698 selected_object->getBlock()->getPos(),
\r
2699 selected_object->getId(),
\r
2702 SignObject *sign_object = (SignObject*)selected_object;
\r
2704 std::wstring wtext =
\r
2705 narrow_to_wide(sign_object->getText());
\r
2707 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2713 Otherwise pass the event to the server as-is
\r
2717 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2718 selected_object->getId(), g_selected_item);
\r
2722 else if(selected_active_object != NULL)
\r
2724 //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
\r
2726 core::aabbox3d<f32> *selection_box
\r
2727 = selected_active_object->getSelectionBox();
\r
2728 // Box should exist because object was returned in the
\r
2730 assert(selection_box);
\r
2732 v3f pos = selected_active_object->getPosition();
\r
2734 core::aabbox3d<f32> box_on_map(
\r
2735 selection_box->MinEdge + pos,
\r
2736 selection_box->MaxEdge + pos
\r
2739 hilightboxes.push_back(box_on_map);
\r
2741 //infotext = narrow_to_wide("A ClientActiveObject");
\r
2742 infotext = narrow_to_wide(selected_active_object->infoText());
\r
2744 if(g_input->getLeftClicked())
\r
2746 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2747 client.clickActiveObject(0,
\r
2748 selected_active_object->getId(), g_selected_item);
\r
2750 else if(g_input->getRightClicked())
\r
2752 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2755 else // selected_object == NULL
\r
2759 Find out which node we are pointing at
\r
2762 bool nodefound = false;
\r
2764 v3s16 neighbourpos;
\r
2765 core::aabbox3d<f32> nodehilightbox;
\r
2767 getPointedNode(player_position,
\r
2768 camera_direction, camera_position,
\r
2769 nodefound, shootline,
\r
2770 nodepos, neighbourpos,
\r
2771 nodehilightbox, d);
\r
2773 static float nodig_delay_counter = 0.0;
\r
2777 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2779 static float dig_time = 0.0;
\r
2780 static u16 dig_index = 0;
\r
2783 Visualize selection
\r
2786 hilightboxes.push_back(nodehilightbox);
\r
2789 Check information text of node
\r
2792 NodeMetadata *meta = client.getNodeMetadata(nodepos);
\r
2795 infotext = narrow_to_wide(meta->infoText());
\r
2798 //MapNode node = client.getNode(nodepos);
\r
2804 if(g_input->getLeftReleased())
\r
2806 client.clearTempMod(nodepos);
\r
2810 if(nodig_delay_counter > 0.0)
\r
2812 nodig_delay_counter -= dtime;
\r
2816 if(nodepos != nodepos_old)
\r
2818 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2819 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2821 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2823 client.clearTempMod(nodepos_old);
\r
2828 if(g_input->getLeftClicked() ||
\r
2829 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2831 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2832 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2834 if(g_input->getLeftClicked())
\r
2836 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2838 if(g_input->getLeftState())
\r
2840 MapNode n = client.getNode(nodepos);
\r
2842 // Get tool name. Default is "" = bare hands
\r
2843 std::string toolname = "";
\r
2844 InventoryList *mlist = local_inventory.getList("main");
\r
2847 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2848 if(item && (std::string)item->getName() == "ToolItem")
\r
2850 ToolItem *titem = (ToolItem*)item;
\r
2851 toolname = titem->getToolName();
\r
2855 // Get digging properties for material and tool
\r
2856 u8 material = n.d;
\r
2857 DiggingProperties prop =
\r
2858 getDiggingProperties(material, toolname);
\r
2860 float dig_time_complete = 0.0;
\r
2862 if(prop.diggable == false)
\r
2864 /*dstream<<"Material "<<(int)material
\r
2865 <<" not diggable with \""
\r
2866 <<toolname<<"\""<<std::endl;*/
\r
2867 // I guess nobody will wait for this long
\r
2868 dig_time_complete = 10000000.0;
\r
2872 dig_time_complete = prop.time;
\r
2875 if(dig_time_complete >= 0.001)
\r
2877 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2878 * dig_time/dig_time_complete);
\r
2880 // This is for torches
\r
2883 dig_index = CRACK_ANIMATION_LENGTH;
\r
2886 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2888 //TimeTaker timer("client.setTempMod");
\r
2889 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2890 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2894 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2895 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2896 client.clearTempMod(nodepos);
\r
2897 client.removeNode(nodepos);
\r
2901 nodig_delay_counter = dig_time_complete
\r
2902 / (float)CRACK_ANIMATION_LENGTH;
\r
2904 // We don't want a corresponding delay to
\r
2905 // very time consuming nodes
\r
2906 if(nodig_delay_counter > 0.5)
\r
2908 nodig_delay_counter = 0.5;
\r
2910 // We want a slight delay to very little
\r
2911 // time consuming nodes
\r
2912 float mindelay = 0.15;
\r
2913 if(nodig_delay_counter < mindelay)
\r
2915 nodig_delay_counter = mindelay;
\r
2919 dig_time += dtime;
\r
2923 if(g_input->getRightClicked())
\r
2925 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2927 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
\r
2929 dstream<<"Sign node right-clicked"<<std::endl;
\r
2931 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
\r
2933 // Get a new text for it
\r
2935 TextDest *dest = new TextDestSignNode(nodepos, &client);
\r
2937 std::wstring wtext =
\r
2938 narrow_to_wide(signmeta->getText());
\r
2940 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2944 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
\r
2946 dstream<<"Chest node right-clicked"<<std::endl;
\r
2948 //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
\r
2950 std::string chest_inv_id;
\r
2951 chest_inv_id += "nodemeta:";
\r
2952 chest_inv_id += itos(nodepos.X);
\r
2953 chest_inv_id += ",";
\r
2954 chest_inv_id += itos(nodepos.Y);
\r
2955 chest_inv_id += ",";
\r
2956 chest_inv_id += itos(nodepos.Z);
\r
2958 GUIInventoryMenu *menu =
\r
2959 new GUIInventoryMenu(guienv, guiroot, -1,
\r
2960 &g_menumgr, v2s16(8,9),
\r
2961 g_client->getInventoryContext(),
\r
2964 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
\r
2966 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
2967 "list", chest_inv_id, "0",
\r
2968 v2s32(0, 0), v2s32(8, 4)));
\r
2969 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
\r
2970 "list", "current_player", "main",
\r
2971 v2s32(0, 5), v2s32(8, 4)));
\r
2973 menu->setDrawSpec(draw_spec);
\r
2978 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
\r
2980 dstream<<"Furnace node right-clicked"<<std::endl;
\r
2982 GUIFurnaceMenu *menu =
\r
2983 new GUIFurnaceMenu(guienv, guiroot, -1,
\r
2984 &g_menumgr, nodepos, g_client);
\r
2991 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2995 nodepos_old = nodepos;
\r
3000 } // selected_object == NULL
\r
3002 g_input->resetLeftClicked();
\r
3003 g_input->resetRightClicked();
\r
3005 if(g_input->getLeftReleased())
\r
3007 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
3009 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
3011 if(g_input->getRightReleased())
\r
3013 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
3017 g_input->resetLeftReleased();
\r
3018 g_input->resetRightReleased();
\r
3021 Calculate stuff for drawing
\r
3024 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
3026 u32 daynight_ratio = client.getDayNightRatio();
\r
3027 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
3028 video::SColor bgcolor = video::SColor(
\r
3030 skycolor.getRed() * l / 255,
\r
3031 skycolor.getGreen() * l / 255,
\r
3032 skycolor.getBlue() * l / 255);
\r
3038 if(g_settings.getBool("enable_fog") == true)
\r
3040 //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
\r
3041 f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
\r
3042 //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
\r
3043 if(draw_control.range_all)
\r
3044 range = 100000*BS;
\r
3048 video::EFT_FOG_LINEAR,
\r
3052 false, // pixel fog
\r
3053 false // range fog
\r
3060 video::EFT_FOG_LINEAR,
\r
3064 false, // pixel fog
\r
3065 false // range fog
\r
3071 Update gui stuff (0ms)
\r
3074 //TimeTaker guiupdatetimer("Gui updating");
\r
3077 static float drawtime_avg = 0;
\r
3078 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
3079 static float beginscenetime_avg = 0;
\r
3080 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
3081 static float scenetime_avg = 0;
\r
3082 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
3083 static float endscenetime_avg = 0;
\r
3084 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
3086 char temptext[300];
\r
3087 snprintf(temptext, 300, "Minetest-c55 ("
\r
3089 ", R: range_all=%i"
\r
3091 " drawtime=%.0f, beginscenetime=%.0f"
\r
3092 ", scenetime=%.0f, endscenetime=%.0f",
\r
3094 draw_control.range_all,
\r
3096 beginscenetime_avg,
\r
3101 guitext->setText(narrow_to_wide(temptext).c_str());
\r
3105 char temptext[300];
\r
3106 snprintf(temptext, 300,
\r
3107 "(% .1f, % .1f, % .1f)"
\r
3108 " (% .3f < btime_jitter < % .3f"
\r
3109 ", dtime_jitter = % .1f %%"
\r
3110 ", v_range = %.1f)",
\r
3111 player_position.X/BS,
\r
3112 player_position.Y/BS,
\r
3113 player_position.Z/BS,
\r
3114 busytime_jitter1_min_sample,
\r
3115 busytime_jitter1_max_sample,
\r
3116 dtime_jitter1_max_fraction * 100.0,
\r
3117 draw_control.wanted_range
\r
3120 guitext2->setText(narrow_to_wide(temptext).c_str());
\r
3124 guitext_info->setText(infotext.c_str());
\r
3128 Get chat messages from client
\r
3131 // Get new messages
\r
3132 std::wstring message;
\r
3133 while(client.getChatMessage(message))
\r
3135 chat_lines.push_back(ChatLine(message));
\r
3136 /*if(chat_lines.size() > 6)
\r
3138 core::list<ChatLine>::Iterator
\r
3139 i = chat_lines.begin();
\r
3140 chat_lines.erase(i);
\r
3143 // Append them to form the whole static text and throw
\r
3144 // it to the gui element
\r
3145 std::wstring whole;
\r
3146 // This will correspond to the line number counted from
\r
3147 // top to bottom, from size-1 to 0
\r
3148 s16 line_number = chat_lines.size();
\r
3149 // Count of messages to be removed from the top
\r
3150 u16 to_be_removed_count = 0;
\r
3151 for(core::list<ChatLine>::Iterator
\r
3152 i = chat_lines.begin();
\r
3153 i != chat_lines.end(); i++)
\r
3155 // After this, line number is valid for this loop
\r
3158 (*i).age += dtime;
\r
3160 This results in a maximum age of 60*6 to the
\r
3161 lowermost line and a maximum of 6 lines
\r
3163 float allowed_age = (6-line_number) * 60.0;
\r
3165 if((*i).age > allowed_age)
\r
3167 to_be_removed_count++;
\r
3170 whole += (*i).text + L'\n';
\r
3172 for(u16 i=0; i<to_be_removed_count; i++)
\r
3174 core::list<ChatLine>::Iterator
\r
3175 it = chat_lines.begin();
\r
3176 chat_lines.erase(it);
\r
3178 guitext_chat->setText(whole.c_str());
\r
3180 // Update gui element size and position
\r
3182 /*core::rect<s32> rect(
\r
3184 screensize.Y - guitext_chat_pad_bottom
\r
3185 - text_height*chat_lines.size(),
\r
3186 screensize.X - 10,
\r
3187 screensize.Y - guitext_chat_pad_bottom
\r
3189 core::rect<s32> rect(
\r
3192 screensize.X - 10,
\r
3193 50 + text_height*chat_lines.size()
\r
3196 guitext_chat->setRelativePosition(rect);
\r
3198 if(chat_lines.size() == 0)
\r
3199 guitext_chat->setVisible(false);
\r
3201 guitext_chat->setVisible(true);
\r
3208 static u16 old_selected_item = 65535;
\r
3209 if(client.getLocalInventoryUpdated()
\r
3210 || g_selected_item != old_selected_item)
\r
3212 old_selected_item = g_selected_item;
\r
3213 //std::cout<<"Updating local inventory"<<std::endl;
\r
3214 client.getLocalInventory(local_inventory);
\r
3218 Send actions returned by the inventory menu
\r
3220 while(inventory_action_queue.size() != 0)
\r
3222 InventoryAction *a = inventory_action_queue.pop_front();
\r
3224 client.sendInventoryAction(a);
\r
3233 TimeTaker drawtimer("Drawing");
\r
3237 TimeTaker timer("beginScene");
\r
3238 driver->beginScene(true, true, bgcolor);
\r
3239 //driver->beginScene(false, true, bgcolor);
\r
3240 beginscenetime = timer.stop(true);
\r
3245 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
3248 TimeTaker timer("smgr");
\r
3250 scenetime = timer.stop(true);
\r
3254 //TimeTaker timer9("auxiliary drawings");
\r
3258 //TimeTaker //timer10("//timer10");
\r
3260 video::SMaterial m;
\r
3261 //m.Thickness = 10;
\r
3263 m.Lighting = false;
\r
3264 driver->setMaterial(m);
\r
3266 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
3268 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
3269 i != hilightboxes.end(); i++)
\r
3271 /*std::cout<<"hilightbox min="
\r
3272 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
3274 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
3276 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
3282 if(g_settings.getBool("frametime_graph") == true)
\r
3285 for(core::list<float>::Iterator
\r
3286 i = frametime_log.begin();
\r
3287 i != frametime_log.end();
\r
3290 driver->draw2DLine(v2s32(x,50),
\r
3291 v2s32(x,50+(*i)*1000),
\r
3292 video::SColor(255,255,255,255));
\r
3300 if(g_show_map_plot && g_map_plot_texture)
\r
3302 core::dimension2d<u32> drawdim(640,480);
\r
3303 core::rect<s32> dest(v2s32(0,0), drawdim);
\r
3305 (screensize.X-drawdim.Width)/2,
\r
3306 (screensize.Y-drawdim.Height)/2
\r
3308 core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
\r
3309 driver->draw2DImage(g_map_plot_texture, dest, source);
\r
3315 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
3316 displaycenter + core::vector2d<s32>(10,0),
\r
3317 video::SColor(255,255,255,255));
\r
3318 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
3319 displaycenter + core::vector2d<s32>(0,10),
\r
3320 video::SColor(255,255,255,255));
\r
3325 //TimeTaker //timer11("//timer11");
\r
3331 guienv->drawAll();
\r
3337 draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
\r
3338 hotbar_imagesize, hotbar_itemcount, &local_inventory,
\r
3345 if(damage_flash_timer > 0.0)
\r
3347 damage_flash_timer -= dtime;
\r
3349 video::SColor color(128,255,0,0);
\r
3350 driver->draw2DRectangle(color,
\r
3351 core::rect<s32>(0,0,screensize.X,screensize.Y),
\r
3359 TimeTaker timer("endScene");
\r
3360 driver->endScene();
\r
3361 endscenetime = timer.stop(true);
\r
3364 drawtime = drawtimer.stop(true);
\r
3372 Refresh map plot if player has moved considerably
\r
3374 if(g_refresh_map_plot)
\r
3376 static v3f old_player_pos = v3f(1,1,1) * 10000000;
\r
3377 v3f p = client.getPlayerPosition() / BS;
\r
3378 if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
\r
3380 updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
\r
3381 old_player_pos = p;
\r
3383 g_refresh_map_plot = false;
\r
3387 static s16 lastFPS = 0;
\r
3388 //u16 fps = driver->getFPS();
\r
3389 u16 fps = (1.0/dtime_avg1);
\r
3391 if (lastFPS != fps)
\r
3393 core::stringw str = L"Minetest [";
\r
3394 str += driver->getName();
\r
3398 device->setWindowCaption(str.c_str());
\r
3403 } // client and server are deleted at this point
\r
3406 catch(con::PeerNotFoundException &e)
\r
3408 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
3409 error_message = L"Connection timed out.";
\r
3412 } // Menu-game loop
\r
3417 In the end, delete the Irrlicht device.
\r
3422 Update configuration file
\r
3424 /*if(configpath != "")
\r
3426 g_settings.updateConfigFile(configpath.c_str());
\r
3429 END_DEBUG_EXCEPTION_HANDLER
\r
3431 debugstreams_deinit();
\r