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
26 - It is not a memory leak but some kind of a buffer.
\r
28 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
29 NOTE: Global locale is now set at initialization
\r
31 SUGG: Fix address to be ipv6 compatible
\r
33 NOTE: When a new sector is generated, it may change the ground level
\r
34 of it's and it's neighbors border that two blocks that are
\r
35 above and below each other and that are generated before and
\r
36 after the sector heightmap generation (order doesn't matter),
\r
37 can have a small gap between each other at the border.
\r
38 SUGG: Use same technique for sector heightmaps as what we're
\r
39 using for UnlimitedHeightmap? (getting all neighbors
\r
42 SUGG: Transfer more blocks in a single packet
\r
43 SUGG: A blockdata combiner class, to which blocks are added and at
\r
44 destruction it sends all the stuff in as few packets as possible.
\r
46 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
47 SUGG: Fetch stuff mainly from the viewing direction
\r
49 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
50 - This enables saving many packets and making a faster connection
\r
51 - This also enables server to check if client has received the
\r
52 most recent block sent, for example.
\r
53 SUGG: Add a sane bandwidth throttling system to Connection
\r
55 SUGG: More fine-grained control of client's dumping of blocks from
\r
57 - ...What does this mean in the first place?
\r
59 SUGG: A map editing mode (similar to dedicated server mode)
\r
61 SUGG: Add a time value to the param of footstepped grass and check it
\r
62 against a global timer when a block is accessed, to make old
\r
65 SUGG: Make a copy of close-range environment on client for showing
\r
66 on screen, with minimal mutexes to slow down the main loop
\r
68 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
69 it by sending more stuff in a single packet.
\r
70 - Add a packet queue to RemoteClient, from which packets will be
\r
71 combined with object data packets
\r
72 - This is not exactly trivial: the object data packets are
\r
73 sometimes very big by themselves
\r
75 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
76 - This will allow saving ages of rats on disk but not sending
\r
79 SUGG: Implement lighting using VoxelManipulator
\r
80 - Would it be significantly faster?
\r
82 SUGG: MovingObject::move and Player::move are basically the same.
\r
85 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
86 if something is already in it)
\r
87 - Use it in active block queue in water flowing
\r
89 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
90 - This is not doable because it is currently hand-made and not
\r
91 based on some mathematical function.
\r
93 SUGG: A version number to blocks, which increments when the block is
\r
94 modified (node add/remove, water update, lighting update)
\r
95 - This can then be used to make sure the most recent version of
\r
96 a block has been sent to client
\r
98 SUGG: Make the amount of blocks sending to client and the total
\r
99 amount of blocks dynamically limited. Transferring blocks is the
\r
100 main network eater of this system, so it is the one that has
\r
101 to be throttled so that RTTs stay low.
\r
103 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
104 different directions and then only those drawn that need to be
\r
105 - Also an 1-dimensional tile map would be nice probably
\r
109 TODO: Get rid of GotSplitPacketException
\r
113 TODO: Add gui option to remove map
\r
115 TODO: Startup and configuration menu
\r
119 TODO: Optimize day/night mesh updating somehow
\r
120 - create copies of all textures for all lighting values and only
\r
121 change texture for material?
\r
122 - Umm... the collecting of the faces is the slow part
\r
123 -> what about just changing the color values of the existing
\r
124 meshbuffers? It should go quite fast.
\r
126 TODO: Draw big amounts of torches better (that is, throw them in the
\r
127 same meshbuffer (can the meshcollector class be used?))
\r
129 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
131 - That is >500 vertices
\r
133 TODO: Make fetching sector's blocks more efficient when rendering
\r
134 sectors that have very large amounts of blocks (on client)
\r
138 TODO: Make the video backend selectable
\r
142 TODO: Untie client network operations from framerate
\r
143 - Needs some input queues or something
\r
144 - Not really necessary?
\r
148 TODO: When player dies, throw items on map
\r
150 TODO: Make an option to the server to disable building and digging near
\r
151 the starting position
\r
153 TODO: Players to only be hidden when the client quits.
\r
154 TODO: - Players to be saved on disk, with inventory
\r
155 TODO: Players to be saved as text in map/players/<name>
\r
156 TODO: Player inventory to be saved on disk
\r
158 TODO: Proper handling of spawning place (try to find something that
\r
159 is not in the middle of an ocean (some land to stand on at
\r
160 least) and save it in map config.
\r
162 TODO: Copy the text of the last picked sign to inventory in creative
\r
165 TODO: Check what goes wrong with caching map to disk (Kray)
\r
168 TODO: When server sees that client is removing an inexistent block or
\r
169 adding a block to an existent position, resend the MapBlock.
\r
171 TODO: Generate map from the area the client is looking at
\r
175 TODO: Better handling of objects and mobs
\r
177 - There has to be some way to do it with less spaghetti code
\r
178 - Make separate classes for client and server
\r
179 - Client should not discriminate between blocks, server should
\r
180 - Make other players utilize the same framework
\r
181 - This is also needed for objects that don't get sent to client
\r
182 but are used for triggers etc
\r
184 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
185 need an additional metadata field for the texts
\r
186 - This is also needed for item container chests
\r
187 TODO: There has to be some better way to handle static objects than to
\r
188 send them all the time. This affects signs and item objects.
\r
190 Block object server side:
\r
191 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
192 - For all blocks in the buffer, objects are stepped(). This
\r
193 means they are active.
\r
194 - TODO: A global active buffer is needed for the server
\r
195 - TODO: A timestamp to blocks
\r
196 - TODO: All blocks going in and out of the buffer are recorded.
\r
197 - TODO: For outgoing blocks, timestamp is written.
\r
198 - TODO: For incoming blocks, time difference is calculated and
\r
199 objects are stepped according to it.
\r
203 TODO: There are some lighting-related todos and fixmes in
\r
204 ServerMap::emergeBlock
\r
206 TODO: When generating a block, check that there is no sunlight
\r
207 below the block if the bottom of the block doesn't have
\r
208 sunlight. If it has, add it to the invalid lighting list.
\r
210 TODO: Map generator version 2
\r
211 - Create surface areas based on central points; a given point's
\r
212 area type is given by the nearest central point
\r
213 - Separate points for heightmap, caves, plants and minerals?
\r
214 - Flat land, mountains, forest, jungle
\r
218 ======================================================================
\r
220 ======================================================================
\r
225 Setting this to 1 enables a special camera mode that forces
\r
226 the renderers to think that the camera statically points from
\r
227 the starting place to a static direction.
\r
229 This allows one to move around with the player and see what
\r
230 is actually drawn behind solid things and behind the player.
\r
232 #define FIELD_OF_VIEW_TEST 0
\r
236 #pragma message ("Disabling unit tests")
\r
238 #warning "Disabling unit tests"
\r
240 // Disable unit tests
\r
241 #define ENABLE_TESTS 0
\r
243 // Enable unit tests
\r
244 #define ENABLE_TESTS 1
\r
248 #pragma comment(lib, "Irrlicht.lib")
\r
249 #pragma comment(lib, "jthread.lib")
\r
250 #pragma comment(lib, "zlibwapi.lib")
\r
251 // This would get rid of the console window
\r
252 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
255 #include <iostream>
\r
257 #include <jmutexautolock.h>
\r
258 #include <locale.h>
\r
259 #include "common_irrlicht.h"
\r
262 #include "player.h"
\r
265 #include "environment.h"
\r
266 #include "server.h"
\r
267 #include "client.h"
\r
268 #include "serialization.h"
\r
269 #include "constants.h"
\r
270 #include "strfnd.h"
\r
271 #include "porting.h"
\r
272 #include "irrlichtwrapper.h"
\r
273 #include "gettime.h"
\r
274 #include "porting.h"
\r
275 #include "guiPauseMenu.h"
\r
276 #include "guiInventoryMenu.h"
\r
277 #include "guiTextInputMenu.h"
\r
278 #include "materials.h"
\r
279 #include "guiMessageMenu.h"
\r
280 #include "filesys.h"
\r
281 #include "config.h"
\r
283 IrrlichtWrapper *g_irrlicht;
\r
285 MapDrawControl draw_control;
\r
289 These are loaded from the config file.
\r
292 Settings g_settings;
\r
294 extern void set_default_settings();
\r
300 IrrlichtDevice *g_device = NULL;
\r
301 Client *g_client = NULL;
\r
306 gui::IGUIEnvironment* guienv = NULL;
\r
307 gui::IGUIStaticText *guiroot = NULL;
\r
308 int g_active_menu_count = 0;
\r
310 bool noMenuActive()
\r
312 return (g_active_menu_count == 0);
\r
315 // Inventory actions from the menu are buffered here before sending
\r
316 Queue<InventoryAction*> inventory_action_queue;
\r
317 // This is a copy of the inventory that the client's environment has
\r
318 Inventory local_inventory;
\r
320 u16 g_selected_item = 0;
\r
327 std::ostream *dout_con_ptr = &dummyout;
\r
328 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
329 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
330 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
331 //std::ostream *dout_con_ptr = &dstream;
\r
332 //std::ostream *derr_con_ptr = &dstream;
\r
335 std::ostream *dout_server_ptr = &dstream;
\r
336 std::ostream *derr_server_ptr = &dstream;
\r
339 std::ostream *dout_client_ptr = &dstream;
\r
340 std::ostream *derr_client_ptr = &dstream;
\r
343 gettime.h implementation
\r
349 Use irrlicht because it is more precise than porting.h's
\r
352 if(g_irrlicht == NULL)
\r
354 return g_irrlicht->getTime();
\r
361 struct TextDestSign : public TextDest
\r
363 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
365 m_blockpos = blockpos;
\r
369 void gotText(std::wstring text)
\r
371 std::string ntext = wide_to_narrow(text);
\r
372 dstream<<"Changing text of a sign object: "
\r
373 <<ntext<<std::endl;
\r
374 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
382 struct TextDestChat : public TextDest
\r
384 TextDestChat(Client *client)
\r
388 void gotText(std::wstring text)
\r
390 m_client->sendChatMessage(text);
\r
391 m_client->addChatMessage(text);
\r
397 class MyEventReceiver : public IEventReceiver
\r
400 // This is the one method that we have to implement
\r
401 virtual bool OnEvent(const SEvent& event)
\r
404 React to nothing here if a menu is active
\r
406 if(noMenuActive() == false)
\r
412 // Remember whether each key is down or up
\r
413 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
415 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
417 if(event.KeyInput.PressedDown)
\r
419 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
425 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
427 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
429 dstream<<DTIME<<"MyEventReceiver: "
\r
430 <<"Launching pause menu"<<std::endl;
\r
431 // It will delete itself by itself
\r
432 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
433 &g_active_menu_count))->drop();
\r
436 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
438 dstream<<DTIME<<"MyEventReceiver: "
\r
439 <<"Launching inventory"<<std::endl;
\r
440 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
441 &local_inventory, &inventory_action_queue,
\r
442 &g_active_menu_count))->drop();
\r
445 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
447 TextDest *dest = new TextDestChat(g_client);
\r
449 (new GUITextInputMenu(guienv, guiroot, -1,
\r
450 &g_active_menu_count, dest,
\r
455 // Material selection
\r
456 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
458 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
461 g_selected_item = 0;
\r
462 dstream<<DTIME<<"Selected item: "
\r
463 <<g_selected_item<<std::endl;
\r
466 // Viewing range selection
\r
467 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
469 if(draw_control.range_all)
\r
471 draw_control.range_all = false;
\r
472 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
476 draw_control.range_all = true;
\r
477 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
481 // Print debug stacks
\r
482 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
484 dstream<<"-----------------------------------------"
\r
486 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
487 dstream<<"-----------------------------------------"
\r
489 debug_stacks_print();
\r
494 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
496 if(noMenuActive() == false)
\r
498 left_active = false;
\r
499 middle_active = false;
\r
500 right_active = false;
\r
504 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
505 left_active = event.MouseInput.isLeftPressed();
\r
506 middle_active = event.MouseInput.isMiddlePressed();
\r
507 right_active = event.MouseInput.isRightPressed();
\r
509 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
511 leftclicked = true;
\r
513 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
515 rightclicked = true;
\r
517 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
519 leftreleased = true;
\r
521 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
523 rightreleased = true;
\r
525 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
527 /*dstream<<"event.MouseInput.Wheel="
\r
528 <<event.MouseInput.Wheel<<std::endl;*/
\r
529 if(event.MouseInput.Wheel < 0)
\r
531 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
534 g_selected_item = 0;
\r
536 else if(event.MouseInput.Wheel > 0)
\r
538 if(g_selected_item > 0)
\r
541 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
550 // This is used to check whether a key is being held down
\r
551 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
553 return keyIsDown[keyCode];
\r
558 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
559 keyIsDown[i] = false;
\r
561 leftclicked = false;
\r
562 rightclicked = false;
\r
563 leftreleased = false;
\r
564 rightreleased = false;
\r
566 left_active = false;
\r
567 middle_active = false;
\r
568 right_active = false;
\r
579 bool rightreleased;
\r
582 bool middle_active;
\r
586 // We use this array to store the current state of each key
\r
587 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
590 IrrlichtDevice *m_device;
\r
599 virtual ~InputHandler()
\r
603 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
605 virtual v2s32 getMousePos() = 0;
\r
606 virtual void setMousePos(s32 x, s32 y) = 0;
\r
608 virtual bool getLeftState() = 0;
\r
609 virtual bool getRightState() = 0;
\r
611 virtual bool getLeftClicked() = 0;
\r
612 virtual bool getRightClicked() = 0;
\r
613 virtual void resetLeftClicked() = 0;
\r
614 virtual void resetRightClicked() = 0;
\r
616 virtual bool getLeftReleased() = 0;
\r
617 virtual bool getRightReleased() = 0;
\r
618 virtual void resetLeftReleased() = 0;
\r
619 virtual void resetRightReleased() = 0;
\r
621 virtual void step(float dtime) {};
\r
623 virtual void clear() {};
\r
626 InputHandler *g_input = NULL;
\r
628 class RealInputHandler : public InputHandler
\r
631 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
633 m_receiver(receiver)
\r
636 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
638 return m_receiver->IsKeyDown(keyCode);
\r
640 virtual v2s32 getMousePos()
\r
642 return m_device->getCursorControl()->getPosition();
\r
644 virtual void setMousePos(s32 x, s32 y)
\r
646 m_device->getCursorControl()->setPosition(x, y);
\r
649 virtual bool getLeftState()
\r
651 return m_receiver->left_active;
\r
653 virtual bool getRightState()
\r
655 return m_receiver->right_active;
\r
658 virtual bool getLeftClicked()
\r
660 return m_receiver->leftclicked;
\r
662 virtual bool getRightClicked()
\r
664 return m_receiver->rightclicked;
\r
666 virtual void resetLeftClicked()
\r
668 m_receiver->leftclicked = false;
\r
670 virtual void resetRightClicked()
\r
672 m_receiver->rightclicked = false;
\r
675 virtual bool getLeftReleased()
\r
677 return m_receiver->leftreleased;
\r
679 virtual bool getRightReleased()
\r
681 return m_receiver->rightreleased;
\r
683 virtual void resetLeftReleased()
\r
685 m_receiver->leftreleased = false;
\r
687 virtual void resetRightReleased()
\r
689 m_receiver->rightreleased = false;
\r
694 resetRightClicked();
\r
695 resetLeftClicked();
\r
698 IrrlichtDevice *m_device;
\r
699 MyEventReceiver *m_receiver;
\r
702 class RandomInputHandler : public InputHandler
\r
705 RandomInputHandler()
\r
707 leftclicked = false;
\r
708 rightclicked = false;
\r
709 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
710 keydown[i] = false;
\r
712 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
714 return keydown[keyCode];
\r
716 virtual v2s32 getMousePos()
\r
720 virtual void setMousePos(s32 x, s32 y)
\r
722 mousepos = v2s32(x,y);
\r
725 virtual bool getLeftState()
\r
729 virtual bool getRightState()
\r
734 virtual bool getLeftClicked()
\r
736 return leftclicked;
\r
738 virtual bool getRightClicked()
\r
740 return rightclicked;
\r
742 virtual void resetLeftClicked()
\r
744 leftclicked = false;
\r
746 virtual void resetRightClicked()
\r
748 rightclicked = false;
\r
751 virtual bool getLeftReleased()
\r
755 virtual bool getRightReleased()
\r
759 virtual void resetLeftReleased()
\r
762 virtual void resetRightReleased()
\r
766 virtual void step(float dtime)
\r
769 static float counter1 = 0;
\r
773 counter1 = 0.1*Rand(1,10);
\r
774 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
775 g_selected_material++;
\r
777 g_selected_material = 0;*/
\r
778 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
781 g_selected_item = 0;
\r
785 static float counter1 = 0;
\r
789 counter1 = 0.1*Rand(1, 40);
\r
790 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
794 static float counter1 = 0;
\r
798 counter1 = 0.1*Rand(1, 40);
\r
799 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
803 static float counter1 = 0;
\r
807 counter1 = 0.1*Rand(1, 40);
\r
808 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
812 static float counter1 = 0;
\r
816 counter1 = 0.1*Rand(1, 40);
\r
817 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
821 static float counter1 = 0;
\r
825 counter1 = 0.1*Rand(1, 20);
\r
826 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
830 static float counter1 = 0;
\r
834 counter1 = 0.1*Rand(1, 30);
\r
835 leftclicked = true;
\r
839 static float counter1 = 0;
\r
843 counter1 = 0.1*Rand(1, 20);
\r
844 rightclicked = true;
\r
847 mousepos += mousespeed;
\r
850 s32 Rand(s32 min, s32 max)
\r
852 return (myrand()%(max-min+1))+min;
\r
855 bool keydown[KEY_KEY_CODES_COUNT];
\r
862 void updateViewingRange(f32 frametime_in, Client *client)
\r
864 if(draw_control.range_all == true)
\r
867 static f32 added_frametime = 0;
\r
868 static s16 added_frames = 0;
\r
870 added_frametime += frametime_in;
\r
873 // Actually this counter kind of sucks because frametime is busytime
\r
874 static f32 counter = 0;
\r
875 counter -= frametime_in;
\r
881 /*dstream<<__FUNCTION_NAME
\r
882 <<": Collected "<<added_frames<<" frames, total of "
\r
883 <<added_frametime<<"s."<<std::endl;*/
\r
885 /*dstream<<"draw_control.blocks_drawn="
\r
886 <<draw_control.blocks_drawn
\r
887 <<", draw_control.blocks_would_have_drawn="
\r
888 <<draw_control.blocks_would_have_drawn
\r
891 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
892 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
894 draw_control.wanted_min_range = range_min;
\r
895 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
897 float block_draw_ratio = 1.0;
\r
898 if(draw_control.blocks_would_have_drawn != 0)
\r
900 block_draw_ratio = (float)draw_control.blocks_drawn
\r
901 / (float)draw_control.blocks_would_have_drawn;
\r
904 // Calculate the average frametime in the case that all wanted
\r
905 // blocks had been drawn
\r
906 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
908 added_frametime = 0.0;
\r
911 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
912 float wanted_frametime = 1.0 / wanted_fps;
\r
914 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
915 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
917 // If needed frametime change is very small, just return
\r
918 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
920 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
924 float range = draw_control.wanted_range;
\r
925 float new_range = range;
\r
927 static s16 range_old = 0;
\r
928 static f32 frametime_old = 0;
\r
930 float d_range = range - range_old;
\r
931 f32 d_frametime = frametime - frametime_old;
\r
932 // A sane default of 30ms per 50 nodes of range
\r
933 static f32 time_per_range = 30. / 50;
\r
936 time_per_range = d_frametime / d_range;
\r
939 // The minimum allowed calculated frametime-range derivative:
\r
940 // Practically this sets the maximum speed of changing the range.
\r
941 // The lower this value, the higher the maximum changing speed.
\r
942 // A low value here results in wobbly range (0.001)
\r
943 // A high value here results in slow changing range (0.0025)
\r
944 // SUGG: This could be dynamically adjusted so that when
\r
945 // the camera is turning, this is lower
\r
946 //float min_time_per_range = 0.0015;
\r
947 float min_time_per_range = 0.0010;
\r
948 //float min_time_per_range = 0.05 / range;
\r
949 if(time_per_range < min_time_per_range)
\r
951 time_per_range = min_time_per_range;
\r
952 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
956 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
959 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
960 // Dampen the change a bit to kill oscillations
\r
961 //wanted_range_change *= 0.9;
\r
962 //wanted_range_change *= 0.75;
\r
963 wanted_range_change *= 0.5;
\r
964 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
966 // If needed range change is very small, just return
\r
967 if(fabs(wanted_range_change) < 0.001)
\r
969 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
973 new_range += wanted_range_change;
\r
974 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
976 //float new_range_unclamped = new_range;
\r
977 if(new_range < range_min)
\r
978 new_range = range_min;
\r
979 if(new_range > range_max)
\r
980 new_range = range_max;
\r
982 /*if(new_range != new_range_unclamped)
\r
983 dstream<<", clamped to "<<new_range<<std::endl;
\r
985 dstream<<std::endl;*/
\r
987 draw_control.wanted_range = new_range;
\r
989 range_old = new_range;
\r
990 frametime_old = frametime;
\r
993 class GUIQuickInventory : public IEventReceiver
\r
997 gui::IGUIEnvironment* env,
\r
998 gui::IGUIElement* parent,
\r
1001 Inventory *inventory):
\r
1002 m_itemcount(itemcount),
\r
1003 m_inventory(inventory)
\r
1005 core::rect<s32> imgsize(0,0,48,48);
\r
1006 core::rect<s32> textsize(0,0,48,16);
\r
1007 v2s32 spacing(0, 64);
\r
1008 for(s32 i=0; i<m_itemcount; i++)
\r
1010 m_images.push_back(env->addImage(
\r
1011 imgsize + pos + spacing*i
\r
1013 m_images[i]->setScaleImage(true);
\r
1014 m_texts.push_back(env->addStaticText(
\r
1016 textsize + pos + spacing*i,
\r
1019 m_texts[i]->setBackgroundColor(
\r
1020 video::SColor(128,0,0,0));
\r
1021 m_texts[i]->setTextAlignment(
\r
1022 gui::EGUIA_CENTER,
\r
1023 gui::EGUIA_UPPERLEFT);
\r
1027 virtual bool OnEvent(const SEvent& event)
\r
1032 void setSelection(s32 i)
\r
1041 start = m_selection - m_itemcount / 2;
\r
1043 InventoryList *mainlist = m_inventory->getList("main");
\r
1045 for(s32 i=0; i<m_itemcount; i++)
\r
1047 s32 j = i + start;
\r
1049 if(j > (s32)mainlist->getSize() - 1)
\r
1050 j -= mainlist->getSize();
\r
1052 j += mainlist->getSize();
\r
1054 InventoryItem *item = mainlist->getItem(j);
\r
1058 m_images[i]->setImage(NULL);
\r
1061 if(m_selection == j)
\r
1062 swprintf(t, 10, L"<-");
\r
1064 swprintf(t, 10, L"");
\r
1065 m_texts[i]->setText(t);
\r
1067 // The next ifs will segfault with a NULL pointer
\r
1072 m_images[i]->setImage(item->getImage());
\r
1075 if(m_selection == j)
\r
1076 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1078 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1079 m_texts[i]->setText(t);
\r
1085 core::array<gui::IGUIStaticText*> m_texts;
\r
1086 core::array<gui::IGUIImage*> m_images;
\r
1087 Inventory *m_inventory;
\r
1098 ChatLine(const std::wstring &a_text):
\r
1104 std::wstring text;
\r
1107 int main(int argc, char *argv[])
\r
1110 Low-level initialization
\r
1113 bool disable_stderr = false;
\r
1115 disable_stderr = true;
\r
1118 // Initialize debug streams
\r
1119 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1120 // Initialize debug stacks
\r
1121 debug_stacks_init();
\r
1123 DSTACK(__FUNCTION_NAME);
\r
1125 porting::initializePaths();
\r
1126 // Create user data directory
\r
1127 fs::CreateDir(porting::path_userdata);
\r
1129 initializeMaterialProperties();
\r
1131 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1133 // Print startup message
\r
1134 dstream<<DTIME<<"minetest-c55"
\r
1135 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1136 <<", "<<BUILD_INFO
\r
1143 Parse command line
\r
1146 // List all allowed options
\r
1147 core::map<std::string, ValueSpec> allowed_options;
\r
1148 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1149 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1150 "Run server directly"));
\r
1151 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1152 "Load configuration from specified file"));
\r
1153 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1154 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1155 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1156 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1157 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1158 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1160 Settings cmd_args;
\r
1162 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1164 if(ret == false || cmd_args.getFlag("help"))
\r
1166 dstream<<"Allowed options:"<<std::endl;
\r
1167 for(core::map<std::string, ValueSpec>::Iterator
\r
1168 i = allowed_options.getIterator();
\r
1169 i.atEnd() == false; i++)
\r
1171 dstream<<" --"<<i.getNode()->getKey();
\r
1172 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1177 dstream<<" <value>";
\r
1179 dstream<<std::endl;
\r
1181 if(i.getNode()->getValue().help != NULL)
\r
1183 dstream<<" "<<i.getNode()->getValue().help
\r
1188 return cmd_args.getFlag("help") ? 0 : 1;
\r
1193 Basic initialization
\r
1196 // Initialize default settings
\r
1197 set_default_settings();
\r
1199 // Set locale. This is for forcing '.' as the decimal point.
\r
1200 std::locale::global(std::locale("C"));
\r
1201 // This enables printing all characters in bitmap font
\r
1202 setlocale(LC_CTYPE, "en_US");
\r
1204 // Initialize sockets
\r
1206 atexit(sockets_cleanup);
\r
1216 // Path of configuration file in use
\r
1217 std::string configpath = "";
\r
1219 if(cmd_args.exists("config"))
\r
1221 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1224 dstream<<"Could not read configuration from \""
\r
1225 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1228 configpath = cmd_args.get("config");
\r
1232 core::array<std::string> filenames;
\r
1233 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1234 #ifdef RUN_IN_PLACE
\r
1235 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1238 for(u32 i=0; i<filenames.size(); i++)
\r
1240 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1243 configpath = filenames[i];
\r
1249 // Initialize random seed
\r
1256 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1257 || cmd_args.getFlag("enable-unittests") == true)
\r
1262 // Read map parameters from settings
\r
1264 HMParams hm_params;
\r
1265 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1266 hm_params.randmax = g_settings.get("height_randmax");
\r
1267 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1268 hm_params.base = g_settings.get("height_base");
\r
1270 MapParams map_params;
\r
1271 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1272 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1278 std::cout<<std::endl<<std::endl;
\r
1281 <<" .__ __ __ "<<std::endl
\r
1282 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1283 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1284 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1285 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1286 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1289 std::cout<<std::endl;
\r
1290 //char templine[100];
\r
1294 if(cmd_args.exists("port"))
\r
1296 port = cmd_args.getU16("port");
\r
1300 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1301 std::cout<<"-> "<<port<<std::endl;
\r
1305 std::string map_dir = porting::path_userdata+"/map";
\r
1306 if(cmd_args.exists("map-dir"))
\r
1307 map_dir = cmd_args.get("map-dir");
\r
1308 else if(g_settings.exists("map-dir"))
\r
1309 map_dir = g_settings.get("map-dir");
\r
1311 if(cmd_args.getFlag("server"))
\r
1313 DSTACK("Dedicated server branch");
\r
1315 std::cout<<std::endl;
\r
1316 std::cout<<"========================"<<std::endl;
\r
1317 std::cout<<"Running dedicated server"<<std::endl;
\r
1318 std::cout<<"========================"<<std::endl;
\r
1319 std::cout<<std::endl;
\r
1321 Server server(map_dir, hm_params, map_params);
\r
1322 server.start(port);
\r
1326 // This is kind of a hack but can be done like this
\r
1327 // because server.step() is very light
\r
1329 server.step(0.030);
\r
1331 static int counter = 0;
\r
1337 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1338 core::list<PlayerInfo>::Iterator i;
\r
1339 static u32 sum_old = 0;
\r
1340 u32 sum = PIChecksum(list);
\r
1341 if(sum != sum_old)
\r
1343 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1344 for(i=list.begin(); i!=list.end(); i++)
\r
1346 i->PrintLine(&std::cout);
\r
1356 bool hosting = false;
\r
1357 char connect_name[100] = "";
\r
1359 if(cmd_args.exists("address"))
\r
1361 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1363 else if(is_yes(g_settings.get("host_game")) == false)
\r
1365 if(g_settings.get("address") != "")
\r
1367 std::cout<<g_settings.get("address")<<std::endl;
\r
1368 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1372 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1373 std::cin.getline(connect_name, 100);
\r
1377 if(connect_name[0] == 0){
\r
1378 snprintf(connect_name, 100, "127.0.0.1");
\r
1383 std::cout<<"> Hosting game"<<std::endl;
\r
1385 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1387 char playername[PLAYERNAME_SIZE] = "";
\r
1388 if(g_settings.get("name") != "")
\r
1390 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1394 std::cout<<"Name of player: ";
\r
1395 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1397 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1400 Resolution selection
\r
1403 bool fullscreen = false;
\r
1404 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1405 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1409 MyEventReceiver receiver;
\r
1411 video::E_DRIVER_TYPE driverType;
\r
1414 //driverType = video::EDT_DIRECT3D9;
\r
1415 driverType = video::EDT_OPENGL;
\r
1417 driverType = video::EDT_OPENGL;
\r
1418 //driverType = video::EDT_BURNINGSVIDEO;
\r
1421 // create device and exit if creation failed
\r
1423 IrrlichtDevice *device;
\r
1424 device = createDevice(driverType,
\r
1425 core::dimension2d<u32>(screenW, screenH),
\r
1426 16, fullscreen, false, false, &receiver);
\r
1429 return 1; // could not create selected driver.
\r
1431 g_device = device;
\r
1432 g_irrlicht = new IrrlichtWrapper(device);
\r
1434 //g_device = device;
\r
1436 device->setResizable(true);
\r
1438 bool random_input = g_settings.getBool("random_input")
\r
1439 || cmd_args.getFlag("random-input");
\r
1441 g_input = new RandomInputHandler();
\r
1443 g_input = new RealInputHandler(device, &receiver);
\r
1446 Continue initialization
\r
1449 video::IVideoDriver* driver = device->getVideoDriver();
\r
1452 This changes the minimum allowed number of vertices in a VBO
\r
1454 //driver->setMinHardwareBufferVertexCount(50);
\r
1456 scene::ISceneManager* smgr = device->getSceneManager();
\r
1458 guienv = device->getGUIEnvironment();
\r
1459 gui::IGUISkin* skin = guienv->getSkin();
\r
1460 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1462 skin->setFont(font);
\r
1464 dstream<<"WARNING: Font file was not found."
\r
1465 " Using default font."<<std::endl;
\r
1466 // If font was not found, this will get us one
\r
1467 font = skin->getFont();
\r
1470 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1471 dstream<<"text_height="<<text_height<<std::endl;
\r
1473 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1474 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1475 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1476 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1477 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1478 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1480 const wchar_t *text = L"Loading and connecting...";
\r
1481 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1482 core::vector2d<s32> textsize(300, text_height);
\r
1483 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1485 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1486 text, textrect, false, false);
\r
1487 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1489 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1490 guienv->drawAll();
\r
1491 driver->endScene();
\r
1494 Preload some textures
\r
1497 init_content_inventory_texture_paths();
\r
1498 init_tile_texture_paths();
\r
1499 tile_materials_preload(g_irrlicht);
\r
1502 Make a scope here for the client so that it gets removed
\r
1503 before the irrlicht device
\r
1507 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1512 SharedPtr<Server> server;
\r
1514 server = new Server(map_dir, hm_params, map_params);
\r
1515 server->start(port);
\r
1522 Client client(device, playername, draw_control);
\r
1524 g_client = &client;
\r
1526 Address connect_address(0,0,0,0, port);
\r
1528 connect_address.Resolve(connect_name);
\r
1530 catch(ResolveError &e)
\r
1532 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1536 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1537 client.connect(connect_address);
\r
1540 while(client.connectedAndInitialized() == false)
\r
1543 if(server != NULL){
\r
1544 server->step(0.1);
\r
1549 catch(con::PeerNotFoundException &e)
\r
1551 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1558 /*scene::ISceneNode* skybox;
\r
1559 skybox = smgr->addSkyBoxSceneNode(
\r
1560 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1561 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1562 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1563 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1564 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1565 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1568 Create the camera node
\r
1571 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1572 0, // Camera parent
\r
1573 v3f(BS*100, BS*2, BS*100), // Look from
\r
1574 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1578 if(camera == NULL)
\r
1581 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1583 camera->setFOV(FOV_ANGLE);
\r
1585 // Just so big a value that everything rendered is visible
\r
1586 camera->setFarValue(100000*BS);
\r
1588 f32 camera_yaw = 0; // "right/left"
\r
1589 f32 camera_pitch = 0; // "up/down"
\r
1595 gui_loadingtext->remove();
\r
1598 Add some gui stuff
\r
1601 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1602 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1605 We need some kind of a root node to be able to add
\r
1606 custom elements directly on the screen.
\r
1607 Otherwise they won't be automatically drawn.
\r
1609 guiroot = guienv->addStaticText(L"",
\r
1610 core::rect<s32>(0, 0, 10000, 10000));
\r
1612 // Test the text input system
\r
1613 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1615 /*GUIMessageMenu *menu =
\r
1616 new GUIMessageMenu(guienv, guiroot, -1,
\r
1617 &g_active_menu_count,
\r
1621 // Launch pause menu
\r
1622 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1623 &g_active_menu_count))->drop();
\r
1625 // First line of debug text
\r
1626 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1628 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1630 // Second line of debug text
\r
1631 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1633 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1636 // At the middle of the screen
\r
1637 // Object infos are shown in this
\r
1638 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1640 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1644 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1645 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1646 core::rect<s32>(70, 60, 795, 150),
\r
1648 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1649 core::list<ChatLine> chat_lines;
\r
1652 Some statistics are collected in these
\r
1655 u32 beginscenetime = 0;
\r
1656 u32 scenetime = 0;
\r
1657 u32 endscenetime = 0;
\r
1660 //throw con::PeerNotFoundException("lol");
\r
1666 bool first_loop_after_window_activation = true;
\r
1668 // Time is in milliseconds
\r
1669 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1670 // NOTE: So we have to use getTime() and call run()s between them
\r
1671 u32 lasttime = device->getTimer()->getTime();
\r
1673 while(device->run())
\r
1676 Run global IrrlichtWrapper's main thread processing stuff
\r
1678 g_irrlicht->Run();
\r
1681 Random calculations
\r
1683 v2u32 screensize = driver->getScreenSize();
\r
1684 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1686 // Hilight boxes collected during the loop and displayed
\r
1687 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1690 std::wstring infotext;
\r
1692 //TimeTaker //timer1("//timer1");
\r
1694 // Time of frame without fps limit
\r
1698 // not using getRealTime is necessary for wine
\r
1699 u32 time = device->getTimer()->getTime();
\r
1700 if(time > lasttime)
\r
1701 busytime_u32 = time - lasttime;
\r
1704 busytime = busytime_u32 / 1000.0;
\r
1707 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1709 // Absolutelu necessary for wine!
\r
1716 updateViewingRange(busytime, &client);
\r
1723 float fps_max = g_settings.getFloat("fps_max");
\r
1724 u32 frametime_min = 1000./fps_max;
\r
1726 if(busytime_u32 < frametime_min)
\r
1728 u32 sleeptime = frametime_min - busytime_u32;
\r
1729 device->sleep(sleeptime);
\r
1733 // Absolutelu necessary for wine!
\r
1737 Time difference calculation
\r
1739 f32 dtime; // in seconds
\r
1741 u32 time = device->getTimer()->getTime();
\r
1742 if(time > lasttime)
\r
1743 dtime = (time - lasttime) / 1000.0;
\r
1749 Time average and jitter calculation
\r
1752 static f32 dtime_avg1 = 0.0;
\r
1753 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1754 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1756 static f32 dtime_jitter1_max_sample = 0.0;
\r
1757 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1759 static f32 jitter1_max = 0.0;
\r
1760 static f32 counter = 0.0;
\r
1761 if(dtime_jitter1 > jitter1_max)
\r
1762 jitter1_max = dtime_jitter1;
\r
1767 dtime_jitter1_max_sample = jitter1_max;
\r
1768 dtime_jitter1_max_fraction
\r
1769 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1770 jitter1_max = 0.0;
\r
1773 Control freetime ratio
\r
1775 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1777 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1778 g_freetime_ratio += 0.01;
\r
1782 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1783 g_freetime_ratio -= 0.01;
\r
1789 Busytime average and jitter calculation
\r
1792 static f32 busytime_avg1 = 0.0;
\r
1793 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1794 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1796 static f32 busytime_jitter1_max_sample = 0.0;
\r
1797 static f32 busytime_jitter1_min_sample = 0.0;
\r
1799 static f32 jitter1_max = 0.0;
\r
1800 static f32 jitter1_min = 0.0;
\r
1801 static f32 counter = 0.0;
\r
1802 if(busytime_jitter1 > jitter1_max)
\r
1803 jitter1_max = busytime_jitter1;
\r
1804 if(busytime_jitter1 < jitter1_min)
\r
1805 jitter1_min = busytime_jitter1;
\r
1807 if(counter > 0.0){
\r
1809 busytime_jitter1_max_sample = jitter1_max;
\r
1810 busytime_jitter1_min_sample = jitter1_min;
\r
1811 jitter1_max = 0.0;
\r
1812 jitter1_min = 0.0;
\r
1817 Debug info for client
\r
1820 static float counter = 0.0;
\r
1825 client.printDebugInfo(std::cout);
\r
1830 Input handler step()
\r
1832 g_input->step(dtime);
\r
1835 Player speed control
\r
1844 bool a_superspeed,
\r
1847 PlayerControl control(
\r
1848 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1849 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1850 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1851 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1852 g_input->isKeyDown(irr::KEY_SPACE),
\r
1853 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1857 client.setPlayerControl(control);
\r
1861 Process environment
\r
1865 //TimeTaker timer("client.step(dtime)");
\r
1866 client.step(dtime);
\r
1867 //client.step(dtime_avg1);
\r
1870 if(server != NULL)
\r
1872 //TimeTaker timer("server->step(dtime)");
\r
1873 server->step(dtime);
\r
1876 v3f player_position = client.getPlayerPosition();
\r
1878 //TimeTaker //timer2("//timer2");
\r
1881 Mouse and camera control
\r
1884 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1887 device->getCursorControl()->setVisible(false);
\r
1889 if(first_loop_after_window_activation){
\r
1890 //std::cout<<"window active, first loop"<<std::endl;
\r
1891 first_loop_after_window_activation = false;
\r
1894 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1895 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1896 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1897 camera_yaw -= dx*0.2;
\r
1898 camera_pitch += dy*0.2;
\r
1899 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1900 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1902 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1905 device->getCursorControl()->setVisible(true);
\r
1907 //std::cout<<"window inactive"<<std::endl;
\r
1908 first_loop_after_window_activation = true;
\r
1911 camera_yaw = wrapDegrees(camera_yaw);
\r
1912 camera_pitch = wrapDegrees(camera_pitch);
\r
1914 v3f camera_direction = v3f(0,0,1);
\r
1915 camera_direction.rotateYZBy(camera_pitch);
\r
1916 camera_direction.rotateXZBy(camera_yaw);
\r
1918 // This is at the height of the eyes of the current figure
\r
1919 v3f camera_position =
\r
1920 player_position + v3f(0, BS+BS/2, 0);
\r
1921 // This is more like in minecraft
\r
1922 /*v3f camera_position =
\r
1923 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1925 camera->setPosition(camera_position);
\r
1926 // *100.0 helps in large map coordinates
\r
1927 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1929 if(FIELD_OF_VIEW_TEST){
\r
1930 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1931 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1934 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1935 //TimeTaker timer("client.updateCamera");
\r
1936 client.updateCamera(camera_position, camera_direction);
\r
1940 //TimeTaker //timer3("//timer3");
\r
1943 Calculate what block is the crosshair pointing to
\r
1946 //u32 t1 = device->getTimer()->getRealTime();
\r
1948 //f32 d = 4; // max. distance
\r
1949 f32 d = 4; // max. distance
\r
1950 core::line3d<f32> shootline(camera_position,
\r
1951 camera_position + camera_direction * BS * (d+1));
\r
1953 MapBlockObject *selected_object = client.getSelectedObject
\r
1954 (d*BS, camera_position, shootline);
\r
1957 If it's pointing to a MapBlockObject
\r
1960 if(selected_object != NULL)
\r
1962 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1964 core::aabbox3d<f32> box_on_map
\r
1965 = selected_object->getSelectionBoxOnMap();
\r
1967 hilightboxes.push_back(box_on_map);
\r
1969 infotext = narrow_to_wide(selected_object->infoText());
\r
1971 if(g_input->getLeftClicked())
\r
1973 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1974 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1975 selected_object->getId(), g_selected_item);
\r
1977 else if(g_input->getRightClicked())
\r
1979 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1981 Check if we want to modify the object ourselves
\r
1983 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1985 dstream<<"Sign object right-clicked"<<std::endl;
\r
1987 if(random_input == false)
\r
1989 // Get a new text for it
\r
1991 TextDest *dest = new TextDestSign(
\r
1992 selected_object->getBlock()->getPos(),
\r
1993 selected_object->getId(),
\r
1996 SignObject *sign_object = (SignObject*)selected_object;
\r
1998 std::wstring wtext =
\r
1999 narrow_to_wide(sign_object->getText());
\r
2001 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2002 &g_active_menu_count, dest,
\r
2007 Otherwise pass the event to the server as-is
\r
2011 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2012 selected_object->getId(), g_selected_item);
\r
2016 else // selected_object == NULL
\r
2020 Find out which node we are pointing at
\r
2023 bool nodefound = false;
\r
2025 v3s16 neighbourpos;
\r
2026 core::aabbox3d<f32> nodefacebox;
\r
2027 f32 mindistance = BS * 1001;
\r
2029 v3s16 pos_i = floatToInt(player_position);
\r
2031 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2035 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2036 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2037 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2038 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2039 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2040 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2042 for(s16 y = ystart; y <= yend; y++)
\r
2043 for(s16 z = zstart; z <= zend; z++)
\r
2044 for(s16 x = xstart; x <= xend; x++)
\r
2049 n = client.getNode(v3s16(x,y,z));
\r
2050 if(content_pointable(n.d) == false)
\r
2053 catch(InvalidPositionException &e)
\r
2059 v3f npf = intToFloat(np);
\r
2064 v3s16(0,0,1), // back
\r
2065 v3s16(0,1,0), // top
\r
2066 v3s16(1,0,0), // right
\r
2067 v3s16(0,0,-1), // front
\r
2068 v3s16(0,-1,0), // bottom
\r
2069 v3s16(-1,0,0), // left
\r
2075 if(n.d == CONTENT_TORCH)
\r
2077 v3s16 dir = unpackDir(n.dir);
\r
2078 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2079 dir_f *= BS/2 - BS/6 - BS/20;
\r
2080 v3f cpf = npf + dir_f;
\r
2081 f32 distance = (cpf - camera_position).getLength();
\r
2083 core::aabbox3d<f32> box;
\r
2086 if(dir == v3s16(0,-1,0))
\r
2088 box = core::aabbox3d<f32>(
\r
2089 npf - v3f(BS/6, BS/2, BS/6),
\r
2090 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2094 else if(dir == v3s16(0,1,0))
\r
2096 box = core::aabbox3d<f32>(
\r
2097 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2098 npf + v3f(BS/6, BS/2, BS/6)
\r
2104 box = core::aabbox3d<f32>(
\r
2105 cpf - v3f(BS/6, BS/3, BS/6),
\r
2106 cpf + v3f(BS/6, BS/3, BS/6)
\r
2110 if(distance < mindistance)
\r
2112 if(box.intersectsWithLine(shootline))
\r
2116 neighbourpos = np;
\r
2117 mindistance = distance;
\r
2118 nodefacebox = box;
\r
2127 for(u16 i=0; i<6; i++)
\r
2129 v3f dir_f = v3f(dirs[i].X,
\r
2130 dirs[i].Y, dirs[i].Z);
\r
2131 v3f centerpoint = npf + dir_f * BS/2;
\r
2133 (centerpoint - camera_position).getLength();
\r
2135 if(distance < mindistance)
\r
2137 core::CMatrix4<f32> m;
\r
2138 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2140 // This is the back face
\r
2141 v3f corners[2] = {
\r
2142 v3f(BS/2, BS/2, BS/2),
\r
2143 v3f(-BS/2, -BS/2, BS/2+d)
\r
2146 for(u16 j=0; j<2; j++)
\r
2148 m.rotateVect(corners[j]);
\r
2149 corners[j] += npf;
\r
2152 core::aabbox3d<f32> facebox(corners[0]);
\r
2153 facebox.addInternalPoint(corners[1]);
\r
2155 if(facebox.intersectsWithLine(shootline))
\r
2159 neighbourpos = np + dirs[i];
\r
2160 mindistance = distance;
\r
2161 nodefacebox = facebox;
\r
2163 } // if distance < mindistance
\r
2165 } // regular block
\r
2168 static float nodig_delay_counter = 0.0;
\r
2172 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2174 static float dig_time = 0.0;
\r
2175 static u16 dig_index = 0;
\r
2177 hilightboxes.push_back(nodefacebox);
\r
2179 if(g_input->getLeftReleased())
\r
2181 client.clearTempMod(nodepos);
\r
2185 if(nodig_delay_counter > 0.0)
\r
2187 nodig_delay_counter -= dtime;
\r
2191 if(nodepos != nodepos_old)
\r
2193 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2194 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2196 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2198 client.clearTempMod(nodepos_old);
\r
2203 if(g_input->getLeftClicked() ||
\r
2204 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2206 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2207 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2209 if(g_input->getLeftClicked())
\r
2211 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2213 if(g_input->getLeftState())
\r
2215 MapNode n = client.getNode(nodepos);
\r
2217 // Get tool name. Default is "" = bare hands
\r
2218 std::string toolname = "";
\r
2219 InventoryList *mlist = local_inventory.getList("main");
\r
2222 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2223 if(item && (std::string)item->getName() == "ToolItem")
\r
2225 ToolItem *titem = (ToolItem*)item;
\r
2226 toolname = titem->getToolName();
\r
2230 // Get digging properties for material and tool
\r
2231 u8 material = n.d;
\r
2232 DiggingProperties prop =
\r
2233 getDiggingProperties(material, toolname);
\r
2235 float dig_time_complete = 0.0;
\r
2237 if(prop.diggable == false)
\r
2239 /*dstream<<"Material "<<(int)material
\r
2240 <<" not diggable with \""
\r
2241 <<toolname<<"\""<<std::endl;*/
\r
2242 // I guess nobody will wait for this long
\r
2243 dig_time_complete = 10000000.0;
\r
2247 dig_time_complete = prop.time;
\r
2250 if(dig_time_complete >= 0.001)
\r
2252 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2253 * dig_time/dig_time_complete);
\r
2255 // This is for torches
\r
2258 dig_index = CRACK_ANIMATION_LENGTH;
\r
2261 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2263 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2264 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2268 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2269 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2270 client.clearTempMod(nodepos);
\r
2271 client.removeNode(nodepos);
\r
2275 nodig_delay_counter = dig_time_complete
\r
2276 / (float)CRACK_ANIMATION_LENGTH;
\r
2278 // We don't want a corresponding delay to
\r
2279 // very time consuming nodes
\r
2280 if(nodig_delay_counter > 0.5)
\r
2282 nodig_delay_counter = 0.5;
\r
2284 // We want a slight delay to very little
\r
2285 // time consuming nodes
\r
2286 //float mindelay = 0.15;
\r
2287 float mindelay = 0.20;
\r
2288 if(nodig_delay_counter < mindelay)
\r
2290 nodig_delay_counter = mindelay;
\r
2294 dig_time += dtime;
\r
2298 if(g_input->getRightClicked())
\r
2300 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2301 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2304 nodepos_old = nodepos;
\r
2309 } // selected_object == NULL
\r
2311 g_input->resetLeftClicked();
\r
2312 g_input->resetRightClicked();
\r
2314 if(g_input->getLeftReleased())
\r
2316 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2318 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2320 if(g_input->getRightReleased())
\r
2322 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2326 g_input->resetLeftReleased();
\r
2327 g_input->resetRightReleased();
\r
2330 Calculate stuff for drawing
\r
2333 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2335 u32 daynight_ratio = client.getDayNightRatio();
\r
2336 /*video::SColor bgcolor = video::SColor(
\r
2338 skycolor.getRed() * daynight_ratio / 1000,
\r
2339 skycolor.getGreen() * daynight_ratio / 1000,
\r
2340 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2342 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2343 video::SColor bgcolor = video::SColor(
\r
2345 skycolor.getRed() * l / 255,
\r
2346 skycolor.getGreen() * l / 255,
\r
2347 skycolor.getBlue() * l / 255);
\r
2353 if(g_settings.getBool("enable_fog") == true)
\r
2355 f32 range = draw_control.wanted_range * BS;
\r
2356 if(draw_control.range_all)
\r
2357 range = 100000*BS;
\r
2361 video::EFT_FOG_LINEAR,
\r
2365 false, // pixel fog
\r
2366 false // range fog
\r
2372 Update gui stuff (0ms)
\r
2375 //TimeTaker guiupdatetimer("Gui updating");
\r
2378 wchar_t temptext[150];
\r
2380 static float drawtime_avg = 0;
\r
2381 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2382 static float beginscenetime_avg = 0;
\r
2383 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2384 static float scenetime_avg = 0;
\r
2385 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2386 static float endscenetime_avg = 0;
\r
2387 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2389 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2391 L", R: range_all=%i"
\r
2393 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2395 draw_control.range_all,
\r
2397 beginscenetime_avg,
\r
2402 guitext->setText(temptext);
\r
2406 wchar_t temptext[150];
\r
2407 swprintf(temptext, 150,
\r
2408 L"(% .1f, % .1f, % .1f)"
\r
2409 L" (% .3f < btime_jitter < % .3f"
\r
2410 L", dtime_jitter = % .1f %%"
\r
2411 L", v_range = %.1f)",
\r
2412 player_position.X/BS,
\r
2413 player_position.Y/BS,
\r
2414 player_position.Z/BS,
\r
2415 busytime_jitter1_min_sample,
\r
2416 busytime_jitter1_max_sample,
\r
2417 dtime_jitter1_max_fraction * 100.0,
\r
2418 draw_control.wanted_range
\r
2421 guitext2->setText(temptext);
\r
2425 guitext_info->setText(infotext.c_str());
\r
2429 Get chat messages from client
\r
2432 // Get new messages
\r
2433 std::wstring message;
\r
2434 while(client.getChatMessage(message))
\r
2436 chat_lines.push_back(ChatLine(message));
\r
2437 /*if(chat_lines.size() > 6)
\r
2439 core::list<ChatLine>::Iterator
\r
2440 i = chat_lines.begin();
\r
2441 chat_lines.erase(i);
\r
2444 // Append them to form the whole static text and throw
\r
2445 // it to the gui element
\r
2446 std::wstring whole;
\r
2447 // This will correspond to the line number counted from
\r
2448 // top to bottom, from size-1 to 0
\r
2449 s16 line_number = chat_lines.size();
\r
2450 // Count of messages to be removed from the top
\r
2451 u16 to_be_removed_count = 0;
\r
2452 for(core::list<ChatLine>::Iterator
\r
2453 i = chat_lines.begin();
\r
2454 i != chat_lines.end(); i++)
\r
2456 // After this, line number is valid for this loop
\r
2459 (*i).age += dtime;
\r
2461 This results in a maximum age of 60*6 to the
\r
2462 lowermost line and a maximum of 6 lines
\r
2464 float allowed_age = (6-line_number) * 60.0;
\r
2466 if((*i).age > allowed_age)
\r
2468 to_be_removed_count++;
\r
2471 whole += (*i).text + L'\n';
\r
2473 for(u16 i=0; i<to_be_removed_count; i++)
\r
2475 core::list<ChatLine>::Iterator
\r
2476 it = chat_lines.begin();
\r
2477 chat_lines.erase(it);
\r
2479 chat_guitext->setText(whole.c_str());
\r
2480 // Update gui element size and position
\r
2481 core::rect<s32> rect(
\r
2483 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2484 screensize.X - 10,
\r
2487 chat_guitext->setRelativePosition(rect);
\r
2489 if(chat_lines.size() == 0)
\r
2490 chat_guitext->setVisible(false);
\r
2492 chat_guitext->setVisible(true);
\r
2499 static u16 old_selected_item = 65535;
\r
2500 if(client.getLocalInventoryUpdated()
\r
2501 || g_selected_item != old_selected_item)
\r
2503 old_selected_item = g_selected_item;
\r
2504 //std::cout<<"Updating local inventory"<<std::endl;
\r
2505 client.getLocalInventory(local_inventory);
\r
2506 quick_inventory->setSelection(g_selected_item);
\r
2507 quick_inventory->update();
\r
2511 Send actions returned by the inventory menu
\r
2513 while(inventory_action_queue.size() != 0)
\r
2515 InventoryAction *a = inventory_action_queue.pop_front();
\r
2517 client.sendInventoryAction(a);
\r
2526 TimeTaker drawtimer("Drawing");
\r
2530 TimeTaker timer("beginScene");
\r
2531 driver->beginScene(true, true, bgcolor);
\r
2532 //driver->beginScene(false, true, bgcolor);
\r
2533 beginscenetime = timer.stop(true);
\r
2538 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2541 TimeTaker timer("smgr");
\r
2543 scenetime = timer.stop(true);
\r
2547 //TimeTaker timer9("auxiliary drawings");
\r
2551 //TimeTaker //timer10("//timer10");
\r
2553 video::SMaterial m;
\r
2555 m.Lighting = false;
\r
2556 driver->setMaterial(m);
\r
2558 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2560 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2561 i != hilightboxes.end(); i++)
\r
2563 /*std::cout<<"hilightbox min="
\r
2564 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2566 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2568 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2574 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2575 displaycenter + core::vector2d<s32>(10,0),
\r
2576 video::SColor(255,255,255,255));
\r
2577 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2578 displaycenter + core::vector2d<s32>(0,10),
\r
2579 video::SColor(255,255,255,255));
\r
2584 //TimeTaker //timer11("//timer11");
\r
2590 guienv->drawAll();
\r
2594 TimeTaker timer("endScene");
\r
2595 driver->endScene();
\r
2596 endscenetime = timer.stop(true);
\r
2599 drawtime = drawtimer.stop(true);
\r
2605 static s16 lastFPS = 0;
\r
2606 //u16 fps = driver->getFPS();
\r
2607 u16 fps = (1.0/dtime_avg1);
\r
2609 if (lastFPS != fps)
\r
2611 core::stringw str = L"Minetest [";
\r
2612 str += driver->getName();
\r
2616 device->setWindowCaption(str.c_str());
\r
2622 device->yield();*/
\r
2625 delete quick_inventory;
\r
2627 } // client is deleted at this point
\r
2632 In the end, delete the Irrlicht device.
\r
2637 Update configuration file
\r
2639 /*if(configpath != "")
\r
2641 g_settings.updateConfigFile(configpath.c_str());
\r
2645 catch(con::PeerNotFoundException &e)
\r
2647 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2651 GUIMessageMenu *menu =
\r
2652 new GUIMessageMenu(guienv, guiroot, -1,
\r
2653 &g_active_menu_count,
\r
2654 L"Connection timed out");
\r
2656 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2658 dstream<<"Created menu"<<std::endl;
\r
2660 while(g_device->run() && menu->getStatus() == false)
\r
2662 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2663 guienv->drawAll();
\r
2664 driver->endScene();
\r
2667 dstream<<"Dropping menu"<<std::endl;
\r
2673 END_DEBUG_EXCEPTION_HANDLER
\r
2675 debugstreams_deinit();
\r