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: Precalculate lighting translation table at runtime (at startup)
\r
94 SUGG: A version number to blocks, which increments when the block is
\r
95 modified (node add/remove, water update, lighting update)
\r
96 - This can then be used to make sure the most recent version of
\r
97 a block has been sent to client
\r
99 SUGG: Make the amount of blocks sending to client and the total
\r
100 amount of blocks dynamically limited. Transferring blocks is the
\r
101 main network eater of this system, so it is the one that has
\r
102 to be throttled so that RTTs stay low.
\r
104 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
105 different directions and then only those drawn that need to be
\r
106 - Also an 1-dimensional tile map would be nice probably
\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
166 - This is also needed for objects that don't get sent to client
\r
167 but are used for triggers etc
\r
169 TODO: Draw big amounts of torches better (that is, throw them in the
\r
170 same meshbuffer (can the meshcollector class be used?))
\r
172 TODO: Check if the usage of Client::isFetchingBlocks() in
\r
173 updateViewingRange() actually does something
\r
175 TODO: Make an option to the server to disable building and digging near
\r
176 the starting position
\r
178 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
179 need an additional metadata field for the texts
\r
180 - This is also needed for item container chests
\r
181 TODO: There has to be some better way to handle static objects than to
\r
182 send them all the time. This affects signs and item objects.
\r
185 ======================================================================
\r
187 TODO: When server sees that client is removing an inexistent block or
\r
188 adding a block to an existent position, resend the MapBlock.
\r
190 TODO: Fix viewing range updater's oscillation when there is large non-
\r
191 linearity in range-speed relation
\r
193 ======================================================================
\r
198 Setting this to 1 enables a special camera mode that forces
\r
199 the renderers to think that the camera statically points from
\r
200 the starting place to a static direction.
\r
202 This allows one to move around with the player and see what
\r
203 is actually drawn behind solid things and behind the player.
\r
205 #define FIELD_OF_VIEW_TEST 0
\r
207 #ifdef UNITTEST_DISABLE
\r
209 #pragma message ("Disabling unit tests")
\r
211 #warning "Disabling unit tests"
\r
213 // Disable unit tests
\r
214 #define ENABLE_TESTS 0
\r
216 // Enable unit tests
\r
217 #define ENABLE_TESTS 1
\r
221 #pragma comment(lib, "Irrlicht.lib")
\r
222 #pragma comment(lib, "jthread.lib")
\r
223 #pragma comment(lib, "zlibwapi.lib")
\r
224 // This would get rid of the console window
\r
225 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
228 #include <iostream>
\r
230 #include <jmutexautolock.h>
\r
231 #include <locale.h>
\r
232 #include "common_irrlicht.h"
\r
235 #include "player.h"
\r
238 #include "environment.h"
\r
239 #include "server.h"
\r
240 #include "client.h"
\r
241 #include "serialization.h"
\r
242 #include "constants.h"
\r
243 #include "strfnd.h"
\r
244 #include "porting.h"
\r
245 #include "irrlichtwrapper.h"
\r
246 #include "gettime.h"
\r
247 #include "porting.h"
\r
248 #include "guiPauseMenu.h"
\r
249 #include "guiInventoryMenu.h"
\r
250 #include "guiTextInputMenu.h"
\r
251 #include "materials.h"
\r
252 #include "guiMessageMenu.h"
\r
254 IrrlichtWrapper *g_irrlicht;
\r
256 MapDrawControl draw_control;
\r
260 These are loaded from the config file.
\r
263 Settings g_settings;
\r
265 extern void set_default_settings();
\r
271 IrrlichtDevice *g_device = NULL;
\r
272 Client *g_client = NULL;
\r
277 gui::IGUIEnvironment* guienv = NULL;
\r
278 gui::IGUIStaticText *guiroot = NULL;
\r
279 int g_active_menu_count = 0;
\r
281 bool noMenuActive()
\r
283 return (g_active_menu_count == 0);
\r
286 // Inventory actions from the menu are buffered here before sending
\r
287 Queue<InventoryAction*> inventory_action_queue;
\r
288 // This is a copy of the inventory that the client's environment has
\r
289 Inventory local_inventory;
\r
291 u16 g_selected_item = 0;
\r
298 std::ostream *dout_con_ptr = &dummyout;
\r
299 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
300 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
301 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
302 //std::ostream *dout_con_ptr = &dstream;
\r
303 //std::ostream *derr_con_ptr = &dstream;
\r
306 std::ostream *dout_server_ptr = &dstream;
\r
307 std::ostream *derr_server_ptr = &dstream;
\r
310 std::ostream *dout_client_ptr = &dstream;
\r
311 std::ostream *derr_client_ptr = &dstream;
\r
314 gettime.h implementation
\r
320 Use irrlicht because it is more precise than porting.h's
\r
323 if(g_irrlicht == NULL)
\r
325 return g_irrlicht->getTime();
\r
332 struct TextDestSign : public TextDest
\r
334 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
336 m_blockpos = blockpos;
\r
340 void gotText(std::wstring text)
\r
342 std::string ntext = wide_to_narrow(text);
\r
343 dstream<<"Changing text of a sign object: "
\r
344 <<ntext<<std::endl;
\r
345 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
353 struct TextDestChat : public TextDest
\r
355 TextDestChat(Client *client)
\r
359 void gotText(std::wstring text)
\r
361 m_client->sendChatMessage(text);
\r
362 m_client->addChatMessage(text);
\r
368 class MyEventReceiver : public IEventReceiver
\r
371 // This is the one method that we have to implement
\r
372 virtual bool OnEvent(const SEvent& event)
\r
375 React to nothing here if a menu is active
\r
377 if(noMenuActive() == false)
\r
383 // Remember whether each key is down or up
\r
384 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
386 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
388 if(event.KeyInput.PressedDown)
\r
390 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
396 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
398 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
400 dstream<<DTIME<<"MyEventReceiver: "
\r
401 <<"Launching pause menu"<<std::endl;
\r
402 // It will delete itself by itself
\r
403 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
404 &g_active_menu_count))->drop();
\r
407 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
409 dstream<<DTIME<<"MyEventReceiver: "
\r
410 <<"Launching inventory"<<std::endl;
\r
411 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
412 &local_inventory, &inventory_action_queue,
\r
413 &g_active_menu_count))->drop();
\r
416 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
418 TextDest *dest = new TextDestChat(g_client);
\r
420 (new GUITextInputMenu(guienv, guiroot, -1,
\r
421 &g_active_menu_count, dest,
\r
426 // Material selection
\r
427 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
429 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
432 g_selected_item = 0;
\r
433 dstream<<DTIME<<"Selected item: "
\r
434 <<g_selected_item<<std::endl;
\r
437 // Viewing range selection
\r
438 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
440 if(draw_control.range_all)
\r
442 draw_control.range_all = false;
\r
443 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
447 draw_control.range_all = true;
\r
448 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
452 // Print debug stacks
\r
453 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
455 dstream<<"-----------------------------------------"
\r
457 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
458 dstream<<"-----------------------------------------"
\r
460 debug_stacks_print();
\r
465 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
467 if(noMenuActive() == false)
\r
469 left_active = false;
\r
470 middle_active = false;
\r
471 right_active = false;
\r
475 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
476 left_active = event.MouseInput.isLeftPressed();
\r
477 middle_active = event.MouseInput.isMiddlePressed();
\r
478 right_active = event.MouseInput.isRightPressed();
\r
480 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
482 leftclicked = true;
\r
484 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
486 rightclicked = true;
\r
488 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
490 leftreleased = true;
\r
492 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
494 rightreleased = true;
\r
496 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
498 /*dstream<<"event.MouseInput.Wheel="
\r
499 <<event.MouseInput.Wheel<<std::endl;*/
\r
500 if(event.MouseInput.Wheel < 0)
\r
502 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
505 g_selected_item = 0;
\r
507 else if(event.MouseInput.Wheel > 0)
\r
509 if(g_selected_item > 0)
\r
512 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
521 // This is used to check whether a key is being held down
\r
522 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
524 return keyIsDown[keyCode];
\r
529 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
530 keyIsDown[i] = false;
\r
532 leftclicked = false;
\r
533 rightclicked = false;
\r
534 leftreleased = false;
\r
535 rightreleased = false;
\r
537 left_active = false;
\r
538 middle_active = false;
\r
539 right_active = false;
\r
550 bool rightreleased;
\r
553 bool middle_active;
\r
557 // We use this array to store the current state of each key
\r
558 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
561 IrrlichtDevice *m_device;
\r
570 virtual ~InputHandler()
\r
574 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
576 virtual v2s32 getMousePos() = 0;
\r
577 virtual void setMousePos(s32 x, s32 y) = 0;
\r
579 virtual bool getLeftState() = 0;
\r
580 virtual bool getRightState() = 0;
\r
582 virtual bool getLeftClicked() = 0;
\r
583 virtual bool getRightClicked() = 0;
\r
584 virtual void resetLeftClicked() = 0;
\r
585 virtual void resetRightClicked() = 0;
\r
587 virtual bool getLeftReleased() = 0;
\r
588 virtual bool getRightReleased() = 0;
\r
589 virtual void resetLeftReleased() = 0;
\r
590 virtual void resetRightReleased() = 0;
\r
592 virtual void step(float dtime) {};
\r
594 virtual void clear() {};
\r
597 InputHandler *g_input = NULL;
\r
599 class RealInputHandler : public InputHandler
\r
602 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
604 m_receiver(receiver)
\r
607 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
609 return m_receiver->IsKeyDown(keyCode);
\r
611 virtual v2s32 getMousePos()
\r
613 return m_device->getCursorControl()->getPosition();
\r
615 virtual void setMousePos(s32 x, s32 y)
\r
617 m_device->getCursorControl()->setPosition(x, y);
\r
620 virtual bool getLeftState()
\r
622 return m_receiver->left_active;
\r
624 virtual bool getRightState()
\r
626 return m_receiver->right_active;
\r
629 virtual bool getLeftClicked()
\r
631 return m_receiver->leftclicked;
\r
633 virtual bool getRightClicked()
\r
635 return m_receiver->rightclicked;
\r
637 virtual void resetLeftClicked()
\r
639 m_receiver->leftclicked = false;
\r
641 virtual void resetRightClicked()
\r
643 m_receiver->rightclicked = false;
\r
646 virtual bool getLeftReleased()
\r
648 return m_receiver->leftreleased;
\r
650 virtual bool getRightReleased()
\r
652 return m_receiver->rightreleased;
\r
654 virtual void resetLeftReleased()
\r
656 m_receiver->leftreleased = false;
\r
658 virtual void resetRightReleased()
\r
660 m_receiver->rightreleased = false;
\r
665 resetRightClicked();
\r
666 resetLeftClicked();
\r
669 IrrlichtDevice *m_device;
\r
670 MyEventReceiver *m_receiver;
\r
673 class RandomInputHandler : public InputHandler
\r
676 RandomInputHandler()
\r
678 leftclicked = false;
\r
679 rightclicked = false;
\r
680 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
681 keydown[i] = false;
\r
683 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
685 return keydown[keyCode];
\r
687 virtual v2s32 getMousePos()
\r
691 virtual void setMousePos(s32 x, s32 y)
\r
693 mousepos = v2s32(x,y);
\r
696 virtual bool getLeftState()
\r
700 virtual bool getRightState()
\r
705 virtual bool getLeftClicked()
\r
707 return leftclicked;
\r
709 virtual bool getRightClicked()
\r
711 return rightclicked;
\r
713 virtual void resetLeftClicked()
\r
715 leftclicked = false;
\r
717 virtual void resetRightClicked()
\r
719 rightclicked = false;
\r
722 virtual bool getLeftReleased()
\r
726 virtual bool getRightReleased()
\r
730 virtual void resetLeftReleased()
\r
733 virtual void resetRightReleased()
\r
737 virtual void step(float dtime)
\r
740 static float counter1 = 0;
\r
744 counter1 = 0.1*Rand(1,10);
\r
745 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
746 g_selected_material++;
\r
748 g_selected_material = 0;*/
\r
749 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
752 g_selected_item = 0;
\r
756 static float counter1 = 0;
\r
760 counter1 = 0.1*Rand(1, 40);
\r
761 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
765 static float counter1 = 0;
\r
769 counter1 = 0.1*Rand(1, 40);
\r
770 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
774 static float counter1 = 0;
\r
778 counter1 = 0.1*Rand(1, 40);
\r
779 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
783 static float counter1 = 0;
\r
787 counter1 = 0.1*Rand(1, 40);
\r
788 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
792 static float counter1 = 0;
\r
796 counter1 = 0.1*Rand(1, 20);
\r
797 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
801 static float counter1 = 0;
\r
805 counter1 = 0.1*Rand(1, 30);
\r
806 leftclicked = true;
\r
810 static float counter1 = 0;
\r
814 counter1 = 0.1*Rand(1, 20);
\r
815 rightclicked = true;
\r
818 mousepos += mousespeed;
\r
821 s32 Rand(s32 min, s32 max)
\r
823 return (rand()%(max-min+1))+min;
\r
826 bool keydown[KEY_KEY_CODES_COUNT];
\r
834 void updateViewingRange(f32 frametime, Client *client)
\r
836 // Range_all messes up frametime_avg
\r
837 if(draw_control.range_all == true)
\r
840 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
842 // Initialize to the target value
\r
843 static float frametime_avg = 1.0/wanted_fps;
\r
844 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
845 //frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
846 //frametime_avg = frametime_avg * 0.5 + frametime * 0.5;
\r
847 //frametime_avg = frametime_avg * 0.0 + frametime * 1.0;
\r
848 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
850 static f32 counter = 0;
\r
852 counter -= frametime;
\r
855 //counter = 1.0; //seconds
\r
856 counter = 0.5; //seconds
\r
857 //counter += 0.1; //seconds
\r
858 //counter = 0.3; //seconds
\r
860 //float freetime_ratio = 0.2;
\r
861 //float freetime_ratio = 0.4;
\r
862 float freetime_ratio = FREETIME_RATIO;
\r
864 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
866 //float fraction = sqrt(frametime_avg / frametime_wanted);
\r
867 //float fraction = pow(frametime_avg / frametime_wanted, 1./3);
\r
869 float fraction_unbiased = frametime_avg / frametime_wanted;
\r
871 float fraction = pow(fraction_unbiased, 20./(float)draw_control.wanted_range);
\r
873 /*float fraction = 1.0;
\r
874 // If frametime is too high
\r
875 if(fraction_unbiased > 1.0)
\r
876 fraction = pow(fraction_unbiased, 1./2);
\r
877 // If frametime is too low
\r
879 fraction = pow(fraction_unbiased, 1./5);*/
\r
881 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
882 + frametime_avg / frametime_wanted / 2.0;*/
\r
884 //float fraction = frametime_avg / frametime_wanted;
\r
886 static bool fraction_is_good = false;
\r
888 //float fraction_good_threshold = 0.1;
\r
889 //float fraction_bad_threshold = 0.25;
\r
891 /*float fraction_good_threshold = 0.075;
\r
892 float fraction_bad_threshold = 0.125;*/
\r
893 // If frametime is too low
\r
894 /*if(fraction < 1.0)
\r
896 fraction_good_threshold = pow(fraction_good_threshold, 4);
\r
897 fraction_bad_threshold = pow(fraction_bad_threshold, 4);
\r
900 float fraction_good_threshold = 0.23;
\r
901 float fraction_bad_threshold = 0.33;
\r
903 float fraction_limit;
\r
904 // Use high limit if fraction is good AND the fraction would
\r
905 // lower the range. We want to keep the range fairly high.
\r
906 if(fraction_is_good && fraction > 1.0)
\r
907 fraction_limit = fraction_bad_threshold;
\r
909 fraction_limit = fraction_good_threshold;
\r
911 //if(fabs(fraction - 1.0) < fraction_limit)
\r
912 if(fabs(fraction_unbiased - 1.0) < fraction_limit)
\r
914 fraction_is_good = true;
\r
919 fraction_is_good = false;
\r
922 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
923 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
924 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
925 <<" faction = "<<fraction<<std::endl;*/
\r
927 JMutexAutoLock lock(g_range_mutex);
\r
929 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
930 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
932 s16 n = (float)draw_control.wanted_range / fraction;
\r
933 if(n < viewing_range_nodes_min)
\r
934 n = viewing_range_nodes_min;
\r
935 if(n > viewing_range_nodes_max)
\r
936 n = viewing_range_nodes_max;
\r
938 bool can_change = true;
\r
940 if(client->isFetchingBlocks() == true && n > draw_control.wanted_range)
\r
941 can_change = false;
\r
944 draw_control.wanted_range = n;
\r
946 /*dstream<<"draw_control.wanted_range = "
\r
947 <<draw_control.wanted_range<<std::endl;*/
\r
951 void updateViewingRange(f32 frametime_in, Client *client)
\r
953 if(draw_control.range_all == true)
\r
956 static f32 added_frametime = 0;
\r
957 static s16 added_frames = 0;
\r
959 added_frametime += frametime_in;
\r
962 // Actually this counter kind of sucks because frametime is busytime
\r
963 static f32 counter = 0;
\r
964 counter -= frametime_in;
\r
970 dstream<<__FUNCTION_NAME
\r
971 <<": Collected "<<added_frames<<" frames, total of "
\r
972 <<added_frametime<<"s."<<std::endl;
\r
974 dstream<<"draw_control.blocks_drawn="
\r
975 <<draw_control.blocks_drawn
\r
976 <<", draw_control.blocks_would_have_drawn="
\r
977 <<draw_control.blocks_would_have_drawn
\r
980 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
981 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
983 draw_control.wanted_min_range = range_min;
\r
984 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
986 float block_draw_ratio = 1.0;
\r
987 if(draw_control.blocks_would_have_drawn != 0)
\r
989 block_draw_ratio = (float)draw_control.blocks_drawn
\r
990 / (float)draw_control.blocks_would_have_drawn;
\r
993 // Calculate the average frametime in the case that all wanted
\r
994 // blocks had been drawn
\r
995 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
997 added_frametime = 0.0;
\r
1000 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1001 float wanted_frametime = 1.0 / wanted_fps;
\r
1003 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1004 dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1006 // If needed frametime change is very small, just return
\r
1007 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1009 dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1013 float range = draw_control.wanted_range;
\r
1014 float new_range = range;
\r
1016 static s16 range_old = 0;
\r
1017 static f32 frametime_old = 0;
\r
1019 float d_range = range - range_old;
\r
1020 f32 d_frametime = frametime - frametime_old;
\r
1021 // A sane default of 30ms per 50 nodes of range
\r
1022 static f32 time_per_range = 30. / 50;
\r
1025 time_per_range = d_frametime / d_range;
\r
1028 // The minimum allowed calculated frametime-range derivative:
\r
1029 // Practically this sets the maximum speed of changing the range.
\r
1030 // The lower this value, the higher the maximum changing speed.
\r
1031 // A low value here results in wobbly range (0.001)
\r
1032 // A high value here results in slow changing range (0.0025)
\r
1033 // SUGG: This could be dynamically adjusted so that when
\r
1034 // the camera is turning, this is lower
\r
1035 //float min_time_per_range = 0.0015;
\r
1036 float min_time_per_range = 0.0010;
\r
1037 //float min_time_per_range = 0.05 / range;
\r
1038 if(time_per_range < min_time_per_range)
\r
1040 time_per_range = min_time_per_range;
\r
1041 dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1045 dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1048 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1049 // Dampen the change a bit to kill oscillations
\r
1050 //wanted_range_change *= 0.9;
\r
1051 //wanted_range_change *= 0.75;
\r
1052 wanted_range_change *= 0.5;
\r
1053 dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1055 // If needed range change is very small, just return
\r
1056 if(fabs(wanted_range_change) < 0.001)
\r
1058 dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1062 new_range += wanted_range_change;
\r
1063 dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1065 float new_range_unclamped = new_range;
\r
1066 if(new_range < range_min)
\r
1067 new_range = range_min;
\r
1068 if(new_range > range_max)
\r
1069 new_range = range_max;
\r
1071 if(new_range != new_range_unclamped)
\r
1072 dstream<<", clamped to "<<new_range<<std::endl;
\r
1074 dstream<<std::endl;
\r
1076 draw_control.wanted_range = new_range;
\r
1078 range_old = new_range;
\r
1079 frametime_old = frametime;
\r
1082 class GUIQuickInventory : public IEventReceiver
\r
1085 GUIQuickInventory(
\r
1086 gui::IGUIEnvironment* env,
\r
1087 gui::IGUIElement* parent,
\r
1090 Inventory *inventory):
\r
1091 m_itemcount(itemcount),
\r
1092 m_inventory(inventory)
\r
1094 core::rect<s32> imgsize(0,0,48,48);
\r
1095 core::rect<s32> textsize(0,0,48,16);
\r
1096 v2s32 spacing(0, 64);
\r
1097 for(s32 i=0; i<m_itemcount; i++)
\r
1099 m_images.push_back(env->addImage(
\r
1100 imgsize + pos + spacing*i
\r
1102 m_images[i]->setScaleImage(true);
\r
1103 m_texts.push_back(env->addStaticText(
\r
1105 textsize + pos + spacing*i,
\r
1108 m_texts[i]->setBackgroundColor(
\r
1109 video::SColor(128,0,0,0));
\r
1110 m_texts[i]->setTextAlignment(
\r
1111 gui::EGUIA_CENTER,
\r
1112 gui::EGUIA_UPPERLEFT);
\r
1116 virtual bool OnEvent(const SEvent& event)
\r
1121 void setSelection(s32 i)
\r
1130 start = m_selection - m_itemcount / 2;
\r
1132 InventoryList *mainlist = m_inventory->getList("main");
\r
1134 for(s32 i=0; i<m_itemcount; i++)
\r
1136 s32 j = i + start;
\r
1138 if(j > (s32)mainlist->getSize() - 1)
\r
1139 j -= mainlist->getSize();
\r
1141 j += mainlist->getSize();
\r
1143 InventoryItem *item = mainlist->getItem(j);
\r
1147 m_images[i]->setImage(NULL);
\r
1150 if(m_selection == j)
\r
1151 swprintf(t, 10, L"<-");
\r
1153 swprintf(t, 10, L"");
\r
1154 m_texts[i]->setText(t);
\r
1156 // The next ifs will segfault with a NULL pointer
\r
1161 m_images[i]->setImage(item->getImage());
\r
1164 if(m_selection == j)
\r
1165 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1167 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1168 m_texts[i]->setText(t);
\r
1174 core::array<gui::IGUIStaticText*> m_texts;
\r
1175 core::array<gui::IGUIImage*> m_images;
\r
1176 Inventory *m_inventory;
\r
1187 ChatLine(const std::wstring &a_text):
\r
1193 std::wstring text;
\r
1196 int main(int argc, char *argv[])
\r
1199 Low-level initialization
\r
1202 bool disable_stderr = false;
\r
1204 disable_stderr = true;
\r
1207 // Initialize debug streams
\r
1208 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1209 // Initialize debug stacks
\r
1210 debug_stacks_init();
\r
1212 DSTACK(__FUNCTION_NAME);
\r
1214 initializeMaterialProperties();
\r
1220 Parse command line
\r
1223 // List all allowed options
\r
1224 core::map<std::string, ValueSpec> allowed_options;
\r
1225 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1226 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1227 "Run server directly"));
\r
1228 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1229 "Load configuration from specified file"));
\r
1230 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1231 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1232 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1233 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1234 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1236 Settings cmd_args;
\r
1238 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1240 if(ret == false || cmd_args.getFlag("help"))
\r
1242 dstream<<"Allowed options:"<<std::endl;
\r
1243 for(core::map<std::string, ValueSpec>::Iterator
\r
1244 i = allowed_options.getIterator();
\r
1245 i.atEnd() == false; i++)
\r
1247 dstream<<" --"<<i.getNode()->getKey();
\r
1248 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1253 dstream<<" <value>";
\r
1255 dstream<<std::endl;
\r
1257 if(i.getNode()->getValue().help != NULL)
\r
1259 dstream<<" "<<i.getNode()->getValue().help
\r
1264 return cmd_args.getFlag("help") ? 0 : 1;
\r
1269 Basic initialization
\r
1272 // Initialize default settings
\r
1273 set_default_settings();
\r
1275 // Print startup message
\r
1276 dstream<<DTIME<<"minetest-c55"
\r
1277 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1278 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1281 // Set locale. This is for forcing '.' as the decimal point.
\r
1282 std::locale::global(std::locale("C"));
\r
1283 // This enables printing all characters in bitmap font
\r
1284 setlocale(LC_CTYPE, "en_US");
\r
1286 // Initialize sockets
\r
1288 atexit(sockets_cleanup);
\r
1298 // Path of configuration file in use
\r
1299 std::string configpath = "";
\r
1301 if(cmd_args.exists("config"))
\r
1303 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1306 dstream<<"Could not read configuration from \""
\r
1307 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1310 configpath = cmd_args.get("config");
\r
1314 const char *filenames[2] =
\r
1316 "../minetest.conf",
\r
1317 "../../minetest.conf"
\r
1320 for(u32 i=0; i<2; i++)
\r
1322 bool r = g_settings.readConfigFile(filenames[i]);
\r
1325 configpath = filenames[i];
\r
1331 // Initialize random seed
\r
1338 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1339 || cmd_args.getFlag("enable-unittests") == true)
\r
1344 // Read map parameters from settings
\r
1346 HMParams hm_params;
\r
1347 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1348 hm_params.randmax = g_settings.get("height_randmax");
\r
1349 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1350 hm_params.base = g_settings.get("height_base");
\r
1352 MapParams map_params;
\r
1353 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1354 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1360 std::cout<<std::endl<<std::endl;
\r
1363 <<" .__ __ __ "<<std::endl
\r
1364 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1365 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1366 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1367 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1368 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1371 std::cout<<std::endl;
\r
1372 //char templine[100];
\r
1376 if(cmd_args.exists("port"))
\r
1378 port = cmd_args.getU16("port");
\r
1382 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1383 std::cout<<"-> "<<port<<std::endl;
\r
1386 if(cmd_args.getFlag("server"))
\r
1388 DSTACK("Dedicated server branch");
\r
1390 std::cout<<std::endl;
\r
1391 std::cout<<"========================"<<std::endl;
\r
1392 std::cout<<"Running dedicated server"<<std::endl;
\r
1393 std::cout<<"========================"<<std::endl;
\r
1394 std::cout<<std::endl;
\r
1396 Server server("../map", hm_params, map_params);
\r
1397 server.start(port);
\r
1401 // This is kind of a hack but can be done like this
\r
1402 // because server.step() is very light
\r
1404 server.step(0.030);
\r
1406 static int counter = 0;
\r
1412 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1413 core::list<PlayerInfo>::Iterator i;
\r
1414 static u32 sum_old = 0;
\r
1415 u32 sum = PIChecksum(list);
\r
1416 if(sum != sum_old)
\r
1418 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1419 for(i=list.begin(); i!=list.end(); i++)
\r
1421 i->PrintLine(&std::cout);
\r
1431 bool hosting = false;
\r
1432 char connect_name[100] = "";
\r
1434 if(cmd_args.exists("address"))
\r
1436 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1438 else if(is_yes(g_settings.get("host_game")) == false)
\r
1440 if(g_settings.get("address") != "")
\r
1442 std::cout<<g_settings.get("address")<<std::endl;
\r
1443 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1447 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1448 std::cin.getline(connect_name, 100);
\r
1452 if(connect_name[0] == 0){
\r
1453 snprintf(connect_name, 100, "127.0.0.1");
\r
1458 std::cout<<"> Hosting game"<<std::endl;
\r
1460 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1462 char playername[PLAYERNAME_SIZE] = "";
\r
1463 if(g_settings.get("name") != "")
\r
1465 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1469 std::cout<<"Name of player: ";
\r
1470 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1472 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1475 Resolution selection
\r
1478 bool fullscreen = false;
\r
1479 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1480 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1484 MyEventReceiver receiver;
\r
1486 video::E_DRIVER_TYPE driverType;
\r
1489 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1490 driverType = video::EDT_OPENGL;
\r
1492 driverType = video::EDT_OPENGL;
\r
1495 // create device and exit if creation failed
\r
1497 IrrlichtDevice *device;
\r
1498 device = createDevice(driverType,
\r
1499 core::dimension2d<u32>(screenW, screenH),
\r
1500 16, fullscreen, false, false, &receiver);
\r
1503 return 1; // could not create selected driver.
\r
1505 g_device = device;
\r
1506 g_irrlicht = new IrrlichtWrapper(device);
\r
1508 //g_device = device;
\r
1510 device->setResizable(true);
\r
1512 bool random_input = g_settings.getBool("random_input")
\r
1513 || cmd_args.getFlag("random-input");
\r
1515 g_input = new RandomInputHandler();
\r
1517 g_input = new RealInputHandler(device, &receiver);
\r
1520 Continue initialization
\r
1523 video::IVideoDriver* driver = device->getVideoDriver();
\r
1526 This changes the minimum allowed number of vertices in a VBO
\r
1528 //driver->setMinHardwareBufferVertexCount(1);
\r
1530 scene::ISceneManager* smgr = device->getSceneManager();
\r
1532 guienv = device->getGUIEnvironment();
\r
1533 gui::IGUISkin* skin = guienv->getSkin();
\r
1534 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1536 skin->setFont(font);
\r
1538 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1539 dstream<<"text_height="<<text_height<<std::endl;
\r
1541 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1542 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1543 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1544 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1545 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1546 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1548 const wchar_t *text = L"Loading and connecting...";
\r
1549 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1550 core::vector2d<s32> textsize(300, text_height);
\r
1551 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1553 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1554 text, textrect, false, false);
\r
1555 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1557 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1558 guienv->drawAll();
\r
1559 driver->endScene();
\r
1562 Preload some textures
\r
1565 tile_materials_preload(g_irrlicht);
\r
1568 Make a scope here for the client so that it gets removed
\r
1569 before the irrlicht device
\r
1573 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1578 SharedPtr<Server> server;
\r
1580 server = new Server("../map", hm_params, map_params);
\r
1581 server->start(port);
\r
1588 Client client(device, playername, draw_control);
\r
1590 g_client = &client;
\r
1592 Address connect_address(0,0,0,0, port);
\r
1594 connect_address.Resolve(connect_name);
\r
1596 catch(ResolveError &e)
\r
1598 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1602 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1603 client.connect(connect_address);
\r
1606 while(client.connectedAndInitialized() == false)
\r
1609 if(server != NULL){
\r
1610 server->step(0.1);
\r
1615 catch(con::PeerNotFoundException &e)
\r
1617 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1624 /*scene::ISceneNode* skybox;
\r
1625 skybox = smgr->addSkyBoxSceneNode(
\r
1626 driver->getTexture("../data/skybox2.png"),
\r
1627 driver->getTexture("../data/skybox3.png"),
\r
1628 driver->getTexture("../data/skybox1.png"),
\r
1629 driver->getTexture("../data/skybox1.png"),
\r
1630 driver->getTexture("../data/skybox1.png"),
\r
1631 driver->getTexture("../data/skybox1.png"));*/
\r
1634 Create the camera node
\r
1637 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1638 0, // Camera parent
\r
1639 v3f(BS*100, BS*2, BS*100), // Look from
\r
1640 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1644 if(camera == NULL)
\r
1647 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1649 camera->setFOV(FOV_ANGLE);
\r
1651 // Just so big a value that everything rendered is visible
\r
1652 camera->setFarValue(100000*BS);
\r
1654 f32 camera_yaw = 0; // "right/left"
\r
1655 f32 camera_pitch = 0; // "up/down"
\r
1661 gui_loadingtext->remove();
\r
1664 Add some gui stuff
\r
1667 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1668 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1671 We need some kind of a root node to be able to add
\r
1672 custom elements directly on the screen.
\r
1673 Otherwise they won't be automatically drawn.
\r
1675 guiroot = guienv->addStaticText(L"",
\r
1676 core::rect<s32>(0, 0, 10000, 10000));
\r
1678 // Test the text input system
\r
1679 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1681 /*GUIMessageMenu *menu =
\r
1682 new GUIMessageMenu(guienv, guiroot, -1,
\r
1683 &g_active_menu_count,
\r
1687 // Launch pause menu
\r
1688 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1689 &g_active_menu_count))->drop();
\r
1691 // First line of debug text
\r
1692 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1694 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1696 // Second line of debug text
\r
1697 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1699 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1702 // At the middle of the screen
\r
1703 // Object infos are shown in this
\r
1704 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1706 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1710 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1711 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1712 core::rect<s32>(70, 60, 795, 150),
\r
1714 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1715 core::list<ChatLine> chat_lines;
\r
1718 Some statistics are collected in these
\r
1721 u32 beginscenetime = 0;
\r
1722 u32 scenetime = 0;
\r
1723 u32 endscenetime = 0;
\r
1726 //throw con::PeerNotFoundException("lol");
\r
1732 bool first_loop_after_window_activation = true;
\r
1734 // Time is in milliseconds
\r
1735 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1736 // NOTE: Have to call run() between calls of this to update the timer
\r
1737 u32 lasttime = device->getTimer()->getTime();
\r
1739 while(device->run())
\r
1742 Run global IrrlichtWrapper's main thread processing stuff
\r
1744 g_irrlicht->Run();
\r
1747 Random calculations
\r
1749 v2u32 screensize = driver->getScreenSize();
\r
1750 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1752 // Hilight boxes collected during the loop and displayed
\r
1753 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1756 std::wstring infotext;
\r
1758 //TimeTaker //timer1("//timer1");
\r
1760 // Time of frame without fps limit
\r
1764 // not using getRealTime is necessary for wine
\r
1765 u32 time = device->getTimer()->getTime();
\r
1766 if(time > lasttime)
\r
1767 busytime_u32 = time - lasttime;
\r
1770 busytime = busytime_u32 / 1000.0;
\r
1773 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1775 // Absolutelu necessary for wine!
\r
1782 updateViewingRange(busytime, &client);
\r
1789 float fps_max = g_settings.getFloat("fps_max");
\r
1790 u32 frametime_min = 1000./fps_max;
\r
1792 if(busytime_u32 < frametime_min)
\r
1794 u32 sleeptime = frametime_min - busytime_u32;
\r
1795 device->sleep(sleeptime);
\r
1799 // Absolutelu necessary for wine!
\r
1803 Time difference calculation
\r
1805 f32 dtime; // in seconds
\r
1807 u32 time = device->getTimer()->getTime();
\r
1808 if(time > lasttime)
\r
1809 dtime = (time - lasttime) / 1000.0;
\r
1815 Time average and jitter calculation
\r
1818 static f32 dtime_avg1 = 0.0;
\r
1819 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1820 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1822 static f32 dtime_jitter1_max_sample = 0.0;
\r
1823 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1825 static f32 jitter1_max = 0.0;
\r
1826 static f32 counter = 0.0;
\r
1827 if(dtime_jitter1 > jitter1_max)
\r
1828 jitter1_max = dtime_jitter1;
\r
1833 dtime_jitter1_max_sample = jitter1_max;
\r
1834 dtime_jitter1_max_fraction
\r
1835 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1836 jitter1_max = 0.0;
\r
1839 Control freetime ratio
\r
1841 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1843 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1844 g_freetime_ratio += 0.01;
\r
1848 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1849 g_freetime_ratio -= 0.01;
\r
1855 Busytime average and jitter calculation
\r
1858 static f32 busytime_avg1 = 0.0;
\r
1859 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1860 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1862 static f32 busytime_jitter1_max_sample = 0.0;
\r
1863 static f32 busytime_jitter1_min_sample = 0.0;
\r
1865 static f32 jitter1_max = 0.0;
\r
1866 static f32 jitter1_min = 0.0;
\r
1867 static f32 counter = 0.0;
\r
1868 if(busytime_jitter1 > jitter1_max)
\r
1869 jitter1_max = busytime_jitter1;
\r
1870 if(busytime_jitter1 < jitter1_min)
\r
1871 jitter1_min = busytime_jitter1;
\r
1873 if(counter > 0.0){
\r
1875 busytime_jitter1_max_sample = jitter1_max;
\r
1876 busytime_jitter1_min_sample = jitter1_min;
\r
1877 jitter1_max = 0.0;
\r
1878 jitter1_min = 0.0;
\r
1883 Debug info for client
\r
1886 static float counter = 0.0;
\r
1891 client.printDebugInfo(std::cout);
\r
1896 Input handler step()
\r
1898 g_input->step(dtime);
\r
1901 Player speed control
\r
1910 bool a_superspeed,
\r
1913 PlayerControl control(
\r
1914 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1915 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1916 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1917 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1918 g_input->isKeyDown(irr::KEY_SPACE),
\r
1919 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1923 client.setPlayerControl(control);
\r
1927 Process environment
\r
1931 //TimeTaker timer("client.step(dtime)");
\r
1932 client.step(dtime);
\r
1933 //client.step(dtime_avg1);
\r
1936 if(server != NULL)
\r
1938 //TimeTaker timer("server->step(dtime)");
\r
1939 server->step(dtime);
\r
1942 v3f player_position = client.getPlayerPosition();
\r
1944 //TimeTaker //timer2("//timer2");
\r
1947 Mouse and camera control
\r
1950 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1953 device->getCursorControl()->setVisible(false);
\r
1955 if(first_loop_after_window_activation){
\r
1956 //std::cout<<"window active, first loop"<<std::endl;
\r
1957 first_loop_after_window_activation = false;
\r
1960 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1961 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1962 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1963 camera_yaw -= dx*0.2;
\r
1964 camera_pitch += dy*0.2;
\r
1965 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1966 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1968 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1971 device->getCursorControl()->setVisible(true);
\r
1973 //std::cout<<"window inactive"<<std::endl;
\r
1974 first_loop_after_window_activation = true;
\r
1977 camera_yaw = wrapDegrees(camera_yaw);
\r
1978 camera_pitch = wrapDegrees(camera_pitch);
\r
1980 v3f camera_direction = v3f(0,0,1);
\r
1981 camera_direction.rotateYZBy(camera_pitch);
\r
1982 camera_direction.rotateXZBy(camera_yaw);
\r
1984 v3f camera_position =
\r
1985 player_position + v3f(0, BS+BS/2, 0);
\r
1987 camera->setPosition(camera_position);
\r
1988 // *100.0 helps in large map coordinates
\r
1989 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1991 if(FIELD_OF_VIEW_TEST){
\r
1992 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1993 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1996 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1997 //TimeTaker timer("client.updateCamera");
\r
1998 client.updateCamera(camera_position, camera_direction);
\r
2002 //TimeTaker //timer3("//timer3");
\r
2005 Calculate what block is the crosshair pointing to
\r
2008 //u32 t1 = device->getTimer()->getRealTime();
\r
2010 //f32 d = 4; // max. distance
\r
2011 f32 d = 4; // max. distance
\r
2012 core::line3d<f32> shootline(camera_position,
\r
2013 camera_position + camera_direction * BS * (d+1));
\r
2015 MapBlockObject *selected_object = client.getSelectedObject
\r
2016 (d*BS, camera_position, shootline);
\r
2019 If it's pointing to a MapBlockObject
\r
2022 if(selected_object != NULL)
\r
2024 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2026 core::aabbox3d<f32> box_on_map
\r
2027 = selected_object->getSelectionBoxOnMap();
\r
2029 hilightboxes.push_back(box_on_map);
\r
2031 infotext = narrow_to_wide(selected_object->infoText());
\r
2033 if(g_input->getLeftClicked())
\r
2035 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2036 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2037 selected_object->getId(), g_selected_item);
\r
2039 else if(g_input->getRightClicked())
\r
2041 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2043 Check if we want to modify the object ourselves
\r
2045 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2047 dstream<<"Sign object right-clicked"<<std::endl;
\r
2049 if(random_input == false)
\r
2051 // Get a new text for it
\r
2053 TextDest *dest = new TextDestSign(
\r
2054 selected_object->getBlock()->getPos(),
\r
2055 selected_object->getId(),
\r
2058 SignObject *sign_object = (SignObject*)selected_object;
\r
2060 std::wstring wtext =
\r
2061 narrow_to_wide(sign_object->getText());
\r
2063 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2064 &g_active_menu_count, dest,
\r
2069 Otherwise pass the event to the server as-is
\r
2073 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2074 selected_object->getId(), g_selected_item);
\r
2078 else // selected_object == NULL
\r
2082 Find out which node we are pointing at
\r
2085 bool nodefound = false;
\r
2087 v3s16 neighbourpos;
\r
2088 core::aabbox3d<f32> nodefacebox;
\r
2089 f32 mindistance = BS * 1001;
\r
2091 v3s16 pos_i = floatToInt(player_position);
\r
2093 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2097 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2098 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2099 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2100 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2101 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2102 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2104 for(s16 y = ystart; y <= yend; y++)
\r
2105 for(s16 z = zstart; z <= zend; z++)
\r
2106 for(s16 x = xstart; x <= xend; x++)
\r
2111 n = client.getNode(v3s16(x,y,z));
\r
2112 if(content_pointable(n.d) == false)
\r
2115 catch(InvalidPositionException &e)
\r
2121 v3f npf = intToFloat(np);
\r
2126 v3s16(0,0,1), // back
\r
2127 v3s16(0,1,0), // top
\r
2128 v3s16(1,0,0), // right
\r
2129 v3s16(0,0,-1), // front
\r
2130 v3s16(0,-1,0), // bottom
\r
2131 v3s16(-1,0,0), // left
\r
2137 if(n.d == CONTENT_TORCH)
\r
2139 v3s16 dir = unpackDir(n.dir);
\r
2140 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2141 dir_f *= BS/2 - BS/6 - BS/20;
\r
2142 v3f cpf = npf + dir_f;
\r
2143 f32 distance = (cpf - camera_position).getLength();
\r
2145 core::aabbox3d<f32> box;
\r
2148 if(dir == v3s16(0,-1,0))
\r
2150 box = core::aabbox3d<f32>(
\r
2151 npf - v3f(BS/6, BS/2, BS/6),
\r
2152 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2156 else if(dir == v3s16(0,1,0))
\r
2158 box = core::aabbox3d<f32>(
\r
2159 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2160 npf + v3f(BS/6, BS/2, BS/6)
\r
2166 box = core::aabbox3d<f32>(
\r
2167 cpf - v3f(BS/6, BS/3, BS/6),
\r
2168 cpf + v3f(BS/6, BS/3, BS/6)
\r
2172 if(distance < mindistance)
\r
2174 if(box.intersectsWithLine(shootline))
\r
2178 neighbourpos = np;
\r
2179 mindistance = distance;
\r
2180 nodefacebox = box;
\r
2189 for(u16 i=0; i<6; i++)
\r
2191 v3f dir_f = v3f(dirs[i].X,
\r
2192 dirs[i].Y, dirs[i].Z);
\r
2193 v3f centerpoint = npf + dir_f * BS/2;
\r
2195 (centerpoint - camera_position).getLength();
\r
2197 if(distance < mindistance)
\r
2199 core::CMatrix4<f32> m;
\r
2200 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2202 // This is the back face
\r
2203 v3f corners[2] = {
\r
2204 v3f(BS/2, BS/2, BS/2),
\r
2205 v3f(-BS/2, -BS/2, BS/2+d)
\r
2208 for(u16 j=0; j<2; j++)
\r
2210 m.rotateVect(corners[j]);
\r
2211 corners[j] += npf;
\r
2214 core::aabbox3d<f32> facebox(corners[0]);
\r
2215 facebox.addInternalPoint(corners[1]);
\r
2217 if(facebox.intersectsWithLine(shootline))
\r
2221 neighbourpos = np + dirs[i];
\r
2222 mindistance = distance;
\r
2223 nodefacebox = facebox;
\r
2225 } // if distance < mindistance
\r
2227 } // regular block
\r
2230 static float nodig_delay_counter = 0.0;
\r
2234 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2236 static float dig_time = 0.0;
\r
2237 static u16 dig_index = 0;
\r
2239 hilightboxes.push_back(nodefacebox);
\r
2241 if(g_input->getLeftReleased())
\r
2243 client.clearTempMod(nodepos);
\r
2247 if(nodig_delay_counter > 0.0)
\r
2249 nodig_delay_counter -= dtime;
\r
2253 if(nodepos != nodepos_old)
\r
2255 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2256 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2258 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2260 client.clearTempMod(nodepos_old);
\r
2265 if(g_input->getLeftClicked() ||
\r
2266 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2268 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2269 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2271 if(g_input->getLeftClicked())
\r
2273 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2275 if(g_input->getLeftState())
\r
2277 MapNode n = client.getNode(nodepos);
\r
2279 // Get tool name. Default is "" = bare hands
\r
2280 std::string toolname = "";
\r
2281 InventoryList *mlist = local_inventory.getList("main");
\r
2284 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2285 if(item && (std::string)item->getName() == "ToolItem")
\r
2287 ToolItem *titem = (ToolItem*)item;
\r
2288 toolname = titem->getToolName();
\r
2292 // Get digging properties for material and tool
\r
2293 u8 material = n.d;
\r
2294 DiggingProperties prop =
\r
2295 getDiggingProperties(material, toolname);
\r
2297 float dig_time_complete = 0.0;
\r
2299 if(prop.diggable == false)
\r
2301 /*dstream<<"Material "<<(int)material
\r
2302 <<" not diggable with \""
\r
2303 <<toolname<<"\""<<std::endl;*/
\r
2304 // I guess nobody will wait for this long
\r
2305 dig_time_complete = 10000000.0;
\r
2309 dig_time_complete = prop.time;
\r
2312 if(dig_time_complete >= 0.001)
\r
2314 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2315 * dig_time/dig_time_complete);
\r
2317 // This is for torches
\r
2320 dig_index = CRACK_ANIMATION_LENGTH;
\r
2323 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2325 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2326 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2330 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2331 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2332 client.clearTempMod(nodepos);
\r
2333 client.removeNode(nodepos);
\r
2337 nodig_delay_counter = dig_time_complete
\r
2338 / (float)CRACK_ANIMATION_LENGTH;
\r
2340 // We don't want a corresponding delay to
\r
2341 // very time consuming nodes
\r
2342 if(nodig_delay_counter > 0.5)
\r
2344 nodig_delay_counter = 0.5;
\r
2346 // We want a slight delay to very little
\r
2347 // time consuming nodes
\r
2348 //float mindelay = 0.15;
\r
2349 float mindelay = 0.20;
\r
2350 if(nodig_delay_counter < mindelay)
\r
2352 nodig_delay_counter = mindelay;
\r
2356 dig_time += dtime;
\r
2360 if(g_input->getRightClicked())
\r
2362 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2363 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2366 nodepos_old = nodepos;
\r
2371 } // selected_object == NULL
\r
2373 g_input->resetLeftClicked();
\r
2374 g_input->resetRightClicked();
\r
2376 if(g_input->getLeftReleased())
\r
2378 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2380 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2382 if(g_input->getRightReleased())
\r
2384 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2388 g_input->resetLeftReleased();
\r
2389 g_input->resetRightReleased();
\r
2392 Calculate stuff for drawing
\r
2395 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2397 u32 daynight_ratio = client.getDayNightRatio();
\r
2398 video::SColor bgcolor = video::SColor(
\r
2400 skycolor.getRed() * daynight_ratio / 1000,
\r
2401 skycolor.getGreen() * daynight_ratio / 1000,
\r
2402 skycolor.getBlue() * daynight_ratio / 1000);
\r
2408 if(g_settings.getBool("enable_fog") == true)
\r
2410 f32 range = draw_control.wanted_range * BS;
\r
2411 if(draw_control.range_all)
\r
2412 range = 100000*BS;
\r
2416 video::EFT_FOG_LINEAR,
\r
2420 false, // pixel fog
\r
2421 false // range fog
\r
2427 Update gui stuff (0ms)
\r
2430 //TimeTaker guiupdatetimer("Gui updating");
\r
2433 wchar_t temptext[150];
\r
2435 static float drawtime_avg = 0;
\r
2436 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2437 static float beginscenetime_avg = 0;
\r
2438 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2439 static float scenetime_avg = 0;
\r
2440 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2441 static float endscenetime_avg = 0;
\r
2442 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2444 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2446 L", R: range_all=%i"
\r
2448 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2450 draw_control.range_all,
\r
2452 beginscenetime_avg,
\r
2457 guitext->setText(temptext);
\r
2461 wchar_t temptext[150];
\r
2462 swprintf(temptext, 150,
\r
2463 L"(% .1f, % .1f, % .1f)"
\r
2464 L" (% .3f < btime_jitter < % .3f"
\r
2465 L", dtime_jitter = % .1f %%"
\r
2466 L", v_range = %.1f)",
\r
2467 player_position.X/BS,
\r
2468 player_position.Y/BS,
\r
2469 player_position.Z/BS,
\r
2470 busytime_jitter1_min_sample,
\r
2471 busytime_jitter1_max_sample,
\r
2472 dtime_jitter1_max_fraction * 100.0,
\r
2473 draw_control.wanted_range
\r
2476 guitext2->setText(temptext);
\r
2480 guitext_info->setText(infotext.c_str());
\r
2484 Get chat messages from client
\r
2487 // Get new messages
\r
2488 std::wstring message;
\r
2489 while(client.getChatMessage(message))
\r
2491 chat_lines.push_back(ChatLine(message));
\r
2492 /*if(chat_lines.size() > 6)
\r
2494 core::list<ChatLine>::Iterator
\r
2495 i = chat_lines.begin();
\r
2496 chat_lines.erase(i);
\r
2499 // Append them to form the whole static text and throw
\r
2500 // it to the gui element
\r
2501 std::wstring whole;
\r
2502 // This will correspond to the line number counted from
\r
2503 // top to bottom, from size-1 to 0
\r
2504 s16 line_number = chat_lines.size();
\r
2505 // Count of messages to be removed from the top
\r
2506 u16 to_be_removed_count = 0;
\r
2507 for(core::list<ChatLine>::Iterator
\r
2508 i = chat_lines.begin();
\r
2509 i != chat_lines.end(); i++)
\r
2511 // After this, line number is valid for this loop
\r
2514 (*i).age += dtime;
\r
2516 This results in a maximum age of 60*6 to the
\r
2517 lowermost line and a maximum of 6 lines
\r
2519 float allowed_age = (6-line_number) * 60.0;
\r
2521 if((*i).age > allowed_age)
\r
2523 to_be_removed_count++;
\r
2526 whole += (*i).text + L'\n';
\r
2528 for(u16 i=0; i<to_be_removed_count; i++)
\r
2530 core::list<ChatLine>::Iterator
\r
2531 it = chat_lines.begin();
\r
2532 chat_lines.erase(it);
\r
2534 chat_guitext->setText(whole.c_str());
\r
2535 // Update gui element size and position
\r
2536 core::rect<s32> rect(
\r
2538 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2539 screensize.X - 10,
\r
2542 chat_guitext->setRelativePosition(rect);
\r
2544 if(chat_lines.size() == 0)
\r
2545 chat_guitext->setVisible(false);
\r
2547 chat_guitext->setVisible(true);
\r
2554 static u16 old_selected_item = 65535;
\r
2555 if(client.getLocalInventoryUpdated()
\r
2556 || g_selected_item != old_selected_item)
\r
2558 old_selected_item = g_selected_item;
\r
2559 //std::cout<<"Updating local inventory"<<std::endl;
\r
2560 client.getLocalInventory(local_inventory);
\r
2561 quick_inventory->setSelection(g_selected_item);
\r
2562 quick_inventory->update();
\r
2566 Send actions returned by the inventory menu
\r
2568 while(inventory_action_queue.size() != 0)
\r
2570 InventoryAction *a = inventory_action_queue.pop_front();
\r
2572 client.sendInventoryAction(a);
\r
2581 TimeTaker drawtimer("Drawing");
\r
2585 TimeTaker timer("beginScene");
\r
2586 driver->beginScene(true, true, bgcolor);
\r
2587 //driver->beginScene(false, true, bgcolor);
\r
2588 beginscenetime = timer.stop(true);
\r
2593 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2596 TimeTaker timer("smgr");
\r
2598 scenetime = timer.stop(true);
\r
2602 //TimeTaker timer9("auxiliary drawings");
\r
2606 //TimeTaker //timer10("//timer10");
\r
2608 video::SMaterial m;
\r
2610 m.Lighting = false;
\r
2611 driver->setMaterial(m);
\r
2613 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2615 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2616 i != hilightboxes.end(); i++)
\r
2618 /*std::cout<<"hilightbox min="
\r
2619 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2621 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2623 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2629 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2630 displaycenter + core::vector2d<s32>(10,0),
\r
2631 video::SColor(255,255,255,255));
\r
2632 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2633 displaycenter + core::vector2d<s32>(0,10),
\r
2634 video::SColor(255,255,255,255));
\r
2639 //TimeTaker //timer11("//timer11");
\r
2645 guienv->drawAll();
\r
2649 TimeTaker timer("endScene");
\r
2650 driver->endScene();
\r
2651 endscenetime = timer.stop(true);
\r
2654 drawtime = drawtimer.stop(true);
\r
2660 static s16 lastFPS = 0;
\r
2661 //u16 fps = driver->getFPS();
\r
2662 u16 fps = (1.0/dtime_avg1);
\r
2664 if (lastFPS != fps)
\r
2666 core::stringw str = L"Minetest [";
\r
2667 str += driver->getName();
\r
2671 device->setWindowCaption(str.c_str());
\r
2677 device->yield();*/
\r
2680 delete quick_inventory;
\r
2682 } // client is deleted at this point
\r
2687 In the end, delete the Irrlicht device.
\r
2692 Update configuration file
\r
2694 /*if(configpath != "")
\r
2696 g_settings.updateConfigFile(configpath.c_str());
\r
2700 catch(con::PeerNotFoundException &e)
\r
2702 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2706 GUIMessageMenu *menu =
\r
2707 new GUIMessageMenu(guienv, guiroot, -1,
\r
2708 &g_active_menu_count,
\r
2709 L"Connection timed out");
\r
2711 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2713 dstream<<"Created menu"<<std::endl;
\r
2715 while(g_device->run() && menu->getStatus() == false)
\r
2717 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2718 guienv->drawAll();
\r
2719 driver->endScene();
\r
2722 dstream<<"Dropping menu"<<std::endl;
\r
2727 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2729 This is what has to be done in every thread to get suitable debug info
\r
2731 catch(std::exception &e)
\r
2733 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2734 <<e.what()<<std::endl;
\r
2739 debugstreams_deinit();
\r