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
121 TODO: Optimize day/night mesh updating somehow
\r
122 - create copies of all textures for all lighting values and only
\r
123 change texture for material?
\r
124 - Umm... the collecting of the faces is the slow part
\r
125 -> what about just changing the color values of the existing
\r
126 meshbuffers? It should go quite fast.
\r
127 - This is not easy; There'd need to be a buffer somewhere
\r
128 that would contain the night and day lighting values.
\r
130 TODO: Draw big amounts of torches better (that is, throw them in the
\r
131 same meshbuffer (can the meshcollector class be used?))
\r
133 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
135 - That is >500 vertices
\r
136 - This is not easy; all the MapBlocks close to the player would
\r
137 still need to be drawn separately and combining the blocks
\r
138 would have to happen in a background thread
\r
140 TODO: Make fetching sector's blocks more efficient when rendering
\r
141 sectors that have very large amounts of blocks (on client)
\r
142 - Is this necessary at all?
\r
146 TODO: Make the video backend selectable
\r
150 TODO: Untie client network operations from framerate
\r
151 - Needs some input queues or something
\r
152 - Not really necessary?
\r
156 TODO: When player dies, throw items on map
\r
158 TODO: Make an option to the server to disable building and digging near
\r
159 the starting position
\r
161 TODO: Players to only be hidden when the client quits.
\r
162 TODO: - Players to be saved on disk, with inventory
\r
163 TODO: Players to be saved as text in map/players/<name>
\r
164 TODO: Player inventory to be saved on disk
\r
166 TODO: Copy the text of the last picked sign to inventory in creative
\r
169 TODO: Check what goes wrong with caching map to disk (Kray)
\r
172 TODO: When server sees that client is removing an inexistent block or
\r
173 adding a block to an existent position, resend the MapBlock.
\r
175 TODO: Save players with inventories to disk
\r
177 TODO: Make water more like in minecraft
\r
181 TODO: Better handling of objects and mobs
\r
183 - There has to be some way to do it with less messy code
\r
184 - Make separate classes for client and server
\r
185 - Client should not discriminate between blocks, server should
\r
186 - Make other players utilize the same framework
\r
187 - This is also needed for objects that don't get sent to client
\r
188 but are used for triggers etc
\r
190 TODO: There has to be some better way to handle static objects than to
\r
191 send them all the time. This affects signs and item objects.
\r
192 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
193 need an additional metadata field for the texts
\r
194 - This is also needed for item container chests
\r
196 Block object server side:
\r
197 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
198 - For all blocks in the buffer, objects are stepped(). This
\r
199 means they are active.
\r
200 - TODO: A global active buffer is needed for the server
\r
201 - TODO: A timestamp to blocks
\r
202 - TODO: All blocks going in and out of the buffer are recorded.
\r
203 - TODO: For outgoing blocks, timestamp is written.
\r
204 - TODO: For incoming blocks, time difference is calculated and
\r
205 objects are stepped according to it.
\r
209 NOTE: There are some lighting-related todos and fixmes in
\r
210 ServerMap::emergeBlock. And there always will be. 8)
\r
212 TODO: Map generator version 2
\r
213 - Create surface areas based on central points; a given point's
\r
214 area type is given by the nearest central point
\r
215 - Separate points for heightmap, caves, plants and minerals?
\r
216 - Flat land, mountains, forest, jungle
\r
218 - There could be a certain height (to which mountains only reach)
\r
219 where some minerals are found
\r
221 FIXME: The new pre-sunlight-propagation code messes up with initial
\r
225 ======================================================================
\r
227 ======================================================================
\r
232 Setting this to 1 enables a special camera mode that forces
\r
233 the renderers to think that the camera statically points from
\r
234 the starting place to a static direction.
\r
236 This allows one to move around with the player and see what
\r
237 is actually drawn behind solid things and behind the player.
\r
239 #define FIELD_OF_VIEW_TEST 0
\r
243 #pragma message ("Disabling unit tests")
\r
245 #warning "Disabling unit tests"
\r
247 // Disable unit tests
\r
248 #define ENABLE_TESTS 0
\r
250 // Enable unit tests
\r
251 #define ENABLE_TESTS 1
\r
255 #pragma comment(lib, "Irrlicht.lib")
\r
256 #pragma comment(lib, "jthread.lib")
\r
257 #pragma comment(lib, "zlibwapi.lib")
\r
258 // This would get rid of the console window
\r
259 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
262 #include <iostream>
\r
264 #include <jmutexautolock.h>
\r
265 #include <locale.h>
\r
266 #include "common_irrlicht.h"
\r
269 #include "player.h"
\r
272 #include "environment.h"
\r
273 #include "server.h"
\r
274 #include "client.h"
\r
275 #include "serialization.h"
\r
276 #include "constants.h"
\r
277 #include "strfnd.h"
\r
278 #include "porting.h"
\r
279 #include "irrlichtwrapper.h"
\r
280 #include "gettime.h"
\r
281 #include "porting.h"
\r
282 #include "guiPauseMenu.h"
\r
283 #include "guiInventoryMenu.h"
\r
284 #include "guiTextInputMenu.h"
\r
285 #include "materials.h"
\r
286 #include "guiMessageMenu.h"
\r
287 #include "filesys.h"
\r
288 #include "config.h"
\r
290 IrrlichtWrapper *g_irrlicht;
\r
292 MapDrawControl draw_control;
\r
296 These are loaded from the config file.
\r
299 Settings g_settings;
\r
301 extern void set_default_settings();
\r
307 IrrlichtDevice *g_device = NULL;
\r
308 Client *g_client = NULL;
\r
313 gui::IGUIEnvironment* guienv = NULL;
\r
314 gui::IGUIStaticText *guiroot = NULL;
\r
315 int g_active_menu_count = 0;
\r
317 bool noMenuActive()
\r
319 return (g_active_menu_count == 0);
\r
322 // Inventory actions from the menu are buffered here before sending
\r
323 Queue<InventoryAction*> inventory_action_queue;
\r
324 // This is a copy of the inventory that the client's environment has
\r
325 Inventory local_inventory;
\r
327 u16 g_selected_item = 0;
\r
334 std::ostream *dout_con_ptr = &dummyout;
\r
335 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
336 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
337 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
338 //std::ostream *dout_con_ptr = &dstream;
\r
339 //std::ostream *derr_con_ptr = &dstream;
\r
342 std::ostream *dout_server_ptr = &dstream;
\r
343 std::ostream *derr_server_ptr = &dstream;
\r
346 std::ostream *dout_client_ptr = &dstream;
\r
347 std::ostream *derr_client_ptr = &dstream;
\r
350 gettime.h implementation
\r
356 Use irrlicht because it is more precise than porting.h's
\r
359 if(g_irrlicht == NULL)
\r
361 return g_irrlicht->getTime();
\r
368 struct TextDestSign : public TextDest
\r
370 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
372 m_blockpos = blockpos;
\r
376 void gotText(std::wstring text)
\r
378 std::string ntext = wide_to_narrow(text);
\r
379 dstream<<"Changing text of a sign object: "
\r
380 <<ntext<<std::endl;
\r
381 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
389 struct TextDestChat : public TextDest
\r
391 TextDestChat(Client *client)
\r
395 void gotText(std::wstring text)
\r
397 m_client->sendChatMessage(text);
\r
398 m_client->addChatMessage(text);
\r
404 class MyEventReceiver : public IEventReceiver
\r
407 // This is the one method that we have to implement
\r
408 virtual bool OnEvent(const SEvent& event)
\r
411 React to nothing here if a menu is active
\r
413 if(noMenuActive() == false)
\r
419 // Remember whether each key is down or up
\r
420 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
422 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
424 if(event.KeyInput.PressedDown)
\r
426 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
432 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
434 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
436 dstream<<DTIME<<"MyEventReceiver: "
\r
437 <<"Launching pause menu"<<std::endl;
\r
438 // It will delete itself by itself
\r
439 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
440 &g_active_menu_count))->drop();
\r
443 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
445 dstream<<DTIME<<"MyEventReceiver: "
\r
446 <<"Launching inventory"<<std::endl;
\r
447 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
448 &local_inventory, &inventory_action_queue,
\r
449 &g_active_menu_count))->drop();
\r
452 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
454 TextDest *dest = new TextDestChat(g_client);
\r
456 (new GUITextInputMenu(guienv, guiroot, -1,
\r
457 &g_active_menu_count, dest,
\r
462 // Material selection
\r
463 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
465 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
468 g_selected_item = 0;
\r
469 dstream<<DTIME<<"Selected item: "
\r
470 <<g_selected_item<<std::endl;
\r
473 // Viewing range selection
\r
474 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
476 if(draw_control.range_all)
\r
478 draw_control.range_all = false;
\r
479 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
483 draw_control.range_all = true;
\r
484 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
488 // Print debug stacks
\r
489 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
491 dstream<<"-----------------------------------------"
\r
493 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
494 dstream<<"-----------------------------------------"
\r
496 debug_stacks_print();
\r
501 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
503 if(noMenuActive() == false)
\r
505 left_active = false;
\r
506 middle_active = false;
\r
507 right_active = false;
\r
511 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
512 left_active = event.MouseInput.isLeftPressed();
\r
513 middle_active = event.MouseInput.isMiddlePressed();
\r
514 right_active = event.MouseInput.isRightPressed();
\r
516 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
518 leftclicked = true;
\r
520 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
522 rightclicked = true;
\r
524 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
526 leftreleased = true;
\r
528 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
530 rightreleased = true;
\r
532 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
534 /*dstream<<"event.MouseInput.Wheel="
\r
535 <<event.MouseInput.Wheel<<std::endl;*/
\r
536 if(event.MouseInput.Wheel < 0)
\r
538 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
541 g_selected_item = 0;
\r
543 else if(event.MouseInput.Wheel > 0)
\r
545 if(g_selected_item > 0)
\r
548 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
557 // This is used to check whether a key is being held down
\r
558 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
560 return keyIsDown[keyCode];
\r
565 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
566 keyIsDown[i] = false;
\r
568 leftclicked = false;
\r
569 rightclicked = false;
\r
570 leftreleased = false;
\r
571 rightreleased = false;
\r
573 left_active = false;
\r
574 middle_active = false;
\r
575 right_active = false;
\r
586 bool rightreleased;
\r
589 bool middle_active;
\r
593 // We use this array to store the current state of each key
\r
594 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
597 IrrlichtDevice *m_device;
\r
606 virtual ~InputHandler()
\r
610 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
612 virtual v2s32 getMousePos() = 0;
\r
613 virtual void setMousePos(s32 x, s32 y) = 0;
\r
615 virtual bool getLeftState() = 0;
\r
616 virtual bool getRightState() = 0;
\r
618 virtual bool getLeftClicked() = 0;
\r
619 virtual bool getRightClicked() = 0;
\r
620 virtual void resetLeftClicked() = 0;
\r
621 virtual void resetRightClicked() = 0;
\r
623 virtual bool getLeftReleased() = 0;
\r
624 virtual bool getRightReleased() = 0;
\r
625 virtual void resetLeftReleased() = 0;
\r
626 virtual void resetRightReleased() = 0;
\r
628 virtual void step(float dtime) {};
\r
630 virtual void clear() {};
\r
633 InputHandler *g_input = NULL;
\r
635 class RealInputHandler : public InputHandler
\r
638 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
640 m_receiver(receiver)
\r
643 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
645 return m_receiver->IsKeyDown(keyCode);
\r
647 virtual v2s32 getMousePos()
\r
649 return m_device->getCursorControl()->getPosition();
\r
651 virtual void setMousePos(s32 x, s32 y)
\r
653 m_device->getCursorControl()->setPosition(x, y);
\r
656 virtual bool getLeftState()
\r
658 return m_receiver->left_active;
\r
660 virtual bool getRightState()
\r
662 return m_receiver->right_active;
\r
665 virtual bool getLeftClicked()
\r
667 return m_receiver->leftclicked;
\r
669 virtual bool getRightClicked()
\r
671 return m_receiver->rightclicked;
\r
673 virtual void resetLeftClicked()
\r
675 m_receiver->leftclicked = false;
\r
677 virtual void resetRightClicked()
\r
679 m_receiver->rightclicked = false;
\r
682 virtual bool getLeftReleased()
\r
684 return m_receiver->leftreleased;
\r
686 virtual bool getRightReleased()
\r
688 return m_receiver->rightreleased;
\r
690 virtual void resetLeftReleased()
\r
692 m_receiver->leftreleased = false;
\r
694 virtual void resetRightReleased()
\r
696 m_receiver->rightreleased = false;
\r
701 resetRightClicked();
\r
702 resetLeftClicked();
\r
705 IrrlichtDevice *m_device;
\r
706 MyEventReceiver *m_receiver;
\r
709 class RandomInputHandler : public InputHandler
\r
712 RandomInputHandler()
\r
714 leftclicked = false;
\r
715 rightclicked = false;
\r
716 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
717 keydown[i] = false;
\r
719 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
721 return keydown[keyCode];
\r
723 virtual v2s32 getMousePos()
\r
727 virtual void setMousePos(s32 x, s32 y)
\r
729 mousepos = v2s32(x,y);
\r
732 virtual bool getLeftState()
\r
736 virtual bool getRightState()
\r
741 virtual bool getLeftClicked()
\r
743 return leftclicked;
\r
745 virtual bool getRightClicked()
\r
747 return rightclicked;
\r
749 virtual void resetLeftClicked()
\r
751 leftclicked = false;
\r
753 virtual void resetRightClicked()
\r
755 rightclicked = false;
\r
758 virtual bool getLeftReleased()
\r
762 virtual bool getRightReleased()
\r
766 virtual void resetLeftReleased()
\r
769 virtual void resetRightReleased()
\r
773 virtual void step(float dtime)
\r
776 static float counter1 = 0;
\r
780 counter1 = 0.1*Rand(1,10);
\r
781 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
782 g_selected_material++;
\r
784 g_selected_material = 0;*/
\r
785 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
788 g_selected_item = 0;
\r
792 static float counter1 = 0;
\r
796 counter1 = 0.1*Rand(1, 40);
\r
797 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
801 static float counter1 = 0;
\r
805 counter1 = 0.1*Rand(1, 40);
\r
806 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
810 static float counter1 = 0;
\r
814 counter1 = 0.1*Rand(1, 40);
\r
815 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
819 static float counter1 = 0;
\r
823 counter1 = 0.1*Rand(1, 40);
\r
824 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
828 static float counter1 = 0;
\r
832 counter1 = 0.1*Rand(1, 20);
\r
833 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
837 static float counter1 = 0;
\r
841 counter1 = 0.1*Rand(1, 30);
\r
842 leftclicked = true;
\r
846 static float counter1 = 0;
\r
850 counter1 = 0.1*Rand(1, 20);
\r
851 rightclicked = true;
\r
854 mousepos += mousespeed;
\r
857 s32 Rand(s32 min, s32 max)
\r
859 return (myrand()%(max-min+1))+min;
\r
862 bool keydown[KEY_KEY_CODES_COUNT];
\r
869 void updateViewingRange(f32 frametime_in, Client *client)
\r
871 if(draw_control.range_all == true)
\r
874 static f32 added_frametime = 0;
\r
875 static s16 added_frames = 0;
\r
877 added_frametime += frametime_in;
\r
880 // Actually this counter kind of sucks because frametime is busytime
\r
881 static f32 counter = 0;
\r
882 counter -= frametime_in;
\r
888 /*dstream<<__FUNCTION_NAME
\r
889 <<": Collected "<<added_frames<<" frames, total of "
\r
890 <<added_frametime<<"s."<<std::endl;*/
\r
892 /*dstream<<"draw_control.blocks_drawn="
\r
893 <<draw_control.blocks_drawn
\r
894 <<", draw_control.blocks_would_have_drawn="
\r
895 <<draw_control.blocks_would_have_drawn
\r
898 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
899 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
901 draw_control.wanted_min_range = range_min;
\r
902 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
904 float block_draw_ratio = 1.0;
\r
905 if(draw_control.blocks_would_have_drawn != 0)
\r
907 block_draw_ratio = (float)draw_control.blocks_drawn
\r
908 / (float)draw_control.blocks_would_have_drawn;
\r
911 // Calculate the average frametime in the case that all wanted
\r
912 // blocks had been drawn
\r
913 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
915 added_frametime = 0.0;
\r
918 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
919 float wanted_frametime = 1.0 / wanted_fps;
\r
921 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
922 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
924 // If needed frametime change is very small, just return
\r
925 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
927 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
931 float range = draw_control.wanted_range;
\r
932 float new_range = range;
\r
934 static s16 range_old = 0;
\r
935 static f32 frametime_old = 0;
\r
937 float d_range = range - range_old;
\r
938 f32 d_frametime = frametime - frametime_old;
\r
939 // A sane default of 30ms per 50 nodes of range
\r
940 static f32 time_per_range = 30. / 50;
\r
943 time_per_range = d_frametime / d_range;
\r
946 // The minimum allowed calculated frametime-range derivative:
\r
947 // Practically this sets the maximum speed of changing the range.
\r
948 // The lower this value, the higher the maximum changing speed.
\r
949 // A low value here results in wobbly range (0.001)
\r
950 // A high value here results in slow changing range (0.0025)
\r
951 // SUGG: This could be dynamically adjusted so that when
\r
952 // the camera is turning, this is lower
\r
953 //float min_time_per_range = 0.0015;
\r
954 float min_time_per_range = 0.0010;
\r
955 //float min_time_per_range = 0.05 / range;
\r
956 if(time_per_range < min_time_per_range)
\r
958 time_per_range = min_time_per_range;
\r
959 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
963 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
966 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
967 // Dampen the change a bit to kill oscillations
\r
968 //wanted_range_change *= 0.9;
\r
969 //wanted_range_change *= 0.75;
\r
970 wanted_range_change *= 0.5;
\r
971 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
973 // If needed range change is very small, just return
\r
974 if(fabs(wanted_range_change) < 0.001)
\r
976 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
980 new_range += wanted_range_change;
\r
981 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
983 //float new_range_unclamped = new_range;
\r
984 if(new_range < range_min)
\r
985 new_range = range_min;
\r
986 if(new_range > range_max)
\r
987 new_range = range_max;
\r
989 /*if(new_range != new_range_unclamped)
\r
990 dstream<<", clamped to "<<new_range<<std::endl;
\r
992 dstream<<std::endl;*/
\r
994 draw_control.wanted_range = new_range;
\r
996 range_old = new_range;
\r
997 frametime_old = frametime;
\r
1000 class GUIQuickInventory : public IEventReceiver
\r
1003 GUIQuickInventory(
\r
1004 gui::IGUIEnvironment* env,
\r
1005 gui::IGUIElement* parent,
\r
1008 Inventory *inventory):
\r
1009 m_itemcount(itemcount),
\r
1010 m_inventory(inventory)
\r
1012 core::rect<s32> imgsize(0,0,48,48);
\r
1013 core::rect<s32> textsize(0,0,48,16);
\r
1014 v2s32 spacing(0, 64);
\r
1015 for(s32 i=0; i<m_itemcount; i++)
\r
1017 m_images.push_back(env->addImage(
\r
1018 imgsize + pos + spacing*i
\r
1020 m_images[i]->setScaleImage(true);
\r
1021 m_texts.push_back(env->addStaticText(
\r
1023 textsize + pos + spacing*i,
\r
1026 m_texts[i]->setBackgroundColor(
\r
1027 video::SColor(128,0,0,0));
\r
1028 m_texts[i]->setTextAlignment(
\r
1029 gui::EGUIA_CENTER,
\r
1030 gui::EGUIA_UPPERLEFT);
\r
1034 virtual bool OnEvent(const SEvent& event)
\r
1039 void setSelection(s32 i)
\r
1048 start = m_selection - m_itemcount / 2;
\r
1050 InventoryList *mainlist = m_inventory->getList("main");
\r
1052 for(s32 i=0; i<m_itemcount; i++)
\r
1054 s32 j = i + start;
\r
1056 if(j > (s32)mainlist->getSize() - 1)
\r
1057 j -= mainlist->getSize();
\r
1059 j += mainlist->getSize();
\r
1061 InventoryItem *item = mainlist->getItem(j);
\r
1065 m_images[i]->setImage(NULL);
\r
1068 if(m_selection == j)
\r
1069 swprintf(t, 10, L"<-");
\r
1071 swprintf(t, 10, L"");
\r
1072 m_texts[i]->setText(t);
\r
1074 // The next ifs will segfault with a NULL pointer
\r
1079 m_images[i]->setImage(item->getImage());
\r
1082 if(m_selection == j)
\r
1083 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1085 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1086 m_texts[i]->setText(t);
\r
1092 core::array<gui::IGUIStaticText*> m_texts;
\r
1093 core::array<gui::IGUIImage*> m_images;
\r
1094 Inventory *m_inventory;
\r
1105 ChatLine(const std::wstring &a_text):
\r
1111 std::wstring text;
\r
1114 int main(int argc, char *argv[])
\r
1117 Low-level initialization
\r
1120 bool disable_stderr = false;
\r
1122 disable_stderr = true;
\r
1125 // Initialize debug streams
\r
1126 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1127 // Initialize debug stacks
\r
1128 debug_stacks_init();
\r
1130 DSTACK(__FUNCTION_NAME);
\r
1132 porting::initializePaths();
\r
1133 // Create user data directory
\r
1134 fs::CreateDir(porting::path_userdata);
\r
1136 initializeMaterialProperties();
\r
1138 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1140 // Print startup message
\r
1141 dstream<<DTIME<<"minetest-c55"
\r
1142 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1143 <<", "<<BUILD_INFO
\r
1150 Parse command line
\r
1153 // List all allowed options
\r
1154 core::map<std::string, ValueSpec> allowed_options;
\r
1155 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1156 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1157 "Run server directly"));
\r
1158 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1159 "Load configuration from specified file"));
\r
1160 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1161 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1162 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1163 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1164 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1165 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1167 Settings cmd_args;
\r
1169 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1171 if(ret == false || cmd_args.getFlag("help"))
\r
1173 dstream<<"Allowed options:"<<std::endl;
\r
1174 for(core::map<std::string, ValueSpec>::Iterator
\r
1175 i = allowed_options.getIterator();
\r
1176 i.atEnd() == false; i++)
\r
1178 dstream<<" --"<<i.getNode()->getKey();
\r
1179 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1184 dstream<<" <value>";
\r
1186 dstream<<std::endl;
\r
1188 if(i.getNode()->getValue().help != NULL)
\r
1190 dstream<<" "<<i.getNode()->getValue().help
\r
1195 return cmd_args.getFlag("help") ? 0 : 1;
\r
1200 Basic initialization
\r
1203 // Initialize default settings
\r
1204 set_default_settings();
\r
1206 // Set locale. This is for forcing '.' as the decimal point.
\r
1207 std::locale::global(std::locale("C"));
\r
1208 // This enables printing all characters in bitmap font
\r
1209 setlocale(LC_CTYPE, "en_US");
\r
1211 // Initialize sockets
\r
1213 atexit(sockets_cleanup);
\r
1223 // Path of configuration file in use
\r
1224 std::string configpath = "";
\r
1226 if(cmd_args.exists("config"))
\r
1228 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1231 dstream<<"Could not read configuration from \""
\r
1232 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1235 configpath = cmd_args.get("config");
\r
1239 core::array<std::string> filenames;
\r
1240 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1241 #ifdef RUN_IN_PLACE
\r
1242 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1245 for(u32 i=0; i<filenames.size(); i++)
\r
1247 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1250 configpath = filenames[i];
\r
1256 // Initialize random seed
\r
1263 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1264 || cmd_args.getFlag("enable-unittests") == true)
\r
1269 // Read map parameters from settings
\r
1271 HMParams hm_params;
\r
1272 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1273 hm_params.randmax = g_settings.get("height_randmax");
\r
1274 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1275 hm_params.base = g_settings.get("height_base");
\r
1277 MapParams map_params;
\r
1278 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1279 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1285 std::cout<<std::endl<<std::endl;
\r
1288 <<" .__ __ __ "<<std::endl
\r
1289 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1290 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1291 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1292 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1293 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1296 std::cout<<std::endl;
\r
1297 //char templine[100];
\r
1301 if(cmd_args.exists("port"))
\r
1303 port = cmd_args.getU16("port");
\r
1307 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1308 std::cout<<"-> "<<port<<std::endl;
\r
1312 std::string map_dir = porting::path_userdata+"/map";
\r
1313 if(cmd_args.exists("map-dir"))
\r
1314 map_dir = cmd_args.get("map-dir");
\r
1315 else if(g_settings.exists("map-dir"))
\r
1316 map_dir = g_settings.get("map-dir");
\r
1318 if(cmd_args.getFlag("server"))
\r
1320 DSTACK("Dedicated server branch");
\r
1322 std::cout<<std::endl;
\r
1323 std::cout<<"========================"<<std::endl;
\r
1324 std::cout<<"Running dedicated server"<<std::endl;
\r
1325 std::cout<<"========================"<<std::endl;
\r
1326 std::cout<<std::endl;
\r
1328 Server server(map_dir, hm_params, map_params);
\r
1329 server.start(port);
\r
1333 // This is kind of a hack but can be done like this
\r
1334 // because server.step() is very light
\r
1336 server.step(0.030);
\r
1338 static int counter = 0;
\r
1344 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1345 core::list<PlayerInfo>::Iterator i;
\r
1346 static u32 sum_old = 0;
\r
1347 u32 sum = PIChecksum(list);
\r
1348 if(sum != sum_old)
\r
1350 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1351 for(i=list.begin(); i!=list.end(); i++)
\r
1353 i->PrintLine(&std::cout);
\r
1363 bool hosting = false;
\r
1364 char connect_name[100] = "";
\r
1366 if(cmd_args.exists("address"))
\r
1368 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1370 else if(is_yes(g_settings.get("host_game")) == false)
\r
1372 if(g_settings.get("address") != "")
\r
1374 std::cout<<g_settings.get("address")<<std::endl;
\r
1375 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1379 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1380 std::cin.getline(connect_name, 100);
\r
1384 if(connect_name[0] == 0){
\r
1385 snprintf(connect_name, 100, "127.0.0.1");
\r
1390 std::cout<<"> Hosting game"<<std::endl;
\r
1392 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1394 char playername[PLAYERNAME_SIZE] = "";
\r
1395 if(g_settings.get("name") != "")
\r
1397 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1401 std::cout<<"Name of player: ";
\r
1402 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1404 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1407 Resolution selection
\r
1410 bool fullscreen = false;
\r
1411 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1412 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1416 MyEventReceiver receiver;
\r
1418 video::E_DRIVER_TYPE driverType;
\r
1421 //driverType = video::EDT_DIRECT3D9;
\r
1422 driverType = video::EDT_OPENGL;
\r
1424 driverType = video::EDT_OPENGL;
\r
1425 //driverType = video::EDT_BURNINGSVIDEO;
\r
1428 // create device and exit if creation failed
\r
1430 IrrlichtDevice *device;
\r
1431 device = createDevice(driverType,
\r
1432 core::dimension2d<u32>(screenW, screenH),
\r
1433 16, fullscreen, false, false, &receiver);
\r
1436 return 1; // could not create selected driver.
\r
1438 g_device = device;
\r
1439 g_irrlicht = new IrrlichtWrapper(device);
\r
1441 //g_device = device;
\r
1443 device->setResizable(true);
\r
1445 bool random_input = g_settings.getBool("random_input")
\r
1446 || cmd_args.getFlag("random-input");
\r
1448 g_input = new RandomInputHandler();
\r
1450 g_input = new RealInputHandler(device, &receiver);
\r
1453 Continue initialization
\r
1456 video::IVideoDriver* driver = device->getVideoDriver();
\r
1459 This changes the minimum allowed number of vertices in a VBO
\r
1461 //driver->setMinHardwareBufferVertexCount(50);
\r
1463 scene::ISceneManager* smgr = device->getSceneManager();
\r
1465 guienv = device->getGUIEnvironment();
\r
1466 gui::IGUISkin* skin = guienv->getSkin();
\r
1467 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1469 skin->setFont(font);
\r
1471 dstream<<"WARNING: Font file was not found."
\r
1472 " Using default font."<<std::endl;
\r
1473 // If font was not found, this will get us one
\r
1474 font = skin->getFont();
\r
1477 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1478 dstream<<"text_height="<<text_height<<std::endl;
\r
1480 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1481 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1482 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1483 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1484 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1485 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1487 const wchar_t *text = L"Loading and connecting...";
\r
1488 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1489 core::vector2d<s32> textsize(300, text_height);
\r
1490 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1492 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1493 text, textrect, false, false);
\r
1494 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1496 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1497 guienv->drawAll();
\r
1498 driver->endScene();
\r
1501 Preload some textures
\r
1504 init_content_inventory_texture_paths();
\r
1505 init_tile_texture_paths();
\r
1506 tile_materials_preload(g_irrlicht);
\r
1509 Make a scope here for the client so that it gets removed
\r
1510 before the irrlicht device
\r
1514 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1519 SharedPtr<Server> server;
\r
1521 server = new Server(map_dir, hm_params, map_params);
\r
1522 server->start(port);
\r
1529 Client client(device, playername, draw_control);
\r
1531 g_client = &client;
\r
1533 Address connect_address(0,0,0,0, port);
\r
1535 connect_address.Resolve(connect_name);
\r
1537 catch(ResolveError &e)
\r
1539 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1543 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1544 client.connect(connect_address);
\r
1547 while(client.connectedAndInitialized() == false)
\r
1550 if(server != NULL){
\r
1551 server->step(0.1);
\r
1556 catch(con::PeerNotFoundException &e)
\r
1558 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1565 /*scene::ISceneNode* skybox;
\r
1566 skybox = smgr->addSkyBoxSceneNode(
\r
1567 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1568 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1569 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1570 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1571 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1572 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1575 Create the camera node
\r
1578 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1579 0, // Camera parent
\r
1580 v3f(BS*100, BS*2, BS*100), // Look from
\r
1581 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1585 if(camera == NULL)
\r
1588 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1590 camera->setFOV(FOV_ANGLE);
\r
1592 // Just so big a value that everything rendered is visible
\r
1593 camera->setFarValue(100000*BS);
\r
1595 f32 camera_yaw = 0; // "right/left"
\r
1596 f32 camera_pitch = 0; // "up/down"
\r
1602 gui_loadingtext->remove();
\r
1605 Add some gui stuff
\r
1608 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1609 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1612 We need some kind of a root node to be able to add
\r
1613 custom elements directly on the screen.
\r
1614 Otherwise they won't be automatically drawn.
\r
1616 guiroot = guienv->addStaticText(L"",
\r
1617 core::rect<s32>(0, 0, 10000, 10000));
\r
1619 // Test the text input system
\r
1620 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1622 /*GUIMessageMenu *menu =
\r
1623 new GUIMessageMenu(guienv, guiroot, -1,
\r
1624 &g_active_menu_count,
\r
1628 // Launch pause menu
\r
1629 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1630 &g_active_menu_count))->drop();
\r
1632 // First line of debug text
\r
1633 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1635 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1637 // Second line of debug text
\r
1638 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1640 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1643 // At the middle of the screen
\r
1644 // Object infos are shown in this
\r
1645 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1647 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1651 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1652 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1653 core::rect<s32>(70, 60, 795, 150),
\r
1655 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1656 core::list<ChatLine> chat_lines;
\r
1659 Some statistics are collected in these
\r
1662 u32 beginscenetime = 0;
\r
1663 u32 scenetime = 0;
\r
1664 u32 endscenetime = 0;
\r
1667 //throw con::PeerNotFoundException("lol");
\r
1673 bool first_loop_after_window_activation = true;
\r
1675 // Time is in milliseconds
\r
1676 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1677 // NOTE: So we have to use getTime() and call run()s between them
\r
1678 u32 lasttime = device->getTimer()->getTime();
\r
1680 while(device->run())
\r
1683 Run global IrrlichtWrapper's main thread processing stuff
\r
1685 g_irrlicht->Run();
\r
1688 Random calculations
\r
1690 v2u32 screensize = driver->getScreenSize();
\r
1691 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1693 // Hilight boxes collected during the loop and displayed
\r
1694 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1697 std::wstring infotext;
\r
1699 //TimeTaker //timer1("//timer1");
\r
1701 // Time of frame without fps limit
\r
1705 // not using getRealTime is necessary for wine
\r
1706 u32 time = device->getTimer()->getTime();
\r
1707 if(time > lasttime)
\r
1708 busytime_u32 = time - lasttime;
\r
1711 busytime = busytime_u32 / 1000.0;
\r
1714 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1716 // Absolutelu necessary for wine!
\r
1723 updateViewingRange(busytime, &client);
\r
1730 float fps_max = g_settings.getFloat("fps_max");
\r
1731 u32 frametime_min = 1000./fps_max;
\r
1733 if(busytime_u32 < frametime_min)
\r
1735 u32 sleeptime = frametime_min - busytime_u32;
\r
1736 device->sleep(sleeptime);
\r
1740 // Absolutelu necessary for wine!
\r
1744 Time difference calculation
\r
1746 f32 dtime; // in seconds
\r
1748 u32 time = device->getTimer()->getTime();
\r
1749 if(time > lasttime)
\r
1750 dtime = (time - lasttime) / 1000.0;
\r
1756 Time average and jitter calculation
\r
1759 static f32 dtime_avg1 = 0.0;
\r
1760 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1761 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1763 static f32 dtime_jitter1_max_sample = 0.0;
\r
1764 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1766 static f32 jitter1_max = 0.0;
\r
1767 static f32 counter = 0.0;
\r
1768 if(dtime_jitter1 > jitter1_max)
\r
1769 jitter1_max = dtime_jitter1;
\r
1774 dtime_jitter1_max_sample = jitter1_max;
\r
1775 dtime_jitter1_max_fraction
\r
1776 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1777 jitter1_max = 0.0;
\r
1780 Control freetime ratio
\r
1782 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1784 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1785 g_freetime_ratio += 0.01;
\r
1789 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1790 g_freetime_ratio -= 0.01;
\r
1796 Busytime average and jitter calculation
\r
1799 static f32 busytime_avg1 = 0.0;
\r
1800 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1801 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1803 static f32 busytime_jitter1_max_sample = 0.0;
\r
1804 static f32 busytime_jitter1_min_sample = 0.0;
\r
1806 static f32 jitter1_max = 0.0;
\r
1807 static f32 jitter1_min = 0.0;
\r
1808 static f32 counter = 0.0;
\r
1809 if(busytime_jitter1 > jitter1_max)
\r
1810 jitter1_max = busytime_jitter1;
\r
1811 if(busytime_jitter1 < jitter1_min)
\r
1812 jitter1_min = busytime_jitter1;
\r
1814 if(counter > 0.0){
\r
1816 busytime_jitter1_max_sample = jitter1_max;
\r
1817 busytime_jitter1_min_sample = jitter1_min;
\r
1818 jitter1_max = 0.0;
\r
1819 jitter1_min = 0.0;
\r
1824 Debug info for client
\r
1827 static float counter = 0.0;
\r
1832 client.printDebugInfo(std::cout);
\r
1837 Input handler step()
\r
1839 g_input->step(dtime);
\r
1842 Player speed control
\r
1851 bool a_superspeed,
\r
1854 PlayerControl control(
\r
1855 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1856 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1857 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1858 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1859 g_input->isKeyDown(irr::KEY_SPACE),
\r
1860 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1864 client.setPlayerControl(control);
\r
1868 Process environment
\r
1872 //TimeTaker timer("client.step(dtime)");
\r
1873 client.step(dtime);
\r
1874 //client.step(dtime_avg1);
\r
1877 if(server != NULL)
\r
1879 //TimeTaker timer("server->step(dtime)");
\r
1880 server->step(dtime);
\r
1883 v3f player_position = client.getPlayerPosition();
\r
1885 //TimeTaker //timer2("//timer2");
\r
1888 Mouse and camera control
\r
1891 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1894 device->getCursorControl()->setVisible(false);
\r
1896 if(first_loop_after_window_activation){
\r
1897 //std::cout<<"window active, first loop"<<std::endl;
\r
1898 first_loop_after_window_activation = false;
\r
1901 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1902 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1903 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1904 camera_yaw -= dx*0.2;
\r
1905 camera_pitch += dy*0.2;
\r
1906 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1907 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1909 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1912 device->getCursorControl()->setVisible(true);
\r
1914 //std::cout<<"window inactive"<<std::endl;
\r
1915 first_loop_after_window_activation = true;
\r
1918 camera_yaw = wrapDegrees(camera_yaw);
\r
1919 camera_pitch = wrapDegrees(camera_pitch);
\r
1921 v3f camera_direction = v3f(0,0,1);
\r
1922 camera_direction.rotateYZBy(camera_pitch);
\r
1923 camera_direction.rotateXZBy(camera_yaw);
\r
1925 // This is at the height of the eyes of the current figure
\r
1926 v3f camera_position =
\r
1927 player_position + v3f(0, BS+BS/2, 0);
\r
1928 // This is more like in minecraft
\r
1929 /*v3f camera_position =
\r
1930 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1932 camera->setPosition(camera_position);
\r
1933 // *100.0 helps in large map coordinates
\r
1934 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1936 if(FIELD_OF_VIEW_TEST){
\r
1937 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1938 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1941 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1942 //TimeTaker timer("client.updateCamera");
\r
1943 client.updateCamera(camera_position, camera_direction);
\r
1947 //TimeTaker //timer3("//timer3");
\r
1950 Calculate what block is the crosshair pointing to
\r
1953 //u32 t1 = device->getTimer()->getRealTime();
\r
1955 //f32 d = 4; // max. distance
\r
1956 f32 d = 4; // max. distance
\r
1957 core::line3d<f32> shootline(camera_position,
\r
1958 camera_position + camera_direction * BS * (d+1));
\r
1960 MapBlockObject *selected_object = client.getSelectedObject
\r
1961 (d*BS, camera_position, shootline);
\r
1964 If it's pointing to a MapBlockObject
\r
1967 if(selected_object != NULL)
\r
1969 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1971 core::aabbox3d<f32> box_on_map
\r
1972 = selected_object->getSelectionBoxOnMap();
\r
1974 hilightboxes.push_back(box_on_map);
\r
1976 infotext = narrow_to_wide(selected_object->infoText());
\r
1978 if(g_input->getLeftClicked())
\r
1980 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1981 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1982 selected_object->getId(), g_selected_item);
\r
1984 else if(g_input->getRightClicked())
\r
1986 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1988 Check if we want to modify the object ourselves
\r
1990 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1992 dstream<<"Sign object right-clicked"<<std::endl;
\r
1994 if(random_input == false)
\r
1996 // Get a new text for it
\r
1998 TextDest *dest = new TextDestSign(
\r
1999 selected_object->getBlock()->getPos(),
\r
2000 selected_object->getId(),
\r
2003 SignObject *sign_object = (SignObject*)selected_object;
\r
2005 std::wstring wtext =
\r
2006 narrow_to_wide(sign_object->getText());
\r
2008 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2009 &g_active_menu_count, dest,
\r
2014 Otherwise pass the event to the server as-is
\r
2018 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2019 selected_object->getId(), g_selected_item);
\r
2023 else // selected_object == NULL
\r
2027 Find out which node we are pointing at
\r
2030 bool nodefound = false;
\r
2032 v3s16 neighbourpos;
\r
2033 core::aabbox3d<f32> nodefacebox;
\r
2034 f32 mindistance = BS * 1001;
\r
2036 v3s16 pos_i = floatToInt(player_position);
\r
2038 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2042 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2043 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2044 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2045 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2046 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2047 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2049 for(s16 y = ystart; y <= yend; y++)
\r
2050 for(s16 z = zstart; z <= zend; z++)
\r
2051 for(s16 x = xstart; x <= xend; x++)
\r
2056 n = client.getNode(v3s16(x,y,z));
\r
2057 if(content_pointable(n.d) == false)
\r
2060 catch(InvalidPositionException &e)
\r
2066 v3f npf = intToFloat(np);
\r
2071 v3s16(0,0,1), // back
\r
2072 v3s16(0,1,0), // top
\r
2073 v3s16(1,0,0), // right
\r
2074 v3s16(0,0,-1), // front
\r
2075 v3s16(0,-1,0), // bottom
\r
2076 v3s16(-1,0,0), // left
\r
2082 if(n.d == CONTENT_TORCH)
\r
2084 v3s16 dir = unpackDir(n.dir);
\r
2085 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2086 dir_f *= BS/2 - BS/6 - BS/20;
\r
2087 v3f cpf = npf + dir_f;
\r
2088 f32 distance = (cpf - camera_position).getLength();
\r
2090 core::aabbox3d<f32> box;
\r
2093 if(dir == v3s16(0,-1,0))
\r
2095 box = core::aabbox3d<f32>(
\r
2096 npf - v3f(BS/6, BS/2, BS/6),
\r
2097 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2101 else if(dir == v3s16(0,1,0))
\r
2103 box = core::aabbox3d<f32>(
\r
2104 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2105 npf + v3f(BS/6, BS/2, BS/6)
\r
2111 box = core::aabbox3d<f32>(
\r
2112 cpf - v3f(BS/6, BS/3, BS/6),
\r
2113 cpf + v3f(BS/6, BS/3, BS/6)
\r
2117 if(distance < mindistance)
\r
2119 if(box.intersectsWithLine(shootline))
\r
2123 neighbourpos = np;
\r
2124 mindistance = distance;
\r
2125 nodefacebox = box;
\r
2134 for(u16 i=0; i<6; i++)
\r
2136 v3f dir_f = v3f(dirs[i].X,
\r
2137 dirs[i].Y, dirs[i].Z);
\r
2138 v3f centerpoint = npf + dir_f * BS/2;
\r
2140 (centerpoint - camera_position).getLength();
\r
2142 if(distance < mindistance)
\r
2144 core::CMatrix4<f32> m;
\r
2145 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2147 // This is the back face
\r
2148 v3f corners[2] = {
\r
2149 v3f(BS/2, BS/2, BS/2),
\r
2150 v3f(-BS/2, -BS/2, BS/2+d)
\r
2153 for(u16 j=0; j<2; j++)
\r
2155 m.rotateVect(corners[j]);
\r
2156 corners[j] += npf;
\r
2159 core::aabbox3d<f32> facebox(corners[0]);
\r
2160 facebox.addInternalPoint(corners[1]);
\r
2162 if(facebox.intersectsWithLine(shootline))
\r
2166 neighbourpos = np + dirs[i];
\r
2167 mindistance = distance;
\r
2168 nodefacebox = facebox;
\r
2170 } // if distance < mindistance
\r
2172 } // regular block
\r
2175 static float nodig_delay_counter = 0.0;
\r
2179 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2181 static float dig_time = 0.0;
\r
2182 static u16 dig_index = 0;
\r
2184 hilightboxes.push_back(nodefacebox);
\r
2186 if(g_input->getLeftReleased())
\r
2188 client.clearTempMod(nodepos);
\r
2192 if(nodig_delay_counter > 0.0)
\r
2194 nodig_delay_counter -= dtime;
\r
2198 if(nodepos != nodepos_old)
\r
2200 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2201 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2203 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2205 client.clearTempMod(nodepos_old);
\r
2210 if(g_input->getLeftClicked() ||
\r
2211 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2213 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2214 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2216 if(g_input->getLeftClicked())
\r
2218 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2220 if(g_input->getLeftState())
\r
2222 MapNode n = client.getNode(nodepos);
\r
2224 // Get tool name. Default is "" = bare hands
\r
2225 std::string toolname = "";
\r
2226 InventoryList *mlist = local_inventory.getList("main");
\r
2229 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2230 if(item && (std::string)item->getName() == "ToolItem")
\r
2232 ToolItem *titem = (ToolItem*)item;
\r
2233 toolname = titem->getToolName();
\r
2237 // Get digging properties for material and tool
\r
2238 u8 material = n.d;
\r
2239 DiggingProperties prop =
\r
2240 getDiggingProperties(material, toolname);
\r
2242 float dig_time_complete = 0.0;
\r
2244 if(prop.diggable == false)
\r
2246 /*dstream<<"Material "<<(int)material
\r
2247 <<" not diggable with \""
\r
2248 <<toolname<<"\""<<std::endl;*/
\r
2249 // I guess nobody will wait for this long
\r
2250 dig_time_complete = 10000000.0;
\r
2254 dig_time_complete = prop.time;
\r
2257 if(dig_time_complete >= 0.001)
\r
2259 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2260 * dig_time/dig_time_complete);
\r
2262 // This is for torches
\r
2265 dig_index = CRACK_ANIMATION_LENGTH;
\r
2268 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2270 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2271 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2275 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2276 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2277 client.clearTempMod(nodepos);
\r
2278 client.removeNode(nodepos);
\r
2282 nodig_delay_counter = dig_time_complete
\r
2283 / (float)CRACK_ANIMATION_LENGTH;
\r
2285 // We don't want a corresponding delay to
\r
2286 // very time consuming nodes
\r
2287 if(nodig_delay_counter > 0.5)
\r
2289 nodig_delay_counter = 0.5;
\r
2291 // We want a slight delay to very little
\r
2292 // time consuming nodes
\r
2293 //float mindelay = 0.15;
\r
2294 float mindelay = 0.20;
\r
2295 if(nodig_delay_counter < mindelay)
\r
2297 nodig_delay_counter = mindelay;
\r
2301 dig_time += dtime;
\r
2305 if(g_input->getRightClicked())
\r
2307 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2308 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2311 nodepos_old = nodepos;
\r
2316 } // selected_object == NULL
\r
2318 g_input->resetLeftClicked();
\r
2319 g_input->resetRightClicked();
\r
2321 if(g_input->getLeftReleased())
\r
2323 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2325 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2327 if(g_input->getRightReleased())
\r
2329 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2333 g_input->resetLeftReleased();
\r
2334 g_input->resetRightReleased();
\r
2337 Calculate stuff for drawing
\r
2340 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2342 u32 daynight_ratio = client.getDayNightRatio();
\r
2343 /*video::SColor bgcolor = video::SColor(
\r
2345 skycolor.getRed() * daynight_ratio / 1000,
\r
2346 skycolor.getGreen() * daynight_ratio / 1000,
\r
2347 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2349 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2350 video::SColor bgcolor = video::SColor(
\r
2352 skycolor.getRed() * l / 255,
\r
2353 skycolor.getGreen() * l / 255,
\r
2354 skycolor.getBlue() * l / 255);
\r
2360 if(g_settings.getBool("enable_fog") == true)
\r
2362 f32 range = draw_control.wanted_range * BS;
\r
2363 if(draw_control.range_all)
\r
2364 range = 100000*BS;
\r
2368 video::EFT_FOG_LINEAR,
\r
2372 false, // pixel fog
\r
2373 false // range fog
\r
2379 Update gui stuff (0ms)
\r
2382 //TimeTaker guiupdatetimer("Gui updating");
\r
2385 wchar_t temptext[150];
\r
2387 static float drawtime_avg = 0;
\r
2388 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2389 static float beginscenetime_avg = 0;
\r
2390 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2391 static float scenetime_avg = 0;
\r
2392 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2393 static float endscenetime_avg = 0;
\r
2394 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2396 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2398 L", R: range_all=%i"
\r
2400 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2402 draw_control.range_all,
\r
2404 beginscenetime_avg,
\r
2409 guitext->setText(temptext);
\r
2413 wchar_t temptext[150];
\r
2414 swprintf(temptext, 150,
\r
2415 L"(% .1f, % .1f, % .1f)"
\r
2416 L" (% .3f < btime_jitter < % .3f"
\r
2417 L", dtime_jitter = % .1f %%"
\r
2418 L", v_range = %.1f)",
\r
2419 player_position.X/BS,
\r
2420 player_position.Y/BS,
\r
2421 player_position.Z/BS,
\r
2422 busytime_jitter1_min_sample,
\r
2423 busytime_jitter1_max_sample,
\r
2424 dtime_jitter1_max_fraction * 100.0,
\r
2425 draw_control.wanted_range
\r
2428 guitext2->setText(temptext);
\r
2432 guitext_info->setText(infotext.c_str());
\r
2436 Get chat messages from client
\r
2439 // Get new messages
\r
2440 std::wstring message;
\r
2441 while(client.getChatMessage(message))
\r
2443 chat_lines.push_back(ChatLine(message));
\r
2444 /*if(chat_lines.size() > 6)
\r
2446 core::list<ChatLine>::Iterator
\r
2447 i = chat_lines.begin();
\r
2448 chat_lines.erase(i);
\r
2451 // Append them to form the whole static text and throw
\r
2452 // it to the gui element
\r
2453 std::wstring whole;
\r
2454 // This will correspond to the line number counted from
\r
2455 // top to bottom, from size-1 to 0
\r
2456 s16 line_number = chat_lines.size();
\r
2457 // Count of messages to be removed from the top
\r
2458 u16 to_be_removed_count = 0;
\r
2459 for(core::list<ChatLine>::Iterator
\r
2460 i = chat_lines.begin();
\r
2461 i != chat_lines.end(); i++)
\r
2463 // After this, line number is valid for this loop
\r
2466 (*i).age += dtime;
\r
2468 This results in a maximum age of 60*6 to the
\r
2469 lowermost line and a maximum of 6 lines
\r
2471 float allowed_age = (6-line_number) * 60.0;
\r
2473 if((*i).age > allowed_age)
\r
2475 to_be_removed_count++;
\r
2478 whole += (*i).text + L'\n';
\r
2480 for(u16 i=0; i<to_be_removed_count; i++)
\r
2482 core::list<ChatLine>::Iterator
\r
2483 it = chat_lines.begin();
\r
2484 chat_lines.erase(it);
\r
2486 chat_guitext->setText(whole.c_str());
\r
2487 // Update gui element size and position
\r
2488 core::rect<s32> rect(
\r
2490 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2491 screensize.X - 10,
\r
2494 chat_guitext->setRelativePosition(rect);
\r
2496 if(chat_lines.size() == 0)
\r
2497 chat_guitext->setVisible(false);
\r
2499 chat_guitext->setVisible(true);
\r
2506 static u16 old_selected_item = 65535;
\r
2507 if(client.getLocalInventoryUpdated()
\r
2508 || g_selected_item != old_selected_item)
\r
2510 old_selected_item = g_selected_item;
\r
2511 //std::cout<<"Updating local inventory"<<std::endl;
\r
2512 client.getLocalInventory(local_inventory);
\r
2513 quick_inventory->setSelection(g_selected_item);
\r
2514 quick_inventory->update();
\r
2518 Send actions returned by the inventory menu
\r
2520 while(inventory_action_queue.size() != 0)
\r
2522 InventoryAction *a = inventory_action_queue.pop_front();
\r
2524 client.sendInventoryAction(a);
\r
2533 TimeTaker drawtimer("Drawing");
\r
2537 TimeTaker timer("beginScene");
\r
2538 driver->beginScene(true, true, bgcolor);
\r
2539 //driver->beginScene(false, true, bgcolor);
\r
2540 beginscenetime = timer.stop(true);
\r
2545 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2548 TimeTaker timer("smgr");
\r
2550 scenetime = timer.stop(true);
\r
2554 //TimeTaker timer9("auxiliary drawings");
\r
2558 //TimeTaker //timer10("//timer10");
\r
2560 video::SMaterial m;
\r
2562 m.Lighting = false;
\r
2563 driver->setMaterial(m);
\r
2565 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2567 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2568 i != hilightboxes.end(); i++)
\r
2570 /*std::cout<<"hilightbox min="
\r
2571 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2573 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2575 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2581 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2582 displaycenter + core::vector2d<s32>(10,0),
\r
2583 video::SColor(255,255,255,255));
\r
2584 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2585 displaycenter + core::vector2d<s32>(0,10),
\r
2586 video::SColor(255,255,255,255));
\r
2591 //TimeTaker //timer11("//timer11");
\r
2597 guienv->drawAll();
\r
2601 TimeTaker timer("endScene");
\r
2602 driver->endScene();
\r
2603 endscenetime = timer.stop(true);
\r
2606 drawtime = drawtimer.stop(true);
\r
2612 static s16 lastFPS = 0;
\r
2613 //u16 fps = driver->getFPS();
\r
2614 u16 fps = (1.0/dtime_avg1);
\r
2616 if (lastFPS != fps)
\r
2618 core::stringw str = L"Minetest [";
\r
2619 str += driver->getName();
\r
2623 device->setWindowCaption(str.c_str());
\r
2629 device->yield();*/
\r
2632 delete quick_inventory;
\r
2634 } // client is deleted at this point
\r
2639 In the end, delete the Irrlicht device.
\r
2644 Update configuration file
\r
2646 /*if(configpath != "")
\r
2648 g_settings.updateConfigFile(configpath.c_str());
\r
2652 catch(con::PeerNotFoundException &e)
\r
2654 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2658 GUIMessageMenu *menu =
\r
2659 new GUIMessageMenu(guienv, guiroot, -1,
\r
2660 &g_active_menu_count,
\r
2661 L"Connection timed out");
\r
2663 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2665 dstream<<"Created menu"<<std::endl;
\r
2667 while(g_device->run() && menu->getStatus() == false)
\r
2669 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2670 guienv->drawAll();
\r
2671 driver->endScene();
\r
2674 dstream<<"Dropping menu"<<std::endl;
\r
2680 END_DEBUG_EXCEPTION_HANDLER
\r
2682 debugstreams_deinit();
\r