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
1444 dstream<<"WARNING: Font file was not found."
\r
1445 " Using default font."<<std::endl;
\r
1446 // If font was not found, this will get us one
\r
1447 font = skin->getFont();
\r
1450 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1451 dstream<<"text_height="<<text_height<<std::endl;
\r
1453 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1454 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1455 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1456 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1457 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1458 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1460 const wchar_t *text = L"Loading and connecting...";
\r
1461 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1462 core::vector2d<s32> textsize(300, text_height);
\r
1463 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1465 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1466 text, textrect, false, false);
\r
1467 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1469 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1470 guienv->drawAll();
\r
1471 driver->endScene();
\r
1474 Preload some textures
\r
1477 init_content_inventory_texture_paths();
\r
1478 init_tile_texture_paths();
\r
1479 tile_materials_preload(g_irrlicht);
\r
1482 Make a scope here for the client so that it gets removed
\r
1483 before the irrlicht device
\r
1487 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1492 SharedPtr<Server> server;
\r
1494 server = new Server(map_dir, hm_params, map_params);
\r
1495 server->start(port);
\r
1502 Client client(device, playername, draw_control);
\r
1504 g_client = &client;
\r
1506 Address connect_address(0,0,0,0, port);
\r
1508 connect_address.Resolve(connect_name);
\r
1510 catch(ResolveError &e)
\r
1512 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1516 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1517 client.connect(connect_address);
\r
1520 while(client.connectedAndInitialized() == false)
\r
1523 if(server != NULL){
\r
1524 server->step(0.1);
\r
1529 catch(con::PeerNotFoundException &e)
\r
1531 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1538 /*scene::ISceneNode* skybox;
\r
1539 skybox = smgr->addSkyBoxSceneNode(
\r
1540 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1541 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1542 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1543 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1544 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1545 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1548 Create the camera node
\r
1551 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1552 0, // Camera parent
\r
1553 v3f(BS*100, BS*2, BS*100), // Look from
\r
1554 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1558 if(camera == NULL)
\r
1561 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1563 camera->setFOV(FOV_ANGLE);
\r
1565 // Just so big a value that everything rendered is visible
\r
1566 camera->setFarValue(100000*BS);
\r
1568 f32 camera_yaw = 0; // "right/left"
\r
1569 f32 camera_pitch = 0; // "up/down"
\r
1575 gui_loadingtext->remove();
\r
1578 Add some gui stuff
\r
1581 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1582 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1585 We need some kind of a root node to be able to add
\r
1586 custom elements directly on the screen.
\r
1587 Otherwise they won't be automatically drawn.
\r
1589 guiroot = guienv->addStaticText(L"",
\r
1590 core::rect<s32>(0, 0, 10000, 10000));
\r
1592 // Test the text input system
\r
1593 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1595 /*GUIMessageMenu *menu =
\r
1596 new GUIMessageMenu(guienv, guiroot, -1,
\r
1597 &g_active_menu_count,
\r
1601 // Launch pause menu
\r
1602 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1603 &g_active_menu_count))->drop();
\r
1605 // First line of debug text
\r
1606 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1608 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1610 // Second line of debug text
\r
1611 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1613 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1616 // At the middle of the screen
\r
1617 // Object infos are shown in this
\r
1618 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1620 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1624 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1625 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1626 core::rect<s32>(70, 60, 795, 150),
\r
1628 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1629 core::list<ChatLine> chat_lines;
\r
1632 Some statistics are collected in these
\r
1635 u32 beginscenetime = 0;
\r
1636 u32 scenetime = 0;
\r
1637 u32 endscenetime = 0;
\r
1640 //throw con::PeerNotFoundException("lol");
\r
1646 bool first_loop_after_window_activation = true;
\r
1648 // Time is in milliseconds
\r
1649 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1650 // NOTE: So we have to use getTime() and call run()s between them
\r
1651 u32 lasttime = device->getTimer()->getTime();
\r
1653 while(device->run())
\r
1656 Run global IrrlichtWrapper's main thread processing stuff
\r
1658 g_irrlicht->Run();
\r
1661 Random calculations
\r
1663 v2u32 screensize = driver->getScreenSize();
\r
1664 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1666 // Hilight boxes collected during the loop and displayed
\r
1667 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1670 std::wstring infotext;
\r
1672 //TimeTaker //timer1("//timer1");
\r
1674 // Time of frame without fps limit
\r
1678 // not using getRealTime is necessary for wine
\r
1679 u32 time = device->getTimer()->getTime();
\r
1680 if(time > lasttime)
\r
1681 busytime_u32 = time - lasttime;
\r
1684 busytime = busytime_u32 / 1000.0;
\r
1687 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1689 // Absolutelu necessary for wine!
\r
1696 updateViewingRange(busytime, &client);
\r
1703 float fps_max = g_settings.getFloat("fps_max");
\r
1704 u32 frametime_min = 1000./fps_max;
\r
1706 if(busytime_u32 < frametime_min)
\r
1708 u32 sleeptime = frametime_min - busytime_u32;
\r
1709 device->sleep(sleeptime);
\r
1713 // Absolutelu necessary for wine!
\r
1717 Time difference calculation
\r
1719 f32 dtime; // in seconds
\r
1721 u32 time = device->getTimer()->getTime();
\r
1722 if(time > lasttime)
\r
1723 dtime = (time - lasttime) / 1000.0;
\r
1729 Time average and jitter calculation
\r
1732 static f32 dtime_avg1 = 0.0;
\r
1733 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1734 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1736 static f32 dtime_jitter1_max_sample = 0.0;
\r
1737 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1739 static f32 jitter1_max = 0.0;
\r
1740 static f32 counter = 0.0;
\r
1741 if(dtime_jitter1 > jitter1_max)
\r
1742 jitter1_max = dtime_jitter1;
\r
1747 dtime_jitter1_max_sample = jitter1_max;
\r
1748 dtime_jitter1_max_fraction
\r
1749 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1750 jitter1_max = 0.0;
\r
1753 Control freetime ratio
\r
1755 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1757 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1758 g_freetime_ratio += 0.01;
\r
1762 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1763 g_freetime_ratio -= 0.01;
\r
1769 Busytime average and jitter calculation
\r
1772 static f32 busytime_avg1 = 0.0;
\r
1773 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1774 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1776 static f32 busytime_jitter1_max_sample = 0.0;
\r
1777 static f32 busytime_jitter1_min_sample = 0.0;
\r
1779 static f32 jitter1_max = 0.0;
\r
1780 static f32 jitter1_min = 0.0;
\r
1781 static f32 counter = 0.0;
\r
1782 if(busytime_jitter1 > jitter1_max)
\r
1783 jitter1_max = busytime_jitter1;
\r
1784 if(busytime_jitter1 < jitter1_min)
\r
1785 jitter1_min = busytime_jitter1;
\r
1787 if(counter > 0.0){
\r
1789 busytime_jitter1_max_sample = jitter1_max;
\r
1790 busytime_jitter1_min_sample = jitter1_min;
\r
1791 jitter1_max = 0.0;
\r
1792 jitter1_min = 0.0;
\r
1797 Debug info for client
\r
1800 static float counter = 0.0;
\r
1805 client.printDebugInfo(std::cout);
\r
1810 Input handler step()
\r
1812 g_input->step(dtime);
\r
1815 Player speed control
\r
1824 bool a_superspeed,
\r
1827 PlayerControl control(
\r
1828 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1829 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1830 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1831 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1832 g_input->isKeyDown(irr::KEY_SPACE),
\r
1833 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1837 client.setPlayerControl(control);
\r
1841 Process environment
\r
1845 //TimeTaker timer("client.step(dtime)");
\r
1846 client.step(dtime);
\r
1847 //client.step(dtime_avg1);
\r
1850 if(server != NULL)
\r
1852 //TimeTaker timer("server->step(dtime)");
\r
1853 server->step(dtime);
\r
1856 v3f player_position = client.getPlayerPosition();
\r
1858 //TimeTaker //timer2("//timer2");
\r
1861 Mouse and camera control
\r
1864 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1867 device->getCursorControl()->setVisible(false);
\r
1869 if(first_loop_after_window_activation){
\r
1870 //std::cout<<"window active, first loop"<<std::endl;
\r
1871 first_loop_after_window_activation = false;
\r
1874 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1875 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1876 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1877 camera_yaw -= dx*0.2;
\r
1878 camera_pitch += dy*0.2;
\r
1879 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1880 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1882 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1885 device->getCursorControl()->setVisible(true);
\r
1887 //std::cout<<"window inactive"<<std::endl;
\r
1888 first_loop_after_window_activation = true;
\r
1891 camera_yaw = wrapDegrees(camera_yaw);
\r
1892 camera_pitch = wrapDegrees(camera_pitch);
\r
1894 v3f camera_direction = v3f(0,0,1);
\r
1895 camera_direction.rotateYZBy(camera_pitch);
\r
1896 camera_direction.rotateXZBy(camera_yaw);
\r
1898 // This is at the height of the eyes of the current figure
\r
1899 v3f camera_position =
\r
1900 player_position + v3f(0, BS+BS/2, 0);
\r
1901 // This is more like in minecraft
\r
1902 /*v3f camera_position =
\r
1903 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1905 camera->setPosition(camera_position);
\r
1906 // *100.0 helps in large map coordinates
\r
1907 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1909 if(FIELD_OF_VIEW_TEST){
\r
1910 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1911 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1914 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1915 //TimeTaker timer("client.updateCamera");
\r
1916 client.updateCamera(camera_position, camera_direction);
\r
1920 //TimeTaker //timer3("//timer3");
\r
1923 Calculate what block is the crosshair pointing to
\r
1926 //u32 t1 = device->getTimer()->getRealTime();
\r
1928 //f32 d = 4; // max. distance
\r
1929 f32 d = 4; // max. distance
\r
1930 core::line3d<f32> shootline(camera_position,
\r
1931 camera_position + camera_direction * BS * (d+1));
\r
1933 MapBlockObject *selected_object = client.getSelectedObject
\r
1934 (d*BS, camera_position, shootline);
\r
1937 If it's pointing to a MapBlockObject
\r
1940 if(selected_object != NULL)
\r
1942 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1944 core::aabbox3d<f32> box_on_map
\r
1945 = selected_object->getSelectionBoxOnMap();
\r
1947 hilightboxes.push_back(box_on_map);
\r
1949 infotext = narrow_to_wide(selected_object->infoText());
\r
1951 if(g_input->getLeftClicked())
\r
1953 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1954 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1955 selected_object->getId(), g_selected_item);
\r
1957 else if(g_input->getRightClicked())
\r
1959 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1961 Check if we want to modify the object ourselves
\r
1963 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1965 dstream<<"Sign object right-clicked"<<std::endl;
\r
1967 if(random_input == false)
\r
1969 // Get a new text for it
\r
1971 TextDest *dest = new TextDestSign(
\r
1972 selected_object->getBlock()->getPos(),
\r
1973 selected_object->getId(),
\r
1976 SignObject *sign_object = (SignObject*)selected_object;
\r
1978 std::wstring wtext =
\r
1979 narrow_to_wide(sign_object->getText());
\r
1981 (new GUITextInputMenu(guienv, guiroot, -1,
\r
1982 &g_active_menu_count, dest,
\r
1987 Otherwise pass the event to the server as-is
\r
1991 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1992 selected_object->getId(), g_selected_item);
\r
1996 else // selected_object == NULL
\r
2000 Find out which node we are pointing at
\r
2003 bool nodefound = false;
\r
2005 v3s16 neighbourpos;
\r
2006 core::aabbox3d<f32> nodefacebox;
\r
2007 f32 mindistance = BS * 1001;
\r
2009 v3s16 pos_i = floatToInt(player_position);
\r
2011 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2015 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2016 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2017 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2018 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2019 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2020 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2022 for(s16 y = ystart; y <= yend; y++)
\r
2023 for(s16 z = zstart; z <= zend; z++)
\r
2024 for(s16 x = xstart; x <= xend; x++)
\r
2029 n = client.getNode(v3s16(x,y,z));
\r
2030 if(content_pointable(n.d) == false)
\r
2033 catch(InvalidPositionException &e)
\r
2039 v3f npf = intToFloat(np);
\r
2044 v3s16(0,0,1), // back
\r
2045 v3s16(0,1,0), // top
\r
2046 v3s16(1,0,0), // right
\r
2047 v3s16(0,0,-1), // front
\r
2048 v3s16(0,-1,0), // bottom
\r
2049 v3s16(-1,0,0), // left
\r
2055 if(n.d == CONTENT_TORCH)
\r
2057 v3s16 dir = unpackDir(n.dir);
\r
2058 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2059 dir_f *= BS/2 - BS/6 - BS/20;
\r
2060 v3f cpf = npf + dir_f;
\r
2061 f32 distance = (cpf - camera_position).getLength();
\r
2063 core::aabbox3d<f32> box;
\r
2066 if(dir == v3s16(0,-1,0))
\r
2068 box = core::aabbox3d<f32>(
\r
2069 npf - v3f(BS/6, BS/2, BS/6),
\r
2070 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2074 else if(dir == v3s16(0,1,0))
\r
2076 box = core::aabbox3d<f32>(
\r
2077 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2078 npf + v3f(BS/6, BS/2, BS/6)
\r
2084 box = core::aabbox3d<f32>(
\r
2085 cpf - v3f(BS/6, BS/3, BS/6),
\r
2086 cpf + v3f(BS/6, BS/3, BS/6)
\r
2090 if(distance < mindistance)
\r
2092 if(box.intersectsWithLine(shootline))
\r
2096 neighbourpos = np;
\r
2097 mindistance = distance;
\r
2098 nodefacebox = box;
\r
2107 for(u16 i=0; i<6; i++)
\r
2109 v3f dir_f = v3f(dirs[i].X,
\r
2110 dirs[i].Y, dirs[i].Z);
\r
2111 v3f centerpoint = npf + dir_f * BS/2;
\r
2113 (centerpoint - camera_position).getLength();
\r
2115 if(distance < mindistance)
\r
2117 core::CMatrix4<f32> m;
\r
2118 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2120 // This is the back face
\r
2121 v3f corners[2] = {
\r
2122 v3f(BS/2, BS/2, BS/2),
\r
2123 v3f(-BS/2, -BS/2, BS/2+d)
\r
2126 for(u16 j=0; j<2; j++)
\r
2128 m.rotateVect(corners[j]);
\r
2129 corners[j] += npf;
\r
2132 core::aabbox3d<f32> facebox(corners[0]);
\r
2133 facebox.addInternalPoint(corners[1]);
\r
2135 if(facebox.intersectsWithLine(shootline))
\r
2139 neighbourpos = np + dirs[i];
\r
2140 mindistance = distance;
\r
2141 nodefacebox = facebox;
\r
2143 } // if distance < mindistance
\r
2145 } // regular block
\r
2148 static float nodig_delay_counter = 0.0;
\r
2152 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2154 static float dig_time = 0.0;
\r
2155 static u16 dig_index = 0;
\r
2157 hilightboxes.push_back(nodefacebox);
\r
2159 if(g_input->getLeftReleased())
\r
2161 client.clearTempMod(nodepos);
\r
2165 if(nodig_delay_counter > 0.0)
\r
2167 nodig_delay_counter -= dtime;
\r
2171 if(nodepos != nodepos_old)
\r
2173 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2174 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2176 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2178 client.clearTempMod(nodepos_old);
\r
2183 if(g_input->getLeftClicked() ||
\r
2184 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2186 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2187 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2189 if(g_input->getLeftClicked())
\r
2191 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2193 if(g_input->getLeftState())
\r
2195 MapNode n = client.getNode(nodepos);
\r
2197 // Get tool name. Default is "" = bare hands
\r
2198 std::string toolname = "";
\r
2199 InventoryList *mlist = local_inventory.getList("main");
\r
2202 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2203 if(item && (std::string)item->getName() == "ToolItem")
\r
2205 ToolItem *titem = (ToolItem*)item;
\r
2206 toolname = titem->getToolName();
\r
2210 // Get digging properties for material and tool
\r
2211 u8 material = n.d;
\r
2212 DiggingProperties prop =
\r
2213 getDiggingProperties(material, toolname);
\r
2215 float dig_time_complete = 0.0;
\r
2217 if(prop.diggable == false)
\r
2219 /*dstream<<"Material "<<(int)material
\r
2220 <<" not diggable with \""
\r
2221 <<toolname<<"\""<<std::endl;*/
\r
2222 // I guess nobody will wait for this long
\r
2223 dig_time_complete = 10000000.0;
\r
2227 dig_time_complete = prop.time;
\r
2230 if(dig_time_complete >= 0.001)
\r
2232 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2233 * dig_time/dig_time_complete);
\r
2235 // This is for torches
\r
2238 dig_index = CRACK_ANIMATION_LENGTH;
\r
2241 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2243 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2244 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2248 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2249 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2250 client.clearTempMod(nodepos);
\r
2251 client.removeNode(nodepos);
\r
2255 nodig_delay_counter = dig_time_complete
\r
2256 / (float)CRACK_ANIMATION_LENGTH;
\r
2258 // We don't want a corresponding delay to
\r
2259 // very time consuming nodes
\r
2260 if(nodig_delay_counter > 0.5)
\r
2262 nodig_delay_counter = 0.5;
\r
2264 // We want a slight delay to very little
\r
2265 // time consuming nodes
\r
2266 //float mindelay = 0.15;
\r
2267 float mindelay = 0.20;
\r
2268 if(nodig_delay_counter < mindelay)
\r
2270 nodig_delay_counter = mindelay;
\r
2274 dig_time += dtime;
\r
2278 if(g_input->getRightClicked())
\r
2280 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2281 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2284 nodepos_old = nodepos;
\r
2289 } // selected_object == NULL
\r
2291 g_input->resetLeftClicked();
\r
2292 g_input->resetRightClicked();
\r
2294 if(g_input->getLeftReleased())
\r
2296 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2298 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2300 if(g_input->getRightReleased())
\r
2302 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2306 g_input->resetLeftReleased();
\r
2307 g_input->resetRightReleased();
\r
2310 Calculate stuff for drawing
\r
2313 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2315 u32 daynight_ratio = client.getDayNightRatio();
\r
2316 /*video::SColor bgcolor = video::SColor(
\r
2318 skycolor.getRed() * daynight_ratio / 1000,
\r
2319 skycolor.getGreen() * daynight_ratio / 1000,
\r
2320 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2322 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2323 video::SColor bgcolor = video::SColor(
\r
2325 skycolor.getRed() * l / 255,
\r
2326 skycolor.getGreen() * l / 255,
\r
2327 skycolor.getBlue() * l / 255);
\r
2333 if(g_settings.getBool("enable_fog") == true)
\r
2335 f32 range = draw_control.wanted_range * BS;
\r
2336 if(draw_control.range_all)
\r
2337 range = 100000*BS;
\r
2341 video::EFT_FOG_LINEAR,
\r
2345 false, // pixel fog
\r
2346 false // range fog
\r
2352 Update gui stuff (0ms)
\r
2355 //TimeTaker guiupdatetimer("Gui updating");
\r
2358 wchar_t temptext[150];
\r
2360 static float drawtime_avg = 0;
\r
2361 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2362 static float beginscenetime_avg = 0;
\r
2363 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2364 static float scenetime_avg = 0;
\r
2365 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2366 static float endscenetime_avg = 0;
\r
2367 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2369 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2371 L", R: range_all=%i"
\r
2373 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2375 draw_control.range_all,
\r
2377 beginscenetime_avg,
\r
2382 guitext->setText(temptext);
\r
2386 wchar_t temptext[150];
\r
2387 swprintf(temptext, 150,
\r
2388 L"(% .1f, % .1f, % .1f)"
\r
2389 L" (% .3f < btime_jitter < % .3f"
\r
2390 L", dtime_jitter = % .1f %%"
\r
2391 L", v_range = %.1f)",
\r
2392 player_position.X/BS,
\r
2393 player_position.Y/BS,
\r
2394 player_position.Z/BS,
\r
2395 busytime_jitter1_min_sample,
\r
2396 busytime_jitter1_max_sample,
\r
2397 dtime_jitter1_max_fraction * 100.0,
\r
2398 draw_control.wanted_range
\r
2401 guitext2->setText(temptext);
\r
2405 guitext_info->setText(infotext.c_str());
\r
2409 Get chat messages from client
\r
2412 // Get new messages
\r
2413 std::wstring message;
\r
2414 while(client.getChatMessage(message))
\r
2416 chat_lines.push_back(ChatLine(message));
\r
2417 /*if(chat_lines.size() > 6)
\r
2419 core::list<ChatLine>::Iterator
\r
2420 i = chat_lines.begin();
\r
2421 chat_lines.erase(i);
\r
2424 // Append them to form the whole static text and throw
\r
2425 // it to the gui element
\r
2426 std::wstring whole;
\r
2427 // This will correspond to the line number counted from
\r
2428 // top to bottom, from size-1 to 0
\r
2429 s16 line_number = chat_lines.size();
\r
2430 // Count of messages to be removed from the top
\r
2431 u16 to_be_removed_count = 0;
\r
2432 for(core::list<ChatLine>::Iterator
\r
2433 i = chat_lines.begin();
\r
2434 i != chat_lines.end(); i++)
\r
2436 // After this, line number is valid for this loop
\r
2439 (*i).age += dtime;
\r
2441 This results in a maximum age of 60*6 to the
\r
2442 lowermost line and a maximum of 6 lines
\r
2444 float allowed_age = (6-line_number) * 60.0;
\r
2446 if((*i).age > allowed_age)
\r
2448 to_be_removed_count++;
\r
2451 whole += (*i).text + L'\n';
\r
2453 for(u16 i=0; i<to_be_removed_count; i++)
\r
2455 core::list<ChatLine>::Iterator
\r
2456 it = chat_lines.begin();
\r
2457 chat_lines.erase(it);
\r
2459 chat_guitext->setText(whole.c_str());
\r
2460 // Update gui element size and position
\r
2461 core::rect<s32> rect(
\r
2463 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2464 screensize.X - 10,
\r
2467 chat_guitext->setRelativePosition(rect);
\r
2469 if(chat_lines.size() == 0)
\r
2470 chat_guitext->setVisible(false);
\r
2472 chat_guitext->setVisible(true);
\r
2479 static u16 old_selected_item = 65535;
\r
2480 if(client.getLocalInventoryUpdated()
\r
2481 || g_selected_item != old_selected_item)
\r
2483 old_selected_item = g_selected_item;
\r
2484 //std::cout<<"Updating local inventory"<<std::endl;
\r
2485 client.getLocalInventory(local_inventory);
\r
2486 quick_inventory->setSelection(g_selected_item);
\r
2487 quick_inventory->update();
\r
2491 Send actions returned by the inventory menu
\r
2493 while(inventory_action_queue.size() != 0)
\r
2495 InventoryAction *a = inventory_action_queue.pop_front();
\r
2497 client.sendInventoryAction(a);
\r
2506 TimeTaker drawtimer("Drawing");
\r
2510 TimeTaker timer("beginScene");
\r
2511 driver->beginScene(true, true, bgcolor);
\r
2512 //driver->beginScene(false, true, bgcolor);
\r
2513 beginscenetime = timer.stop(true);
\r
2518 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2521 TimeTaker timer("smgr");
\r
2523 scenetime = timer.stop(true);
\r
2527 //TimeTaker timer9("auxiliary drawings");
\r
2531 //TimeTaker //timer10("//timer10");
\r
2533 video::SMaterial m;
\r
2535 m.Lighting = false;
\r
2536 driver->setMaterial(m);
\r
2538 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2540 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2541 i != hilightboxes.end(); i++)
\r
2543 /*std::cout<<"hilightbox min="
\r
2544 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2546 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2548 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2554 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2555 displaycenter + core::vector2d<s32>(10,0),
\r
2556 video::SColor(255,255,255,255));
\r
2557 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2558 displaycenter + core::vector2d<s32>(0,10),
\r
2559 video::SColor(255,255,255,255));
\r
2564 //TimeTaker //timer11("//timer11");
\r
2570 guienv->drawAll();
\r
2574 TimeTaker timer("endScene");
\r
2575 driver->endScene();
\r
2576 endscenetime = timer.stop(true);
\r
2579 drawtime = drawtimer.stop(true);
\r
2585 static s16 lastFPS = 0;
\r
2586 //u16 fps = driver->getFPS();
\r
2587 u16 fps = (1.0/dtime_avg1);
\r
2589 if (lastFPS != fps)
\r
2591 core::stringw str = L"Minetest [";
\r
2592 str += driver->getName();
\r
2596 device->setWindowCaption(str.c_str());
\r
2602 device->yield();*/
\r
2605 delete quick_inventory;
\r
2607 } // client is deleted at this point
\r
2612 In the end, delete the Irrlicht device.
\r
2617 Update configuration file
\r
2619 /*if(configpath != "")
\r
2621 g_settings.updateConfigFile(configpath.c_str());
\r
2625 catch(con::PeerNotFoundException &e)
\r
2627 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2631 GUIMessageMenu *menu =
\r
2632 new GUIMessageMenu(guienv, guiroot, -1,
\r
2633 &g_active_menu_count,
\r
2634 L"Connection timed out");
\r
2636 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2638 dstream<<"Created menu"<<std::endl;
\r
2640 while(g_device->run() && menu->getStatus() == false)
\r
2642 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2643 guienv->drawAll();
\r
2644 driver->endScene();
\r
2647 dstream<<"Dropping menu"<<std::endl;
\r
2653 END_DEBUG_EXCEPTION_HANDLER
\r
2655 debugstreams_deinit();
\r