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
173 TODO: Better handling of objects and mobs
\r
175 - There has to be some way to do it with less spaghetti code
\r
176 - Make separate classes for client and server
\r
177 - Client should not discriminate between blocks, server should
\r
178 - Make other players utilize the same framework
\r
179 - This is also needed for objects that don't get sent to client
\r
180 but are used for triggers etc
\r
182 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
183 need an additional metadata field for the texts
\r
184 - This is also needed for item container chests
\r
185 TODO: There has to be some better way to handle static objects than to
\r
186 send them all the time. This affects signs and item objects.
\r
188 Block object server side:
\r
189 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
190 - For all blocks in the buffer, objects are stepped(). This
\r
191 means they are active.
\r
192 - TODO: A global active buffer is needed for the server
\r
193 - TODO: A timestamp to blocks
\r
194 - TODO: All blocks going in and out of the buffer are recorded.
\r
195 - TODO: For outgoing blocks, timestamp is written.
\r
196 - TODO: For incoming blocks, time difference is calculated and
\r
197 objects are stepped according to it.
\r
201 TODO: There are some lighting-related todos and fixmes in
\r
202 ServerMap::emergeBlock
\r
204 TODO: When generating a block, check that there is no sunlight
\r
205 below the block if the bottom of the block doesn't have
\r
206 sunlight. If it has, add it to the invalid lighting list.
\r
208 TODO: Map generator version 2
\r
209 - Create surface areas based on central points; a given point's
\r
210 area type is given by the nearest central point
\r
211 - Separate points for heightmap, caves, plants and minerals?
\r
212 - Flat land, mountains, forest, jungle
\r
216 ======================================================================
\r
218 ======================================================================
\r
223 Setting this to 1 enables a special camera mode that forces
\r
224 the renderers to think that the camera statically points from
\r
225 the starting place to a static direction.
\r
227 This allows one to move around with the player and see what
\r
228 is actually drawn behind solid things and behind the player.
\r
230 #define FIELD_OF_VIEW_TEST 0
\r
234 #pragma message ("Disabling unit tests")
\r
236 #warning "Disabling unit tests"
\r
238 // Disable unit tests
\r
239 #define ENABLE_TESTS 0
\r
241 // Enable unit tests
\r
242 #define ENABLE_TESTS 1
\r
246 #pragma comment(lib, "Irrlicht.lib")
\r
247 #pragma comment(lib, "jthread.lib")
\r
248 #pragma comment(lib, "zlibwapi.lib")
\r
249 // This would get rid of the console window
\r
250 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
253 #include <iostream>
\r
255 #include <jmutexautolock.h>
\r
256 #include <locale.h>
\r
257 #include "common_irrlicht.h"
\r
260 #include "player.h"
\r
263 #include "environment.h"
\r
264 #include "server.h"
\r
265 #include "client.h"
\r
266 #include "serialization.h"
\r
267 #include "constants.h"
\r
268 #include "strfnd.h"
\r
269 #include "porting.h"
\r
270 #include "irrlichtwrapper.h"
\r
271 #include "gettime.h"
\r
272 #include "porting.h"
\r
273 #include "guiPauseMenu.h"
\r
274 #include "guiInventoryMenu.h"
\r
275 #include "guiTextInputMenu.h"
\r
276 #include "materials.h"
\r
277 #include "guiMessageMenu.h"
\r
278 #include "filesys.h"
\r
279 #include "config.h"
\r
281 IrrlichtWrapper *g_irrlicht;
\r
283 MapDrawControl draw_control;
\r
287 These are loaded from the config file.
\r
290 Settings g_settings;
\r
292 extern void set_default_settings();
\r
298 IrrlichtDevice *g_device = NULL;
\r
299 Client *g_client = NULL;
\r
304 gui::IGUIEnvironment* guienv = NULL;
\r
305 gui::IGUIStaticText *guiroot = NULL;
\r
306 int g_active_menu_count = 0;
\r
308 bool noMenuActive()
\r
310 return (g_active_menu_count == 0);
\r
313 // Inventory actions from the menu are buffered here before sending
\r
314 Queue<InventoryAction*> inventory_action_queue;
\r
315 // This is a copy of the inventory that the client's environment has
\r
316 Inventory local_inventory;
\r
318 u16 g_selected_item = 0;
\r
325 std::ostream *dout_con_ptr = &dummyout;
\r
326 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
327 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
328 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
329 //std::ostream *dout_con_ptr = &dstream;
\r
330 //std::ostream *derr_con_ptr = &dstream;
\r
333 std::ostream *dout_server_ptr = &dstream;
\r
334 std::ostream *derr_server_ptr = &dstream;
\r
337 std::ostream *dout_client_ptr = &dstream;
\r
338 std::ostream *derr_client_ptr = &dstream;
\r
341 gettime.h implementation
\r
347 Use irrlicht because it is more precise than porting.h's
\r
350 if(g_irrlicht == NULL)
\r
352 return g_irrlicht->getTime();
\r
359 struct TextDestSign : public TextDest
\r
361 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
363 m_blockpos = blockpos;
\r
367 void gotText(std::wstring text)
\r
369 std::string ntext = wide_to_narrow(text);
\r
370 dstream<<"Changing text of a sign object: "
\r
371 <<ntext<<std::endl;
\r
372 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
380 struct TextDestChat : public TextDest
\r
382 TextDestChat(Client *client)
\r
386 void gotText(std::wstring text)
\r
388 m_client->sendChatMessage(text);
\r
389 m_client->addChatMessage(text);
\r
395 class MyEventReceiver : public IEventReceiver
\r
398 // This is the one method that we have to implement
\r
399 virtual bool OnEvent(const SEvent& event)
\r
402 React to nothing here if a menu is active
\r
404 if(noMenuActive() == false)
\r
410 // Remember whether each key is down or up
\r
411 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
413 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
415 if(event.KeyInput.PressedDown)
\r
417 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
423 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
425 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
427 dstream<<DTIME<<"MyEventReceiver: "
\r
428 <<"Launching pause menu"<<std::endl;
\r
429 // It will delete itself by itself
\r
430 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
431 &g_active_menu_count))->drop();
\r
434 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
436 dstream<<DTIME<<"MyEventReceiver: "
\r
437 <<"Launching inventory"<<std::endl;
\r
438 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
439 &local_inventory, &inventory_action_queue,
\r
440 &g_active_menu_count))->drop();
\r
443 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
445 TextDest *dest = new TextDestChat(g_client);
\r
447 (new GUITextInputMenu(guienv, guiroot, -1,
\r
448 &g_active_menu_count, dest,
\r
453 // Material selection
\r
454 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
456 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
459 g_selected_item = 0;
\r
460 dstream<<DTIME<<"Selected item: "
\r
461 <<g_selected_item<<std::endl;
\r
464 // Viewing range selection
\r
465 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
467 if(draw_control.range_all)
\r
469 draw_control.range_all = false;
\r
470 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
474 draw_control.range_all = true;
\r
475 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
479 // Print debug stacks
\r
480 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
482 dstream<<"-----------------------------------------"
\r
484 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
485 dstream<<"-----------------------------------------"
\r
487 debug_stacks_print();
\r
492 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
494 if(noMenuActive() == false)
\r
496 left_active = false;
\r
497 middle_active = false;
\r
498 right_active = false;
\r
502 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
503 left_active = event.MouseInput.isLeftPressed();
\r
504 middle_active = event.MouseInput.isMiddlePressed();
\r
505 right_active = event.MouseInput.isRightPressed();
\r
507 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
509 leftclicked = true;
\r
511 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
513 rightclicked = true;
\r
515 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
517 leftreleased = true;
\r
519 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
521 rightreleased = true;
\r
523 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
525 /*dstream<<"event.MouseInput.Wheel="
\r
526 <<event.MouseInput.Wheel<<std::endl;*/
\r
527 if(event.MouseInput.Wheel < 0)
\r
529 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
532 g_selected_item = 0;
\r
534 else if(event.MouseInput.Wheel > 0)
\r
536 if(g_selected_item > 0)
\r
539 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
548 // This is used to check whether a key is being held down
\r
549 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
551 return keyIsDown[keyCode];
\r
556 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
557 keyIsDown[i] = false;
\r
559 leftclicked = false;
\r
560 rightclicked = false;
\r
561 leftreleased = false;
\r
562 rightreleased = false;
\r
564 left_active = false;
\r
565 middle_active = false;
\r
566 right_active = false;
\r
577 bool rightreleased;
\r
580 bool middle_active;
\r
584 // We use this array to store the current state of each key
\r
585 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
588 IrrlichtDevice *m_device;
\r
597 virtual ~InputHandler()
\r
601 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
603 virtual v2s32 getMousePos() = 0;
\r
604 virtual void setMousePos(s32 x, s32 y) = 0;
\r
606 virtual bool getLeftState() = 0;
\r
607 virtual bool getRightState() = 0;
\r
609 virtual bool getLeftClicked() = 0;
\r
610 virtual bool getRightClicked() = 0;
\r
611 virtual void resetLeftClicked() = 0;
\r
612 virtual void resetRightClicked() = 0;
\r
614 virtual bool getLeftReleased() = 0;
\r
615 virtual bool getRightReleased() = 0;
\r
616 virtual void resetLeftReleased() = 0;
\r
617 virtual void resetRightReleased() = 0;
\r
619 virtual void step(float dtime) {};
\r
621 virtual void clear() {};
\r
624 InputHandler *g_input = NULL;
\r
626 class RealInputHandler : public InputHandler
\r
629 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
631 m_receiver(receiver)
\r
634 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
636 return m_receiver->IsKeyDown(keyCode);
\r
638 virtual v2s32 getMousePos()
\r
640 return m_device->getCursorControl()->getPosition();
\r
642 virtual void setMousePos(s32 x, s32 y)
\r
644 m_device->getCursorControl()->setPosition(x, y);
\r
647 virtual bool getLeftState()
\r
649 return m_receiver->left_active;
\r
651 virtual bool getRightState()
\r
653 return m_receiver->right_active;
\r
656 virtual bool getLeftClicked()
\r
658 return m_receiver->leftclicked;
\r
660 virtual bool getRightClicked()
\r
662 return m_receiver->rightclicked;
\r
664 virtual void resetLeftClicked()
\r
666 m_receiver->leftclicked = false;
\r
668 virtual void resetRightClicked()
\r
670 m_receiver->rightclicked = false;
\r
673 virtual bool getLeftReleased()
\r
675 return m_receiver->leftreleased;
\r
677 virtual bool getRightReleased()
\r
679 return m_receiver->rightreleased;
\r
681 virtual void resetLeftReleased()
\r
683 m_receiver->leftreleased = false;
\r
685 virtual void resetRightReleased()
\r
687 m_receiver->rightreleased = false;
\r
692 resetRightClicked();
\r
693 resetLeftClicked();
\r
696 IrrlichtDevice *m_device;
\r
697 MyEventReceiver *m_receiver;
\r
700 class RandomInputHandler : public InputHandler
\r
703 RandomInputHandler()
\r
705 leftclicked = false;
\r
706 rightclicked = false;
\r
707 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
708 keydown[i] = false;
\r
710 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
712 return keydown[keyCode];
\r
714 virtual v2s32 getMousePos()
\r
718 virtual void setMousePos(s32 x, s32 y)
\r
720 mousepos = v2s32(x,y);
\r
723 virtual bool getLeftState()
\r
727 virtual bool getRightState()
\r
732 virtual bool getLeftClicked()
\r
734 return leftclicked;
\r
736 virtual bool getRightClicked()
\r
738 return rightclicked;
\r
740 virtual void resetLeftClicked()
\r
742 leftclicked = false;
\r
744 virtual void resetRightClicked()
\r
746 rightclicked = false;
\r
749 virtual bool getLeftReleased()
\r
753 virtual bool getRightReleased()
\r
757 virtual void resetLeftReleased()
\r
760 virtual void resetRightReleased()
\r
764 virtual void step(float dtime)
\r
767 static float counter1 = 0;
\r
771 counter1 = 0.1*Rand(1,10);
\r
772 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
773 g_selected_material++;
\r
775 g_selected_material = 0;*/
\r
776 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
779 g_selected_item = 0;
\r
783 static float counter1 = 0;
\r
787 counter1 = 0.1*Rand(1, 40);
\r
788 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
792 static float counter1 = 0;
\r
796 counter1 = 0.1*Rand(1, 40);
\r
797 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
801 static float counter1 = 0;
\r
805 counter1 = 0.1*Rand(1, 40);
\r
806 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
810 static float counter1 = 0;
\r
814 counter1 = 0.1*Rand(1, 40);
\r
815 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
819 static float counter1 = 0;
\r
823 counter1 = 0.1*Rand(1, 20);
\r
824 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
828 static float counter1 = 0;
\r
832 counter1 = 0.1*Rand(1, 30);
\r
833 leftclicked = true;
\r
837 static float counter1 = 0;
\r
841 counter1 = 0.1*Rand(1, 20);
\r
842 rightclicked = true;
\r
845 mousepos += mousespeed;
\r
848 s32 Rand(s32 min, s32 max)
\r
850 return (myrand()%(max-min+1))+min;
\r
853 bool keydown[KEY_KEY_CODES_COUNT];
\r
860 void updateViewingRange(f32 frametime_in, Client *client)
\r
862 if(draw_control.range_all == true)
\r
865 static f32 added_frametime = 0;
\r
866 static s16 added_frames = 0;
\r
868 added_frametime += frametime_in;
\r
871 // Actually this counter kind of sucks because frametime is busytime
\r
872 static f32 counter = 0;
\r
873 counter -= frametime_in;
\r
879 /*dstream<<__FUNCTION_NAME
\r
880 <<": Collected "<<added_frames<<" frames, total of "
\r
881 <<added_frametime<<"s."<<std::endl;*/
\r
883 /*dstream<<"draw_control.blocks_drawn="
\r
884 <<draw_control.blocks_drawn
\r
885 <<", draw_control.blocks_would_have_drawn="
\r
886 <<draw_control.blocks_would_have_drawn
\r
889 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
890 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
892 draw_control.wanted_min_range = range_min;
\r
893 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
895 float block_draw_ratio = 1.0;
\r
896 if(draw_control.blocks_would_have_drawn != 0)
\r
898 block_draw_ratio = (float)draw_control.blocks_drawn
\r
899 / (float)draw_control.blocks_would_have_drawn;
\r
902 // Calculate the average frametime in the case that all wanted
\r
903 // blocks had been drawn
\r
904 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
906 added_frametime = 0.0;
\r
909 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
910 float wanted_frametime = 1.0 / wanted_fps;
\r
912 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
913 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
915 // If needed frametime change is very small, just return
\r
916 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
918 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
922 float range = draw_control.wanted_range;
\r
923 float new_range = range;
\r
925 static s16 range_old = 0;
\r
926 static f32 frametime_old = 0;
\r
928 float d_range = range - range_old;
\r
929 f32 d_frametime = frametime - frametime_old;
\r
930 // A sane default of 30ms per 50 nodes of range
\r
931 static f32 time_per_range = 30. / 50;
\r
934 time_per_range = d_frametime / d_range;
\r
937 // The minimum allowed calculated frametime-range derivative:
\r
938 // Practically this sets the maximum speed of changing the range.
\r
939 // The lower this value, the higher the maximum changing speed.
\r
940 // A low value here results in wobbly range (0.001)
\r
941 // A high value here results in slow changing range (0.0025)
\r
942 // SUGG: This could be dynamically adjusted so that when
\r
943 // the camera is turning, this is lower
\r
944 //float min_time_per_range = 0.0015;
\r
945 float min_time_per_range = 0.0010;
\r
946 //float min_time_per_range = 0.05 / range;
\r
947 if(time_per_range < min_time_per_range)
\r
949 time_per_range = min_time_per_range;
\r
950 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
954 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
957 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
958 // Dampen the change a bit to kill oscillations
\r
959 //wanted_range_change *= 0.9;
\r
960 //wanted_range_change *= 0.75;
\r
961 wanted_range_change *= 0.5;
\r
962 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
964 // If needed range change is very small, just return
\r
965 if(fabs(wanted_range_change) < 0.001)
\r
967 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
971 new_range += wanted_range_change;
\r
972 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
974 //float new_range_unclamped = new_range;
\r
975 if(new_range < range_min)
\r
976 new_range = range_min;
\r
977 if(new_range > range_max)
\r
978 new_range = range_max;
\r
980 /*if(new_range != new_range_unclamped)
\r
981 dstream<<", clamped to "<<new_range<<std::endl;
\r
983 dstream<<std::endl;*/
\r
985 draw_control.wanted_range = new_range;
\r
987 range_old = new_range;
\r
988 frametime_old = frametime;
\r
991 class GUIQuickInventory : public IEventReceiver
\r
995 gui::IGUIEnvironment* env,
\r
996 gui::IGUIElement* parent,
\r
999 Inventory *inventory):
\r
1000 m_itemcount(itemcount),
\r
1001 m_inventory(inventory)
\r
1003 core::rect<s32> imgsize(0,0,48,48);
\r
1004 core::rect<s32> textsize(0,0,48,16);
\r
1005 v2s32 spacing(0, 64);
\r
1006 for(s32 i=0; i<m_itemcount; i++)
\r
1008 m_images.push_back(env->addImage(
\r
1009 imgsize + pos + spacing*i
\r
1011 m_images[i]->setScaleImage(true);
\r
1012 m_texts.push_back(env->addStaticText(
\r
1014 textsize + pos + spacing*i,
\r
1017 m_texts[i]->setBackgroundColor(
\r
1018 video::SColor(128,0,0,0));
\r
1019 m_texts[i]->setTextAlignment(
\r
1020 gui::EGUIA_CENTER,
\r
1021 gui::EGUIA_UPPERLEFT);
\r
1025 virtual bool OnEvent(const SEvent& event)
\r
1030 void setSelection(s32 i)
\r
1039 start = m_selection - m_itemcount / 2;
\r
1041 InventoryList *mainlist = m_inventory->getList("main");
\r
1043 for(s32 i=0; i<m_itemcount; i++)
\r
1045 s32 j = i + start;
\r
1047 if(j > (s32)mainlist->getSize() - 1)
\r
1048 j -= mainlist->getSize();
\r
1050 j += mainlist->getSize();
\r
1052 InventoryItem *item = mainlist->getItem(j);
\r
1056 m_images[i]->setImage(NULL);
\r
1059 if(m_selection == j)
\r
1060 swprintf(t, 10, L"<-");
\r
1062 swprintf(t, 10, L"");
\r
1063 m_texts[i]->setText(t);
\r
1065 // The next ifs will segfault with a NULL pointer
\r
1070 m_images[i]->setImage(item->getImage());
\r
1073 if(m_selection == j)
\r
1074 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1076 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1077 m_texts[i]->setText(t);
\r
1083 core::array<gui::IGUIStaticText*> m_texts;
\r
1084 core::array<gui::IGUIImage*> m_images;
\r
1085 Inventory *m_inventory;
\r
1096 ChatLine(const std::wstring &a_text):
\r
1102 std::wstring text;
\r
1105 int main(int argc, char *argv[])
\r
1108 Low-level initialization
\r
1111 bool disable_stderr = false;
\r
1113 disable_stderr = true;
\r
1116 // Initialize debug streams
\r
1117 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1118 // Initialize debug stacks
\r
1119 debug_stacks_init();
\r
1121 DSTACK(__FUNCTION_NAME);
\r
1123 porting::initializePaths();
\r
1124 // Create user data directory
\r
1125 fs::CreateDir(porting::path_userdata);
\r
1127 initializeMaterialProperties();
\r
1129 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1131 // Print startup message
\r
1132 dstream<<DTIME<<"minetest-c55"
\r
1133 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1134 <<", "<<BUILD_INFO
\r
1141 Parse command line
\r
1144 // List all allowed options
\r
1145 core::map<std::string, ValueSpec> allowed_options;
\r
1146 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1147 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1148 "Run server directly"));
\r
1149 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1150 "Load configuration from specified file"));
\r
1151 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1152 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1153 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1154 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1155 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1156 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1158 Settings cmd_args;
\r
1160 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1162 if(ret == false || cmd_args.getFlag("help"))
\r
1164 dstream<<"Allowed options:"<<std::endl;
\r
1165 for(core::map<std::string, ValueSpec>::Iterator
\r
1166 i = allowed_options.getIterator();
\r
1167 i.atEnd() == false; i++)
\r
1169 dstream<<" --"<<i.getNode()->getKey();
\r
1170 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1175 dstream<<" <value>";
\r
1177 dstream<<std::endl;
\r
1179 if(i.getNode()->getValue().help != NULL)
\r
1181 dstream<<" "<<i.getNode()->getValue().help
\r
1186 return cmd_args.getFlag("help") ? 0 : 1;
\r
1191 Basic initialization
\r
1194 // Initialize default settings
\r
1195 set_default_settings();
\r
1197 // Set locale. This is for forcing '.' as the decimal point.
\r
1198 std::locale::global(std::locale("C"));
\r
1199 // This enables printing all characters in bitmap font
\r
1200 setlocale(LC_CTYPE, "en_US");
\r
1202 // Initialize sockets
\r
1204 atexit(sockets_cleanup);
\r
1214 // Path of configuration file in use
\r
1215 std::string configpath = "";
\r
1217 if(cmd_args.exists("config"))
\r
1219 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1222 dstream<<"Could not read configuration from \""
\r
1223 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1226 configpath = cmd_args.get("config");
\r
1230 core::array<std::string> filenames;
\r
1231 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1232 #ifdef RUN_IN_PLACE
\r
1233 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1236 for(u32 i=0; i<filenames.size(); i++)
\r
1238 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1241 configpath = filenames[i];
\r
1247 // Initialize random seed
\r
1254 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1255 || cmd_args.getFlag("enable-unittests") == true)
\r
1260 // Read map parameters from settings
\r
1262 HMParams hm_params;
\r
1263 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1264 hm_params.randmax = g_settings.get("height_randmax");
\r
1265 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1266 hm_params.base = g_settings.get("height_base");
\r
1268 MapParams map_params;
\r
1269 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1270 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1276 std::cout<<std::endl<<std::endl;
\r
1279 <<" .__ __ __ "<<std::endl
\r
1280 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1281 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1282 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1283 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1284 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1287 std::cout<<std::endl;
\r
1288 //char templine[100];
\r
1292 if(cmd_args.exists("port"))
\r
1294 port = cmd_args.getU16("port");
\r
1298 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1299 std::cout<<"-> "<<port<<std::endl;
\r
1303 std::string map_dir = porting::path_userdata+"/map";
\r
1304 if(cmd_args.exists("map-dir"))
\r
1305 map_dir = cmd_args.get("map-dir");
\r
1306 else if(g_settings.exists("map-dir"))
\r
1307 map_dir = g_settings.get("map-dir");
\r
1309 if(cmd_args.getFlag("server"))
\r
1311 DSTACK("Dedicated server branch");
\r
1313 std::cout<<std::endl;
\r
1314 std::cout<<"========================"<<std::endl;
\r
1315 std::cout<<"Running dedicated server"<<std::endl;
\r
1316 std::cout<<"========================"<<std::endl;
\r
1317 std::cout<<std::endl;
\r
1319 Server server(map_dir, hm_params, map_params);
\r
1320 server.start(port);
\r
1324 // This is kind of a hack but can be done like this
\r
1325 // because server.step() is very light
\r
1327 server.step(0.030);
\r
1329 static int counter = 0;
\r
1335 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1336 core::list<PlayerInfo>::Iterator i;
\r
1337 static u32 sum_old = 0;
\r
1338 u32 sum = PIChecksum(list);
\r
1339 if(sum != sum_old)
\r
1341 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1342 for(i=list.begin(); i!=list.end(); i++)
\r
1344 i->PrintLine(&std::cout);
\r
1354 bool hosting = false;
\r
1355 char connect_name[100] = "";
\r
1357 if(cmd_args.exists("address"))
\r
1359 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1361 else if(is_yes(g_settings.get("host_game")) == false)
\r
1363 if(g_settings.get("address") != "")
\r
1365 std::cout<<g_settings.get("address")<<std::endl;
\r
1366 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1370 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1371 std::cin.getline(connect_name, 100);
\r
1375 if(connect_name[0] == 0){
\r
1376 snprintf(connect_name, 100, "127.0.0.1");
\r
1381 std::cout<<"> Hosting game"<<std::endl;
\r
1383 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1385 char playername[PLAYERNAME_SIZE] = "";
\r
1386 if(g_settings.get("name") != "")
\r
1388 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1392 std::cout<<"Name of player: ";
\r
1393 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1395 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1398 Resolution selection
\r
1401 bool fullscreen = false;
\r
1402 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1403 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1407 MyEventReceiver receiver;
\r
1409 video::E_DRIVER_TYPE driverType;
\r
1412 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1413 driverType = video::EDT_OPENGL;
\r
1415 driverType = video::EDT_OPENGL;
\r
1418 // create device and exit if creation failed
\r
1420 IrrlichtDevice *device;
\r
1421 device = createDevice(driverType,
\r
1422 core::dimension2d<u32>(screenW, screenH),
\r
1423 16, fullscreen, false, false, &receiver);
\r
1426 return 1; // could not create selected driver.
\r
1428 g_device = device;
\r
1429 g_irrlicht = new IrrlichtWrapper(device);
\r
1431 //g_device = device;
\r
1433 device->setResizable(true);
\r
1435 bool random_input = g_settings.getBool("random_input")
\r
1436 || cmd_args.getFlag("random-input");
\r
1438 g_input = new RandomInputHandler();
\r
1440 g_input = new RealInputHandler(device, &receiver);
\r
1443 Continue initialization
\r
1446 video::IVideoDriver* driver = device->getVideoDriver();
\r
1449 This changes the minimum allowed number of vertices in a VBO
\r
1451 //driver->setMinHardwareBufferVertexCount(50);
\r
1453 scene::ISceneManager* smgr = device->getSceneManager();
\r
1455 guienv = device->getGUIEnvironment();
\r
1456 gui::IGUISkin* skin = guienv->getSkin();
\r
1457 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1459 skin->setFont(font);
\r
1461 dstream<<"WARNING: Font file was not found."
\r
1462 " Using default font."<<std::endl;
\r
1463 // If font was not found, this will get us one
\r
1464 font = skin->getFont();
\r
1467 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1468 dstream<<"text_height="<<text_height<<std::endl;
\r
1470 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1471 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1472 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1473 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1474 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1475 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1477 const wchar_t *text = L"Loading and connecting...";
\r
1478 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1479 core::vector2d<s32> textsize(300, text_height);
\r
1480 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1482 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1483 text, textrect, false, false);
\r
1484 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1486 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1487 guienv->drawAll();
\r
1488 driver->endScene();
\r
1491 Preload some textures
\r
1494 init_content_inventory_texture_paths();
\r
1495 init_tile_texture_paths();
\r
1496 tile_materials_preload(g_irrlicht);
\r
1499 Make a scope here for the client so that it gets removed
\r
1500 before the irrlicht device
\r
1504 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1509 SharedPtr<Server> server;
\r
1511 server = new Server(map_dir, hm_params, map_params);
\r
1512 server->start(port);
\r
1519 Client client(device, playername, draw_control);
\r
1521 g_client = &client;
\r
1523 Address connect_address(0,0,0,0, port);
\r
1525 connect_address.Resolve(connect_name);
\r
1527 catch(ResolveError &e)
\r
1529 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1533 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1534 client.connect(connect_address);
\r
1537 while(client.connectedAndInitialized() == false)
\r
1540 if(server != NULL){
\r
1541 server->step(0.1);
\r
1546 catch(con::PeerNotFoundException &e)
\r
1548 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1555 /*scene::ISceneNode* skybox;
\r
1556 skybox = smgr->addSkyBoxSceneNode(
\r
1557 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1558 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1559 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1560 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1561 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1562 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1565 Create the camera node
\r
1568 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1569 0, // Camera parent
\r
1570 v3f(BS*100, BS*2, BS*100), // Look from
\r
1571 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1575 if(camera == NULL)
\r
1578 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1580 camera->setFOV(FOV_ANGLE);
\r
1582 // Just so big a value that everything rendered is visible
\r
1583 camera->setFarValue(100000*BS);
\r
1585 f32 camera_yaw = 0; // "right/left"
\r
1586 f32 camera_pitch = 0; // "up/down"
\r
1592 gui_loadingtext->remove();
\r
1595 Add some gui stuff
\r
1598 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1599 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1602 We need some kind of a root node to be able to add
\r
1603 custom elements directly on the screen.
\r
1604 Otherwise they won't be automatically drawn.
\r
1606 guiroot = guienv->addStaticText(L"",
\r
1607 core::rect<s32>(0, 0, 10000, 10000));
\r
1609 // Test the text input system
\r
1610 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1612 /*GUIMessageMenu *menu =
\r
1613 new GUIMessageMenu(guienv, guiroot, -1,
\r
1614 &g_active_menu_count,
\r
1618 // Launch pause menu
\r
1619 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1620 &g_active_menu_count))->drop();
\r
1622 // First line of debug text
\r
1623 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1625 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1627 // Second line of debug text
\r
1628 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1630 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1633 // At the middle of the screen
\r
1634 // Object infos are shown in this
\r
1635 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1637 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1641 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1642 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1643 core::rect<s32>(70, 60, 795, 150),
\r
1645 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1646 core::list<ChatLine> chat_lines;
\r
1649 Some statistics are collected in these
\r
1652 u32 beginscenetime = 0;
\r
1653 u32 scenetime = 0;
\r
1654 u32 endscenetime = 0;
\r
1657 //throw con::PeerNotFoundException("lol");
\r
1663 bool first_loop_after_window_activation = true;
\r
1665 // Time is in milliseconds
\r
1666 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1667 // NOTE: So we have to use getTime() and call run()s between them
\r
1668 u32 lasttime = device->getTimer()->getTime();
\r
1670 while(device->run())
\r
1673 Run global IrrlichtWrapper's main thread processing stuff
\r
1675 g_irrlicht->Run();
\r
1678 Random calculations
\r
1680 v2u32 screensize = driver->getScreenSize();
\r
1681 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1683 // Hilight boxes collected during the loop and displayed
\r
1684 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1687 std::wstring infotext;
\r
1689 //TimeTaker //timer1("//timer1");
\r
1691 // Time of frame without fps limit
\r
1695 // not using getRealTime is necessary for wine
\r
1696 u32 time = device->getTimer()->getTime();
\r
1697 if(time > lasttime)
\r
1698 busytime_u32 = time - lasttime;
\r
1701 busytime = busytime_u32 / 1000.0;
\r
1704 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1706 // Absolutelu necessary for wine!
\r
1713 updateViewingRange(busytime, &client);
\r
1720 float fps_max = g_settings.getFloat("fps_max");
\r
1721 u32 frametime_min = 1000./fps_max;
\r
1723 if(busytime_u32 < frametime_min)
\r
1725 u32 sleeptime = frametime_min - busytime_u32;
\r
1726 device->sleep(sleeptime);
\r
1730 // Absolutelu necessary for wine!
\r
1734 Time difference calculation
\r
1736 f32 dtime; // in seconds
\r
1738 u32 time = device->getTimer()->getTime();
\r
1739 if(time > lasttime)
\r
1740 dtime = (time - lasttime) / 1000.0;
\r
1746 Time average and jitter calculation
\r
1749 static f32 dtime_avg1 = 0.0;
\r
1750 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1751 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1753 static f32 dtime_jitter1_max_sample = 0.0;
\r
1754 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1756 static f32 jitter1_max = 0.0;
\r
1757 static f32 counter = 0.0;
\r
1758 if(dtime_jitter1 > jitter1_max)
\r
1759 jitter1_max = dtime_jitter1;
\r
1764 dtime_jitter1_max_sample = jitter1_max;
\r
1765 dtime_jitter1_max_fraction
\r
1766 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1767 jitter1_max = 0.0;
\r
1770 Control freetime ratio
\r
1772 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1774 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1775 g_freetime_ratio += 0.01;
\r
1779 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1780 g_freetime_ratio -= 0.01;
\r
1786 Busytime average and jitter calculation
\r
1789 static f32 busytime_avg1 = 0.0;
\r
1790 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1791 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1793 static f32 busytime_jitter1_max_sample = 0.0;
\r
1794 static f32 busytime_jitter1_min_sample = 0.0;
\r
1796 static f32 jitter1_max = 0.0;
\r
1797 static f32 jitter1_min = 0.0;
\r
1798 static f32 counter = 0.0;
\r
1799 if(busytime_jitter1 > jitter1_max)
\r
1800 jitter1_max = busytime_jitter1;
\r
1801 if(busytime_jitter1 < jitter1_min)
\r
1802 jitter1_min = busytime_jitter1;
\r
1804 if(counter > 0.0){
\r
1806 busytime_jitter1_max_sample = jitter1_max;
\r
1807 busytime_jitter1_min_sample = jitter1_min;
\r
1808 jitter1_max = 0.0;
\r
1809 jitter1_min = 0.0;
\r
1814 Debug info for client
\r
1817 static float counter = 0.0;
\r
1822 client.printDebugInfo(std::cout);
\r
1827 Input handler step()
\r
1829 g_input->step(dtime);
\r
1832 Player speed control
\r
1841 bool a_superspeed,
\r
1844 PlayerControl control(
\r
1845 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1846 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1847 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1848 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1849 g_input->isKeyDown(irr::KEY_SPACE),
\r
1850 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1854 client.setPlayerControl(control);
\r
1858 Process environment
\r
1862 //TimeTaker timer("client.step(dtime)");
\r
1863 client.step(dtime);
\r
1864 //client.step(dtime_avg1);
\r
1867 if(server != NULL)
\r
1869 //TimeTaker timer("server->step(dtime)");
\r
1870 server->step(dtime);
\r
1873 v3f player_position = client.getPlayerPosition();
\r
1875 //TimeTaker //timer2("//timer2");
\r
1878 Mouse and camera control
\r
1881 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1884 device->getCursorControl()->setVisible(false);
\r
1886 if(first_loop_after_window_activation){
\r
1887 //std::cout<<"window active, first loop"<<std::endl;
\r
1888 first_loop_after_window_activation = false;
\r
1891 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1892 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1893 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1894 camera_yaw -= dx*0.2;
\r
1895 camera_pitch += dy*0.2;
\r
1896 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1897 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1899 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1902 device->getCursorControl()->setVisible(true);
\r
1904 //std::cout<<"window inactive"<<std::endl;
\r
1905 first_loop_after_window_activation = true;
\r
1908 camera_yaw = wrapDegrees(camera_yaw);
\r
1909 camera_pitch = wrapDegrees(camera_pitch);
\r
1911 v3f camera_direction = v3f(0,0,1);
\r
1912 camera_direction.rotateYZBy(camera_pitch);
\r
1913 camera_direction.rotateXZBy(camera_yaw);
\r
1915 // This is at the height of the eyes of the current figure
\r
1916 v3f camera_position =
\r
1917 player_position + v3f(0, BS+BS/2, 0);
\r
1918 // This is more like in minecraft
\r
1919 /*v3f camera_position =
\r
1920 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1922 camera->setPosition(camera_position);
\r
1923 // *100.0 helps in large map coordinates
\r
1924 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1926 if(FIELD_OF_VIEW_TEST){
\r
1927 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1928 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1931 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1932 //TimeTaker timer("client.updateCamera");
\r
1933 client.updateCamera(camera_position, camera_direction);
\r
1937 //TimeTaker //timer3("//timer3");
\r
1940 Calculate what block is the crosshair pointing to
\r
1943 //u32 t1 = device->getTimer()->getRealTime();
\r
1945 //f32 d = 4; // max. distance
\r
1946 f32 d = 4; // max. distance
\r
1947 core::line3d<f32> shootline(camera_position,
\r
1948 camera_position + camera_direction * BS * (d+1));
\r
1950 MapBlockObject *selected_object = client.getSelectedObject
\r
1951 (d*BS, camera_position, shootline);
\r
1954 If it's pointing to a MapBlockObject
\r
1957 if(selected_object != NULL)
\r
1959 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1961 core::aabbox3d<f32> box_on_map
\r
1962 = selected_object->getSelectionBoxOnMap();
\r
1964 hilightboxes.push_back(box_on_map);
\r
1966 infotext = narrow_to_wide(selected_object->infoText());
\r
1968 if(g_input->getLeftClicked())
\r
1970 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1971 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1972 selected_object->getId(), g_selected_item);
\r
1974 else if(g_input->getRightClicked())
\r
1976 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1978 Check if we want to modify the object ourselves
\r
1980 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1982 dstream<<"Sign object right-clicked"<<std::endl;
\r
1984 if(random_input == false)
\r
1986 // Get a new text for it
\r
1988 TextDest *dest = new TextDestSign(
\r
1989 selected_object->getBlock()->getPos(),
\r
1990 selected_object->getId(),
\r
1993 SignObject *sign_object = (SignObject*)selected_object;
\r
1995 std::wstring wtext =
\r
1996 narrow_to_wide(sign_object->getText());
\r
1998 (new GUITextInputMenu(guienv, guiroot, -1,
\r
1999 &g_active_menu_count, dest,
\r
2004 Otherwise pass the event to the server as-is
\r
2008 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2009 selected_object->getId(), g_selected_item);
\r
2013 else // selected_object == NULL
\r
2017 Find out which node we are pointing at
\r
2020 bool nodefound = false;
\r
2022 v3s16 neighbourpos;
\r
2023 core::aabbox3d<f32> nodefacebox;
\r
2024 f32 mindistance = BS * 1001;
\r
2026 v3s16 pos_i = floatToInt(player_position);
\r
2028 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2032 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2033 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2034 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2035 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2036 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2037 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2039 for(s16 y = ystart; y <= yend; y++)
\r
2040 for(s16 z = zstart; z <= zend; z++)
\r
2041 for(s16 x = xstart; x <= xend; x++)
\r
2046 n = client.getNode(v3s16(x,y,z));
\r
2047 if(content_pointable(n.d) == false)
\r
2050 catch(InvalidPositionException &e)
\r
2056 v3f npf = intToFloat(np);
\r
2061 v3s16(0,0,1), // back
\r
2062 v3s16(0,1,0), // top
\r
2063 v3s16(1,0,0), // right
\r
2064 v3s16(0,0,-1), // front
\r
2065 v3s16(0,-1,0), // bottom
\r
2066 v3s16(-1,0,0), // left
\r
2072 if(n.d == CONTENT_TORCH)
\r
2074 v3s16 dir = unpackDir(n.dir);
\r
2075 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2076 dir_f *= BS/2 - BS/6 - BS/20;
\r
2077 v3f cpf = npf + dir_f;
\r
2078 f32 distance = (cpf - camera_position).getLength();
\r
2080 core::aabbox3d<f32> box;
\r
2083 if(dir == v3s16(0,-1,0))
\r
2085 box = core::aabbox3d<f32>(
\r
2086 npf - v3f(BS/6, BS/2, BS/6),
\r
2087 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2091 else if(dir == v3s16(0,1,0))
\r
2093 box = core::aabbox3d<f32>(
\r
2094 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2095 npf + v3f(BS/6, BS/2, BS/6)
\r
2101 box = core::aabbox3d<f32>(
\r
2102 cpf - v3f(BS/6, BS/3, BS/6),
\r
2103 cpf + v3f(BS/6, BS/3, BS/6)
\r
2107 if(distance < mindistance)
\r
2109 if(box.intersectsWithLine(shootline))
\r
2113 neighbourpos = np;
\r
2114 mindistance = distance;
\r
2115 nodefacebox = box;
\r
2124 for(u16 i=0; i<6; i++)
\r
2126 v3f dir_f = v3f(dirs[i].X,
\r
2127 dirs[i].Y, dirs[i].Z);
\r
2128 v3f centerpoint = npf + dir_f * BS/2;
\r
2130 (centerpoint - camera_position).getLength();
\r
2132 if(distance < mindistance)
\r
2134 core::CMatrix4<f32> m;
\r
2135 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2137 // This is the back face
\r
2138 v3f corners[2] = {
\r
2139 v3f(BS/2, BS/2, BS/2),
\r
2140 v3f(-BS/2, -BS/2, BS/2+d)
\r
2143 for(u16 j=0; j<2; j++)
\r
2145 m.rotateVect(corners[j]);
\r
2146 corners[j] += npf;
\r
2149 core::aabbox3d<f32> facebox(corners[0]);
\r
2150 facebox.addInternalPoint(corners[1]);
\r
2152 if(facebox.intersectsWithLine(shootline))
\r
2156 neighbourpos = np + dirs[i];
\r
2157 mindistance = distance;
\r
2158 nodefacebox = facebox;
\r
2160 } // if distance < mindistance
\r
2162 } // regular block
\r
2165 static float nodig_delay_counter = 0.0;
\r
2169 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2171 static float dig_time = 0.0;
\r
2172 static u16 dig_index = 0;
\r
2174 hilightboxes.push_back(nodefacebox);
\r
2176 if(g_input->getLeftReleased())
\r
2178 client.clearTempMod(nodepos);
\r
2182 if(nodig_delay_counter > 0.0)
\r
2184 nodig_delay_counter -= dtime;
\r
2188 if(nodepos != nodepos_old)
\r
2190 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2191 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2193 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2195 client.clearTempMod(nodepos_old);
\r
2200 if(g_input->getLeftClicked() ||
\r
2201 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2203 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2204 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2206 if(g_input->getLeftClicked())
\r
2208 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2210 if(g_input->getLeftState())
\r
2212 MapNode n = client.getNode(nodepos);
\r
2214 // Get tool name. Default is "" = bare hands
\r
2215 std::string toolname = "";
\r
2216 InventoryList *mlist = local_inventory.getList("main");
\r
2219 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2220 if(item && (std::string)item->getName() == "ToolItem")
\r
2222 ToolItem *titem = (ToolItem*)item;
\r
2223 toolname = titem->getToolName();
\r
2227 // Get digging properties for material and tool
\r
2228 u8 material = n.d;
\r
2229 DiggingProperties prop =
\r
2230 getDiggingProperties(material, toolname);
\r
2232 float dig_time_complete = 0.0;
\r
2234 if(prop.diggable == false)
\r
2236 /*dstream<<"Material "<<(int)material
\r
2237 <<" not diggable with \""
\r
2238 <<toolname<<"\""<<std::endl;*/
\r
2239 // I guess nobody will wait for this long
\r
2240 dig_time_complete = 10000000.0;
\r
2244 dig_time_complete = prop.time;
\r
2247 if(dig_time_complete >= 0.001)
\r
2249 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2250 * dig_time/dig_time_complete);
\r
2252 // This is for torches
\r
2255 dig_index = CRACK_ANIMATION_LENGTH;
\r
2258 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2260 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2261 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2265 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2266 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2267 client.clearTempMod(nodepos);
\r
2268 client.removeNode(nodepos);
\r
2272 nodig_delay_counter = dig_time_complete
\r
2273 / (float)CRACK_ANIMATION_LENGTH;
\r
2275 // We don't want a corresponding delay to
\r
2276 // very time consuming nodes
\r
2277 if(nodig_delay_counter > 0.5)
\r
2279 nodig_delay_counter = 0.5;
\r
2281 // We want a slight delay to very little
\r
2282 // time consuming nodes
\r
2283 //float mindelay = 0.15;
\r
2284 float mindelay = 0.20;
\r
2285 if(nodig_delay_counter < mindelay)
\r
2287 nodig_delay_counter = mindelay;
\r
2291 dig_time += dtime;
\r
2295 if(g_input->getRightClicked())
\r
2297 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2298 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2301 nodepos_old = nodepos;
\r
2306 } // selected_object == NULL
\r
2308 g_input->resetLeftClicked();
\r
2309 g_input->resetRightClicked();
\r
2311 if(g_input->getLeftReleased())
\r
2313 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2315 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2317 if(g_input->getRightReleased())
\r
2319 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2323 g_input->resetLeftReleased();
\r
2324 g_input->resetRightReleased();
\r
2327 Calculate stuff for drawing
\r
2330 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2332 u32 daynight_ratio = client.getDayNightRatio();
\r
2333 /*video::SColor bgcolor = video::SColor(
\r
2335 skycolor.getRed() * daynight_ratio / 1000,
\r
2336 skycolor.getGreen() * daynight_ratio / 1000,
\r
2337 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2339 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2340 video::SColor bgcolor = video::SColor(
\r
2342 skycolor.getRed() * l / 255,
\r
2343 skycolor.getGreen() * l / 255,
\r
2344 skycolor.getBlue() * l / 255);
\r
2350 if(g_settings.getBool("enable_fog") == true)
\r
2352 f32 range = draw_control.wanted_range * BS;
\r
2353 if(draw_control.range_all)
\r
2354 range = 100000*BS;
\r
2358 video::EFT_FOG_LINEAR,
\r
2362 false, // pixel fog
\r
2363 false // range fog
\r
2369 Update gui stuff (0ms)
\r
2372 //TimeTaker guiupdatetimer("Gui updating");
\r
2375 wchar_t temptext[150];
\r
2377 static float drawtime_avg = 0;
\r
2378 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2379 static float beginscenetime_avg = 0;
\r
2380 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2381 static float scenetime_avg = 0;
\r
2382 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2383 static float endscenetime_avg = 0;
\r
2384 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2386 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2388 L", R: range_all=%i"
\r
2390 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2392 draw_control.range_all,
\r
2394 beginscenetime_avg,
\r
2399 guitext->setText(temptext);
\r
2403 wchar_t temptext[150];
\r
2404 swprintf(temptext, 150,
\r
2405 L"(% .1f, % .1f, % .1f)"
\r
2406 L" (% .3f < btime_jitter < % .3f"
\r
2407 L", dtime_jitter = % .1f %%"
\r
2408 L", v_range = %.1f)",
\r
2409 player_position.X/BS,
\r
2410 player_position.Y/BS,
\r
2411 player_position.Z/BS,
\r
2412 busytime_jitter1_min_sample,
\r
2413 busytime_jitter1_max_sample,
\r
2414 dtime_jitter1_max_fraction * 100.0,
\r
2415 draw_control.wanted_range
\r
2418 guitext2->setText(temptext);
\r
2422 guitext_info->setText(infotext.c_str());
\r
2426 Get chat messages from client
\r
2429 // Get new messages
\r
2430 std::wstring message;
\r
2431 while(client.getChatMessage(message))
\r
2433 chat_lines.push_back(ChatLine(message));
\r
2434 /*if(chat_lines.size() > 6)
\r
2436 core::list<ChatLine>::Iterator
\r
2437 i = chat_lines.begin();
\r
2438 chat_lines.erase(i);
\r
2441 // Append them to form the whole static text and throw
\r
2442 // it to the gui element
\r
2443 std::wstring whole;
\r
2444 // This will correspond to the line number counted from
\r
2445 // top to bottom, from size-1 to 0
\r
2446 s16 line_number = chat_lines.size();
\r
2447 // Count of messages to be removed from the top
\r
2448 u16 to_be_removed_count = 0;
\r
2449 for(core::list<ChatLine>::Iterator
\r
2450 i = chat_lines.begin();
\r
2451 i != chat_lines.end(); i++)
\r
2453 // After this, line number is valid for this loop
\r
2456 (*i).age += dtime;
\r
2458 This results in a maximum age of 60*6 to the
\r
2459 lowermost line and a maximum of 6 lines
\r
2461 float allowed_age = (6-line_number) * 60.0;
\r
2463 if((*i).age > allowed_age)
\r
2465 to_be_removed_count++;
\r
2468 whole += (*i).text + L'\n';
\r
2470 for(u16 i=0; i<to_be_removed_count; i++)
\r
2472 core::list<ChatLine>::Iterator
\r
2473 it = chat_lines.begin();
\r
2474 chat_lines.erase(it);
\r
2476 chat_guitext->setText(whole.c_str());
\r
2477 // Update gui element size and position
\r
2478 core::rect<s32> rect(
\r
2480 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2481 screensize.X - 10,
\r
2484 chat_guitext->setRelativePosition(rect);
\r
2486 if(chat_lines.size() == 0)
\r
2487 chat_guitext->setVisible(false);
\r
2489 chat_guitext->setVisible(true);
\r
2496 static u16 old_selected_item = 65535;
\r
2497 if(client.getLocalInventoryUpdated()
\r
2498 || g_selected_item != old_selected_item)
\r
2500 old_selected_item = g_selected_item;
\r
2501 //std::cout<<"Updating local inventory"<<std::endl;
\r
2502 client.getLocalInventory(local_inventory);
\r
2503 quick_inventory->setSelection(g_selected_item);
\r
2504 quick_inventory->update();
\r
2508 Send actions returned by the inventory menu
\r
2510 while(inventory_action_queue.size() != 0)
\r
2512 InventoryAction *a = inventory_action_queue.pop_front();
\r
2514 client.sendInventoryAction(a);
\r
2523 TimeTaker drawtimer("Drawing");
\r
2527 TimeTaker timer("beginScene");
\r
2528 driver->beginScene(true, true, bgcolor);
\r
2529 //driver->beginScene(false, true, bgcolor);
\r
2530 beginscenetime = timer.stop(true);
\r
2535 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2538 TimeTaker timer("smgr");
\r
2540 scenetime = timer.stop(true);
\r
2544 //TimeTaker timer9("auxiliary drawings");
\r
2548 //TimeTaker //timer10("//timer10");
\r
2550 video::SMaterial m;
\r
2552 m.Lighting = false;
\r
2553 driver->setMaterial(m);
\r
2555 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2557 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2558 i != hilightboxes.end(); i++)
\r
2560 /*std::cout<<"hilightbox min="
\r
2561 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2563 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2565 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2571 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2572 displaycenter + core::vector2d<s32>(10,0),
\r
2573 video::SColor(255,255,255,255));
\r
2574 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2575 displaycenter + core::vector2d<s32>(0,10),
\r
2576 video::SColor(255,255,255,255));
\r
2581 //TimeTaker //timer11("//timer11");
\r
2587 guienv->drawAll();
\r
2591 TimeTaker timer("endScene");
\r
2592 driver->endScene();
\r
2593 endscenetime = timer.stop(true);
\r
2596 drawtime = drawtimer.stop(true);
\r
2602 static s16 lastFPS = 0;
\r
2603 //u16 fps = driver->getFPS();
\r
2604 u16 fps = (1.0/dtime_avg1);
\r
2606 if (lastFPS != fps)
\r
2608 core::stringw str = L"Minetest [";
\r
2609 str += driver->getName();
\r
2613 device->setWindowCaption(str.c_str());
\r
2619 device->yield();*/
\r
2622 delete quick_inventory;
\r
2624 } // client is deleted at this point
\r
2629 In the end, delete the Irrlicht device.
\r
2634 Update configuration file
\r
2636 /*if(configpath != "")
\r
2638 g_settings.updateConfigFile(configpath.c_str());
\r
2642 catch(con::PeerNotFoundException &e)
\r
2644 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2648 GUIMessageMenu *menu =
\r
2649 new GUIMessageMenu(guienv, guiroot, -1,
\r
2650 &g_active_menu_count,
\r
2651 L"Connection timed out");
\r
2653 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2655 dstream<<"Created menu"<<std::endl;
\r
2657 while(g_device->run() && menu->getStatus() == false)
\r
2659 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2660 guienv->drawAll();
\r
2661 driver->endScene();
\r
2664 dstream<<"Dropping menu"<<std::endl;
\r
2670 END_DEBUG_EXCEPTION_HANDLER
\r
2672 debugstreams_deinit();
\r