3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
\r
5 This program is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU General Public License as published by
\r
7 the Free Software Foundation; either version 2 of the License, or
\r
8 (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License along
\r
16 with this program; if not, write to the Free Software Foundation, Inc.,
\r
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 =============================== NOTES ==============================
\r
22 NOTE: Things starting with TODO are sometimes only suggestions.
\r
24 NOTE: VBO cannot be turned on for fast-changing stuff because there
\r
25 is an apparanet memory leak in irrlicht when using it (not sure)
\r
27 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
28 NOTE: Global locale is now set at initialization
\r
30 SUGG: Fix address to be ipv6 compatible
\r
32 FIXME: When a new sector is generated, it may change the ground level
\r
33 of it's and it's neighbors border that two blocks that are
\r
34 above and below each other and that are generated before and
\r
35 after the sector heightmap generation (order doesn't matter),
\r
36 can have a small gap between each other at the border.
\r
37 SUGGESTION: Use same technique for sector heightmaps as what we're
\r
38 using for UnlimitedHeightmap? (getting all neighbors
\r
41 SUGG: Transfer more blocks in a single packet
\r
42 SUGG: A blockdata combiner class, to which blocks are added and at
\r
43 destruction it sends all the stuff in as few packets as possible.
\r
45 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
46 SUGG: Fetch stuff mainly from the viewing direction
\r
48 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
49 - This enables saving many packets and making a faster connection
\r
50 - This also enables server to check if client has received the
\r
51 most recent block sent, for example.
\r
52 SUGG: Add a sane bandwidth throttling system to Connection
\r
54 SUGG: More fine-grained control of client's dumping of blocks from
\r
56 - ...What does this mean in the first place?
\r
58 SUGG: A map editing mode (similar to dedicated server mode)
\r
60 SUGG: Add a time value to the param of footstepped grass and check it
\r
61 against a global timer when a block is accessed, to make old
\r
64 SUGG: Make a copy of close-range environment on client for showing
\r
65 on screen, with minimal mutexes to slow down the main loop
\r
67 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
68 it by sending more stuff in a single packet.
\r
69 - Add a packet queue to RemoteClient, from which packets will be
\r
70 combined with object data packets
\r
71 - This is not exactly trivial: the object data packets are
\r
72 sometimes very big by themselves
\r
74 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
75 - This will allow saving ages of rats on disk but not sending
\r
78 SUGG: Implement lighting using VoxelManipulator
\r
79 - Would it be significantly faster?
\r
81 FIXME: Rats somehow go underground sometimes (you can see it in water)
\r
82 - Does their position get saved to a border value or something?
\r
83 - Does this happen anymore?
\r
85 SUGG: MovingObject::move and Player::move are basically the same.
\r
88 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
89 if something is already in it)
\r
90 - Use it in active block queue in water flowing
\r
92 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
93 need an additional metadata field for the texts
\r
94 - This is also needed for item container chests
\r
96 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
98 SUGG: A version number to blocks, which increments when the block is
\r
99 modified (node add/remove, water update, lighting update)
\r
100 - This can then be used to make sure the most recent version of
\r
101 a block has been sent to client
\r
103 SUGG: Make the amount of blocks sending to client and the total
\r
104 amount of blocks dynamically limited. Transferring blocks is the
\r
105 main network eater of this system, so it is the one that has
\r
106 to be throttled so that RTTs stay low.
\r
108 TODO: Untie client network operations from framerate
\r
109 - Needs some input queues or something
\r
110 - Not really necessary?
\r
112 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
114 - That is >500 vertices
\r
116 TODO: Better dungeons
\r
119 TODO: Startup and configuration menu
\r
121 TODO: There are some lighting-related todos and fixmes in
\r
122 ServerMap::emergeBlock
\r
124 TODO: Proper handling of spawning place (try to find something that
\r
125 is not in the middle of an ocean (some land to stand on at
\r
126 least) and save it in map config.
\r
128 TODO: Players to only be hidden when the client quits.
\r
129 TODO: - Players to be saved on disk, with inventory
\r
130 TODO: Players to be saved as text in map/players/<name>
\r
131 TODO: Player inventory to be saved on disk
\r
133 TODO: Make fetching sector's blocks more efficient when rendering
\r
134 sectors that have very large amounts of blocks (on client)
\r
136 TODO: Make the video backend selectable
\r
138 Block object server side:
\r
139 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
140 - For all blocks in the buffer, objects are stepped(). This
\r
141 means they are active.
\r
142 - TODO: A global active buffer is needed for the server
\r
143 - TODO: A timestamp to blocks
\r
144 - TODO: All blocks going in and out of the buffer are recorded.
\r
145 - TODO: For outgoing blocks, timestamp is written.
\r
146 - TODO: For incoming blocks, time difference is calculated and
\r
147 objects are stepped according to it.
\r
149 TODO: Copy the text of the last picked sign to inventory in creative
\r
152 TODO: Get rid of GotSplitPacketException
\r
154 TODO: Check what goes wrong with caching map to disk (Kray)
\r
156 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
158 TODO: TOSERVER_LEAVE
\r
160 TODO: Better handling of objects and mobs
\r
162 - There has to be some way to do it with less spaghetti code
\r
163 - Make separate classes for client and server
\r
164 - Client should not discriminate between blocks, server should
\r
165 - Make other players utilize the same framework
\r
167 TODO: Draw big amounts of torches better (that is, throw them in the
\r
168 same meshbuffer (can the meshcollector class be used?))
\r
171 ======================================================================
\r
173 TODO: Convert the text input system to use a modal menu... or something
\r
175 ======================================================================
\r
180 Setting this to 1 enables a special camera mode that forces
\r
181 the renderers to think that the camera statically points from
\r
182 the starting place to a static direction.
\r
184 This allows one to move around with the player and see what
\r
185 is actually drawn behind solid things and behind the player.
\r
187 #define FIELD_OF_VIEW_TEST 0
\r
189 #ifdef UNITTEST_DISABLE
\r
191 #pragma message ("Disabling unit tests")
\r
193 #warning "Disabling unit tests"
\r
195 // Disable unit tests
\r
196 #define ENABLE_TESTS 0
\r
198 // Enable unit tests
\r
199 #define ENABLE_TESTS 1
\r
203 #pragma comment(lib, "Irrlicht.lib")
\r
204 #pragma comment(lib, "jthread.lib")
\r
205 #pragma comment(lib, "zlibwapi.lib")
\r
206 // This would get rid of the console window
\r
207 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
210 #include <iostream>
\r
212 #include <jmutexautolock.h>
\r
213 #include <locale.h>
\r
214 #include "common_irrlicht.h"
\r
217 #include "player.h"
\r
220 #include "environment.h"
\r
221 #include "server.h"
\r
222 #include "client.h"
\r
223 #include "serialization.h"
\r
224 #include "constants.h"
\r
225 #include "strfnd.h"
\r
226 #include "porting.h"
\r
227 #include "guiPauseMenu.h"
\r
228 #include "irrlichtwrapper.h"
\r
229 #include "gettime.h"
\r
230 #include "porting.h"
\r
231 #include "guiInventoryMenu.h"
\r
233 IrrlichtWrapper *g_irrlicht;
\r
235 // All range-related stuff below is locked behind this
\r
236 JMutex g_range_mutex;
\r
238 // Blocks are viewed in this range from the player
\r
239 s16 g_viewing_range_nodes = 60;
\r
240 //s16 g_viewing_range_nodes = 0;
\r
242 // This is updated by the client's fetchBlocks routine
\r
243 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
245 // If true, the preceding value has no meaning and all blocks
\r
246 // already existing in memory are drawn
\r
247 bool g_viewing_range_all = false;
\r
249 // This is the freetime ratio imposed by the dynamic viewing
\r
250 // range changing code.
\r
251 // It is controlled by the main loop to the smallest value that
\r
252 // inhibits glitches (dtime jitter) in the main loop.
\r
253 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
257 These are loaded from the config file.
\r
260 Settings g_settings;
\r
262 extern void set_default_settings();
\r
268 //u16 g_selected_material = 0;
\r
269 u16 g_selected_item = 0;
\r
271 IrrlichtDevice *g_device = NULL;
\r
276 gui::IGUIEnvironment* guienv = NULL;
\r
277 gui::IGUIStaticText *guiroot = NULL;
\r
278 int g_active_menu_count = 0;
\r
280 bool noMenuActive()
\r
282 return (g_active_menu_count == 0);
\r
285 // Inventory actions from the menu are buffered here before sending
\r
286 Queue<InventoryAction*> inventory_action_queue;
\r
287 // This is a copy of the inventory that the client's environment has
\r
288 Inventory local_inventory;
\r
290 std::wstring g_text_buffer;
\r
291 bool g_text_buffer_accepted = false;
\r
293 // When true, the mouse and keyboard are grabbed
\r
294 bool g_game_focused = true;
\r
301 std::ostream *dout_con_ptr = &dummyout;
\r
302 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
303 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
304 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
305 //std::ostream *dout_con_ptr = &dstream;
\r
306 //std::ostream *derr_con_ptr = &dstream;
\r
309 std::ostream *dout_server_ptr = &dstream;
\r
310 std::ostream *derr_server_ptr = &dstream;
\r
313 std::ostream *dout_client_ptr = &dstream;
\r
314 std::ostream *derr_client_ptr = &dstream;
\r
317 gettime.h implementation
\r
323 Use irrlicht because it is more precise than porting.h's
\r
326 if(g_irrlicht == NULL)
\r
328 return g_irrlicht->getTime();
\r
331 class MyEventReceiver : public IEventReceiver
\r
334 // This is the one method that we have to implement
\r
335 virtual bool OnEvent(const SEvent& event)
\r
337 // Remember whether each key is down or up
\r
338 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
340 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
342 if(event.KeyInput.PressedDown)
\r
344 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
345 if(g_game_focused == false)
\r
347 s16 key = event.KeyInput.Key;
\r
348 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
350 g_text_buffer_accepted = true;
\r
352 else if(key == irr::KEY_BACK)
\r
354 if(g_text_buffer.size() > 0)
\r
355 g_text_buffer = g_text_buffer.substr
\r
356 (0, g_text_buffer.size()-1);
\r
360 wchar_t wc = event.KeyInput.Char;
\r
362 g_text_buffer += wc;
\r
366 //if(pauseMenu != NULL)
\r
367 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
369 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
371 if(g_game_focused == true && noMenuActive())
\r
373 dstream<<DTIME<<"MyEventReceiver: "
\r
374 <<"Launching pause menu"<<std::endl;
\r
375 // It will delete itself by itself
\r
376 GUIPauseMenu *menu = new
\r
377 GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
378 &g_active_menu_count);
\r
385 //if(inventoryMenu != NULL)
\r
386 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
388 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
390 if(g_game_focused == true && noMenuActive())
\r
392 dstream<<DTIME<<"MyEventReceiver: "
\r
393 <<"Launching inventory"<<std::endl;
\r
394 GUIInventoryMenu *inventoryMenu = new
\r
395 GUIInventoryMenu(guienv, guiroot, -1,
\r
396 &local_inventory, &inventory_action_queue,
\r
397 &g_active_menu_count);
\r
398 inventoryMenu->drop();
\r
404 // Material selection
\r
405 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
407 if(g_game_focused == true)
\r
409 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
412 g_selected_item = 0;
\r
413 dstream<<DTIME<<"Selected item: "
\r
414 <<g_selected_item<<std::endl;
\r
418 // Viewing range selection
\r
419 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
422 JMutexAutoLock lock(g_range_mutex);
\r
423 if(g_viewing_range_all)
\r
425 g_viewing_range_all = false;
\r
426 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
430 g_viewing_range_all = true;
\r
431 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
435 // Print debug stacks
\r
436 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
439 dstream<<"-----------------------------------------"
\r
441 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
442 dstream<<"-----------------------------------------"
\r
444 debug_stacks_print();
\r
449 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
451 if(noMenuActive() == false)
\r
453 left_active = false;
\r
454 middle_active = false;
\r
455 right_active = false;
\r
459 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
460 left_active = event.MouseInput.isLeftPressed();
\r
461 middle_active = event.MouseInput.isMiddlePressed();
\r
462 right_active = event.MouseInput.isRightPressed();
\r
464 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
466 leftclicked = true;
\r
468 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
470 rightclicked = true;
\r
472 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
474 leftreleased = true;
\r
476 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
478 rightreleased = true;
\r
480 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
482 /*dstream<<"event.MouseInput.Wheel="
\r
483 <<event.MouseInput.Wheel<<std::endl;*/
\r
484 if(event.MouseInput.Wheel < 0)
\r
486 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
489 g_selected_item = 0;
\r
491 else if(event.MouseInput.Wheel > 0)
\r
493 if(g_selected_item > 0)
\r
496 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
505 // This is used to check whether a key is being held down
\r
506 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
508 return keyIsDown[keyCode];
\r
513 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
514 keyIsDown[i] = false;
\r
515 leftclicked = false;
\r
516 rightclicked = false;
\r
517 leftreleased = false;
\r
518 rightreleased = false;
\r
520 left_active = false;
\r
521 middle_active = false;
\r
522 right_active = false;
\r
528 bool rightreleased;
\r
531 bool middle_active;
\r
535 // We use this array to store the current state of each key
\r
536 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
539 IrrlichtDevice *m_device;
\r
548 virtual ~InputHandler()
\r
552 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
554 virtual v2s32 getMousePos() = 0;
\r
555 virtual void setMousePos(s32 x, s32 y) = 0;
\r
557 virtual bool getLeftState() = 0;
\r
558 virtual bool getRightState() = 0;
\r
560 virtual bool getLeftClicked() = 0;
\r
561 virtual bool getRightClicked() = 0;
\r
562 virtual void resetLeftClicked() = 0;
\r
563 virtual void resetRightClicked() = 0;
\r
565 virtual bool getLeftReleased() = 0;
\r
566 virtual bool getRightReleased() = 0;
\r
567 virtual void resetLeftReleased() = 0;
\r
568 virtual void resetRightReleased() = 0;
\r
570 virtual void step(float dtime) {};
\r
572 virtual void clear() {};
\r
575 InputHandler *g_input = NULL;
\r
580 g_game_focused = true;
\r
585 g_game_focused = false;
\r
588 class RealInputHandler : public InputHandler
\r
591 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
593 m_receiver(receiver)
\r
596 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
598 return m_receiver->IsKeyDown(keyCode);
\r
600 virtual v2s32 getMousePos()
\r
602 return m_device->getCursorControl()->getPosition();
\r
604 virtual void setMousePos(s32 x, s32 y)
\r
606 m_device->getCursorControl()->setPosition(x, y);
\r
609 virtual bool getLeftState()
\r
611 return m_receiver->left_active;
\r
613 virtual bool getRightState()
\r
615 return m_receiver->right_active;
\r
618 virtual bool getLeftClicked()
\r
620 if(g_game_focused == false)
\r
622 return m_receiver->leftclicked;
\r
624 virtual bool getRightClicked()
\r
626 if(g_game_focused == false)
\r
628 return m_receiver->rightclicked;
\r
630 virtual void resetLeftClicked()
\r
632 m_receiver->leftclicked = false;
\r
634 virtual void resetRightClicked()
\r
636 m_receiver->rightclicked = false;
\r
639 virtual bool getLeftReleased()
\r
641 if(g_game_focused == false)
\r
643 return m_receiver->leftreleased;
\r
645 virtual bool getRightReleased()
\r
647 if(g_game_focused == false)
\r
649 return m_receiver->rightreleased;
\r
651 virtual void resetLeftReleased()
\r
653 m_receiver->leftreleased = false;
\r
655 virtual void resetRightReleased()
\r
657 m_receiver->rightreleased = false;
\r
662 resetRightClicked();
\r
663 resetLeftClicked();
\r
666 IrrlichtDevice *m_device;
\r
667 MyEventReceiver *m_receiver;
\r
670 class RandomInputHandler : public InputHandler
\r
673 RandomInputHandler()
\r
675 leftclicked = false;
\r
676 rightclicked = false;
\r
677 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
678 keydown[i] = false;
\r
680 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
682 return keydown[keyCode];
\r
684 virtual v2s32 getMousePos()
\r
688 virtual void setMousePos(s32 x, s32 y)
\r
690 mousepos = v2s32(x,y);
\r
693 virtual bool getLeftState()
\r
697 virtual bool getRightState()
\r
702 virtual bool getLeftClicked()
\r
704 return leftclicked;
\r
706 virtual bool getRightClicked()
\r
708 return rightclicked;
\r
710 virtual void resetLeftClicked()
\r
712 leftclicked = false;
\r
714 virtual void resetRightClicked()
\r
716 rightclicked = false;
\r
719 virtual bool getLeftReleased()
\r
723 virtual bool getRightReleased()
\r
727 virtual void resetLeftReleased()
\r
730 virtual void resetRightReleased()
\r
734 virtual void step(float dtime)
\r
737 static float counter1 = 0;
\r
741 counter1 = 0.1*Rand(1,10);
\r
742 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
743 g_selected_material++;
\r
745 g_selected_material = 0;*/
\r
746 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
749 g_selected_item = 0;
\r
753 static float counter1 = 0;
\r
757 counter1 = 0.1*Rand(1, 40);
\r
758 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
762 static float counter1 = 0;
\r
766 counter1 = 0.1*Rand(1, 40);
\r
767 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
771 static float counter1 = 0;
\r
775 counter1 = 0.1*Rand(1, 40);
\r
776 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
780 static float counter1 = 0;
\r
784 counter1 = 0.1*Rand(1, 40);
\r
785 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
789 static float counter1 = 0;
\r
793 counter1 = 0.1*Rand(1, 20);
\r
794 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
798 static float counter1 = 0;
\r
802 counter1 = 0.1*Rand(1, 30);
\r
803 leftclicked = true;
\r
807 static float counter1 = 0;
\r
811 counter1 = 0.1*Rand(1, 20);
\r
812 rightclicked = true;
\r
815 mousepos += mousespeed;
\r
818 s32 Rand(s32 min, s32 max)
\r
820 return (rand()%(max-min+1))+min;
\r
823 bool keydown[KEY_KEY_CODES_COUNT];
\r
830 void updateViewingRange(f32 frametime, Client *client)
\r
832 // Range_all messes up frametime_avg
\r
833 if(g_viewing_range_all == true)
\r
836 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
838 // Initialize to the target value
\r
839 static float frametime_avg = 1.0/wanted_fps;
\r
840 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
841 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
843 static f32 counter = 0;
\r
845 counter -= frametime;
\r
848 //counter = 1.0; //seconds
\r
849 counter = 0.5; //seconds
\r
851 //float freetime_ratio = 0.2;
\r
852 //float freetime_ratio = 0.4;
\r
853 float freetime_ratio = FREETIME_RATIO;
\r
855 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
857 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
859 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
860 + frametime_avg / frametime_wanted / 2.0;*/
\r
862 //float fraction = frametime_avg / frametime_wanted;
\r
864 static bool fraction_is_good = false;
\r
866 //float fraction_good_threshold = 0.1;
\r
867 //float fraction_bad_threshold = 0.25;
\r
868 float fraction_good_threshold = 0.075;
\r
869 float fraction_bad_threshold = 0.125;
\r
870 float fraction_limit;
\r
871 // Use high limit if fraction is good AND the fraction would
\r
872 // lower the range. We want to keep the range fairly high.
\r
873 if(fraction_is_good && fraction > 1.0)
\r
874 fraction_limit = fraction_bad_threshold;
\r
876 fraction_limit = fraction_good_threshold;
\r
878 if(fabs(fraction - 1.0) < fraction_limit)
\r
880 fraction_is_good = true;
\r
885 fraction_is_good = false;
\r
888 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
889 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
890 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
891 <<" faction = "<<fraction<<std::endl;*/
\r
893 JMutexAutoLock lock(g_range_mutex);
\r
895 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
896 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
898 s16 n = (float)g_viewing_range_nodes / fraction;
\r
899 if(n < viewing_range_nodes_min)
\r
900 n = viewing_range_nodes_min;
\r
901 if(n > viewing_range_nodes_max)
\r
902 n = viewing_range_nodes_max;
\r
904 bool can_change = true;
\r
906 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
907 can_change = false;
\r
910 g_viewing_range_nodes = n;
\r
912 /*dstream<<"g_viewing_range_nodes = "
\r
913 <<g_viewing_range_nodes<<std::endl;*/
\r
916 class GUIQuickInventory : public IEventReceiver
\r
920 gui::IGUIEnvironment* env,
\r
921 gui::IGUIElement* parent,
\r
924 Inventory *inventory):
\r
925 m_itemcount(itemcount),
\r
926 m_inventory(inventory)
\r
928 core::rect<s32> imgsize(0,0,48,48);
\r
929 core::rect<s32> textsize(0,0,48,16);
\r
930 v2s32 spacing(0, 64);
\r
931 for(s32 i=0; i<m_itemcount; i++)
\r
933 m_images.push_back(env->addImage(
\r
934 imgsize + pos + spacing*i
\r
936 m_images[i]->setScaleImage(true);
\r
937 m_texts.push_back(env->addStaticText(
\r
939 textsize + pos + spacing*i,
\r
942 m_texts[i]->setBackgroundColor(
\r
943 video::SColor(128,0,0,0));
\r
944 m_texts[i]->setTextAlignment(
\r
946 gui::EGUIA_UPPERLEFT);
\r
950 virtual bool OnEvent(const SEvent& event)
\r
955 void setSelection(s32 i)
\r
964 start = m_selection - m_itemcount / 2;
\r
966 InventoryList *mainlist = m_inventory->getList("main");
\r
968 for(s32 i=0; i<m_itemcount; i++)
\r
972 if(j > (s32)mainlist->getSize() - 1)
\r
973 j -= mainlist->getSize();
\r
975 j += mainlist->getSize();
\r
977 InventoryItem *item = mainlist->getItem(j);
\r
981 m_images[i]->setImage(NULL);
\r
984 if(m_selection == j)
\r
985 swprintf(t, 10, L"<-");
\r
987 swprintf(t, 10, L"");
\r
988 m_texts[i]->setText(t);
\r
990 // The next ifs will segfault with a NULL pointer
\r
995 m_images[i]->setImage(item->getImage());
\r
998 if(m_selection == j)
\r
999 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1001 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1002 m_texts[i]->setText(t);
\r
1008 core::array<gui::IGUIStaticText*> m_texts;
\r
1009 core::array<gui::IGUIImage*> m_images;
\r
1010 Inventory *m_inventory;
\r
1020 virtual void sendText(std::string text) = 0;
\r
1023 struct TextDestSign : public TextDest
\r
1025 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1027 m_blockpos = blockpos;
\r
1029 m_client = client;
\r
1031 void sendText(std::string text)
\r
1033 dstream<<"Changing text of a sign object: "
\r
1034 <<text<<std::endl;
\r
1035 m_client->sendSignText(m_blockpos, m_id, text);
\r
1046 gui::IGUIStaticText* guitext;
\r
1047 /*std::wstring buffer;
\r
1048 bool buffer_accepted;*/
\r
1054 //buffer_accepted = false;
\r
1057 void start(TextDest *a_dest)
\r
1061 guitext = guienv->addStaticText(L"",
\r
1062 core::rect<s32>(150,100,550,120),
\r
1064 false, // wordwrap?
\r
1067 guitext->setDrawBackground(true);
\r
1069 g_text_buffer = L"";
\r
1070 g_text_buffer_accepted = false;
\r
1076 int main(int argc, char *argv[])
\r
1079 Low-level initialization
\r
1082 bool disable_stderr = false;
\r
1084 disable_stderr = true;
\r
1087 // Initialize debug streams
\r
1088 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1089 // Initialize debug stacks
\r
1090 debug_stacks_init();
\r
1092 DSTACK(__FUNCTION_NAME);
\r
1098 Parse command line
\r
1101 // List all allowed options
\r
1102 core::map<std::string, ValueSpec> allowed_options;
\r
1103 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1104 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1105 "Run server directly"));
\r
1106 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1107 "Load configuration from specified file"));
\r
1108 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1109 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1110 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1111 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1112 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1114 Settings cmd_args;
\r
1116 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1118 if(ret == false || cmd_args.getFlag("help"))
\r
1120 dstream<<"Allowed options:"<<std::endl;
\r
1121 for(core::map<std::string, ValueSpec>::Iterator
\r
1122 i = allowed_options.getIterator();
\r
1123 i.atEnd() == false; i++)
\r
1125 dstream<<" --"<<i.getNode()->getKey();
\r
1126 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1131 dstream<<" <value>";
\r
1133 dstream<<std::endl;
\r
1135 if(i.getNode()->getValue().help != NULL)
\r
1137 dstream<<" "<<i.getNode()->getValue().help
\r
1142 return cmd_args.getFlag("help") ? 0 : 1;
\r
1147 Basic initialization
\r
1150 // Initialize default settings
\r
1151 set_default_settings();
\r
1153 // Print startup message
\r
1154 dstream<<DTIME<<"minetest-c55"
\r
1155 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1156 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1159 // Set locale. This is for forcing '.' as the decimal point.
\r
1160 std::locale::global(std::locale("C"));
\r
1161 // This enables printing all characters in bitmap font
\r
1162 setlocale(LC_CTYPE, "en_US");
\r
1164 // Initialize sockets
\r
1166 atexit(sockets_cleanup);
\r
1176 // Path of configuration file in use
\r
1177 std::string configpath = "";
\r
1179 if(cmd_args.exists("config"))
\r
1181 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1184 dstream<<"Could not read configuration from \""
\r
1185 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1188 configpath = cmd_args.get("config");
\r
1192 const char *filenames[2] =
\r
1194 "../minetest.conf",
\r
1195 "../../minetest.conf"
\r
1198 for(u32 i=0; i<2; i++)
\r
1200 bool r = g_settings.readConfigFile(filenames[i]);
\r
1203 configpath = filenames[i];
\r
1209 // Initialize random seed
\r
1215 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1216 || cmd_args.getFlag("enable-unittests") == true)
\r
1222 Global range mutex
\r
1224 g_range_mutex.Init();
\r
1225 assert(g_range_mutex.IsInitialized());
\r
1227 // Read map parameters from settings
\r
1229 HMParams hm_params;
\r
1230 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1231 hm_params.randmax = g_settings.get("height_randmax");
\r
1232 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1233 hm_params.base = g_settings.get("height_base");
\r
1235 MapParams map_params;
\r
1236 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1237 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1243 std::cout<<std::endl<<std::endl;
\r
1246 <<" .__ __ __ "<<std::endl
\r
1247 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1248 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1249 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1250 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1251 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1254 std::cout<<std::endl;
\r
1255 //char templine[100];
\r
1259 if(cmd_args.exists("port"))
\r
1261 port = cmd_args.getU16("port");
\r
1265 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1266 std::cout<<"-> "<<port<<std::endl;
\r
1269 if(cmd_args.getFlag("server"))
\r
1271 DSTACK("Dedicated server branch");
\r
1273 std::cout<<std::endl;
\r
1274 std::cout<<"========================"<<std::endl;
\r
1275 std::cout<<"Running dedicated server"<<std::endl;
\r
1276 std::cout<<"========================"<<std::endl;
\r
1277 std::cout<<std::endl;
\r
1279 Server server("../map", hm_params, map_params);
\r
1280 server.start(port);
\r
1284 // This is kind of a hack but can be done like this
\r
1285 // because server.step() is very light
\r
1287 server.step(0.030);
\r
1289 static int counter = 0;
\r
1295 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1296 core::list<PlayerInfo>::Iterator i;
\r
1297 static u32 sum_old = 0;
\r
1298 u32 sum = PIChecksum(list);
\r
1299 if(sum != sum_old)
\r
1301 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1302 for(i=list.begin(); i!=list.end(); i++)
\r
1304 i->PrintLine(&std::cout);
\r
1314 bool hosting = false;
\r
1315 char connect_name[100] = "";
\r
1317 if(cmd_args.exists("address"))
\r
1319 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1321 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1323 std::cout<<g_settings.get("address")<<std::endl;
\r
1324 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1328 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1329 std::cin.getline(connect_name, 100);
\r
1332 if(connect_name[0] == 0){
\r
1333 snprintf(connect_name, 100, "127.0.0.1");
\r
1338 std::cout<<"> Hosting game"<<std::endl;
\r
1340 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1342 char playername[PLAYERNAME_SIZE] = "";
\r
1343 if(g_settings.get("name") != "")
\r
1345 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1349 std::cout<<"Name of player: ";
\r
1350 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1352 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1355 Resolution selection
\r
1358 bool fullscreen = false;
\r
1359 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1360 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1364 MyEventReceiver receiver;
\r
1366 video::E_DRIVER_TYPE driverType;
\r
1369 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1370 driverType = video::EDT_OPENGL;
\r
1372 driverType = video::EDT_OPENGL;
\r
1375 // create device and exit if creation failed
\r
1377 IrrlichtDevice *device;
\r
1378 device = createDevice(driverType,
\r
1379 core::dimension2d<u32>(screenW, screenH),
\r
1380 16, fullscreen, false, false, &receiver);
\r
1383 return 1; // could not create selected driver.
\r
1385 g_device = device;
\r
1386 g_irrlicht = new IrrlichtWrapper(device);
\r
1388 //g_device = device;
\r
1390 device->setResizable(true);
\r
1392 bool random_input = g_settings.getBool("random_input")
\r
1393 || cmd_args.getFlag("random-input");
\r
1395 g_input = new RandomInputHandler();
\r
1397 g_input = new RealInputHandler(device, &receiver);
\r
1400 Continue initialization
\r
1403 video::IVideoDriver* driver = device->getVideoDriver();
\r
1406 This changes the minimum allowed number of vertices in a VBO
\r
1408 //driver->setMinHardwareBufferVertexCount(1);
\r
1410 scene::ISceneManager* smgr = device->getSceneManager();
\r
1412 guienv = device->getGUIEnvironment();
\r
1413 gui::IGUISkin* skin = guienv->getSkin();
\r
1414 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1416 skin->setFont(font);
\r
1417 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1418 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1419 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1420 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1421 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1422 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1424 const wchar_t *text = L"Loading and connecting...";
\r
1425 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1426 core::dimension2d<u32> textd = font->getDimension(text);
\r
1427 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1428 // Have to add a bit to disable the text from word wrapping
\r
1429 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1430 core::vector2d<s32> textsize(300, textd.Height);
\r
1431 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1433 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1434 text, textrect, false, false);
\r
1435 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1437 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1438 guienv->drawAll();
\r
1439 driver->endScene();
\r
1442 Preload some textures
\r
1445 tile_materials_preload(g_irrlicht);
\r
1448 Make a scope here for the client so that it gets removed
\r
1449 before the irrlicht device
\r
1453 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1458 SharedPtr<Server> server;
\r
1460 server = new Server("../map", hm_params, map_params);
\r
1461 server->start(port);
\r
1468 Client client(device, playername,
\r
1470 g_viewing_range_nodes,
\r
1471 g_viewing_range_all);
\r
1473 Address connect_address(0,0,0,0, port);
\r
1475 connect_address.Resolve(connect_name);
\r
1477 catch(ResolveError &e)
\r
1479 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1483 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1484 client.connect(connect_address);
\r
1487 while(client.connectedAndInitialized() == false)
\r
1490 if(server != NULL){
\r
1491 server->step(0.1);
\r
1496 catch(con::PeerNotFoundException &e)
\r
1498 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1505 /*scene::ISceneNode* skybox;
\r
1506 skybox = smgr->addSkyBoxSceneNode(
\r
1507 driver->getTexture("../data/skybox2.png"),
\r
1508 driver->getTexture("../data/skybox3.png"),
\r
1509 driver->getTexture("../data/skybox1.png"),
\r
1510 driver->getTexture("../data/skybox1.png"),
\r
1511 driver->getTexture("../data/skybox1.png"),
\r
1512 driver->getTexture("../data/skybox1.png"));*/
\r
1515 Create the camera node
\r
1518 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1519 0, // Camera parent
\r
1520 v3f(BS*100, BS*2, BS*100), // Look from
\r
1521 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1525 if(camera == NULL)
\r
1528 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1530 camera->setFOV(FOV_ANGLE);
\r
1532 // Just so big a value that everything rendered is visible
\r
1533 camera->setFarValue(100000*BS);
\r
1535 f32 camera_yaw = 0; // "right/left"
\r
1536 f32 camera_pitch = 0; // "up/down"
\r
1542 gui_loadingtext->remove();
\r
1545 Add some gui stuff
\r
1548 // Text input system
\r
1549 TextInput text_input;
\r
1551 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1552 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1555 We need some kind of a root node to be able to add
\r
1556 custom elements directly on the screen.
\r
1557 Otherwise they won't be automatically drawn.
\r
1559 guiroot = guienv->addStaticText(L"",
\r
1560 core::rect<s32>(0, 0, 10000, 10000));
\r
1563 //pauseMenu = new GUIPauseMenu(guienv, root, -1, device);
\r
1566 /*inventoryMenu = new GUIInventoryMenu(guienv, guiroot, -1, &local_inventory,
\r
1567 &inventory_action_queue);*/
\r
1569 //pauseMenu->launch();
\r
1570 //inventoryMenu->launch();
\r
1572 // First line of debug text
\r
1573 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1575 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1577 // Second line of debug text
\r
1578 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1580 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1583 // At the middle of the screen
\r
1584 // Object infos are shown in this
\r
1585 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1587 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1591 Some statistics are collected in these
\r
1594 u32 beginscenetime = 0;
\r
1595 u32 scenetime = 0;
\r
1596 u32 endscenetime = 0;
\r
1602 bool first_loop_after_window_activation = true;
\r
1604 // Time is in milliseconds
\r
1605 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1606 // NOTE: Have to call run() between calls of this to update the timer
\r
1607 u32 lasttime = device->getTimer()->getTime();
\r
1609 while(device->run())
\r
1612 Run global IrrlichtWrapper's main thread processing stuff
\r
1614 g_irrlicht->Run();
\r
1617 Random calculations
\r
1619 v2u32 screensize = driver->getScreenSize();
\r
1620 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1622 /*pauseMenu->resizeGui();
\r
1623 inventoryMenu->resizeGui();*/
\r
1625 // Hilight boxes collected during the loop and displayed
\r
1626 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1629 std::wstring infotext;
\r
1631 //TimeTaker //timer1("//timer1");
\r
1633 // Time of frame without fps limit
\r
1637 // not using getRealTime is necessary for wine
\r
1638 u32 time = device->getTimer()->getTime();
\r
1639 if(time > lasttime)
\r
1640 busytime_u32 = time - lasttime;
\r
1643 busytime = busytime_u32 / 1000.0;
\r
1646 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1648 // Absolutelu necessary for wine!
\r
1655 //updateViewingRange(dtime, &client);
\r
1656 updateViewingRange(busytime, &client);
\r
1663 float fps_max = g_settings.getFloat("fps_max");
\r
1664 u32 frametime_min = 1000./fps_max;
\r
1666 if(busytime_u32 < frametime_min)
\r
1668 u32 sleeptime = frametime_min - busytime_u32;
\r
1669 device->sleep(sleeptime);
\r
1673 // Absolutelu necessary for wine!
\r
1677 Time difference calculation
\r
1679 f32 dtime; // in seconds
\r
1681 u32 time = device->getTimer()->getTime();
\r
1682 if(time > lasttime)
\r
1683 dtime = (time - lasttime) / 1000.0;
\r
1689 Time average and jitter calculation
\r
1692 static f32 dtime_avg1 = 0.0;
\r
1693 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1694 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1696 static f32 dtime_jitter1_max_sample = 0.0;
\r
1697 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1699 static f32 jitter1_max = 0.0;
\r
1700 static f32 counter = 0.0;
\r
1701 if(dtime_jitter1 > jitter1_max)
\r
1702 jitter1_max = dtime_jitter1;
\r
1707 dtime_jitter1_max_sample = jitter1_max;
\r
1708 dtime_jitter1_max_fraction
\r
1709 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1710 jitter1_max = 0.0;
\r
1713 Control freetime ratio
\r
1715 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1717 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1718 g_freetime_ratio += 0.01;
\r
1722 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1723 g_freetime_ratio -= 0.01;
\r
1729 Busytime average and jitter calculation
\r
1732 static f32 busytime_avg1 = 0.0;
\r
1733 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1734 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1736 static f32 busytime_jitter1_max_sample = 0.0;
\r
1737 static f32 busytime_jitter1_min_sample = 0.0;
\r
1739 static f32 jitter1_max = 0.0;
\r
1740 static f32 jitter1_min = 0.0;
\r
1741 static f32 counter = 0.0;
\r
1742 if(busytime_jitter1 > jitter1_max)
\r
1743 jitter1_max = busytime_jitter1;
\r
1744 if(busytime_jitter1 < jitter1_min)
\r
1745 jitter1_min = busytime_jitter1;
\r
1747 if(counter > 0.0){
\r
1749 busytime_jitter1_max_sample = jitter1_max;
\r
1750 busytime_jitter1_min_sample = jitter1_min;
\r
1751 jitter1_max = 0.0;
\r
1752 jitter1_min = 0.0;
\r
1757 Debug info for client
\r
1760 static float counter = 0.0;
\r
1765 client.printDebugInfo(std::cout);
\r
1770 Input handler step()
\r
1772 g_input->step(dtime);
\r
1777 /*if(g_esc_pressed)
\r
1783 inventoryMenu->setVisible(true);
\r
1784 g_i_pressed = false;
\r
1788 Player speed control
\r
1791 if(g_game_focused)
\r
1798 bool a_superspeed,
\r
1801 PlayerControl control(
\r
1802 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1803 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1804 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1805 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1806 g_input->isKeyDown(irr::KEY_SPACE),
\r
1807 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1811 client.setPlayerControl(control);
\r
1815 // Set every key to inactive
\r
1816 PlayerControl control;
\r
1817 client.setPlayerControl(control);
\r
1822 Process environment
\r
1826 //TimeTaker timer("client.step(dtime)");
\r
1827 client.step(dtime);
\r
1828 //client.step(dtime_avg1);
\r
1831 if(server != NULL)
\r
1833 //TimeTaker timer("server->step(dtime)");
\r
1834 server->step(dtime);
\r
1837 v3f player_position = client.getPlayerPosition();
\r
1839 //TimeTaker //timer2("//timer2");
\r
1842 Mouse and camera control
\r
1845 if((device->isWindowActive()
\r
1852 device->getCursorControl()->setVisible(false);
\r
1854 if(first_loop_after_window_activation){
\r
1855 //std::cout<<"window active, first loop"<<std::endl;
\r
1856 first_loop_after_window_activation = false;
\r
1859 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1860 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1861 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1862 camera_yaw -= dx*0.2;
\r
1863 camera_pitch += dy*0.2;
\r
1864 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1865 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1867 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1870 device->getCursorControl()->setVisible(true);
\r
1872 //std::cout<<"window inactive"<<std::endl;
\r
1873 first_loop_after_window_activation = true;
\r
1876 camera_yaw = wrapDegrees(camera_yaw);
\r
1877 camera_pitch = wrapDegrees(camera_pitch);
\r
1879 v3f camera_direction = v3f(0,0,1);
\r
1880 camera_direction.rotateYZBy(camera_pitch);
\r
1881 camera_direction.rotateXZBy(camera_yaw);
\r
1883 v3f camera_position =
\r
1884 player_position + v3f(0, BS+BS/2, 0);
\r
1886 camera->setPosition(camera_position);
\r
1887 // *100.0 helps in large map coordinates
\r
1888 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1890 if(FIELD_OF_VIEW_TEST){
\r
1891 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1892 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1895 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1896 //TimeTaker timer("client.updateCamera");
\r
1897 client.updateCamera(camera_position, camera_direction);
\r
1901 //TimeTaker //timer3("//timer3");
\r
1904 Calculate what block is the crosshair pointing to
\r
1907 //u32 t1 = device->getTimer()->getRealTime();
\r
1909 //f32 d = 4; // max. distance
\r
1910 f32 d = 4; // max. distance
\r
1911 core::line3d<f32> shootline(camera_position,
\r
1912 camera_position + camera_direction * BS * (d+1));
\r
1914 MapBlockObject *selected_object = client.getSelectedObject
\r
1915 (d*BS, camera_position, shootline);
\r
1917 if(selected_object != NULL)
\r
1919 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1921 core::aabbox3d<f32> box_on_map
\r
1922 = selected_object->getSelectionBoxOnMap();
\r
1924 hilightboxes.push_back(box_on_map);
\r
1926 infotext = narrow_to_wide(selected_object->infoText());
\r
1928 if(g_input->getLeftClicked())
\r
1930 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1931 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1932 selected_object->getId(), g_selected_item);
\r
1934 else if(g_input->getRightClicked())
\r
1936 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1938 Check if we want to modify the object ourselves
\r
1940 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1942 dstream<<"Sign object right-clicked"<<std::endl;
\r
1944 text_input.start(new TextDestSign(
\r
1945 selected_object->getBlock()->getPos(),
\r
1946 selected_object->getId(),
\r
1951 g_text_buffer = L"ASD LOL 8)";
\r
1952 g_text_buffer_accepted = true;
\r
1956 Otherwise pass the event to the server as-is
\r
1960 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1961 selected_object->getId(), g_selected_item);
\r
1965 else // selected_object == NULL
\r
1969 Find out which node we are pointing at
\r
1972 bool nodefound = false;
\r
1974 v3s16 neighbourpos;
\r
1975 core::aabbox3d<f32> nodefacebox;
\r
1976 f32 mindistance = BS * 1001;
\r
1978 v3s16 pos_i = floatToInt(player_position);
\r
1980 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1984 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1985 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1986 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1987 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1988 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1989 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1991 for(s16 y = ystart; y <= yend; y++)
\r
1992 for(s16 z = zstart; z <= zend; z++)
\r
1993 for(s16 x = xstart; x <= xend; x++)
\r
1998 n = client.getNode(v3s16(x,y,z));
\r
1999 if(content_pointable(n.d) == false)
\r
2002 catch(InvalidPositionException &e)
\r
2008 v3f npf = intToFloat(np);
\r
2013 v3s16(0,0,1), // back
\r
2014 v3s16(0,1,0), // top
\r
2015 v3s16(1,0,0), // right
\r
2016 v3s16(0,0,-1), // front
\r
2017 v3s16(0,-1,0), // bottom
\r
2018 v3s16(-1,0,0), // left
\r
2024 if(n.d == CONTENT_TORCH)
\r
2026 v3s16 dir = unpackDir(n.dir);
\r
2027 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2028 dir_f *= BS/2 - BS/6 - BS/20;
\r
2029 v3f cpf = npf + dir_f;
\r
2030 f32 distance = (cpf - camera_position).getLength();
\r
2032 core::aabbox3d<f32> box;
\r
2035 if(dir == v3s16(0,-1,0))
\r
2037 box = core::aabbox3d<f32>(
\r
2038 npf - v3f(BS/6, BS/2, BS/6),
\r
2039 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2043 else if(dir == v3s16(0,1,0))
\r
2045 box = core::aabbox3d<f32>(
\r
2046 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2047 npf + v3f(BS/6, BS/2, BS/6)
\r
2053 box = core::aabbox3d<f32>(
\r
2054 cpf - v3f(BS/6, BS/3, BS/6),
\r
2055 cpf + v3f(BS/6, BS/3, BS/6)
\r
2059 if(distance < mindistance)
\r
2061 if(box.intersectsWithLine(shootline))
\r
2065 neighbourpos = np;
\r
2066 mindistance = distance;
\r
2067 nodefacebox = box;
\r
2076 for(u16 i=0; i<6; i++)
\r
2078 v3f dir_f = v3f(dirs[i].X,
\r
2079 dirs[i].Y, dirs[i].Z);
\r
2080 v3f centerpoint = npf + dir_f * BS/2;
\r
2082 (centerpoint - camera_position).getLength();
\r
2084 if(distance < mindistance)
\r
2086 core::CMatrix4<f32> m;
\r
2087 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2089 // This is the back face
\r
2090 v3f corners[2] = {
\r
2091 v3f(BS/2, BS/2, BS/2),
\r
2092 v3f(-BS/2, -BS/2, BS/2+d)
\r
2095 for(u16 j=0; j<2; j++)
\r
2097 m.rotateVect(corners[j]);
\r
2098 corners[j] += npf;
\r
2101 core::aabbox3d<f32> facebox(corners[0]);
\r
2102 facebox.addInternalPoint(corners[1]);
\r
2104 if(facebox.intersectsWithLine(shootline))
\r
2108 neighbourpos = np + dirs[i];
\r
2109 mindistance = distance;
\r
2110 nodefacebox = facebox;
\r
2112 } // if distance < mindistance
\r
2114 } // regular block
\r
2119 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2121 static float dig_time = 0.0;
\r
2122 static u16 dig_index = 0;
\r
2124 if(nodepos != nodepos_old)
\r
2126 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2127 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2129 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2131 client.clearTempMod(nodepos_old);
\r
2136 hilightboxes.push_back(nodefacebox);
\r
2138 if(g_input->getLeftReleased())
\r
2140 client.clearTempMod(nodepos);
\r
2143 if(g_input->getLeftClicked() ||
\r
2144 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2146 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2147 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2149 if(g_input->getLeftClicked())
\r
2151 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2153 if(g_input->getLeftState())
\r
2155 float dig_time_complete = 0.5;
\r
2156 MapNode n = client.getNode(nodepos);
\r
2157 if(n.d == CONTENT_STONE)
\r
2158 dig_time_complete = 1.5;
\r
2160 float dig_time_complete0 = dig_time_complete+client.getAvgRtt()*2;
\r
2161 if(dig_time_complete0 < 0.0)
\r
2162 dig_time_complete0 = 0.0;
\r
2164 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2165 * dig_time/dig_time_complete0);
\r
2167 if(dig_time > 0.125 && dig_index < CRACK_ANIMATION_LENGTH)
\r
2169 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2170 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2173 dig_time += dtime;
\r
2176 if(g_input->getRightClicked())
\r
2178 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2179 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2182 nodepos_old = nodepos;
\r
2187 } // selected_object == NULL
\r
2189 g_input->resetLeftClicked();
\r
2190 g_input->resetRightClicked();
\r
2192 if(g_input->getLeftReleased())
\r
2194 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2195 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2197 if(g_input->getRightReleased())
\r
2199 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2203 g_input->resetLeftReleased();
\r
2204 g_input->resetRightReleased();
\r
2207 Calculate stuff for drawing
\r
2210 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2212 u32 daynight_ratio = client.getDayNightRatio();
\r
2213 video::SColor bgcolor = video::SColor(
\r
2215 skycolor.getRed() * daynight_ratio / 1000,
\r
2216 skycolor.getGreen() * daynight_ratio / 1000,
\r
2217 skycolor.getBlue() * daynight_ratio / 1000);
\r
2223 if(g_settings.getBool("enable_fog") == true)
\r
2225 f32 range = g_viewing_range_nodes * BS;
\r
2226 if(g_viewing_range_all)
\r
2227 range = 100000*BS;
\r
2231 video::EFT_FOG_LINEAR,
\r
2235 false, // pixel fog
\r
2236 false // range fog
\r
2242 Update gui stuff (0ms)
\r
2245 //TimeTaker guiupdatetimer("Gui updating");
\r
2248 wchar_t temptext[150];
\r
2250 static float drawtime_avg = 0;
\r
2251 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2252 static float beginscenetime_avg = 0;
\r
2253 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2254 static float scenetime_avg = 0;
\r
2255 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2256 static float endscenetime_avg = 0;
\r
2257 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2259 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2261 L", R: range_all=%i"
\r
2263 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2265 g_viewing_range_all,
\r
2267 beginscenetime_avg,
\r
2272 guitext->setText(temptext);
\r
2276 wchar_t temptext[150];
\r
2277 swprintf(temptext, 150,
\r
2278 L"(% .1f, % .1f, % .1f)"
\r
2279 L" (% .3f < btime_jitter < % .3f"
\r
2280 L", dtime_jitter = % .1f %%)",
\r
2281 player_position.X/BS,
\r
2282 player_position.Y/BS,
\r
2283 player_position.Z/BS,
\r
2284 busytime_jitter1_min_sample,
\r
2285 busytime_jitter1_max_sample,
\r
2286 dtime_jitter1_max_fraction * 100.0
\r
2289 guitext2->setText(temptext);
\r
2293 /*wchar_t temptext[100];
\r
2294 swprintf(temptext, 100,
\r
2295 SWPRINTF_CHARSTRING,
\r
2296 infotext.substr(0,99).c_str()
\r
2299 guitext_info->setText(temptext);*/
\r
2301 guitext_info->setText(infotext.c_str());
\r
2308 static u16 old_selected_item = 65535;
\r
2309 if(client.getLocalInventoryUpdated()
\r
2310 || g_selected_item != old_selected_item)
\r
2312 old_selected_item = g_selected_item;
\r
2313 //std::cout<<"Updating local inventory"<<std::endl;
\r
2314 client.getLocalInventory(local_inventory);
\r
2315 quick_inventory->setSelection(g_selected_item);
\r
2316 quick_inventory->update();
\r
2320 Send actions returned by the inventory menu
\r
2322 while(inventory_action_queue.size() != 0)
\r
2324 InventoryAction *a = inventory_action_queue.pop_front();
\r
2326 client.sendInventoryAction(a);
\r
2331 if(text_input.guitext != NULL)
\r
2333 /*wchar_t temptext[100];
\r
2334 swprintf(temptext, 100,
\r
2335 SWPRINTF_CHARSTRING,
\r
2336 g_text_buffer.substr(0,99).c_str()
\r
2338 text_input.guitext->setText(g_text_buffer.c_str());
\r
2344 if(text_input.guitext != NULL && g_text_buffer_accepted)
\r
2346 text_input.guitext->remove();
\r
2347 text_input.guitext = NULL;
\r
2349 if(text_input.dest != NULL)
\r
2351 std::string text = wide_to_narrow(g_text_buffer);
\r
2352 dstream<<"Sending text: "<<text<<std::endl;
\r
2353 text_input.dest->sendText(text);
\r
2354 delete text_input.dest;
\r
2355 text_input.dest = NULL;
\r
2361 //guiupdatetimer.stop();
\r
2367 TimeTaker drawtimer("Drawing");
\r
2371 TimeTaker timer("beginScene");
\r
2372 driver->beginScene(true, true, bgcolor);
\r
2373 //driver->beginScene(false, true, bgcolor);
\r
2374 beginscenetime = timer.stop(true);
\r
2379 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2382 TimeTaker timer("smgr");
\r
2384 scenetime = timer.stop(true);
\r
2388 //TimeTaker timer9("auxiliary drawings");
\r
2391 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2392 displaycenter + core::vector2d<s32>(10,0),
\r
2393 video::SColor(255,255,255,255));
\r
2394 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2395 displaycenter + core::vector2d<s32>(0,10),
\r
2396 video::SColor(255,255,255,255));
\r
2399 //TimeTaker //timer10("//timer10");
\r
2401 video::SMaterial m;
\r
2403 m.Lighting = false;
\r
2404 driver->setMaterial(m);
\r
2406 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2408 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2409 i != hilightboxes.end(); i++)
\r
2411 /*std::cout<<"hilightbox min="
\r
2412 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2414 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2416 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2422 //TimeTaker //timer11("//timer11");
\r
2428 guienv->drawAll();
\r
2432 TimeTaker timer("endScene");
\r
2433 driver->endScene();
\r
2434 endscenetime = timer.stop(true);
\r
2437 drawtime = drawtimer.stop(true);
\r
2443 static s16 lastFPS = 0;
\r
2444 //u16 fps = driver->getFPS();
\r
2445 u16 fps = (1.0/dtime_avg1);
\r
2447 if (lastFPS != fps)
\r
2449 core::stringw str = L"Minetest [";
\r
2450 str += driver->getName();
\r
2454 device->setWindowCaption(str.c_str());
\r
2460 device->yield();*/
\r
2463 delete quick_inventory;
\r
2465 } // client is deleted at this point
\r
2470 In the end, delete the Irrlicht device.
\r
2475 Update configuration file
\r
2477 /*if(configpath != "")
\r
2479 g_settings.updateConfigFile(configpath.c_str());
\r
2483 catch(con::PeerNotFoundException &e)
\r
2485 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2487 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2489 This is what has to be done in every thread to get suitable debug info
\r
2491 catch(std::exception &e)
\r
2493 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2494 <<e.what()<<std::endl;
\r
2499 debugstreams_deinit();
\r