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
23 NOTE: VBO cannot be turned on for fast-changing stuff because there
\r
24 is an apparanet memory leak in irrlicht when using it (not sure)
\r
26 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
27 NOTE: Global locale is now set at initialization
\r
29 SUGGESTION: add a second lighting value to the MS nibble of param of
\r
30 air to tell how bright the air node is when there is no sunlight.
\r
31 When day changes to night, these two values can be interpolated.
\r
33 TODO: Fix address to be ipv6 compatible
\r
35 TODO: ESC Pause mode in which the cursor is not kept at the center of window.
\r
36 TODO: Stop player if focus of window is taken away (go to pause mode)
\r
37 TODO: Optimize and fix makeFastFace or whatever it's called
\r
38 - Face calculation is the source of CPU usage on the client
\r
39 SUGGESTION: The client will calculate and send lighting changes and
\r
40 the server will randomly check some of them and kick the client out
\r
41 if it fails to calculate them right.
\r
42 - Actually, it could just start ignoring them and calculate them
\r
44 SUGGESTION: Combine MapBlock's face caches to so big pieces that VBO
\r
46 - That is >500 vertices
\r
48 TODO: Better dungeons
\r
49 TODO: There should be very slight natural caves also, starting from
\r
50 only a straightened-up cliff
\r
52 TODO: Changing of block with mouse wheel or something
\r
57 - One single map container with ids as keys
\r
60 TODO: - Keep track of the place of the mob in the last few hundreth's
\r
61 of a second - then, if a player hits it, take the value that is
\r
62 avg_rtt/2 before the moment the packet is received.
\r
65 SUGGESTION: Modify client to calculate single changes asynchronously
\r
67 TODO: Moving players more smoothly. Calculate moving animation from
\r
68 data sent by server.
\r
70 TODO: There are some lighting-related todos and fixmes in
\r
71 ServerMap::emergeBlock
\r
73 TODO: Make a dirt node and use it under water
\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: Set server to automatically find a good spawning place in some
\r
85 place where there is water and land.
\r
86 - Map to have a getWalkableNear(p)
\r
88 TODO: Transfer more blocks in a single packet
\r
89 SUGG: A blockdata combiner class, to which blocks are added and at
\r
90 destruction it sends all the stuff in as few packets as possible.
\r
92 TODO: If player is on ground, mainly fetch ground-level blocks
\r
93 TODO: Fetch stuff mainly from the viewing direction
\r
95 TODO: Expose Connection's seqnums and ACKs to server and client.
\r
96 - This enables saving many packets and making a faster connection
\r
97 - This also enables server to check if client has received the
\r
98 most recent block sent, for example.
\r
100 TODO: Add a sane bandwidth throttling system to Connection
\r
102 FIXME: There still are *some* tiny glitches in lighting as seen from
\r
103 the client side. The server calculates them right but sometimes
\r
104 they don't get transferred properly.
\r
105 - Server probably checks that a block is not sent, then continues
\r
106 to sending it, then the emerge thread marks it as unsent and then
\r
107 the sender sends the block as it was before emerging?
\r
108 TODO: How about adding a "revision" field to MapBlocks?
\r
110 SUGG: More fine-grained control of client's dumping of blocks from
\r
112 - ...What does this mean in the first place?
\r
114 TODO: Somehow prioritize the sending of blocks and combine the block
\r
116 - Take two blocks to be sent next from each client and assign
\r
117 a priority value to them
\r
118 - Priority is the same as distance from player
\r
119 - Take the highest priority ones and send them. Send as many as
\r
120 fits in the global send queue maximum length (sum of lengths
\r
122 TODO: Make the amount of blocks sending to client and the total
\r
123 amount of blocks dynamically limited. Transferring blocks is the
\r
124 main network eater of this system, so it is the one that has
\r
125 to be throttled so that RTTs stay low.
\r
126 FIXME: There is a bug that sometimes the EmergeThread bumps to
\r
127 the client's emerge counter being already 0, and also the
\r
128 sending queue size of the client can float to 1 or 2, which
\r
129 stops the map from loading at all.
\r
130 - A quick hack could be applied to ignore the error of
\r
131 being at 0 and timing out old entries
\r
132 SUGG: Make client send GOTBLOCKS before updating meshes
\r
134 TODO: Server to load starting inventory from disk
\r
136 TODO: PLayers to only be hidden when the client quits.
\r
137 TODO: - Players to be saved on disk, with inventory
\r
138 TODO: Players to be saved as text in map/players/<name>
\r
140 SUGGESTION: A map editing mode (similar to dedicated server mode)
\r
142 TODO: Maybe: Create a face calculation queue on the client that is
\r
143 processed in a separate thread
\r
144 TODO: Make client's mesh updates to happen in a thread similar to
\r
145 server's EmergeThread.
\r
146 - This is not really needed, mesh update is really fast
\r
147 - Instead, the lighting update can be slow
\r
148 - So, this todo is not really a todo. It is a not-todo.
\r
149 SUGG: Make server to send all modified blocks after a node change
\r
150 after all the stuff including lighting have been updated
\r
152 TODO: Make fetching sector's blocks more efficient when rendering
\r
153 sectors that have very large amounts of blocks (on client)
\r
155 TODO: Make the video backend selectable
\r
157 Block object server side:
\r
158 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
159 - For all blocks in the buffer, objects are stepped(). This
\r
160 means they are active.
\r
161 - TODO: A global active buffer is needed for the server
\r
162 - TODO: All blocks going in and out of the buffer are recorded.
\r
163 - TODO: For outgoing blocks, a timestamp is written.
\r
164 - TODO: For incoming blocks, the time difference is calculated and
\r
165 objects are stepped according to it.
\r
166 TODO: A timestamp to blocks
\r
168 SUGG: Add a time value to the param of footstepped grass and check it
\r
169 against a global timer when a block is accessed, to make old
\r
173 TODO: Add config parameters for server's sending and generating distance
\r
175 TODO: Copy the text of the last picked sign to inventory in creative
\r
178 TODO: Untie client network operations from framerate
\r
180 SUGG: Make a copy of close-range environment on client for showing
\r
181 on screen, with minimal mutexes to slow the main loop down
\r
183 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
184 it by sending more stuff in a single packet.
\r
185 - Add a packet queue to RemoteClient, from which packets will be
\r
186 combined with object data packets
\r
187 - This is not exactly trivial: the object data packets are
\r
188 sometimes very big by themselves
\r
190 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
191 - This will allow saving ages of rats on disk but not sending
\r
194 TODO: Get rid of GotSplitPacketException
\r
198 TODO: Check what goes wrong with caching map to disk (Kray)
\r
200 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
203 ======================================================================
\r
205 TODO: Implement lighting using VoxelManipulator
\r
207 ======================================================================
\r
212 Setting this to 1 enables a special camera mode that forces
\r
213 the renderers to think that the camera statically points from
\r
214 the starting place to a static direction.
\r
216 This allows one to move around with the player and see what
\r
217 is actually drawn behind solid things and behind the player.
\r
219 #define FIELD_OF_VIEW_TEST 0
\r
221 #ifdef UNITTEST_DISABLE
\r
223 #pragma message ("Disabling unit tests")
\r
225 #warning "Disabling unit tests"
\r
227 // Disable unit tests
\r
228 #define ENABLE_TESTS 0
\r
230 // Enable unit tests
\r
231 #define ENABLE_TESTS 1
\r
235 #pragma comment(lib, "Irrlicht.lib")
\r
236 #pragma comment(lib, "jthread.lib")
\r
237 // This would get rid of the console window
\r
238 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
242 #define WIN32_LEAN_AND_MEAN
\r
243 #include <windows.h>
\r
244 #define sleep_ms(x) Sleep(x)
\r
246 #include <unistd.h>
\r
247 #define sleep_ms(x) usleep(x*1000)
\r
250 #include <iostream>
\r
253 #include <jmutexautolock.h>
\r
254 #include "common_irrlicht.h"
\r
257 #include "player.h"
\r
260 #include "environment.h"
\r
261 #include "server.h"
\r
262 #include "client.h"
\r
263 #include "serialization.h"
\r
264 #include "constants.h"
\r
265 #include "strfnd.h"
\r
266 #include "porting.h"
\r
267 #include <locale.h>
\r
269 IrrlichtDevice *g_device = NULL;
\r
271 const char *g_material_filenames[MATERIALS_COUNT] =
\r
273 "../data/stone.png",
\r
274 "../data/grass.png",
\r
275 "../data/water.png",
\r
276 "../data/light.png",
\r
277 "../data/tree.png",
\r
278 "../data/leaves.png",
\r
279 "../data/grass_footsteps.png",
\r
280 "../data/mese.png",
\r
284 video::SMaterial g_materials[MATERIALS_COUNT];
\r
285 //video::SMaterial g_mesh_materials[3];
\r
287 // All range-related stuff below is locked behind this
\r
288 JMutex g_range_mutex;
\r
290 // Blocks are viewed in this range from the player
\r
291 s16 g_viewing_range_nodes = 60;
\r
293 // This is updated by the client's fetchBlocks routine
\r
294 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
296 // If true, the preceding value has no meaning and all blocks
\r
297 // already existing in memory are drawn
\r
298 bool g_viewing_range_all = false;
\r
300 // This is the freetime ratio imposed by the dynamic viewing
\r
301 // range changing code.
\r
302 // It is controlled by the main loop to the smallest value that
\r
303 // inhibits glitches (dtime jitter) in the main loop.
\r
304 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
309 These are loaded from the config file.
\r
312 Settings g_settings;
\r
314 // Sets default settings
\r
315 void set_default_settings()
\r
317 g_settings.set("dedicated_server", "");
\r
320 g_settings.set("wanted_fps", "30");
\r
321 g_settings.set("fps_max", "60");
\r
322 g_settings.set("viewing_range_nodes_max", "300");
\r
323 g_settings.set("viewing_range_nodes_min", "20");
\r
324 g_settings.set("screenW", "");
\r
325 g_settings.set("screenH", "");
\r
326 g_settings.set("host_game", "");
\r
327 g_settings.set("port", "");
\r
328 g_settings.set("address", "");
\r
329 g_settings.set("name", "");
\r
330 g_settings.set("random_input", "false");
\r
331 g_settings.set("client_delete_unused_sectors_timeout", "1200");
\r
334 g_settings.set("creative_mode", "false");
\r
335 g_settings.set("heightmap_blocksize", "128");
\r
336 g_settings.set("height_randmax", "constant 70.0");
\r
337 g_settings.set("height_randfactor", "constant 0.6");
\r
338 g_settings.set("height_base", "linear 0 35 0");
\r
339 g_settings.set("plants_amount", "1.0");
\r
340 g_settings.set("ravines_amount", "1.0");
\r
341 g_settings.set("objectdata_interval", "0.2");
\r
342 g_settings.set("active_object_range", "2");
\r
343 g_settings.set("max_simultaneous_block_sends_per_client", "2");
\r
344 g_settings.set("max_simultaneous_block_sends_server_total", "4");
\r
351 //u16 g_selected_material = 0;
\r
352 u16 g_selected_item = 0;
\r
354 bool g_esc_pressed = false;
\r
356 std::wstring g_text_buffer;
\r
357 bool g_text_buffer_accepted = false;
\r
359 // When true, the mouse and keyboard are grabbed
\r
360 bool g_game_focused = true;
\r
367 std::ostream *dout_con_ptr = &dummyout;
\r
368 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
369 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
370 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
371 //std::ostream *dout_con_ptr = &dstream;
\r
372 //std::ostream *derr_con_ptr = &dstream;
\r
375 std::ostream *dout_server_ptr = &dstream;
\r
376 std::ostream *derr_server_ptr = &dstream;
\r
379 std::ostream *dout_client_ptr = &dstream;
\r
380 std::ostream *derr_client_ptr = &dstream;
\r
387 JMutex g_timestamp_mutex;
\r
388 //std::string g_timestamp;
\r
390 std::string getTimestamp()
\r
392 if(g_timestamp_mutex.IsInitialized()==false)
\r
394 JMutexAutoLock lock(g_timestamp_mutex);
\r
395 //return g_timestamp;
\r
396 time_t t = time(NULL);
\r
397 struct tm *tm = localtime(&t);
\r
399 strftime(cs, 20, "%H:%M:%S", tm);
\r
403 class MyEventReceiver : public IEventReceiver
\r
406 // This is the one method that we have to implement
\r
407 virtual bool OnEvent(const SEvent& event)
\r
409 // Remember whether each key is down or up
\r
410 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
412 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
414 if(event.KeyInput.PressedDown)
\r
416 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
417 if(g_game_focused == false)
\r
419 s16 key = event.KeyInput.Key;
\r
420 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
422 g_text_buffer_accepted = true;
\r
424 else if(key == irr::KEY_BACK)
\r
426 if(g_text_buffer.size() > 0)
\r
427 g_text_buffer = g_text_buffer.substr
\r
428 (0, g_text_buffer.size()-1);
\r
432 wchar_t wc = event.KeyInput.Char;
\r
434 g_text_buffer += wc;
\r
438 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
440 if(g_game_focused == true)
\r
442 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
443 g_esc_pressed = true;
\r
447 // Material selection
\r
448 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
450 if(g_game_focused == true)
\r
452 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
455 g_selected_item = 0;
\r
456 dstream<<DTIME<<"Selected item: "
\r
457 <<g_selected_item<<std::endl;
\r
461 // Viewing range selection
\r
462 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
465 JMutexAutoLock lock(g_range_mutex);
\r
466 if(g_viewing_range_all)
\r
468 g_viewing_range_all = false;
\r
469 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
473 g_viewing_range_all = true;
\r
474 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
480 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
482 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
484 leftclicked = true;
\r
486 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
488 rightclicked = true;
\r
490 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
492 /*dstream<<"event.MouseInput.Wheel="
\r
493 <<event.MouseInput.Wheel<<std::endl;*/
\r
494 if(event.MouseInput.Wheel < 0)
\r
496 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
499 g_selected_item = 0;
\r
501 else if(event.MouseInput.Wheel > 0)
\r
503 if(g_selected_item > 0)
\r
506 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
514 // This is used to check whether a key is being held down
\r
515 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
517 return keyIsDown[keyCode];
\r
522 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
523 keyIsDown[i] = false;
\r
524 leftclicked = false;
\r
525 rightclicked = false;
\r
531 // We use this array to store the current state of each key
\r
532 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
543 virtual ~InputHandler()
\r
546 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
547 virtual v2s32 getMousePos() = 0;
\r
548 virtual void setMousePos(s32 x, s32 y) = 0;
\r
549 virtual bool getLeftClicked() = 0;
\r
550 virtual bool getRightClicked() = 0;
\r
551 virtual void resetLeftClicked() = 0;
\r
552 virtual void resetRightClicked() = 0;
\r
554 virtual void step(float dtime) {};
\r
556 virtual void clear() {};
\r
559 InputHandler *g_input = NULL;
\r
564 g_game_focused = true;
\r
569 g_game_focused = false;
\r
572 class RealInputHandler : public InputHandler
\r
575 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
577 m_receiver(receiver)
\r
580 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
582 return m_receiver->IsKeyDown(keyCode);
\r
584 virtual v2s32 getMousePos()
\r
586 return m_device->getCursorControl()->getPosition();
\r
588 virtual void setMousePos(s32 x, s32 y)
\r
590 m_device->getCursorControl()->setPosition(x, y);
\r
593 virtual bool getLeftClicked()
\r
595 if(g_game_focused == false)
\r
597 return m_receiver->leftclicked;
\r
599 virtual bool getRightClicked()
\r
601 if(g_game_focused == false)
\r
603 return m_receiver->rightclicked;
\r
605 virtual void resetLeftClicked()
\r
607 m_receiver->leftclicked = false;
\r
609 virtual void resetRightClicked()
\r
611 m_receiver->rightclicked = false;
\r
616 resetRightClicked();
\r
617 resetLeftClicked();
\r
620 IrrlichtDevice *m_device;
\r
621 MyEventReceiver *m_receiver;
\r
624 class RandomInputHandler : public InputHandler
\r
627 RandomInputHandler()
\r
629 leftclicked = false;
\r
630 rightclicked = false;
\r
631 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
632 keydown[i] = false;
\r
634 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
636 return keydown[keyCode];
\r
638 virtual v2s32 getMousePos()
\r
642 virtual void setMousePos(s32 x, s32 y)
\r
644 mousepos = v2s32(x,y);
\r
647 virtual bool getLeftClicked()
\r
649 return leftclicked;
\r
651 virtual bool getRightClicked()
\r
653 return rightclicked;
\r
655 virtual void resetLeftClicked()
\r
657 leftclicked = false;
\r
659 virtual void resetRightClicked()
\r
661 rightclicked = false;
\r
664 virtual void step(float dtime)
\r
667 static float counter1 = 0;
\r
671 counter1 = 0.1*Rand(1,10);
\r
672 /*if(g_selected_material < USEFUL_MATERIAL_COUNT-1)
\r
673 g_selected_material++;
\r
675 g_selected_material = 0;*/
\r
676 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
679 g_selected_item = 0;
\r
683 static float counter1 = 0;
\r
687 counter1 = 0.1*Rand(1, 40);
\r
688 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
692 static float counter1 = 0;
\r
696 counter1 = 0.1*Rand(1, 40);
\r
697 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
701 static float counter1 = 0;
\r
705 counter1 = 0.1*Rand(1, 40);
\r
706 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
710 static float counter1 = 0;
\r
714 counter1 = 0.1*Rand(1, 40);
\r
715 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
719 static float counter1 = 0;
\r
723 counter1 = 0.1*Rand(1, 20);
\r
724 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
728 static float counter1 = 0;
\r
732 counter1 = 0.1*Rand(1, 30);
\r
733 leftclicked = true;
\r
737 static float counter1 = 0;
\r
741 counter1 = 0.1*Rand(1, 20);
\r
742 rightclicked = true;
\r
745 mousepos += mousespeed;
\r
748 s32 Rand(s32 min, s32 max)
\r
750 return (rand()%(max-min+1))+min;
\r
753 bool keydown[KEY_KEY_CODES_COUNT];
\r
760 void updateViewingRange(f32 frametime, Client *client)
\r
762 // Range_all messes up frametime_avg
\r
763 if(g_viewing_range_all == true)
\r
766 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
768 // Initialize to the target value
\r
769 static float frametime_avg = 1.0/wanted_fps;
\r
770 frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
772 static f32 counter = 0;
\r
774 counter -= frametime;
\r
777 //counter = 1.0; //seconds
\r
778 counter = 0.5; //seconds
\r
780 //float freetime_ratio = 0.2;
\r
781 //float freetime_ratio = 0.4;
\r
782 float freetime_ratio = FREETIME_RATIO;
\r
784 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
786 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
788 static bool fraction_is_good = false;
\r
790 float fraction_good_threshold = 0.1;
\r
791 float fraction_bad_threshold = 0.25;
\r
792 float fraction_limit;
\r
793 // Use high limit if fraction is good AND the fraction would
\r
794 // lower the range. We want to keep the range fairly high.
\r
795 if(fraction_is_good && fraction > 1.0)
\r
796 fraction_limit = fraction_bad_threshold;
\r
798 fraction_limit = fraction_good_threshold;
\r
800 if(fabs(fraction - 1.0) < fraction_limit)
\r
802 fraction_is_good = true;
\r
807 fraction_is_good = false;
\r
810 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
811 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
812 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
813 <<" faction = "<<fraction<<std::endl;*/
\r
815 JMutexAutoLock lock(g_range_mutex);
\r
817 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
818 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
820 s16 n = (float)g_viewing_range_nodes / fraction;
\r
821 if(n < viewing_range_nodes_min)
\r
822 n = viewing_range_nodes_min;
\r
823 if(n > viewing_range_nodes_max)
\r
824 n = viewing_range_nodes_max;
\r
826 bool can_change = true;
\r
828 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
829 can_change = false;
\r
832 g_viewing_range_nodes = n;
\r
834 /*dstream<<"g_viewing_range_nodes = "
\r
835 <<g_viewing_range_nodes<<std::endl;*/
\r
838 class GUIQuickInventory : public IEventReceiver
\r
842 gui::IGUIEnvironment* env,
\r
843 gui::IGUIElement* parent,
\r
846 Inventory *inventory):
\r
847 m_itemcount(itemcount),
\r
848 m_inventory(inventory)
\r
850 core::rect<s32> imgsize(0,0,48,48);
\r
851 core::rect<s32> textsize(0,0,48,16);
\r
852 v2s32 spacing(0, 64);
\r
853 for(s32 i=0; i<m_itemcount; i++)
\r
855 m_images.push_back(env->addImage(
\r
856 imgsize + pos + spacing*i
\r
858 m_images[i]->setScaleImage(true);
\r
859 m_texts.push_back(env->addStaticText(
\r
861 textsize + pos + spacing*i,
\r
864 m_texts[i]->setBackgroundColor(
\r
865 video::SColor(128,0,0,0));
\r
866 m_texts[i]->setTextAlignment(
\r
868 gui::EGUIA_UPPERLEFT);
\r
872 virtual bool OnEvent(const SEvent& event)
\r
877 void setSelection(s32 i)
\r
886 start = m_selection - m_itemcount / 2;
\r
888 for(s32 i=0; i<m_itemcount; i++)
\r
892 if(j > (s32)m_inventory->getSize() - 1)
\r
893 j -= m_inventory->getSize();
\r
895 j += m_inventory->getSize();
\r
897 InventoryItem *item = m_inventory->getItem(j);
\r
901 m_images[i]->setImage(NULL);
\r
904 if(m_selection == j)
\r
905 swprintf(t, 10, L"<-");
\r
907 swprintf(t, 10, L"");
\r
908 m_texts[i]->setText(t);
\r
910 // The next ifs will segfault with a NULL pointer
\r
915 m_images[i]->setImage(item->getImage());
\r
918 if(m_selection == j)
\r
919 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
921 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
922 m_texts[i]->setText(t);
\r
928 core::array<gui::IGUIStaticText*> m_texts;
\r
929 core::array<gui::IGUIImage*> m_images;
\r
930 Inventory *m_inventory;
\r
934 int main(int argc, char *argv[])
\r
937 Low-level initialization
\r
940 bool disable_stderr = false;
\r
942 disable_stderr = true;
\r
945 // Initialize debug streams
\r
946 debugstreams_init(disable_stderr, DEBUGFILE);
\r
947 // Initialize debug stacks
\r
948 debug_stacks_init();
\r
950 DSTACK(__FUNCTION_NAME);
\r
956 Basic initialization
\r
959 // Initialize default settings
\r
960 set_default_settings();
\r
962 // Print startup message
\r
963 dstream<<DTIME<<"minetest-c55"
\r
964 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
965 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
968 // Set locale. This is for forcing '.' as the decimal point.
\r
969 std::locale::global(std::locale("C"));
\r
970 // This enables printing all characters in bitmap font
\r
971 setlocale(LC_CTYPE, "en_US");
\r
973 // Initialize sockets
\r
975 atexit(sockets_cleanup);
\r
977 // Initialize timestamp mutex
\r
978 g_timestamp_mutex.Init();
\r
992 // Read config file
\r
996 g_settings.readConfigFile(argv[1]);
\r
1000 const char *filenames[2] =
\r
1002 "../minetest.conf",
\r
1003 "../../minetest.conf"
\r
1006 for(u32 i=0; i<2; i++)
\r
1008 bool r = g_settings.readConfigFile(filenames[i]);
\r
1014 // Initialize random seed
\r
1017 g_range_mutex.Init();
\r
1018 assert(g_range_mutex.IsInitialized());
\r
1020 // Read map parameters from settings
\r
1022 HMParams hm_params;
\r
1023 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1024 hm_params.randmax = g_settings.get("height_randmax");
\r
1025 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1026 hm_params.base = g_settings.get("height_base");
\r
1028 MapParams map_params;
\r
1029 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1030 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1036 std::cout<<std::endl<<std::endl;
\r
1037 char templine[100];
\r
1040 bool dedicated = g_settings.getBoolAsk
\r
1041 ("dedicated_server", "Dedicated server?", false);
\r
1042 std::cout<<"dedicated = "<<dedicated<<std::endl;
\r
1045 u16 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1046 std::cout<<"-> "<<port<<std::endl;
\r
1050 DSTACK("Dedicated server branch");
\r
1052 std::cout<<std::endl;
\r
1053 std::cout<<"========================"<<std::endl;
\r
1054 std::cout<<"Running dedicated server"<<std::endl;
\r
1055 std::cout<<"========================"<<std::endl;
\r
1056 std::cout<<std::endl;
\r
1058 Server server("../map", hm_params, map_params);
\r
1059 server.start(port);
\r
1063 // This is kind of a hack but can be done like this
\r
1064 // because server.step() is very light
\r
1066 server.step(0.030);
\r
1068 static int counter = 0;
\r
1074 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1075 core::list<PlayerInfo>::Iterator i;
\r
1076 static u32 sum_old = 0;
\r
1077 u32 sum = PIChecksum(list);
\r
1078 if(sum != sum_old)
\r
1080 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1081 for(i=list.begin(); i!=list.end(); i++)
\r
1083 i->PrintLine(&std::cout);
\r
1093 bool hosting = false;
\r
1094 char connect_name[100] = "";
\r
1096 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1097 if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1099 std::cout<<g_settings.get("address")<<std::endl;
\r
1100 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1104 std::cin.getline(connect_name, 100);
\r
1107 if(connect_name[0] == 0){
\r
1108 snprintf(connect_name, 100, "127.0.0.1");
\r
1113 std::cout<<"-> hosting"<<std::endl;
\r
1115 std::cout<<"-> "<<connect_name<<std::endl;
\r
1117 char playername[PLAYERNAME_SIZE] = "";
\r
1118 if(g_settings.get("name") != "")
\r
1120 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1124 std::cout<<"Name of player: ";
\r
1125 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1127 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1130 Resolution selection
\r
1135 bool fullscreen = false;
\r
1137 if(g_settings.get("screenW") != "" && g_settings.get("screenH") != "")
\r
1139 screenW = atoi(g_settings.get("screenW").c_str());
\r
1140 screenH = atoi(g_settings.get("screenH").c_str());
\r
1144 u16 resolutions[][3] = {
\r
1145 //W, H, fullscreen
\r
1156 u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
\r
1158 for(u16 i=0; i<res_count; i++)
\r
1160 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1161 <<resolutions[i][1];
\r
1162 if(resolutions[i][2])
\r
1163 std::cout<<" fullscreen"<<std::endl;
\r
1165 std::cout<<" windowed"<<std::endl;
\r
1167 std::cout<<"Select a window resolution number [empty = 2]: ";
\r
1168 std::cin.getline(templine, 100);
\r
1171 if(templine[0] == 0)
\r
1174 r0 = atoi(templine);
\r
1176 if(r0 > res_count || r0 == 0)
\r
1182 std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
\r
1183 <<resolutions[i][1];
\r
1184 if(resolutions[i][2])
\r
1185 std::cout<<" fullscreen"<<std::endl;
\r
1187 std::cout<<" windowed"<<std::endl;
\r
1190 screenW = resolutions[r0-1][0];
\r
1191 screenH = resolutions[r0-1][1];
\r
1192 fullscreen = resolutions[r0-1][2];
\r
1197 MyEventReceiver receiver;
\r
1199 video::E_DRIVER_TYPE driverType;
\r
1202 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1203 driverType = video::EDT_OPENGL;
\r
1205 driverType = video::EDT_OPENGL;
\r
1208 // create device and exit if creation failed
\r
1210 IrrlichtDevice *device;
\r
1211 device = createDevice(driverType,
\r
1212 core::dimension2d<u32>(screenW, screenH),
\r
1213 16, fullscreen, false, false, &receiver);
\r
1215 /*device = createDevice(driverType,
\r
1216 core::dimension2d<u32>(screenW, screenH),
\r
1217 16, fullscreen, false, true, &receiver);*/
\r
1220 return 1; // could not create selected driver.
\r
1222 g_device = device;
\r
1224 device->setResizable(true);
\r
1226 if(g_settings.getBool("random_input"))
\r
1227 g_input = new RandomInputHandler();
\r
1229 g_input = new RealInputHandler(device, &receiver);
\r
1232 Continue initialization
\r
1235 video::IVideoDriver* driver = device->getVideoDriver();
\r
1236 // These make the textures not to show at all
\r
1237 //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
\r
1238 //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );
\r
1240 //driver->setMinHardwareBufferVertexCount(1);
\r
1242 scene::ISceneManager* smgr = device->getSceneManager();
\r
1244 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1245 gui::IGUISkin* skin = guienv->getSkin();
\r
1246 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1248 skin->setFont(font);
\r
1249 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1250 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1251 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1252 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1253 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1254 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1256 const wchar_t *text = L"Loading and connecting...";
\r
1257 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1258 core::dimension2d<u32> textd = font->getDimension(text);
\r
1259 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1260 // Have to add a bit to disable the text from word wrapping
\r
1261 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1262 core::vector2d<s32> textsize(300, textd.Height);
\r
1263 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1265 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1266 text, textrect, false, false);
\r
1267 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1269 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1270 guienv->drawAll();
\r
1271 driver->endScene();
\r
1274 Initialize material array
\r
1277 //video::SMaterial g_materials[MATERIALS_COUNT];
\r
1278 for(u16 i=0; i<MATERIALS_COUNT; i++)
\r
1280 g_materials[i].Lighting = false;
\r
1281 g_materials[i].BackfaceCulling = false;
\r
1283 const char *filename = g_material_filenames[i];
\r
1284 if(filename != NULL){
\r
1285 video::ITexture *t = driver->getTexture(filename);
\r
1287 std::cout<<DTIME<<"Texture could not be loaded: \""
\r
1288 <<filename<<"\""<<std::endl;
\r
1291 g_materials[i].setTexture(0, driver->getTexture(filename));
\r
1293 //g_materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
\r
1294 g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1295 //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
\r
1296 //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1297 if(i == MATERIAL_WATER)
\r
1299 g_materials[i].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
\r
1300 //g_materials[i].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
\r
1304 /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png"));
\r
1305 g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png"));
\r
1306 g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png"));
\r
1307 for(u32 i=0; i<3; i++)
\r
1309 g_mesh_materials[i].Lighting = false;
\r
1310 g_mesh_materials[i].BackfaceCulling = false;
\r
1311 g_mesh_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
\r
1312 g_mesh_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
\r
1315 // Make a scope here for the client so that it gets removed
\r
1316 // before the irrlicht device
\r
1319 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1324 SharedPtr<Server> server;
\r
1326 server = new Server("../map", hm_params, map_params);
\r
1327 server->start(port);
\r
1334 // TODO: Get rid of the g_materials parameter or it's globalness
\r
1335 Client client(device, g_materials,
\r
1336 g_settings.getFloat("client_delete_unused_sectors_timeout"),
\r
1339 Address connect_address(0,0,0,0, port);
\r
1341 connect_address.Resolve(connect_name);
\r
1343 catch(ResolveError &e)
\r
1345 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1349 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1350 client.connect(connect_address);
\r
1353 while(client.connectedAndInitialized() == false)
\r
1356 if(server != NULL){
\r
1357 server->step(0.1);
\r
1362 catch(con::PeerNotFoundException &e)
\r
1364 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1369 Create the camera node
\r
1372 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1373 0, // Camera parent
\r
1374 v3f(BS*100, BS*2, BS*100), // Look from
\r
1375 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1379 if(camera == NULL)
\r
1382 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1384 camera->setFOV(FOV_ANGLE);
\r
1386 // Just so big a value that everything rendered is visible
\r
1387 camera->setFarValue(100000*BS);
\r
1389 /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1390 f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
\r
1392 camera->setFarValue(range);
\r
1396 video::EFT_FOG_LINEAR,
\r
1404 f32 camera_yaw = 0; // "right/left"
\r
1405 f32 camera_pitch = 0; // "up/down"
\r
1407 gui_loadingtext->remove();
\r
1410 Add some gui stuff
\r
1413 // First line of debug text
\r
1414 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1416 core::rect<s32>(5, 5, 5+600, 5+textsize.Y),
\r
1418 // Second line of debug text
\r
1419 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1421 core::rect<s32>(5, 5+(textsize.Y+5)*1, 5+600, (5+textsize.Y)*2),
\r
1424 // At the middle of the screen
\r
1425 // Object infos are shown in this
\r
1426 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1428 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1431 // This is a copy of the inventory that the client's environment has
\r
1432 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1434 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1435 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1438 Some statistics are collected in these
\r
1441 u32 scenetime = 0;
\r
1442 u32 endscenetime = 0;
\r
1450 virtual void sendText(std::string text) = 0;
\r
1453 struct TextDestSign : public TextDest
\r
1455 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1457 m_blockpos = blockpos;
\r
1459 m_client = client;
\r
1461 void sendText(std::string text)
\r
1463 dstream<<"Changing text of a sign object: "
\r
1464 <<text<<std::endl;
\r
1465 m_client->sendSignText(m_blockpos, m_id, text);
\r
1473 TextDest *textbuf_dest = NULL;
\r
1475 //gui::IGUIWindow* input_window = NULL;
\r
1476 gui::IGUIStaticText* input_guitext = NULL;
\r
1482 bool first_loop_after_window_activation = true;
\r
1484 // Time is in milliseconds
\r
1485 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1486 // NOTE: Have to call run() between calls of this to update the timer
\r
1487 u32 lasttime = device->getTimer()->getTime();
\r
1489 while(device->run())
\r
1491 // Hilight boxes collected during the loop and displayed
\r
1492 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1495 std::wstring infotext;
\r
1497 //TimeTaker //timer1("//timer1", device);
\r
1499 // Time of frame without fps limit
\r
1503 // not using getRealTime is necessary for wine
\r
1504 u32 time = device->getTimer()->getTime();
\r
1505 if(time > lasttime)
\r
1506 busytime_u32 = time - lasttime;
\r
1509 busytime = busytime_u32 / 1000.0;
\r
1512 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1514 // Absolutelu necessary for wine!
\r
1521 //updateViewingRange(dtime, &client);
\r
1522 updateViewingRange(busytime, &client);
\r
1529 float fps_max = g_settings.getFloat("fps_max");
\r
1530 u32 frametime_min = 1000./fps_max;
\r
1532 if(busytime_u32 < frametime_min)
\r
1534 u32 sleeptime = frametime_min - busytime_u32;
\r
1535 device->sleep(sleeptime);
\r
1539 // Absolutelu necessary for wine!
\r
1543 Time difference calculation
\r
1545 f32 dtime; // in seconds
\r
1547 u32 time = device->getTimer()->getTime();
\r
1548 if(time > lasttime)
\r
1549 dtime = (time - lasttime) / 1000.0;
\r
1555 Time average and jitter calculation
\r
1558 static f32 dtime_avg1 = 0.0;
\r
1559 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1560 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1562 static f32 dtime_jitter1_max_sample = 0.0;
\r
1563 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1565 static f32 jitter1_max = 0.0;
\r
1566 static f32 counter = 0.0;
\r
1567 if(dtime_jitter1 > jitter1_max)
\r
1568 jitter1_max = dtime_jitter1;
\r
1573 dtime_jitter1_max_sample = jitter1_max;
\r
1574 dtime_jitter1_max_fraction
\r
1575 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1576 jitter1_max = 0.0;
\r
1579 Control freetime ratio
\r
1581 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1583 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1584 g_freetime_ratio += 0.01;
\r
1588 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1589 g_freetime_ratio -= 0.01;
\r
1595 Busytime average and jitter calculation
\r
1598 static f32 busytime_avg1 = 0.0;
\r
1599 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1600 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1602 static f32 busytime_jitter1_max_sample = 0.0;
\r
1603 static f32 busytime_jitter1_min_sample = 0.0;
\r
1605 static f32 jitter1_max = 0.0;
\r
1606 static f32 jitter1_min = 0.0;
\r
1607 static f32 counter = 0.0;
\r
1608 if(busytime_jitter1 > jitter1_max)
\r
1609 jitter1_max = busytime_jitter1;
\r
1610 if(busytime_jitter1 < jitter1_min)
\r
1611 jitter1_min = busytime_jitter1;
\r
1613 if(counter > 0.0){
\r
1615 busytime_jitter1_max_sample = jitter1_max;
\r
1616 busytime_jitter1_min_sample = jitter1_min;
\r
1617 jitter1_max = 0.0;
\r
1618 jitter1_min = 0.0;
\r
1623 Debug info for client
\r
1626 static float counter = 0.0;
\r
1631 client.printDebugInfo(std::cout);
\r
1636 Input handler step()
\r
1638 g_input->step(dtime);
\r
1649 Player speed control
\r
1652 if(g_game_focused)
\r
1659 bool a_superspeed,
\r
1662 PlayerControl control(
\r
1663 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1664 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1665 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1666 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1667 g_input->isKeyDown(irr::KEY_SPACE),
\r
1668 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1672 client.setPlayerControl(control);
\r
1676 // Set every key to inactive
\r
1677 PlayerControl control;
\r
1678 client.setPlayerControl(control);
\r
1683 Process environment
\r
1687 //TimeTaker timer("client.step(dtime)", device);
\r
1688 client.step(dtime);
\r
1689 //client.step(dtime_avg1);
\r
1692 if(server != NULL)
\r
1694 //TimeTaker timer("server->step(dtime)", device);
\r
1695 server->step(dtime);
\r
1698 v3f player_position = client.getPlayerPosition();
\r
1700 //TimeTaker //timer2("//timer2", device);
\r
1703 Mouse and camera control
\r
1706 if(device->isWindowActive() && g_game_focused)
\r
1708 device->getCursorControl()->setVisible(false);
\r
1710 if(first_loop_after_window_activation){
\r
1711 //std::cout<<"window active, first loop"<<std::endl;
\r
1712 first_loop_after_window_activation = false;
\r
1715 s32 dx = g_input->getMousePos().X - 320;
\r
1716 s32 dy = g_input->getMousePos().Y - 240;
\r
1717 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1718 camera_yaw -= dx*0.2;
\r
1719 camera_pitch += dy*0.2;
\r
1720 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1721 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1723 g_input->setMousePos(320, 240);
\r
1726 device->getCursorControl()->setVisible(true);
\r
1728 //std::cout<<"window inactive"<<std::endl;
\r
1729 first_loop_after_window_activation = true;
\r
1732 camera_yaw = wrapDegrees(camera_yaw);
\r
1733 camera_pitch = wrapDegrees(camera_pitch);
\r
1735 v3f camera_direction = v3f(0,0,1);
\r
1736 camera_direction.rotateYZBy(camera_pitch);
\r
1737 camera_direction.rotateXZBy(camera_yaw);
\r
1739 v3f camera_position =
\r
1740 player_position + v3f(0, BS+BS/2, 0);
\r
1742 camera->setPosition(camera_position);
\r
1743 // *100.0 helps in large map coordinates
\r
1744 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1746 if(FIELD_OF_VIEW_TEST){
\r
1747 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1748 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1751 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1752 //TimeTaker timer("client.updateCamera", device);
\r
1753 client.updateCamera(camera_position, camera_direction);
\r
1757 //TimeTaker //timer3("//timer3", device);
\r
1760 Calculate what block is the crosshair pointing to
\r
1763 //u32 t1 = device->getTimer()->getRealTime();
\r
1765 //f32 d = 4; // max. distance
\r
1766 f32 d = 4; // max. distance
\r
1767 core::line3d<f32> shootline(camera_position,
\r
1768 camera_position + camera_direction * BS * (d+1));
\r
1770 MapBlockObject *selected_object = client.getSelectedObject
\r
1771 (d*BS, camera_position, shootline);
\r
1773 if(selected_object != NULL)
\r
1775 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1777 core::aabbox3d<f32> box_on_map
\r
1778 = selected_object->getSelectionBoxOnMap();
\r
1780 hilightboxes.push_back(box_on_map);
\r
1782 infotext = narrow_to_wide(selected_object->infoText());
\r
1784 if(g_input->getLeftClicked())
\r
1786 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1787 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1788 selected_object->getId(), g_selected_item);
\r
1790 else if(g_input->getRightClicked())
\r
1792 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1794 Check if we want to modify the object ourselves
\r
1796 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1798 dstream<<"Sign object right-clicked"<<std::endl;
\r
1802 input_guitext = guienv->addStaticText(L"",
\r
1803 core::rect<s32>(150,100,350,120),
\r
1805 false, // wordwrap?
\r
1808 input_guitext->setDrawBackground(true);
\r
1810 g_text_buffer = L"";
\r
1811 g_text_buffer_accepted = false;
\r
1812 textbuf_dest = new TextDestSign(
\r
1813 selected_object->getBlock()->getPos(),
\r
1814 selected_object->getId(),
\r
1818 Otherwise pass the event to the server as-is
\r
1822 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1823 selected_object->getId(), g_selected_item);
\r
1827 else // selected_object == NULL
\r
1830 bool nodefound = false;
\r
1832 v3s16 neighbourpos;
\r
1833 core::aabbox3d<f32> nodefacebox;
\r
1834 f32 mindistance = BS * 1001;
\r
1836 v3s16 pos_i = floatToInt(player_position);
\r
1838 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1842 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1843 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1844 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1845 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1846 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1847 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1849 for(s16 y = ystart; y <= yend; y++){
\r
1850 for(s16 z = zstart; z <= zend; z++){
\r
1851 for(s16 x = xstart; x <= xend; x++)
\r
1854 if(client.getNode(v3s16(x,y,z)).d == MATERIAL_AIR){
\r
1857 }catch(InvalidPositionException &e){
\r
1862 v3f npf = intToFloat(np);
\r
1866 v3s16 directions[6] = {
\r
1867 v3s16(0,0,1), // back
\r
1868 v3s16(0,1,0), // top
\r
1869 v3s16(1,0,0), // right
\r
1875 for(u16 i=0; i<6; i++){
\r
1877 v3f dir_f = v3f(directions[i].X,
\r
1878 directions[i].Y, directions[i].Z);
\r
1879 v3f centerpoint = npf + dir_f * BS/2;
\r
1881 (centerpoint - camera_position).getLength();
\r
1883 if(distance < mindistance){
\r
1884 //std::cout<<DTIME<<"for centerpoint=("<<centerpoint.X<<","<<centerpoint.Y<<","<<centerpoint.Z<<"): distance < mindistance"<<std::endl;
\r
1885 //std::cout<<DTIME<<"npf=("<<npf.X<<","<<npf.Y<<","<<npf.Z<<")"<<std::endl;
\r
1886 core::CMatrix4<f32> m;
\r
1887 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1889 // This is the back face
\r
1890 v3f corners[2] = {
\r
1891 v3f(BS/2, BS/2, BS/2),
\r
1892 v3f(-BS/2, -BS/2, BS/2+d)
\r
1895 for(u16 j=0; j<2; j++){
\r
1896 m.rotateVect(corners[j]);
\r
1897 corners[j] += npf;
\r
1898 //std::cout<<DTIME<<"box corners["<<j<<"]: ("<<corners[j].X<<","<<corners[j].Y<<","<<corners[j].Z<<")"<<std::endl;
\r
1901 //core::aabbox3d<f32> facebox(corners[0],corners[1]);
\r
1902 core::aabbox3d<f32> facebox(corners[0]);
\r
1903 facebox.addInternalPoint(corners[1]);
\r
1905 if(facebox.intersectsWithLine(shootline)){
\r
1908 neighbourpos = np + directions[i];
\r
1909 mindistance = distance;
\r
1910 nodefacebox = facebox;
\r
1918 //std::cout<<DTIME<<"nodefound == true"<<std::endl;
\r
1919 //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
1920 //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
\r
1922 static v3s16 nodepos_old(-1,-1,-1);
\r
1923 if(nodepos != nodepos_old){
\r
1924 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
1925 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
1926 nodepos_old = nodepos;
\r
1928 /*wchar_t positiontext[20];
\r
1929 swprintf(positiontext, 20, L"(%i,%i,%i)",
\r
1930 nodepos.X, nodepos.Y, nodepos.Z);
\r
1931 positiontextgui->setText(positiontext);*/
\r
1934 hilightboxes.push_back(nodefacebox);
\r
1936 if(g_input->getLeftClicked())
\r
1938 //std::cout<<DTIME<<"Removing node"<<std::endl;
\r
1939 //client.removeNode(nodepos);
\r
1940 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
1941 client.clickGround(0, nodepos, neighbourpos, g_selected_item);
\r
1943 if(g_input->getRightClicked())
\r
1945 //std::cout<<DTIME<<"Placing node"<<std::endl;
\r
1946 //client.addNodeFromInventory(neighbourpos, g_selected_item);
\r
1947 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
1948 client.clickGround(1, nodepos, neighbourpos, g_selected_item);
\r
1952 //std::cout<<DTIME<<"nodefound == false"<<std::endl;
\r
1953 //positiontextgui->setText(L"");
\r
1956 } // selected_object == NULL
\r
1958 g_input->resetLeftClicked();
\r
1959 g_input->resetRightClicked();
\r
1962 Calculate stuff for drawing
\r
1965 v2u32 screensize = driver->getScreenSize();
\r
1966 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1968 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
1971 Update gui stuff (0ms)
\r
1974 //TimeTaker guiupdatetimer("Gui updating", device);
\r
1977 wchar_t temptext[100];
\r
1979 static float drawtime_avg = 0;
\r
1980 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
1981 static float scenetime_avg = 0;
\r
1982 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
1983 static float endscenetime_avg = 0;
\r
1984 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
1986 swprintf(temptext, 100, L"Minetest-c55 ("
\r
1988 L", R: range_all=%i"
\r
1990 L" drawtime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
1992 g_viewing_range_all,
\r
1998 guitext->setText(temptext);
\r
2002 wchar_t temptext[100];
\r
2003 /*swprintf(temptext, 100,
\r
2005 L"% .3f < btime_jitter < % .3f"
\r
2006 L", dtime_jitter = % .1f %%"
\r
2007 //L", ftime_ratio = % .3f"
\r
2009 busytime_jitter1_min_sample,
\r
2010 busytime_jitter1_max_sample,
\r
2011 dtime_jitter1_max_fraction * 100.0
\r
2012 //g_freetime_ratio
\r
2014 swprintf(temptext, 100,
\r
2015 L"(% .1f, % .1f, % .1f)"
\r
2016 L" (% .3f < btime_jitter < % .3f"
\r
2017 L", dtime_jitter = % .1f %%)",
\r
2018 player_position.X/BS,
\r
2019 player_position.Y/BS,
\r
2020 player_position.Z/BS,
\r
2021 busytime_jitter1_min_sample,
\r
2022 busytime_jitter1_max_sample,
\r
2023 dtime_jitter1_max_fraction * 100.0
\r
2026 guitext2->setText(temptext);
\r
2030 /*wchar_t temptext[100];
\r
2031 swprintf(temptext, 100,
\r
2032 SWPRINTF_CHARSTRING,
\r
2033 infotext.substr(0,99).c_str()
\r
2036 guitext_info->setText(temptext);*/
\r
2038 guitext_info->setText(infotext.c_str());
\r
2045 static u16 old_selected_item = 65535;
\r
2046 if(client.getLocalInventoryUpdated()
\r
2047 || g_selected_item != old_selected_item)
\r
2049 old_selected_item = g_selected_item;
\r
2050 //std::cout<<"Updating local inventory"<<std::endl;
\r
2051 client.getLocalInventory(local_inventory);
\r
2052 quick_inventory->setSelection(g_selected_item);
\r
2053 quick_inventory->update();
\r
2056 if(input_guitext != NULL)
\r
2058 /*wchar_t temptext[100];
\r
2059 swprintf(temptext, 100,
\r
2060 SWPRINTF_CHARSTRING,
\r
2061 g_text_buffer.substr(0,99).c_str()
\r
2063 input_guitext->setText(g_text_buffer.c_str());
\r
2069 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2071 input_guitext->remove();
\r
2072 input_guitext = NULL;
\r
2074 if(textbuf_dest != NULL)
\r
2076 std::string text = wide_to_narrow(g_text_buffer);
\r
2077 dstream<<"Sending text: "<<text<<std::endl;
\r
2078 textbuf_dest->sendText(text);
\r
2079 delete textbuf_dest;
\r
2080 textbuf_dest = NULL;
\r
2086 //guiupdatetimer.stop();
\r
2092 TimeTaker drawtimer("Drawing", device);
\r
2095 Background color is choosen based on whether the player is
\r
2096 much beyond the initial ground level
\r
2098 /*video::SColor bgcolor;
\r
2099 v3s16 p0 = Map::floatToInt(player_position);
\r
2100 // Does this make short random delays?
\r
2101 // NOTE: no need for this, sky doesn't show underground with
\r
2103 bool is_underground = client.isNodeUnderground(p0);
\r
2104 //bool is_underground = false;
\r
2105 if(is_underground == false)
\r
2106 bgcolor = video::SColor(255,90,140,200);
\r
2108 bgcolor = video::SColor(255,0,0,0);*/
\r
2110 //video::SColor bgcolor = video::SColor(255,90,140,200);
\r
2111 video::SColor bgcolor = skycolor;
\r
2114 driver->beginScene(true, true, bgcolor);
\r
2118 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2121 TimeTaker timer("smgr", device);
\r
2123 scenetime = timer.stop(true);
\r
2127 //TimeTaker timer9("auxiliary drawings", device);
\r
2130 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2131 displaycenter + core::vector2d<s32>(10,0),
\r
2132 video::SColor(255,255,255,255));
\r
2133 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2134 displaycenter + core::vector2d<s32>(0,10),
\r
2135 video::SColor(255,255,255,255));
\r
2138 //TimeTaker //timer10("//timer10", device);
\r
2140 video::SMaterial m;
\r
2142 m.Lighting = false;
\r
2143 driver->setMaterial(m);
\r
2145 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2147 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2148 i != hilightboxes.end(); i++)
\r
2150 /*std::cout<<"hilightbox min="
\r
2151 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2153 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2155 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2161 //TimeTaker //timer11("//timer11", device);
\r
2167 guienv->drawAll();
\r
2171 TimeTaker timer("endScene", device);
\r
2172 driver->endScene();
\r
2173 endscenetime = timer.stop(true);
\r
2176 drawtime = drawtimer.stop(true);
\r
2182 static s16 lastFPS = 0;
\r
2183 //u16 fps = driver->getFPS();
\r
2184 u16 fps = (1.0/dtime_avg1);
\r
2186 if (lastFPS != fps)
\r
2188 core::stringw str = L"Minetest [";
\r
2189 str += driver->getName();
\r
2193 device->setWindowCaption(str.c_str());
\r
2199 device->yield();*/
\r
2202 } // client is deleted at this point
\r
2207 In the end, delete the Irrlicht device.
\r
2212 catch(con::PeerNotFoundException &e)
\r
2214 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2216 #if CATCH_EXCEPTIONS
\r
2218 This is what has to be done in every thread to get suitable debug info
\r
2220 catch(std::exception &e)
\r
2222 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2223 <<e.what()<<std::endl;
\r
2228 debugstreams_deinit();
\r