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 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1445 dstream<<"text_height="<<text_height<<std::endl;
\r
1447 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1448 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1449 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1450 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1451 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1452 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1454 const wchar_t *text = L"Loading and connecting...";
\r
1455 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1456 core::vector2d<s32> textsize(300, text_height);
\r
1457 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1459 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1460 text, textrect, false, false);
\r
1461 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1463 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1464 guienv->drawAll();
\r
1465 driver->endScene();
\r
1468 Preload some textures
\r
1471 init_content_inventory_texture_paths();
\r
1472 init_tile_texture_paths();
\r
1473 tile_materials_preload(g_irrlicht);
\r
1476 Make a scope here for the client so that it gets removed
\r
1477 before the irrlicht device
\r
1481 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1486 SharedPtr<Server> server;
\r
1488 server = new Server(map_dir, hm_params, map_params);
\r
1489 server->start(port);
\r
1496 Client client(device, playername, draw_control);
\r
1498 g_client = &client;
\r
1500 Address connect_address(0,0,0,0, port);
\r
1502 connect_address.Resolve(connect_name);
\r
1504 catch(ResolveError &e)
\r
1506 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1510 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1511 client.connect(connect_address);
\r
1514 while(client.connectedAndInitialized() == false)
\r
1517 if(server != NULL){
\r
1518 server->step(0.1);
\r
1523 catch(con::PeerNotFoundException &e)
\r
1525 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1532 /*scene::ISceneNode* skybox;
\r
1533 skybox = smgr->addSkyBoxSceneNode(
\r
1534 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1535 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1536 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1537 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1538 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1539 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1542 Create the camera node
\r
1545 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1546 0, // Camera parent
\r
1547 v3f(BS*100, BS*2, BS*100), // Look from
\r
1548 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1552 if(camera == NULL)
\r
1555 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1557 camera->setFOV(FOV_ANGLE);
\r
1559 // Just so big a value that everything rendered is visible
\r
1560 camera->setFarValue(100000*BS);
\r
1562 f32 camera_yaw = 0; // "right/left"
\r
1563 f32 camera_pitch = 0; // "up/down"
\r
1569 gui_loadingtext->remove();
\r
1572 Add some gui stuff
\r
1575 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1576 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1579 We need some kind of a root node to be able to add
\r
1580 custom elements directly on the screen.
\r
1581 Otherwise they won't be automatically drawn.
\r
1583 guiroot = guienv->addStaticText(L"",
\r
1584 core::rect<s32>(0, 0, 10000, 10000));
\r
1586 // Test the text input system
\r
1587 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1589 /*GUIMessageMenu *menu =
\r
1590 new GUIMessageMenu(guienv, guiroot, -1,
\r
1591 &g_active_menu_count,
\r
1595 // Launch pause menu
\r
1596 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1597 &g_active_menu_count))->drop();
\r
1599 // First line of debug text
\r
1600 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1602 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1604 // Second line of debug text
\r
1605 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1607 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1610 // At the middle of the screen
\r
1611 // Object infos are shown in this
\r
1612 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1614 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1618 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1619 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1620 core::rect<s32>(70, 60, 795, 150),
\r
1622 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1623 core::list<ChatLine> chat_lines;
\r
1626 Some statistics are collected in these
\r
1629 u32 beginscenetime = 0;
\r
1630 u32 scenetime = 0;
\r
1631 u32 endscenetime = 0;
\r
1634 //throw con::PeerNotFoundException("lol");
\r
1640 bool first_loop_after_window_activation = true;
\r
1642 // Time is in milliseconds
\r
1643 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1644 // NOTE: So we have to use getTime() and call run()s between them
\r
1645 u32 lasttime = device->getTimer()->getTime();
\r
1647 while(device->run())
\r
1650 Run global IrrlichtWrapper's main thread processing stuff
\r
1652 g_irrlicht->Run();
\r
1655 Random calculations
\r
1657 v2u32 screensize = driver->getScreenSize();
\r
1658 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1660 // Hilight boxes collected during the loop and displayed
\r
1661 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1664 std::wstring infotext;
\r
1666 //TimeTaker //timer1("//timer1");
\r
1668 // Time of frame without fps limit
\r
1672 // not using getRealTime is necessary for wine
\r
1673 u32 time = device->getTimer()->getTime();
\r
1674 if(time > lasttime)
\r
1675 busytime_u32 = time - lasttime;
\r
1678 busytime = busytime_u32 / 1000.0;
\r
1681 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1683 // Absolutelu necessary for wine!
\r
1690 updateViewingRange(busytime, &client);
\r
1697 float fps_max = g_settings.getFloat("fps_max");
\r
1698 u32 frametime_min = 1000./fps_max;
\r
1700 if(busytime_u32 < frametime_min)
\r
1702 u32 sleeptime = frametime_min - busytime_u32;
\r
1703 device->sleep(sleeptime);
\r
1707 // Absolutelu necessary for wine!
\r
1711 Time difference calculation
\r
1713 f32 dtime; // in seconds
\r
1715 u32 time = device->getTimer()->getTime();
\r
1716 if(time > lasttime)
\r
1717 dtime = (time - lasttime) / 1000.0;
\r
1723 Time average and jitter calculation
\r
1726 static f32 dtime_avg1 = 0.0;
\r
1727 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1728 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1730 static f32 dtime_jitter1_max_sample = 0.0;
\r
1731 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1733 static f32 jitter1_max = 0.0;
\r
1734 static f32 counter = 0.0;
\r
1735 if(dtime_jitter1 > jitter1_max)
\r
1736 jitter1_max = dtime_jitter1;
\r
1741 dtime_jitter1_max_sample = jitter1_max;
\r
1742 dtime_jitter1_max_fraction
\r
1743 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1744 jitter1_max = 0.0;
\r
1747 Control freetime ratio
\r
1749 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1751 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1752 g_freetime_ratio += 0.01;
\r
1756 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1757 g_freetime_ratio -= 0.01;
\r
1763 Busytime average and jitter calculation
\r
1766 static f32 busytime_avg1 = 0.0;
\r
1767 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1768 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1770 static f32 busytime_jitter1_max_sample = 0.0;
\r
1771 static f32 busytime_jitter1_min_sample = 0.0;
\r
1773 static f32 jitter1_max = 0.0;
\r
1774 static f32 jitter1_min = 0.0;
\r
1775 static f32 counter = 0.0;
\r
1776 if(busytime_jitter1 > jitter1_max)
\r
1777 jitter1_max = busytime_jitter1;
\r
1778 if(busytime_jitter1 < jitter1_min)
\r
1779 jitter1_min = busytime_jitter1;
\r
1781 if(counter > 0.0){
\r
1783 busytime_jitter1_max_sample = jitter1_max;
\r
1784 busytime_jitter1_min_sample = jitter1_min;
\r
1785 jitter1_max = 0.0;
\r
1786 jitter1_min = 0.0;
\r
1791 Debug info for client
\r
1794 static float counter = 0.0;
\r
1799 client.printDebugInfo(std::cout);
\r
1804 Input handler step()
\r
1806 g_input->step(dtime);
\r
1809 Player speed control
\r
1818 bool a_superspeed,
\r
1821 PlayerControl control(
\r
1822 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1823 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1824 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1825 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1826 g_input->isKeyDown(irr::KEY_SPACE),
\r
1827 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1831 client.setPlayerControl(control);
\r
1835 Process environment
\r
1839 //TimeTaker timer("client.step(dtime)");
\r
1840 client.step(dtime);
\r
1841 //client.step(dtime_avg1);
\r
1844 if(server != NULL)
\r
1846 //TimeTaker timer("server->step(dtime)");
\r
1847 server->step(dtime);
\r
1850 v3f player_position = client.getPlayerPosition();
\r
1852 //TimeTaker //timer2("//timer2");
\r
1855 Mouse and camera control
\r
1858 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1861 device->getCursorControl()->setVisible(false);
\r
1863 if(first_loop_after_window_activation){
\r
1864 //std::cout<<"window active, first loop"<<std::endl;
\r
1865 first_loop_after_window_activation = false;
\r
1868 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1869 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1870 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1871 camera_yaw -= dx*0.2;
\r
1872 camera_pitch += dy*0.2;
\r
1873 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1874 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1876 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1879 device->getCursorControl()->setVisible(true);
\r
1881 //std::cout<<"window inactive"<<std::endl;
\r
1882 first_loop_after_window_activation = true;
\r
1885 camera_yaw = wrapDegrees(camera_yaw);
\r
1886 camera_pitch = wrapDegrees(camera_pitch);
\r
1888 v3f camera_direction = v3f(0,0,1);
\r
1889 camera_direction.rotateYZBy(camera_pitch);
\r
1890 camera_direction.rotateXZBy(camera_yaw);
\r
1892 // This is at the height of the eyes of the current figure
\r
1893 v3f camera_position =
\r
1894 player_position + v3f(0, BS+BS/2, 0);
\r
1895 // This is more like in minecraft
\r
1896 /*v3f camera_position =
\r
1897 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1899 camera->setPosition(camera_position);
\r
1900 // *100.0 helps in large map coordinates
\r
1901 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1903 if(FIELD_OF_VIEW_TEST){
\r
1904 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1905 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1908 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1909 //TimeTaker timer("client.updateCamera");
\r
1910 client.updateCamera(camera_position, camera_direction);
\r
1914 //TimeTaker //timer3("//timer3");
\r
1917 Calculate what block is the crosshair pointing to
\r
1920 //u32 t1 = device->getTimer()->getRealTime();
\r
1922 //f32 d = 4; // max. distance
\r
1923 f32 d = 4; // max. distance
\r
1924 core::line3d<f32> shootline(camera_position,
\r
1925 camera_position + camera_direction * BS * (d+1));
\r
1927 MapBlockObject *selected_object = client.getSelectedObject
\r
1928 (d*BS, camera_position, shootline);
\r
1931 If it's pointing to a MapBlockObject
\r
1934 if(selected_object != NULL)
\r
1936 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1938 core::aabbox3d<f32> box_on_map
\r
1939 = selected_object->getSelectionBoxOnMap();
\r
1941 hilightboxes.push_back(box_on_map);
\r
1943 infotext = narrow_to_wide(selected_object->infoText());
\r
1945 if(g_input->getLeftClicked())
\r
1947 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1948 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1949 selected_object->getId(), g_selected_item);
\r
1951 else if(g_input->getRightClicked())
\r
1953 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1955 Check if we want to modify the object ourselves
\r
1957 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1959 dstream<<"Sign object right-clicked"<<std::endl;
\r
1961 if(random_input == false)
\r
1963 // Get a new text for it
\r
1965 TextDest *dest = new TextDestSign(
\r
1966 selected_object->getBlock()->getPos(),
\r
1967 selected_object->getId(),
\r
1970 SignObject *sign_object = (SignObject*)selected_object;
\r
1972 std::wstring wtext =
\r
1973 narrow_to_wide(sign_object->getText());
\r
1975 (new GUITextInputMenu(guienv, guiroot, -1,
\r
1976 &g_active_menu_count, dest,
\r
1981 Otherwise pass the event to the server as-is
\r
1985 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1986 selected_object->getId(), g_selected_item);
\r
1990 else // selected_object == NULL
\r
1994 Find out which node we are pointing at
\r
1997 bool nodefound = false;
\r
1999 v3s16 neighbourpos;
\r
2000 core::aabbox3d<f32> nodefacebox;
\r
2001 f32 mindistance = BS * 1001;
\r
2003 v3s16 pos_i = floatToInt(player_position);
\r
2005 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2009 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2010 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2011 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2012 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2013 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2014 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2016 for(s16 y = ystart; y <= yend; y++)
\r
2017 for(s16 z = zstart; z <= zend; z++)
\r
2018 for(s16 x = xstart; x <= xend; x++)
\r
2023 n = client.getNode(v3s16(x,y,z));
\r
2024 if(content_pointable(n.d) == false)
\r
2027 catch(InvalidPositionException &e)
\r
2033 v3f npf = intToFloat(np);
\r
2038 v3s16(0,0,1), // back
\r
2039 v3s16(0,1,0), // top
\r
2040 v3s16(1,0,0), // right
\r
2041 v3s16(0,0,-1), // front
\r
2042 v3s16(0,-1,0), // bottom
\r
2043 v3s16(-1,0,0), // left
\r
2049 if(n.d == CONTENT_TORCH)
\r
2051 v3s16 dir = unpackDir(n.dir);
\r
2052 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2053 dir_f *= BS/2 - BS/6 - BS/20;
\r
2054 v3f cpf = npf + dir_f;
\r
2055 f32 distance = (cpf - camera_position).getLength();
\r
2057 core::aabbox3d<f32> box;
\r
2060 if(dir == v3s16(0,-1,0))
\r
2062 box = core::aabbox3d<f32>(
\r
2063 npf - v3f(BS/6, BS/2, BS/6),
\r
2064 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2068 else if(dir == v3s16(0,1,0))
\r
2070 box = core::aabbox3d<f32>(
\r
2071 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2072 npf + v3f(BS/6, BS/2, BS/6)
\r
2078 box = core::aabbox3d<f32>(
\r
2079 cpf - v3f(BS/6, BS/3, BS/6),
\r
2080 cpf + v3f(BS/6, BS/3, BS/6)
\r
2084 if(distance < mindistance)
\r
2086 if(box.intersectsWithLine(shootline))
\r
2090 neighbourpos = np;
\r
2091 mindistance = distance;
\r
2092 nodefacebox = box;
\r
2101 for(u16 i=0; i<6; i++)
\r
2103 v3f dir_f = v3f(dirs[i].X,
\r
2104 dirs[i].Y, dirs[i].Z);
\r
2105 v3f centerpoint = npf + dir_f * BS/2;
\r
2107 (centerpoint - camera_position).getLength();
\r
2109 if(distance < mindistance)
\r
2111 core::CMatrix4<f32> m;
\r
2112 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2114 // This is the back face
\r
2115 v3f corners[2] = {
\r
2116 v3f(BS/2, BS/2, BS/2),
\r
2117 v3f(-BS/2, -BS/2, BS/2+d)
\r
2120 for(u16 j=0; j<2; j++)
\r
2122 m.rotateVect(corners[j]);
\r
2123 corners[j] += npf;
\r
2126 core::aabbox3d<f32> facebox(corners[0]);
\r
2127 facebox.addInternalPoint(corners[1]);
\r
2129 if(facebox.intersectsWithLine(shootline))
\r
2133 neighbourpos = np + dirs[i];
\r
2134 mindistance = distance;
\r
2135 nodefacebox = facebox;
\r
2137 } // if distance < mindistance
\r
2139 } // regular block
\r
2142 static float nodig_delay_counter = 0.0;
\r
2146 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2148 static float dig_time = 0.0;
\r
2149 static u16 dig_index = 0;
\r
2151 hilightboxes.push_back(nodefacebox);
\r
2153 if(g_input->getLeftReleased())
\r
2155 client.clearTempMod(nodepos);
\r
2159 if(nodig_delay_counter > 0.0)
\r
2161 nodig_delay_counter -= dtime;
\r
2165 if(nodepos != nodepos_old)
\r
2167 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2168 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2170 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2172 client.clearTempMod(nodepos_old);
\r
2177 if(g_input->getLeftClicked() ||
\r
2178 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2180 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2181 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2183 if(g_input->getLeftClicked())
\r
2185 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2187 if(g_input->getLeftState())
\r
2189 MapNode n = client.getNode(nodepos);
\r
2191 // Get tool name. Default is "" = bare hands
\r
2192 std::string toolname = "";
\r
2193 InventoryList *mlist = local_inventory.getList("main");
\r
2196 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2197 if(item && (std::string)item->getName() == "ToolItem")
\r
2199 ToolItem *titem = (ToolItem*)item;
\r
2200 toolname = titem->getToolName();
\r
2204 // Get digging properties for material and tool
\r
2205 u8 material = n.d;
\r
2206 DiggingProperties prop =
\r
2207 getDiggingProperties(material, toolname);
\r
2209 float dig_time_complete = 0.0;
\r
2211 if(prop.diggable == false)
\r
2213 /*dstream<<"Material "<<(int)material
\r
2214 <<" not diggable with \""
\r
2215 <<toolname<<"\""<<std::endl;*/
\r
2216 // I guess nobody will wait for this long
\r
2217 dig_time_complete = 10000000.0;
\r
2221 dig_time_complete = prop.time;
\r
2224 if(dig_time_complete >= 0.001)
\r
2226 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2227 * dig_time/dig_time_complete);
\r
2229 // This is for torches
\r
2232 dig_index = CRACK_ANIMATION_LENGTH;
\r
2235 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2237 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2238 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2242 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2243 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2244 client.clearTempMod(nodepos);
\r
2245 client.removeNode(nodepos);
\r
2249 nodig_delay_counter = dig_time_complete
\r
2250 / (float)CRACK_ANIMATION_LENGTH;
\r
2252 // We don't want a corresponding delay to
\r
2253 // very time consuming nodes
\r
2254 if(nodig_delay_counter > 0.5)
\r
2256 nodig_delay_counter = 0.5;
\r
2258 // We want a slight delay to very little
\r
2259 // time consuming nodes
\r
2260 //float mindelay = 0.15;
\r
2261 float mindelay = 0.20;
\r
2262 if(nodig_delay_counter < mindelay)
\r
2264 nodig_delay_counter = mindelay;
\r
2268 dig_time += dtime;
\r
2272 if(g_input->getRightClicked())
\r
2274 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2275 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2278 nodepos_old = nodepos;
\r
2283 } // selected_object == NULL
\r
2285 g_input->resetLeftClicked();
\r
2286 g_input->resetRightClicked();
\r
2288 if(g_input->getLeftReleased())
\r
2290 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2292 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2294 if(g_input->getRightReleased())
\r
2296 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2300 g_input->resetLeftReleased();
\r
2301 g_input->resetRightReleased();
\r
2304 Calculate stuff for drawing
\r
2307 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2309 u32 daynight_ratio = client.getDayNightRatio();
\r
2310 /*video::SColor bgcolor = video::SColor(
\r
2312 skycolor.getRed() * daynight_ratio / 1000,
\r
2313 skycolor.getGreen() * daynight_ratio / 1000,
\r
2314 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2316 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2317 video::SColor bgcolor = video::SColor(
\r
2319 skycolor.getRed() * l / 255,
\r
2320 skycolor.getGreen() * l / 255,
\r
2321 skycolor.getBlue() * l / 255);
\r
2327 if(g_settings.getBool("enable_fog") == true)
\r
2329 f32 range = draw_control.wanted_range * BS;
\r
2330 if(draw_control.range_all)
\r
2331 range = 100000*BS;
\r
2335 video::EFT_FOG_LINEAR,
\r
2339 false, // pixel fog
\r
2340 false // range fog
\r
2346 Update gui stuff (0ms)
\r
2349 //TimeTaker guiupdatetimer("Gui updating");
\r
2352 wchar_t temptext[150];
\r
2354 static float drawtime_avg = 0;
\r
2355 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2356 static float beginscenetime_avg = 0;
\r
2357 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2358 static float scenetime_avg = 0;
\r
2359 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2360 static float endscenetime_avg = 0;
\r
2361 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2363 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2365 L", R: range_all=%i"
\r
2367 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2369 draw_control.range_all,
\r
2371 beginscenetime_avg,
\r
2376 guitext->setText(temptext);
\r
2380 wchar_t temptext[150];
\r
2381 swprintf(temptext, 150,
\r
2382 L"(% .1f, % .1f, % .1f)"
\r
2383 L" (% .3f < btime_jitter < % .3f"
\r
2384 L", dtime_jitter = % .1f %%"
\r
2385 L", v_range = %.1f)",
\r
2386 player_position.X/BS,
\r
2387 player_position.Y/BS,
\r
2388 player_position.Z/BS,
\r
2389 busytime_jitter1_min_sample,
\r
2390 busytime_jitter1_max_sample,
\r
2391 dtime_jitter1_max_fraction * 100.0,
\r
2392 draw_control.wanted_range
\r
2395 guitext2->setText(temptext);
\r
2399 guitext_info->setText(infotext.c_str());
\r
2403 Get chat messages from client
\r
2406 // Get new messages
\r
2407 std::wstring message;
\r
2408 while(client.getChatMessage(message))
\r
2410 chat_lines.push_back(ChatLine(message));
\r
2411 /*if(chat_lines.size() > 6)
\r
2413 core::list<ChatLine>::Iterator
\r
2414 i = chat_lines.begin();
\r
2415 chat_lines.erase(i);
\r
2418 // Append them to form the whole static text and throw
\r
2419 // it to the gui element
\r
2420 std::wstring whole;
\r
2421 // This will correspond to the line number counted from
\r
2422 // top to bottom, from size-1 to 0
\r
2423 s16 line_number = chat_lines.size();
\r
2424 // Count of messages to be removed from the top
\r
2425 u16 to_be_removed_count = 0;
\r
2426 for(core::list<ChatLine>::Iterator
\r
2427 i = chat_lines.begin();
\r
2428 i != chat_lines.end(); i++)
\r
2430 // After this, line number is valid for this loop
\r
2433 (*i).age += dtime;
\r
2435 This results in a maximum age of 60*6 to the
\r
2436 lowermost line and a maximum of 6 lines
\r
2438 float allowed_age = (6-line_number) * 60.0;
\r
2440 if((*i).age > allowed_age)
\r
2442 to_be_removed_count++;
\r
2445 whole += (*i).text + L'\n';
\r
2447 for(u16 i=0; i<to_be_removed_count; i++)
\r
2449 core::list<ChatLine>::Iterator
\r
2450 it = chat_lines.begin();
\r
2451 chat_lines.erase(it);
\r
2453 chat_guitext->setText(whole.c_str());
\r
2454 // Update gui element size and position
\r
2455 core::rect<s32> rect(
\r
2457 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2458 screensize.X - 10,
\r
2461 chat_guitext->setRelativePosition(rect);
\r
2463 if(chat_lines.size() == 0)
\r
2464 chat_guitext->setVisible(false);
\r
2466 chat_guitext->setVisible(true);
\r
2473 static u16 old_selected_item = 65535;
\r
2474 if(client.getLocalInventoryUpdated()
\r
2475 || g_selected_item != old_selected_item)
\r
2477 old_selected_item = g_selected_item;
\r
2478 //std::cout<<"Updating local inventory"<<std::endl;
\r
2479 client.getLocalInventory(local_inventory);
\r
2480 quick_inventory->setSelection(g_selected_item);
\r
2481 quick_inventory->update();
\r
2485 Send actions returned by the inventory menu
\r
2487 while(inventory_action_queue.size() != 0)
\r
2489 InventoryAction *a = inventory_action_queue.pop_front();
\r
2491 client.sendInventoryAction(a);
\r
2500 TimeTaker drawtimer("Drawing");
\r
2504 TimeTaker timer("beginScene");
\r
2505 driver->beginScene(true, true, bgcolor);
\r
2506 //driver->beginScene(false, true, bgcolor);
\r
2507 beginscenetime = timer.stop(true);
\r
2512 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2515 TimeTaker timer("smgr");
\r
2517 scenetime = timer.stop(true);
\r
2521 //TimeTaker timer9("auxiliary drawings");
\r
2525 //TimeTaker //timer10("//timer10");
\r
2527 video::SMaterial m;
\r
2529 m.Lighting = false;
\r
2530 driver->setMaterial(m);
\r
2532 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2534 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2535 i != hilightboxes.end(); i++)
\r
2537 /*std::cout<<"hilightbox min="
\r
2538 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2540 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2542 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2548 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2549 displaycenter + core::vector2d<s32>(10,0),
\r
2550 video::SColor(255,255,255,255));
\r
2551 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2552 displaycenter + core::vector2d<s32>(0,10),
\r
2553 video::SColor(255,255,255,255));
\r
2558 //TimeTaker //timer11("//timer11");
\r
2564 guienv->drawAll();
\r
2568 TimeTaker timer("endScene");
\r
2569 driver->endScene();
\r
2570 endscenetime = timer.stop(true);
\r
2573 drawtime = drawtimer.stop(true);
\r
2579 static s16 lastFPS = 0;
\r
2580 //u16 fps = driver->getFPS();
\r
2581 u16 fps = (1.0/dtime_avg1);
\r
2583 if (lastFPS != fps)
\r
2585 core::stringw str = L"Minetest [";
\r
2586 str += driver->getName();
\r
2590 device->setWindowCaption(str.c_str());
\r
2596 device->yield();*/
\r
2599 delete quick_inventory;
\r
2601 } // client is deleted at this point
\r
2606 In the end, delete the Irrlicht device.
\r
2611 Update configuration file
\r
2613 /*if(configpath != "")
\r
2615 g_settings.updateConfigFile(configpath.c_str());
\r
2619 catch(con::PeerNotFoundException &e)
\r
2621 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2625 GUIMessageMenu *menu =
\r
2626 new GUIMessageMenu(guienv, guiroot, -1,
\r
2627 &g_active_menu_count,
\r
2628 L"Connection timed out");
\r
2630 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2632 dstream<<"Created menu"<<std::endl;
\r
2634 while(g_device->run() && menu->getStatus() == false)
\r
2636 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2637 guienv->drawAll();
\r
2638 driver->endScene();
\r
2641 dstream<<"Dropping menu"<<std::endl;
\r
2647 END_DEBUG_EXCEPTION_HANDLER
\r
2649 debugstreams_deinit();
\r