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: Startup and configuration menu
\r
118 TODO: There are some lighting-related todos and fixmes in
\r
119 ServerMap::emergeBlock
\r
121 TODO: Proper handling of spawning place (try to find something that
\r
122 is not in the middle of an ocean (some land to stand on at
\r
123 least) and save it in map config.
\r
125 TODO: Players to only be hidden when the client quits.
\r
126 TODO: - Players to be saved on disk, with inventory
\r
127 TODO: Players to be saved as text in map/players/<name>
\r
128 TODO: Player inventory to be saved on disk
\r
130 TODO: Make fetching sector's blocks more efficient when rendering
\r
131 sectors that have very large amounts of blocks (on client)
\r
133 TODO: Make the video backend selectable
\r
135 TODO: Copy the text of the last picked sign to inventory in creative
\r
138 TODO: Get rid of GotSplitPacketException
\r
140 TODO: Check what goes wrong with caching map to disk (Kray)
\r
143 Block object server side:
\r
144 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
145 - For all blocks in the buffer, objects are stepped(). This
\r
146 means they are active.
\r
147 - TODO: A global active buffer is needed for the server
\r
148 - TODO: A timestamp to blocks
\r
149 - TODO: All blocks going in and out of the buffer are recorded.
\r
150 - TODO: For outgoing blocks, timestamp is written.
\r
151 - TODO: For incoming blocks, time difference is calculated and
\r
152 objects are stepped according to it.
\r
154 TODO: Better handling of objects and mobs
\r
156 - There has to be some way to do it with less spaghetti code
\r
157 - Make separate classes for client and server
\r
158 - Client should not discriminate between blocks, server should
\r
159 - Make other players utilize the same framework
\r
160 - This is also needed for objects that don't get sent to client
\r
161 but are used for triggers etc
\r
163 TODO: Draw big amounts of torches better (that is, throw them in the
\r
164 same meshbuffer (can the meshcollector class be used?))
\r
166 TODO: Make an option to the server to disable building and digging near
\r
167 the starting position
\r
169 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
170 need an additional metadata field for the texts
\r
171 - This is also needed for item container chests
\r
172 TODO: There has to be some better way to handle static objects than to
\r
173 send them all the time. This affects signs and item objects.
\r
175 TODO: When server sees that client is removing an inexistent block or
\r
176 adding a block to an existent position, resend the MapBlock.
\r
178 TODO: When player dies, throw items on map
\r
180 TODO: Use porting::path_userdata for configuration file
\r
182 TODO: Optimize day/night mesh updating somehow
\r
183 - create copies of all textures for all lighting values and only
\r
184 change texture for material?
\r
185 - Umm... the collecting of the faces is the slow part
\r
186 -> what about just changing the color values of the existing
\r
187 meshbuffers? It should go quite fast.
\r
189 TODO: Map generator version 2
\r
190 - Create surface areas based on central points; a given point's
\r
191 area type is given by the nearest central point
\r
192 - Separate points for heightmap, caves, plants and minerals?
\r
193 - Flat land, mountains, forest, jungle
\r
196 TODO: Add gui option to remove map
\r
199 ======================================================================
\r
201 ======================================================================
\r
206 Setting this to 1 enables a special camera mode that forces
\r
207 the renderers to think that the camera statically points from
\r
208 the starting place to a static direction.
\r
210 This allows one to move around with the player and see what
\r
211 is actually drawn behind solid things and behind the player.
\r
213 #define FIELD_OF_VIEW_TEST 0
\r
217 #pragma message ("Disabling unit tests")
\r
219 #warning "Disabling unit tests"
\r
221 // Disable unit tests
\r
222 #define ENABLE_TESTS 0
\r
224 // Enable unit tests
\r
225 #define ENABLE_TESTS 1
\r
229 #pragma comment(lib, "Irrlicht.lib")
\r
230 #pragma comment(lib, "jthread.lib")
\r
231 #pragma comment(lib, "zlibwapi.lib")
\r
232 // This would get rid of the console window
\r
233 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
236 #include <iostream>
\r
238 #include <jmutexautolock.h>
\r
239 #include <locale.h>
\r
240 #include "common_irrlicht.h"
\r
243 #include "player.h"
\r
246 #include "environment.h"
\r
247 #include "server.h"
\r
248 #include "client.h"
\r
249 #include "serialization.h"
\r
250 #include "constants.h"
\r
251 #include "strfnd.h"
\r
252 #include "porting.h"
\r
253 #include "irrlichtwrapper.h"
\r
254 #include "gettime.h"
\r
255 #include "porting.h"
\r
256 #include "guiPauseMenu.h"
\r
257 #include "guiInventoryMenu.h"
\r
258 #include "guiTextInputMenu.h"
\r
259 #include "materials.h"
\r
260 #include "guiMessageMenu.h"
\r
261 #include "filesys.h"
\r
262 #include "config.h"
\r
264 IrrlichtWrapper *g_irrlicht;
\r
266 MapDrawControl draw_control;
\r
270 These are loaded from the config file.
\r
273 Settings g_settings;
\r
275 extern void set_default_settings();
\r
281 IrrlichtDevice *g_device = NULL;
\r
282 Client *g_client = NULL;
\r
287 gui::IGUIEnvironment* guienv = NULL;
\r
288 gui::IGUIStaticText *guiroot = NULL;
\r
289 int g_active_menu_count = 0;
\r
291 bool noMenuActive()
\r
293 return (g_active_menu_count == 0);
\r
296 // Inventory actions from the menu are buffered here before sending
\r
297 Queue<InventoryAction*> inventory_action_queue;
\r
298 // This is a copy of the inventory that the client's environment has
\r
299 Inventory local_inventory;
\r
301 u16 g_selected_item = 0;
\r
308 std::ostream *dout_con_ptr = &dummyout;
\r
309 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
310 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
311 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
312 //std::ostream *dout_con_ptr = &dstream;
\r
313 //std::ostream *derr_con_ptr = &dstream;
\r
316 std::ostream *dout_server_ptr = &dstream;
\r
317 std::ostream *derr_server_ptr = &dstream;
\r
320 std::ostream *dout_client_ptr = &dstream;
\r
321 std::ostream *derr_client_ptr = &dstream;
\r
324 gettime.h implementation
\r
330 Use irrlicht because it is more precise than porting.h's
\r
333 if(g_irrlicht == NULL)
\r
335 return g_irrlicht->getTime();
\r
342 struct TextDestSign : public TextDest
\r
344 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
346 m_blockpos = blockpos;
\r
350 void gotText(std::wstring text)
\r
352 std::string ntext = wide_to_narrow(text);
\r
353 dstream<<"Changing text of a sign object: "
\r
354 <<ntext<<std::endl;
\r
355 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
363 struct TextDestChat : public TextDest
\r
365 TextDestChat(Client *client)
\r
369 void gotText(std::wstring text)
\r
371 m_client->sendChatMessage(text);
\r
372 m_client->addChatMessage(text);
\r
378 class MyEventReceiver : public IEventReceiver
\r
381 // This is the one method that we have to implement
\r
382 virtual bool OnEvent(const SEvent& event)
\r
385 React to nothing here if a menu is active
\r
387 if(noMenuActive() == false)
\r
393 // Remember whether each key is down or up
\r
394 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
396 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
398 if(event.KeyInput.PressedDown)
\r
400 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
406 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
408 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
410 dstream<<DTIME<<"MyEventReceiver: "
\r
411 <<"Launching pause menu"<<std::endl;
\r
412 // It will delete itself by itself
\r
413 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
414 &g_active_menu_count))->drop();
\r
417 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
419 dstream<<DTIME<<"MyEventReceiver: "
\r
420 <<"Launching inventory"<<std::endl;
\r
421 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
422 &local_inventory, &inventory_action_queue,
\r
423 &g_active_menu_count))->drop();
\r
426 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
428 TextDest *dest = new TextDestChat(g_client);
\r
430 (new GUITextInputMenu(guienv, guiroot, -1,
\r
431 &g_active_menu_count, dest,
\r
436 // Material selection
\r
437 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
439 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
442 g_selected_item = 0;
\r
443 dstream<<DTIME<<"Selected item: "
\r
444 <<g_selected_item<<std::endl;
\r
447 // Viewing range selection
\r
448 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
450 if(draw_control.range_all)
\r
452 draw_control.range_all = false;
\r
453 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
457 draw_control.range_all = true;
\r
458 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
462 // Print debug stacks
\r
463 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
465 dstream<<"-----------------------------------------"
\r
467 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
468 dstream<<"-----------------------------------------"
\r
470 debug_stacks_print();
\r
475 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
477 if(noMenuActive() == false)
\r
479 left_active = false;
\r
480 middle_active = false;
\r
481 right_active = false;
\r
485 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
486 left_active = event.MouseInput.isLeftPressed();
\r
487 middle_active = event.MouseInput.isMiddlePressed();
\r
488 right_active = event.MouseInput.isRightPressed();
\r
490 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
492 leftclicked = true;
\r
494 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
496 rightclicked = true;
\r
498 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
500 leftreleased = true;
\r
502 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
504 rightreleased = true;
\r
506 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
508 /*dstream<<"event.MouseInput.Wheel="
\r
509 <<event.MouseInput.Wheel<<std::endl;*/
\r
510 if(event.MouseInput.Wheel < 0)
\r
512 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
515 g_selected_item = 0;
\r
517 else if(event.MouseInput.Wheel > 0)
\r
519 if(g_selected_item > 0)
\r
522 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
531 // This is used to check whether a key is being held down
\r
532 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
534 return keyIsDown[keyCode];
\r
539 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
540 keyIsDown[i] = false;
\r
542 leftclicked = false;
\r
543 rightclicked = false;
\r
544 leftreleased = false;
\r
545 rightreleased = false;
\r
547 left_active = false;
\r
548 middle_active = false;
\r
549 right_active = false;
\r
560 bool rightreleased;
\r
563 bool middle_active;
\r
567 // We use this array to store the current state of each key
\r
568 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
571 IrrlichtDevice *m_device;
\r
580 virtual ~InputHandler()
\r
584 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
586 virtual v2s32 getMousePos() = 0;
\r
587 virtual void setMousePos(s32 x, s32 y) = 0;
\r
589 virtual bool getLeftState() = 0;
\r
590 virtual bool getRightState() = 0;
\r
592 virtual bool getLeftClicked() = 0;
\r
593 virtual bool getRightClicked() = 0;
\r
594 virtual void resetLeftClicked() = 0;
\r
595 virtual void resetRightClicked() = 0;
\r
597 virtual bool getLeftReleased() = 0;
\r
598 virtual bool getRightReleased() = 0;
\r
599 virtual void resetLeftReleased() = 0;
\r
600 virtual void resetRightReleased() = 0;
\r
602 virtual void step(float dtime) {};
\r
604 virtual void clear() {};
\r
607 InputHandler *g_input = NULL;
\r
609 class RealInputHandler : public InputHandler
\r
612 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
614 m_receiver(receiver)
\r
617 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
619 return m_receiver->IsKeyDown(keyCode);
\r
621 virtual v2s32 getMousePos()
\r
623 return m_device->getCursorControl()->getPosition();
\r
625 virtual void setMousePos(s32 x, s32 y)
\r
627 m_device->getCursorControl()->setPosition(x, y);
\r
630 virtual bool getLeftState()
\r
632 return m_receiver->left_active;
\r
634 virtual bool getRightState()
\r
636 return m_receiver->right_active;
\r
639 virtual bool getLeftClicked()
\r
641 return m_receiver->leftclicked;
\r
643 virtual bool getRightClicked()
\r
645 return m_receiver->rightclicked;
\r
647 virtual void resetLeftClicked()
\r
649 m_receiver->leftclicked = false;
\r
651 virtual void resetRightClicked()
\r
653 m_receiver->rightclicked = false;
\r
656 virtual bool getLeftReleased()
\r
658 return m_receiver->leftreleased;
\r
660 virtual bool getRightReleased()
\r
662 return m_receiver->rightreleased;
\r
664 virtual void resetLeftReleased()
\r
666 m_receiver->leftreleased = false;
\r
668 virtual void resetRightReleased()
\r
670 m_receiver->rightreleased = false;
\r
675 resetRightClicked();
\r
676 resetLeftClicked();
\r
679 IrrlichtDevice *m_device;
\r
680 MyEventReceiver *m_receiver;
\r
683 class RandomInputHandler : public InputHandler
\r
686 RandomInputHandler()
\r
688 leftclicked = false;
\r
689 rightclicked = false;
\r
690 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
691 keydown[i] = false;
\r
693 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
695 return keydown[keyCode];
\r
697 virtual v2s32 getMousePos()
\r
701 virtual void setMousePos(s32 x, s32 y)
\r
703 mousepos = v2s32(x,y);
\r
706 virtual bool getLeftState()
\r
710 virtual bool getRightState()
\r
715 virtual bool getLeftClicked()
\r
717 return leftclicked;
\r
719 virtual bool getRightClicked()
\r
721 return rightclicked;
\r
723 virtual void resetLeftClicked()
\r
725 leftclicked = false;
\r
727 virtual void resetRightClicked()
\r
729 rightclicked = false;
\r
732 virtual bool getLeftReleased()
\r
736 virtual bool getRightReleased()
\r
740 virtual void resetLeftReleased()
\r
743 virtual void resetRightReleased()
\r
747 virtual void step(float dtime)
\r
750 static float counter1 = 0;
\r
754 counter1 = 0.1*Rand(1,10);
\r
755 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
756 g_selected_material++;
\r
758 g_selected_material = 0;*/
\r
759 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
762 g_selected_item = 0;
\r
766 static float counter1 = 0;
\r
770 counter1 = 0.1*Rand(1, 40);
\r
771 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
775 static float counter1 = 0;
\r
779 counter1 = 0.1*Rand(1, 40);
\r
780 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
784 static float counter1 = 0;
\r
788 counter1 = 0.1*Rand(1, 40);
\r
789 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
793 static float counter1 = 0;
\r
797 counter1 = 0.1*Rand(1, 40);
\r
798 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
802 static float counter1 = 0;
\r
806 counter1 = 0.1*Rand(1, 20);
\r
807 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
811 static float counter1 = 0;
\r
815 counter1 = 0.1*Rand(1, 30);
\r
816 leftclicked = true;
\r
820 static float counter1 = 0;
\r
824 counter1 = 0.1*Rand(1, 20);
\r
825 rightclicked = true;
\r
828 mousepos += mousespeed;
\r
831 s32 Rand(s32 min, s32 max)
\r
833 return (myrand()%(max-min+1))+min;
\r
836 bool keydown[KEY_KEY_CODES_COUNT];
\r
843 void updateViewingRange(f32 frametime_in, Client *client)
\r
845 if(draw_control.range_all == true)
\r
848 static f32 added_frametime = 0;
\r
849 static s16 added_frames = 0;
\r
851 added_frametime += frametime_in;
\r
854 // Actually this counter kind of sucks because frametime is busytime
\r
855 static f32 counter = 0;
\r
856 counter -= frametime_in;
\r
862 /*dstream<<__FUNCTION_NAME
\r
863 <<": Collected "<<added_frames<<" frames, total of "
\r
864 <<added_frametime<<"s."<<std::endl;*/
\r
866 /*dstream<<"draw_control.blocks_drawn="
\r
867 <<draw_control.blocks_drawn
\r
868 <<", draw_control.blocks_would_have_drawn="
\r
869 <<draw_control.blocks_would_have_drawn
\r
872 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
873 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
875 draw_control.wanted_min_range = range_min;
\r
876 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
878 float block_draw_ratio = 1.0;
\r
879 if(draw_control.blocks_would_have_drawn != 0)
\r
881 block_draw_ratio = (float)draw_control.blocks_drawn
\r
882 / (float)draw_control.blocks_would_have_drawn;
\r
885 // Calculate the average frametime in the case that all wanted
\r
886 // blocks had been drawn
\r
887 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
889 added_frametime = 0.0;
\r
892 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
893 float wanted_frametime = 1.0 / wanted_fps;
\r
895 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
896 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
898 // If needed frametime change is very small, just return
\r
899 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
901 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
905 float range = draw_control.wanted_range;
\r
906 float new_range = range;
\r
908 static s16 range_old = 0;
\r
909 static f32 frametime_old = 0;
\r
911 float d_range = range - range_old;
\r
912 f32 d_frametime = frametime - frametime_old;
\r
913 // A sane default of 30ms per 50 nodes of range
\r
914 static f32 time_per_range = 30. / 50;
\r
917 time_per_range = d_frametime / d_range;
\r
920 // The minimum allowed calculated frametime-range derivative:
\r
921 // Practically this sets the maximum speed of changing the range.
\r
922 // The lower this value, the higher the maximum changing speed.
\r
923 // A low value here results in wobbly range (0.001)
\r
924 // A high value here results in slow changing range (0.0025)
\r
925 // SUGG: This could be dynamically adjusted so that when
\r
926 // the camera is turning, this is lower
\r
927 //float min_time_per_range = 0.0015;
\r
928 float min_time_per_range = 0.0010;
\r
929 //float min_time_per_range = 0.05 / range;
\r
930 if(time_per_range < min_time_per_range)
\r
932 time_per_range = min_time_per_range;
\r
933 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
937 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
940 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
941 // Dampen the change a bit to kill oscillations
\r
942 //wanted_range_change *= 0.9;
\r
943 //wanted_range_change *= 0.75;
\r
944 wanted_range_change *= 0.5;
\r
945 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
947 // If needed range change is very small, just return
\r
948 if(fabs(wanted_range_change) < 0.001)
\r
950 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
954 new_range += wanted_range_change;
\r
955 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
957 //float new_range_unclamped = new_range;
\r
958 if(new_range < range_min)
\r
959 new_range = range_min;
\r
960 if(new_range > range_max)
\r
961 new_range = range_max;
\r
963 /*if(new_range != new_range_unclamped)
\r
964 dstream<<", clamped to "<<new_range<<std::endl;
\r
966 dstream<<std::endl;*/
\r
968 draw_control.wanted_range = new_range;
\r
970 range_old = new_range;
\r
971 frametime_old = frametime;
\r
974 class GUIQuickInventory : public IEventReceiver
\r
978 gui::IGUIEnvironment* env,
\r
979 gui::IGUIElement* parent,
\r
982 Inventory *inventory):
\r
983 m_itemcount(itemcount),
\r
984 m_inventory(inventory)
\r
986 core::rect<s32> imgsize(0,0,48,48);
\r
987 core::rect<s32> textsize(0,0,48,16);
\r
988 v2s32 spacing(0, 64);
\r
989 for(s32 i=0; i<m_itemcount; i++)
\r
991 m_images.push_back(env->addImage(
\r
992 imgsize + pos + spacing*i
\r
994 m_images[i]->setScaleImage(true);
\r
995 m_texts.push_back(env->addStaticText(
\r
997 textsize + pos + spacing*i,
\r
1000 m_texts[i]->setBackgroundColor(
\r
1001 video::SColor(128,0,0,0));
\r
1002 m_texts[i]->setTextAlignment(
\r
1003 gui::EGUIA_CENTER,
\r
1004 gui::EGUIA_UPPERLEFT);
\r
1008 virtual bool OnEvent(const SEvent& event)
\r
1013 void setSelection(s32 i)
\r
1022 start = m_selection - m_itemcount / 2;
\r
1024 InventoryList *mainlist = m_inventory->getList("main");
\r
1026 for(s32 i=0; i<m_itemcount; i++)
\r
1028 s32 j = i + start;
\r
1030 if(j > (s32)mainlist->getSize() - 1)
\r
1031 j -= mainlist->getSize();
\r
1033 j += mainlist->getSize();
\r
1035 InventoryItem *item = mainlist->getItem(j);
\r
1039 m_images[i]->setImage(NULL);
\r
1042 if(m_selection == j)
\r
1043 swprintf(t, 10, L"<-");
\r
1045 swprintf(t, 10, L"");
\r
1046 m_texts[i]->setText(t);
\r
1048 // The next ifs will segfault with a NULL pointer
\r
1053 m_images[i]->setImage(item->getImage());
\r
1056 if(m_selection == j)
\r
1057 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1059 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1060 m_texts[i]->setText(t);
\r
1066 core::array<gui::IGUIStaticText*> m_texts;
\r
1067 core::array<gui::IGUIImage*> m_images;
\r
1068 Inventory *m_inventory;
\r
1079 ChatLine(const std::wstring &a_text):
\r
1085 std::wstring text;
\r
1088 int main(int argc, char *argv[])
\r
1091 Low-level initialization
\r
1094 bool disable_stderr = false;
\r
1096 disable_stderr = true;
\r
1099 // Initialize debug streams
\r
1100 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1101 // Initialize debug stacks
\r
1102 debug_stacks_init();
\r
1104 DSTACK(__FUNCTION_NAME);
\r
1106 porting::initializePaths();
\r
1107 // Create user data directory
\r
1108 fs::CreateDir(porting::path_userdata);
\r
1110 initializeMaterialProperties();
\r
1112 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1114 // Print startup message
\r
1115 dstream<<DTIME<<"minetest-c55"
\r
1116 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1117 <<", "<<BUILD_INFO
\r
1124 Parse command line
\r
1127 // List all allowed options
\r
1128 core::map<std::string, ValueSpec> allowed_options;
\r
1129 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1130 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1131 "Run server directly"));
\r
1132 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1133 "Load configuration from specified file"));
\r
1134 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1135 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1136 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1137 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1138 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1139 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1141 Settings cmd_args;
\r
1143 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1145 if(ret == false || cmd_args.getFlag("help"))
\r
1147 dstream<<"Allowed options:"<<std::endl;
\r
1148 for(core::map<std::string, ValueSpec>::Iterator
\r
1149 i = allowed_options.getIterator();
\r
1150 i.atEnd() == false; i++)
\r
1152 dstream<<" --"<<i.getNode()->getKey();
\r
1153 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1158 dstream<<" <value>";
\r
1160 dstream<<std::endl;
\r
1162 if(i.getNode()->getValue().help != NULL)
\r
1164 dstream<<" "<<i.getNode()->getValue().help
\r
1169 return cmd_args.getFlag("help") ? 0 : 1;
\r
1174 Basic initialization
\r
1177 // Initialize default settings
\r
1178 set_default_settings();
\r
1180 // Set locale. This is for forcing '.' as the decimal point.
\r
1181 std::locale::global(std::locale("C"));
\r
1182 // This enables printing all characters in bitmap font
\r
1183 setlocale(LC_CTYPE, "en_US");
\r
1185 // Initialize sockets
\r
1187 atexit(sockets_cleanup);
\r
1197 // Path of configuration file in use
\r
1198 std::string configpath = "";
\r
1200 if(cmd_args.exists("config"))
\r
1202 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1205 dstream<<"Could not read configuration from \""
\r
1206 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1209 configpath = cmd_args.get("config");
\r
1213 core::array<std::string> filenames;
\r
1214 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1215 #ifdef RUN_IN_PLACE
\r
1216 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1219 for(u32 i=0; i<filenames.size(); i++)
\r
1221 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1224 configpath = filenames[i];
\r
1230 // Initialize random seed
\r
1237 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1238 || cmd_args.getFlag("enable-unittests") == true)
\r
1243 // Read map parameters from settings
\r
1245 HMParams hm_params;
\r
1246 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1247 hm_params.randmax = g_settings.get("height_randmax");
\r
1248 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1249 hm_params.base = g_settings.get("height_base");
\r
1251 MapParams map_params;
\r
1252 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1253 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1259 std::cout<<std::endl<<std::endl;
\r
1262 <<" .__ __ __ "<<std::endl
\r
1263 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1264 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1265 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1266 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1267 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1270 std::cout<<std::endl;
\r
1271 //char templine[100];
\r
1275 if(cmd_args.exists("port"))
\r
1277 port = cmd_args.getU16("port");
\r
1281 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1282 std::cout<<"-> "<<port<<std::endl;
\r
1286 std::string map_dir = porting::path_userdata+"/map";
\r
1287 if(cmd_args.exists("map-dir"))
\r
1288 map_dir = cmd_args.get("map-dir");
\r
1289 else if(g_settings.exists("map-dir"))
\r
1290 map_dir = g_settings.get("map-dir");
\r
1292 if(cmd_args.getFlag("server"))
\r
1294 DSTACK("Dedicated server branch");
\r
1296 std::cout<<std::endl;
\r
1297 std::cout<<"========================"<<std::endl;
\r
1298 std::cout<<"Running dedicated server"<<std::endl;
\r
1299 std::cout<<"========================"<<std::endl;
\r
1300 std::cout<<std::endl;
\r
1302 Server server(map_dir, hm_params, map_params);
\r
1303 server.start(port);
\r
1307 // This is kind of a hack but can be done like this
\r
1308 // because server.step() is very light
\r
1310 server.step(0.030);
\r
1312 static int counter = 0;
\r
1318 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1319 core::list<PlayerInfo>::Iterator i;
\r
1320 static u32 sum_old = 0;
\r
1321 u32 sum = PIChecksum(list);
\r
1322 if(sum != sum_old)
\r
1324 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1325 for(i=list.begin(); i!=list.end(); i++)
\r
1327 i->PrintLine(&std::cout);
\r
1337 bool hosting = false;
\r
1338 char connect_name[100] = "";
\r
1340 if(cmd_args.exists("address"))
\r
1342 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1344 else if(is_yes(g_settings.get("host_game")) == false)
\r
1346 if(g_settings.get("address") != "")
\r
1348 std::cout<<g_settings.get("address")<<std::endl;
\r
1349 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1353 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1354 std::cin.getline(connect_name, 100);
\r
1358 if(connect_name[0] == 0){
\r
1359 snprintf(connect_name, 100, "127.0.0.1");
\r
1364 std::cout<<"> Hosting game"<<std::endl;
\r
1366 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1368 char playername[PLAYERNAME_SIZE] = "";
\r
1369 if(g_settings.get("name") != "")
\r
1371 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1375 std::cout<<"Name of player: ";
\r
1376 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1378 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1381 Resolution selection
\r
1384 bool fullscreen = false;
\r
1385 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1386 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1390 MyEventReceiver receiver;
\r
1392 video::E_DRIVER_TYPE driverType;
\r
1395 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1396 driverType = video::EDT_OPENGL;
\r
1398 driverType = video::EDT_OPENGL;
\r
1401 // create device and exit if creation failed
\r
1403 IrrlichtDevice *device;
\r
1404 device = createDevice(driverType,
\r
1405 core::dimension2d<u32>(screenW, screenH),
\r
1406 16, fullscreen, false, false, &receiver);
\r
1409 return 1; // could not create selected driver.
\r
1411 g_device = device;
\r
1412 g_irrlicht = new IrrlichtWrapper(device);
\r
1414 //g_device = device;
\r
1416 device->setResizable(true);
\r
1418 bool random_input = g_settings.getBool("random_input")
\r
1419 || cmd_args.getFlag("random-input");
\r
1421 g_input = new RandomInputHandler();
\r
1423 g_input = new RealInputHandler(device, &receiver);
\r
1426 Continue initialization
\r
1429 video::IVideoDriver* driver = device->getVideoDriver();
\r
1432 This changes the minimum allowed number of vertices in a VBO
\r
1434 //driver->setMinHardwareBufferVertexCount(50);
\r
1436 scene::ISceneManager* smgr = device->getSceneManager();
\r
1438 guienv = device->getGUIEnvironment();
\r
1439 gui::IGUISkin* skin = guienv->getSkin();
\r
1440 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1442 skin->setFont(font);
\r
1443 // If font was not found, this will get us one
\r
1444 font = skin->getFont();
\r
1446 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1447 dstream<<"text_height="<<text_height<<std::endl;
\r
1449 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1450 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1451 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1452 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1453 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1454 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1456 const wchar_t *text = L"Loading and connecting...";
\r
1457 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1458 core::vector2d<s32> textsize(300, text_height);
\r
1459 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1461 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1462 text, textrect, false, false);
\r
1463 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1465 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1466 guienv->drawAll();
\r
1467 driver->endScene();
\r
1470 Preload some textures
\r
1473 init_content_inventory_texture_paths();
\r
1474 init_tile_texture_paths();
\r
1475 tile_materials_preload(g_irrlicht);
\r
1478 Make a scope here for the client so that it gets removed
\r
1479 before the irrlicht device
\r
1483 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1488 SharedPtr<Server> server;
\r
1490 server = new Server(map_dir, hm_params, map_params);
\r
1491 server->start(port);
\r
1498 Client client(device, playername, draw_control);
\r
1500 g_client = &client;
\r
1502 Address connect_address(0,0,0,0, port);
\r
1504 connect_address.Resolve(connect_name);
\r
1506 catch(ResolveError &e)
\r
1508 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1512 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1513 client.connect(connect_address);
\r
1516 while(client.connectedAndInitialized() == false)
\r
1519 if(server != NULL){
\r
1520 server->step(0.1);
\r
1525 catch(con::PeerNotFoundException &e)
\r
1527 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1534 /*scene::ISceneNode* skybox;
\r
1535 skybox = smgr->addSkyBoxSceneNode(
\r
1536 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1537 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1538 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1539 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1540 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1541 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1544 Create the camera node
\r
1547 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1548 0, // Camera parent
\r
1549 v3f(BS*100, BS*2, BS*100), // Look from
\r
1550 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1554 if(camera == NULL)
\r
1557 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1559 camera->setFOV(FOV_ANGLE);
\r
1561 // Just so big a value that everything rendered is visible
\r
1562 camera->setFarValue(100000*BS);
\r
1564 f32 camera_yaw = 0; // "right/left"
\r
1565 f32 camera_pitch = 0; // "up/down"
\r
1571 gui_loadingtext->remove();
\r
1574 Add some gui stuff
\r
1577 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1578 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1581 We need some kind of a root node to be able to add
\r
1582 custom elements directly on the screen.
\r
1583 Otherwise they won't be automatically drawn.
\r
1585 guiroot = guienv->addStaticText(L"",
\r
1586 core::rect<s32>(0, 0, 10000, 10000));
\r
1588 // Test the text input system
\r
1589 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1591 /*GUIMessageMenu *menu =
\r
1592 new GUIMessageMenu(guienv, guiroot, -1,
\r
1593 &g_active_menu_count,
\r
1597 // Launch pause menu
\r
1598 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1599 &g_active_menu_count))->drop();
\r
1601 // First line of debug text
\r
1602 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1604 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1606 // Second line of debug text
\r
1607 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1609 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1612 // At the middle of the screen
\r
1613 // Object infos are shown in this
\r
1614 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1616 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1620 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1621 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1622 core::rect<s32>(70, 60, 795, 150),
\r
1624 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1625 core::list<ChatLine> chat_lines;
\r
1628 Some statistics are collected in these
\r
1631 u32 beginscenetime = 0;
\r
1632 u32 scenetime = 0;
\r
1633 u32 endscenetime = 0;
\r
1636 //throw con::PeerNotFoundException("lol");
\r
1642 bool first_loop_after_window_activation = true;
\r
1644 // Time is in milliseconds
\r
1645 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1646 // NOTE: So we have to use getTime() and call run()s between them
\r
1647 u32 lasttime = device->getTimer()->getTime();
\r
1649 while(device->run())
\r
1652 Run global IrrlichtWrapper's main thread processing stuff
\r
1654 g_irrlicht->Run();
\r
1657 Random calculations
\r
1659 v2u32 screensize = driver->getScreenSize();
\r
1660 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1662 // Hilight boxes collected during the loop and displayed
\r
1663 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1666 std::wstring infotext;
\r
1668 //TimeTaker //timer1("//timer1");
\r
1670 // Time of frame without fps limit
\r
1674 // not using getRealTime is necessary for wine
\r
1675 u32 time = device->getTimer()->getTime();
\r
1676 if(time > lasttime)
\r
1677 busytime_u32 = time - lasttime;
\r
1680 busytime = busytime_u32 / 1000.0;
\r
1683 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1685 // Absolutelu necessary for wine!
\r
1692 updateViewingRange(busytime, &client);
\r
1699 float fps_max = g_settings.getFloat("fps_max");
\r
1700 u32 frametime_min = 1000./fps_max;
\r
1702 if(busytime_u32 < frametime_min)
\r
1704 u32 sleeptime = frametime_min - busytime_u32;
\r
1705 device->sleep(sleeptime);
\r
1709 // Absolutelu necessary for wine!
\r
1713 Time difference calculation
\r
1715 f32 dtime; // in seconds
\r
1717 u32 time = device->getTimer()->getTime();
\r
1718 if(time > lasttime)
\r
1719 dtime = (time - lasttime) / 1000.0;
\r
1725 Time average and jitter calculation
\r
1728 static f32 dtime_avg1 = 0.0;
\r
1729 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1730 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1732 static f32 dtime_jitter1_max_sample = 0.0;
\r
1733 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1735 static f32 jitter1_max = 0.0;
\r
1736 static f32 counter = 0.0;
\r
1737 if(dtime_jitter1 > jitter1_max)
\r
1738 jitter1_max = dtime_jitter1;
\r
1743 dtime_jitter1_max_sample = jitter1_max;
\r
1744 dtime_jitter1_max_fraction
\r
1745 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1746 jitter1_max = 0.0;
\r
1749 Control freetime ratio
\r
1751 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1753 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1754 g_freetime_ratio += 0.01;
\r
1758 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1759 g_freetime_ratio -= 0.01;
\r
1765 Busytime average and jitter calculation
\r
1768 static f32 busytime_avg1 = 0.0;
\r
1769 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1770 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1772 static f32 busytime_jitter1_max_sample = 0.0;
\r
1773 static f32 busytime_jitter1_min_sample = 0.0;
\r
1775 static f32 jitter1_max = 0.0;
\r
1776 static f32 jitter1_min = 0.0;
\r
1777 static f32 counter = 0.0;
\r
1778 if(busytime_jitter1 > jitter1_max)
\r
1779 jitter1_max = busytime_jitter1;
\r
1780 if(busytime_jitter1 < jitter1_min)
\r
1781 jitter1_min = busytime_jitter1;
\r
1783 if(counter > 0.0){
\r
1785 busytime_jitter1_max_sample = jitter1_max;
\r
1786 busytime_jitter1_min_sample = jitter1_min;
\r
1787 jitter1_max = 0.0;
\r
1788 jitter1_min = 0.0;
\r
1793 Debug info for client
\r
1796 static float counter = 0.0;
\r
1801 client.printDebugInfo(std::cout);
\r
1806 Input handler step()
\r
1808 g_input->step(dtime);
\r
1811 Player speed control
\r
1820 bool a_superspeed,
\r
1823 PlayerControl control(
\r
1824 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1825 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1826 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1827 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1828 g_input->isKeyDown(irr::KEY_SPACE),
\r
1829 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1833 client.setPlayerControl(control);
\r
1837 Process environment
\r
1841 //TimeTaker timer("client.step(dtime)");
\r
1842 client.step(dtime);
\r
1843 //client.step(dtime_avg1);
\r
1846 if(server != NULL)
\r
1848 //TimeTaker timer("server->step(dtime)");
\r
1849 server->step(dtime);
\r
1852 v3f player_position = client.getPlayerPosition();
\r
1854 //TimeTaker //timer2("//timer2");
\r
1857 Mouse and camera control
\r
1860 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1863 device->getCursorControl()->setVisible(false);
\r
1865 if(first_loop_after_window_activation){
\r
1866 //std::cout<<"window active, first loop"<<std::endl;
\r
1867 first_loop_after_window_activation = false;
\r
1870 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1871 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1872 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1873 camera_yaw -= dx*0.2;
\r
1874 camera_pitch += dy*0.2;
\r
1875 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1876 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1878 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1881 device->getCursorControl()->setVisible(true);
\r
1883 //std::cout<<"window inactive"<<std::endl;
\r
1884 first_loop_after_window_activation = true;
\r
1887 camera_yaw = wrapDegrees(camera_yaw);
\r
1888 camera_pitch = wrapDegrees(camera_pitch);
\r
1890 v3f camera_direction = v3f(0,0,1);
\r
1891 camera_direction.rotateYZBy(camera_pitch);
\r
1892 camera_direction.rotateXZBy(camera_yaw);
\r
1894 // This is at the height of the eyes of the current figure
\r
1895 v3f camera_position =
\r
1896 player_position + v3f(0, BS+BS/2, 0);
\r
1897 // This is more like in minecraft
\r
1898 /*v3f camera_position =
\r
1899 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1901 camera->setPosition(camera_position);
\r
1902 // *100.0 helps in large map coordinates
\r
1903 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1905 if(FIELD_OF_VIEW_TEST){
\r
1906 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1907 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1910 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1911 //TimeTaker timer("client.updateCamera");
\r
1912 client.updateCamera(camera_position, camera_direction);
\r
1916 //TimeTaker //timer3("//timer3");
\r
1919 Calculate what block is the crosshair pointing to
\r
1922 //u32 t1 = device->getTimer()->getRealTime();
\r
1924 //f32 d = 4; // max. distance
\r
1925 f32 d = 4; // max. distance
\r
1926 core::line3d<f32> shootline(camera_position,
\r
1927 camera_position + camera_direction * BS * (d+1));
\r
1929 MapBlockObject *selected_object = client.getSelectedObject
\r
1930 (d*BS, camera_position, shootline);
\r
1933 If it's pointing to a MapBlockObject
\r
1936 if(selected_object != NULL)
\r
1938 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1940 core::aabbox3d<f32> box_on_map
\r
1941 = selected_object->getSelectionBoxOnMap();
\r
1943 hilightboxes.push_back(box_on_map);
\r
1945 infotext = narrow_to_wide(selected_object->infoText());
\r
1947 if(g_input->getLeftClicked())
\r
1949 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1950 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1951 selected_object->getId(), g_selected_item);
\r
1953 else if(g_input->getRightClicked())
\r
1955 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1957 Check if we want to modify the object ourselves
\r
1959 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1961 dstream<<"Sign object right-clicked"<<std::endl;
\r
1963 if(random_input == false)
\r
1965 // Get a new text for it
\r
1967 TextDest *dest = new TextDestSign(
\r
1968 selected_object->getBlock()->getPos(),
\r
1969 selected_object->getId(),
\r
1972 SignObject *sign_object = (SignObject*)selected_object;
\r
1974 std::wstring wtext =
\r
1975 narrow_to_wide(sign_object->getText());
\r
1977 (new GUITextInputMenu(guienv, guiroot, -1,
\r
1978 &g_active_menu_count, dest,
\r
1983 Otherwise pass the event to the server as-is
\r
1987 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1988 selected_object->getId(), g_selected_item);
\r
1992 else // selected_object == NULL
\r
1996 Find out which node we are pointing at
\r
1999 bool nodefound = false;
\r
2001 v3s16 neighbourpos;
\r
2002 core::aabbox3d<f32> nodefacebox;
\r
2003 f32 mindistance = BS * 1001;
\r
2005 v3s16 pos_i = floatToInt(player_position);
\r
2007 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2011 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2012 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2013 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2014 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2015 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2016 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2018 for(s16 y = ystart; y <= yend; y++)
\r
2019 for(s16 z = zstart; z <= zend; z++)
\r
2020 for(s16 x = xstart; x <= xend; x++)
\r
2025 n = client.getNode(v3s16(x,y,z));
\r
2026 if(content_pointable(n.d) == false)
\r
2029 catch(InvalidPositionException &e)
\r
2035 v3f npf = intToFloat(np);
\r
2040 v3s16(0,0,1), // back
\r
2041 v3s16(0,1,0), // top
\r
2042 v3s16(1,0,0), // right
\r
2043 v3s16(0,0,-1), // front
\r
2044 v3s16(0,-1,0), // bottom
\r
2045 v3s16(-1,0,0), // left
\r
2051 if(n.d == CONTENT_TORCH)
\r
2053 v3s16 dir = unpackDir(n.dir);
\r
2054 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2055 dir_f *= BS/2 - BS/6 - BS/20;
\r
2056 v3f cpf = npf + dir_f;
\r
2057 f32 distance = (cpf - camera_position).getLength();
\r
2059 core::aabbox3d<f32> box;
\r
2062 if(dir == v3s16(0,-1,0))
\r
2064 box = core::aabbox3d<f32>(
\r
2065 npf - v3f(BS/6, BS/2, BS/6),
\r
2066 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2070 else if(dir == v3s16(0,1,0))
\r
2072 box = core::aabbox3d<f32>(
\r
2073 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2074 npf + v3f(BS/6, BS/2, BS/6)
\r
2080 box = core::aabbox3d<f32>(
\r
2081 cpf - v3f(BS/6, BS/3, BS/6),
\r
2082 cpf + v3f(BS/6, BS/3, BS/6)
\r
2086 if(distance < mindistance)
\r
2088 if(box.intersectsWithLine(shootline))
\r
2092 neighbourpos = np;
\r
2093 mindistance = distance;
\r
2094 nodefacebox = box;
\r
2103 for(u16 i=0; i<6; i++)
\r
2105 v3f dir_f = v3f(dirs[i].X,
\r
2106 dirs[i].Y, dirs[i].Z);
\r
2107 v3f centerpoint = npf + dir_f * BS/2;
\r
2109 (centerpoint - camera_position).getLength();
\r
2111 if(distance < mindistance)
\r
2113 core::CMatrix4<f32> m;
\r
2114 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2116 // This is the back face
\r
2117 v3f corners[2] = {
\r
2118 v3f(BS/2, BS/2, BS/2),
\r
2119 v3f(-BS/2, -BS/2, BS/2+d)
\r
2122 for(u16 j=0; j<2; j++)
\r
2124 m.rotateVect(corners[j]);
\r
2125 corners[j] += npf;
\r
2128 core::aabbox3d<f32> facebox(corners[0]);
\r
2129 facebox.addInternalPoint(corners[1]);
\r
2131 if(facebox.intersectsWithLine(shootline))
\r
2135 neighbourpos = np + dirs[i];
\r
2136 mindistance = distance;
\r
2137 nodefacebox = facebox;
\r
2139 } // if distance < mindistance
\r
2141 } // regular block
\r
2144 static float nodig_delay_counter = 0.0;
\r
2148 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2150 static float dig_time = 0.0;
\r
2151 static u16 dig_index = 0;
\r
2153 hilightboxes.push_back(nodefacebox);
\r
2155 if(g_input->getLeftReleased())
\r
2157 client.clearTempMod(nodepos);
\r
2161 if(nodig_delay_counter > 0.0)
\r
2163 nodig_delay_counter -= dtime;
\r
2167 if(nodepos != nodepos_old)
\r
2169 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2170 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2172 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2174 client.clearTempMod(nodepos_old);
\r
2179 if(g_input->getLeftClicked() ||
\r
2180 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2182 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2183 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2185 if(g_input->getLeftClicked())
\r
2187 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2189 if(g_input->getLeftState())
\r
2191 MapNode n = client.getNode(nodepos);
\r
2193 // Get tool name. Default is "" = bare hands
\r
2194 std::string toolname = "";
\r
2195 InventoryList *mlist = local_inventory.getList("main");
\r
2198 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2199 if(item && (std::string)item->getName() == "ToolItem")
\r
2201 ToolItem *titem = (ToolItem*)item;
\r
2202 toolname = titem->getToolName();
\r
2206 // Get digging properties for material and tool
\r
2207 u8 material = n.d;
\r
2208 DiggingProperties prop =
\r
2209 getDiggingProperties(material, toolname);
\r
2211 float dig_time_complete = 0.0;
\r
2213 if(prop.diggable == false)
\r
2215 /*dstream<<"Material "<<(int)material
\r
2216 <<" not diggable with \""
\r
2217 <<toolname<<"\""<<std::endl;*/
\r
2218 // I guess nobody will wait for this long
\r
2219 dig_time_complete = 10000000.0;
\r
2223 dig_time_complete = prop.time;
\r
2226 if(dig_time_complete >= 0.001)
\r
2228 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2229 * dig_time/dig_time_complete);
\r
2231 // This is for torches
\r
2234 dig_index = CRACK_ANIMATION_LENGTH;
\r
2237 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2239 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2240 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2244 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2245 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2246 client.clearTempMod(nodepos);
\r
2247 client.removeNode(nodepos);
\r
2251 nodig_delay_counter = dig_time_complete
\r
2252 / (float)CRACK_ANIMATION_LENGTH;
\r
2254 // We don't want a corresponding delay to
\r
2255 // very time consuming nodes
\r
2256 if(nodig_delay_counter > 0.5)
\r
2258 nodig_delay_counter = 0.5;
\r
2260 // We want a slight delay to very little
\r
2261 // time consuming nodes
\r
2262 //float mindelay = 0.15;
\r
2263 float mindelay = 0.20;
\r
2264 if(nodig_delay_counter < mindelay)
\r
2266 nodig_delay_counter = mindelay;
\r
2270 dig_time += dtime;
\r
2274 if(g_input->getRightClicked())
\r
2276 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2277 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2280 nodepos_old = nodepos;
\r
2285 } // selected_object == NULL
\r
2287 g_input->resetLeftClicked();
\r
2288 g_input->resetRightClicked();
\r
2290 if(g_input->getLeftReleased())
\r
2292 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2294 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2296 if(g_input->getRightReleased())
\r
2298 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2302 g_input->resetLeftReleased();
\r
2303 g_input->resetRightReleased();
\r
2306 Calculate stuff for drawing
\r
2309 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2311 u32 daynight_ratio = client.getDayNightRatio();
\r
2312 /*video::SColor bgcolor = video::SColor(
\r
2314 skycolor.getRed() * daynight_ratio / 1000,
\r
2315 skycolor.getGreen() * daynight_ratio / 1000,
\r
2316 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2318 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2319 video::SColor bgcolor = video::SColor(
\r
2321 skycolor.getRed() * l / 255,
\r
2322 skycolor.getGreen() * l / 255,
\r
2323 skycolor.getBlue() * l / 255);
\r
2329 if(g_settings.getBool("enable_fog") == true)
\r
2331 f32 range = draw_control.wanted_range * BS;
\r
2332 if(draw_control.range_all)
\r
2333 range = 100000*BS;
\r
2337 video::EFT_FOG_LINEAR,
\r
2341 false, // pixel fog
\r
2342 false // range fog
\r
2348 Update gui stuff (0ms)
\r
2351 //TimeTaker guiupdatetimer("Gui updating");
\r
2354 wchar_t temptext[150];
\r
2356 static float drawtime_avg = 0;
\r
2357 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2358 static float beginscenetime_avg = 0;
\r
2359 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2360 static float scenetime_avg = 0;
\r
2361 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2362 static float endscenetime_avg = 0;
\r
2363 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2365 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2367 L", R: range_all=%i"
\r
2369 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2371 draw_control.range_all,
\r
2373 beginscenetime_avg,
\r
2378 guitext->setText(temptext);
\r
2382 wchar_t temptext[150];
\r
2383 swprintf(temptext, 150,
\r
2384 L"(% .1f, % .1f, % .1f)"
\r
2385 L" (% .3f < btime_jitter < % .3f"
\r
2386 L", dtime_jitter = % .1f %%"
\r
2387 L", v_range = %.1f)",
\r
2388 player_position.X/BS,
\r
2389 player_position.Y/BS,
\r
2390 player_position.Z/BS,
\r
2391 busytime_jitter1_min_sample,
\r
2392 busytime_jitter1_max_sample,
\r
2393 dtime_jitter1_max_fraction * 100.0,
\r
2394 draw_control.wanted_range
\r
2397 guitext2->setText(temptext);
\r
2401 guitext_info->setText(infotext.c_str());
\r
2405 Get chat messages from client
\r
2408 // Get new messages
\r
2409 std::wstring message;
\r
2410 while(client.getChatMessage(message))
\r
2412 chat_lines.push_back(ChatLine(message));
\r
2413 /*if(chat_lines.size() > 6)
\r
2415 core::list<ChatLine>::Iterator
\r
2416 i = chat_lines.begin();
\r
2417 chat_lines.erase(i);
\r
2420 // Append them to form the whole static text and throw
\r
2421 // it to the gui element
\r
2422 std::wstring whole;
\r
2423 // This will correspond to the line number counted from
\r
2424 // top to bottom, from size-1 to 0
\r
2425 s16 line_number = chat_lines.size();
\r
2426 // Count of messages to be removed from the top
\r
2427 u16 to_be_removed_count = 0;
\r
2428 for(core::list<ChatLine>::Iterator
\r
2429 i = chat_lines.begin();
\r
2430 i != chat_lines.end(); i++)
\r
2432 // After this, line number is valid for this loop
\r
2435 (*i).age += dtime;
\r
2437 This results in a maximum age of 60*6 to the
\r
2438 lowermost line and a maximum of 6 lines
\r
2440 float allowed_age = (6-line_number) * 60.0;
\r
2442 if((*i).age > allowed_age)
\r
2444 to_be_removed_count++;
\r
2447 whole += (*i).text + L'\n';
\r
2449 for(u16 i=0; i<to_be_removed_count; i++)
\r
2451 core::list<ChatLine>::Iterator
\r
2452 it = chat_lines.begin();
\r
2453 chat_lines.erase(it);
\r
2455 chat_guitext->setText(whole.c_str());
\r
2456 // Update gui element size and position
\r
2457 core::rect<s32> rect(
\r
2459 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2460 screensize.X - 10,
\r
2463 chat_guitext->setRelativePosition(rect);
\r
2465 if(chat_lines.size() == 0)
\r
2466 chat_guitext->setVisible(false);
\r
2468 chat_guitext->setVisible(true);
\r
2475 static u16 old_selected_item = 65535;
\r
2476 if(client.getLocalInventoryUpdated()
\r
2477 || g_selected_item != old_selected_item)
\r
2479 old_selected_item = g_selected_item;
\r
2480 //std::cout<<"Updating local inventory"<<std::endl;
\r
2481 client.getLocalInventory(local_inventory);
\r
2482 quick_inventory->setSelection(g_selected_item);
\r
2483 quick_inventory->update();
\r
2487 Send actions returned by the inventory menu
\r
2489 while(inventory_action_queue.size() != 0)
\r
2491 InventoryAction *a = inventory_action_queue.pop_front();
\r
2493 client.sendInventoryAction(a);
\r
2502 TimeTaker drawtimer("Drawing");
\r
2506 TimeTaker timer("beginScene");
\r
2507 driver->beginScene(true, true, bgcolor);
\r
2508 //driver->beginScene(false, true, bgcolor);
\r
2509 beginscenetime = timer.stop(true);
\r
2514 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2517 TimeTaker timer("smgr");
\r
2519 scenetime = timer.stop(true);
\r
2523 //TimeTaker timer9("auxiliary drawings");
\r
2527 //TimeTaker //timer10("//timer10");
\r
2529 video::SMaterial m;
\r
2531 m.Lighting = false;
\r
2532 driver->setMaterial(m);
\r
2534 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2536 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2537 i != hilightboxes.end(); i++)
\r
2539 /*std::cout<<"hilightbox min="
\r
2540 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2542 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2544 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2550 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2551 displaycenter + core::vector2d<s32>(10,0),
\r
2552 video::SColor(255,255,255,255));
\r
2553 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2554 displaycenter + core::vector2d<s32>(0,10),
\r
2555 video::SColor(255,255,255,255));
\r
2560 //TimeTaker //timer11("//timer11");
\r
2566 guienv->drawAll();
\r
2570 TimeTaker timer("endScene");
\r
2571 driver->endScene();
\r
2572 endscenetime = timer.stop(true);
\r
2575 drawtime = drawtimer.stop(true);
\r
2581 static s16 lastFPS = 0;
\r
2582 //u16 fps = driver->getFPS();
\r
2583 u16 fps = (1.0/dtime_avg1);
\r
2585 if (lastFPS != fps)
\r
2587 core::stringw str = L"Minetest [";
\r
2588 str += driver->getName();
\r
2592 device->setWindowCaption(str.c_str());
\r
2598 device->yield();*/
\r
2601 delete quick_inventory;
\r
2603 } // client is deleted at this point
\r
2608 In the end, delete the Irrlicht device.
\r
2613 Update configuration file
\r
2615 /*if(configpath != "")
\r
2617 g_settings.updateConfigFile(configpath.c_str());
\r
2621 catch(con::PeerNotFoundException &e)
\r
2623 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2627 GUIMessageMenu *menu =
\r
2628 new GUIMessageMenu(guienv, guiroot, -1,
\r
2629 &g_active_menu_count,
\r
2630 L"Connection timed out");
\r
2632 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2634 dstream<<"Created menu"<<std::endl;
\r
2636 while(g_device->run() && menu->getStatus() == false)
\r
2638 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2639 guienv->drawAll();
\r
2640 driver->endScene();
\r
2643 dstream<<"Dropping menu"<<std::endl;
\r
2649 END_DEBUG_EXCEPTION_HANDLER
\r
2651 debugstreams_deinit();
\r