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: Stop player if focus of window is taken away (go to pause mode)
\r
104 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
106 - That is >500 vertices
\r
108 TODO: Better dungeons
\r
115 - One single map container with ids as keys
\r
118 TODO: - Keep track of the place of the mob in the last few hundreth's
\r
119 of a second - then, if a player hits it, take the value that is
\r
120 avg_rtt/2 before the moment the packet is received.
\r
123 TODO: Moving players more smoothly. Calculate moving animation
\r
124 in a way that doesn't make the player jump to the right place
\r
125 immediately when the server sends a new position
\r
127 TODO: There are some lighting-related todos and fixmes in
\r
128 ServerMap::emergeBlock
\r
130 TODO: Proper handling of spawning place (try to find something that
\r
131 is not in the middle of an ocean (some land to stand on at
\r
132 least) and save it in map config.
\r
134 TODO: Make the amount of blocks sending to client and the total
\r
135 amount of blocks dynamically limited. Transferring blocks is the
\r
136 main network eater of this system, so it is the one that has
\r
137 to be throttled so that RTTs stay low.
\r
139 TODO: Server to load starting inventory from disk
\r
141 TODO: Players to only be hidden when the client quits.
\r
142 TODO: - Players to be saved on disk, with inventory
\r
143 TODO: Players to be saved as text in map/players/<name>
\r
145 TODO: Make fetching sector's blocks more efficient when rendering
\r
146 sectors that have very large amounts of blocks (on client)
\r
148 TODO: Make the video backend selectable
\r
150 Block object server side:
\r
151 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
152 - For all blocks in the buffer, objects are stepped(). This
\r
153 means they are active.
\r
154 - TODO: A global active buffer is needed for the server
\r
155 - TODO: A timestamp to blocks
\r
156 - TODO: All blocks going in and out of the buffer are recorded.
\r
157 - TODO: For outgoing blocks, timestamp is written.
\r
158 - TODO: For incoming blocks, time difference is calculated and
\r
159 objects are stepped according to it.
\r
161 TODO: Add config parameters for server's sending and generating distance
\r
163 TODO: Copy the text of the last picked sign to inventory in creative
\r
166 TODO: Untie client network operations from framerate
\r
167 - Needs some input queues or something
\r
169 TODO: Get rid of GotSplitPacketException
\r
171 TODO: Check what goes wrong with caching map to disk (Kray)
\r
173 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
175 TODO: Add server unused sector deletion settings to settings
\r
177 TODO: TOSERVER_LEAVE
\r
180 ======================================================================
\r
183 ======================================================================
\r
188 Setting this to 1 enables a special camera mode that forces
\r
189 the renderers to think that the camera statically points from
\r
190 the starting place to a static direction.
\r
192 This allows one to move around with the player and see what
\r
193 is actually drawn behind solid things and behind the player.
\r
195 #define FIELD_OF_VIEW_TEST 0
\r
197 #ifdef UNITTEST_DISABLE
\r
199 #pragma message ("Disabling unit tests")
\r
201 #warning "Disabling unit tests"
\r
203 // Disable unit tests
\r
204 #define ENABLE_TESTS 0
\r
206 // Enable unit tests
\r
207 #define ENABLE_TESTS 1
\r
211 #pragma comment(lib, "Irrlicht.lib")
\r
212 #pragma comment(lib, "jthread.lib")
\r
213 #pragma comment(lib, "zlibwapi.lib")
\r
214 // This would get rid of the console window
\r
215 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
219 #define WIN32_LEAN_AND_MEAN
\r
220 #include <windows.h>
\r
221 #define sleep_ms(x) Sleep(x)
\r
223 #include <unistd.h>
\r
224 #define sleep_ms(x) usleep(x*1000)
\r
227 #include <iostream>
\r
230 #include <jmutexautolock.h>
\r
231 #include <locale.h>
\r
232 #include "common_irrlicht.h"
\r
235 #include "player.h"
\r
238 #include "environment.h"
\r
239 #include "server.h"
\r
240 #include "client.h"
\r
241 #include "serialization.h"
\r
242 #include "constants.h"
\r
243 #include "strfnd.h"
\r
244 #include "porting.h"
\r
245 #include "guiPauseMenu.h"
\r
247 IrrlichtWrapper *g_irrlicht;
\r
249 // All range-related stuff below is locked behind this
\r
250 JMutex g_range_mutex;
\r
252 // Blocks are viewed in this range from the player
\r
253 s16 g_viewing_range_nodes = 60;
\r
254 //s16 g_viewing_range_nodes = 0;
\r
256 // This is updated by the client's fetchBlocks routine
\r
257 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
259 // If true, the preceding value has no meaning and all blocks
\r
260 // already existing in memory are drawn
\r
261 bool g_viewing_range_all = false;
\r
263 // This is the freetime ratio imposed by the dynamic viewing
\r
264 // range changing code.
\r
265 // It is controlled by the main loop to the smallest value that
\r
266 // inhibits glitches (dtime jitter) in the main loop.
\r
267 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
272 These are loaded from the config file.
\r
275 Settings g_settings;
\r
277 extern void set_default_settings();
\r
283 //u16 g_selected_material = 0;
\r
284 u16 g_selected_item = 0;
\r
286 bool g_esc_pressed = false;
\r
288 std::wstring g_text_buffer;
\r
289 bool g_text_buffer_accepted = false;
\r
291 // When true, the mouse and keyboard are grabbed
\r
292 bool g_game_focused = true;
\r
299 std::ostream *dout_con_ptr = &dummyout;
\r
300 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
301 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
302 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
303 //std::ostream *dout_con_ptr = &dstream;
\r
304 //std::ostream *derr_con_ptr = &dstream;
\r
307 std::ostream *dout_server_ptr = &dstream;
\r
308 std::ostream *derr_server_ptr = &dstream;
\r
311 std::ostream *dout_client_ptr = &dstream;
\r
312 std::ostream *derr_client_ptr = &dstream;
\r
319 JMutex g_timestamp_mutex;
\r
320 //std::string g_timestamp;
\r
322 std::string getTimestamp()
\r
324 if(g_timestamp_mutex.IsInitialized()==false)
\r
326 JMutexAutoLock lock(g_timestamp_mutex);
\r
327 //return g_timestamp;
\r
328 time_t t = time(NULL);
\r
329 struct tm *tm = localtime(&t);
\r
331 strftime(cs, 20, "%H:%M:%S", tm);
\r
335 class MyEventReceiver : public IEventReceiver
\r
338 // This is the one method that we have to implement
\r
339 virtual bool OnEvent(const SEvent& event)
\r
341 // Remember whether each key is down or up
\r
342 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
344 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
346 if(event.KeyInput.PressedDown)
\r
348 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
349 if(g_game_focused == false)
\r
351 s16 key = event.KeyInput.Key;
\r
352 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
354 g_text_buffer_accepted = true;
\r
356 else if(key == irr::KEY_BACK)
\r
358 if(g_text_buffer.size() > 0)
\r
359 g_text_buffer = g_text_buffer.substr
\r
360 (0, g_text_buffer.size()-1);
\r
364 wchar_t wc = event.KeyInput.Char;
\r
366 g_text_buffer += wc;
\r
370 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
372 //TODO: Not used anymore?
\r
373 if(g_game_focused == true)
\r
375 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
376 g_esc_pressed = true;
\r
380 // Material selection
\r
381 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
383 if(g_game_focused == true)
\r
385 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
388 g_selected_item = 0;
\r
389 dstream<<DTIME<<"Selected item: "
\r
390 <<g_selected_item<<std::endl;
\r
394 // Viewing range selection
\r
395 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
398 JMutexAutoLock lock(g_range_mutex);
\r
399 if(g_viewing_range_all)
\r
401 g_viewing_range_all = false;
\r
402 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
406 g_viewing_range_all = true;
\r
407 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
411 // Print debug stacks
\r
412 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
415 dstream<<"-----------------------------------------"
\r
417 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
418 dstream<<"-----------------------------------------"
\r
420 debug_stacks_print();
\r
425 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
427 left_active = event.MouseInput.isLeftPressed();
\r
428 middle_active = event.MouseInput.isMiddlePressed();
\r
429 right_active = event.MouseInput.isRightPressed();
\r
431 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
433 leftclicked = true;
\r
435 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
437 rightclicked = true;
\r
439 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
441 leftreleased = true;
\r
443 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
445 rightreleased = true;
\r
447 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
449 /*dstream<<"event.MouseInput.Wheel="
\r
450 <<event.MouseInput.Wheel<<std::endl;*/
\r
451 if(event.MouseInput.Wheel < 0)
\r
453 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
456 g_selected_item = 0;
\r
458 else if(event.MouseInput.Wheel > 0)
\r
460 if(g_selected_item > 0)
\r
463 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
471 // This is used to check whether a key is being held down
\r
472 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
474 return keyIsDown[keyCode];
\r
479 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
480 keyIsDown[i] = false;
\r
481 leftclicked = false;
\r
482 rightclicked = false;
\r
483 leftreleased = false;
\r
484 rightreleased = false;
\r
486 left_active = false;
\r
487 middle_active = false;
\r
488 right_active = false;
\r
494 bool rightreleased;
\r
497 bool middle_active;
\r
501 // We use this array to store the current state of each key
\r
502 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
513 virtual ~InputHandler()
\r
517 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
519 virtual v2s32 getMousePos() = 0;
\r
520 virtual void setMousePos(s32 x, s32 y) = 0;
\r
522 virtual bool getLeftState() = 0;
\r
523 virtual bool getRightState() = 0;
\r
525 virtual bool getLeftClicked() = 0;
\r
526 virtual bool getRightClicked() = 0;
\r
527 virtual void resetLeftClicked() = 0;
\r
528 virtual void resetRightClicked() = 0;
\r
530 virtual bool getLeftReleased() = 0;
\r
531 virtual bool getRightReleased() = 0;
\r
532 virtual void resetLeftReleased() = 0;
\r
533 virtual void resetRightReleased() = 0;
\r
535 virtual void step(float dtime) {};
\r
537 virtual void clear() {};
\r
540 InputHandler *g_input = NULL;
\r
545 g_game_focused = true;
\r
550 g_game_focused = false;
\r
553 class RealInputHandler : public InputHandler
\r
556 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
558 m_receiver(receiver)
\r
561 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
563 return m_receiver->IsKeyDown(keyCode);
\r
565 virtual v2s32 getMousePos()
\r
567 return m_device->getCursorControl()->getPosition();
\r
569 virtual void setMousePos(s32 x, s32 y)
\r
571 m_device->getCursorControl()->setPosition(x, y);
\r
574 virtual bool getLeftState()
\r
576 return m_receiver->left_active;
\r
578 virtual bool getRightState()
\r
580 return m_receiver->right_active;
\r
583 virtual bool getLeftClicked()
\r
585 if(g_game_focused == false)
\r
587 return m_receiver->leftclicked;
\r
589 virtual bool getRightClicked()
\r
591 if(g_game_focused == false)
\r
593 return m_receiver->rightclicked;
\r
595 virtual void resetLeftClicked()
\r
597 m_receiver->leftclicked = false;
\r
599 virtual void resetRightClicked()
\r
601 m_receiver->rightclicked = false;
\r
604 virtual bool getLeftReleased()
\r
606 if(g_game_focused == false)
\r
608 return m_receiver->leftreleased;
\r
610 virtual bool getRightReleased()
\r
612 if(g_game_focused == false)
\r
614 return m_receiver->rightreleased;
\r
616 virtual void resetLeftReleased()
\r
618 m_receiver->leftreleased = false;
\r
620 virtual void resetRightReleased()
\r
622 m_receiver->rightreleased = false;
\r
627 resetRightClicked();
\r
628 resetLeftClicked();
\r
631 IrrlichtDevice *m_device;
\r
632 MyEventReceiver *m_receiver;
\r
635 class RandomInputHandler : public InputHandler
\r
638 RandomInputHandler()
\r
640 leftclicked = false;
\r
641 rightclicked = false;
\r
642 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
643 keydown[i] = false;
\r
645 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
647 return keydown[keyCode];
\r
649 virtual v2s32 getMousePos()
\r
653 virtual void setMousePos(s32 x, s32 y)
\r
655 mousepos = v2s32(x,y);
\r
658 virtual bool getLeftState()
\r
662 virtual bool getRightState()
\r
667 virtual bool getLeftClicked()
\r
669 return leftclicked;
\r
671 virtual bool getRightClicked()
\r
673 return rightclicked;
\r
675 virtual void resetLeftClicked()
\r
677 leftclicked = false;
\r
679 virtual void resetRightClicked()
\r
681 rightclicked = false;
\r
684 virtual bool getLeftReleased()
\r
688 virtual bool getRightReleased()
\r
692 virtual void resetLeftReleased()
\r
695 virtual void resetRightReleased()
\r
699 virtual void step(float dtime)
\r
702 static float counter1 = 0;
\r
706 counter1 = 0.1*Rand(1,10);
\r
707 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
708 g_selected_material++;
\r
710 g_selected_material = 0;*/
\r
711 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
714 g_selected_item = 0;
\r
718 static float counter1 = 0;
\r
722 counter1 = 0.1*Rand(1, 40);
\r
723 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
727 static float counter1 = 0;
\r
731 counter1 = 0.1*Rand(1, 40);
\r
732 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
736 static float counter1 = 0;
\r
740 counter1 = 0.1*Rand(1, 40);
\r
741 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
745 static float counter1 = 0;
\r
749 counter1 = 0.1*Rand(1, 40);
\r
750 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
754 static float counter1 = 0;
\r
758 counter1 = 0.1*Rand(1, 20);
\r
759 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
763 static float counter1 = 0;
\r
767 counter1 = 0.1*Rand(1, 30);
\r
768 leftclicked = true;
\r
772 static float counter1 = 0;
\r
776 counter1 = 0.1*Rand(1, 20);
\r
777 rightclicked = true;
\r
780 mousepos += mousespeed;
\r
783 s32 Rand(s32 min, s32 max)
\r
785 return (rand()%(max-min+1))+min;
\r
788 bool keydown[KEY_KEY_CODES_COUNT];
\r
795 void updateViewingRange(f32 frametime, Client *client)
\r
797 // Range_all messes up frametime_avg
\r
798 if(g_viewing_range_all == true)
\r
801 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
803 // Initialize to the target value
\r
804 static float frametime_avg = 1.0/wanted_fps;
\r
805 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
806 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
808 static f32 counter = 0;
\r
810 counter -= frametime;
\r
813 //counter = 1.0; //seconds
\r
814 counter = 0.5; //seconds
\r
816 //float freetime_ratio = 0.2;
\r
817 //float freetime_ratio = 0.4;
\r
818 float freetime_ratio = FREETIME_RATIO;
\r
820 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
822 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
824 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
825 + frametime_avg / frametime_wanted / 2.0;*/
\r
827 //float fraction = frametime_avg / frametime_wanted;
\r
829 static bool fraction_is_good = false;
\r
831 //float fraction_good_threshold = 0.1;
\r
832 //float fraction_bad_threshold = 0.25;
\r
833 float fraction_good_threshold = 0.075;
\r
834 float fraction_bad_threshold = 0.125;
\r
835 float fraction_limit;
\r
836 // Use high limit if fraction is good AND the fraction would
\r
837 // lower the range. We want to keep the range fairly high.
\r
838 if(fraction_is_good && fraction > 1.0)
\r
839 fraction_limit = fraction_bad_threshold;
\r
841 fraction_limit = fraction_good_threshold;
\r
843 if(fabs(fraction - 1.0) < fraction_limit)
\r
845 fraction_is_good = true;
\r
850 fraction_is_good = false;
\r
853 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
854 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
855 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
856 <<" faction = "<<fraction<<std::endl;*/
\r
858 JMutexAutoLock lock(g_range_mutex);
\r
860 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
861 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
863 s16 n = (float)g_viewing_range_nodes / fraction;
\r
864 if(n < viewing_range_nodes_min)
\r
865 n = viewing_range_nodes_min;
\r
866 if(n > viewing_range_nodes_max)
\r
867 n = viewing_range_nodes_max;
\r
869 bool can_change = true;
\r
871 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
872 can_change = false;
\r
875 g_viewing_range_nodes = n;
\r
877 /*dstream<<"g_viewing_range_nodes = "
\r
878 <<g_viewing_range_nodes<<std::endl;*/
\r
881 class GUIQuickInventory : public IEventReceiver
\r
885 gui::IGUIEnvironment* env,
\r
886 gui::IGUIElement* parent,
\r
889 Inventory *inventory):
\r
890 m_itemcount(itemcount),
\r
891 m_inventory(inventory)
\r
893 core::rect<s32> imgsize(0,0,48,48);
\r
894 core::rect<s32> textsize(0,0,48,16);
\r
895 v2s32 spacing(0, 64);
\r
896 for(s32 i=0; i<m_itemcount; i++)
\r
898 m_images.push_back(env->addImage(
\r
899 imgsize + pos + spacing*i
\r
901 m_images[i]->setScaleImage(true);
\r
902 m_texts.push_back(env->addStaticText(
\r
904 textsize + pos + spacing*i,
\r
907 m_texts[i]->setBackgroundColor(
\r
908 video::SColor(128,0,0,0));
\r
909 m_texts[i]->setTextAlignment(
\r
911 gui::EGUIA_UPPERLEFT);
\r
915 virtual bool OnEvent(const SEvent& event)
\r
920 void setSelection(s32 i)
\r
929 start = m_selection - m_itemcount / 2;
\r
931 for(s32 i=0; i<m_itemcount; i++)
\r
935 if(j > (s32)m_inventory->getSize() - 1)
\r
936 j -= m_inventory->getSize();
\r
938 j += m_inventory->getSize();
\r
940 InventoryItem *item = m_inventory->getItem(j);
\r
944 m_images[i]->setImage(NULL);
\r
947 if(m_selection == j)
\r
948 swprintf(t, 10, L"<-");
\r
950 swprintf(t, 10, L"");
\r
951 m_texts[i]->setText(t);
\r
953 // The next ifs will segfault with a NULL pointer
\r
958 m_images[i]->setImage(item->getImage());
\r
961 if(m_selection == j)
\r
962 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
964 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
965 m_texts[i]->setText(t);
\r
971 core::array<gui::IGUIStaticText*> m_texts;
\r
972 core::array<gui::IGUIImage*> m_images;
\r
973 Inventory *m_inventory;
\r
977 int main(int argc, char *argv[])
\r
980 Low-level initialization
\r
983 bool disable_stderr = false;
\r
985 disable_stderr = true;
\r
988 // Initialize debug streams
\r
989 debugstreams_init(disable_stderr, DEBUGFILE);
\r
990 // Initialize debug stacks
\r
991 debug_stacks_init();
\r
993 DSTACK(__FUNCTION_NAME);
\r
1002 // List all allowed options
\r
1003 core::map<std::string, ValueSpec> allowed_options;
\r
1004 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1005 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1006 "Run server directly"));
\r
1007 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1008 "Load configuration from specified file"));
\r
1009 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1010 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1011 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1012 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1013 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1015 Settings cmd_args;
\r
1017 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1019 if(ret == false || cmd_args.getFlag("help"))
\r
1021 dstream<<"Allowed options:"<<std::endl;
\r
1022 for(core::map<std::string, ValueSpec>::Iterator
\r
1023 i = allowed_options.getIterator();
\r
1024 i.atEnd() == false; i++)
\r
1026 dstream<<" --"<<i.getNode()->getKey();
\r
1027 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1032 dstream<<" <value>";
\r
1034 dstream<<std::endl;
\r
1036 if(i.getNode()->getValue().help != NULL)
\r
1038 dstream<<" "<<i.getNode()->getValue().help
\r
1043 return cmd_args.getFlag("help") ? 0 : 1;
\r
1048 Basic initialization
\r
1051 // Initialize default settings
\r
1052 set_default_settings();
\r
1054 // Print startup message
\r
1055 dstream<<DTIME<<"minetest-c55"
\r
1056 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1057 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1060 // Set locale. This is for forcing '.' as the decimal point.
\r
1061 std::locale::global(std::locale("C"));
\r
1062 // This enables printing all characters in bitmap font
\r
1063 setlocale(LC_CTYPE, "en_US");
\r
1065 // Initialize sockets
\r
1067 atexit(sockets_cleanup);
\r
1069 // Initialize timestamp mutex
\r
1070 g_timestamp_mutex.Init();
\r
1080 // Path of configuration file in use
\r
1081 std::string configpath = "";
\r
1083 if(cmd_args.exists("config"))
\r
1085 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1088 dstream<<"Could not read configuration from \""
\r
1089 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1092 configpath = cmd_args.get("config");
\r
1096 const char *filenames[2] =
\r
1098 "../minetest.conf",
\r
1099 "../../minetest.conf"
\r
1102 for(u32 i=0; i<2; i++)
\r
1104 bool r = g_settings.readConfigFile(filenames[i]);
\r
1107 configpath = filenames[i];
\r
1113 // Initialize random seed
\r
1119 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1120 || cmd_args.getFlag("enable-unittests") == true)
\r
1126 Global range mutex
\r
1128 g_range_mutex.Init();
\r
1129 assert(g_range_mutex.IsInitialized());
\r
1131 // Read map parameters from settings
\r
1133 HMParams hm_params;
\r
1134 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1135 hm_params.randmax = g_settings.get("height_randmax");
\r
1136 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1137 hm_params.base = g_settings.get("height_base");
\r
1139 MapParams map_params;
\r
1140 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1141 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1147 std::cout<<std::endl<<std::endl;
\r
1150 <<" .__ __ __ "<<std::endl
\r
1151 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1152 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1153 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1154 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1155 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1157 <<"Now with more waterish water!"
\r
1160 std::cout<<std::endl;
\r
1161 //char templine[100];
\r
1165 if(cmd_args.exists("port"))
\r
1167 port = cmd_args.getU16("port");
\r
1171 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1172 std::cout<<"-> "<<port<<std::endl;
\r
1175 if(cmd_args.getFlag("server"))
\r
1177 DSTACK("Dedicated server branch");
\r
1179 std::cout<<std::endl;
\r
1180 std::cout<<"========================"<<std::endl;
\r
1181 std::cout<<"Running dedicated server"<<std::endl;
\r
1182 std::cout<<"========================"<<std::endl;
\r
1183 std::cout<<std::endl;
\r
1185 Server server("../map", hm_params, map_params);
\r
1186 server.start(port);
\r
1190 // This is kind of a hack but can be done like this
\r
1191 // because server.step() is very light
\r
1193 server.step(0.030);
\r
1195 static int counter = 0;
\r
1201 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1202 core::list<PlayerInfo>::Iterator i;
\r
1203 static u32 sum_old = 0;
\r
1204 u32 sum = PIChecksum(list);
\r
1205 if(sum != sum_old)
\r
1207 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1208 for(i=list.begin(); i!=list.end(); i++)
\r
1210 i->PrintLine(&std::cout);
\r
1220 bool hosting = false;
\r
1221 char connect_name[100] = "";
\r
1223 if(cmd_args.exists("address"))
\r
1225 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1227 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1229 std::cout<<g_settings.get("address")<<std::endl;
\r
1230 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1234 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1235 std::cin.getline(connect_name, 100);
\r
1238 if(connect_name[0] == 0){
\r
1239 snprintf(connect_name, 100, "127.0.0.1");
\r
1244 std::cout<<"> Hosting game"<<std::endl;
\r
1246 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1248 char playername[PLAYERNAME_SIZE] = "";
\r
1249 if(g_settings.get("name") != "")
\r
1251 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1255 std::cout<<"Name of player: ";
\r
1256 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1258 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1261 Resolution selection
\r
1264 bool fullscreen = false;
\r
1265 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1266 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1270 MyEventReceiver receiver;
\r
1272 video::E_DRIVER_TYPE driverType;
\r
1275 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1276 driverType = video::EDT_OPENGL;
\r
1278 driverType = video::EDT_OPENGL;
\r
1281 // create device and exit if creation failed
\r
1283 IrrlichtDevice *device;
\r
1284 device = createDevice(driverType,
\r
1285 core::dimension2d<u32>(screenW, screenH),
\r
1286 16, fullscreen, false, false, &receiver);
\r
1289 return 1; // could not create selected driver.
\r
1291 g_irrlicht = new IrrlichtWrapper(device);
\r
1293 //g_device = device;
\r
1295 device->setResizable(true);
\r
1297 bool random_input = g_settings.getBool("random_input")
\r
1298 || cmd_args.getFlag("random-input");
\r
1300 g_input = new RandomInputHandler();
\r
1302 g_input = new RealInputHandler(device, &receiver);
\r
1305 Continue initialization
\r
1308 video::IVideoDriver* driver = device->getVideoDriver();
\r
1311 This changes the minimum allowed number of vertices in a VBO
\r
1313 //driver->setMinHardwareBufferVertexCount(1);
\r
1315 scene::ISceneManager* smgr = device->getSceneManager();
\r
1318 guiPauseMenu pauseMenu(device, &receiver);
\r
1320 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1321 gui::IGUISkin* skin = guienv->getSkin();
\r
1322 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1324 skin->setFont(font);
\r
1325 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1326 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1327 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1328 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1329 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1330 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1332 const wchar_t *text = L"Loading and connecting...";
\r
1333 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1334 core::dimension2d<u32> textd = font->getDimension(text);
\r
1335 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1336 // Have to add a bit to disable the text from word wrapping
\r
1337 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1338 core::vector2d<s32> textsize(300, textd.Height);
\r
1339 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1341 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1342 text, textrect, false, false);
\r
1343 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1345 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1346 guienv->drawAll();
\r
1347 driver->endScene();
\r
1350 Preload some random textures that are used in threads
\r
1353 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1354 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1355 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1356 g_texturecache.set("crack", driver->getTexture("../data/crack.png"));
\r
1359 Load tile textures
\r
1361 for(s32 i=0; i<TILES_COUNT; i++)
\r
1363 if(g_tile_texture_names[i] == NULL)
\r
1365 std::string name = g_tile_texture_names[i];
\r
1366 std::string filename;
\r
1367 filename += "../data/";
\r
1369 filename += ".png";
\r
1370 g_texturecache.set(name, driver->getTexture(filename.c_str()));
\r
1375 //tile_materials_preload(g_texturecache);
\r
1376 tile_materials_preload(g_irrlicht);
\r
1377 //tile_materials_init();
\r
1380 Make a scope here for the client so that it gets removed
\r
1381 before the irrlicht device
\r
1385 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1390 SharedPtr<Server> server;
\r
1392 server = new Server("../map", hm_params, map_params);
\r
1393 server->start(port);
\r
1400 Client client(device, playername,
\r
1402 g_viewing_range_nodes,
\r
1403 g_viewing_range_all);
\r
1405 Address connect_address(0,0,0,0, port);
\r
1407 connect_address.Resolve(connect_name);
\r
1409 catch(ResolveError &e)
\r
1411 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1415 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1416 client.connect(connect_address);
\r
1419 while(client.connectedAndInitialized() == false)
\r
1422 if(server != NULL){
\r
1423 server->step(0.1);
\r
1428 catch(con::PeerNotFoundException &e)
\r
1430 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1437 /*scene::ISceneNode* skybox;
\r
1438 skybox = smgr->addSkyBoxSceneNode(
\r
1439 driver->getTexture("../data/skybox2.png"),
\r
1440 driver->getTexture("../data/skybox3.png"),
\r
1441 driver->getTexture("../data/skybox1.png"),
\r
1442 driver->getTexture("../data/skybox1.png"),
\r
1443 driver->getTexture("../data/skybox1.png"),
\r
1444 driver->getTexture("../data/skybox1.png"));*/
\r
1447 Create the camera node
\r
1450 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1451 0, // Camera parent
\r
1452 v3f(BS*100, BS*2, BS*100), // Look from
\r
1453 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1457 if(camera == NULL)
\r
1460 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1462 camera->setFOV(FOV_ANGLE);
\r
1464 // Just so big a value that everything rendered is visible
\r
1465 camera->setFarValue(100000*BS);
\r
1467 f32 camera_yaw = 0; // "right/left"
\r
1468 f32 camera_pitch = 0; // "up/down"
\r
1474 gui_loadingtext->remove();
\r
1476 pauseMenu.setVisible(true);
\r
1479 Add some gui stuff
\r
1482 // First line of debug text
\r
1483 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1485 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1487 // Second line of debug text
\r
1488 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1490 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1493 // At the middle of the screen
\r
1494 // Object infos are shown in this
\r
1495 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1497 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1500 // This is a copy of the inventory that the client's environment has
\r
1501 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1503 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1504 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1507 Some statistics are collected in these
\r
1510 u32 beginscenetime = 0;
\r
1511 u32 scenetime = 0;
\r
1512 u32 endscenetime = 0;
\r
1520 virtual void sendText(std::string text) = 0;
\r
1523 struct TextDestSign : public TextDest
\r
1525 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1527 m_blockpos = blockpos;
\r
1529 m_client = client;
\r
1531 void sendText(std::string text)
\r
1533 dstream<<"Changing text of a sign object: "
\r
1534 <<text<<std::endl;
\r
1535 m_client->sendSignText(m_blockpos, m_id, text);
\r
1543 TextDest *textbuf_dest = NULL;
\r
1545 //gui::IGUIWindow* input_window = NULL;
\r
1546 gui::IGUIStaticText* input_guitext = NULL;
\r
1557 bool first_loop_after_window_activation = true;
\r
1559 // Time is in milliseconds
\r
1560 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1561 // NOTE: Have to call run() between calls of this to update the timer
\r
1562 u32 lasttime = device->getTimer()->getTime();
\r
1564 while(device->run())
\r
1567 Run global IrrlichtWrapper's main thread processing stuff
\r
1569 g_irrlicht->Run();
\r
1572 Random calculations
\r
1574 v2u32 screensize = driver->getScreenSize();
\r
1575 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1577 // Hilight boxes collected during the loop and displayed
\r
1578 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1581 std::wstring infotext;
\r
1583 //TimeTaker //timer1("//timer1", g_irrlicht);
\r
1585 // Time of frame without fps limit
\r
1589 // not using getRealTime is necessary for wine
\r
1590 u32 time = device->getTimer()->getTime();
\r
1591 if(time > lasttime)
\r
1592 busytime_u32 = time - lasttime;
\r
1595 busytime = busytime_u32 / 1000.0;
\r
1598 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1600 // Absolutelu necessary for wine!
\r
1607 //updateViewingRange(dtime, &client);
\r
1608 updateViewingRange(busytime, &client);
\r
1615 float fps_max = g_settings.getFloat("fps_max");
\r
1616 u32 frametime_min = 1000./fps_max;
\r
1618 if(busytime_u32 < frametime_min)
\r
1620 u32 sleeptime = frametime_min - busytime_u32;
\r
1621 device->sleep(sleeptime);
\r
1625 // Absolutelu necessary for wine!
\r
1629 Time difference calculation
\r
1631 f32 dtime; // in seconds
\r
1633 u32 time = device->getTimer()->getTime();
\r
1634 if(time > lasttime)
\r
1635 dtime = (time - lasttime) / 1000.0;
\r
1641 Time average and jitter calculation
\r
1644 static f32 dtime_avg1 = 0.0;
\r
1645 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1646 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1648 static f32 dtime_jitter1_max_sample = 0.0;
\r
1649 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1651 static f32 jitter1_max = 0.0;
\r
1652 static f32 counter = 0.0;
\r
1653 if(dtime_jitter1 > jitter1_max)
\r
1654 jitter1_max = dtime_jitter1;
\r
1659 dtime_jitter1_max_sample = jitter1_max;
\r
1660 dtime_jitter1_max_fraction
\r
1661 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1662 jitter1_max = 0.0;
\r
1665 Control freetime ratio
\r
1667 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1669 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1670 g_freetime_ratio += 0.01;
\r
1674 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1675 g_freetime_ratio -= 0.01;
\r
1681 Busytime average and jitter calculation
\r
1684 static f32 busytime_avg1 = 0.0;
\r
1685 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1686 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1688 static f32 busytime_jitter1_max_sample = 0.0;
\r
1689 static f32 busytime_jitter1_min_sample = 0.0;
\r
1691 static f32 jitter1_max = 0.0;
\r
1692 static f32 jitter1_min = 0.0;
\r
1693 static f32 counter = 0.0;
\r
1694 if(busytime_jitter1 > jitter1_max)
\r
1695 jitter1_max = busytime_jitter1;
\r
1696 if(busytime_jitter1 < jitter1_min)
\r
1697 jitter1_min = busytime_jitter1;
\r
1699 if(counter > 0.0){
\r
1701 busytime_jitter1_max_sample = jitter1_max;
\r
1702 busytime_jitter1_min_sample = jitter1_min;
\r
1703 jitter1_max = 0.0;
\r
1704 jitter1_min = 0.0;
\r
1709 Debug info for client
\r
1712 static float counter = 0.0;
\r
1717 client.printDebugInfo(std::cout);
\r
1722 Input handler step()
\r
1724 g_input->step(dtime);
\r
1729 /*if(g_esc_pressed)
\r
1735 Player speed control
\r
1738 if(g_game_focused)
\r
1745 bool a_superspeed,
\r
1748 PlayerControl control(
\r
1749 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1750 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1751 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1752 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1753 g_input->isKeyDown(irr::KEY_SPACE),
\r
1754 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1758 client.setPlayerControl(control);
\r
1762 // Set every key to inactive
\r
1763 PlayerControl control;
\r
1764 client.setPlayerControl(control);
\r
1769 Process environment
\r
1773 //TimeTaker timer("client.step(dtime)", g_irrlicht);
\r
1774 client.step(dtime);
\r
1775 //client.step(dtime_avg1);
\r
1778 if(server != NULL)
\r
1780 //TimeTaker timer("server->step(dtime)", g_irrlicht);
\r
1781 server->step(dtime);
\r
1784 v3f player_position = client.getPlayerPosition();
\r
1786 //TimeTaker //timer2("//timer2", g_irrlicht);
\r
1789 Mouse and camera control
\r
1792 if((device->isWindowActive() && g_game_focused && !pauseMenu.isVisible())
\r
1796 device->getCursorControl()->setVisible(false);
\r
1798 if(first_loop_after_window_activation){
\r
1799 //std::cout<<"window active, first loop"<<std::endl;
\r
1800 first_loop_after_window_activation = false;
\r
1803 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1804 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1805 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1806 camera_yaw -= dx*0.2;
\r
1807 camera_pitch += dy*0.2;
\r
1808 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1809 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1811 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1814 device->getCursorControl()->setVisible(true);
\r
1816 //std::cout<<"window inactive"<<std::endl;
\r
1817 first_loop_after_window_activation = true;
\r
1820 camera_yaw = wrapDegrees(camera_yaw);
\r
1821 camera_pitch = wrapDegrees(camera_pitch);
\r
1823 v3f camera_direction = v3f(0,0,1);
\r
1824 camera_direction.rotateYZBy(camera_pitch);
\r
1825 camera_direction.rotateXZBy(camera_yaw);
\r
1827 v3f camera_position =
\r
1828 player_position + v3f(0, BS+BS/2, 0);
\r
1830 camera->setPosition(camera_position);
\r
1831 // *100.0 helps in large map coordinates
\r
1832 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1834 if(FIELD_OF_VIEW_TEST){
\r
1835 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1836 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1839 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1840 //TimeTaker timer("client.updateCamera", g_irrlicht);
\r
1841 client.updateCamera(camera_position, camera_direction);
\r
1845 //TimeTaker //timer3("//timer3", g_irrlicht);
\r
1848 Calculate what block is the crosshair pointing to
\r
1851 //u32 t1 = device->getTimer()->getRealTime();
\r
1853 //f32 d = 4; // max. distance
\r
1854 f32 d = 4; // max. distance
\r
1855 core::line3d<f32> shootline(camera_position,
\r
1856 camera_position + camera_direction * BS * (d+1));
\r
1858 MapBlockObject *selected_object = client.getSelectedObject
\r
1859 (d*BS, camera_position, shootline);
\r
1861 if(selected_object != NULL)
\r
1863 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1865 core::aabbox3d<f32> box_on_map
\r
1866 = selected_object->getSelectionBoxOnMap();
\r
1868 hilightboxes.push_back(box_on_map);
\r
1870 infotext = narrow_to_wide(selected_object->infoText());
\r
1872 if(g_input->getLeftClicked())
\r
1874 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1875 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1876 selected_object->getId(), g_selected_item);
\r
1878 else if(g_input->getRightClicked())
\r
1880 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1882 Check if we want to modify the object ourselves
\r
1884 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1886 dstream<<"Sign object right-clicked"<<std::endl;
\r
1890 input_guitext = guienv->addStaticText(L"",
\r
1891 core::rect<s32>(150,100,350,120),
\r
1893 false, // wordwrap?
\r
1896 input_guitext->setDrawBackground(true);
\r
1900 g_text_buffer = L"ASD LOL 8)";
\r
1901 g_text_buffer_accepted = true;
\r
1905 g_text_buffer = L"";
\r
1906 g_text_buffer_accepted = false;
\r
1909 textbuf_dest = new TextDestSign(
\r
1910 selected_object->getBlock()->getPos(),
\r
1911 selected_object->getId(),
\r
1915 Otherwise pass the event to the server as-is
\r
1919 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1920 selected_object->getId(), g_selected_item);
\r
1924 else // selected_object == NULL
\r
1928 Find out which node we are pointing at
\r
1931 bool nodefound = false;
\r
1933 v3s16 neighbourpos;
\r
1934 core::aabbox3d<f32> nodefacebox;
\r
1935 f32 mindistance = BS * 1001;
\r
1937 v3s16 pos_i = floatToInt(player_position);
\r
1939 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1943 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1944 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1945 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1946 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1947 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1948 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1950 for(s16 y = ystart; y <= yend; y++)
\r
1951 for(s16 z = zstart; z <= zend; z++)
\r
1952 for(s16 x = xstart; x <= xend; x++)
\r
1957 n = client.getNode(v3s16(x,y,z));
\r
1958 if(content_pointable(n.d) == false)
\r
1961 catch(InvalidPositionException &e)
\r
1967 v3f npf = intToFloat(np);
\r
1972 v3s16(0,0,1), // back
\r
1973 v3s16(0,1,0), // top
\r
1974 v3s16(1,0,0), // right
\r
1975 v3s16(0,0,-1), // front
\r
1976 v3s16(0,-1,0), // bottom
\r
1977 v3s16(-1,0,0), // left
\r
1983 if(n.d == CONTENT_TORCH)
\r
1985 v3s16 dir = unpackDir(n.dir);
\r
1986 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1987 dir_f *= BS/2 - BS/6 - BS/20;
\r
1988 v3f cpf = npf + dir_f;
\r
1989 f32 distance = (cpf - camera_position).getLength();
\r
1991 core::aabbox3d<f32> box;
\r
1994 if(dir == v3s16(0,-1,0))
\r
1996 box = core::aabbox3d<f32>(
\r
1997 npf - v3f(BS/6, BS/2, BS/6),
\r
1998 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2002 else if(dir == v3s16(0,1,0))
\r
2004 box = core::aabbox3d<f32>(
\r
2005 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2006 npf + v3f(BS/6, BS/2, BS/6)
\r
2012 box = core::aabbox3d<f32>(
\r
2013 cpf - v3f(BS/6, BS/3, BS/6),
\r
2014 cpf + v3f(BS/6, BS/3, BS/6)
\r
2018 if(distance < mindistance)
\r
2020 if(box.intersectsWithLine(shootline))
\r
2024 neighbourpos = np;
\r
2025 mindistance = distance;
\r
2026 nodefacebox = box;
\r
2035 for(u16 i=0; i<6; i++)
\r
2037 v3f dir_f = v3f(dirs[i].X,
\r
2038 dirs[i].Y, dirs[i].Z);
\r
2039 v3f centerpoint = npf + dir_f * BS/2;
\r
2041 (centerpoint - camera_position).getLength();
\r
2043 if(distance < mindistance)
\r
2045 core::CMatrix4<f32> m;
\r
2046 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2048 // This is the back face
\r
2049 v3f corners[2] = {
\r
2050 v3f(BS/2, BS/2, BS/2),
\r
2051 v3f(-BS/2, -BS/2, BS/2+d)
\r
2054 for(u16 j=0; j<2; j++)
\r
2056 m.rotateVect(corners[j]);
\r
2057 corners[j] += npf;
\r
2060 core::aabbox3d<f32> facebox(corners[0]);
\r
2061 facebox.addInternalPoint(corners[1]);
\r
2063 if(facebox.intersectsWithLine(shootline))
\r
2067 neighbourpos = np + dirs[i];
\r
2068 mindistance = distance;
\r
2069 nodefacebox = facebox;
\r
2071 } // if distance < mindistance
\r
2073 } // regular block
\r
2078 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2080 static float dig_time = 0.0;
\r
2082 if(nodepos != nodepos_old)
\r
2084 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2085 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2087 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2089 client.clearTempMod(nodepos_old);
\r
2094 hilightboxes.push_back(nodefacebox);
\r
2096 if(g_input->getLeftReleased())
\r
2098 client.clearTempMod(nodepos);
\r
2101 if(g_input->getLeftClicked() ||
\r
2102 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2104 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2105 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2107 if(g_input->getLeftClicked())
\r
2109 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2111 if(g_input->getLeftState())
\r
2113 dig_time += dtime;
\r
2115 float dig_time_complete = 0.5;
\r
2116 MapNode n = client.getNode(nodepos);
\r
2117 if(n.d == CONTENT_STONE)
\r
2118 dig_time_complete = 1.5;
\r
2120 u16 dig_index = (u16)(3.99*dig_time/dig_time_complete);
\r
2121 if(dig_time > 0.2)
\r
2123 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2124 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2128 if(g_input->getRightClicked())
\r
2130 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2131 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2134 nodepos_old = nodepos;
\r
2139 } // selected_object == NULL
\r
2141 g_input->resetLeftClicked();
\r
2142 g_input->resetRightClicked();
\r
2144 if(g_input->getLeftReleased())
\r
2146 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2147 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2149 if(g_input->getRightReleased())
\r
2151 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2155 g_input->resetLeftReleased();
\r
2156 g_input->resetRightReleased();
\r
2159 Calculate stuff for drawing
\r
2162 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2164 u32 daynight_ratio = client.getDayNightRatio();
\r
2165 video::SColor bgcolor = video::SColor(
\r
2167 skycolor.getRed() * daynight_ratio / 1000,
\r
2168 skycolor.getGreen() * daynight_ratio / 1000,
\r
2169 skycolor.getBlue() * daynight_ratio / 1000);
\r
2175 if(g_settings.getBool("enable_fog") == true)
\r
2177 f32 range = g_viewing_range_nodes * BS;
\r
2178 if(g_viewing_range_all)
\r
2179 range = 100000*BS;
\r
2183 video::EFT_FOG_LINEAR,
\r
2187 false, // pixel fog
\r
2188 false // range fog
\r
2194 Update gui stuff (0ms)
\r
2197 //TimeTaker guiupdatetimer("Gui updating", g_irrlicht);
\r
2200 wchar_t temptext[150];
\r
2202 static float drawtime_avg = 0;
\r
2203 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2204 static float beginscenetime_avg = 0;
\r
2205 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2206 static float scenetime_avg = 0;
\r
2207 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2208 static float endscenetime_avg = 0;
\r
2209 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2211 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2213 L", R: range_all=%i"
\r
2215 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2217 g_viewing_range_all,
\r
2219 beginscenetime_avg,
\r
2224 guitext->setText(temptext);
\r
2228 wchar_t temptext[150];
\r
2229 swprintf(temptext, 150,
\r
2230 L"(% .1f, % .1f, % .1f)"
\r
2231 L" (% .3f < btime_jitter < % .3f"
\r
2232 L", dtime_jitter = % .1f %%)",
\r
2233 player_position.X/BS,
\r
2234 player_position.Y/BS,
\r
2235 player_position.Z/BS,
\r
2236 busytime_jitter1_min_sample,
\r
2237 busytime_jitter1_max_sample,
\r
2238 dtime_jitter1_max_fraction * 100.0
\r
2241 guitext2->setText(temptext);
\r
2245 /*wchar_t temptext[100];
\r
2246 swprintf(temptext, 100,
\r
2247 SWPRINTF_CHARSTRING,
\r
2248 infotext.substr(0,99).c_str()
\r
2251 guitext_info->setText(temptext);*/
\r
2253 guitext_info->setText(infotext.c_str());
\r
2260 static u16 old_selected_item = 65535;
\r
2261 if(client.getLocalInventoryUpdated()
\r
2262 || g_selected_item != old_selected_item)
\r
2264 old_selected_item = g_selected_item;
\r
2265 //std::cout<<"Updating local inventory"<<std::endl;
\r
2266 client.getLocalInventory(local_inventory);
\r
2267 quick_inventory->setSelection(g_selected_item);
\r
2268 quick_inventory->update();
\r
2271 if(input_guitext != NULL)
\r
2273 /*wchar_t temptext[100];
\r
2274 swprintf(temptext, 100,
\r
2275 SWPRINTF_CHARSTRING,
\r
2276 g_text_buffer.substr(0,99).c_str()
\r
2278 input_guitext->setText(g_text_buffer.c_str());
\r
2284 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2286 input_guitext->remove();
\r
2287 input_guitext = NULL;
\r
2289 if(textbuf_dest != NULL)
\r
2291 std::string text = wide_to_narrow(g_text_buffer);
\r
2292 dstream<<"Sending text: "<<text<<std::endl;
\r
2293 textbuf_dest->sendText(text);
\r
2294 delete textbuf_dest;
\r
2295 textbuf_dest = NULL;
\r
2301 //guiupdatetimer.stop();
\r
2307 TimeTaker drawtimer("Drawing", g_irrlicht);
\r
2311 TimeTaker timer("beginScene", g_irrlicht);
\r
2312 driver->beginScene(true, true, bgcolor);
\r
2313 //driver->beginScene(false, true, bgcolor);
\r
2314 beginscenetime = timer.stop(true);
\r
2319 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2322 TimeTaker timer("smgr", g_irrlicht);
\r
2324 scenetime = timer.stop(true);
\r
2328 //TimeTaker timer9("auxiliary drawings", g_irrlicht);
\r
2331 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2332 displaycenter + core::vector2d<s32>(10,0),
\r
2333 video::SColor(255,255,255,255));
\r
2334 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2335 displaycenter + core::vector2d<s32>(0,10),
\r
2336 video::SColor(255,255,255,255));
\r
2339 //TimeTaker //timer10("//timer10", g_irrlicht);
\r
2341 video::SMaterial m;
\r
2343 m.Lighting = false;
\r
2344 driver->setMaterial(m);
\r
2346 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2348 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2349 i != hilightboxes.end(); i++)
\r
2351 /*std::cout<<"hilightbox min="
\r
2352 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2354 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2356 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2362 //TimeTaker //timer11("//timer11", g_irrlicht);
\r
2368 guienv->drawAll();
\r
2372 TimeTaker timer("endScene", g_irrlicht);
\r
2373 driver->endScene();
\r
2374 endscenetime = timer.stop(true);
\r
2377 drawtime = drawtimer.stop(true);
\r
2383 static s16 lastFPS = 0;
\r
2384 //u16 fps = driver->getFPS();
\r
2385 u16 fps = (1.0/dtime_avg1);
\r
2387 if (lastFPS != fps)
\r
2389 core::stringw str = L"Minetest [";
\r
2390 str += driver->getName();
\r
2394 device->setWindowCaption(str.c_str());
\r
2400 device->yield();*/
\r
2403 delete quick_inventory;
\r
2405 } // client is deleted at this point
\r
2410 In the end, delete the Irrlicht device.
\r
2415 Update configuration file
\r
2417 /*if(configpath != "")
\r
2419 g_settings.updateConfigFile(configpath.c_str());
\r
2423 catch(con::PeerNotFoundException &e)
\r
2425 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2427 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2429 This is what has to be done in every thread to get suitable debug info
\r
2431 catch(std::exception &e)
\r
2433 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2434 <<e.what()<<std::endl;
\r
2439 debugstreams_deinit();
\r