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
176 SUGG: MovingObject::move and Player::move are basically the same.
\r
179 TODO: Transfer sign texts as metadata of block and not as data of
\r
182 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
183 if something is already in it)
\r
184 - TODO: Use it in active block queue in water flowing
\r
186 TODO: Proper looking torches.
\r
187 - Signs could be done in the same way?
\r
190 ======================================================================
\r
192 TODO: A system for showing some nodes in some other way than cubes
\r
193 - Needed for torches
\r
194 - Also for signs, stairs, etc
\r
196 ======================================================================
\r
201 Setting this to 1 enables a special camera mode that forces
\r
202 the renderers to think that the camera statically points from
\r
203 the starting place to a static direction.
\r
205 This allows one to move around with the player and see what
\r
206 is actually drawn behind solid things and behind the player.
\r
208 #define FIELD_OF_VIEW_TEST 0
\r
210 #ifdef UNITTEST_DISABLE
\r
212 #pragma message ("Disabling unit tests")
\r
214 #warning "Disabling unit tests"
\r
216 // Disable unit tests
\r
217 #define ENABLE_TESTS 0
\r
219 // Enable unit tests
\r
220 #define ENABLE_TESTS 1
\r
224 #pragma comment(lib, "Irrlicht.lib")
\r
225 #pragma comment(lib, "jthread.lib")
\r
226 // This would get rid of the console window
\r
227 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
231 #define WIN32_LEAN_AND_MEAN
\r
232 #include <windows.h>
\r
233 #define sleep_ms(x) Sleep(x)
\r
235 #include <unistd.h>
\r
236 #define sleep_ms(x) usleep(x*1000)
\r
239 #include <iostream>
\r
242 #include <jmutexautolock.h>
\r
243 #include "common_irrlicht.h"
\r
246 #include "player.h"
\r
249 #include "environment.h"
\r
250 #include "server.h"
\r
251 #include "client.h"
\r
252 #include "serialization.h"
\r
253 #include "constants.h"
\r
254 #include "strfnd.h"
\r
255 #include "porting.h"
\r
256 #include <locale.h>
\r
258 IrrlichtDevice *g_device = NULL;
\r
260 const char *g_material_filenames[MATERIALS_COUNT] =
\r
262 "../data/stone.png",
\r
263 "../data/grass.png",
\r
264 "../data/water.png",
\r
265 "../data/light.png",
\r
266 "../data/tree.png",
\r
267 "../data/leaves.png",
\r
268 "../data/grass_footsteps.png",
\r
269 "../data/mese.png",
\r
271 "../data/water.png", // ocean
\r
274 video::SMaterial g_materials[MATERIALS_COUNT];
\r
275 //video::SMaterial g_mesh_materials[3];
\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", "5");
\r
326 g_settings.set("creative_mode", "false");
\r
327 g_settings.set("heightmap_blocksize", "128");
\r
328 g_settings.set("height_randmax", "constant 70.0");
\r
329 g_settings.set("height_randfactor", "constant 0.6");
\r
330 g_settings.set("height_base", "linear 0 35 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
343 //u16 g_selected_material = 0;
\r
344 u16 g_selected_item = 0;
\r
346 bool g_esc_pressed = false;
\r
348 std::wstring g_text_buffer;
\r
349 bool g_text_buffer_accepted = false;
\r
351 // When true, the mouse and keyboard are grabbed
\r
352 bool g_game_focused = true;
\r
359 std::ostream *dout_con_ptr = &dummyout;
\r
360 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
361 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
362 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
363 //std::ostream *dout_con_ptr = &dstream;
\r
364 //std::ostream *derr_con_ptr = &dstream;
\r
367 std::ostream *dout_server_ptr = &dstream;
\r
368 std::ostream *derr_server_ptr = &dstream;
\r
371 std::ostream *dout_client_ptr = &dstream;
\r
372 std::ostream *derr_client_ptr = &dstream;
\r
379 JMutex g_timestamp_mutex;
\r
380 //std::string g_timestamp;
\r
382 std::string getTimestamp()
\r
384 if(g_timestamp_mutex.IsInitialized()==false)
\r
386 JMutexAutoLock lock(g_timestamp_mutex);
\r
387 //return g_timestamp;
\r
388 time_t t = time(NULL);
\r
389 struct tm *tm = localtime(&t);
\r
391 strftime(cs, 20, "%H:%M:%S", tm);
\r
395 class MyEventReceiver : public IEventReceiver
\r
398 // This is the one method that we have to implement
\r
399 virtual bool OnEvent(const SEvent& event)
\r
401 // Remember whether each key is down or up
\r
402 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
404 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
406 if(event.KeyInput.PressedDown)
\r
408 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
409 if(g_game_focused == false)
\r
411 s16 key = event.KeyInput.Key;
\r
412 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
414 g_text_buffer_accepted = true;
\r
416 else if(key == irr::KEY_BACK)
\r
418 if(g_text_buffer.size() > 0)
\r
419 g_text_buffer = g_text_buffer.substr
\r
420 (0, g_text_buffer.size()-1);
\r
424 wchar_t wc = event.KeyInput.Char;
\r
426 g_text_buffer += wc;
\r
430 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
432 if(g_game_focused == true)
\r
434 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
435 g_esc_pressed = true;
\r
439 // Material selection
\r
440 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
442 if(g_game_focused == true)
\r
444 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
447 g_selected_item = 0;
\r
448 dstream<<DTIME<<"Selected item: "
\r
449 <<g_selected_item<<std::endl;
\r
453 // Viewing range selection
\r
454 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
457 JMutexAutoLock lock(g_range_mutex);
\r
458 if(g_viewing_range_all)
\r
460 g_viewing_range_all = false;
\r
461 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
465 g_viewing_range_all = true;
\r
466 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
470 // Print debug stacks
\r
471 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
474 dstream<<"-----------------------------------------"
\r
476 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
477 dstream<<"-----------------------------------------"
\r
479 debug_stacks_print();
\r
484 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
486 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
488 leftclicked = true;
\r
490 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
492 rightclicked = true;
\r
494 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
496 /*dstream<<"event.MouseInput.Wheel="
\r
497 <<event.MouseInput.Wheel<<std::endl;*/
\r
498 if(event.MouseInput.Wheel < 0)
\r
500 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
503 g_selected_item = 0;
\r
505 else if(event.MouseInput.Wheel > 0)
\r
507 if(g_selected_item > 0)
\r
510 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
518 // This is used to check whether a key is being held down
\r
519 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
521 return keyIsDown[keyCode];
\r
526 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
527 keyIsDown[i] = false;
\r
528 leftclicked = false;
\r
529 rightclicked = false;
\r
535 // We use this array to store the current state of each key
\r
536 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
547 virtual ~InputHandler()
\r
550 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
551 virtual v2s32 getMousePos() = 0;
\r
552 virtual void setMousePos(s32 x, s32 y) = 0;
\r
553 virtual bool getLeftClicked() = 0;
\r
554 virtual bool getRightClicked() = 0;
\r
555 virtual void resetLeftClicked() = 0;
\r
556 virtual void resetRightClicked() = 0;
\r
558 virtual void step(float dtime) {};
\r
560 virtual void clear() {};
\r
563 InputHandler *g_input = NULL;
\r
568 g_game_focused = true;
\r
573 g_game_focused = false;
\r
576 class RealInputHandler : public InputHandler
\r
579 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
581 m_receiver(receiver)
\r
584 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
586 return m_receiver->IsKeyDown(keyCode);
\r
588 virtual v2s32 getMousePos()
\r
590 return m_device->getCursorControl()->getPosition();
\r
592 virtual void setMousePos(s32 x, s32 y)
\r
594 m_device->getCursorControl()->setPosition(x, y);
\r
597 virtual bool getLeftClicked()
\r
599 if(g_game_focused == false)
\r
601 return m_receiver->leftclicked;
\r
603 virtual bool getRightClicked()
\r
605 if(g_game_focused == false)
\r
607 return m_receiver->rightclicked;
\r
609 virtual void resetLeftClicked()
\r
611 m_receiver->leftclicked = false;
\r
613 virtual void resetRightClicked()
\r
615 m_receiver->rightclicked = false;
\r
620 resetRightClicked();
\r
621 resetLeftClicked();
\r
624 IrrlichtDevice *m_device;
\r
625 MyEventReceiver *m_receiver;
\r
628 class RandomInputHandler : public InputHandler
\r
631 RandomInputHandler()
\r
633 leftclicked = false;
\r
634 rightclicked = false;
\r
635 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
636 keydown[i] = false;
\r
638 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
640 return keydown[keyCode];
\r
642 virtual v2s32 getMousePos()
\r
646 virtual void setMousePos(s32 x, s32 y)
\r
648 mousepos = v2s32(x,y);
\r
651 virtual bool getLeftClicked()
\r
653 return leftclicked;
\r
655 virtual bool getRightClicked()
\r
657 return rightclicked;
\r
659 virtual void resetLeftClicked()
\r
661 leftclicked = false;
\r
663 virtual void resetRightClicked()
\r
665 rightclicked = false;
\r
668 virtual void step(float dtime)
\r
671 static float counter1 = 0;
\r
675 counter1 = 0.1*Rand(1,10);
\r
676 /*if(g_selected_material < USEFUL_MATERIAL_COUNT-1)
\r
677 g_selected_material++;
\r
679 g_selected_material = 0;*/
\r
680 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
683 g_selected_item = 0;
\r
687 static float counter1 = 0;
\r
691 counter1 = 0.1*Rand(1, 40);
\r
692 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
696 static float counter1 = 0;
\r
700 counter1 = 0.1*Rand(1, 40);
\r
701 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
705 static float counter1 = 0;
\r
709 counter1 = 0.1*Rand(1, 40);
\r
710 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
714 static float counter1 = 0;
\r
718 counter1 = 0.1*Rand(1, 40);
\r
719 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
723 static float counter1 = 0;
\r
727 counter1 = 0.1*Rand(1, 20);
\r
728 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
732 static float counter1 = 0;
\r
736 counter1 = 0.1*Rand(1, 30);
\r
737 leftclicked = true;
\r
741 static float counter1 = 0;
\r
745 counter1 = 0.1*Rand(1, 20);
\r
746 rightclicked = true;
\r
749 mousepos += mousespeed;
\r
752 s32 Rand(s32 min, s32 max)
\r
754 return (rand()%(max-min+1))+min;
\r
757 bool keydown[KEY_KEY_CODES_COUNT];
\r
764 void updateViewingRange(f32 frametime, Client *client)
\r
766 // Range_all messes up frametime_avg
\r
767 if(g_viewing_range_all == true)
\r
770 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
772 // Initialize to the target value
\r
773 static float frametime_avg = 1.0/wanted_fps;
\r
774 frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
776 static f32 counter = 0;
\r
778 counter -= frametime;
\r
781 //counter = 1.0; //seconds
\r
782 counter = 0.5; //seconds
\r
784 //float freetime_ratio = 0.2;
\r
785 //float freetime_ratio = 0.4;
\r
786 float freetime_ratio = FREETIME_RATIO;
\r
788 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
790 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
792 static bool fraction_is_good = false;
\r
794 float fraction_good_threshold = 0.1;
\r
795 float fraction_bad_threshold = 0.25;
\r
796 float fraction_limit;
\r
797 // Use high limit if fraction is good AND the fraction would
\r
798 // lower the range. We want to keep the range fairly high.
\r
799 if(fraction_is_good && fraction > 1.0)
\r
800 fraction_limit = fraction_bad_threshold;
\r
802 fraction_limit = fraction_good_threshold;
\r
804 if(fabs(fraction - 1.0) < fraction_limit)
\r
806 fraction_is_good = true;
\r
811 fraction_is_good = false;
\r
814 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
815 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
816 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
817 <<" faction = "<<fraction<<std::endl;*/
\r
819 JMutexAutoLock lock(g_range_mutex);
\r
821 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
822 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
824 s16 n = (float)g_viewing_range_nodes / fraction;
\r
825 if(n < viewing_range_nodes_min)
\r
826 n = viewing_range_nodes_min;
\r
827 if(n > viewing_range_nodes_max)
\r
828 n = viewing_range_nodes_max;
\r
830 bool can_change = true;
\r
832 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
833 can_change = false;
\r
836 g_viewing_range_nodes = n;
\r
838 /*dstream<<"g_viewing_range_nodes = "
\r
839 <<g_viewing_range_nodes<<std::endl;*/
\r
842 class GUIQuickInventory : public IEventReceiver
\r
846 gui::IGUIEnvironment* env,
\r
847 gui::IGUIElement* parent,
\r
850 Inventory *inventory):
\r
851 m_itemcount(itemcount),
\r
852 m_inventory(inventory)
\r
854 core::rect<s32> imgsize(0,0,48,48);
\r
855 core::rect<s32> textsize(0,0,48,16);
\r
856 v2s32 spacing(0, 64);
\r
857 for(s32 i=0; i<m_itemcount; i++)
\r
859 m_images.push_back(env->addImage(
\r
860 imgsize + pos + spacing*i
\r
862 m_images[i]->setScaleImage(true);
\r
863 m_texts.push_back(env->addStaticText(
\r
865 textsize + pos + spacing*i,
\r
868 m_texts[i]->setBackgroundColor(
\r
869 video::SColor(128,0,0,0));
\r
870 m_texts[i]->setTextAlignment(
\r
872 gui::EGUIA_UPPERLEFT);
\r
876 virtual bool OnEvent(const SEvent& event)
\r
881 void setSelection(s32 i)
\r
890 start = m_selection - m_itemcount / 2;
\r
892 for(s32 i=0; i<m_itemcount; i++)
\r
896 if(j > (s32)m_inventory->getSize() - 1)
\r
897 j -= m_inventory->getSize();
\r
899 j += m_inventory->getSize();
\r
901 InventoryItem *item = m_inventory->getItem(j);
\r
905 m_images[i]->setImage(NULL);
\r
908 if(m_selection == j)
\r
909 swprintf(t, 10, L"<-");
\r
911 swprintf(t, 10, L"");
\r
912 m_texts[i]->setText(t);
\r
914 // The next ifs will segfault with a NULL pointer
\r
919 m_images[i]->setImage(item->getImage());
\r
922 if(m_selection == j)
\r
923 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
925 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
926 m_texts[i]->setText(t);
\r
932 core::array<gui::IGUIStaticText*> m_texts;
\r
933 core::array<gui::IGUIImage*> m_images;
\r
934 Inventory *m_inventory;
\r
938 int main(int argc, char *argv[])
\r
941 Low-level initialization
\r
944 bool disable_stderr = false;
\r
946 disable_stderr = true;
\r
949 // Initialize debug streams
\r
950 debugstreams_init(disable_stderr, DEBUGFILE);
\r
951 // Initialize debug stacks
\r
952 debug_stacks_init();
\r
954 DSTACK(__FUNCTION_NAME);
\r
960 Basic initialization
\r
963 // Initialize default settings
\r
964 set_default_settings();
\r
966 // Print startup message
\r
967 dstream<<DTIME<<"minetest-c55"
\r
968 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
969 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
972 // Set locale. This is for forcing '.' as the decimal point.
\r
973 std::locale::global(std::locale("C"));
\r
974 // This enables printing all characters in bitmap font
\r
975 setlocale(LC_CTYPE, "en_US");
\r
977 // Initialize sockets
\r
979 atexit(sockets_cleanup);
\r
981 // Initialize timestamp mutex
\r
982 g_timestamp_mutex.Init();
\r
996 // Read config file
\r
1000 g_settings.readConfigFile(argv[1]);
\r
1004 const char *filenames[2] =
\r
1006 "../minetest.conf",
\r
1007 "../../minetest.conf"
\r
1010 for(u32 i=0; i<2; i++)
\r
1012 bool r = g_settings.readConfigFile(filenames[i]);
\r
1018 // Initialize random seed
\r
1021 g_range_mutex.Init();
\r
1022 assert(g_range_mutex.IsInitialized());
\r
1024 // Read map parameters from settings
\r
1026 HMParams hm_params;
\r
1027 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1028 hm_params.randmax = g_settings.get("height_randmax");
\r
1029 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1030 hm_params.base = g_settings.get("height_base");
\r
1032 MapParams map_params;
\r
1033 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1034 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1040 std::cout<<std::endl<<std::endl;
\r
1043 <<" .__ __ __ "<<std::endl
\r
1044 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1045 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1046 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1047 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1048 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1050 <<"Now with more waterish water!"
\r
1053 std::cout<<std::endl;
\r
1054 char templine[100];
\r
1057 bool dedicated = g_settings.getBoolAsk
\r
1058 ("dedicated_server", "Dedicated server?", false);
\r
1059 std::cout<<"dedicated = "<<dedicated<<std::endl;
\r
1062 u16 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1063 std::cout<<"-> "<<port<<std::endl;
\r
1067 DSTACK("Dedicated server branch");
\r
1069 std::cout<<std::endl;
\r
1070 std::cout<<"========================"<<std::endl;
\r
1071 std::cout<<"Running dedicated server"<<std::endl;
\r
1072 std::cout<<"========================"<<std::endl;
\r
1073 std::cout<<std::endl;
\r
1075 Server server("../map", hm_params, map_params);
\r
1076 server.start(port);
\r
1080 // This is kind of a hack but can be done like this
\r
1081 // because server.step() is very light
\r
1083 server.step(0.030);
\r
1085 static int counter = 0;
\r
1091 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1092 core::list<PlayerInfo>::Iterator i;
\r
1093 static u32 sum_old = 0;
\r
1094 u32 sum = PIChecksum(list);
\r
1095 if(sum != sum_old)
\r
1097 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1098 for(i=list.begin(); i!=list.end(); i++)
\r
1100 i->PrintLine(&std::cout);
\r
1110 bool hosting = false;
\r
1111 char connect_name[100] = "";
\r
1113 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1114 if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1116 std::cout<<g_settings.get("address")<<std::endl;
\r
1117 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1121 std::cin.getline(connect_name, 100);
\r
1124 if(connect_name[0] == 0){
\r
1125 snprintf(connect_name, 100, "127.0.0.1");
\r
1130 std::cout<<"-> hosting"<<std::endl;
\r
1132 std::cout<<"-> "<<connect_name<<std::endl;
\r
1134 char playername[PLAYERNAME_SIZE] = "";
\r
1135 if(g_settings.get("name") != "")
\r
1137 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1141 std::cout<<"Name of player: ";
\r
1142 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1144 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1147 Resolution selection
\r
1152 bool fullscreen = false;
\r
1154 if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")
\r
1156 screenW = atoi(g_settings.get("screenW").c_str());
\r
1157 screenH = atoi(g_settings.get("screenH").c_str());
\r
1161 u16 resolutions[][3] = {
\r
1162 //W, H, fullscreen
\r
1173 u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
\r
1175 for(u16 i=0; i<res_count; i++)
\r
1177 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1178 <<resolutions[i][1];
\r
1179 if(resolutions[i][2])
\r
1180 std::cout<<" fullscreen"<<std::endl;
\r
1182 std::cout<<" windowed"<<std::endl;
\r
1184 std::cout<<"Select a window resolution number [empty = 2]: ";
\r
1185 std::cin.getline(templine, 100);
\r
1188 if(templine[0] == 0)
\r
1191 r0 = atoi(templine);
\r
1193 if(r0 > res_count || r0 == 0)
\r
1199 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1200 <<resolutions[i][1];
\r
1201 if(resolutions[i][2])
\r
1202 std::cout<<" fullscreen"<<std::endl;
\r
1204 std::cout<<" windowed"<<std::endl;
\r
1207 screenW = resolutions[r0-1][0];
\r
1208 screenH = resolutions[r0-1][1];
\r
1209 fullscreen = resolutions[r0-1][2];
\r
1214 MyEventReceiver receiver;
\r
1216 video::E_DRIVER_TYPE driverType;
\r
1219 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1220 driverType = video::EDT_OPENGL;
\r
1222 driverType = video::EDT_OPENGL;
\r
1225 // create device and exit if creation failed
\r
1227 IrrlichtDevice *device;
\r
1228 device = createDevice(driverType,
\r
1229 core::dimension2d<u32>(screenW, screenH),
\r
1230 16, fullscreen, false, false, &receiver);
\r
1232 /*device = createDevice(driverType,
\r
1233 core::dimension2d<u32>(screenW, screenH),
\r
1234 16, fullscreen, false, true, &receiver);*/
\r
1237 return 1; // could not create selected driver.
\r
1239 g_device = device;
\r
1241 device->setResizable(true);
\r
1243 if(g_settings.getBool("random_input"))
\r
1244 g_input = new RandomInputHandler();
\r
1246 g_input = new RealInputHandler(device, &receiver);
\r
1249 Continue initialization
\r
1252 video::IVideoDriver* driver = device->getVideoDriver();
\r
1253 // These make the textures not to show at all
\r
1254 //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
\r
1255 //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );
\r
1257 //driver->setMinHardwareBufferVertexCount(1);
\r
1259 scene::ISceneManager* smgr = device->getSceneManager();
\r
1261 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1262 gui::IGUISkin* skin = guienv->getSkin();
\r
1263 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1265 skin->setFont(font);
\r
1266 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1267 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1268 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1269 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1270 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1271 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1273 const wchar_t *text = L"Loading and connecting...";
\r
1274 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1275 core::dimension2d<u32> textd = font->getDimension(text);
\r
1276 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1277 // Have to add a bit to disable the text from word wrapping
\r
1278 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1279 core::vector2d<s32> textsize(300, textd.Height);
\r
1280 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1282 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1283 text, textrect, false, false);
\r
1284 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1286 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1287 guienv->drawAll();
\r
1288 driver->endScene();
\r
1291 Initialize material array
\r
1294 //video::SMaterial g_materials[MATERIALS_COUNT];
\r
1295 for(u16 i=0; i<MATERIALS_COUNT; i++)
\r
1297 g_materials[i].Lighting = false;
\r
1298 g_materials[i].BackfaceCulling = false;
\r
1300 const char *filename = g_material_filenames[i];
\r
1301 if(filename != NULL){
\r
1302 video::ITexture *t = driver->getTexture(filename);
\r
1304 std::cout<<DTIME<<"Texture could not be loaded: \""
\r
1305 <<filename<<"\""<<std::endl;
\r
1308 g_materials[i].setTexture(0, driver->getTexture(filename));
\r
1310 //g_materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
\r
1311 g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1312 //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
\r
1313 //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1316 g_materials[MATERIAL_WATER].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1317 //g_materials[MATERIAL_WATER].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
\r
1318 g_materials[MATERIAL_OCEAN].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1320 /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png"));
\r
1321 g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png"));
\r
1322 g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png"));
\r
1323 for(u32 i=0; i<3; i++)
\r
1325 g_mesh_materials[i].Lighting = false;
\r
1326 g_mesh_materials[i].BackfaceCulling = false;
\r
1327 g_mesh_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1328 g_mesh_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1331 // Make a scope here for the client so that it gets removed
\r
1332 // before the irrlicht device
\r
1335 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1340 SharedPtr<Server> server;
\r
1342 server = new Server("../map", hm_params, map_params);
\r
1343 server->start(port);
\r
1350 // TODO: Get rid of the g_materials parameter or it's globalness
\r
1351 Client client(device, g_materials,
\r
1352 g_settings.getFloat("client_delete_unused_sectors_timeout"),
\r
1355 Address connect_address(0,0,0,0, port);
\r
1357 connect_address.Resolve(connect_name);
\r
1359 catch(ResolveError &e)
\r
1361 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1365 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1366 client.connect(connect_address);
\r
1369 while(client.connectedAndInitialized() == false)
\r
1372 if(server != NULL){
\r
1373 server->step(0.1);
\r
1378 catch(con::PeerNotFoundException &e)
\r
1380 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1385 Create the camera node
\r
1388 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1389 0, // Camera parent
\r
1390 v3f(BS*100, BS*2, BS*100), // Look from
\r
1391 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1395 if(camera == NULL)
\r
1398 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1400 camera->setFOV(FOV_ANGLE);
\r
1402 // Just so big a value that everything rendered is visible
\r
1403 camera->setFarValue(100000*BS);
\r
1405 /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1406 f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1408 camera->setFarValue(range);
\r
1412 video::EFT_FOG_LINEAR,
\r
1420 f32 camera_yaw = 0; // "right/left"
\r
1421 f32 camera_pitch = 0; // "up/down"
\r
1423 gui_loadingtext->remove();
\r
1426 Add some gui stuff
\r
1429 // First line of debug text
\r
1430 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1432 core::rect<s32>(5, 5, 5+600, 5+textsize.Y),
\r
1434 // Second line of debug text
\r
1435 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1437 core::rect<s32>(5, 5+(textsize.Y+5)*1, 5+600, (5+textsize.Y)*2),
\r
1440 // At the middle of the screen
\r
1441 // Object infos are shown in this
\r
1442 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1444 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1447 // This is a copy of the inventory that the client's environment has
\r
1448 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1450 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1451 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1454 Some statistics are collected in these
\r
1457 u32 scenetime = 0;
\r
1458 u32 endscenetime = 0;
\r
1466 virtual void sendText(std::string text) = 0;
\r
1469 struct TextDestSign : public TextDest
\r
1471 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1473 m_blockpos = blockpos;
\r
1475 m_client = client;
\r
1477 void sendText(std::string text)
\r
1479 dstream<<"Changing text of a sign object: "
\r
1480 <<text<<std::endl;
\r
1481 m_client->sendSignText(m_blockpos, m_id, text);
\r
1489 TextDest *textbuf_dest = NULL;
\r
1491 //gui::IGUIWindow* input_window = NULL;
\r
1492 gui::IGUIStaticText* input_guitext = NULL;
\r
1498 bool first_loop_after_window_activation = true;
\r
1500 // Time is in milliseconds
\r
1501 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1502 // NOTE: Have to call run() between calls of this to update the timer
\r
1503 u32 lasttime = device->getTimer()->getTime();
\r
1505 while(device->run())
\r
1507 // Hilight boxes collected during the loop and displayed
\r
1508 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1511 std::wstring infotext;
\r
1513 //TimeTaker //timer1("//timer1", device);
\r
1515 // Time of frame without fps limit
\r
1519 // not using getRealTime is necessary for wine
\r
1520 u32 time = device->getTimer()->getTime();
\r
1521 if(time > lasttime)
\r
1522 busytime_u32 = time - lasttime;
\r
1525 busytime = busytime_u32 / 1000.0;
\r
1528 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1530 // Absolutelu necessary for wine!
\r
1537 //updateViewingRange(dtime, &client);
\r
1538 updateViewingRange(busytime, &client);
\r
1545 float fps_max = g_settings.getFloat("fps_max");
\r
1546 u32 frametime_min = 1000./fps_max;
\r
1548 if(busytime_u32 < frametime_min)
\r
1550 u32 sleeptime = frametime_min - busytime_u32;
\r
1551 device->sleep(sleeptime);
\r
1555 // Absolutelu necessary for wine!
\r
1559 Time difference calculation
\r
1561 f32 dtime; // in seconds
\r
1563 u32 time = device->getTimer()->getTime();
\r
1564 if(time > lasttime)
\r
1565 dtime = (time - lasttime) / 1000.0;
\r
1571 Time average and jitter calculation
\r
1574 static f32 dtime_avg1 = 0.0;
\r
1575 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1576 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1578 static f32 dtime_jitter1_max_sample = 0.0;
\r
1579 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1581 static f32 jitter1_max = 0.0;
\r
1582 static f32 counter = 0.0;
\r
1583 if(dtime_jitter1 > jitter1_max)
\r
1584 jitter1_max = dtime_jitter1;
\r
1589 dtime_jitter1_max_sample = jitter1_max;
\r
1590 dtime_jitter1_max_fraction
\r
1591 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1592 jitter1_max = 0.0;
\r
1595 Control freetime ratio
\r
1597 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1599 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1600 g_freetime_ratio += 0.01;
\r
1604 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1605 g_freetime_ratio -= 0.01;
\r
1611 Busytime average and jitter calculation
\r
1614 static f32 busytime_avg1 = 0.0;
\r
1615 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1616 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1618 static f32 busytime_jitter1_max_sample = 0.0;
\r
1619 static f32 busytime_jitter1_min_sample = 0.0;
\r
1621 static f32 jitter1_max = 0.0;
\r
1622 static f32 jitter1_min = 0.0;
\r
1623 static f32 counter = 0.0;
\r
1624 if(busytime_jitter1 > jitter1_max)
\r
1625 jitter1_max = busytime_jitter1;
\r
1626 if(busytime_jitter1 < jitter1_min)
\r
1627 jitter1_min = busytime_jitter1;
\r
1629 if(counter > 0.0){
\r
1631 busytime_jitter1_max_sample = jitter1_max;
\r
1632 busytime_jitter1_min_sample = jitter1_min;
\r
1633 jitter1_max = 0.0;
\r
1634 jitter1_min = 0.0;
\r
1639 Debug info for client
\r
1642 static float counter = 0.0;
\r
1647 client.printDebugInfo(std::cout);
\r
1652 Input handler step()
\r
1654 g_input->step(dtime);
\r
1665 Player speed control
\r
1668 if(g_game_focused)
\r
1675 bool a_superspeed,
\r
1678 PlayerControl control(
\r
1679 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1680 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1681 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1682 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1683 g_input->isKeyDown(irr::KEY_SPACE),
\r
1684 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1688 client.setPlayerControl(control);
\r
1692 // Set every key to inactive
\r
1693 PlayerControl control;
\r
1694 client.setPlayerControl(control);
\r
1699 Process environment
\r
1703 //TimeTaker timer("client.step(dtime)", device);
\r
1704 client.step(dtime);
\r
1705 //client.step(dtime_avg1);
\r
1708 if(server != NULL)
\r
1710 //TimeTaker timer("server->step(dtime)", device);
\r
1711 server->step(dtime);
\r
1714 v3f player_position = client.getPlayerPosition();
\r
1716 //TimeTaker //timer2("//timer2", device);
\r
1719 Mouse and camera control
\r
1722 if(device->isWindowActive() && g_game_focused)
\r
1724 device->getCursorControl()->setVisible(false);
\r
1726 if(first_loop_after_window_activation){
\r
1727 //std::cout<<"window active, first loop"<<std::endl;
\r
1728 first_loop_after_window_activation = false;
\r
1731 s32 dx = g_input->getMousePos().X - 320;
\r
1732 s32 dy = g_input->getMousePos().Y - 240;
\r
1733 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1734 camera_yaw -= dx*0.2;
\r
1735 camera_pitch += dy*0.2;
\r
1736 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1737 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1739 g_input->setMousePos(320, 240);
\r
1742 device->getCursorControl()->setVisible(true);
\r
1744 //std::cout<<"window inactive"<<std::endl;
\r
1745 first_loop_after_window_activation = true;
\r
1748 camera_yaw = wrapDegrees(camera_yaw);
\r
1749 camera_pitch = wrapDegrees(camera_pitch);
\r
1751 v3f camera_direction = v3f(0,0,1);
\r
1752 camera_direction.rotateYZBy(camera_pitch);
\r
1753 camera_direction.rotateXZBy(camera_yaw);
\r
1755 v3f camera_position =
\r
1756 player_position + v3f(0, BS+BS/2, 0);
\r
1758 camera->setPosition(camera_position);
\r
1759 // *100.0 helps in large map coordinates
\r
1760 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1762 if(FIELD_OF_VIEW_TEST){
\r
1763 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1764 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1767 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1768 //TimeTaker timer("client.updateCamera", device);
\r
1769 client.updateCamera(camera_position, camera_direction);
\r
1773 //TimeTaker //timer3("//timer3", device);
\r
1776 Calculate what block is the crosshair pointing to
\r
1779 //u32 t1 = device->getTimer()->getRealTime();
\r
1781 //f32 d = 4; // max. distance
\r
1782 f32 d = 4; // max. distance
\r
1783 core::line3d<f32> shootline(camera_position,
\r
1784 camera_position + camera_direction * BS * (d+1));
\r
1786 MapBlockObject *selected_object = client.getSelectedObject
\r
1787 (d*BS, camera_position, shootline);
\r
1789 if(selected_object != NULL)
\r
1791 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1793 core::aabbox3d<f32> box_on_map
\r
1794 = selected_object->getSelectionBoxOnMap();
\r
1796 hilightboxes.push_back(box_on_map);
\r
1798 infotext = narrow_to_wide(selected_object->infoText());
\r
1800 if(g_input->getLeftClicked())
\r
1802 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1803 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1804 selected_object->getId(), g_selected_item);
\r
1806 else if(g_input->getRightClicked())
\r
1808 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1810 Check if we want to modify the object ourselves
\r
1812 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1814 dstream<<"Sign object right-clicked"<<std::endl;
\r
1818 input_guitext = guienv->addStaticText(L"",
\r
1819 core::rect<s32>(150,100,350,120),
\r
1821 false, // wordwrap?
\r
1824 input_guitext->setDrawBackground(true);
\r
1826 g_text_buffer = L"";
\r
1827 g_text_buffer_accepted = false;
\r
1828 textbuf_dest = new TextDestSign(
\r
1829 selected_object->getBlock()->getPos(),
\r
1830 selected_object->getId(),
\r
1834 Otherwise pass the event to the server as-is
\r
1838 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1839 selected_object->getId(), g_selected_item);
\r
1843 else // selected_object == NULL
\r
1846 bool nodefound = false;
\r
1848 v3s16 neighbourpos;
\r
1849 core::aabbox3d<f32> nodefacebox;
\r
1850 f32 mindistance = BS * 1001;
\r
1852 v3s16 pos_i = floatToInt(player_position);
\r
1854 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1858 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1859 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1860 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1861 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1862 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1863 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1865 for(s16 y = ystart; y <= yend; y++){
\r
1866 for(s16 z = zstart; z <= zend; z++){
\r
1867 for(s16 x = xstart; x <= xend; x++)
\r
1870 if(material_pointable(client.getNode(v3s16(x,y,z)).d) == false)
\r
1872 }catch(InvalidPositionException &e){
\r
1877 v3f npf = intToFloat(np);
\r
1881 v3s16 directions[6] = {
\r
1882 v3s16(0,0,1), // back
\r
1883 v3s16(0,1,0), // top
\r
1884 v3s16(1,0,0), // right
\r
1890 for(u16 i=0; i<6; i++){
\r
1892 v3f dir_f = v3f(directions[i].X,
\r
1893 directions[i].Y, directions[i].Z);
\r
1894 v3f centerpoint = npf + dir_f * BS/2;
\r
1896 (centerpoint - camera_position).getLength();
\r
1898 if(distance < mindistance){
\r
1899 //std::cout<<DTIME<<"for centerpoint=("<<centerpoint.X<<","<<centerpoint.Y<<","<<centerpoint.Z<<"): distance < mindistance"<<std::endl;
\r
1900 //std::cout<<DTIME<<"npf=("<<npf.X<<","<<npf.Y<<","<<npf.Z<<")"<<std::endl;
\r
1901 core::CMatrix4<f32> m;
\r
1902 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1904 // This is the back face
\r
1905 v3f corners[2] = {
\r
1906 v3f(BS/2, BS/2, BS/2),
\r
1907 v3f(-BS/2, -BS/2, BS/2+d)
\r
1910 for(u16 j=0; j<2; j++){
\r
1911 m.rotateVect(corners[j]);
\r
1912 corners[j] += npf;
\r
1913 //std::cout<<DTIME<<"box corners["<<j<<"]: ("<<corners[j].X<<","<<corners[j].Y<<","<<corners[j].Z<<")"<<std::endl;
\r
1916 //core::aabbox3d<f32> facebox(corners[0],corners[1]);
\r
1917 core::aabbox3d<f32> facebox(corners[0]);
\r
1918 facebox.addInternalPoint(corners[1]);
\r
1920 if(facebox.intersectsWithLine(shootline)){
\r
1923 neighbourpos = np + directions[i];
\r
1924 mindistance = distance;
\r
1925 nodefacebox = facebox;
\r
1933 //std::cout<<DTIME<<"nodefound == true"<<std::endl;
\r
1934 //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
1935 //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
\r
1937 static v3s16 nodepos_old(-1,-1,-1);
\r
1938 if(nodepos != nodepos_old){
\r
1939 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
1940 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
1941 nodepos_old = nodepos;
\r
1943 /*wchar_t positiontext[20];
\r
1944 swprintf(positiontext, 20, L"(%i,%i,%i)",
\r
1945 nodepos.X, nodepos.Y, nodepos.Z);
\r
1946 positiontextgui->setText(positiontext);*/
\r
1949 hilightboxes.push_back(nodefacebox);
\r
1951 if(g_input->getLeftClicked())
\r
1953 //std::cout<<DTIME<<"Removing node"<<std::endl;
\r
1954 //client.removeNode(nodepos);
\r
1955 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
1956 client.clickGround(0, nodepos, neighbourpos, g_selected_item);
\r
1958 if(g_input->getRightClicked())
\r
1960 //std::cout<<DTIME<<"Placing node"<<std::endl;
\r
1961 //client.addNodeFromInventory(neighbourpos, g_selected_item);
\r
1962 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
1963 client.clickGround(1, nodepos, neighbourpos, g_selected_item);
\r
1967 //std::cout<<DTIME<<"nodefound == false"<<std::endl;
\r
1968 //positiontextgui->setText(L"");
\r
1971 } // selected_object == NULL
\r
1973 g_input->resetLeftClicked();
\r
1974 g_input->resetRightClicked();
\r
1977 Calculate stuff for drawing
\r
1980 v2u32 screensize = driver->getScreenSize();
\r
1981 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1983 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
1986 Update gui stuff (0ms)
\r
1989 //TimeTaker guiupdatetimer("Gui updating", device);
\r
1992 wchar_t temptext[100];
\r
1994 static float drawtime_avg = 0;
\r
1995 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
1996 static float scenetime_avg = 0;
\r
1997 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
1998 static float endscenetime_avg = 0;
\r
1999 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2001 swprintf(temptext, 100, L"Minetest-c55 ("
\r
2003 L", R: range_all=%i"
\r
2005 L" drawtime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2007 g_viewing_range_all,
\r
2013 guitext->setText(temptext);
\r
2017 wchar_t temptext[100];
\r
2018 /*swprintf(temptext, 100,
\r
2020 L"% .3f < btime_jitter < % .3f"
\r
2021 L", dtime_jitter = % .1f %%"
\r
2022 //L", ftime_ratio = % .3f"
\r
2024 busytime_jitter1_min_sample,
\r
2025 busytime_jitter1_max_sample,
\r
2026 dtime_jitter1_max_fraction * 100.0
\r
2027 //g_freetime_ratio
\r
2029 swprintf(temptext, 100,
\r
2030 L"(% .1f, % .1f, % .1f)"
\r
2031 L" (% .3f < btime_jitter < % .3f"
\r
2032 L", dtime_jitter = % .1f %%)",
\r
2033 player_position.X/BS,
\r
2034 player_position.Y/BS,
\r
2035 player_position.Z/BS,
\r
2036 busytime_jitter1_min_sample,
\r
2037 busytime_jitter1_max_sample,
\r
2038 dtime_jitter1_max_fraction * 100.0
\r
2041 guitext2->setText(temptext);
\r
2045 /*wchar_t temptext[100];
\r
2046 swprintf(temptext, 100,
\r
2047 SWPRINTF_CHARSTRING,
\r
2048 infotext.substr(0,99).c_str()
\r
2051 guitext_info->setText(temptext);*/
\r
2053 guitext_info->setText(infotext.c_str());
\r
2060 static u16 old_selected_item = 65535;
\r
2061 if(client.getLocalInventoryUpdated()
\r
2062 || g_selected_item != old_selected_item)
\r
2064 old_selected_item = g_selected_item;
\r
2065 //std::cout<<"Updating local inventory"<<std::endl;
\r
2066 client.getLocalInventory(local_inventory);
\r
2067 quick_inventory->setSelection(g_selected_item);
\r
2068 quick_inventory->update();
\r
2071 if(input_guitext != NULL)
\r
2073 /*wchar_t temptext[100];
\r
2074 swprintf(temptext, 100,
\r
2075 SWPRINTF_CHARSTRING,
\r
2076 g_text_buffer.substr(0,99).c_str()
\r
2078 input_guitext->setText(g_text_buffer.c_str());
\r
2084 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2086 input_guitext->remove();
\r
2087 input_guitext = NULL;
\r
2089 if(textbuf_dest != NULL)
\r
2091 std::string text = wide_to_narrow(g_text_buffer);
\r
2092 dstream<<"Sending text: "<<text<<std::endl;
\r
2093 textbuf_dest->sendText(text);
\r
2094 delete textbuf_dest;
\r
2095 textbuf_dest = NULL;
\r
2101 //guiupdatetimer.stop();
\r
2107 TimeTaker drawtimer("Drawing", device);
\r
2110 Background color is choosen based on whether the player is
\r
2111 much beyond the initial ground level
\r
2113 /*video::SColor bgcolor;
\r
2114 v3s16 p0 = Map::floatToInt(player_position);
\r
2115 // Does this make short random delays?
\r
2116 // NOTE: no need for this, sky doesn't show underground with
\r
2118 bool is_underground = client.isNodeUnderground(p0);
\r
2119 //bool is_underground = false;
\r
2120 if(is_underground == false)
\r
2121 bgcolor = video::SColor(255,90,140,200);
\r
2123 bgcolor = video::SColor(255,0,0,0);*/
\r
2125 //video::SColor bgcolor = video::SColor(255,90,140,200);
\r
2126 video::SColor bgcolor = skycolor;
\r
2129 driver->beginScene(true, true, bgcolor);
\r
2133 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2136 TimeTaker timer("smgr", device);
\r
2138 scenetime = timer.stop(true);
\r
2142 //TimeTaker timer9("auxiliary drawings", device);
\r
2145 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2146 displaycenter + core::vector2d<s32>(10,0),
\r
2147 video::SColor(255,255,255,255));
\r
2148 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2149 displaycenter + core::vector2d<s32>(0,10),
\r
2150 video::SColor(255,255,255,255));
\r
2153 //TimeTaker //timer10("//timer10", device);
\r
2155 video::SMaterial m;
\r
2157 m.Lighting = false;
\r
2158 driver->setMaterial(m);
\r
2160 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2162 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2163 i != hilightboxes.end(); i++)
\r
2165 /*std::cout<<"hilightbox min="
\r
2166 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2168 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2170 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2176 //TimeTaker //timer11("//timer11", device);
\r
2182 guienv->drawAll();
\r
2186 TimeTaker timer("endScene", device);
\r
2187 driver->endScene();
\r
2188 endscenetime = timer.stop(true);
\r
2191 drawtime = drawtimer.stop(true);
\r
2197 static s16 lastFPS = 0;
\r
2198 //u16 fps = driver->getFPS();
\r
2199 u16 fps = (1.0/dtime_avg1);
\r
2201 if (lastFPS != fps)
\r
2203 core::stringw str = L"Minetest [";
\r
2204 str += driver->getName();
\r
2208 device->setWindowCaption(str.c_str());
\r
2214 device->yield();*/
\r
2217 } // client is deleted at this point
\r
2222 In the end, delete the Irrlicht device.
\r
2227 catch(con::PeerNotFoundException &e)
\r
2229 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2231 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2233 This is what has to be done in every thread to get suitable debug info
\r
2235 catch(std::exception &e)
\r
2237 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2238 <<e.what()<<std::endl;
\r
2243 debugstreams_deinit();
\r