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
27 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
28 NOTE: Global locale is now set at initialization
\r
30 SUGG: Fix address to be ipv6 compatible
\r
32 FIXME: When a new sector is generated, it may change the ground level
\r
33 of it's and it's neighbors border that two blocks that are
\r
34 above and below each other and that are generated before and
\r
35 after the sector heightmap generation (order doesn't matter),
\r
36 can have a small gap between each other at the border.
\r
37 SUGGESTION: Use same technique for sector heightmaps as what we're
\r
38 using for UnlimitedHeightmap? (getting all neighbors
\r
41 SUGG: Transfer more blocks in a single packet
\r
42 SUGG: A blockdata combiner class, to which blocks are added and at
\r
43 destruction it sends all the stuff in as few packets as possible.
\r
45 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
46 SUGG: Fetch stuff mainly from the viewing direction
\r
48 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
49 - This enables saving many packets and making a faster connection
\r
50 - This also enables server to check if client has received the
\r
51 most recent block sent, for example.
\r
52 SUGG: Add a sane bandwidth throttling system to Connection
\r
54 SUGG: More fine-grained control of client's dumping of blocks from
\r
56 - ...What does this mean in the first place?
\r
58 SUGG: A map editing mode (similar to dedicated server mode)
\r
60 SUGG: Add a time value to the param of footstepped grass and check it
\r
61 against a global timer when a block is accessed, to make old
\r
64 SUGG: Make a copy of close-range environment on client for showing
\r
65 on screen, with minimal mutexes to slow down the main loop
\r
67 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
68 it by sending more stuff in a single packet.
\r
69 - Add a packet queue to RemoteClient, from which packets will be
\r
70 combined with object data packets
\r
71 - This is not exactly trivial: the object data packets are
\r
72 sometimes very big by themselves
\r
74 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
75 - This will allow saving ages of rats on disk but not sending
\r
78 SUGG: Implement lighting using VoxelManipulator
\r
79 - Would it be significantly faster?
\r
81 FIXME: Rats somehow go underground sometimes (you can see it in water)
\r
82 - Does their position get saved to a border value or something?
\r
83 - Does this happen anymore?
\r
85 SUGG: MovingObject::move and Player::move are basically the same.
\r
88 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
89 if something is already in it)
\r
90 - Use it in active block queue in water flowing
\r
92 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
93 need an additional metadata field for the texts
\r
95 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
97 SUGG: A version number to blocks, which increments when the block is
\r
98 modified (node add/remove, water update, lighting update)
\r
99 - This can then be used to make sure the most recent version of
\r
100 a block has been sent to client
\r
102 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
104 - That is >500 vertices
\r
106 TODO: Better dungeons
\r
111 TODO: Moving players more smoothly. Calculate moving animation
\r
112 in a way that doesn't make the player jump to the right place
\r
113 immediately when the server sends a new position
\r
115 TODO: There are some lighting-related todos and fixmes in
\r
116 ServerMap::emergeBlock
\r
118 TODO: Proper handling of spawning place (try to find something that
\r
119 is not in the middle of an ocean (some land to stand on at
\r
120 least) and save it in map config.
\r
122 TODO: Make the amount of blocks sending to client and the total
\r
123 amount of blocks dynamically limited. Transferring blocks is the
\r
124 main network eater of this system, so it is the one that has
\r
125 to be throttled so that RTTs stay low.
\r
127 TODO: Server to load starting inventory from disk
\r
129 TODO: Players to only be hidden when the client quits.
\r
130 TODO: - Players to be saved on disk, with inventory
\r
131 TODO: Players to be saved as text in map/players/<name>
\r
133 TODO: Make fetching sector's blocks more efficient when rendering
\r
134 sectors that have very large amounts of blocks (on client)
\r
136 TODO: Make the video backend selectable
\r
138 Block object server side:
\r
139 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
140 - For all blocks in the buffer, objects are stepped(). This
\r
141 means they are active.
\r
142 - TODO: A global active buffer is needed for the server
\r
143 - TODO: A timestamp to blocks
\r
144 - TODO: All blocks going in and out of the buffer are recorded.
\r
145 - TODO: For outgoing blocks, timestamp is written.
\r
146 - TODO: For incoming blocks, time difference is calculated and
\r
147 objects are stepped according to it.
\r
149 TODO: Copy the text of the last picked sign to inventory in creative
\r
152 TODO: Untie client network operations from framerate
\r
153 - Needs some input queues or something
\r
155 TODO: Get rid of GotSplitPacketException
\r
157 TODO: Check what goes wrong with caching map to disk (Kray)
\r
159 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
161 TODO: TOSERVER_LEAVE
\r
163 TODO: Better handling of objects and mobs
\r
165 - There has to be some way to do it with less spaghetti code
\r
166 - Make separate classes for client and server
\r
167 - Client should not discriminate between blocks, server should
\r
168 - Make other players utilize the same framework
\r
170 SUGG: Split Inventory into ClientInventory and ServerInventory
\r
173 ======================================================================
\r
175 TODO: Convert the text input system to use a modal menu
\r
177 ======================================================================
\r
182 Setting this to 1 enables a special camera mode that forces
\r
183 the renderers to think that the camera statically points from
\r
184 the starting place to a static direction.
\r
186 This allows one to move around with the player and see what
\r
187 is actually drawn behind solid things and behind the player.
\r
189 #define FIELD_OF_VIEW_TEST 0
\r
191 #ifdef UNITTEST_DISABLE
\r
193 #pragma message ("Disabling unit tests")
\r
195 #warning "Disabling unit tests"
\r
197 // Disable unit tests
\r
198 #define ENABLE_TESTS 0
\r
200 // Enable unit tests
\r
201 #define ENABLE_TESTS 1
\r
205 #pragma comment(lib, "Irrlicht.lib")
\r
206 #pragma comment(lib, "jthread.lib")
\r
207 #pragma comment(lib, "zlibwapi.lib")
\r
208 // This would get rid of the console window
\r
209 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
212 #include <iostream>
\r
214 #include <jmutexautolock.h>
\r
215 #include <locale.h>
\r
216 #include "common_irrlicht.h"
\r
219 #include "player.h"
\r
222 #include "environment.h"
\r
223 #include "server.h"
\r
224 #include "client.h"
\r
225 #include "serialization.h"
\r
226 #include "constants.h"
\r
227 #include "strfnd.h"
\r
228 #include "porting.h"
\r
229 #include "guiPauseMenu.h"
\r
230 #include "irrlichtwrapper.h"
\r
231 #include "gettime.h"
\r
232 #include "porting.h"
\r
233 #include "guiInventoryMenu.h"
\r
235 IrrlichtWrapper *g_irrlicht;
\r
237 // All range-related stuff below is locked behind this
\r
238 JMutex g_range_mutex;
\r
240 // Blocks are viewed in this range from the player
\r
241 s16 g_viewing_range_nodes = 60;
\r
242 //s16 g_viewing_range_nodes = 0;
\r
244 // This is updated by the client's fetchBlocks routine
\r
245 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
247 // If true, the preceding value has no meaning and all blocks
\r
248 // already existing in memory are drawn
\r
249 bool g_viewing_range_all = false;
\r
251 // This is the freetime ratio imposed by the dynamic viewing
\r
252 // range changing code.
\r
253 // It is controlled by the main loop to the smallest value that
\r
254 // inhibits glitches (dtime jitter) in the main loop.
\r
255 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
259 These are loaded from the config file.
\r
262 Settings g_settings;
\r
264 extern void set_default_settings();
\r
270 //u16 g_selected_material = 0;
\r
271 u16 g_selected_item = 0;
\r
276 gui::IGUIEnvironment* guienv = NULL;
\r
277 GUIPauseMenu *pauseMenu = NULL;
\r
278 GUIInventoryMenu *inventoryMenu = NULL;
\r
280 bool noMenuActive()
\r
282 if(pauseMenu && pauseMenu->isVisible())
\r
284 if(inventoryMenu && inventoryMenu->isVisible())
\r
289 std::wstring g_text_buffer;
\r
290 bool g_text_buffer_accepted = false;
\r
292 // When true, the mouse and keyboard are grabbed
\r
293 bool g_game_focused = true;
\r
300 std::ostream *dout_con_ptr = &dummyout;
\r
301 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
302 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
303 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
304 //std::ostream *dout_con_ptr = &dstream;
\r
305 //std::ostream *derr_con_ptr = &dstream;
\r
308 std::ostream *dout_server_ptr = &dstream;
\r
309 std::ostream *derr_server_ptr = &dstream;
\r
312 std::ostream *dout_client_ptr = &dstream;
\r
313 std::ostream *derr_client_ptr = &dstream;
\r
316 gettime.h implementation
\r
322 Use irrlicht because it is more precise than porting.h's
\r
325 if(g_irrlicht == NULL)
\r
327 return g_irrlicht->getTime();
\r
330 class MyEventReceiver : public IEventReceiver
\r
333 // This is the one method that we have to implement
\r
334 virtual bool OnEvent(const SEvent& event)
\r
336 // Remember whether each key is down or up
\r
337 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
339 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
341 if(event.KeyInput.PressedDown)
\r
343 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
344 if(g_game_focused == false)
\r
346 s16 key = event.KeyInput.Key;
\r
347 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
349 g_text_buffer_accepted = true;
\r
351 else if(key == irr::KEY_BACK)
\r
353 if(g_text_buffer.size() > 0)
\r
354 g_text_buffer = g_text_buffer.substr
\r
355 (0, g_text_buffer.size()-1);
\r
359 wchar_t wc = event.KeyInput.Char;
\r
361 g_text_buffer += wc;
\r
365 if(pauseMenu != NULL)
\r
367 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
369 if(g_game_focused == true && noMenuActive())
\r
371 dstream<<DTIME<<"MyEventReceiver: "
\r
372 <<"Launching pause menu"<<std::endl;
\r
373 pauseMenu->launch();
\r
379 if(inventoryMenu != NULL)
\r
381 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
383 if(g_game_focused == true && noMenuActive())
\r
385 dstream<<DTIME<<"MyEventReceiver: "
\r
386 <<"Launching inventory"<<std::endl;
\r
387 inventoryMenu->launch();
\r
393 // Material selection
\r
394 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
396 if(g_game_focused == true)
\r
398 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
401 g_selected_item = 0;
\r
402 dstream<<DTIME<<"Selected item: "
\r
403 <<g_selected_item<<std::endl;
\r
407 // Viewing range selection
\r
408 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
411 JMutexAutoLock lock(g_range_mutex);
\r
412 if(g_viewing_range_all)
\r
414 g_viewing_range_all = false;
\r
415 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
419 g_viewing_range_all = true;
\r
420 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
424 // Print debug stacks
\r
425 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
428 dstream<<"-----------------------------------------"
\r
430 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
431 dstream<<"-----------------------------------------"
\r
433 debug_stacks_print();
\r
438 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
440 if(noMenuActive() == false)
\r
442 left_active = false;
\r
443 middle_active = false;
\r
444 right_active = false;
\r
448 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
449 left_active = event.MouseInput.isLeftPressed();
\r
450 middle_active = event.MouseInput.isMiddlePressed();
\r
451 right_active = event.MouseInput.isRightPressed();
\r
453 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
455 leftclicked = true;
\r
457 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
459 rightclicked = true;
\r
461 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
463 leftreleased = true;
\r
465 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
467 rightreleased = true;
\r
469 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
471 /*dstream<<"event.MouseInput.Wheel="
\r
472 <<event.MouseInput.Wheel<<std::endl;*/
\r
473 if(event.MouseInput.Wheel < 0)
\r
475 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
478 g_selected_item = 0;
\r
480 else if(event.MouseInput.Wheel > 0)
\r
482 if(g_selected_item > 0)
\r
485 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
494 // This is used to check whether a key is being held down
\r
495 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
497 return keyIsDown[keyCode];
\r
502 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
503 keyIsDown[i] = false;
\r
504 leftclicked = false;
\r
505 rightclicked = false;
\r
506 leftreleased = false;
\r
507 rightreleased = false;
\r
509 left_active = false;
\r
510 middle_active = false;
\r
511 right_active = false;
\r
517 bool rightreleased;
\r
520 bool middle_active;
\r
524 // We use this array to store the current state of each key
\r
525 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
536 virtual ~InputHandler()
\r
540 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
542 virtual v2s32 getMousePos() = 0;
\r
543 virtual void setMousePos(s32 x, s32 y) = 0;
\r
545 virtual bool getLeftState() = 0;
\r
546 virtual bool getRightState() = 0;
\r
548 virtual bool getLeftClicked() = 0;
\r
549 virtual bool getRightClicked() = 0;
\r
550 virtual void resetLeftClicked() = 0;
\r
551 virtual void resetRightClicked() = 0;
\r
553 virtual bool getLeftReleased() = 0;
\r
554 virtual bool getRightReleased() = 0;
\r
555 virtual void resetLeftReleased() = 0;
\r
556 virtual void resetRightReleased() = 0;
\r
558 virtual void step(float dtime) {};
\r
560 virtual void clear() {};
\r
563 InputHandler *g_input = NULL;
\r
568 g_game_focused = true;
\r
573 g_game_focused = false;
\r
576 class RealInputHandler : public InputHandler
\r
579 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
581 m_receiver(receiver)
\r
584 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
586 return m_receiver->IsKeyDown(keyCode);
\r
588 virtual v2s32 getMousePos()
\r
590 return m_device->getCursorControl()->getPosition();
\r
592 virtual void setMousePos(s32 x, s32 y)
\r
594 m_device->getCursorControl()->setPosition(x, y);
\r
597 virtual bool getLeftState()
\r
599 return m_receiver->left_active;
\r
601 virtual bool getRightState()
\r
603 return m_receiver->right_active;
\r
606 virtual bool getLeftClicked()
\r
608 if(g_game_focused == false)
\r
610 return m_receiver->leftclicked;
\r
612 virtual bool getRightClicked()
\r
614 if(g_game_focused == false)
\r
616 return m_receiver->rightclicked;
\r
618 virtual void resetLeftClicked()
\r
620 m_receiver->leftclicked = false;
\r
622 virtual void resetRightClicked()
\r
624 m_receiver->rightclicked = false;
\r
627 virtual bool getLeftReleased()
\r
629 if(g_game_focused == false)
\r
631 return m_receiver->leftreleased;
\r
633 virtual bool getRightReleased()
\r
635 if(g_game_focused == false)
\r
637 return m_receiver->rightreleased;
\r
639 virtual void resetLeftReleased()
\r
641 m_receiver->leftreleased = false;
\r
643 virtual void resetRightReleased()
\r
645 m_receiver->rightreleased = false;
\r
650 resetRightClicked();
\r
651 resetLeftClicked();
\r
654 IrrlichtDevice *m_device;
\r
655 MyEventReceiver *m_receiver;
\r
658 class RandomInputHandler : public InputHandler
\r
661 RandomInputHandler()
\r
663 leftclicked = false;
\r
664 rightclicked = false;
\r
665 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
666 keydown[i] = false;
\r
668 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
670 return keydown[keyCode];
\r
672 virtual v2s32 getMousePos()
\r
676 virtual void setMousePos(s32 x, s32 y)
\r
678 mousepos = v2s32(x,y);
\r
681 virtual bool getLeftState()
\r
685 virtual bool getRightState()
\r
690 virtual bool getLeftClicked()
\r
692 return leftclicked;
\r
694 virtual bool getRightClicked()
\r
696 return rightclicked;
\r
698 virtual void resetLeftClicked()
\r
700 leftclicked = false;
\r
702 virtual void resetRightClicked()
\r
704 rightclicked = false;
\r
707 virtual bool getLeftReleased()
\r
711 virtual bool getRightReleased()
\r
715 virtual void resetLeftReleased()
\r
718 virtual void resetRightReleased()
\r
722 virtual void step(float dtime)
\r
725 static float counter1 = 0;
\r
729 counter1 = 0.1*Rand(1,10);
\r
730 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
731 g_selected_material++;
\r
733 g_selected_material = 0;*/
\r
734 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
737 g_selected_item = 0;
\r
741 static float counter1 = 0;
\r
745 counter1 = 0.1*Rand(1, 40);
\r
746 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
750 static float counter1 = 0;
\r
754 counter1 = 0.1*Rand(1, 40);
\r
755 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
759 static float counter1 = 0;
\r
763 counter1 = 0.1*Rand(1, 40);
\r
764 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
768 static float counter1 = 0;
\r
772 counter1 = 0.1*Rand(1, 40);
\r
773 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
777 static float counter1 = 0;
\r
781 counter1 = 0.1*Rand(1, 20);
\r
782 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
786 static float counter1 = 0;
\r
790 counter1 = 0.1*Rand(1, 30);
\r
791 leftclicked = true;
\r
795 static float counter1 = 0;
\r
799 counter1 = 0.1*Rand(1, 20);
\r
800 rightclicked = true;
\r
803 mousepos += mousespeed;
\r
806 s32 Rand(s32 min, s32 max)
\r
808 return (rand()%(max-min+1))+min;
\r
811 bool keydown[KEY_KEY_CODES_COUNT];
\r
818 void updateViewingRange(f32 frametime, Client *client)
\r
820 // Range_all messes up frametime_avg
\r
821 if(g_viewing_range_all == true)
\r
824 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
826 // Initialize to the target value
\r
827 static float frametime_avg = 1.0/wanted_fps;
\r
828 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
829 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
831 static f32 counter = 0;
\r
833 counter -= frametime;
\r
836 //counter = 1.0; //seconds
\r
837 counter = 0.5; //seconds
\r
839 //float freetime_ratio = 0.2;
\r
840 //float freetime_ratio = 0.4;
\r
841 float freetime_ratio = FREETIME_RATIO;
\r
843 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
845 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
847 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
848 + frametime_avg / frametime_wanted / 2.0;*/
\r
850 //float fraction = frametime_avg / frametime_wanted;
\r
852 static bool fraction_is_good = false;
\r
854 //float fraction_good_threshold = 0.1;
\r
855 //float fraction_bad_threshold = 0.25;
\r
856 float fraction_good_threshold = 0.075;
\r
857 float fraction_bad_threshold = 0.125;
\r
858 float fraction_limit;
\r
859 // Use high limit if fraction is good AND the fraction would
\r
860 // lower the range. We want to keep the range fairly high.
\r
861 if(fraction_is_good && fraction > 1.0)
\r
862 fraction_limit = fraction_bad_threshold;
\r
864 fraction_limit = fraction_good_threshold;
\r
866 if(fabs(fraction - 1.0) < fraction_limit)
\r
868 fraction_is_good = true;
\r
873 fraction_is_good = false;
\r
876 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
877 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
878 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
879 <<" faction = "<<fraction<<std::endl;*/
\r
881 JMutexAutoLock lock(g_range_mutex);
\r
883 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
884 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
886 s16 n = (float)g_viewing_range_nodes / fraction;
\r
887 if(n < viewing_range_nodes_min)
\r
888 n = viewing_range_nodes_min;
\r
889 if(n > viewing_range_nodes_max)
\r
890 n = viewing_range_nodes_max;
\r
892 bool can_change = true;
\r
894 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
895 can_change = false;
\r
898 g_viewing_range_nodes = n;
\r
900 /*dstream<<"g_viewing_range_nodes = "
\r
901 <<g_viewing_range_nodes<<std::endl;*/
\r
904 class GUIQuickInventory : public IEventReceiver
\r
908 gui::IGUIEnvironment* env,
\r
909 gui::IGUIElement* parent,
\r
912 Inventory *inventory):
\r
913 m_itemcount(itemcount),
\r
914 m_inventory(inventory)
\r
916 core::rect<s32> imgsize(0,0,48,48);
\r
917 core::rect<s32> textsize(0,0,48,16);
\r
918 v2s32 spacing(0, 64);
\r
919 for(s32 i=0; i<m_itemcount; i++)
\r
921 m_images.push_back(env->addImage(
\r
922 imgsize + pos + spacing*i
\r
924 m_images[i]->setScaleImage(true);
\r
925 m_texts.push_back(env->addStaticText(
\r
927 textsize + pos + spacing*i,
\r
930 m_texts[i]->setBackgroundColor(
\r
931 video::SColor(128,0,0,0));
\r
932 m_texts[i]->setTextAlignment(
\r
934 gui::EGUIA_UPPERLEFT);
\r
938 virtual bool OnEvent(const SEvent& event)
\r
943 void setSelection(s32 i)
\r
952 start = m_selection - m_itemcount / 2;
\r
954 InventoryList *mainlist = m_inventory->getList("main");
\r
956 for(s32 i=0; i<m_itemcount; i++)
\r
960 if(j > (s32)mainlist->getSize() - 1)
\r
961 j -= mainlist->getSize();
\r
963 j += mainlist->getSize();
\r
965 InventoryItem *item = mainlist->getItem(j);
\r
969 m_images[i]->setImage(NULL);
\r
972 if(m_selection == j)
\r
973 swprintf(t, 10, L"<-");
\r
975 swprintf(t, 10, L"");
\r
976 m_texts[i]->setText(t);
\r
978 // The next ifs will segfault with a NULL pointer
\r
983 m_images[i]->setImage(item->getImage());
\r
986 if(m_selection == j)
\r
987 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
989 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
990 m_texts[i]->setText(t);
\r
996 core::array<gui::IGUIStaticText*> m_texts;
\r
997 core::array<gui::IGUIImage*> m_images;
\r
998 Inventory *m_inventory;
\r
1002 int main(int argc, char *argv[])
\r
1005 Low-level initialization
\r
1008 bool disable_stderr = false;
\r
1010 disable_stderr = true;
\r
1013 // Initialize debug streams
\r
1014 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1015 // Initialize debug stacks
\r
1016 debug_stacks_init();
\r
1018 DSTACK(__FUNCTION_NAME);
\r
1024 Parse command line
\r
1027 // List all allowed options
\r
1028 core::map<std::string, ValueSpec> allowed_options;
\r
1029 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1030 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1031 "Run server directly"));
\r
1032 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1033 "Load configuration from specified file"));
\r
1034 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1035 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1036 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1037 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1038 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1040 Settings cmd_args;
\r
1042 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1044 if(ret == false || cmd_args.getFlag("help"))
\r
1046 dstream<<"Allowed options:"<<std::endl;
\r
1047 for(core::map<std::string, ValueSpec>::Iterator
\r
1048 i = allowed_options.getIterator();
\r
1049 i.atEnd() == false; i++)
\r
1051 dstream<<" --"<<i.getNode()->getKey();
\r
1052 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1057 dstream<<" <value>";
\r
1059 dstream<<std::endl;
\r
1061 if(i.getNode()->getValue().help != NULL)
\r
1063 dstream<<" "<<i.getNode()->getValue().help
\r
1068 return cmd_args.getFlag("help") ? 0 : 1;
\r
1073 Basic initialization
\r
1076 // Initialize default settings
\r
1077 set_default_settings();
\r
1079 // Print startup message
\r
1080 dstream<<DTIME<<"minetest-c55"
\r
1081 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1082 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1085 // Set locale. This is for forcing '.' as the decimal point.
\r
1086 std::locale::global(std::locale("C"));
\r
1087 // This enables printing all characters in bitmap font
\r
1088 setlocale(LC_CTYPE, "en_US");
\r
1090 // Initialize sockets
\r
1092 atexit(sockets_cleanup);
\r
1102 // Path of configuration file in use
\r
1103 std::string configpath = "";
\r
1105 if(cmd_args.exists("config"))
\r
1107 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1110 dstream<<"Could not read configuration from \""
\r
1111 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1114 configpath = cmd_args.get("config");
\r
1118 const char *filenames[2] =
\r
1120 "../minetest.conf",
\r
1121 "../../minetest.conf"
\r
1124 for(u32 i=0; i<2; i++)
\r
1126 bool r = g_settings.readConfigFile(filenames[i]);
\r
1129 configpath = filenames[i];
\r
1135 // Initialize random seed
\r
1141 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1142 || cmd_args.getFlag("enable-unittests") == true)
\r
1148 Global range mutex
\r
1150 g_range_mutex.Init();
\r
1151 assert(g_range_mutex.IsInitialized());
\r
1153 // Read map parameters from settings
\r
1155 HMParams hm_params;
\r
1156 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1157 hm_params.randmax = g_settings.get("height_randmax");
\r
1158 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1159 hm_params.base = g_settings.get("height_base");
\r
1161 MapParams map_params;
\r
1162 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1163 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1169 std::cout<<std::endl<<std::endl;
\r
1172 <<" .__ __ __ "<<std::endl
\r
1173 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1174 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1175 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1176 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1177 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1180 std::cout<<std::endl;
\r
1181 //char templine[100];
\r
1185 if(cmd_args.exists("port"))
\r
1187 port = cmd_args.getU16("port");
\r
1191 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1192 std::cout<<"-> "<<port<<std::endl;
\r
1195 if(cmd_args.getFlag("server"))
\r
1197 DSTACK("Dedicated server branch");
\r
1199 std::cout<<std::endl;
\r
1200 std::cout<<"========================"<<std::endl;
\r
1201 std::cout<<"Running dedicated server"<<std::endl;
\r
1202 std::cout<<"========================"<<std::endl;
\r
1203 std::cout<<std::endl;
\r
1205 Server server("../map", hm_params, map_params);
\r
1206 server.start(port);
\r
1210 // This is kind of a hack but can be done like this
\r
1211 // because server.step() is very light
\r
1213 server.step(0.030);
\r
1215 static int counter = 0;
\r
1221 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1222 core::list<PlayerInfo>::Iterator i;
\r
1223 static u32 sum_old = 0;
\r
1224 u32 sum = PIChecksum(list);
\r
1225 if(sum != sum_old)
\r
1227 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1228 for(i=list.begin(); i!=list.end(); i++)
\r
1230 i->PrintLine(&std::cout);
\r
1240 bool hosting = false;
\r
1241 char connect_name[100] = "";
\r
1243 if(cmd_args.exists("address"))
\r
1245 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1247 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1249 std::cout<<g_settings.get("address")<<std::endl;
\r
1250 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1254 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1255 std::cin.getline(connect_name, 100);
\r
1258 if(connect_name[0] == 0){
\r
1259 snprintf(connect_name, 100, "127.0.0.1");
\r
1264 std::cout<<"> Hosting game"<<std::endl;
\r
1266 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1268 char playername[PLAYERNAME_SIZE] = "";
\r
1269 if(g_settings.get("name") != "")
\r
1271 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1275 std::cout<<"Name of player: ";
\r
1276 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1278 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1281 Resolution selection
\r
1284 bool fullscreen = false;
\r
1285 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1286 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1290 MyEventReceiver receiver;
\r
1292 video::E_DRIVER_TYPE driverType;
\r
1295 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1296 driverType = video::EDT_OPENGL;
\r
1298 driverType = video::EDT_OPENGL;
\r
1301 // create device and exit if creation failed
\r
1303 IrrlichtDevice *device;
\r
1304 device = createDevice(driverType,
\r
1305 core::dimension2d<u32>(screenW, screenH),
\r
1306 16, fullscreen, false, false, &receiver);
\r
1309 return 1; // could not create selected driver.
\r
1311 g_irrlicht = new IrrlichtWrapper(device);
\r
1313 //g_device = device;
\r
1315 device->setResizable(true);
\r
1317 bool random_input = g_settings.getBool("random_input")
\r
1318 || cmd_args.getFlag("random-input");
\r
1320 g_input = new RandomInputHandler();
\r
1322 g_input = new RealInputHandler(device, &receiver);
\r
1325 Continue initialization
\r
1328 video::IVideoDriver* driver = device->getVideoDriver();
\r
1331 This changes the minimum allowed number of vertices in a VBO
\r
1333 //driver->setMinHardwareBufferVertexCount(1);
\r
1335 scene::ISceneManager* smgr = device->getSceneManager();
\r
1337 guienv = device->getGUIEnvironment();
\r
1338 gui::IGUISkin* skin = guienv->getSkin();
\r
1339 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1341 skin->setFont(font);
\r
1342 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1343 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1344 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1345 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1346 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1347 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1349 const wchar_t *text = L"Loading and connecting...";
\r
1350 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1351 core::dimension2d<u32> textd = font->getDimension(text);
\r
1352 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1353 // Have to add a bit to disable the text from word wrapping
\r
1354 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1355 core::vector2d<s32> textsize(300, textd.Height);
\r
1356 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1358 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1359 text, textrect, false, false);
\r
1360 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1362 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1363 guienv->drawAll();
\r
1364 driver->endScene();
\r
1367 Preload some random textures that are used in threads
\r
1370 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1371 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1372 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1373 g_texturecache.set("crack", driver->getTexture("../data/crack.png"));
\r
1376 Load tile textures
\r
1378 for(s32 i=0; i<TILES_COUNT; i++)
\r
1380 if(g_tile_texture_names[i] == NULL)
\r
1382 std::string name = g_tile_texture_names[i];
\r
1383 std::string filename;
\r
1384 filename += "../data/";
\r
1386 filename += ".png";
\r
1387 g_texturecache.set(name, driver->getTexture(filename.c_str()));
\r
1392 //tile_materials_preload(g_texturecache);
\r
1393 tile_materials_preload(g_irrlicht);
\r
1394 //tile_materials_init();
\r
1397 Make a scope here for the client so that it gets removed
\r
1398 before the irrlicht device
\r
1402 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1407 SharedPtr<Server> server;
\r
1409 server = new Server("../map", hm_params, map_params);
\r
1410 server->start(port);
\r
1417 Client client(device, playername,
\r
1419 g_viewing_range_nodes,
\r
1420 g_viewing_range_all);
\r
1422 Address connect_address(0,0,0,0, port);
\r
1424 connect_address.Resolve(connect_name);
\r
1426 catch(ResolveError &e)
\r
1428 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1432 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1433 client.connect(connect_address);
\r
1436 while(client.connectedAndInitialized() == false)
\r
1439 if(server != NULL){
\r
1440 server->step(0.1);
\r
1445 catch(con::PeerNotFoundException &e)
\r
1447 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1454 /*scene::ISceneNode* skybox;
\r
1455 skybox = smgr->addSkyBoxSceneNode(
\r
1456 driver->getTexture("../data/skybox2.png"),
\r
1457 driver->getTexture("../data/skybox3.png"),
\r
1458 driver->getTexture("../data/skybox1.png"),
\r
1459 driver->getTexture("../data/skybox1.png"),
\r
1460 driver->getTexture("../data/skybox1.png"),
\r
1461 driver->getTexture("../data/skybox1.png"));*/
\r
1464 Create the camera node
\r
1467 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1468 0, // Camera parent
\r
1469 v3f(BS*100, BS*2, BS*100), // Look from
\r
1470 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1474 if(camera == NULL)
\r
1477 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1479 camera->setFOV(FOV_ANGLE);
\r
1481 // Just so big a value that everything rendered is visible
\r
1482 camera->setFarValue(100000*BS);
\r
1484 f32 camera_yaw = 0; // "right/left"
\r
1485 f32 camera_pitch = 0; // "up/down"
\r
1491 gui_loadingtext->remove();
\r
1494 Add some gui stuff
\r
1497 // This is a copy of the inventory that the client's environment has
\r
1498 Inventory local_inventory;
\r
1500 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1501 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1504 We need some kind of a root node to be able to add
\r
1505 custom elements directly on the screen.
\r
1506 Otherwise they won't be automatically drawn.
\r
1508 gui::IGUIStaticText *root = guienv->addStaticText(L"",
\r
1509 core::rect<s32>(0, 0, 10000, 10000));
\r
1512 pauseMenu = new GUIPauseMenu(guienv, root, -1, device);
\r
1515 inventoryMenu = new GUIInventoryMenu(guienv, root, -1, &local_inventory);
\r
1517 pauseMenu->launch();
\r
1518 //inventoryMenu->launch();
\r
1520 // First line of debug text
\r
1521 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1523 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1525 // Second line of debug text
\r
1526 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1528 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1531 // At the middle of the screen
\r
1532 // Object infos are shown in this
\r
1533 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1535 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1539 Some statistics are collected in these
\r
1542 u32 beginscenetime = 0;
\r
1543 u32 scenetime = 0;
\r
1544 u32 endscenetime = 0;
\r
1552 virtual void sendText(std::string text) = 0;
\r
1555 struct TextDestSign : public TextDest
\r
1557 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1559 m_blockpos = blockpos;
\r
1561 m_client = client;
\r
1563 void sendText(std::string text)
\r
1565 dstream<<"Changing text of a sign object: "
\r
1566 <<text<<std::endl;
\r
1567 m_client->sendSignText(m_blockpos, m_id, text);
\r
1575 TextDest *textbuf_dest = NULL;
\r
1577 //gui::IGUIWindow* input_window = NULL;
\r
1578 gui::IGUIStaticText* input_guitext = NULL;
\r
1584 bool first_loop_after_window_activation = true;
\r
1586 // Time is in milliseconds
\r
1587 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1588 // NOTE: Have to call run() between calls of this to update the timer
\r
1589 u32 lasttime = device->getTimer()->getTime();
\r
1591 while(device->run())
\r
1594 Run global IrrlichtWrapper's main thread processing stuff
\r
1596 g_irrlicht->Run();
\r
1599 Random calculations
\r
1601 v2u32 screensize = driver->getScreenSize();
\r
1602 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1604 pauseMenu->resizeGui();
\r
1605 inventoryMenu->resizeGui();
\r
1607 // Hilight boxes collected during the loop and displayed
\r
1608 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1611 std::wstring infotext;
\r
1613 //TimeTaker //timer1("//timer1");
\r
1615 // Time of frame without fps limit
\r
1619 // not using getRealTime is necessary for wine
\r
1620 u32 time = device->getTimer()->getTime();
\r
1621 if(time > lasttime)
\r
1622 busytime_u32 = time - lasttime;
\r
1625 busytime = busytime_u32 / 1000.0;
\r
1628 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1630 // Absolutelu necessary for wine!
\r
1637 //updateViewingRange(dtime, &client);
\r
1638 updateViewingRange(busytime, &client);
\r
1645 float fps_max = g_settings.getFloat("fps_max");
\r
1646 u32 frametime_min = 1000./fps_max;
\r
1648 if(busytime_u32 < frametime_min)
\r
1650 u32 sleeptime = frametime_min - busytime_u32;
\r
1651 device->sleep(sleeptime);
\r
1655 // Absolutelu necessary for wine!
\r
1659 Time difference calculation
\r
1661 f32 dtime; // in seconds
\r
1663 u32 time = device->getTimer()->getTime();
\r
1664 if(time > lasttime)
\r
1665 dtime = (time - lasttime) / 1000.0;
\r
1671 Time average and jitter calculation
\r
1674 static f32 dtime_avg1 = 0.0;
\r
1675 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1676 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1678 static f32 dtime_jitter1_max_sample = 0.0;
\r
1679 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1681 static f32 jitter1_max = 0.0;
\r
1682 static f32 counter = 0.0;
\r
1683 if(dtime_jitter1 > jitter1_max)
\r
1684 jitter1_max = dtime_jitter1;
\r
1689 dtime_jitter1_max_sample = jitter1_max;
\r
1690 dtime_jitter1_max_fraction
\r
1691 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1692 jitter1_max = 0.0;
\r
1695 Control freetime ratio
\r
1697 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1699 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1700 g_freetime_ratio += 0.01;
\r
1704 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1705 g_freetime_ratio -= 0.01;
\r
1711 Busytime average and jitter calculation
\r
1714 static f32 busytime_avg1 = 0.0;
\r
1715 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1716 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1718 static f32 busytime_jitter1_max_sample = 0.0;
\r
1719 static f32 busytime_jitter1_min_sample = 0.0;
\r
1721 static f32 jitter1_max = 0.0;
\r
1722 static f32 jitter1_min = 0.0;
\r
1723 static f32 counter = 0.0;
\r
1724 if(busytime_jitter1 > jitter1_max)
\r
1725 jitter1_max = busytime_jitter1;
\r
1726 if(busytime_jitter1 < jitter1_min)
\r
1727 jitter1_min = busytime_jitter1;
\r
1729 if(counter > 0.0){
\r
1731 busytime_jitter1_max_sample = jitter1_max;
\r
1732 busytime_jitter1_min_sample = jitter1_min;
\r
1733 jitter1_max = 0.0;
\r
1734 jitter1_min = 0.0;
\r
1739 Debug info for client
\r
1742 static float counter = 0.0;
\r
1747 client.printDebugInfo(std::cout);
\r
1752 Input handler step()
\r
1754 g_input->step(dtime);
\r
1759 /*if(g_esc_pressed)
\r
1765 inventoryMenu->setVisible(true);
\r
1766 g_i_pressed = false;
\r
1770 Player speed control
\r
1773 if(g_game_focused)
\r
1780 bool a_superspeed,
\r
1783 PlayerControl control(
\r
1784 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1785 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1786 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1787 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1788 g_input->isKeyDown(irr::KEY_SPACE),
\r
1789 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1793 client.setPlayerControl(control);
\r
1797 // Set every key to inactive
\r
1798 PlayerControl control;
\r
1799 client.setPlayerControl(control);
\r
1804 Process environment
\r
1808 //TimeTaker timer("client.step(dtime)");
\r
1809 client.step(dtime);
\r
1810 //client.step(dtime_avg1);
\r
1813 if(server != NULL)
\r
1815 //TimeTaker timer("server->step(dtime)");
\r
1816 server->step(dtime);
\r
1819 v3f player_position = client.getPlayerPosition();
\r
1821 //TimeTaker //timer2("//timer2");
\r
1824 Mouse and camera control
\r
1827 if((device->isWindowActive()
\r
1834 device->getCursorControl()->setVisible(false);
\r
1836 if(first_loop_after_window_activation){
\r
1837 //std::cout<<"window active, first loop"<<std::endl;
\r
1838 first_loop_after_window_activation = false;
\r
1841 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1842 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1843 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1844 camera_yaw -= dx*0.2;
\r
1845 camera_pitch += dy*0.2;
\r
1846 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1847 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1849 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1852 device->getCursorControl()->setVisible(true);
\r
1854 //std::cout<<"window inactive"<<std::endl;
\r
1855 first_loop_after_window_activation = true;
\r
1858 camera_yaw = wrapDegrees(camera_yaw);
\r
1859 camera_pitch = wrapDegrees(camera_pitch);
\r
1861 v3f camera_direction = v3f(0,0,1);
\r
1862 camera_direction.rotateYZBy(camera_pitch);
\r
1863 camera_direction.rotateXZBy(camera_yaw);
\r
1865 v3f camera_position =
\r
1866 player_position + v3f(0, BS+BS/2, 0);
\r
1868 camera->setPosition(camera_position);
\r
1869 // *100.0 helps in large map coordinates
\r
1870 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1872 if(FIELD_OF_VIEW_TEST){
\r
1873 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1874 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1877 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1878 //TimeTaker timer("client.updateCamera");
\r
1879 client.updateCamera(camera_position, camera_direction);
\r
1883 //TimeTaker //timer3("//timer3");
\r
1886 Calculate what block is the crosshair pointing to
\r
1889 //u32 t1 = device->getTimer()->getRealTime();
\r
1891 //f32 d = 4; // max. distance
\r
1892 f32 d = 4; // max. distance
\r
1893 core::line3d<f32> shootline(camera_position,
\r
1894 camera_position + camera_direction * BS * (d+1));
\r
1896 MapBlockObject *selected_object = client.getSelectedObject
\r
1897 (d*BS, camera_position, shootline);
\r
1899 if(selected_object != NULL)
\r
1901 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1903 core::aabbox3d<f32> box_on_map
\r
1904 = selected_object->getSelectionBoxOnMap();
\r
1906 hilightboxes.push_back(box_on_map);
\r
1908 infotext = narrow_to_wide(selected_object->infoText());
\r
1910 if(g_input->getLeftClicked())
\r
1912 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1913 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1914 selected_object->getId(), g_selected_item);
\r
1916 else if(g_input->getRightClicked())
\r
1918 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1920 Check if we want to modify the object ourselves
\r
1922 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1924 dstream<<"Sign object right-clicked"<<std::endl;
\r
1928 input_guitext = guienv->addStaticText(L"",
\r
1929 core::rect<s32>(150,100,350,120),
\r
1931 false, // wordwrap?
\r
1934 input_guitext->setDrawBackground(true);
\r
1938 g_text_buffer = L"ASD LOL 8)";
\r
1939 g_text_buffer_accepted = true;
\r
1943 g_text_buffer = L"";
\r
1944 g_text_buffer_accepted = false;
\r
1947 textbuf_dest = new TextDestSign(
\r
1948 selected_object->getBlock()->getPos(),
\r
1949 selected_object->getId(),
\r
1953 Otherwise pass the event to the server as-is
\r
1957 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1958 selected_object->getId(), g_selected_item);
\r
1962 else // selected_object == NULL
\r
1966 Find out which node we are pointing at
\r
1969 bool nodefound = false;
\r
1971 v3s16 neighbourpos;
\r
1972 core::aabbox3d<f32> nodefacebox;
\r
1973 f32 mindistance = BS * 1001;
\r
1975 v3s16 pos_i = floatToInt(player_position);
\r
1977 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1981 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1982 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1983 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1984 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1985 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1986 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1988 for(s16 y = ystart; y <= yend; y++)
\r
1989 for(s16 z = zstart; z <= zend; z++)
\r
1990 for(s16 x = xstart; x <= xend; x++)
\r
1995 n = client.getNode(v3s16(x,y,z));
\r
1996 if(content_pointable(n.d) == false)
\r
1999 catch(InvalidPositionException &e)
\r
2005 v3f npf = intToFloat(np);
\r
2010 v3s16(0,0,1), // back
\r
2011 v3s16(0,1,0), // top
\r
2012 v3s16(1,0,0), // right
\r
2013 v3s16(0,0,-1), // front
\r
2014 v3s16(0,-1,0), // bottom
\r
2015 v3s16(-1,0,0), // left
\r
2021 if(n.d == CONTENT_TORCH)
\r
2023 v3s16 dir = unpackDir(n.dir);
\r
2024 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2025 dir_f *= BS/2 - BS/6 - BS/20;
\r
2026 v3f cpf = npf + dir_f;
\r
2027 f32 distance = (cpf - camera_position).getLength();
\r
2029 core::aabbox3d<f32> box;
\r
2032 if(dir == v3s16(0,-1,0))
\r
2034 box = core::aabbox3d<f32>(
\r
2035 npf - v3f(BS/6, BS/2, BS/6),
\r
2036 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2040 else if(dir == v3s16(0,1,0))
\r
2042 box = core::aabbox3d<f32>(
\r
2043 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2044 npf + v3f(BS/6, BS/2, BS/6)
\r
2050 box = core::aabbox3d<f32>(
\r
2051 cpf - v3f(BS/6, BS/3, BS/6),
\r
2052 cpf + v3f(BS/6, BS/3, BS/6)
\r
2056 if(distance < mindistance)
\r
2058 if(box.intersectsWithLine(shootline))
\r
2062 neighbourpos = np;
\r
2063 mindistance = distance;
\r
2064 nodefacebox = box;
\r
2073 for(u16 i=0; i<6; i++)
\r
2075 v3f dir_f = v3f(dirs[i].X,
\r
2076 dirs[i].Y, dirs[i].Z);
\r
2077 v3f centerpoint = npf + dir_f * BS/2;
\r
2079 (centerpoint - camera_position).getLength();
\r
2081 if(distance < mindistance)
\r
2083 core::CMatrix4<f32> m;
\r
2084 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2086 // This is the back face
\r
2087 v3f corners[2] = {
\r
2088 v3f(BS/2, BS/2, BS/2),
\r
2089 v3f(-BS/2, -BS/2, BS/2+d)
\r
2092 for(u16 j=0; j<2; j++)
\r
2094 m.rotateVect(corners[j]);
\r
2095 corners[j] += npf;
\r
2098 core::aabbox3d<f32> facebox(corners[0]);
\r
2099 facebox.addInternalPoint(corners[1]);
\r
2101 if(facebox.intersectsWithLine(shootline))
\r
2105 neighbourpos = np + dirs[i];
\r
2106 mindistance = distance;
\r
2107 nodefacebox = facebox;
\r
2109 } // if distance < mindistance
\r
2111 } // regular block
\r
2116 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2118 static float dig_time = 0.0;
\r
2120 if(nodepos != nodepos_old)
\r
2122 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2123 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2125 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2127 client.clearTempMod(nodepos_old);
\r
2132 hilightboxes.push_back(nodefacebox);
\r
2134 if(g_input->getLeftReleased())
\r
2136 client.clearTempMod(nodepos);
\r
2139 if(g_input->getLeftClicked() ||
\r
2140 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2142 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2143 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2145 if(g_input->getLeftClicked())
\r
2147 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2149 if(g_input->getLeftState())
\r
2151 dig_time += dtime;
\r
2153 float dig_time_complete = 0.5;
\r
2154 MapNode n = client.getNode(nodepos);
\r
2155 if(n.d == CONTENT_STONE)
\r
2156 dig_time_complete = 1.5;
\r
2158 u16 dig_index = (u16)(3.99*dig_time/dig_time_complete);
\r
2159 if(dig_time > 0.125)
\r
2161 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2162 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2166 if(g_input->getRightClicked())
\r
2168 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2169 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2172 nodepos_old = nodepos;
\r
2177 } // selected_object == NULL
\r
2179 g_input->resetLeftClicked();
\r
2180 g_input->resetRightClicked();
\r
2182 if(g_input->getLeftReleased())
\r
2184 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2185 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2187 if(g_input->getRightReleased())
\r
2189 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2193 g_input->resetLeftReleased();
\r
2194 g_input->resetRightReleased();
\r
2197 Calculate stuff for drawing
\r
2200 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2202 u32 daynight_ratio = client.getDayNightRatio();
\r
2203 video::SColor bgcolor = video::SColor(
\r
2205 skycolor.getRed() * daynight_ratio / 1000,
\r
2206 skycolor.getGreen() * daynight_ratio / 1000,
\r
2207 skycolor.getBlue() * daynight_ratio / 1000);
\r
2213 if(g_settings.getBool("enable_fog") == true)
\r
2215 f32 range = g_viewing_range_nodes * BS;
\r
2216 if(g_viewing_range_all)
\r
2217 range = 100000*BS;
\r
2221 video::EFT_FOG_LINEAR,
\r
2225 false, // pixel fog
\r
2226 false // range fog
\r
2232 Update gui stuff (0ms)
\r
2235 //TimeTaker guiupdatetimer("Gui updating");
\r
2238 wchar_t temptext[150];
\r
2240 static float drawtime_avg = 0;
\r
2241 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2242 static float beginscenetime_avg = 0;
\r
2243 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2244 static float scenetime_avg = 0;
\r
2245 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2246 static float endscenetime_avg = 0;
\r
2247 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2249 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2251 L", R: range_all=%i"
\r
2253 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2255 g_viewing_range_all,
\r
2257 beginscenetime_avg,
\r
2262 guitext->setText(temptext);
\r
2266 wchar_t temptext[150];
\r
2267 swprintf(temptext, 150,
\r
2268 L"(% .1f, % .1f, % .1f)"
\r
2269 L" (% .3f < btime_jitter < % .3f"
\r
2270 L", dtime_jitter = % .1f %%)",
\r
2271 player_position.X/BS,
\r
2272 player_position.Y/BS,
\r
2273 player_position.Z/BS,
\r
2274 busytime_jitter1_min_sample,
\r
2275 busytime_jitter1_max_sample,
\r
2276 dtime_jitter1_max_fraction * 100.0
\r
2279 guitext2->setText(temptext);
\r
2283 /*wchar_t temptext[100];
\r
2284 swprintf(temptext, 100,
\r
2285 SWPRINTF_CHARSTRING,
\r
2286 infotext.substr(0,99).c_str()
\r
2289 guitext_info->setText(temptext);*/
\r
2291 guitext_info->setText(infotext.c_str());
\r
2298 static u16 old_selected_item = 65535;
\r
2299 if(client.getLocalInventoryUpdated()
\r
2300 || g_selected_item != old_selected_item)
\r
2302 old_selected_item = g_selected_item;
\r
2303 //std::cout<<"Updating local inventory"<<std::endl;
\r
2304 client.getLocalInventory(local_inventory);
\r
2305 quick_inventory->setSelection(g_selected_item);
\r
2306 quick_inventory->update();
\r
2310 Send actions returned by the inventory menu
\r
2312 while(InventoryAction *a = inventoryMenu->getNextAction())
\r
2314 client.sendInventoryAction(a);
\r
2319 if(input_guitext != NULL)
\r
2321 /*wchar_t temptext[100];
\r
2322 swprintf(temptext, 100,
\r
2323 SWPRINTF_CHARSTRING,
\r
2324 g_text_buffer.substr(0,99).c_str()
\r
2326 input_guitext->setText(g_text_buffer.c_str());
\r
2332 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2334 input_guitext->remove();
\r
2335 input_guitext = NULL;
\r
2337 if(textbuf_dest != NULL)
\r
2339 std::string text = wide_to_narrow(g_text_buffer);
\r
2340 dstream<<"Sending text: "<<text<<std::endl;
\r
2341 textbuf_dest->sendText(text);
\r
2342 delete textbuf_dest;
\r
2343 textbuf_dest = NULL;
\r
2349 //guiupdatetimer.stop();
\r
2355 TimeTaker drawtimer("Drawing");
\r
2359 TimeTaker timer("beginScene");
\r
2360 driver->beginScene(true, true, bgcolor);
\r
2361 //driver->beginScene(false, true, bgcolor);
\r
2362 beginscenetime = timer.stop(true);
\r
2367 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2370 TimeTaker timer("smgr");
\r
2372 scenetime = timer.stop(true);
\r
2376 //TimeTaker timer9("auxiliary drawings");
\r
2379 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2380 displaycenter + core::vector2d<s32>(10,0),
\r
2381 video::SColor(255,255,255,255));
\r
2382 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2383 displaycenter + core::vector2d<s32>(0,10),
\r
2384 video::SColor(255,255,255,255));
\r
2387 //TimeTaker //timer10("//timer10");
\r
2389 video::SMaterial m;
\r
2391 m.Lighting = false;
\r
2392 driver->setMaterial(m);
\r
2394 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2396 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2397 i != hilightboxes.end(); i++)
\r
2399 /*std::cout<<"hilightbox min="
\r
2400 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2402 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2404 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2410 //TimeTaker //timer11("//timer11");
\r
2416 guienv->drawAll();
\r
2420 TimeTaker timer("endScene");
\r
2421 driver->endScene();
\r
2422 endscenetime = timer.stop(true);
\r
2425 drawtime = drawtimer.stop(true);
\r
2431 static s16 lastFPS = 0;
\r
2432 //u16 fps = driver->getFPS();
\r
2433 u16 fps = (1.0/dtime_avg1);
\r
2435 if (lastFPS != fps)
\r
2437 core::stringw str = L"Minetest [";
\r
2438 str += driver->getName();
\r
2442 device->setWindowCaption(str.c_str());
\r
2448 device->yield();*/
\r
2451 delete quick_inventory;
\r
2453 } // client is deleted at this point
\r
2458 In the end, delete the Irrlicht device.
\r
2463 Update configuration file
\r
2465 /*if(configpath != "")
\r
2467 g_settings.updateConfigFile(configpath.c_str());
\r
2471 catch(con::PeerNotFoundException &e)
\r
2473 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2475 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2477 This is what has to be done in every thread to get suitable debug info
\r
2479 catch(std::exception &e)
\r
2481 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2482 <<e.what()<<std::endl;
\r
2487 debugstreams_deinit();
\r