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 TODO: Draw big amounts of torches better (that is, throw them in the
\r
171 same meshbuffer (can the meshcombiner class be used?))
\r
174 ======================================================================
\r
176 TODO: Convert the text input system to use a modal menu... or something
\r
178 ======================================================================
\r
183 Setting this to 1 enables a special camera mode that forces
\r
184 the renderers to think that the camera statically points from
\r
185 the starting place to a static direction.
\r
187 This allows one to move around with the player and see what
\r
188 is actually drawn behind solid things and behind the player.
\r
190 #define FIELD_OF_VIEW_TEST 0
\r
192 #ifdef UNITTEST_DISABLE
\r
194 #pragma message ("Disabling unit tests")
\r
196 #warning "Disabling unit tests"
\r
198 // Disable unit tests
\r
199 #define ENABLE_TESTS 0
\r
201 // Enable unit tests
\r
202 #define ENABLE_TESTS 1
\r
206 #pragma comment(lib, "Irrlicht.lib")
\r
207 #pragma comment(lib, "jthread.lib")
\r
208 #pragma comment(lib, "zlibwapi.lib")
\r
209 // This would get rid of the console window
\r
210 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
213 #include <iostream>
\r
215 #include <jmutexautolock.h>
\r
216 #include <locale.h>
\r
217 #include "common_irrlicht.h"
\r
220 #include "player.h"
\r
223 #include "environment.h"
\r
224 #include "server.h"
\r
225 #include "client.h"
\r
226 #include "serialization.h"
\r
227 #include "constants.h"
\r
228 #include "strfnd.h"
\r
229 #include "porting.h"
\r
230 #include "guiPauseMenu.h"
\r
231 #include "irrlichtwrapper.h"
\r
232 #include "gettime.h"
\r
233 #include "porting.h"
\r
234 #include "guiInventoryMenu.h"
\r
236 IrrlichtWrapper *g_irrlicht;
\r
238 // All range-related stuff below is locked behind this
\r
239 JMutex g_range_mutex;
\r
241 // Blocks are viewed in this range from the player
\r
242 s16 g_viewing_range_nodes = 60;
\r
243 //s16 g_viewing_range_nodes = 0;
\r
245 // This is updated by the client's fetchBlocks routine
\r
246 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
248 // If true, the preceding value has no meaning and all blocks
\r
249 // already existing in memory are drawn
\r
250 bool g_viewing_range_all = false;
\r
252 // This is the freetime ratio imposed by the dynamic viewing
\r
253 // range changing code.
\r
254 // It is controlled by the main loop to the smallest value that
\r
255 // inhibits glitches (dtime jitter) in the main loop.
\r
256 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
260 These are loaded from the config file.
\r
263 Settings g_settings;
\r
265 extern void set_default_settings();
\r
271 //u16 g_selected_material = 0;
\r
272 u16 g_selected_item = 0;
\r
277 gui::IGUIEnvironment* guienv = NULL;
\r
278 GUIPauseMenu *pauseMenu = NULL;
\r
279 GUIInventoryMenu *inventoryMenu = NULL;
\r
281 bool noMenuActive()
\r
283 if(pauseMenu && pauseMenu->isVisible())
\r
285 if(inventoryMenu && inventoryMenu->isVisible())
\r
290 std::wstring g_text_buffer;
\r
291 bool g_text_buffer_accepted = false;
\r
293 // When true, the mouse and keyboard are grabbed
\r
294 bool g_game_focused = true;
\r
301 std::ostream *dout_con_ptr = &dummyout;
\r
302 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
303 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
304 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
305 //std::ostream *dout_con_ptr = &dstream;
\r
306 //std::ostream *derr_con_ptr = &dstream;
\r
309 std::ostream *dout_server_ptr = &dstream;
\r
310 std::ostream *derr_server_ptr = &dstream;
\r
313 std::ostream *dout_client_ptr = &dstream;
\r
314 std::ostream *derr_client_ptr = &dstream;
\r
317 gettime.h implementation
\r
323 Use irrlicht because it is more precise than porting.h's
\r
326 if(g_irrlicht == NULL)
\r
328 return g_irrlicht->getTime();
\r
331 class MyEventReceiver : public IEventReceiver
\r
334 // This is the one method that we have to implement
\r
335 virtual bool OnEvent(const SEvent& event)
\r
337 // Remember whether each key is down or up
\r
338 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
340 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
342 if(event.KeyInput.PressedDown)
\r
344 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
345 if(g_game_focused == false)
\r
347 s16 key = event.KeyInput.Key;
\r
348 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
350 g_text_buffer_accepted = true;
\r
352 else if(key == irr::KEY_BACK)
\r
354 if(g_text_buffer.size() > 0)
\r
355 g_text_buffer = g_text_buffer.substr
\r
356 (0, g_text_buffer.size()-1);
\r
360 wchar_t wc = event.KeyInput.Char;
\r
362 g_text_buffer += wc;
\r
366 if(pauseMenu != NULL)
\r
368 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
370 if(g_game_focused == true && noMenuActive())
\r
372 dstream<<DTIME<<"MyEventReceiver: "
\r
373 <<"Launching pause menu"<<std::endl;
\r
374 pauseMenu->launch();
\r
380 if(inventoryMenu != NULL)
\r
382 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
384 if(g_game_focused == true && noMenuActive())
\r
386 dstream<<DTIME<<"MyEventReceiver: "
\r
387 <<"Launching inventory"<<std::endl;
\r
388 inventoryMenu->launch();
\r
394 // Material selection
\r
395 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
397 if(g_game_focused == true)
\r
399 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
402 g_selected_item = 0;
\r
403 dstream<<DTIME<<"Selected item: "
\r
404 <<g_selected_item<<std::endl;
\r
408 // Viewing range selection
\r
409 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
412 JMutexAutoLock lock(g_range_mutex);
\r
413 if(g_viewing_range_all)
\r
415 g_viewing_range_all = false;
\r
416 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
420 g_viewing_range_all = true;
\r
421 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
425 // Print debug stacks
\r
426 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
429 dstream<<"-----------------------------------------"
\r
431 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
432 dstream<<"-----------------------------------------"
\r
434 debug_stacks_print();
\r
439 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
441 if(noMenuActive() == false)
\r
443 left_active = false;
\r
444 middle_active = false;
\r
445 right_active = false;
\r
449 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
450 left_active = event.MouseInput.isLeftPressed();
\r
451 middle_active = event.MouseInput.isMiddlePressed();
\r
452 right_active = event.MouseInput.isRightPressed();
\r
454 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
456 leftclicked = true;
\r
458 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
460 rightclicked = true;
\r
462 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
464 leftreleased = true;
\r
466 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
468 rightreleased = true;
\r
470 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
472 /*dstream<<"event.MouseInput.Wheel="
\r
473 <<event.MouseInput.Wheel<<std::endl;*/
\r
474 if(event.MouseInput.Wheel < 0)
\r
476 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
479 g_selected_item = 0;
\r
481 else if(event.MouseInput.Wheel > 0)
\r
483 if(g_selected_item > 0)
\r
486 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
495 // This is used to check whether a key is being held down
\r
496 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
498 return keyIsDown[keyCode];
\r
503 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
504 keyIsDown[i] = false;
\r
505 leftclicked = false;
\r
506 rightclicked = false;
\r
507 leftreleased = false;
\r
508 rightreleased = false;
\r
510 left_active = false;
\r
511 middle_active = false;
\r
512 right_active = false;
\r
518 bool rightreleased;
\r
521 bool middle_active;
\r
525 // We use this array to store the current state of each key
\r
526 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
537 virtual ~InputHandler()
\r
541 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
543 virtual v2s32 getMousePos() = 0;
\r
544 virtual void setMousePos(s32 x, s32 y) = 0;
\r
546 virtual bool getLeftState() = 0;
\r
547 virtual bool getRightState() = 0;
\r
549 virtual bool getLeftClicked() = 0;
\r
550 virtual bool getRightClicked() = 0;
\r
551 virtual void resetLeftClicked() = 0;
\r
552 virtual void resetRightClicked() = 0;
\r
554 virtual bool getLeftReleased() = 0;
\r
555 virtual bool getRightReleased() = 0;
\r
556 virtual void resetLeftReleased() = 0;
\r
557 virtual void resetRightReleased() = 0;
\r
559 virtual void step(float dtime) {};
\r
561 virtual void clear() {};
\r
564 InputHandler *g_input = NULL;
\r
569 g_game_focused = true;
\r
574 g_game_focused = false;
\r
577 class RealInputHandler : public InputHandler
\r
580 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
582 m_receiver(receiver)
\r
585 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
587 return m_receiver->IsKeyDown(keyCode);
\r
589 virtual v2s32 getMousePos()
\r
591 return m_device->getCursorControl()->getPosition();
\r
593 virtual void setMousePos(s32 x, s32 y)
\r
595 m_device->getCursorControl()->setPosition(x, y);
\r
598 virtual bool getLeftState()
\r
600 return m_receiver->left_active;
\r
602 virtual bool getRightState()
\r
604 return m_receiver->right_active;
\r
607 virtual bool getLeftClicked()
\r
609 if(g_game_focused == false)
\r
611 return m_receiver->leftclicked;
\r
613 virtual bool getRightClicked()
\r
615 if(g_game_focused == false)
\r
617 return m_receiver->rightclicked;
\r
619 virtual void resetLeftClicked()
\r
621 m_receiver->leftclicked = false;
\r
623 virtual void resetRightClicked()
\r
625 m_receiver->rightclicked = false;
\r
628 virtual bool getLeftReleased()
\r
630 if(g_game_focused == false)
\r
632 return m_receiver->leftreleased;
\r
634 virtual bool getRightReleased()
\r
636 if(g_game_focused == false)
\r
638 return m_receiver->rightreleased;
\r
640 virtual void resetLeftReleased()
\r
642 m_receiver->leftreleased = false;
\r
644 virtual void resetRightReleased()
\r
646 m_receiver->rightreleased = false;
\r
651 resetRightClicked();
\r
652 resetLeftClicked();
\r
655 IrrlichtDevice *m_device;
\r
656 MyEventReceiver *m_receiver;
\r
659 class RandomInputHandler : public InputHandler
\r
662 RandomInputHandler()
\r
664 leftclicked = false;
\r
665 rightclicked = false;
\r
666 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
667 keydown[i] = false;
\r
669 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
671 return keydown[keyCode];
\r
673 virtual v2s32 getMousePos()
\r
677 virtual void setMousePos(s32 x, s32 y)
\r
679 mousepos = v2s32(x,y);
\r
682 virtual bool getLeftState()
\r
686 virtual bool getRightState()
\r
691 virtual bool getLeftClicked()
\r
693 return leftclicked;
\r
695 virtual bool getRightClicked()
\r
697 return rightclicked;
\r
699 virtual void resetLeftClicked()
\r
701 leftclicked = false;
\r
703 virtual void resetRightClicked()
\r
705 rightclicked = false;
\r
708 virtual bool getLeftReleased()
\r
712 virtual bool getRightReleased()
\r
716 virtual void resetLeftReleased()
\r
719 virtual void resetRightReleased()
\r
723 virtual void step(float dtime)
\r
726 static float counter1 = 0;
\r
730 counter1 = 0.1*Rand(1,10);
\r
731 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
732 g_selected_material++;
\r
734 g_selected_material = 0;*/
\r
735 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
738 g_selected_item = 0;
\r
742 static float counter1 = 0;
\r
746 counter1 = 0.1*Rand(1, 40);
\r
747 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
751 static float counter1 = 0;
\r
755 counter1 = 0.1*Rand(1, 40);
\r
756 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
760 static float counter1 = 0;
\r
764 counter1 = 0.1*Rand(1, 40);
\r
765 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
769 static float counter1 = 0;
\r
773 counter1 = 0.1*Rand(1, 40);
\r
774 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
778 static float counter1 = 0;
\r
782 counter1 = 0.1*Rand(1, 20);
\r
783 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
787 static float counter1 = 0;
\r
791 counter1 = 0.1*Rand(1, 30);
\r
792 leftclicked = true;
\r
796 static float counter1 = 0;
\r
800 counter1 = 0.1*Rand(1, 20);
\r
801 rightclicked = true;
\r
804 mousepos += mousespeed;
\r
807 s32 Rand(s32 min, s32 max)
\r
809 return (rand()%(max-min+1))+min;
\r
812 bool keydown[KEY_KEY_CODES_COUNT];
\r
819 void updateViewingRange(f32 frametime, Client *client)
\r
821 // Range_all messes up frametime_avg
\r
822 if(g_viewing_range_all == true)
\r
825 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
827 // Initialize to the target value
\r
828 static float frametime_avg = 1.0/wanted_fps;
\r
829 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
830 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
832 static f32 counter = 0;
\r
834 counter -= frametime;
\r
837 //counter = 1.0; //seconds
\r
838 counter = 0.5; //seconds
\r
840 //float freetime_ratio = 0.2;
\r
841 //float freetime_ratio = 0.4;
\r
842 float freetime_ratio = FREETIME_RATIO;
\r
844 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
846 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
848 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
849 + frametime_avg / frametime_wanted / 2.0;*/
\r
851 //float fraction = frametime_avg / frametime_wanted;
\r
853 static bool fraction_is_good = false;
\r
855 //float fraction_good_threshold = 0.1;
\r
856 //float fraction_bad_threshold = 0.25;
\r
857 float fraction_good_threshold = 0.075;
\r
858 float fraction_bad_threshold = 0.125;
\r
859 float fraction_limit;
\r
860 // Use high limit if fraction is good AND the fraction would
\r
861 // lower the range. We want to keep the range fairly high.
\r
862 if(fraction_is_good && fraction > 1.0)
\r
863 fraction_limit = fraction_bad_threshold;
\r
865 fraction_limit = fraction_good_threshold;
\r
867 if(fabs(fraction - 1.0) < fraction_limit)
\r
869 fraction_is_good = true;
\r
874 fraction_is_good = false;
\r
877 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
878 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
879 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
880 <<" faction = "<<fraction<<std::endl;*/
\r
882 JMutexAutoLock lock(g_range_mutex);
\r
884 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
885 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
887 s16 n = (float)g_viewing_range_nodes / fraction;
\r
888 if(n < viewing_range_nodes_min)
\r
889 n = viewing_range_nodes_min;
\r
890 if(n > viewing_range_nodes_max)
\r
891 n = viewing_range_nodes_max;
\r
893 bool can_change = true;
\r
895 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
896 can_change = false;
\r
899 g_viewing_range_nodes = n;
\r
901 /*dstream<<"g_viewing_range_nodes = "
\r
902 <<g_viewing_range_nodes<<std::endl;*/
\r
905 class GUIQuickInventory : public IEventReceiver
\r
909 gui::IGUIEnvironment* env,
\r
910 gui::IGUIElement* parent,
\r
913 Inventory *inventory):
\r
914 m_itemcount(itemcount),
\r
915 m_inventory(inventory)
\r
917 core::rect<s32> imgsize(0,0,48,48);
\r
918 core::rect<s32> textsize(0,0,48,16);
\r
919 v2s32 spacing(0, 64);
\r
920 for(s32 i=0; i<m_itemcount; i++)
\r
922 m_images.push_back(env->addImage(
\r
923 imgsize + pos + spacing*i
\r
925 m_images[i]->setScaleImage(true);
\r
926 m_texts.push_back(env->addStaticText(
\r
928 textsize + pos + spacing*i,
\r
931 m_texts[i]->setBackgroundColor(
\r
932 video::SColor(128,0,0,0));
\r
933 m_texts[i]->setTextAlignment(
\r
935 gui::EGUIA_UPPERLEFT);
\r
939 virtual bool OnEvent(const SEvent& event)
\r
944 void setSelection(s32 i)
\r
953 start = m_selection - m_itemcount / 2;
\r
955 InventoryList *mainlist = m_inventory->getList("main");
\r
957 for(s32 i=0; i<m_itemcount; i++)
\r
961 if(j > (s32)mainlist->getSize() - 1)
\r
962 j -= mainlist->getSize();
\r
964 j += mainlist->getSize();
\r
966 InventoryItem *item = mainlist->getItem(j);
\r
970 m_images[i]->setImage(NULL);
\r
973 if(m_selection == j)
\r
974 swprintf(t, 10, L"<-");
\r
976 swprintf(t, 10, L"");
\r
977 m_texts[i]->setText(t);
\r
979 // The next ifs will segfault with a NULL pointer
\r
984 m_images[i]->setImage(item->getImage());
\r
987 if(m_selection == j)
\r
988 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
990 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
991 m_texts[i]->setText(t);
\r
997 core::array<gui::IGUIStaticText*> m_texts;
\r
998 core::array<gui::IGUIImage*> m_images;
\r
999 Inventory *m_inventory;
\r
1003 int main(int argc, char *argv[])
\r
1006 Low-level initialization
\r
1009 bool disable_stderr = false;
\r
1011 disable_stderr = true;
\r
1014 // Initialize debug streams
\r
1015 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1016 // Initialize debug stacks
\r
1017 debug_stacks_init();
\r
1019 DSTACK(__FUNCTION_NAME);
\r
1025 Parse command line
\r
1028 // List all allowed options
\r
1029 core::map<std::string, ValueSpec> allowed_options;
\r
1030 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1031 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1032 "Run server directly"));
\r
1033 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1034 "Load configuration from specified file"));
\r
1035 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1036 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1037 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1038 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1039 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1041 Settings cmd_args;
\r
1043 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1045 if(ret == false || cmd_args.getFlag("help"))
\r
1047 dstream<<"Allowed options:"<<std::endl;
\r
1048 for(core::map<std::string, ValueSpec>::Iterator
\r
1049 i = allowed_options.getIterator();
\r
1050 i.atEnd() == false; i++)
\r
1052 dstream<<" --"<<i.getNode()->getKey();
\r
1053 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1058 dstream<<" <value>";
\r
1060 dstream<<std::endl;
\r
1062 if(i.getNode()->getValue().help != NULL)
\r
1064 dstream<<" "<<i.getNode()->getValue().help
\r
1069 return cmd_args.getFlag("help") ? 0 : 1;
\r
1074 Basic initialization
\r
1077 // Initialize default settings
\r
1078 set_default_settings();
\r
1080 // Print startup message
\r
1081 dstream<<DTIME<<"minetest-c55"
\r
1082 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1083 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1086 // Set locale. This is for forcing '.' as the decimal point.
\r
1087 std::locale::global(std::locale("C"));
\r
1088 // This enables printing all characters in bitmap font
\r
1089 setlocale(LC_CTYPE, "en_US");
\r
1091 // Initialize sockets
\r
1093 atexit(sockets_cleanup);
\r
1103 // Path of configuration file in use
\r
1104 std::string configpath = "";
\r
1106 if(cmd_args.exists("config"))
\r
1108 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1111 dstream<<"Could not read configuration from \""
\r
1112 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1115 configpath = cmd_args.get("config");
\r
1119 const char *filenames[2] =
\r
1121 "../minetest.conf",
\r
1122 "../../minetest.conf"
\r
1125 for(u32 i=0; i<2; i++)
\r
1127 bool r = g_settings.readConfigFile(filenames[i]);
\r
1130 configpath = filenames[i];
\r
1136 // Initialize random seed
\r
1142 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1143 || cmd_args.getFlag("enable-unittests") == true)
\r
1149 Global range mutex
\r
1151 g_range_mutex.Init();
\r
1152 assert(g_range_mutex.IsInitialized());
\r
1154 // Read map parameters from settings
\r
1156 HMParams hm_params;
\r
1157 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1158 hm_params.randmax = g_settings.get("height_randmax");
\r
1159 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1160 hm_params.base = g_settings.get("height_base");
\r
1162 MapParams map_params;
\r
1163 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1164 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1170 std::cout<<std::endl<<std::endl;
\r
1173 <<" .__ __ __ "<<std::endl
\r
1174 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1175 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1176 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1177 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1178 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1181 std::cout<<std::endl;
\r
1182 //char templine[100];
\r
1186 if(cmd_args.exists("port"))
\r
1188 port = cmd_args.getU16("port");
\r
1192 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1193 std::cout<<"-> "<<port<<std::endl;
\r
1196 if(cmd_args.getFlag("server"))
\r
1198 DSTACK("Dedicated server branch");
\r
1200 std::cout<<std::endl;
\r
1201 std::cout<<"========================"<<std::endl;
\r
1202 std::cout<<"Running dedicated server"<<std::endl;
\r
1203 std::cout<<"========================"<<std::endl;
\r
1204 std::cout<<std::endl;
\r
1206 Server server("../map", hm_params, map_params);
\r
1207 server.start(port);
\r
1211 // This is kind of a hack but can be done like this
\r
1212 // because server.step() is very light
\r
1214 server.step(0.030);
\r
1216 static int counter = 0;
\r
1222 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1223 core::list<PlayerInfo>::Iterator i;
\r
1224 static u32 sum_old = 0;
\r
1225 u32 sum = PIChecksum(list);
\r
1226 if(sum != sum_old)
\r
1228 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1229 for(i=list.begin(); i!=list.end(); i++)
\r
1231 i->PrintLine(&std::cout);
\r
1241 bool hosting = false;
\r
1242 char connect_name[100] = "";
\r
1244 if(cmd_args.exists("address"))
\r
1246 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1248 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1250 std::cout<<g_settings.get("address")<<std::endl;
\r
1251 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1255 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1256 std::cin.getline(connect_name, 100);
\r
1259 if(connect_name[0] == 0){
\r
1260 snprintf(connect_name, 100, "127.0.0.1");
\r
1265 std::cout<<"> Hosting game"<<std::endl;
\r
1267 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1269 char playername[PLAYERNAME_SIZE] = "";
\r
1270 if(g_settings.get("name") != "")
\r
1272 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1276 std::cout<<"Name of player: ";
\r
1277 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1279 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1282 Resolution selection
\r
1285 bool fullscreen = false;
\r
1286 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1287 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1291 MyEventReceiver receiver;
\r
1293 video::E_DRIVER_TYPE driverType;
\r
1296 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1297 driverType = video::EDT_OPENGL;
\r
1299 driverType = video::EDT_OPENGL;
\r
1302 // create device and exit if creation failed
\r
1304 IrrlichtDevice *device;
\r
1305 device = createDevice(driverType,
\r
1306 core::dimension2d<u32>(screenW, screenH),
\r
1307 16, fullscreen, false, false, &receiver);
\r
1310 return 1; // could not create selected driver.
\r
1312 g_irrlicht = new IrrlichtWrapper(device);
\r
1314 //g_device = device;
\r
1316 device->setResizable(true);
\r
1318 bool random_input = g_settings.getBool("random_input")
\r
1319 || cmd_args.getFlag("random-input");
\r
1321 g_input = new RandomInputHandler();
\r
1323 g_input = new RealInputHandler(device, &receiver);
\r
1326 Continue initialization
\r
1329 video::IVideoDriver* driver = device->getVideoDriver();
\r
1332 This changes the minimum allowed number of vertices in a VBO
\r
1334 //driver->setMinHardwareBufferVertexCount(1);
\r
1336 scene::ISceneManager* smgr = device->getSceneManager();
\r
1338 guienv = device->getGUIEnvironment();
\r
1339 gui::IGUISkin* skin = guienv->getSkin();
\r
1340 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1342 skin->setFont(font);
\r
1343 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1344 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1345 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1346 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1347 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1348 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1350 const wchar_t *text = L"Loading and connecting...";
\r
1351 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1352 core::dimension2d<u32> textd = font->getDimension(text);
\r
1353 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1354 // Have to add a bit to disable the text from word wrapping
\r
1355 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1356 core::vector2d<s32> textsize(300, textd.Height);
\r
1357 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1359 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1360 text, textrect, false, false);
\r
1361 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1363 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1364 guienv->drawAll();
\r
1365 driver->endScene();
\r
1368 Preload some random textures that are used in threads
\r
1371 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1372 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1373 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1374 g_texturecache.set("crack", driver->getTexture("../data/crack.png"));
\r
1377 Load tile textures
\r
1379 for(s32 i=0; i<TILES_COUNT; i++)
\r
1381 if(g_tile_texture_names[i] == NULL)
\r
1383 std::string name = g_tile_texture_names[i];
\r
1384 std::string filename;
\r
1385 filename += "../data/";
\r
1387 filename += ".png";
\r
1388 g_texturecache.set(name, driver->getTexture(filename.c_str()));
\r
1393 //tile_materials_preload(g_texturecache);
\r
1394 tile_materials_preload(g_irrlicht);
\r
1395 //tile_materials_init();
\r
1398 Make a scope here for the client so that it gets removed
\r
1399 before the irrlicht device
\r
1403 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1408 SharedPtr<Server> server;
\r
1410 server = new Server("../map", hm_params, map_params);
\r
1411 server->start(port);
\r
1418 Client client(device, playername,
\r
1420 g_viewing_range_nodes,
\r
1421 g_viewing_range_all);
\r
1423 Address connect_address(0,0,0,0, port);
\r
1425 connect_address.Resolve(connect_name);
\r
1427 catch(ResolveError &e)
\r
1429 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1433 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1434 client.connect(connect_address);
\r
1437 while(client.connectedAndInitialized() == false)
\r
1440 if(server != NULL){
\r
1441 server->step(0.1);
\r
1446 catch(con::PeerNotFoundException &e)
\r
1448 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1455 /*scene::ISceneNode* skybox;
\r
1456 skybox = smgr->addSkyBoxSceneNode(
\r
1457 driver->getTexture("../data/skybox2.png"),
\r
1458 driver->getTexture("../data/skybox3.png"),
\r
1459 driver->getTexture("../data/skybox1.png"),
\r
1460 driver->getTexture("../data/skybox1.png"),
\r
1461 driver->getTexture("../data/skybox1.png"),
\r
1462 driver->getTexture("../data/skybox1.png"));*/
\r
1465 Create the camera node
\r
1468 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1469 0, // Camera parent
\r
1470 v3f(BS*100, BS*2, BS*100), // Look from
\r
1471 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1475 if(camera == NULL)
\r
1478 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1480 camera->setFOV(FOV_ANGLE);
\r
1482 // Just so big a value that everything rendered is visible
\r
1483 camera->setFarValue(100000*BS);
\r
1485 f32 camera_yaw = 0; // "right/left"
\r
1486 f32 camera_pitch = 0; // "up/down"
\r
1492 gui_loadingtext->remove();
\r
1495 Add some gui stuff
\r
1498 // This is a copy of the inventory that the client's environment has
\r
1499 Inventory local_inventory;
\r
1501 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1502 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1505 We need some kind of a root node to be able to add
\r
1506 custom elements directly on the screen.
\r
1507 Otherwise they won't be automatically drawn.
\r
1509 gui::IGUIStaticText *root = guienv->addStaticText(L"",
\r
1510 core::rect<s32>(0, 0, 10000, 10000));
\r
1513 pauseMenu = new GUIPauseMenu(guienv, root, -1, device);
\r
1516 inventoryMenu = new GUIInventoryMenu(guienv, root, -1, &local_inventory);
\r
1518 pauseMenu->launch();
\r
1519 //inventoryMenu->launch();
\r
1521 // First line of debug text
\r
1522 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1524 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1526 // Second line of debug text
\r
1527 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1529 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1532 // At the middle of the screen
\r
1533 // Object infos are shown in this
\r
1534 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1536 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1540 Some statistics are collected in these
\r
1543 u32 beginscenetime = 0;
\r
1544 u32 scenetime = 0;
\r
1545 u32 endscenetime = 0;
\r
1553 virtual void sendText(std::string text) = 0;
\r
1556 struct TextDestSign : public TextDest
\r
1558 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1560 m_blockpos = blockpos;
\r
1562 m_client = client;
\r
1564 void sendText(std::string text)
\r
1566 dstream<<"Changing text of a sign object: "
\r
1567 <<text<<std::endl;
\r
1568 m_client->sendSignText(m_blockpos, m_id, text);
\r
1576 TextDest *textbuf_dest = NULL;
\r
1578 //gui::IGUIWindow* input_window = NULL;
\r
1579 gui::IGUIStaticText* input_guitext = NULL;
\r
1585 bool first_loop_after_window_activation = true;
\r
1587 // Time is in milliseconds
\r
1588 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1589 // NOTE: Have to call run() between calls of this to update the timer
\r
1590 u32 lasttime = device->getTimer()->getTime();
\r
1592 while(device->run())
\r
1595 Run global IrrlichtWrapper's main thread processing stuff
\r
1597 g_irrlicht->Run();
\r
1600 Random calculations
\r
1602 v2u32 screensize = driver->getScreenSize();
\r
1603 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1605 pauseMenu->resizeGui();
\r
1606 inventoryMenu->resizeGui();
\r
1608 // Hilight boxes collected during the loop and displayed
\r
1609 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1612 std::wstring infotext;
\r
1614 //TimeTaker //timer1("//timer1");
\r
1616 // Time of frame without fps limit
\r
1620 // not using getRealTime is necessary for wine
\r
1621 u32 time = device->getTimer()->getTime();
\r
1622 if(time > lasttime)
\r
1623 busytime_u32 = time - lasttime;
\r
1626 busytime = busytime_u32 / 1000.0;
\r
1629 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1631 // Absolutelu necessary for wine!
\r
1638 //updateViewingRange(dtime, &client);
\r
1639 updateViewingRange(busytime, &client);
\r
1646 float fps_max = g_settings.getFloat("fps_max");
\r
1647 u32 frametime_min = 1000./fps_max;
\r
1649 if(busytime_u32 < frametime_min)
\r
1651 u32 sleeptime = frametime_min - busytime_u32;
\r
1652 device->sleep(sleeptime);
\r
1656 // Absolutelu necessary for wine!
\r
1660 Time difference calculation
\r
1662 f32 dtime; // in seconds
\r
1664 u32 time = device->getTimer()->getTime();
\r
1665 if(time > lasttime)
\r
1666 dtime = (time - lasttime) / 1000.0;
\r
1672 Time average and jitter calculation
\r
1675 static f32 dtime_avg1 = 0.0;
\r
1676 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1677 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1679 static f32 dtime_jitter1_max_sample = 0.0;
\r
1680 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1682 static f32 jitter1_max = 0.0;
\r
1683 static f32 counter = 0.0;
\r
1684 if(dtime_jitter1 > jitter1_max)
\r
1685 jitter1_max = dtime_jitter1;
\r
1690 dtime_jitter1_max_sample = jitter1_max;
\r
1691 dtime_jitter1_max_fraction
\r
1692 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1693 jitter1_max = 0.0;
\r
1696 Control freetime ratio
\r
1698 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1700 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1701 g_freetime_ratio += 0.01;
\r
1705 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1706 g_freetime_ratio -= 0.01;
\r
1712 Busytime average and jitter calculation
\r
1715 static f32 busytime_avg1 = 0.0;
\r
1716 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1717 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1719 static f32 busytime_jitter1_max_sample = 0.0;
\r
1720 static f32 busytime_jitter1_min_sample = 0.0;
\r
1722 static f32 jitter1_max = 0.0;
\r
1723 static f32 jitter1_min = 0.0;
\r
1724 static f32 counter = 0.0;
\r
1725 if(busytime_jitter1 > jitter1_max)
\r
1726 jitter1_max = busytime_jitter1;
\r
1727 if(busytime_jitter1 < jitter1_min)
\r
1728 jitter1_min = busytime_jitter1;
\r
1730 if(counter > 0.0){
\r
1732 busytime_jitter1_max_sample = jitter1_max;
\r
1733 busytime_jitter1_min_sample = jitter1_min;
\r
1734 jitter1_max = 0.0;
\r
1735 jitter1_min = 0.0;
\r
1740 Debug info for client
\r
1743 static float counter = 0.0;
\r
1748 client.printDebugInfo(std::cout);
\r
1753 Input handler step()
\r
1755 g_input->step(dtime);
\r
1760 /*if(g_esc_pressed)
\r
1766 inventoryMenu->setVisible(true);
\r
1767 g_i_pressed = false;
\r
1771 Player speed control
\r
1774 if(g_game_focused)
\r
1781 bool a_superspeed,
\r
1784 PlayerControl control(
\r
1785 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1786 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1787 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1788 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1789 g_input->isKeyDown(irr::KEY_SPACE),
\r
1790 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1794 client.setPlayerControl(control);
\r
1798 // Set every key to inactive
\r
1799 PlayerControl control;
\r
1800 client.setPlayerControl(control);
\r
1805 Process environment
\r
1809 //TimeTaker timer("client.step(dtime)");
\r
1810 client.step(dtime);
\r
1811 //client.step(dtime_avg1);
\r
1814 if(server != NULL)
\r
1816 //TimeTaker timer("server->step(dtime)");
\r
1817 server->step(dtime);
\r
1820 v3f player_position = client.getPlayerPosition();
\r
1822 //TimeTaker //timer2("//timer2");
\r
1825 Mouse and camera control
\r
1828 if((device->isWindowActive()
\r
1835 device->getCursorControl()->setVisible(false);
\r
1837 if(first_loop_after_window_activation){
\r
1838 //std::cout<<"window active, first loop"<<std::endl;
\r
1839 first_loop_after_window_activation = false;
\r
1842 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1843 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1844 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1845 camera_yaw -= dx*0.2;
\r
1846 camera_pitch += dy*0.2;
\r
1847 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1848 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1850 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1853 device->getCursorControl()->setVisible(true);
\r
1855 //std::cout<<"window inactive"<<std::endl;
\r
1856 first_loop_after_window_activation = true;
\r
1859 camera_yaw = wrapDegrees(camera_yaw);
\r
1860 camera_pitch = wrapDegrees(camera_pitch);
\r
1862 v3f camera_direction = v3f(0,0,1);
\r
1863 camera_direction.rotateYZBy(camera_pitch);
\r
1864 camera_direction.rotateXZBy(camera_yaw);
\r
1866 v3f camera_position =
\r
1867 player_position + v3f(0, BS+BS/2, 0);
\r
1869 camera->setPosition(camera_position);
\r
1870 // *100.0 helps in large map coordinates
\r
1871 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1873 if(FIELD_OF_VIEW_TEST){
\r
1874 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1875 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1878 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1879 //TimeTaker timer("client.updateCamera");
\r
1880 client.updateCamera(camera_position, camera_direction);
\r
1884 //TimeTaker //timer3("//timer3");
\r
1887 Calculate what block is the crosshair pointing to
\r
1890 //u32 t1 = device->getTimer()->getRealTime();
\r
1892 //f32 d = 4; // max. distance
\r
1893 f32 d = 4; // max. distance
\r
1894 core::line3d<f32> shootline(camera_position,
\r
1895 camera_position + camera_direction * BS * (d+1));
\r
1897 MapBlockObject *selected_object = client.getSelectedObject
\r
1898 (d*BS, camera_position, shootline);
\r
1900 if(selected_object != NULL)
\r
1902 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1904 core::aabbox3d<f32> box_on_map
\r
1905 = selected_object->getSelectionBoxOnMap();
\r
1907 hilightboxes.push_back(box_on_map);
\r
1909 infotext = narrow_to_wide(selected_object->infoText());
\r
1911 if(g_input->getLeftClicked())
\r
1913 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1914 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1915 selected_object->getId(), g_selected_item);
\r
1917 else if(g_input->getRightClicked())
\r
1919 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1921 Check if we want to modify the object ourselves
\r
1923 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1925 dstream<<"Sign object right-clicked"<<std::endl;
\r
1929 input_guitext = guienv->addStaticText(L"",
\r
1930 core::rect<s32>(150,100,350,120),
\r
1932 false, // wordwrap?
\r
1935 input_guitext->setDrawBackground(true);
\r
1939 g_text_buffer = L"ASD LOL 8)";
\r
1940 g_text_buffer_accepted = true;
\r
1944 g_text_buffer = L"";
\r
1945 g_text_buffer_accepted = false;
\r
1948 textbuf_dest = new TextDestSign(
\r
1949 selected_object->getBlock()->getPos(),
\r
1950 selected_object->getId(),
\r
1954 Otherwise pass the event to the server as-is
\r
1958 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1959 selected_object->getId(), g_selected_item);
\r
1963 else // selected_object == NULL
\r
1967 Find out which node we are pointing at
\r
1970 bool nodefound = false;
\r
1972 v3s16 neighbourpos;
\r
1973 core::aabbox3d<f32> nodefacebox;
\r
1974 f32 mindistance = BS * 1001;
\r
1976 v3s16 pos_i = floatToInt(player_position);
\r
1978 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1982 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1983 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1984 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1985 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1986 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1987 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1989 for(s16 y = ystart; y <= yend; y++)
\r
1990 for(s16 z = zstart; z <= zend; z++)
\r
1991 for(s16 x = xstart; x <= xend; x++)
\r
1996 n = client.getNode(v3s16(x,y,z));
\r
1997 if(content_pointable(n.d) == false)
\r
2000 catch(InvalidPositionException &e)
\r
2006 v3f npf = intToFloat(np);
\r
2011 v3s16(0,0,1), // back
\r
2012 v3s16(0,1,0), // top
\r
2013 v3s16(1,0,0), // right
\r
2014 v3s16(0,0,-1), // front
\r
2015 v3s16(0,-1,0), // bottom
\r
2016 v3s16(-1,0,0), // left
\r
2022 if(n.d == CONTENT_TORCH)
\r
2024 v3s16 dir = unpackDir(n.dir);
\r
2025 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2026 dir_f *= BS/2 - BS/6 - BS/20;
\r
2027 v3f cpf = npf + dir_f;
\r
2028 f32 distance = (cpf - camera_position).getLength();
\r
2030 core::aabbox3d<f32> box;
\r
2033 if(dir == v3s16(0,-1,0))
\r
2035 box = core::aabbox3d<f32>(
\r
2036 npf - v3f(BS/6, BS/2, BS/6),
\r
2037 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2041 else if(dir == v3s16(0,1,0))
\r
2043 box = core::aabbox3d<f32>(
\r
2044 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2045 npf + v3f(BS/6, BS/2, BS/6)
\r
2051 box = core::aabbox3d<f32>(
\r
2052 cpf - v3f(BS/6, BS/3, BS/6),
\r
2053 cpf + v3f(BS/6, BS/3, BS/6)
\r
2057 if(distance < mindistance)
\r
2059 if(box.intersectsWithLine(shootline))
\r
2063 neighbourpos = np;
\r
2064 mindistance = distance;
\r
2065 nodefacebox = box;
\r
2074 for(u16 i=0; i<6; i++)
\r
2076 v3f dir_f = v3f(dirs[i].X,
\r
2077 dirs[i].Y, dirs[i].Z);
\r
2078 v3f centerpoint = npf + dir_f * BS/2;
\r
2080 (centerpoint - camera_position).getLength();
\r
2082 if(distance < mindistance)
\r
2084 core::CMatrix4<f32> m;
\r
2085 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2087 // This is the back face
\r
2088 v3f corners[2] = {
\r
2089 v3f(BS/2, BS/2, BS/2),
\r
2090 v3f(-BS/2, -BS/2, BS/2+d)
\r
2093 for(u16 j=0; j<2; j++)
\r
2095 m.rotateVect(corners[j]);
\r
2096 corners[j] += npf;
\r
2099 core::aabbox3d<f32> facebox(corners[0]);
\r
2100 facebox.addInternalPoint(corners[1]);
\r
2102 if(facebox.intersectsWithLine(shootline))
\r
2106 neighbourpos = np + dirs[i];
\r
2107 mindistance = distance;
\r
2108 nodefacebox = facebox;
\r
2110 } // if distance < mindistance
\r
2112 } // regular block
\r
2117 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2119 static float dig_time = 0.0;
\r
2121 if(nodepos != nodepos_old)
\r
2123 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2124 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2126 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2128 client.clearTempMod(nodepos_old);
\r
2133 hilightboxes.push_back(nodefacebox);
\r
2135 if(g_input->getLeftReleased())
\r
2137 client.clearTempMod(nodepos);
\r
2140 if(g_input->getLeftClicked() ||
\r
2141 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2143 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2144 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2146 if(g_input->getLeftClicked())
\r
2148 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2150 if(g_input->getLeftState())
\r
2152 dig_time += dtime;
\r
2154 float dig_time_complete = 0.5;
\r
2155 MapNode n = client.getNode(nodepos);
\r
2156 if(n.d == CONTENT_STONE)
\r
2157 dig_time_complete = 1.5;
\r
2159 u16 dig_index = (u16)(3.99*dig_time/dig_time_complete);
\r
2160 if(dig_time > 0.125)
\r
2162 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2163 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2167 if(g_input->getRightClicked())
\r
2169 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2170 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2173 nodepos_old = nodepos;
\r
2178 } // selected_object == NULL
\r
2180 g_input->resetLeftClicked();
\r
2181 g_input->resetRightClicked();
\r
2183 if(g_input->getLeftReleased())
\r
2185 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2186 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2188 if(g_input->getRightReleased())
\r
2190 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2194 g_input->resetLeftReleased();
\r
2195 g_input->resetRightReleased();
\r
2198 Calculate stuff for drawing
\r
2201 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2203 u32 daynight_ratio = client.getDayNightRatio();
\r
2204 video::SColor bgcolor = video::SColor(
\r
2206 skycolor.getRed() * daynight_ratio / 1000,
\r
2207 skycolor.getGreen() * daynight_ratio / 1000,
\r
2208 skycolor.getBlue() * daynight_ratio / 1000);
\r
2214 if(g_settings.getBool("enable_fog") == true)
\r
2216 f32 range = g_viewing_range_nodes * BS;
\r
2217 if(g_viewing_range_all)
\r
2218 range = 100000*BS;
\r
2222 video::EFT_FOG_LINEAR,
\r
2226 false, // pixel fog
\r
2227 false // range fog
\r
2233 Update gui stuff (0ms)
\r
2236 //TimeTaker guiupdatetimer("Gui updating");
\r
2239 wchar_t temptext[150];
\r
2241 static float drawtime_avg = 0;
\r
2242 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2243 static float beginscenetime_avg = 0;
\r
2244 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2245 static float scenetime_avg = 0;
\r
2246 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2247 static float endscenetime_avg = 0;
\r
2248 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2250 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2252 L", R: range_all=%i"
\r
2254 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2256 g_viewing_range_all,
\r
2258 beginscenetime_avg,
\r
2263 guitext->setText(temptext);
\r
2267 wchar_t temptext[150];
\r
2268 swprintf(temptext, 150,
\r
2269 L"(% .1f, % .1f, % .1f)"
\r
2270 L" (% .3f < btime_jitter < % .3f"
\r
2271 L", dtime_jitter = % .1f %%)",
\r
2272 player_position.X/BS,
\r
2273 player_position.Y/BS,
\r
2274 player_position.Z/BS,
\r
2275 busytime_jitter1_min_sample,
\r
2276 busytime_jitter1_max_sample,
\r
2277 dtime_jitter1_max_fraction * 100.0
\r
2280 guitext2->setText(temptext);
\r
2284 /*wchar_t temptext[100];
\r
2285 swprintf(temptext, 100,
\r
2286 SWPRINTF_CHARSTRING,
\r
2287 infotext.substr(0,99).c_str()
\r
2290 guitext_info->setText(temptext);*/
\r
2292 guitext_info->setText(infotext.c_str());
\r
2299 static u16 old_selected_item = 65535;
\r
2300 if(client.getLocalInventoryUpdated()
\r
2301 || g_selected_item != old_selected_item)
\r
2303 old_selected_item = g_selected_item;
\r
2304 //std::cout<<"Updating local inventory"<<std::endl;
\r
2305 client.getLocalInventory(local_inventory);
\r
2306 quick_inventory->setSelection(g_selected_item);
\r
2307 quick_inventory->update();
\r
2311 Send actions returned by the inventory menu
\r
2313 while(InventoryAction *a = inventoryMenu->getNextAction())
\r
2315 client.sendInventoryAction(a);
\r
2320 if(input_guitext != NULL)
\r
2322 /*wchar_t temptext[100];
\r
2323 swprintf(temptext, 100,
\r
2324 SWPRINTF_CHARSTRING,
\r
2325 g_text_buffer.substr(0,99).c_str()
\r
2327 input_guitext->setText(g_text_buffer.c_str());
\r
2333 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2335 input_guitext->remove();
\r
2336 input_guitext = NULL;
\r
2338 if(textbuf_dest != NULL)
\r
2340 std::string text = wide_to_narrow(g_text_buffer);
\r
2341 dstream<<"Sending text: "<<text<<std::endl;
\r
2342 textbuf_dest->sendText(text);
\r
2343 delete textbuf_dest;
\r
2344 textbuf_dest = NULL;
\r
2350 //guiupdatetimer.stop();
\r
2356 TimeTaker drawtimer("Drawing");
\r
2360 TimeTaker timer("beginScene");
\r
2361 driver->beginScene(true, true, bgcolor);
\r
2362 //driver->beginScene(false, true, bgcolor);
\r
2363 beginscenetime = timer.stop(true);
\r
2368 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2371 TimeTaker timer("smgr");
\r
2373 scenetime = timer.stop(true);
\r
2377 //TimeTaker timer9("auxiliary drawings");
\r
2380 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2381 displaycenter + core::vector2d<s32>(10,0),
\r
2382 video::SColor(255,255,255,255));
\r
2383 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2384 displaycenter + core::vector2d<s32>(0,10),
\r
2385 video::SColor(255,255,255,255));
\r
2388 //TimeTaker //timer10("//timer10");
\r
2390 video::SMaterial m;
\r
2392 m.Lighting = false;
\r
2393 driver->setMaterial(m);
\r
2395 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2397 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2398 i != hilightboxes.end(); i++)
\r
2400 /*std::cout<<"hilightbox min="
\r
2401 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2403 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2405 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2411 //TimeTaker //timer11("//timer11");
\r
2417 guienv->drawAll();
\r
2421 TimeTaker timer("endScene");
\r
2422 driver->endScene();
\r
2423 endscenetime = timer.stop(true);
\r
2426 drawtime = drawtimer.stop(true);
\r
2432 static s16 lastFPS = 0;
\r
2433 //u16 fps = driver->getFPS();
\r
2434 u16 fps = (1.0/dtime_avg1);
\r
2436 if (lastFPS != fps)
\r
2438 core::stringw str = L"Minetest [";
\r
2439 str += driver->getName();
\r
2443 device->setWindowCaption(str.c_str());
\r
2449 device->yield();*/
\r
2452 delete quick_inventory;
\r
2454 } // client is deleted at this point
\r
2459 In the end, delete the Irrlicht device.
\r
2464 Update configuration file
\r
2466 /*if(configpath != "")
\r
2468 g_settings.updateConfigFile(configpath.c_str());
\r
2472 catch(con::PeerNotFoundException &e)
\r
2474 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2476 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2478 This is what has to be done in every thread to get suitable debug info
\r
2480 catch(std::exception &e)
\r
2482 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2483 <<e.what()<<std::endl;
\r
2488 debugstreams_deinit();
\r