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: Node cracking animation when digging
\r
176 - TODO: A way to generate new textures by combining textures
\r
177 - TODO: Mesh update to fetch cracked faces from the former
\r
179 TODO: Add server unused sector deletion settings to settings
\r
181 TODO: TOSERVER_LEAVE
\r
184 ======================================================================
\r
186 ======================================================================
\r
191 Setting this to 1 enables a special camera mode that forces
\r
192 the renderers to think that the camera statically points from
\r
193 the starting place to a static direction.
\r
195 This allows one to move around with the player and see what
\r
196 is actually drawn behind solid things and behind the player.
\r
198 #define FIELD_OF_VIEW_TEST 0
\r
200 #ifdef UNITTEST_DISABLE
\r
202 #pragma message ("Disabling unit tests")
\r
204 #warning "Disabling unit tests"
\r
206 // Disable unit tests
\r
207 #define ENABLE_TESTS 0
\r
209 // Enable unit tests
\r
210 #define ENABLE_TESTS 1
\r
214 #pragma comment(lib, "Irrlicht.lib")
\r
215 #pragma comment(lib, "jthread.lib")
\r
216 #pragma comment(lib, "zlibwapi.lib")
\r
217 // This would get rid of the console window
\r
218 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
222 #define WIN32_LEAN_AND_MEAN
\r
223 #include <windows.h>
\r
224 #define sleep_ms(x) Sleep(x)
\r
226 #include <unistd.h>
\r
227 #define sleep_ms(x) usleep(x*1000)
\r
230 #include <iostream>
\r
233 #include <jmutexautolock.h>
\r
234 #include <locale.h>
\r
235 #include "common_irrlicht.h"
\r
238 #include "player.h"
\r
241 #include "environment.h"
\r
242 #include "server.h"
\r
243 #include "client.h"
\r
244 #include "serialization.h"
\r
245 #include "constants.h"
\r
246 #include "strfnd.h"
\r
247 #include "porting.h"
\r
248 #include "guiPauseMenu.h"
\r
250 IrrlichtDevice *g_device = NULL;
\r
252 /*const char *g_content_filenames[MATERIALS_COUNT] =
\r
254 "../data/stone.png",
\r
255 "../data/grass.png",
\r
256 "../data/water.png",
\r
257 "../data/torch_on_floor.png",
\r
258 "../data/tree.png",
\r
259 "../data/leaves.png",
\r
260 "../data/grass_footsteps.png",
\r
261 "../data/mese.png",
\r
263 "../data/water.png", // CONTENT_OCEAN
\r
267 video::SMaterial g_materials[MATERIALS_COUNT];*/
\r
270 TextureCache g_texturecache;
\r
273 // All range-related stuff below is locked behind this
\r
274 JMutex g_range_mutex;
\r
276 // Blocks are viewed in this range from the player
\r
277 s16 g_viewing_range_nodes = 60;
\r
278 //s16 g_viewing_range_nodes = 0;
\r
280 // This is updated by the client's fetchBlocks routine
\r
281 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
283 // If true, the preceding value has no meaning and all blocks
\r
284 // already existing in memory are drawn
\r
285 bool g_viewing_range_all = false;
\r
287 // This is the freetime ratio imposed by the dynamic viewing
\r
288 // range changing code.
\r
289 // It is controlled by the main loop to the smallest value that
\r
290 // inhibits glitches (dtime jitter) in the main loop.
\r
291 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
296 These are loaded from the config file.
\r
299 Settings g_settings;
\r
301 // Sets default settings
\r
302 void set_default_settings()
\r
305 g_settings.setDefault("wanted_fps", "30");
\r
306 g_settings.setDefault("fps_max", "60");
\r
307 g_settings.setDefault("viewing_range_nodes_max", "300");
\r
308 g_settings.setDefault("viewing_range_nodes_min", "35");
\r
309 g_settings.setDefault("screenW", "");
\r
310 g_settings.setDefault("screenH", "");
\r
311 g_settings.setDefault("host_game", "");
\r
312 g_settings.setDefault("port", "");
\r
313 g_settings.setDefault("address", "");
\r
314 g_settings.setDefault("name", "");
\r
315 g_settings.setDefault("random_input", "false");
\r
316 g_settings.setDefault("client_delete_unused_sectors_timeout", "1200");
\r
317 g_settings.setDefault("enable_fog", "true");
\r
320 g_settings.setDefault("creative_mode", "false");
\r
321 g_settings.setDefault("heightmap_blocksize", "32");
\r
322 g_settings.setDefault("height_randmax", "constant 50.0");
\r
323 g_settings.setDefault("height_randfactor", "constant 0.6");
\r
324 g_settings.setDefault("height_base", "linear 0 0 0");
\r
325 g_settings.setDefault("plants_amount", "1.0");
\r
326 g_settings.setDefault("ravines_amount", "1.0");
\r
327 g_settings.setDefault("objectdata_interval", "0.2");
\r
328 g_settings.setDefault("active_object_range", "2");
\r
329 g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
\r
330 g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
\r
331 g_settings.setDefault("disable_water_climb", "true");
\r
332 g_settings.setDefault("endless_water", "true");
\r
333 g_settings.setDefault("max_block_send_distance", "5");
\r
334 g_settings.setDefault("max_block_generate_distance", "4");
\r
341 //u16 g_selected_material = 0;
\r
342 u16 g_selected_item = 0;
\r
344 bool g_esc_pressed = false;
\r
346 std::wstring g_text_buffer;
\r
347 bool g_text_buffer_accepted = false;
\r
349 // When true, the mouse and keyboard are grabbed
\r
350 bool g_game_focused = true;
\r
357 std::ostream *dout_con_ptr = &dummyout;
\r
358 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
359 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
360 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
361 //std::ostream *dout_con_ptr = &dstream;
\r
362 //std::ostream *derr_con_ptr = &dstream;
\r
365 std::ostream *dout_server_ptr = &dstream;
\r
366 std::ostream *derr_server_ptr = &dstream;
\r
369 std::ostream *dout_client_ptr = &dstream;
\r
370 std::ostream *derr_client_ptr = &dstream;
\r
377 JMutex g_timestamp_mutex;
\r
378 //std::string g_timestamp;
\r
380 std::string getTimestamp()
\r
382 if(g_timestamp_mutex.IsInitialized()==false)
\r
384 JMutexAutoLock lock(g_timestamp_mutex);
\r
385 //return g_timestamp;
\r
386 time_t t = time(NULL);
\r
387 struct tm *tm = localtime(&t);
\r
389 strftime(cs, 20, "%H:%M:%S", tm);
\r
393 class MyEventReceiver : public IEventReceiver
\r
396 // This is the one method that we have to implement
\r
397 virtual bool OnEvent(const SEvent& event)
\r
399 // Remember whether each key is down or up
\r
400 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
402 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
404 if(event.KeyInput.PressedDown)
\r
406 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
407 if(g_game_focused == false)
\r
409 s16 key = event.KeyInput.Key;
\r
410 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
412 g_text_buffer_accepted = true;
\r
414 else if(key == irr::KEY_BACK)
\r
416 if(g_text_buffer.size() > 0)
\r
417 g_text_buffer = g_text_buffer.substr
\r
418 (0, g_text_buffer.size()-1);
\r
422 wchar_t wc = event.KeyInput.Char;
\r
424 g_text_buffer += wc;
\r
428 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
430 //TODO: Not used anymore?
\r
431 if(g_game_focused == true)
\r
433 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
434 g_esc_pressed = true;
\r
438 // Material selection
\r
439 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
441 if(g_game_focused == true)
\r
443 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
446 g_selected_item = 0;
\r
447 dstream<<DTIME<<"Selected item: "
\r
448 <<g_selected_item<<std::endl;
\r
452 // Viewing range selection
\r
453 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
456 JMutexAutoLock lock(g_range_mutex);
\r
457 if(g_viewing_range_all)
\r
459 g_viewing_range_all = false;
\r
460 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
464 g_viewing_range_all = true;
\r
465 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
469 // Print debug stacks
\r
470 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
473 dstream<<"-----------------------------------------"
\r
475 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
476 dstream<<"-----------------------------------------"
\r
478 debug_stacks_print();
\r
483 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
485 left_active = event.MouseInput.isLeftPressed();
\r
486 middle_active = event.MouseInput.isMiddlePressed();
\r
487 right_active = event.MouseInput.isRightPressed();
\r
489 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
491 leftclicked = true;
\r
493 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
495 rightclicked = true;
\r
497 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
499 leftreleased = true;
\r
501 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
503 rightreleased = true;
\r
505 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
507 /*dstream<<"event.MouseInput.Wheel="
\r
508 <<event.MouseInput.Wheel<<std::endl;*/
\r
509 if(event.MouseInput.Wheel < 0)
\r
511 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
514 g_selected_item = 0;
\r
516 else if(event.MouseInput.Wheel > 0)
\r
518 if(g_selected_item > 0)
\r
521 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
529 // This is used to check whether a key is being held down
\r
530 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
532 return keyIsDown[keyCode];
\r
537 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
538 keyIsDown[i] = false;
\r
539 leftclicked = false;
\r
540 rightclicked = false;
\r
541 leftreleased = false;
\r
542 rightreleased = false;
\r
544 left_active = false;
\r
545 middle_active = false;
\r
546 right_active = false;
\r
552 bool rightreleased;
\r
555 bool middle_active;
\r
559 // We use this array to store the current state of each key
\r
560 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
571 virtual ~InputHandler()
\r
575 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
577 virtual v2s32 getMousePos() = 0;
\r
578 virtual void setMousePos(s32 x, s32 y) = 0;
\r
580 virtual bool getLeftState() = 0;
\r
581 virtual bool getRightState() = 0;
\r
583 virtual bool getLeftClicked() = 0;
\r
584 virtual bool getRightClicked() = 0;
\r
585 virtual void resetLeftClicked() = 0;
\r
586 virtual void resetRightClicked() = 0;
\r
588 virtual bool getLeftReleased() = 0;
\r
589 virtual bool getRightReleased() = 0;
\r
590 virtual void resetLeftReleased() = 0;
\r
591 virtual void resetRightReleased() = 0;
\r
593 virtual void step(float dtime) {};
\r
595 virtual void clear() {};
\r
598 InputHandler *g_input = NULL;
\r
603 g_game_focused = true;
\r
608 g_game_focused = false;
\r
611 class RealInputHandler : public InputHandler
\r
614 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
616 m_receiver(receiver)
\r
619 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
621 return m_receiver->IsKeyDown(keyCode);
\r
623 virtual v2s32 getMousePos()
\r
625 return m_device->getCursorControl()->getPosition();
\r
627 virtual void setMousePos(s32 x, s32 y)
\r
629 m_device->getCursorControl()->setPosition(x, y);
\r
632 virtual bool getLeftState()
\r
634 return m_receiver->left_active;
\r
636 virtual bool getRightState()
\r
638 return m_receiver->right_active;
\r
641 virtual bool getLeftClicked()
\r
643 if(g_game_focused == false)
\r
645 return m_receiver->leftclicked;
\r
647 virtual bool getRightClicked()
\r
649 if(g_game_focused == false)
\r
651 return m_receiver->rightclicked;
\r
653 virtual void resetLeftClicked()
\r
655 m_receiver->leftclicked = false;
\r
657 virtual void resetRightClicked()
\r
659 m_receiver->rightclicked = false;
\r
662 virtual bool getLeftReleased()
\r
664 if(g_game_focused == false)
\r
666 return m_receiver->leftreleased;
\r
668 virtual bool getRightReleased()
\r
670 if(g_game_focused == false)
\r
672 return m_receiver->rightreleased;
\r
674 virtual void resetLeftReleased()
\r
676 m_receiver->leftreleased = false;
\r
678 virtual void resetRightReleased()
\r
680 m_receiver->rightreleased = false;
\r
685 resetRightClicked();
\r
686 resetLeftClicked();
\r
689 IrrlichtDevice *m_device;
\r
690 MyEventReceiver *m_receiver;
\r
693 class RandomInputHandler : public InputHandler
\r
696 RandomInputHandler()
\r
698 leftclicked = false;
\r
699 rightclicked = false;
\r
700 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
701 keydown[i] = false;
\r
703 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
705 return keydown[keyCode];
\r
707 virtual v2s32 getMousePos()
\r
711 virtual void setMousePos(s32 x, s32 y)
\r
713 mousepos = v2s32(x,y);
\r
716 virtual bool getLeftState()
\r
720 virtual bool getRightState()
\r
725 virtual bool getLeftClicked()
\r
727 return leftclicked;
\r
729 virtual bool getRightClicked()
\r
731 return rightclicked;
\r
733 virtual void resetLeftClicked()
\r
735 leftclicked = false;
\r
737 virtual void resetRightClicked()
\r
739 rightclicked = false;
\r
742 virtual bool getLeftReleased()
\r
746 virtual bool getRightReleased()
\r
750 virtual void resetLeftReleased()
\r
753 virtual void resetRightReleased()
\r
757 virtual void step(float dtime)
\r
760 static float counter1 = 0;
\r
764 counter1 = 0.1*Rand(1,10);
\r
765 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
766 g_selected_material++;
\r
768 g_selected_material = 0;*/
\r
769 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
772 g_selected_item = 0;
\r
776 static float counter1 = 0;
\r
780 counter1 = 0.1*Rand(1, 40);
\r
781 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
785 static float counter1 = 0;
\r
789 counter1 = 0.1*Rand(1, 40);
\r
790 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
794 static float counter1 = 0;
\r
798 counter1 = 0.1*Rand(1, 40);
\r
799 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
803 static float counter1 = 0;
\r
807 counter1 = 0.1*Rand(1, 40);
\r
808 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
812 static float counter1 = 0;
\r
816 counter1 = 0.1*Rand(1, 20);
\r
817 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
821 static float counter1 = 0;
\r
825 counter1 = 0.1*Rand(1, 30);
\r
826 leftclicked = true;
\r
830 static float counter1 = 0;
\r
834 counter1 = 0.1*Rand(1, 20);
\r
835 rightclicked = true;
\r
838 mousepos += mousespeed;
\r
841 s32 Rand(s32 min, s32 max)
\r
843 return (rand()%(max-min+1))+min;
\r
846 bool keydown[KEY_KEY_CODES_COUNT];
\r
853 void updateViewingRange(f32 frametime, Client *client)
\r
855 // Range_all messes up frametime_avg
\r
856 if(g_viewing_range_all == true)
\r
859 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
861 // Initialize to the target value
\r
862 static float frametime_avg = 1.0/wanted_fps;
\r
863 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
864 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
866 static f32 counter = 0;
\r
868 counter -= frametime;
\r
871 //counter = 1.0; //seconds
\r
872 counter = 0.5; //seconds
\r
874 //float freetime_ratio = 0.2;
\r
875 //float freetime_ratio = 0.4;
\r
876 float freetime_ratio = FREETIME_RATIO;
\r
878 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
880 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
882 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
883 + frametime_avg / frametime_wanted / 2.0;*/
\r
885 //float fraction = frametime_avg / frametime_wanted;
\r
887 static bool fraction_is_good = false;
\r
889 float fraction_good_threshold = 0.1;
\r
890 //float fraction_bad_threshold = 0.25;
\r
891 float fraction_bad_threshold = 0.1;
\r
892 float fraction_limit;
\r
893 // Use high limit if fraction is good AND the fraction would
\r
894 // lower the range. We want to keep the range fairly high.
\r
895 if(fraction_is_good && fraction > 1.0)
\r
896 fraction_limit = fraction_bad_threshold;
\r
898 fraction_limit = fraction_good_threshold;
\r
900 if(fabs(fraction - 1.0) < fraction_limit)
\r
902 fraction_is_good = true;
\r
907 fraction_is_good = false;
\r
910 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
911 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
912 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
913 <<" faction = "<<fraction<<std::endl;*/
\r
915 JMutexAutoLock lock(g_range_mutex);
\r
917 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
918 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
920 s16 n = (float)g_viewing_range_nodes / fraction;
\r
921 if(n < viewing_range_nodes_min)
\r
922 n = viewing_range_nodes_min;
\r
923 if(n > viewing_range_nodes_max)
\r
924 n = viewing_range_nodes_max;
\r
926 bool can_change = true;
\r
928 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
929 can_change = false;
\r
932 g_viewing_range_nodes = n;
\r
934 /*dstream<<"g_viewing_range_nodes = "
\r
935 <<g_viewing_range_nodes<<std::endl;*/
\r
938 class GUIQuickInventory : public IEventReceiver
\r
942 gui::IGUIEnvironment* env,
\r
943 gui::IGUIElement* parent,
\r
946 Inventory *inventory):
\r
947 m_itemcount(itemcount),
\r
948 m_inventory(inventory)
\r
950 core::rect<s32> imgsize(0,0,48,48);
\r
951 core::rect<s32> textsize(0,0,48,16);
\r
952 v2s32 spacing(0, 64);
\r
953 for(s32 i=0; i<m_itemcount; i++)
\r
955 m_images.push_back(env->addImage(
\r
956 imgsize + pos + spacing*i
\r
958 m_images[i]->setScaleImage(true);
\r
959 m_texts.push_back(env->addStaticText(
\r
961 textsize + pos + spacing*i,
\r
964 m_texts[i]->setBackgroundColor(
\r
965 video::SColor(128,0,0,0));
\r
966 m_texts[i]->setTextAlignment(
\r
968 gui::EGUIA_UPPERLEFT);
\r
972 virtual bool OnEvent(const SEvent& event)
\r
977 void setSelection(s32 i)
\r
986 start = m_selection - m_itemcount / 2;
\r
988 for(s32 i=0; i<m_itemcount; i++)
\r
992 if(j > (s32)m_inventory->getSize() - 1)
\r
993 j -= m_inventory->getSize();
\r
995 j += m_inventory->getSize();
\r
997 InventoryItem *item = m_inventory->getItem(j);
\r
1001 m_images[i]->setImage(NULL);
\r
1004 if(m_selection == j)
\r
1005 swprintf(t, 10, L"<-");
\r
1007 swprintf(t, 10, L"");
\r
1008 m_texts[i]->setText(t);
\r
1010 // The next ifs will segfault with a NULL pointer
\r
1015 m_images[i]->setImage(item->getImage());
\r
1018 if(m_selection == j)
\r
1019 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1021 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1022 m_texts[i]->setText(t);
\r
1028 core::array<gui::IGUIStaticText*> m_texts;
\r
1029 core::array<gui::IGUIImage*> m_images;
\r
1030 Inventory *m_inventory;
\r
1034 int main(int argc, char *argv[])
\r
1037 Low-level initialization
\r
1040 bool disable_stderr = false;
\r
1042 disable_stderr = true;
\r
1045 // Initialize debug streams
\r
1046 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1047 // Initialize debug stacks
\r
1048 debug_stacks_init();
\r
1050 DSTACK(__FUNCTION_NAME);
\r
1056 Parse command line
\r
1059 // List all allowed options
\r
1060 core::map<std::string, ValueSpec> allowed_options;
\r
1061 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1062 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1063 "Run server directly"));
\r
1064 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1065 "Load configuration from specified file"));
\r
1066 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1067 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1068 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1069 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1070 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1072 Settings cmd_args;
\r
1074 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1076 if(ret == false || cmd_args.getFlag("help"))
\r
1078 dstream<<"Allowed options:"<<std::endl;
\r
1079 for(core::map<std::string, ValueSpec>::Iterator
\r
1080 i = allowed_options.getIterator();
\r
1081 i.atEnd() == false; i++)
\r
1083 dstream<<" --"<<i.getNode()->getKey();
\r
1084 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1089 dstream<<" <value>";
\r
1091 dstream<<std::endl;
\r
1093 if(i.getNode()->getValue().help != NULL)
\r
1095 dstream<<" "<<i.getNode()->getValue().help
\r
1100 return cmd_args.getFlag("help") ? 0 : 1;
\r
1105 Basic initialization
\r
1108 // Initialize default settings
\r
1109 set_default_settings();
\r
1111 // Print startup message
\r
1112 dstream<<DTIME<<"minetest-c55"
\r
1113 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1114 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1117 // Set locale. This is for forcing '.' as the decimal point.
\r
1118 std::locale::global(std::locale("C"));
\r
1119 // This enables printing all characters in bitmap font
\r
1120 setlocale(LC_CTYPE, "en_US");
\r
1122 // Initialize sockets
\r
1124 atexit(sockets_cleanup);
\r
1126 // Initialize timestamp mutex
\r
1127 g_timestamp_mutex.Init();
\r
1137 // Path of configuration file in use
\r
1138 std::string configpath = "";
\r
1140 if(cmd_args.exists("config"))
\r
1142 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1145 dstream<<"Could not read configuration from \""
\r
1146 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1149 configpath = cmd_args.get("config");
\r
1153 const char *filenames[2] =
\r
1155 "../minetest.conf",
\r
1156 "../../minetest.conf"
\r
1159 for(u32 i=0; i<2; i++)
\r
1161 bool r = g_settings.readConfigFile(filenames[i]);
\r
1164 configpath = filenames[i];
\r
1170 // Initialize random seed
\r
1176 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1177 || cmd_args.getFlag("enable-unittests") == true)
\r
1183 Global range mutex
\r
1185 g_range_mutex.Init();
\r
1186 assert(g_range_mutex.IsInitialized());
\r
1188 // Read map parameters from settings
\r
1190 HMParams hm_params;
\r
1191 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1192 hm_params.randmax = g_settings.get("height_randmax");
\r
1193 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1194 hm_params.base = g_settings.get("height_base");
\r
1196 MapParams map_params;
\r
1197 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1198 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1204 std::cout<<std::endl<<std::endl;
\r
1207 <<" .__ __ __ "<<std::endl
\r
1208 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1209 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1210 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1211 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1212 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1214 <<"Now with more waterish water!"
\r
1217 std::cout<<std::endl;
\r
1218 char templine[100];
\r
1222 if(cmd_args.exists("port"))
\r
1224 port = cmd_args.getU16("port");
\r
1228 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1229 std::cout<<"-> "<<port<<std::endl;
\r
1232 if(cmd_args.getFlag("server"))
\r
1234 DSTACK("Dedicated server branch");
\r
1236 std::cout<<std::endl;
\r
1237 std::cout<<"========================"<<std::endl;
\r
1238 std::cout<<"Running dedicated server"<<std::endl;
\r
1239 std::cout<<"========================"<<std::endl;
\r
1240 std::cout<<std::endl;
\r
1242 Server server("../map", hm_params, map_params);
\r
1243 server.start(port);
\r
1247 // This is kind of a hack but can be done like this
\r
1248 // because server.step() is very light
\r
1250 server.step(0.030);
\r
1252 static int counter = 0;
\r
1258 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1259 core::list<PlayerInfo>::Iterator i;
\r
1260 static u32 sum_old = 0;
\r
1261 u32 sum = PIChecksum(list);
\r
1262 if(sum != sum_old)
\r
1264 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1265 for(i=list.begin(); i!=list.end(); i++)
\r
1267 i->PrintLine(&std::cout);
\r
1277 bool hosting = false;
\r
1278 char connect_name[100] = "";
\r
1280 if(cmd_args.exists("address"))
\r
1282 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1284 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1286 std::cout<<g_settings.get("address")<<std::endl;
\r
1287 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1291 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1292 std::cin.getline(connect_name, 100);
\r
1295 if(connect_name[0] == 0){
\r
1296 snprintf(connect_name, 100, "127.0.0.1");
\r
1301 std::cout<<"> Hosting game"<<std::endl;
\r
1303 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1305 char playername[PLAYERNAME_SIZE] = "";
\r
1306 if(g_settings.get("name") != "")
\r
1308 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1312 std::cout<<"Name of player: ";
\r
1313 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1315 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1318 Resolution selection
\r
1323 bool fullscreen = false;
\r
1325 if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")
\r
1327 screenW = atoi(g_settings.get("screenW").c_str());
\r
1328 screenH = atoi(g_settings.get("screenH").c_str());
\r
1332 u16 resolutions[][3] = {
\r
1333 //W, H, fullscreen
\r
1344 u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
\r
1346 for(u16 i=0; i<res_count; i++)
\r
1348 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1349 <<resolutions[i][1];
\r
1350 if(resolutions[i][2])
\r
1351 std::cout<<" fullscreen"<<std::endl;
\r
1353 std::cout<<" windowed"<<std::endl;
\r
1355 std::cout<<"Select a window resolution number [empty = 2]: ";
\r
1356 std::cin.getline(templine, 100);
\r
1359 if(templine[0] == 0)
\r
1362 r0 = atoi(templine);
\r
1364 if(r0 > res_count || r0 == 0)
\r
1370 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1371 <<resolutions[i][1];
\r
1372 if(resolutions[i][2])
\r
1373 std::cout<<" fullscreen"<<std::endl;
\r
1375 std::cout<<" windowed"<<std::endl;
\r
1378 screenW = resolutions[r0-1][0];
\r
1379 screenH = resolutions[r0-1][1];
\r
1380 fullscreen = resolutions[r0-1][2];
\r
1385 MyEventReceiver receiver;
\r
1387 video::E_DRIVER_TYPE driverType;
\r
1390 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1391 driverType = video::EDT_OPENGL;
\r
1393 driverType = video::EDT_OPENGL;
\r
1396 // create device and exit if creation failed
\r
1398 IrrlichtDevice *device;
\r
1399 device = createDevice(driverType,
\r
1400 core::dimension2d<u32>(screenW, screenH),
\r
1401 16, fullscreen, false, false, &receiver);
\r
1403 /*device = createDevice(driverType,
\r
1404 core::dimension2d<u32>(screenW, screenH),
\r
1405 16, fullscreen, false, true, &receiver);*/
\r
1408 return 1; // could not create selected driver.
\r
1410 g_device = device;
\r
1412 device->setResizable(true);
\r
1414 bool random_input = g_settings.getBool("random_input")
\r
1415 || cmd_args.getFlag("random-input");
\r
1417 g_input = new RandomInputHandler();
\r
1419 g_input = new RealInputHandler(device, &receiver);
\r
1422 Continue initialization
\r
1425 video::IVideoDriver* driver = device->getVideoDriver();
\r
1426 // These make the textures not to show at all
\r
1427 //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
\r
1428 //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );
\r
1430 //driver->setMinHardwareBufferVertexCount(1);
\r
1432 scene::ISceneManager* smgr = device->getSceneManager();
\r
1435 guiPauseMenu pauseMenu(device, &receiver);
\r
1437 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1438 gui::IGUISkin* skin = guienv->getSkin();
\r
1439 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1441 skin->setFont(font);
\r
1442 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1443 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1444 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1445 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1446 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1447 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1449 const wchar_t *text = L"Loading and connecting...";
\r
1450 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1451 core::dimension2d<u32> textd = font->getDimension(text);
\r
1452 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1453 // Have to add a bit to disable the text from word wrapping
\r
1454 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1455 core::vector2d<s32> textsize(300, textd.Height);
\r
1456 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1458 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1459 text, textrect, false, false);
\r
1460 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1462 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1463 guienv->drawAll();
\r
1464 driver->endScene();
\r
1467 Preload some random textures that are used in threads
\r
1470 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1471 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1472 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1475 Load tile textures
\r
1477 for(s32 i=0; i<TILES_COUNT; i++)
\r
1479 if(g_tile_texture_names[i] == NULL)
\r
1481 std::string name = g_tile_texture_names[i];
\r
1482 std::string filename;
\r
1483 filename += "../data/";
\r
1485 filename += ".png";
\r
1486 g_texturecache.set(name, driver->getTexture(filename.c_str()));
\r
1489 tile_materials_preload(g_texturecache);
\r
1492 Make a scope here for the client so that it gets removed
\r
1493 before the irrlicht device
\r
1497 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1502 SharedPtr<Server> server;
\r
1504 server = new Server("../map", hm_params, map_params);
\r
1505 server->start(port);
\r
1512 Client client(device, playername);
\r
1514 Address connect_address(0,0,0,0, port);
\r
1516 connect_address.Resolve(connect_name);
\r
1518 catch(ResolveError &e)
\r
1520 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1524 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1525 client.connect(connect_address);
\r
1528 while(client.connectedAndInitialized() == false)
\r
1531 if(server != NULL){
\r
1532 server->step(0.1);
\r
1537 catch(con::PeerNotFoundException &e)
\r
1539 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1546 /*scene::ISceneNode* skybox;
\r
1547 skybox = smgr->addSkyBoxSceneNode(
\r
1548 driver->getTexture("../data/skybox2.png"),
\r
1549 driver->getTexture("../data/skybox3.png"),
\r
1550 driver->getTexture("../data/skybox1.png"),
\r
1551 driver->getTexture("../data/skybox1.png"),
\r
1552 driver->getTexture("../data/skybox1.png"),
\r
1553 driver->getTexture("../data/skybox1.png"));*/
\r
1556 Create the camera node
\r
1559 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1560 0, // Camera parent
\r
1561 v3f(BS*100, BS*2, BS*100), // Look from
\r
1562 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1566 if(camera == NULL)
\r
1569 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1571 camera->setFOV(FOV_ANGLE);
\r
1573 // Just so big a value that everything rendered is visible
\r
1574 camera->setFarValue(100000*BS);
\r
1576 f32 camera_yaw = 0; // "right/left"
\r
1577 f32 camera_pitch = 0; // "up/down"
\r
1583 gui_loadingtext->remove();
\r
1585 pauseMenu.setVisible(true);
\r
1588 Add some gui stuff
\r
1591 // First line of debug text
\r
1592 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1594 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1596 // Second line of debug text
\r
1597 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1599 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1602 // At the middle of the screen
\r
1603 // Object infos are shown in this
\r
1604 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1606 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1609 // This is a copy of the inventory that the client's environment has
\r
1610 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1612 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1613 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1616 Some statistics are collected in these
\r
1619 u32 beginscenetime = 0;
\r
1620 u32 scenetime = 0;
\r
1621 u32 endscenetime = 0;
\r
1629 virtual void sendText(std::string text) = 0;
\r
1632 struct TextDestSign : public TextDest
\r
1634 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1636 m_blockpos = blockpos;
\r
1638 m_client = client;
\r
1640 void sendText(std::string text)
\r
1642 dstream<<"Changing text of a sign object: "
\r
1643 <<text<<std::endl;
\r
1644 m_client->sendSignText(m_blockpos, m_id, text);
\r
1652 TextDest *textbuf_dest = NULL;
\r
1654 //gui::IGUIWindow* input_window = NULL;
\r
1655 gui::IGUIStaticText* input_guitext = NULL;
\r
1666 bool first_loop_after_window_activation = true;
\r
1668 // Time is in milliseconds
\r
1669 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1670 // NOTE: Have to call run() between calls of this to update the timer
\r
1671 u32 lasttime = device->getTimer()->getTime();
\r
1673 while(device->run())
\r
1676 Random calculations
\r
1678 v2u32 screensize = driver->getScreenSize();
\r
1679 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1681 // Hilight boxes collected during the loop and displayed
\r
1682 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1685 std::wstring infotext;
\r
1687 //TimeTaker //timer1("//timer1", device);
\r
1689 // Time of frame without fps limit
\r
1693 // not using getRealTime is necessary for wine
\r
1694 u32 time = device->getTimer()->getTime();
\r
1695 if(time > lasttime)
\r
1696 busytime_u32 = time - lasttime;
\r
1699 busytime = busytime_u32 / 1000.0;
\r
1702 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1704 // Absolutelu necessary for wine!
\r
1711 //updateViewingRange(dtime, &client);
\r
1712 updateViewingRange(busytime, &client);
\r
1719 float fps_max = g_settings.getFloat("fps_max");
\r
1720 u32 frametime_min = 1000./fps_max;
\r
1722 if(busytime_u32 < frametime_min)
\r
1724 u32 sleeptime = frametime_min - busytime_u32;
\r
1725 device->sleep(sleeptime);
\r
1729 // Absolutelu necessary for wine!
\r
1733 Time difference calculation
\r
1735 f32 dtime; // in seconds
\r
1737 u32 time = device->getTimer()->getTime();
\r
1738 if(time > lasttime)
\r
1739 dtime = (time - lasttime) / 1000.0;
\r
1745 Time average and jitter calculation
\r
1748 static f32 dtime_avg1 = 0.0;
\r
1749 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1750 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1752 static f32 dtime_jitter1_max_sample = 0.0;
\r
1753 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1755 static f32 jitter1_max = 0.0;
\r
1756 static f32 counter = 0.0;
\r
1757 if(dtime_jitter1 > jitter1_max)
\r
1758 jitter1_max = dtime_jitter1;
\r
1763 dtime_jitter1_max_sample = jitter1_max;
\r
1764 dtime_jitter1_max_fraction
\r
1765 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1766 jitter1_max = 0.0;
\r
1769 Control freetime ratio
\r
1771 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1773 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1774 g_freetime_ratio += 0.01;
\r
1778 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1779 g_freetime_ratio -= 0.01;
\r
1785 Busytime average and jitter calculation
\r
1788 static f32 busytime_avg1 = 0.0;
\r
1789 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1790 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1792 static f32 busytime_jitter1_max_sample = 0.0;
\r
1793 static f32 busytime_jitter1_min_sample = 0.0;
\r
1795 static f32 jitter1_max = 0.0;
\r
1796 static f32 jitter1_min = 0.0;
\r
1797 static f32 counter = 0.0;
\r
1798 if(busytime_jitter1 > jitter1_max)
\r
1799 jitter1_max = busytime_jitter1;
\r
1800 if(busytime_jitter1 < jitter1_min)
\r
1801 jitter1_min = busytime_jitter1;
\r
1803 if(counter > 0.0){
\r
1805 busytime_jitter1_max_sample = jitter1_max;
\r
1806 busytime_jitter1_min_sample = jitter1_min;
\r
1807 jitter1_max = 0.0;
\r
1808 jitter1_min = 0.0;
\r
1813 Debug info for client
\r
1816 static float counter = 0.0;
\r
1821 client.printDebugInfo(std::cout);
\r
1826 Input handler step()
\r
1828 g_input->step(dtime);
\r
1833 /*if(g_esc_pressed)
\r
1839 Player speed control
\r
1842 if(g_game_focused)
\r
1849 bool a_superspeed,
\r
1852 PlayerControl control(
\r
1853 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1854 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1855 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1856 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1857 g_input->isKeyDown(irr::KEY_SPACE),
\r
1858 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1862 client.setPlayerControl(control);
\r
1866 // Set every key to inactive
\r
1867 PlayerControl control;
\r
1868 client.setPlayerControl(control);
\r
1873 Process environment
\r
1877 //TimeTaker timer("client.step(dtime)", device);
\r
1878 client.step(dtime);
\r
1879 //client.step(dtime_avg1);
\r
1882 if(server != NULL)
\r
1884 //TimeTaker timer("server->step(dtime)", device);
\r
1885 server->step(dtime);
\r
1888 v3f player_position = client.getPlayerPosition();
\r
1890 //TimeTaker //timer2("//timer2", device);
\r
1893 Mouse and camera control
\r
1896 if((device->isWindowActive() && g_game_focused && !pauseMenu.isVisible())
\r
1900 device->getCursorControl()->setVisible(false);
\r
1902 if(first_loop_after_window_activation){
\r
1903 //std::cout<<"window active, first loop"<<std::endl;
\r
1904 first_loop_after_window_activation = false;
\r
1907 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1908 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1909 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1910 camera_yaw -= dx*0.2;
\r
1911 camera_pitch += dy*0.2;
\r
1912 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1913 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1915 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1918 device->getCursorControl()->setVisible(true);
\r
1920 //std::cout<<"window inactive"<<std::endl;
\r
1921 first_loop_after_window_activation = true;
\r
1924 camera_yaw = wrapDegrees(camera_yaw);
\r
1925 camera_pitch = wrapDegrees(camera_pitch);
\r
1927 v3f camera_direction = v3f(0,0,1);
\r
1928 camera_direction.rotateYZBy(camera_pitch);
\r
1929 camera_direction.rotateXZBy(camera_yaw);
\r
1931 v3f camera_position =
\r
1932 player_position + v3f(0, BS+BS/2, 0);
\r
1934 camera->setPosition(camera_position);
\r
1935 // *100.0 helps in large map coordinates
\r
1936 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1938 if(FIELD_OF_VIEW_TEST){
\r
1939 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1940 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1943 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1944 //TimeTaker timer("client.updateCamera", device);
\r
1945 client.updateCamera(camera_position, camera_direction);
\r
1949 //TimeTaker //timer3("//timer3", device);
\r
1952 Calculate what block is the crosshair pointing to
\r
1955 //u32 t1 = device->getTimer()->getRealTime();
\r
1957 //f32 d = 4; // max. distance
\r
1958 f32 d = 4; // max. distance
\r
1959 core::line3d<f32> shootline(camera_position,
\r
1960 camera_position + camera_direction * BS * (d+1));
\r
1962 MapBlockObject *selected_object = client.getSelectedObject
\r
1963 (d*BS, camera_position, shootline);
\r
1965 if(selected_object != NULL)
\r
1967 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1969 core::aabbox3d<f32> box_on_map
\r
1970 = selected_object->getSelectionBoxOnMap();
\r
1972 hilightboxes.push_back(box_on_map);
\r
1974 infotext = narrow_to_wide(selected_object->infoText());
\r
1976 if(g_input->getLeftClicked())
\r
1978 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1979 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1980 selected_object->getId(), g_selected_item);
\r
1982 else if(g_input->getRightClicked())
\r
1984 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1986 Check if we want to modify the object ourselves
\r
1988 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1990 dstream<<"Sign object right-clicked"<<std::endl;
\r
1994 input_guitext = guienv->addStaticText(L"",
\r
1995 core::rect<s32>(150,100,350,120),
\r
1997 false, // wordwrap?
\r
2000 input_guitext->setDrawBackground(true);
\r
2004 g_text_buffer = L"ASD LOL 8)";
\r
2005 g_text_buffer_accepted = true;
\r
2009 g_text_buffer = L"";
\r
2010 g_text_buffer_accepted = false;
\r
2013 textbuf_dest = new TextDestSign(
\r
2014 selected_object->getBlock()->getPos(),
\r
2015 selected_object->getId(),
\r
2019 Otherwise pass the event to the server as-is
\r
2023 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2024 selected_object->getId(), g_selected_item);
\r
2028 else // selected_object == NULL
\r
2032 Find out which node we are pointing at
\r
2035 bool nodefound = false;
\r
2037 v3s16 neighbourpos;
\r
2038 core::aabbox3d<f32> nodefacebox;
\r
2039 f32 mindistance = BS * 1001;
\r
2041 v3s16 pos_i = floatToInt(player_position);
\r
2043 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2047 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2048 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2049 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2050 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2051 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2052 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2054 for(s16 y = ystart; y <= yend; y++)
\r
2055 for(s16 z = zstart; z <= zend; z++)
\r
2056 for(s16 x = xstart; x <= xend; x++)
\r
2061 n = client.getNode(v3s16(x,y,z));
\r
2062 if(content_pointable(n.d) == false)
\r
2065 catch(InvalidPositionException &e)
\r
2071 v3f npf = intToFloat(np);
\r
2076 v3s16(0,0,1), // back
\r
2077 v3s16(0,1,0), // top
\r
2078 v3s16(1,0,0), // right
\r
2079 v3s16(0,0,-1), // front
\r
2080 v3s16(0,-1,0), // bottom
\r
2081 v3s16(-1,0,0), // left
\r
2087 if(n.d == CONTENT_TORCH)
\r
2089 v3s16 dir = unpackDir(n.dir);
\r
2090 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2091 dir_f *= BS/2 - BS/6 - BS/20;
\r
2092 v3f cpf = npf + dir_f;
\r
2093 f32 distance = (cpf - camera_position).getLength();
\r
2095 core::aabbox3d<f32> box;
\r
2098 if(dir == v3s16(0,-1,0))
\r
2100 box = core::aabbox3d<f32>(
\r
2101 npf - v3f(BS/6, BS/2, BS/6),
\r
2102 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2106 else if(dir == v3s16(0,1,0))
\r
2108 box = core::aabbox3d<f32>(
\r
2109 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2110 npf + v3f(BS/6, BS/2, BS/6)
\r
2116 box = core::aabbox3d<f32>(
\r
2117 cpf - v3f(BS/6, BS/3, BS/6),
\r
2118 cpf + v3f(BS/6, BS/3, BS/6)
\r
2122 if(distance < mindistance)
\r
2124 if(box.intersectsWithLine(shootline))
\r
2128 neighbourpos = np;
\r
2129 mindistance = distance;
\r
2130 nodefacebox = box;
\r
2139 for(u16 i=0; i<6; i++)
\r
2141 v3f dir_f = v3f(dirs[i].X,
\r
2142 dirs[i].Y, dirs[i].Z);
\r
2143 v3f centerpoint = npf + dir_f * BS/2;
\r
2145 (centerpoint - camera_position).getLength();
\r
2147 if(distance < mindistance)
\r
2149 core::CMatrix4<f32> m;
\r
2150 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2152 // This is the back face
\r
2153 v3f corners[2] = {
\r
2154 v3f(BS/2, BS/2, BS/2),
\r
2155 v3f(-BS/2, -BS/2, BS/2+d)
\r
2158 for(u16 j=0; j<2; j++)
\r
2160 m.rotateVect(corners[j]);
\r
2161 corners[j] += npf;
\r
2164 core::aabbox3d<f32> facebox(corners[0]);
\r
2165 facebox.addInternalPoint(corners[1]);
\r
2167 if(facebox.intersectsWithLine(shootline))
\r
2171 neighbourpos = np + dirs[i];
\r
2172 mindistance = distance;
\r
2173 nodefacebox = facebox;
\r
2175 } // if distance < mindistance
\r
2177 } // regular block
\r
2180 /*static v3s16 oldnodepos;
\r
2181 static bool oldnodefound = false;*/
\r
2185 //std::cout<<DTIME<<"nodefound == true"<<std::endl;
\r
2186 //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2187 //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
\r
2189 static v3s16 nodepos_old(-1,-1,-1);
\r
2190 if(nodepos != nodepos_old){
\r
2191 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2192 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2195 hilightboxes.push_back(nodefacebox);
\r
2197 //if(g_input->getLeftClicked())
\r
2198 if(g_input->getLeftClicked() ||
\r
2199 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2201 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2202 client.pressGround(0, nodepos, neighbourpos, g_selected_item);
\r
2204 if(g_input->getRightClicked())
\r
2205 /*if(g_input->getRightClicked() ||
\r
2206 (g_input->getRightState() && nodepos != nodepos_old))*/
\r
2208 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2209 client.pressGround(1, nodepos, neighbourpos, g_selected_item);
\r
2212 nodepos_old = nodepos;
\r
2215 //std::cout<<DTIME<<"nodefound == false"<<std::endl;
\r
2216 //positiontextgui->setText(L"");
\r
2219 /*oldnodefound = nodefound;
\r
2220 oldnodepos = nodepos;*/
\r
2222 } // selected_object == NULL
\r
2224 g_input->resetLeftClicked();
\r
2225 g_input->resetRightClicked();
\r
2227 if(g_input->getLeftReleased())
\r
2229 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2230 client.stopDigging();
\r
2232 if(g_input->getRightReleased())
\r
2234 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2238 g_input->resetLeftReleased();
\r
2239 g_input->resetRightReleased();
\r
2242 Calculate stuff for drawing
\r
2245 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2247 // Background color is choosen based on whether the player is
\r
2248 // much beyond the initial ground level
\r
2249 /*video::SColor bgcolor;
\r
2250 v3s16 p0 = Map::floatToInt(player_position);
\r
2251 // Does this make short random delays?
\r
2252 // NOTE: no need for this, sky doesn't show underground with
\r
2254 bool is_underground = client.isNodeUnderground(p0);
\r
2255 //bool is_underground = false;
\r
2256 if(is_underground == false)
\r
2257 bgcolor = video::SColor(255,90,140,200);
\r
2259 bgcolor = video::SColor(255,0,0,0);*/
\r
2261 //video::SColor bgcolor = video::SColor(255,90,140,200);
\r
2262 //video::SColor bgcolor = skycolor;
\r
2264 //s32 daynight_i = client.getDayNightIndex();
\r
2265 //video::SColor bgcolor = skycolor[daynight_i];
\r
2267 u32 daynight_ratio = client.getDayNightRatio();
\r
2268 video::SColor bgcolor = video::SColor(
\r
2270 skycolor.getRed() * daynight_ratio / 1000,
\r
2271 skycolor.getGreen() * daynight_ratio / 1000,
\r
2272 skycolor.getBlue() * daynight_ratio / 1000);
\r
2278 if(g_settings.getBool("enable_fog") == true)
\r
2280 f32 range = g_viewing_range_nodes * BS;
\r
2281 if(g_viewing_range_all)
\r
2282 range = 100000*BS;
\r
2286 video::EFT_FOG_LINEAR,
\r
2290 false, // pixel fog
\r
2291 false // range fog
\r
2297 Update gui stuff (0ms)
\r
2300 //TimeTaker guiupdatetimer("Gui updating", device);
\r
2303 wchar_t temptext[150];
\r
2305 static float drawtime_avg = 0;
\r
2306 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2307 static float beginscenetime_avg = 0;
\r
2308 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2309 static float scenetime_avg = 0;
\r
2310 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2311 static float endscenetime_avg = 0;
\r
2312 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2314 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2316 L", R: range_all=%i"
\r
2318 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2320 g_viewing_range_all,
\r
2322 beginscenetime_avg,
\r
2327 guitext->setText(temptext);
\r
2331 wchar_t temptext[150];
\r
2332 swprintf(temptext, 150,
\r
2333 L"(% .1f, % .1f, % .1f)"
\r
2334 L" (% .3f < btime_jitter < % .3f"
\r
2335 L", dtime_jitter = % .1f %%)",
\r
2336 player_position.X/BS,
\r
2337 player_position.Y/BS,
\r
2338 player_position.Z/BS,
\r
2339 busytime_jitter1_min_sample,
\r
2340 busytime_jitter1_max_sample,
\r
2341 dtime_jitter1_max_fraction * 100.0
\r
2344 guitext2->setText(temptext);
\r
2348 /*wchar_t temptext[100];
\r
2349 swprintf(temptext, 100,
\r
2350 SWPRINTF_CHARSTRING,
\r
2351 infotext.substr(0,99).c_str()
\r
2354 guitext_info->setText(temptext);*/
\r
2356 guitext_info->setText(infotext.c_str());
\r
2363 static u16 old_selected_item = 65535;
\r
2364 if(client.getLocalInventoryUpdated()
\r
2365 || g_selected_item != old_selected_item)
\r
2367 old_selected_item = g_selected_item;
\r
2368 //std::cout<<"Updating local inventory"<<std::endl;
\r
2369 client.getLocalInventory(local_inventory);
\r
2370 quick_inventory->setSelection(g_selected_item);
\r
2371 quick_inventory->update();
\r
2374 if(input_guitext != NULL)
\r
2376 /*wchar_t temptext[100];
\r
2377 swprintf(temptext, 100,
\r
2378 SWPRINTF_CHARSTRING,
\r
2379 g_text_buffer.substr(0,99).c_str()
\r
2381 input_guitext->setText(g_text_buffer.c_str());
\r
2387 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2389 input_guitext->remove();
\r
2390 input_guitext = NULL;
\r
2392 if(textbuf_dest != NULL)
\r
2394 std::string text = wide_to_narrow(g_text_buffer);
\r
2395 dstream<<"Sending text: "<<text<<std::endl;
\r
2396 textbuf_dest->sendText(text);
\r
2397 delete textbuf_dest;
\r
2398 textbuf_dest = NULL;
\r
2404 //guiupdatetimer.stop();
\r
2410 TimeTaker drawtimer("Drawing", device);
\r
2414 TimeTaker timer("beginScene", device);
\r
2415 driver->beginScene(true, true, bgcolor);
\r
2416 //driver->beginScene(false, true, bgcolor);
\r
2417 beginscenetime = timer.stop(true);
\r
2422 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2425 TimeTaker timer("smgr", device);
\r
2427 scenetime = timer.stop(true);
\r
2431 //TimeTaker timer9("auxiliary drawings", device);
\r
2434 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2435 displaycenter + core::vector2d<s32>(10,0),
\r
2436 video::SColor(255,255,255,255));
\r
2437 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2438 displaycenter + core::vector2d<s32>(0,10),
\r
2439 video::SColor(255,255,255,255));
\r
2442 //TimeTaker //timer10("//timer10", device);
\r
2444 video::SMaterial m;
\r
2446 m.Lighting = false;
\r
2447 driver->setMaterial(m);
\r
2449 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2451 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2452 i != hilightboxes.end(); i++)
\r
2454 /*std::cout<<"hilightbox min="
\r
2455 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2457 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2459 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2465 //TimeTaker //timer11("//timer11", device);
\r
2471 guienv->drawAll();
\r
2475 TimeTaker timer("endScene", device);
\r
2476 driver->endScene();
\r
2477 endscenetime = timer.stop(true);
\r
2480 drawtime = drawtimer.stop(true);
\r
2486 static s16 lastFPS = 0;
\r
2487 //u16 fps = driver->getFPS();
\r
2488 u16 fps = (1.0/dtime_avg1);
\r
2490 if (lastFPS != fps)
\r
2492 core::stringw str = L"Minetest [";
\r
2493 str += driver->getName();
\r
2497 device->setWindowCaption(str.c_str());
\r
2503 device->yield();*/
\r
2506 delete quick_inventory;
\r
2508 } // client is deleted at this point
\r
2513 In the end, delete the Irrlicht device.
\r
2518 Update configuration file
\r
2520 if(configpath != "")
\r
2522 g_settings.updateConfigFile(configpath.c_str());
\r
2526 catch(con::PeerNotFoundException &e)
\r
2528 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2530 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2532 This is what has to be done in every thread to get suitable debug info
\r
2534 catch(std::exception &e)
\r
2536 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2537 <<e.what()<<std::endl;
\r
2542 debugstreams_deinit();
\r