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 SUGGESTION: add a second lighting value to the MS nibble of param of
\r
31 air to tell how bright the air node is when there is no sunlight.
\r
32 When day changes to night, these two values can be interpolated.
\r
34 TODO: Fix address to be ipv6 compatible
\r
36 TODO: ESC Pause mode in which the cursor is not kept at the center of window.
\r
37 TODO: Stop player if focus of window is taken away (go to pause mode)
\r
38 TODO: Optimize and fix makeFastFace or whatever it's called
\r
39 - Face calculation is the source of CPU usage on the client
\r
40 SUGGESTION: The client will calculate and send lighting changes and
\r
41 the server will randomly check some of them and kick the client out
\r
42 if it fails to calculate them right.
\r
43 - Actually, it could just start ignoring them and calculate them
\r
45 SUGGESTION: Combine MapBlock's face caches to so big pieces that VBO
\r
47 - That is >500 vertices
\r
49 TODO: Better dungeons
\r
50 TODO: There should be very slight natural caves also, starting from
\r
51 only a straightened-up cliff
\r
53 TODO: Changing of block with mouse wheel or something
\r
58 - One single map container with ids as keys
\r
61 TODO: - Keep track of the place of the mob in the last few hundreth's
\r
62 of a second - then, if a player hits it, take the value that is
\r
63 avg_rtt/2 before the moment the packet is received.
\r
66 SUGGESTION: Modify client to calculate single changes asynchronously
\r
68 TODO: Moving players more smoothly. Calculate moving animation
\r
69 in a way that doesn't make the player jump to the right place
\r
70 immediately when the server sends a new position
\r
72 TODO: There are some lighting-related todos and fixmes in
\r
73 ServerMap::emergeBlock
\r
75 FIXME: When a new sector is generated, it may change the ground level
\r
76 of it's and it's neighbors border that two blocks that are
\r
77 above and below each other and that are generated before and
\r
78 after the sector heightmap generation (order doesn't matter),
\r
79 can have a small gap between each other at the border.
\r
80 SUGGESTION: Use same technique for sector heightmaps as what we're
\r
81 using for UnlimitedHeightmap? (getting all neighbors
\r
84 TODO: Proper handling of spawning place (try to find something that
\r
85 is not in the middle of an ocean (some land to stand on at
\r
86 least) and save it in map config.
\r
87 SUGG: Set server to automatically find a good spawning place in some
\r
88 place where there is water and land.
\r
89 - Map to have a getWalkableNear(p)
\r
90 - Is this a good idea? It's part of the game to find a good place.
\r
92 TODO: Transfer more blocks in a single packet
\r
93 SUGG: A blockdata combiner class, to which blocks are added and at
\r
94 destruction it sends all the stuff in as few packets as possible.
\r
96 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
97 SUGG: Fetch stuff mainly from the viewing direction
\r
99 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
100 - This enables saving many packets and making a faster connection
\r
101 - This also enables server to check if client has received the
\r
102 most recent block sent, for example.
\r
103 TODO: Add a sane bandwidth throttling system to Connection
\r
105 SUGG: More fine-grained control of client's dumping of blocks from
\r
107 - ...What does this mean in the first place?
\r
109 TODO: Make the amount of blocks sending to client and the total
\r
110 amount of blocks dynamically limited. Transferring blocks is the
\r
111 main network eater of this system, so it is the one that has
\r
112 to be throttled so that RTTs stay low.
\r
114 TODO: Server to load starting inventory from disk
\r
116 TODO: PLayers to only be hidden when the client quits.
\r
117 TODO: - Players to be saved on disk, with inventory
\r
118 TODO: Players to be saved as text in map/players/<name>
\r
120 SUGG: A map editing mode (similar to dedicated server mode)
\r
122 TODO: Make fetching sector's blocks more efficient when rendering
\r
123 sectors that have very large amounts of blocks (on client)
\r
125 TODO: Make the video backend selectable
\r
127 Block object server side:
\r
128 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
129 - For all blocks in the buffer, objects are stepped(). This
\r
130 means they are active.
\r
131 - TODO: A global active buffer is needed for the server
\r
132 - TODO: All blocks going in and out of the buffer are recorded.
\r
133 - TODO: For outgoing blocks, a timestamp is written.
\r
134 - TODO: For incoming blocks, the time difference is calculated and
\r
135 objects are stepped according to it.
\r
136 TODO: A timestamp to blocks
\r
138 SUGG: Add a time value to the param of footstepped grass and check it
\r
139 against a global timer when a block is accessed, to make old
\r
142 TODO: Add config parameters for server's sending and generating distance
\r
144 TODO: Copy the text of the last picked sign to inventory in creative
\r
147 TODO: Untie client network operations from framerate
\r
148 - Needs some input queues or something
\r
150 SUGG: Make a copy of close-range environment on client for showing
\r
151 on screen, with minimal mutexes to slow down the main loop
\r
153 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
154 it by sending more stuff in a single packet.
\r
155 - Add a packet queue to RemoteClient, from which packets will be
\r
156 combined with object data packets
\r
157 - This is not exactly trivial: the object data packets are
\r
158 sometimes very big by themselves
\r
160 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
161 - This will allow saving ages of rats on disk but not sending
\r
164 TODO: Get rid of GotSplitPacketException
\r
166 SUGG: Implement lighting using VoxelManipulator
\r
167 - Would it be significantly faster?
\r
169 TODO: Check what goes wrong with caching map to disk (Kray)
\r
171 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
173 FIXME: Rats somehow go underground sometimes (you can see it in water)
\r
174 - Does their position get saved to a border value or something?
\r
175 - Does this happen anymore?
\r
177 SUGG: MovingObject::move and Player::move are basically the same.
\r
180 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
181 if something is already in it)
\r
182 - Use it in active block queue in water flowing
\r
184 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
185 need an additional metadata field for the texts
\r
188 ======================================================================
\r
190 TODO: A mapper to map contents to tile names (for each side)
\r
192 ======================================================================
\r
197 Setting this to 1 enables a special camera mode that forces
\r
198 the renderers to think that the camera statically points from
\r
199 the starting place to a static direction.
\r
201 This allows one to move around with the player and see what
\r
202 is actually drawn behind solid things and behind the player.
\r
204 #define FIELD_OF_VIEW_TEST 0
\r
206 #ifdef UNITTEST_DISABLE
\r
208 #pragma message ("Disabling unit tests")
\r
210 #warning "Disabling unit tests"
\r
212 // Disable unit tests
\r
213 #define ENABLE_TESTS 0
\r
215 // Enable unit tests
\r
216 #define ENABLE_TESTS 1
\r
220 #pragma comment(lib, "Irrlicht.lib")
\r
221 #pragma comment(lib, "jthread.lib")
\r
222 // This would get rid of the console window
\r
223 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
227 #define WIN32_LEAN_AND_MEAN
\r
228 #include <windows.h>
\r
229 #define sleep_ms(x) Sleep(x)
\r
231 #include <unistd.h>
\r
232 #define sleep_ms(x) usleep(x*1000)
\r
235 #include <iostream>
\r
238 #include <jmutexautolock.h>
\r
239 #include "common_irrlicht.h"
\r
242 #include "player.h"
\r
245 #include "environment.h"
\r
246 #include "server.h"
\r
247 #include "client.h"
\r
248 #include "serialization.h"
\r
249 #include "constants.h"
\r
250 #include "strfnd.h"
\r
251 #include "porting.h"
\r
252 #include <locale.h>
\r
254 IrrlichtDevice *g_device = NULL;
\r
256 const char *g_content_filenames[MATERIALS_COUNT] =
\r
258 "../data/stone.png",
\r
259 "../data/grass.png",
\r
260 "../data/water.png",
\r
261 "../data/torch_on_floor.png",
\r
262 "../data/tree.png",
\r
263 "../data/leaves.png",
\r
264 "../data/grass_footsteps.png",
\r
265 "../data/mese.png",
\r
267 "../data/water.png", // CONTENT_OCEAN
\r
271 video::SMaterial g_materials[MATERIALS_COUNT];
\r
274 TextureCache g_texturecache;
\r
277 // All range-related stuff below is locked behind this
\r
278 JMutex g_range_mutex;
\r
280 // Blocks are viewed in this range from the player
\r
281 s16 g_viewing_range_nodes = 60;
\r
283 // This is updated by the client's fetchBlocks routine
\r
284 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
286 // If true, the preceding value has no meaning and all blocks
\r
287 // already existing in memory are drawn
\r
288 bool g_viewing_range_all = false;
\r
290 // This is the freetime ratio imposed by the dynamic viewing
\r
291 // range changing code.
\r
292 // It is controlled by the main loop to the smallest value that
\r
293 // inhibits glitches (dtime jitter) in the main loop.
\r
294 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
299 These are loaded from the config file.
\r
302 Settings g_settings;
\r
304 // Sets default settings
\r
305 void set_default_settings()
\r
307 g_settings.set("dedicated_server", "");
\r
310 g_settings.set("wanted_fps", "30");
\r
311 g_settings.set("fps_max", "60");
\r
312 g_settings.set("viewing_range_nodes_max", "300");
\r
313 g_settings.set("viewing_range_nodes_min", "20");
\r
314 g_settings.set("screenW", "");
\r
315 g_settings.set("screenH", "");
\r
316 g_settings.set("host_game", "");
\r
317 g_settings.set("port", "");
\r
318 g_settings.set("address", "");
\r
319 g_settings.set("name", "");
\r
320 g_settings.set("random_input", "false");
\r
321 g_settings.set("client_delete_unused_sectors_timeout", "1200");
\r
322 g_settings.set("max_block_send_distance", "8");
\r
323 g_settings.set("max_block_generate_distance", "6");
\r
326 g_settings.set("creative_mode", "false");
\r
327 g_settings.set("heightmap_blocksize", "32");
\r
328 g_settings.set("height_randmax", "constant 50.0");
\r
329 g_settings.set("height_randfactor", "constant 0.6");
\r
330 g_settings.set("height_base", "linear 0 0 0");
\r
331 g_settings.set("plants_amount", "1.0");
\r
332 g_settings.set("ravines_amount", "1.0");
\r
333 g_settings.set("objectdata_interval", "0.2");
\r
334 g_settings.set("active_object_range", "2");
\r
335 g_settings.set("max_simultaneous_block_sends_per_client", "1");
\r
336 g_settings.set("max_simultaneous_block_sends_server_total", "4");
\r
337 g_settings.set("disable_water_climb", "true");
\r
338 g_settings.set("endless_water", "true");
\r
345 //u16 g_selected_material = 0;
\r
346 u16 g_selected_item = 0;
\r
348 bool g_esc_pressed = false;
\r
350 std::wstring g_text_buffer;
\r
351 bool g_text_buffer_accepted = false;
\r
353 // When true, the mouse and keyboard are grabbed
\r
354 bool g_game_focused = true;
\r
361 std::ostream *dout_con_ptr = &dummyout;
\r
362 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
363 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
364 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
365 //std::ostream *dout_con_ptr = &dstream;
\r
366 //std::ostream *derr_con_ptr = &dstream;
\r
369 std::ostream *dout_server_ptr = &dstream;
\r
370 std::ostream *derr_server_ptr = &dstream;
\r
373 std::ostream *dout_client_ptr = &dstream;
\r
374 std::ostream *derr_client_ptr = &dstream;
\r
381 JMutex g_timestamp_mutex;
\r
382 //std::string g_timestamp;
\r
384 std::string getTimestamp()
\r
386 if(g_timestamp_mutex.IsInitialized()==false)
\r
388 JMutexAutoLock lock(g_timestamp_mutex);
\r
389 //return g_timestamp;
\r
390 time_t t = time(NULL);
\r
391 struct tm *tm = localtime(&t);
\r
393 strftime(cs, 20, "%H:%M:%S", tm);
\r
397 class MyEventReceiver : public IEventReceiver
\r
400 // This is the one method that we have to implement
\r
401 virtual bool OnEvent(const SEvent& event)
\r
403 // Remember whether each key is down or up
\r
404 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
406 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
408 if(event.KeyInput.PressedDown)
\r
410 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
411 if(g_game_focused == false)
\r
413 s16 key = event.KeyInput.Key;
\r
414 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
416 g_text_buffer_accepted = true;
\r
418 else if(key == irr::KEY_BACK)
\r
420 if(g_text_buffer.size() > 0)
\r
421 g_text_buffer = g_text_buffer.substr
\r
422 (0, g_text_buffer.size()-1);
\r
426 wchar_t wc = event.KeyInput.Char;
\r
428 g_text_buffer += wc;
\r
432 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
434 if(g_game_focused == true)
\r
436 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
437 g_esc_pressed = true;
\r
441 // Material selection
\r
442 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
444 if(g_game_focused == true)
\r
446 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
449 g_selected_item = 0;
\r
450 dstream<<DTIME<<"Selected item: "
\r
451 <<g_selected_item<<std::endl;
\r
455 // Viewing range selection
\r
456 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
459 JMutexAutoLock lock(g_range_mutex);
\r
460 if(g_viewing_range_all)
\r
462 g_viewing_range_all = false;
\r
463 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
467 g_viewing_range_all = true;
\r
468 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
472 // Print debug stacks
\r
473 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
476 dstream<<"-----------------------------------------"
\r
478 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
479 dstream<<"-----------------------------------------"
\r
481 debug_stacks_print();
\r
486 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
488 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
490 leftclicked = true;
\r
492 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
494 rightclicked = true;
\r
496 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
498 /*dstream<<"event.MouseInput.Wheel="
\r
499 <<event.MouseInput.Wheel<<std::endl;*/
\r
500 if(event.MouseInput.Wheel < 0)
\r
502 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
505 g_selected_item = 0;
\r
507 else if(event.MouseInput.Wheel > 0)
\r
509 if(g_selected_item > 0)
\r
512 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
520 // This is used to check whether a key is being held down
\r
521 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
523 return keyIsDown[keyCode];
\r
528 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
529 keyIsDown[i] = false;
\r
530 leftclicked = false;
\r
531 rightclicked = false;
\r
537 // We use this array to store the current state of each key
\r
538 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
549 virtual ~InputHandler()
\r
552 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
553 virtual v2s32 getMousePos() = 0;
\r
554 virtual void setMousePos(s32 x, s32 y) = 0;
\r
555 virtual bool getLeftClicked() = 0;
\r
556 virtual bool getRightClicked() = 0;
\r
557 virtual void resetLeftClicked() = 0;
\r
558 virtual void resetRightClicked() = 0;
\r
560 virtual void step(float dtime) {};
\r
562 virtual void clear() {};
\r
565 InputHandler *g_input = NULL;
\r
570 g_game_focused = true;
\r
575 g_game_focused = false;
\r
578 class RealInputHandler : public InputHandler
\r
581 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
583 m_receiver(receiver)
\r
586 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
588 return m_receiver->IsKeyDown(keyCode);
\r
590 virtual v2s32 getMousePos()
\r
592 return m_device->getCursorControl()->getPosition();
\r
594 virtual void setMousePos(s32 x, s32 y)
\r
596 m_device->getCursorControl()->setPosition(x, y);
\r
599 virtual bool getLeftClicked()
\r
601 if(g_game_focused == false)
\r
603 return m_receiver->leftclicked;
\r
605 virtual bool getRightClicked()
\r
607 if(g_game_focused == false)
\r
609 return m_receiver->rightclicked;
\r
611 virtual void resetLeftClicked()
\r
613 m_receiver->leftclicked = false;
\r
615 virtual void resetRightClicked()
\r
617 m_receiver->rightclicked = false;
\r
622 resetRightClicked();
\r
623 resetLeftClicked();
\r
626 IrrlichtDevice *m_device;
\r
627 MyEventReceiver *m_receiver;
\r
630 class RandomInputHandler : public InputHandler
\r
633 RandomInputHandler()
\r
635 leftclicked = false;
\r
636 rightclicked = false;
\r
637 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
638 keydown[i] = false;
\r
640 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
642 return keydown[keyCode];
\r
644 virtual v2s32 getMousePos()
\r
648 virtual void setMousePos(s32 x, s32 y)
\r
650 mousepos = v2s32(x,y);
\r
653 virtual bool getLeftClicked()
\r
655 return leftclicked;
\r
657 virtual bool getRightClicked()
\r
659 return rightclicked;
\r
661 virtual void resetLeftClicked()
\r
663 leftclicked = false;
\r
665 virtual void resetRightClicked()
\r
667 rightclicked = false;
\r
670 virtual void step(float dtime)
\r
673 static float counter1 = 0;
\r
677 counter1 = 0.1*Rand(1,10);
\r
678 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
679 g_selected_material++;
\r
681 g_selected_material = 0;*/
\r
682 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
685 g_selected_item = 0;
\r
689 static float counter1 = 0;
\r
693 counter1 = 0.1*Rand(1, 40);
\r
694 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
698 static float counter1 = 0;
\r
702 counter1 = 0.1*Rand(1, 40);
\r
703 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
707 static float counter1 = 0;
\r
711 counter1 = 0.1*Rand(1, 40);
\r
712 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
716 static float counter1 = 0;
\r
720 counter1 = 0.1*Rand(1, 40);
\r
721 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
725 static float counter1 = 0;
\r
729 counter1 = 0.1*Rand(1, 20);
\r
730 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
734 static float counter1 = 0;
\r
738 counter1 = 0.1*Rand(1, 30);
\r
739 leftclicked = true;
\r
743 static float counter1 = 0;
\r
747 counter1 = 0.1*Rand(1, 20);
\r
748 rightclicked = true;
\r
751 mousepos += mousespeed;
\r
754 s32 Rand(s32 min, s32 max)
\r
756 return (rand()%(max-min+1))+min;
\r
759 bool keydown[KEY_KEY_CODES_COUNT];
\r
766 void updateViewingRange(f32 frametime, Client *client)
\r
768 // Range_all messes up frametime_avg
\r
769 if(g_viewing_range_all == true)
\r
772 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
774 // Initialize to the target value
\r
775 static float frametime_avg = 1.0/wanted_fps;
\r
776 frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
778 static f32 counter = 0;
\r
780 counter -= frametime;
\r
783 //counter = 1.0; //seconds
\r
784 counter = 0.5; //seconds
\r
786 //float freetime_ratio = 0.2;
\r
787 //float freetime_ratio = 0.4;
\r
788 float freetime_ratio = FREETIME_RATIO;
\r
790 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
792 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
794 static bool fraction_is_good = false;
\r
796 float fraction_good_threshold = 0.1;
\r
797 float fraction_bad_threshold = 0.25;
\r
798 float fraction_limit;
\r
799 // Use high limit if fraction is good AND the fraction would
\r
800 // lower the range. We want to keep the range fairly high.
\r
801 if(fraction_is_good && fraction > 1.0)
\r
802 fraction_limit = fraction_bad_threshold;
\r
804 fraction_limit = fraction_good_threshold;
\r
806 if(fabs(fraction - 1.0) < fraction_limit)
\r
808 fraction_is_good = true;
\r
813 fraction_is_good = false;
\r
816 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
817 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
818 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
819 <<" faction = "<<fraction<<std::endl;*/
\r
821 JMutexAutoLock lock(g_range_mutex);
\r
823 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
824 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
826 s16 n = (float)g_viewing_range_nodes / fraction;
\r
827 if(n < viewing_range_nodes_min)
\r
828 n = viewing_range_nodes_min;
\r
829 if(n > viewing_range_nodes_max)
\r
830 n = viewing_range_nodes_max;
\r
832 bool can_change = true;
\r
834 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
835 can_change = false;
\r
838 g_viewing_range_nodes = n;
\r
840 /*dstream<<"g_viewing_range_nodes = "
\r
841 <<g_viewing_range_nodes<<std::endl;*/
\r
844 class GUIQuickInventory : public IEventReceiver
\r
848 gui::IGUIEnvironment* env,
\r
849 gui::IGUIElement* parent,
\r
852 Inventory *inventory):
\r
853 m_itemcount(itemcount),
\r
854 m_inventory(inventory)
\r
856 core::rect<s32> imgsize(0,0,48,48);
\r
857 core::rect<s32> textsize(0,0,48,16);
\r
858 v2s32 spacing(0, 64);
\r
859 for(s32 i=0; i<m_itemcount; i++)
\r
861 m_images.push_back(env->addImage(
\r
862 imgsize + pos + spacing*i
\r
864 m_images[i]->setScaleImage(true);
\r
865 m_texts.push_back(env->addStaticText(
\r
867 textsize + pos + spacing*i,
\r
870 m_texts[i]->setBackgroundColor(
\r
871 video::SColor(128,0,0,0));
\r
872 m_texts[i]->setTextAlignment(
\r
874 gui::EGUIA_UPPERLEFT);
\r
878 virtual bool OnEvent(const SEvent& event)
\r
883 void setSelection(s32 i)
\r
892 start = m_selection - m_itemcount / 2;
\r
894 for(s32 i=0; i<m_itemcount; i++)
\r
898 if(j > (s32)m_inventory->getSize() - 1)
\r
899 j -= m_inventory->getSize();
\r
901 j += m_inventory->getSize();
\r
903 InventoryItem *item = m_inventory->getItem(j);
\r
907 m_images[i]->setImage(NULL);
\r
910 if(m_selection == j)
\r
911 swprintf(t, 10, L"<-");
\r
913 swprintf(t, 10, L"");
\r
914 m_texts[i]->setText(t);
\r
916 // The next ifs will segfault with a NULL pointer
\r
921 m_images[i]->setImage(item->getImage());
\r
924 if(m_selection == j)
\r
925 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
927 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
928 m_texts[i]->setText(t);
\r
934 core::array<gui::IGUIStaticText*> m_texts;
\r
935 core::array<gui::IGUIImage*> m_images;
\r
936 Inventory *m_inventory;
\r
940 int main(int argc, char *argv[])
\r
943 Low-level initialization
\r
946 bool disable_stderr = false;
\r
948 disable_stderr = true;
\r
951 // Initialize debug streams
\r
952 debugstreams_init(disable_stderr, DEBUGFILE);
\r
953 // Initialize debug stacks
\r
954 debug_stacks_init();
\r
956 DSTACK(__FUNCTION_NAME);
\r
962 Basic initialization
\r
965 // Initialize default settings
\r
966 set_default_settings();
\r
968 // Print startup message
\r
969 dstream<<DTIME<<"minetest-c55"
\r
970 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
971 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
974 // Set locale. This is for forcing '.' as the decimal point.
\r
975 std::locale::global(std::locale("C"));
\r
976 // This enables printing all characters in bitmap font
\r
977 setlocale(LC_CTYPE, "en_US");
\r
979 // Initialize sockets
\r
981 atexit(sockets_cleanup);
\r
983 // Initialize timestamp mutex
\r
984 g_timestamp_mutex.Init();
\r
998 // Read config file
\r
1002 g_settings.readConfigFile(argv[1]);
\r
1006 const char *filenames[2] =
\r
1008 "../minetest.conf",
\r
1009 "../../minetest.conf"
\r
1012 for(u32 i=0; i<2; i++)
\r
1014 bool r = g_settings.readConfigFile(filenames[i]);
\r
1020 // Initialize random seed
\r
1023 g_range_mutex.Init();
\r
1024 assert(g_range_mutex.IsInitialized());
\r
1026 // Read map parameters from settings
\r
1028 HMParams hm_params;
\r
1029 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1030 hm_params.randmax = g_settings.get("height_randmax");
\r
1031 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1032 hm_params.base = g_settings.get("height_base");
\r
1034 MapParams map_params;
\r
1035 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1036 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1042 std::cout<<std::endl<<std::endl;
\r
1045 <<" .__ __ __ "<<std::endl
\r
1046 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1047 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1048 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1049 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1050 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1052 <<"Now with more waterish water!"
\r
1055 std::cout<<std::endl;
\r
1056 char templine[100];
\r
1059 bool dedicated = g_settings.getBoolAsk
\r
1060 ("dedicated_server", "Dedicated server?", false);
\r
1061 std::cout<<"dedicated = "<<dedicated<<std::endl;
\r
1064 u16 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1065 std::cout<<"-> "<<port<<std::endl;
\r
1069 DSTACK("Dedicated server branch");
\r
1071 std::cout<<std::endl;
\r
1072 std::cout<<"========================"<<std::endl;
\r
1073 std::cout<<"Running dedicated server"<<std::endl;
\r
1074 std::cout<<"========================"<<std::endl;
\r
1075 std::cout<<std::endl;
\r
1077 Server server("../map", hm_params, map_params);
\r
1078 server.start(port);
\r
1082 // This is kind of a hack but can be done like this
\r
1083 // because server.step() is very light
\r
1085 server.step(0.030);
\r
1087 static int counter = 0;
\r
1093 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1094 core::list<PlayerInfo>::Iterator i;
\r
1095 static u32 sum_old = 0;
\r
1096 u32 sum = PIChecksum(list);
\r
1097 if(sum != sum_old)
\r
1099 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1100 for(i=list.begin(); i!=list.end(); i++)
\r
1102 i->PrintLine(&std::cout);
\r
1112 bool hosting = false;
\r
1113 char connect_name[100] = "";
\r
1115 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1116 if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1118 std::cout<<g_settings.get("address")<<std::endl;
\r
1119 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1123 std::cin.getline(connect_name, 100);
\r
1126 if(connect_name[0] == 0){
\r
1127 snprintf(connect_name, 100, "127.0.0.1");
\r
1132 std::cout<<"-> hosting"<<std::endl;
\r
1134 std::cout<<"-> "<<connect_name<<std::endl;
\r
1136 char playername[PLAYERNAME_SIZE] = "";
\r
1137 if(g_settings.get("name") != "")
\r
1139 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1143 std::cout<<"Name of player: ";
\r
1144 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1146 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1149 Resolution selection
\r
1154 bool fullscreen = false;
\r
1156 if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")
\r
1158 screenW = atoi(g_settings.get("screenW").c_str());
\r
1159 screenH = atoi(g_settings.get("screenH").c_str());
\r
1163 u16 resolutions[][3] = {
\r
1164 //W, H, fullscreen
\r
1175 u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
\r
1177 for(u16 i=0; i<res_count; i++)
\r
1179 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1180 <<resolutions[i][1];
\r
1181 if(resolutions[i][2])
\r
1182 std::cout<<" fullscreen"<<std::endl;
\r
1184 std::cout<<" windowed"<<std::endl;
\r
1186 std::cout<<"Select a window resolution number [empty = 2]: ";
\r
1187 std::cin.getline(templine, 100);
\r
1190 if(templine[0] == 0)
\r
1193 r0 = atoi(templine);
\r
1195 if(r0 > res_count || r0 == 0)
\r
1201 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1202 <<resolutions[i][1];
\r
1203 if(resolutions[i][2])
\r
1204 std::cout<<" fullscreen"<<std::endl;
\r
1206 std::cout<<" windowed"<<std::endl;
\r
1209 screenW = resolutions[r0-1][0];
\r
1210 screenH = resolutions[r0-1][1];
\r
1211 fullscreen = resolutions[r0-1][2];
\r
1216 MyEventReceiver receiver;
\r
1218 video::E_DRIVER_TYPE driverType;
\r
1221 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1222 driverType = video::EDT_OPENGL;
\r
1224 driverType = video::EDT_OPENGL;
\r
1227 // create device and exit if creation failed
\r
1229 IrrlichtDevice *device;
\r
1230 device = createDevice(driverType,
\r
1231 core::dimension2d<u32>(screenW, screenH),
\r
1232 16, fullscreen, false, false, &receiver);
\r
1234 /*device = createDevice(driverType,
\r
1235 core::dimension2d<u32>(screenW, screenH),
\r
1236 16, fullscreen, false, true, &receiver);*/
\r
1239 return 1; // could not create selected driver.
\r
1241 g_device = device;
\r
1243 device->setResizable(true);
\r
1245 if(g_settings.getBool("random_input"))
\r
1246 g_input = new RandomInputHandler();
\r
1248 g_input = new RealInputHandler(device, &receiver);
\r
1251 Continue initialization
\r
1254 video::IVideoDriver* driver = device->getVideoDriver();
\r
1255 // These make the textures not to show at all
\r
1256 //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
\r
1257 //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );
\r
1259 //driver->setMinHardwareBufferVertexCount(1);
\r
1261 scene::ISceneManager* smgr = device->getSceneManager();
\r
1263 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1264 gui::IGUISkin* skin = guienv->getSkin();
\r
1265 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1267 skin->setFont(font);
\r
1268 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1269 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1270 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1271 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1272 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1273 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1275 const wchar_t *text = L"Loading and connecting...";
\r
1276 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1277 core::dimension2d<u32> textd = font->getDimension(text);
\r
1278 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1279 // Have to add a bit to disable the text from word wrapping
\r
1280 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1281 core::vector2d<s32> textsize(300, textd.Height);
\r
1282 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1284 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1285 text, textrect, false, false);
\r
1286 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1288 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1289 guienv->drawAll();
\r
1290 driver->endScene();
\r
1293 Initialize material array
\r
1296 //video::SMaterial g_materials[MATERIALS_COUNT];
\r
1297 for(u16 i=0; i<MATERIALS_COUNT; i++)
\r
1299 g_materials[i].Lighting = false;
\r
1300 g_materials[i].BackfaceCulling = false;
\r
1302 const char *filename = g_content_filenames[i];
\r
1303 if(filename != NULL){
\r
1304 video::ITexture *t = driver->getTexture(filename);
\r
1306 std::cout<<DTIME<<"Texture could not be loaded: \""
\r
1307 <<filename<<"\""<<std::endl;
\r
1310 g_materials[i].setTexture(0, driver->getTexture(filename));
\r
1312 //g_materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
\r
1313 g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1314 //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
\r
1315 //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1318 g_materials[CONTENT_WATER].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1319 //g_materials[CONTENT_WATER].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
\r
1320 g_materials[CONTENT_OCEAN].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1322 /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png"));
\r
1323 g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png"));
\r
1324 g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png"));
\r
1325 for(u32 i=0; i<3; i++)
\r
1327 g_mesh_materials[i].Lighting = false;
\r
1328 g_mesh_materials[i].BackfaceCulling = false;
\r
1329 g_mesh_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1330 g_mesh_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1334 Preload some random textures that are used in threads
\r
1337 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1338 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1339 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1342 Make a scope here for the client so that it gets removed
\r
1343 before the irrlicht device
\r
1347 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1352 SharedPtr<Server> server;
\r
1354 server = new Server("../map", hm_params, map_params);
\r
1355 server->start(port);
\r
1362 // TODO: Get rid of the g_materials parameter or it's globalness
\r
1363 Client client(device, g_materials,
\r
1364 g_settings.getFloat("client_delete_unused_sectors_timeout"),
\r
1367 Address connect_address(0,0,0,0, port);
\r
1369 connect_address.Resolve(connect_name);
\r
1371 catch(ResolveError &e)
\r
1373 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1377 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1378 client.connect(connect_address);
\r
1381 while(client.connectedAndInitialized() == false)
\r
1384 if(server != NULL){
\r
1385 server->step(0.1);
\r
1390 catch(con::PeerNotFoundException &e)
\r
1392 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1399 scene::ISceneNode* skybox = smgr->addSkyBoxSceneNode(
\r
1400 driver->getTexture("../data/skybox2.png"),
\r
1401 driver->getTexture("../data/skybox3.png"),
\r
1402 driver->getTexture("../data/skybox1.png"),
\r
1403 driver->getTexture("../data/skybox1.png"),
\r
1404 driver->getTexture("../data/skybox1.png"),
\r
1405 driver->getTexture("../data/skybox1.png"));
\r
1406 /* driver->getTexture("../data/irrlicht2_up.jpg"),
\r
1407 driver->getTexture("../data/irrlicht2_dn.jpg"),
\r
1408 driver->getTexture("../data/irrlicht2_lf.jpg"),
\r
1409 driver->getTexture("../data/irrlicht2_rt.jpg"),
\r
1410 driver->getTexture("../data/irrlicht2_ft.jpg"),
\r
1411 driver->getTexture("../data/irrlicht2_bk.jpg"));*/
\r
1414 Create the camera node
\r
1417 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1418 0, // Camera parent
\r
1419 v3f(BS*100, BS*2, BS*100), // Look from
\r
1420 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1424 if(camera == NULL)
\r
1427 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1429 camera->setFOV(FOV_ANGLE);
\r
1431 // Just so big a value that everything rendered is visible
\r
1432 camera->setFarValue(100000*BS);
\r
1434 /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1435 f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1437 camera->setFarValue(range);
\r
1441 video::EFT_FOG_LINEAR,
\r
1449 f32 camera_yaw = 0; // "right/left"
\r
1450 f32 camera_pitch = 0; // "up/down"
\r
1452 gui_loadingtext->remove();
\r
1455 Add some gui stuff
\r
1458 // First line of debug text
\r
1459 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1461 core::rect<s32>(5, 5, 5+600, 5+textsize.Y),
\r
1463 // Second line of debug text
\r
1464 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1466 core::rect<s32>(5, 5+(textsize.Y+5)*1, 5+600, (5+textsize.Y)*2),
\r
1469 // At the middle of the screen
\r
1470 // Object infos are shown in this
\r
1471 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1473 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1476 // This is a copy of the inventory that the client's environment has
\r
1477 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1479 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1480 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1483 Some statistics are collected in these
\r
1486 u32 scenetime = 0;
\r
1487 u32 endscenetime = 0;
\r
1495 virtual void sendText(std::string text) = 0;
\r
1498 struct TextDestSign : public TextDest
\r
1500 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1502 m_blockpos = blockpos;
\r
1504 m_client = client;
\r
1506 void sendText(std::string text)
\r
1508 dstream<<"Changing text of a sign object: "
\r
1509 <<text<<std::endl;
\r
1510 m_client->sendSignText(m_blockpos, m_id, text);
\r
1518 TextDest *textbuf_dest = NULL;
\r
1520 //gui::IGUIWindow* input_window = NULL;
\r
1521 gui::IGUIStaticText* input_guitext = NULL;
\r
1527 bool first_loop_after_window_activation = true;
\r
1529 // Time is in milliseconds
\r
1530 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1531 // NOTE: Have to call run() between calls of this to update the timer
\r
1532 u32 lasttime = device->getTimer()->getTime();
\r
1534 while(device->run())
\r
1536 // Hilight boxes collected during the loop and displayed
\r
1537 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1540 std::wstring infotext;
\r
1542 //TimeTaker //timer1("//timer1", device);
\r
1544 // Time of frame without fps limit
\r
1548 // not using getRealTime is necessary for wine
\r
1549 u32 time = device->getTimer()->getTime();
\r
1550 if(time > lasttime)
\r
1551 busytime_u32 = time - lasttime;
\r
1554 busytime = busytime_u32 / 1000.0;
\r
1557 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1559 // Absolutelu necessary for wine!
\r
1566 //updateViewingRange(dtime, &client);
\r
1567 updateViewingRange(busytime, &client);
\r
1574 float fps_max = g_settings.getFloat("fps_max");
\r
1575 u32 frametime_min = 1000./fps_max;
\r
1577 if(busytime_u32 < frametime_min)
\r
1579 u32 sleeptime = frametime_min - busytime_u32;
\r
1580 device->sleep(sleeptime);
\r
1584 // Absolutelu necessary for wine!
\r
1588 Time difference calculation
\r
1590 f32 dtime; // in seconds
\r
1592 u32 time = device->getTimer()->getTime();
\r
1593 if(time > lasttime)
\r
1594 dtime = (time - lasttime) / 1000.0;
\r
1600 Time average and jitter calculation
\r
1603 static f32 dtime_avg1 = 0.0;
\r
1604 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1605 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1607 static f32 dtime_jitter1_max_sample = 0.0;
\r
1608 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1610 static f32 jitter1_max = 0.0;
\r
1611 static f32 counter = 0.0;
\r
1612 if(dtime_jitter1 > jitter1_max)
\r
1613 jitter1_max = dtime_jitter1;
\r
1618 dtime_jitter1_max_sample = jitter1_max;
\r
1619 dtime_jitter1_max_fraction
\r
1620 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1621 jitter1_max = 0.0;
\r
1624 Control freetime ratio
\r
1626 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1628 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1629 g_freetime_ratio += 0.01;
\r
1633 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1634 g_freetime_ratio -= 0.01;
\r
1640 Busytime average and jitter calculation
\r
1643 static f32 busytime_avg1 = 0.0;
\r
1644 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1645 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1647 static f32 busytime_jitter1_max_sample = 0.0;
\r
1648 static f32 busytime_jitter1_min_sample = 0.0;
\r
1650 static f32 jitter1_max = 0.0;
\r
1651 static f32 jitter1_min = 0.0;
\r
1652 static f32 counter = 0.0;
\r
1653 if(busytime_jitter1 > jitter1_max)
\r
1654 jitter1_max = busytime_jitter1;
\r
1655 if(busytime_jitter1 < jitter1_min)
\r
1656 jitter1_min = busytime_jitter1;
\r
1658 if(counter > 0.0){
\r
1660 busytime_jitter1_max_sample = jitter1_max;
\r
1661 busytime_jitter1_min_sample = jitter1_min;
\r
1662 jitter1_max = 0.0;
\r
1663 jitter1_min = 0.0;
\r
1668 Debug info for client
\r
1671 static float counter = 0.0;
\r
1676 client.printDebugInfo(std::cout);
\r
1681 Input handler step()
\r
1683 g_input->step(dtime);
\r
1694 Player speed control
\r
1697 if(g_game_focused)
\r
1704 bool a_superspeed,
\r
1707 PlayerControl control(
\r
1708 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1709 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1710 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1711 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1712 g_input->isKeyDown(irr::KEY_SPACE),
\r
1713 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1717 client.setPlayerControl(control);
\r
1721 // Set every key to inactive
\r
1722 PlayerControl control;
\r
1723 client.setPlayerControl(control);
\r
1728 Process environment
\r
1732 //TimeTaker timer("client.step(dtime)", device);
\r
1733 client.step(dtime);
\r
1734 //client.step(dtime_avg1);
\r
1737 if(server != NULL)
\r
1739 //TimeTaker timer("server->step(dtime)", device);
\r
1740 server->step(dtime);
\r
1743 v3f player_position = client.getPlayerPosition();
\r
1745 //TimeTaker //timer2("//timer2", device);
\r
1748 Mouse and camera control
\r
1751 if(device->isWindowActive() && g_game_focused)
\r
1753 device->getCursorControl()->setVisible(false);
\r
1755 if(first_loop_after_window_activation){
\r
1756 //std::cout<<"window active, first loop"<<std::endl;
\r
1757 first_loop_after_window_activation = false;
\r
1760 s32 dx = g_input->getMousePos().X - 320;
\r
1761 s32 dy = g_input->getMousePos().Y - 240;
\r
1762 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1763 camera_yaw -= dx*0.2;
\r
1764 camera_pitch += dy*0.2;
\r
1765 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1766 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1768 g_input->setMousePos(320, 240);
\r
1771 device->getCursorControl()->setVisible(true);
\r
1773 //std::cout<<"window inactive"<<std::endl;
\r
1774 first_loop_after_window_activation = true;
\r
1777 camera_yaw = wrapDegrees(camera_yaw);
\r
1778 camera_pitch = wrapDegrees(camera_pitch);
\r
1780 v3f camera_direction = v3f(0,0,1);
\r
1781 camera_direction.rotateYZBy(camera_pitch);
\r
1782 camera_direction.rotateXZBy(camera_yaw);
\r
1784 v3f camera_position =
\r
1785 player_position + v3f(0, BS+BS/2, 0);
\r
1787 camera->setPosition(camera_position);
\r
1788 // *100.0 helps in large map coordinates
\r
1789 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1791 if(FIELD_OF_VIEW_TEST){
\r
1792 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1793 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1796 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1797 //TimeTaker timer("client.updateCamera", device);
\r
1798 client.updateCamera(camera_position, camera_direction);
\r
1802 //TimeTaker //timer3("//timer3", device);
\r
1805 Calculate what block is the crosshair pointing to
\r
1808 //u32 t1 = device->getTimer()->getRealTime();
\r
1810 //f32 d = 4; // max. distance
\r
1811 f32 d = 4; // max. distance
\r
1812 core::line3d<f32> shootline(camera_position,
\r
1813 camera_position + camera_direction * BS * (d+1));
\r
1815 MapBlockObject *selected_object = client.getSelectedObject
\r
1816 (d*BS, camera_position, shootline);
\r
1818 if(selected_object != NULL)
\r
1820 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1822 core::aabbox3d<f32> box_on_map
\r
1823 = selected_object->getSelectionBoxOnMap();
\r
1825 hilightboxes.push_back(box_on_map);
\r
1827 infotext = narrow_to_wide(selected_object->infoText());
\r
1829 if(g_input->getLeftClicked())
\r
1831 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1832 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1833 selected_object->getId(), g_selected_item);
\r
1835 else if(g_input->getRightClicked())
\r
1837 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1839 Check if we want to modify the object ourselves
\r
1841 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1843 dstream<<"Sign object right-clicked"<<std::endl;
\r
1847 input_guitext = guienv->addStaticText(L"",
\r
1848 core::rect<s32>(150,100,350,120),
\r
1850 false, // wordwrap?
\r
1853 input_guitext->setDrawBackground(true);
\r
1855 g_text_buffer = L"";
\r
1856 g_text_buffer_accepted = false;
\r
1857 textbuf_dest = new TextDestSign(
\r
1858 selected_object->getBlock()->getPos(),
\r
1859 selected_object->getId(),
\r
1863 Otherwise pass the event to the server as-is
\r
1867 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1868 selected_object->getId(), g_selected_item);
\r
1872 else // selected_object == NULL
\r
1875 bool nodefound = false;
\r
1877 v3s16 neighbourpos;
\r
1878 core::aabbox3d<f32> nodefacebox;
\r
1879 f32 mindistance = BS * 1001;
\r
1881 v3s16 pos_i = floatToInt(player_position);
\r
1883 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1887 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1888 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1889 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1890 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1891 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1892 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1894 for(s16 y = ystart; y <= yend; y++)
\r
1895 for(s16 z = zstart; z <= zend; z++)
\r
1896 for(s16 x = xstart; x <= xend; x++)
\r
1901 n = client.getNode(v3s16(x,y,z));
\r
1902 if(content_pointable(n.d) == false)
\r
1905 catch(InvalidPositionException &e)
\r
1911 v3f npf = intToFloat(np);
\r
1916 v3s16(0,0,1), // back
\r
1917 v3s16(0,1,0), // top
\r
1918 v3s16(1,0,0), // right
\r
1919 v3s16(0,0,-1), // front
\r
1920 v3s16(0,-1,0), // bottom
\r
1921 v3s16(-1,0,0), // left
\r
1927 if(n.d == CONTENT_LIGHT)
\r
1929 v3s16 dir = unpackDir(n.dir);
\r
1930 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1931 dir_f *= BS/2 - BS/6 - BS/20;
\r
1932 v3f cpf = npf + dir_f;
\r
1933 f32 distance = (cpf - camera_position).getLength();
\r
1935 core::aabbox3d<f32> box;
\r
1938 if(dir == v3s16(0,-1,0))
\r
1940 box = core::aabbox3d<f32>(
\r
1941 npf - v3f(BS/6, BS/2, BS/6),
\r
1942 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
1946 else if(dir == v3s16(0,1,0))
\r
1948 box = core::aabbox3d<f32>(
\r
1949 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
1950 npf + v3f(BS/6, BS/2, BS/6)
\r
1956 box = core::aabbox3d<f32>(
\r
1957 cpf - v3f(BS/6, BS/3, BS/6),
\r
1958 cpf + v3f(BS/6, BS/3, BS/6)
\r
1962 if(distance < mindistance)
\r
1964 if(box.intersectsWithLine(shootline))
\r
1968 neighbourpos = np;
\r
1969 mindistance = distance;
\r
1970 nodefacebox = box;
\r
1979 for(u16 i=0; i<6; i++)
\r
1981 v3f dir_f = v3f(dirs[i].X,
\r
1982 dirs[i].Y, dirs[i].Z);
\r
1983 v3f centerpoint = npf + dir_f * BS/2;
\r
1985 (centerpoint - camera_position).getLength();
\r
1987 if(distance < mindistance)
\r
1989 core::CMatrix4<f32> m;
\r
1990 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1992 // This is the back face
\r
1993 v3f corners[2] = {
\r
1994 v3f(BS/2, BS/2, BS/2),
\r
1995 v3f(-BS/2, -BS/2, BS/2+d)
\r
1998 for(u16 j=0; j<2; j++)
\r
2000 m.rotateVect(corners[j]);
\r
2001 corners[j] += npf;
\r
2004 core::aabbox3d<f32> facebox(corners[0]);
\r
2005 facebox.addInternalPoint(corners[1]);
\r
2007 if(facebox.intersectsWithLine(shootline))
\r
2011 neighbourpos = np + dirs[i];
\r
2012 mindistance = distance;
\r
2013 nodefacebox = facebox;
\r
2015 } // if distance < mindistance
\r
2017 } // regular block
\r
2022 //std::cout<<DTIME<<"nodefound == true"<<std::endl;
\r
2023 //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2024 //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
\r
2026 static v3s16 nodepos_old(-1,-1,-1);
\r
2027 if(nodepos != nodepos_old){
\r
2028 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2029 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2030 nodepos_old = nodepos;
\r
2032 /*wchar_t positiontext[20];
\r
2033 swprintf(positiontext, 20, L"(%i,%i,%i)",
\r
2034 nodepos.X, nodepos.Y, nodepos.Z);
\r
2035 positiontextgui->setText(positiontext);*/
\r
2038 hilightboxes.push_back(nodefacebox);
\r
2040 if(g_input->getLeftClicked())
\r
2042 //std::cout<<DTIME<<"Removing node"<<std::endl;
\r
2043 //client.removeNode(nodepos);
\r
2044 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2045 client.clickGround(0, nodepos, neighbourpos, g_selected_item);
\r
2047 if(g_input->getRightClicked())
\r
2049 //std::cout<<DTIME<<"Placing node"<<std::endl;
\r
2050 //client.addNodeFromInventory(neighbourpos, g_selected_item);
\r
2051 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2052 client.clickGround(1, nodepos, neighbourpos, g_selected_item);
\r
2056 //std::cout<<DTIME<<"nodefound == false"<<std::endl;
\r
2057 //positiontextgui->setText(L"");
\r
2060 } // selected_object == NULL
\r
2062 g_input->resetLeftClicked();
\r
2063 g_input->resetRightClicked();
\r
2066 Calculate stuff for drawing
\r
2069 v2u32 screensize = driver->getScreenSize();
\r
2070 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
2072 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2075 Update gui stuff (0ms)
\r
2078 //TimeTaker guiupdatetimer("Gui updating", device);
\r
2081 wchar_t temptext[100];
\r
2083 static float drawtime_avg = 0;
\r
2084 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2085 static float scenetime_avg = 0;
\r
2086 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2087 static float endscenetime_avg = 0;
\r
2088 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2090 swprintf(temptext, 100, L"Minetest-c55 ("
\r
2092 L", R: range_all=%i"
\r
2094 L" drawtime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2096 g_viewing_range_all,
\r
2102 guitext->setText(temptext);
\r
2106 wchar_t temptext[100];
\r
2107 /*swprintf(temptext, 100,
\r
2109 L"% .3f < btime_jitter < % .3f"
\r
2110 L", dtime_jitter = % .1f %%"
\r
2111 //L", ftime_ratio = % .3f"
\r
2113 busytime_jitter1_min_sample,
\r
2114 busytime_jitter1_max_sample,
\r
2115 dtime_jitter1_max_fraction * 100.0
\r
2116 //g_freetime_ratio
\r
2118 swprintf(temptext, 100,
\r
2119 L"(% .1f, % .1f, % .1f)"
\r
2120 L" (% .3f < btime_jitter < % .3f"
\r
2121 L", dtime_jitter = % .1f %%)",
\r
2122 player_position.X/BS,
\r
2123 player_position.Y/BS,
\r
2124 player_position.Z/BS,
\r
2125 busytime_jitter1_min_sample,
\r
2126 busytime_jitter1_max_sample,
\r
2127 dtime_jitter1_max_fraction * 100.0
\r
2130 guitext2->setText(temptext);
\r
2134 /*wchar_t temptext[100];
\r
2135 swprintf(temptext, 100,
\r
2136 SWPRINTF_CHARSTRING,
\r
2137 infotext.substr(0,99).c_str()
\r
2140 guitext_info->setText(temptext);*/
\r
2142 guitext_info->setText(infotext.c_str());
\r
2149 static u16 old_selected_item = 65535;
\r
2150 if(client.getLocalInventoryUpdated()
\r
2151 || g_selected_item != old_selected_item)
\r
2153 old_selected_item = g_selected_item;
\r
2154 //std::cout<<"Updating local inventory"<<std::endl;
\r
2155 client.getLocalInventory(local_inventory);
\r
2156 quick_inventory->setSelection(g_selected_item);
\r
2157 quick_inventory->update();
\r
2160 if(input_guitext != NULL)
\r
2162 /*wchar_t temptext[100];
\r
2163 swprintf(temptext, 100,
\r
2164 SWPRINTF_CHARSTRING,
\r
2165 g_text_buffer.substr(0,99).c_str()
\r
2167 input_guitext->setText(g_text_buffer.c_str());
\r
2173 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2175 input_guitext->remove();
\r
2176 input_guitext = NULL;
\r
2178 if(textbuf_dest != NULL)
\r
2180 std::string text = wide_to_narrow(g_text_buffer);
\r
2181 dstream<<"Sending text: "<<text<<std::endl;
\r
2182 textbuf_dest->sendText(text);
\r
2183 delete textbuf_dest;
\r
2184 textbuf_dest = NULL;
\r
2190 //guiupdatetimer.stop();
\r
2196 TimeTaker drawtimer("Drawing", device);
\r
2199 Background color is choosen based on whether the player is
\r
2200 much beyond the initial ground level
\r
2202 /*video::SColor bgcolor;
\r
2203 v3s16 p0 = Map::floatToInt(player_position);
\r
2204 // Does this make short random delays?
\r
2205 // NOTE: no need for this, sky doesn't show underground with
\r
2207 bool is_underground = client.isNodeUnderground(p0);
\r
2208 //bool is_underground = false;
\r
2209 if(is_underground == false)
\r
2210 bgcolor = video::SColor(255,90,140,200);
\r
2212 bgcolor = video::SColor(255,0,0,0);*/
\r
2214 //video::SColor bgcolor = video::SColor(255,90,140,200);
\r
2215 video::SColor bgcolor = skycolor;
\r
2218 driver->beginScene(true, true, bgcolor);
\r
2222 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2225 TimeTaker timer("smgr", device);
\r
2227 scenetime = timer.stop(true);
\r
2231 //TimeTaker timer9("auxiliary drawings", device);
\r
2234 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2235 displaycenter + core::vector2d<s32>(10,0),
\r
2236 video::SColor(255,255,255,255));
\r
2237 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2238 displaycenter + core::vector2d<s32>(0,10),
\r
2239 video::SColor(255,255,255,255));
\r
2242 //TimeTaker //timer10("//timer10", device);
\r
2244 video::SMaterial m;
\r
2246 m.Lighting = false;
\r
2247 driver->setMaterial(m);
\r
2249 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2251 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2252 i != hilightboxes.end(); i++)
\r
2254 /*std::cout<<"hilightbox min="
\r
2255 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2257 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2259 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2265 //TimeTaker //timer11("//timer11", device);
\r
2271 guienv->drawAll();
\r
2275 TimeTaker timer("endScene", device);
\r
2276 driver->endScene();
\r
2277 endscenetime = timer.stop(true);
\r
2280 drawtime = drawtimer.stop(true);
\r
2286 static s16 lastFPS = 0;
\r
2287 //u16 fps = driver->getFPS();
\r
2288 u16 fps = (1.0/dtime_avg1);
\r
2290 if (lastFPS != fps)
\r
2292 core::stringw str = L"Minetest [";
\r
2293 str += driver->getName();
\r
2297 device->setWindowCaption(str.c_str());
\r
2303 device->yield();*/
\r
2306 } // client is deleted at this point
\r
2311 In the end, delete the Irrlicht device.
\r
2316 catch(con::PeerNotFoundException &e)
\r
2318 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2320 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2322 This is what has to be done in every thread to get suitable debug info
\r
2324 catch(std::exception &e)
\r
2326 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2327 <<e.what()<<std::endl;
\r
2332 debugstreams_deinit();
\r