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
110 TODO: Copy build instructions from website to README.txt
\r
112 Build system / running:
\r
113 -----------------------
\r
115 NOTE: The following fixme is not apparently valid, and it does work.
\r
116 FIXME: Graphical mode seems to segfault with Irrlicht 1.7.1 on 64-bit
\r
118 - http://pastebin.no/32bo
\r
119 - Might be just a bad build, too
\r
120 - Doesn't affect Irrlicht 1.7.2 or 32-bit 1.7.1. (Arch/Debian)
\r
121 - A similar error occurs when getTexture is called from a thread
\r
122 when the texture has not been already loaded from disk:
\r
123 http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=68830
\r
125 FIXME: Some network errors on Windows that cause local game to not work
\r
126 - See siggjen's emails.
\r
131 TODO: Get rid of GotSplitPacketException
\r
136 TODO: Add gui option to remove map
\r
138 TODO: Startup and configuration menu
\r
143 TODO: Optimize day/night mesh updating somehow
\r
144 - create copies of all textures for all lighting values and only
\r
145 change texture for material?
\r
146 - Umm... the collecting of the faces is the slow part
\r
147 -> what about just changing the color values of the existing
\r
148 meshbuffers? It should go quite fast.
\r
149 - This is not easy; There'd need to be a buffer somewhere
\r
150 that would contain the night and day lighting values.
\r
152 TODO: Draw big amounts of torches better (that is, throw them in the
\r
153 same meshbuffer (can the meshcollector class be used?))
\r
155 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
157 - That is >500 vertices
\r
158 - This is not easy; all the MapBlocks close to the player would
\r
159 still need to be drawn separately and combining the blocks
\r
160 would have to happen in a background thread
\r
162 TODO: Make fetching sector's blocks more efficient when rendering
\r
163 sectors that have very large amounts of blocks (on client)
\r
164 - Is this necessary at all?
\r
169 TODO: Make the video backend selectable
\r
174 TODO: Untie client network operations from framerate
\r
175 - Needs some input queues or something
\r
176 - Not really necessary?
\r
178 TODO: Make morning and evening shorter
\r
183 TODO: When player dies, throw items on map
\r
185 TODO: Make an option to the server to disable building and digging near
\r
186 the starting position
\r
188 TODO: Players to only be hidden when the client quits.
\r
189 TODO: - Players to be saved on disk, with inventory
\r
190 TODO: Players to be saved as text in map/players/<name>
\r
191 TODO: Player inventory to be saved on disk
\r
193 TODO: Copy the text of the last picked sign to inventory in creative
\r
196 TODO: Check what goes wrong with caching map to disk (Kray)
\r
199 TODO: When server sees that client is removing an inexistent block or
\r
200 adding a block to an existent position, resend the MapBlock.
\r
202 TODO: Save players with inventories to disk
\r
204 TODO: Make water more like in minecraft
\r
209 TODO: Better handling of objects and mobs
\r
211 - There has to be some way to do it with less messy code
\r
212 - Make separate classes for client and server
\r
213 - Client should not discriminate between blocks, server should
\r
214 - Make other players utilize the same framework
\r
215 - This is also needed for objects that don't get sent to client
\r
216 but are used for triggers etc
\r
218 TODO: There has to be some better way to handle static objects than to
\r
219 send them all the time. This affects signs and item objects.
\r
220 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
221 need an additional metadata field for the texts
\r
222 - This is also needed for item container chests
\r
224 Block object server side:
\r
225 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
226 - For all blocks in the buffer, objects are stepped(). This
\r
227 means they are active.
\r
228 - TODO: A global active buffer is needed for the server
\r
229 - TODO: A timestamp to blocks
\r
230 - TODO: All blocks going in and out of the buffer are recorded.
\r
231 - TODO: For outgoing blocks, timestamp is written.
\r
232 - TODO: For incoming blocks, time difference is calculated and
\r
233 objects are stepped according to it.
\r
238 NOTE: There are some lighting-related todos and fixmes in
\r
239 ServerMap::emergeBlock. And there always will be. 8)
\r
241 TODO: Map generator version 2
\r
242 - Create surface areas based on central points; a given point's
\r
243 area type is given by the nearest central point
\r
244 - Separate points for heightmap, caves, plants and minerals?
\r
245 - Flat land, mountains, forest, jungle
\r
247 - There could be a certain height (to which mountains only reach)
\r
248 where some minerals are found
\r
250 FIXME: The new pre-sunlight-propagation code messes up with initial
\r
253 TODO: Remove HMParams
\r
255 TODO: Change AttributeList to split the area into smaller sections so
\r
256 that searching won't be as heavy.
\r
257 TODO: Change AttributeList to be 2D, as it would be too slow to search
\r
258 in 3D fields anyway.
\r
263 ======================================================================
\r
268 Setting this to 1 enables a special camera mode that forces
\r
269 the renderers to think that the camera statically points from
\r
270 the starting place to a static direction.
\r
272 This allows one to move around with the player and see what
\r
273 is actually drawn behind solid things and behind the player.
\r
275 #define FIELD_OF_VIEW_TEST 0
\r
279 #pragma message ("Disabling unit tests")
\r
281 #warning "Disabling unit tests"
\r
283 // Disable unit tests
\r
284 #define ENABLE_TESTS 0
\r
286 // Enable unit tests
\r
287 #define ENABLE_TESTS 1
\r
291 #pragma comment(lib, "Irrlicht.lib")
\r
292 #pragma comment(lib, "jthread.lib")
\r
293 #pragma comment(lib, "zlibwapi.lib")
\r
294 // This would get rid of the console window
\r
295 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
298 #include <iostream>
\r
300 #include <jmutexautolock.h>
\r
301 #include <locale.h>
\r
302 #include "common_irrlicht.h"
\r
305 #include "player.h"
\r
308 #include "environment.h"
\r
309 #include "server.h"
\r
310 #include "client.h"
\r
311 #include "serialization.h"
\r
312 #include "constants.h"
\r
313 #include "strfnd.h"
\r
314 #include "porting.h"
\r
315 #include "irrlichtwrapper.h"
\r
316 #include "gettime.h"
\r
317 #include "porting.h"
\r
318 #include "guiPauseMenu.h"
\r
319 #include "guiInventoryMenu.h"
\r
320 #include "guiTextInputMenu.h"
\r
321 #include "materials.h"
\r
322 #include "guiMessageMenu.h"
\r
323 #include "filesys.h"
\r
324 #include "config.h"
\r
326 IrrlichtWrapper *g_irrlicht;
\r
328 MapDrawControl draw_control;
\r
332 These are loaded from the config file.
\r
335 Settings g_settings;
\r
337 extern void set_default_settings();
\r
343 IrrlichtDevice *g_device = NULL;
\r
344 Client *g_client = NULL;
\r
349 gui::IGUIEnvironment* guienv = NULL;
\r
350 gui::IGUIStaticText *guiroot = NULL;
\r
351 int g_active_menu_count = 0;
\r
353 bool noMenuActive()
\r
355 return (g_active_menu_count == 0);
\r
358 // Inventory actions from the menu are buffered here before sending
\r
359 Queue<InventoryAction*> inventory_action_queue;
\r
360 // This is a copy of the inventory that the client's environment has
\r
361 Inventory local_inventory;
\r
363 u16 g_selected_item = 0;
\r
370 std::ostream *dout_con_ptr = &dummyout;
\r
371 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
372 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
373 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
374 //std::ostream *dout_con_ptr = &dstream;
\r
375 //std::ostream *derr_con_ptr = &dstream;
\r
378 std::ostream *dout_server_ptr = &dstream;
\r
379 std::ostream *derr_server_ptr = &dstream;
\r
382 std::ostream *dout_client_ptr = &dstream;
\r
383 std::ostream *derr_client_ptr = &dstream;
\r
386 gettime.h implementation
\r
392 Use irrlicht because it is more precise than porting.h's
\r
395 if(g_irrlicht == NULL)
\r
397 return g_irrlicht->getTime();
\r
404 struct TextDestSign : public TextDest
\r
406 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
408 m_blockpos = blockpos;
\r
412 void gotText(std::wstring text)
\r
414 std::string ntext = wide_to_narrow(text);
\r
415 dstream<<"Changing text of a sign object: "
\r
416 <<ntext<<std::endl;
\r
417 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
425 struct TextDestChat : public TextDest
\r
427 TextDestChat(Client *client)
\r
431 void gotText(std::wstring text)
\r
433 m_client->sendChatMessage(text);
\r
434 m_client->addChatMessage(text);
\r
440 class MyEventReceiver : public IEventReceiver
\r
443 // This is the one method that we have to implement
\r
444 virtual bool OnEvent(const SEvent& event)
\r
447 React to nothing here if a menu is active
\r
449 if(noMenuActive() == false)
\r
455 // Remember whether each key is down or up
\r
456 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
458 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
460 if(event.KeyInput.PressedDown)
\r
462 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
468 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
470 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
472 dstream<<DTIME<<"MyEventReceiver: "
\r
473 <<"Launching pause menu"<<std::endl;
\r
474 // It will delete itself by itself
\r
475 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
476 &g_active_menu_count))->drop();
\r
479 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
481 dstream<<DTIME<<"MyEventReceiver: "
\r
482 <<"Launching inventory"<<std::endl;
\r
483 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
484 &local_inventory, &inventory_action_queue,
\r
485 &g_active_menu_count))->drop();
\r
488 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
490 TextDest *dest = new TextDestChat(g_client);
\r
492 (new GUITextInputMenu(guienv, guiroot, -1,
\r
493 &g_active_menu_count, dest,
\r
498 // Material selection
\r
499 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
501 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
504 g_selected_item = 0;
\r
505 dstream<<DTIME<<"Selected item: "
\r
506 <<g_selected_item<<std::endl;
\r
509 // Viewing range selection
\r
510 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
512 if(draw_control.range_all)
\r
514 draw_control.range_all = false;
\r
515 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
519 draw_control.range_all = true;
\r
520 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
524 // Print debug stacks
\r
525 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
527 dstream<<"-----------------------------------------"
\r
529 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
530 dstream<<"-----------------------------------------"
\r
532 debug_stacks_print();
\r
537 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
539 if(noMenuActive() == false)
\r
541 left_active = false;
\r
542 middle_active = false;
\r
543 right_active = false;
\r
547 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
548 left_active = event.MouseInput.isLeftPressed();
\r
549 middle_active = event.MouseInput.isMiddlePressed();
\r
550 right_active = event.MouseInput.isRightPressed();
\r
552 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
554 leftclicked = true;
\r
556 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
558 rightclicked = true;
\r
560 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
562 leftreleased = true;
\r
564 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
566 rightreleased = true;
\r
568 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
570 /*dstream<<"event.MouseInput.Wheel="
\r
571 <<event.MouseInput.Wheel<<std::endl;*/
\r
572 if(event.MouseInput.Wheel < 0)
\r
574 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
577 g_selected_item = 0;
\r
579 else if(event.MouseInput.Wheel > 0)
\r
581 if(g_selected_item > 0)
\r
584 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
593 // This is used to check whether a key is being held down
\r
594 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
596 return keyIsDown[keyCode];
\r
601 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
602 keyIsDown[i] = false;
\r
604 leftclicked = false;
\r
605 rightclicked = false;
\r
606 leftreleased = false;
\r
607 rightreleased = false;
\r
609 left_active = false;
\r
610 middle_active = false;
\r
611 right_active = false;
\r
622 bool rightreleased;
\r
625 bool middle_active;
\r
629 // We use this array to store the current state of each key
\r
630 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
633 IrrlichtDevice *m_device;
\r
642 virtual ~InputHandler()
\r
646 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
648 virtual v2s32 getMousePos() = 0;
\r
649 virtual void setMousePos(s32 x, s32 y) = 0;
\r
651 virtual bool getLeftState() = 0;
\r
652 virtual bool getRightState() = 0;
\r
654 virtual bool getLeftClicked() = 0;
\r
655 virtual bool getRightClicked() = 0;
\r
656 virtual void resetLeftClicked() = 0;
\r
657 virtual void resetRightClicked() = 0;
\r
659 virtual bool getLeftReleased() = 0;
\r
660 virtual bool getRightReleased() = 0;
\r
661 virtual void resetLeftReleased() = 0;
\r
662 virtual void resetRightReleased() = 0;
\r
664 virtual void step(float dtime) {};
\r
666 virtual void clear() {};
\r
669 InputHandler *g_input = NULL;
\r
671 class RealInputHandler : public InputHandler
\r
674 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
676 m_receiver(receiver)
\r
679 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
681 return m_receiver->IsKeyDown(keyCode);
\r
683 virtual v2s32 getMousePos()
\r
685 return m_device->getCursorControl()->getPosition();
\r
687 virtual void setMousePos(s32 x, s32 y)
\r
689 m_device->getCursorControl()->setPosition(x, y);
\r
692 virtual bool getLeftState()
\r
694 return m_receiver->left_active;
\r
696 virtual bool getRightState()
\r
698 return m_receiver->right_active;
\r
701 virtual bool getLeftClicked()
\r
703 return m_receiver->leftclicked;
\r
705 virtual bool getRightClicked()
\r
707 return m_receiver->rightclicked;
\r
709 virtual void resetLeftClicked()
\r
711 m_receiver->leftclicked = false;
\r
713 virtual void resetRightClicked()
\r
715 m_receiver->rightclicked = false;
\r
718 virtual bool getLeftReleased()
\r
720 return m_receiver->leftreleased;
\r
722 virtual bool getRightReleased()
\r
724 return m_receiver->rightreleased;
\r
726 virtual void resetLeftReleased()
\r
728 m_receiver->leftreleased = false;
\r
730 virtual void resetRightReleased()
\r
732 m_receiver->rightreleased = false;
\r
737 resetRightClicked();
\r
738 resetLeftClicked();
\r
741 IrrlichtDevice *m_device;
\r
742 MyEventReceiver *m_receiver;
\r
745 class RandomInputHandler : public InputHandler
\r
748 RandomInputHandler()
\r
750 leftclicked = false;
\r
751 rightclicked = false;
\r
752 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
753 keydown[i] = false;
\r
755 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
757 return keydown[keyCode];
\r
759 virtual v2s32 getMousePos()
\r
763 virtual void setMousePos(s32 x, s32 y)
\r
765 mousepos = v2s32(x,y);
\r
768 virtual bool getLeftState()
\r
772 virtual bool getRightState()
\r
777 virtual bool getLeftClicked()
\r
779 return leftclicked;
\r
781 virtual bool getRightClicked()
\r
783 return rightclicked;
\r
785 virtual void resetLeftClicked()
\r
787 leftclicked = false;
\r
789 virtual void resetRightClicked()
\r
791 rightclicked = false;
\r
794 virtual bool getLeftReleased()
\r
798 virtual bool getRightReleased()
\r
802 virtual void resetLeftReleased()
\r
805 virtual void resetRightReleased()
\r
809 virtual void step(float dtime)
\r
812 static float counter1 = 0;
\r
816 counter1 = 0.1*Rand(1,10);
\r
817 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
818 g_selected_material++;
\r
820 g_selected_material = 0;*/
\r
821 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
824 g_selected_item = 0;
\r
828 static float counter1 = 0;
\r
832 counter1 = 0.1*Rand(1, 40);
\r
833 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
837 static float counter1 = 0;
\r
841 counter1 = 0.1*Rand(1, 40);
\r
842 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
846 static float counter1 = 0;
\r
850 counter1 = 0.1*Rand(1, 40);
\r
851 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
855 static float counter1 = 0;
\r
859 counter1 = 0.1*Rand(1, 40);
\r
860 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
864 static float counter1 = 0;
\r
868 counter1 = 0.1*Rand(1, 20);
\r
869 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
873 static float counter1 = 0;
\r
877 counter1 = 0.1*Rand(1, 30);
\r
878 leftclicked = true;
\r
882 static float counter1 = 0;
\r
886 counter1 = 0.1*Rand(1, 20);
\r
887 rightclicked = true;
\r
890 mousepos += mousespeed;
\r
893 s32 Rand(s32 min, s32 max)
\r
895 return (myrand()%(max-min+1))+min;
\r
898 bool keydown[KEY_KEY_CODES_COUNT];
\r
905 void updateViewingRange(f32 frametime_in, Client *client)
\r
907 if(draw_control.range_all == true)
\r
910 static f32 added_frametime = 0;
\r
911 static s16 added_frames = 0;
\r
913 added_frametime += frametime_in;
\r
916 // Actually this counter kind of sucks because frametime is busytime
\r
917 static f32 counter = 0;
\r
918 counter -= frametime_in;
\r
924 /*dstream<<__FUNCTION_NAME
\r
925 <<": Collected "<<added_frames<<" frames, total of "
\r
926 <<added_frametime<<"s."<<std::endl;*/
\r
928 /*dstream<<"draw_control.blocks_drawn="
\r
929 <<draw_control.blocks_drawn
\r
930 <<", draw_control.blocks_would_have_drawn="
\r
931 <<draw_control.blocks_would_have_drawn
\r
934 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
935 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
937 draw_control.wanted_min_range = range_min;
\r
938 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
940 float block_draw_ratio = 1.0;
\r
941 if(draw_control.blocks_would_have_drawn != 0)
\r
943 block_draw_ratio = (float)draw_control.blocks_drawn
\r
944 / (float)draw_control.blocks_would_have_drawn;
\r
947 // Calculate the average frametime in the case that all wanted
\r
948 // blocks had been drawn
\r
949 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
951 added_frametime = 0.0;
\r
954 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
955 float wanted_frametime = 1.0 / wanted_fps;
\r
957 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
958 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
960 // If needed frametime change is very small, just return
\r
961 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
963 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
967 float range = draw_control.wanted_range;
\r
968 float new_range = range;
\r
970 static s16 range_old = 0;
\r
971 static f32 frametime_old = 0;
\r
973 float d_range = range - range_old;
\r
974 f32 d_frametime = frametime - frametime_old;
\r
975 // A sane default of 30ms per 50 nodes of range
\r
976 static f32 time_per_range = 30. / 50;
\r
979 time_per_range = d_frametime / d_range;
\r
982 // The minimum allowed calculated frametime-range derivative:
\r
983 // Practically this sets the maximum speed of changing the range.
\r
984 // The lower this value, the higher the maximum changing speed.
\r
985 // A low value here results in wobbly range (0.001)
\r
986 // A high value here results in slow changing range (0.0025)
\r
987 // SUGG: This could be dynamically adjusted so that when
\r
988 // the camera is turning, this is lower
\r
989 //float min_time_per_range = 0.0015;
\r
990 float min_time_per_range = 0.0010;
\r
991 //float min_time_per_range = 0.05 / range;
\r
992 if(time_per_range < min_time_per_range)
\r
994 time_per_range = min_time_per_range;
\r
995 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
999 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1002 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1003 // Dampen the change a bit to kill oscillations
\r
1004 //wanted_range_change *= 0.9;
\r
1005 //wanted_range_change *= 0.75;
\r
1006 wanted_range_change *= 0.5;
\r
1007 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1009 // If needed range change is very small, just return
\r
1010 if(fabs(wanted_range_change) < 0.001)
\r
1012 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1016 new_range += wanted_range_change;
\r
1017 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1019 //float new_range_unclamped = new_range;
\r
1020 if(new_range < range_min)
\r
1021 new_range = range_min;
\r
1022 if(new_range > range_max)
\r
1023 new_range = range_max;
\r
1025 /*if(new_range != new_range_unclamped)
\r
1026 dstream<<", clamped to "<<new_range<<std::endl;
\r
1028 dstream<<std::endl;*/
\r
1030 draw_control.wanted_range = new_range;
\r
1032 range_old = new_range;
\r
1033 frametime_old = frametime;
\r
1036 class GUIQuickInventory : public IEventReceiver
\r
1039 GUIQuickInventory(
\r
1040 gui::IGUIEnvironment* env,
\r
1041 gui::IGUIElement* parent,
\r
1044 Inventory *inventory):
\r
1045 m_itemcount(itemcount),
\r
1046 m_inventory(inventory)
\r
1048 core::rect<s32> imgsize(0,0,48,48);
\r
1049 core::rect<s32> textsize(0,0,48,16);
\r
1050 v2s32 spacing(0, 64);
\r
1051 for(s32 i=0; i<m_itemcount; i++)
\r
1053 m_images.push_back(env->addImage(
\r
1054 imgsize + pos + spacing*i
\r
1056 m_images[i]->setScaleImage(true);
\r
1057 m_texts.push_back(env->addStaticText(
\r
1059 textsize + pos + spacing*i,
\r
1062 m_texts[i]->setBackgroundColor(
\r
1063 video::SColor(128,0,0,0));
\r
1064 m_texts[i]->setTextAlignment(
\r
1065 gui::EGUIA_CENTER,
\r
1066 gui::EGUIA_UPPERLEFT);
\r
1070 virtual bool OnEvent(const SEvent& event)
\r
1075 void setSelection(s32 i)
\r
1084 start = m_selection - m_itemcount / 2;
\r
1086 InventoryList *mainlist = m_inventory->getList("main");
\r
1088 for(s32 i=0; i<m_itemcount; i++)
\r
1090 s32 j = i + start;
\r
1092 if(j > (s32)mainlist->getSize() - 1)
\r
1093 j -= mainlist->getSize();
\r
1095 j += mainlist->getSize();
\r
1097 InventoryItem *item = mainlist->getItem(j);
\r
1101 m_images[i]->setImage(NULL);
\r
1104 if(m_selection == j)
\r
1105 swprintf(t, 10, L"<-");
\r
1107 swprintf(t, 10, L"");
\r
1108 m_texts[i]->setText(t);
\r
1110 // The next ifs will segfault with a NULL pointer
\r
1115 m_images[i]->setImage(item->getImage());
\r
1118 if(m_selection == j)
\r
1119 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1121 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1122 m_texts[i]->setText(t);
\r
1128 core::array<gui::IGUIStaticText*> m_texts;
\r
1129 core::array<gui::IGUIImage*> m_images;
\r
1130 Inventory *m_inventory;
\r
1141 ChatLine(const std::wstring &a_text):
\r
1147 std::wstring text;
\r
1150 int main(int argc, char *argv[])
\r
1153 Low-level initialization
\r
1156 bool disable_stderr = false;
\r
1158 disable_stderr = true;
\r
1161 // Initialize debug streams
\r
1162 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1163 // Initialize debug stacks
\r
1164 debug_stacks_init();
\r
1166 DSTACK(__FUNCTION_NAME);
\r
1168 porting::initializePaths();
\r
1169 // Create user data directory
\r
1170 fs::CreateDir(porting::path_userdata);
\r
1172 initializeMaterialProperties();
\r
1174 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1176 // Print startup message
\r
1177 dstream<<DTIME<<"minetest-c55"
\r
1178 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1179 <<", "<<BUILD_INFO
\r
1186 Parse command line
\r
1189 // List all allowed options
\r
1190 core::map<std::string, ValueSpec> allowed_options;
\r
1191 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1192 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1193 "Run server directly"));
\r
1194 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1195 "Load configuration from specified file"));
\r
1196 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1197 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1198 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1199 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1200 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1201 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1203 Settings cmd_args;
\r
1205 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1207 if(ret == false || cmd_args.getFlag("help"))
\r
1209 dstream<<"Allowed options:"<<std::endl;
\r
1210 for(core::map<std::string, ValueSpec>::Iterator
\r
1211 i = allowed_options.getIterator();
\r
1212 i.atEnd() == false; i++)
\r
1214 dstream<<" --"<<i.getNode()->getKey();
\r
1215 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1220 dstream<<" <value>";
\r
1222 dstream<<std::endl;
\r
1224 if(i.getNode()->getValue().help != NULL)
\r
1226 dstream<<" "<<i.getNode()->getValue().help
\r
1231 return cmd_args.getFlag("help") ? 0 : 1;
\r
1236 Basic initialization
\r
1239 // Initialize default settings
\r
1240 set_default_settings();
\r
1242 // Set locale. This is for forcing '.' as the decimal point.
\r
1243 std::locale::global(std::locale("C"));
\r
1244 // This enables printing all characters in bitmap font
\r
1245 setlocale(LC_CTYPE, "en_US");
\r
1247 // Initialize sockets
\r
1249 atexit(sockets_cleanup);
\r
1259 // Path of configuration file in use
\r
1260 std::string configpath = "";
\r
1262 if(cmd_args.exists("config"))
\r
1264 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1267 dstream<<"Could not read configuration from \""
\r
1268 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1271 configpath = cmd_args.get("config");
\r
1275 core::array<std::string> filenames;
\r
1276 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1277 #ifdef RUN_IN_PLACE
\r
1278 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1281 for(u32 i=0; i<filenames.size(); i++)
\r
1283 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1286 configpath = filenames[i];
\r
1292 // Initialize random seed
\r
1299 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1300 || cmd_args.getFlag("enable-unittests") == true)
\r
1305 // Read map parameters from settings
\r
1307 HMParams hm_params;
\r
1308 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1309 hm_params.randmax = g_settings.get("height_randmax");
\r
1310 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1311 hm_params.base = g_settings.get("height_base");*/
\r
1313 MapParams map_params;
\r
1314 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1315 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1321 std::cout<<std::endl<<std::endl;
\r
1324 <<" .__ __ __ "<<std::endl
\r
1325 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1326 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1327 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1328 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1329 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1332 std::cout<<std::endl;
\r
1333 //char templine[100];
\r
1337 if(cmd_args.exists("port"))
\r
1339 port = cmd_args.getU16("port");
\r
1343 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1344 std::cout<<"-> "<<port<<std::endl;
\r
1348 std::string map_dir = porting::path_userdata+"/map";
\r
1349 if(cmd_args.exists("map-dir"))
\r
1350 map_dir = cmd_args.get("map-dir");
\r
1351 else if(g_settings.exists("map-dir"))
\r
1352 map_dir = g_settings.get("map-dir");
\r
1354 if(cmd_args.getFlag("server"))
\r
1356 DSTACK("Dedicated server branch");
\r
1358 std::cout<<std::endl;
\r
1359 std::cout<<"========================"<<std::endl;
\r
1360 std::cout<<"Running dedicated server"<<std::endl;
\r
1361 std::cout<<"========================"<<std::endl;
\r
1362 std::cout<<std::endl;
\r
1364 Server server(map_dir, hm_params, map_params);
\r
1365 server.start(port);
\r
1369 // This is kind of a hack but can be done like this
\r
1370 // because server.step() is very light
\r
1372 server.step(0.030);
\r
1374 static int counter = 0;
\r
1380 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1381 core::list<PlayerInfo>::Iterator i;
\r
1382 static u32 sum_old = 0;
\r
1383 u32 sum = PIChecksum(list);
\r
1384 if(sum != sum_old)
\r
1386 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1387 for(i=list.begin(); i!=list.end(); i++)
\r
1389 i->PrintLine(&std::cout);
\r
1399 bool hosting = false;
\r
1400 char connect_name[100] = "";
\r
1402 if(cmd_args.exists("address"))
\r
1404 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1406 else if(is_yes(g_settings.get("host_game")) == false)
\r
1408 if(g_settings.get("address") != "")
\r
1410 std::cout<<g_settings.get("address")<<std::endl;
\r
1411 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1415 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1416 std::cin.getline(connect_name, 100);
\r
1420 if(connect_name[0] == 0){
\r
1421 snprintf(connect_name, 100, "127.0.0.1");
\r
1426 std::cout<<"> Hosting game"<<std::endl;
\r
1428 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1430 char playername[PLAYERNAME_SIZE] = "";
\r
1431 if(g_settings.get("name") != "")
\r
1433 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1437 std::cout<<"Name of player: ";
\r
1438 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1440 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1443 Resolution selection
\r
1446 bool fullscreen = false;
\r
1447 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1448 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1452 MyEventReceiver receiver;
\r
1454 video::E_DRIVER_TYPE driverType;
\r
1457 //driverType = video::EDT_DIRECT3D9;
\r
1458 driverType = video::EDT_OPENGL;
\r
1460 driverType = video::EDT_OPENGL;
\r
1461 //driverType = video::EDT_BURNINGSVIDEO;
\r
1464 // create device and exit if creation failed
\r
1466 IrrlichtDevice *device;
\r
1467 device = createDevice(driverType,
\r
1468 core::dimension2d<u32>(screenW, screenH),
\r
1469 16, fullscreen, false, false, &receiver);
\r
1472 return 1; // could not create selected driver.
\r
1474 g_device = device;
\r
1475 g_irrlicht = new IrrlichtWrapper(device);
\r
1477 //g_device = device;
\r
1479 device->setResizable(true);
\r
1481 bool random_input = g_settings.getBool("random_input")
\r
1482 || cmd_args.getFlag("random-input");
\r
1484 g_input = new RandomInputHandler();
\r
1486 g_input = new RealInputHandler(device, &receiver);
\r
1489 Continue initialization
\r
1492 video::IVideoDriver* driver = device->getVideoDriver();
\r
1495 This changes the minimum allowed number of vertices in a VBO
\r
1497 //driver->setMinHardwareBufferVertexCount(50);
\r
1499 scene::ISceneManager* smgr = device->getSceneManager();
\r
1501 guienv = device->getGUIEnvironment();
\r
1502 gui::IGUISkin* skin = guienv->getSkin();
\r
1503 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1505 skin->setFont(font);
\r
1507 dstream<<"WARNING: Font file was not found."
\r
1508 " Using default font."<<std::endl;
\r
1509 // If font was not found, this will get us one
\r
1510 font = skin->getFont();
\r
1513 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1514 dstream<<"text_height="<<text_height<<std::endl;
\r
1516 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1517 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1518 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1519 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1520 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1521 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1523 const wchar_t *text = L"Loading and connecting...";
\r
1524 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1525 core::vector2d<s32> textsize(300, text_height);
\r
1526 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1528 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1529 text, textrect, false, false);
\r
1530 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1532 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1533 guienv->drawAll();
\r
1534 driver->endScene();
\r
1537 Preload some textures
\r
1540 init_content_inventory_texture_paths();
\r
1541 init_tile_texture_paths();
\r
1542 tile_materials_preload(g_irrlicht);
\r
1545 Make a scope here for the client so that it gets removed
\r
1546 before the irrlicht device
\r
1550 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1555 SharedPtr<Server> server;
\r
1557 server = new Server(map_dir, hm_params, map_params);
\r
1558 server->start(port);
\r
1565 Client client(device, playername, draw_control);
\r
1567 g_client = &client;
\r
1569 Address connect_address(0,0,0,0, port);
\r
1571 connect_address.Resolve(connect_name);
\r
1573 catch(ResolveError &e)
\r
1575 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1579 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1580 client.connect(connect_address);
\r
1583 while(client.connectedAndInitialized() == false)
\r
1586 if(server != NULL){
\r
1587 server->step(0.1);
\r
1592 catch(con::PeerNotFoundException &e)
\r
1594 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1601 /*scene::ISceneNode* skybox;
\r
1602 skybox = smgr->addSkyBoxSceneNode(
\r
1603 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1604 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1605 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1606 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1607 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1608 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1611 Create the camera node
\r
1614 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1615 0, // Camera parent
\r
1616 v3f(BS*100, BS*2, BS*100), // Look from
\r
1617 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1621 if(camera == NULL)
\r
1624 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1626 camera->setFOV(FOV_ANGLE);
\r
1628 // Just so big a value that everything rendered is visible
\r
1629 camera->setFarValue(100000*BS);
\r
1631 f32 camera_yaw = 0; // "right/left"
\r
1632 f32 camera_pitch = 0; // "up/down"
\r
1638 gui_loadingtext->remove();
\r
1641 Add some gui stuff
\r
1644 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1645 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1648 We need some kind of a root node to be able to add
\r
1649 custom elements directly on the screen.
\r
1650 Otherwise they won't be automatically drawn.
\r
1652 guiroot = guienv->addStaticText(L"",
\r
1653 core::rect<s32>(0, 0, 10000, 10000));
\r
1655 // Test the text input system
\r
1656 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1658 /*GUIMessageMenu *menu =
\r
1659 new GUIMessageMenu(guienv, guiroot, -1,
\r
1660 &g_active_menu_count,
\r
1664 // Launch pause menu
\r
1665 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
1666 &g_active_menu_count))->drop();
\r
1668 // First line of debug text
\r
1669 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1671 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1673 // Second line of debug text
\r
1674 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1676 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1679 // At the middle of the screen
\r
1680 // Object infos are shown in this
\r
1681 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1683 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1687 gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
\r
1688 L"Chat here\nOther line\nOther line\nOther line\nOther line",
\r
1689 core::rect<s32>(70, 60, 795, 150),
\r
1691 chat_guitext->setBackgroundColor(video::SColor(96,0,0,0));
\r
1692 core::list<ChatLine> chat_lines;
\r
1695 Some statistics are collected in these
\r
1698 u32 beginscenetime = 0;
\r
1699 u32 scenetime = 0;
\r
1700 u32 endscenetime = 0;
\r
1703 //throw con::PeerNotFoundException("lol");
\r
1709 bool first_loop_after_window_activation = true;
\r
1711 // Time is in milliseconds
\r
1712 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1713 // NOTE: So we have to use getTime() and call run()s between them
\r
1714 u32 lasttime = device->getTimer()->getTime();
\r
1716 while(device->run())
\r
1719 Run global IrrlichtWrapper's main thread processing stuff
\r
1721 g_irrlicht->Run();
\r
1724 Random calculations
\r
1726 v2u32 screensize = driver->getScreenSize();
\r
1727 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1729 // Hilight boxes collected during the loop and displayed
\r
1730 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1733 std::wstring infotext;
\r
1735 //TimeTaker //timer1("//timer1");
\r
1737 // Time of frame without fps limit
\r
1741 // not using getRealTime is necessary for wine
\r
1742 u32 time = device->getTimer()->getTime();
\r
1743 if(time > lasttime)
\r
1744 busytime_u32 = time - lasttime;
\r
1747 busytime = busytime_u32 / 1000.0;
\r
1750 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1752 // Absolutelu necessary for wine!
\r
1759 updateViewingRange(busytime, &client);
\r
1766 float fps_max = g_settings.getFloat("fps_max");
\r
1767 u32 frametime_min = 1000./fps_max;
\r
1769 if(busytime_u32 < frametime_min)
\r
1771 u32 sleeptime = frametime_min - busytime_u32;
\r
1772 device->sleep(sleeptime);
\r
1776 // Absolutelu necessary for wine!
\r
1780 Time difference calculation
\r
1782 f32 dtime; // in seconds
\r
1784 u32 time = device->getTimer()->getTime();
\r
1785 if(time > lasttime)
\r
1786 dtime = (time - lasttime) / 1000.0;
\r
1792 Time average and jitter calculation
\r
1795 static f32 dtime_avg1 = 0.0;
\r
1796 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1797 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1799 static f32 dtime_jitter1_max_sample = 0.0;
\r
1800 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1802 static f32 jitter1_max = 0.0;
\r
1803 static f32 counter = 0.0;
\r
1804 if(dtime_jitter1 > jitter1_max)
\r
1805 jitter1_max = dtime_jitter1;
\r
1810 dtime_jitter1_max_sample = jitter1_max;
\r
1811 dtime_jitter1_max_fraction
\r
1812 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1813 jitter1_max = 0.0;
\r
1816 Control freetime ratio
\r
1818 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1820 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1821 g_freetime_ratio += 0.01;
\r
1825 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1826 g_freetime_ratio -= 0.01;
\r
1832 Busytime average and jitter calculation
\r
1835 static f32 busytime_avg1 = 0.0;
\r
1836 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1837 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1839 static f32 busytime_jitter1_max_sample = 0.0;
\r
1840 static f32 busytime_jitter1_min_sample = 0.0;
\r
1842 static f32 jitter1_max = 0.0;
\r
1843 static f32 jitter1_min = 0.0;
\r
1844 static f32 counter = 0.0;
\r
1845 if(busytime_jitter1 > jitter1_max)
\r
1846 jitter1_max = busytime_jitter1;
\r
1847 if(busytime_jitter1 < jitter1_min)
\r
1848 jitter1_min = busytime_jitter1;
\r
1850 if(counter > 0.0){
\r
1852 busytime_jitter1_max_sample = jitter1_max;
\r
1853 busytime_jitter1_min_sample = jitter1_min;
\r
1854 jitter1_max = 0.0;
\r
1855 jitter1_min = 0.0;
\r
1860 Debug info for client
\r
1863 static float counter = 0.0;
\r
1868 client.printDebugInfo(std::cout);
\r
1873 Input handler step()
\r
1875 g_input->step(dtime);
\r
1878 Player speed control
\r
1887 bool a_superspeed,
\r
1890 PlayerControl control(
\r
1891 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1892 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1893 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1894 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1895 g_input->isKeyDown(irr::KEY_SPACE),
\r
1896 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1900 client.setPlayerControl(control);
\r
1904 Process environment
\r
1908 //TimeTaker timer("client.step(dtime)");
\r
1909 client.step(dtime);
\r
1910 //client.step(dtime_avg1);
\r
1913 if(server != NULL)
\r
1915 //TimeTaker timer("server->step(dtime)");
\r
1916 server->step(dtime);
\r
1919 v3f player_position = client.getPlayerPosition();
\r
1921 //TimeTaker //timer2("//timer2");
\r
1924 Mouse and camera control
\r
1927 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1930 device->getCursorControl()->setVisible(false);
\r
1932 if(first_loop_after_window_activation){
\r
1933 //std::cout<<"window active, first loop"<<std::endl;
\r
1934 first_loop_after_window_activation = false;
\r
1937 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1938 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1939 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1940 camera_yaw -= dx*0.2;
\r
1941 camera_pitch += dy*0.2;
\r
1942 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1943 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1945 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1948 device->getCursorControl()->setVisible(true);
\r
1950 //std::cout<<"window inactive"<<std::endl;
\r
1951 first_loop_after_window_activation = true;
\r
1954 camera_yaw = wrapDegrees(camera_yaw);
\r
1955 camera_pitch = wrapDegrees(camera_pitch);
\r
1957 v3f camera_direction = v3f(0,0,1);
\r
1958 camera_direction.rotateYZBy(camera_pitch);
\r
1959 camera_direction.rotateXZBy(camera_yaw);
\r
1961 // This is at the height of the eyes of the current figure
\r
1962 v3f camera_position =
\r
1963 player_position + v3f(0, BS+BS/2, 0);
\r
1964 // This is more like in minecraft
\r
1965 /*v3f camera_position =
\r
1966 player_position + v3f(0, BS+BS*0.65, 0);*/
\r
1968 camera->setPosition(camera_position);
\r
1969 // *100.0 helps in large map coordinates
\r
1970 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1972 if(FIELD_OF_VIEW_TEST){
\r
1973 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1974 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1977 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1978 //TimeTaker timer("client.updateCamera");
\r
1979 client.updateCamera(camera_position, camera_direction);
\r
1983 //TimeTaker //timer3("//timer3");
\r
1986 Calculate what block is the crosshair pointing to
\r
1989 //u32 t1 = device->getTimer()->getRealTime();
\r
1991 //f32 d = 4; // max. distance
\r
1992 f32 d = 4; // max. distance
\r
1993 core::line3d<f32> shootline(camera_position,
\r
1994 camera_position + camera_direction * BS * (d+1));
\r
1996 MapBlockObject *selected_object = client.getSelectedObject
\r
1997 (d*BS, camera_position, shootline);
\r
2000 If it's pointing to a MapBlockObject
\r
2003 if(selected_object != NULL)
\r
2005 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2007 core::aabbox3d<f32> box_on_map
\r
2008 = selected_object->getSelectionBoxOnMap();
\r
2010 hilightboxes.push_back(box_on_map);
\r
2012 infotext = narrow_to_wide(selected_object->infoText());
\r
2014 if(g_input->getLeftClicked())
\r
2016 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2017 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2018 selected_object->getId(), g_selected_item);
\r
2020 else if(g_input->getRightClicked())
\r
2022 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2024 Check if we want to modify the object ourselves
\r
2026 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2028 dstream<<"Sign object right-clicked"<<std::endl;
\r
2030 if(random_input == false)
\r
2032 // Get a new text for it
\r
2034 TextDest *dest = new TextDestSign(
\r
2035 selected_object->getBlock()->getPos(),
\r
2036 selected_object->getId(),
\r
2039 SignObject *sign_object = (SignObject*)selected_object;
\r
2041 std::wstring wtext =
\r
2042 narrow_to_wide(sign_object->getText());
\r
2044 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2045 &g_active_menu_count, dest,
\r
2050 Otherwise pass the event to the server as-is
\r
2054 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2055 selected_object->getId(), g_selected_item);
\r
2059 else // selected_object == NULL
\r
2063 Find out which node we are pointing at
\r
2066 bool nodefound = false;
\r
2068 v3s16 neighbourpos;
\r
2069 core::aabbox3d<f32> nodefacebox;
\r
2070 f32 mindistance = BS * 1001;
\r
2072 v3s16 pos_i = floatToInt(player_position);
\r
2074 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2078 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2079 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2080 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2081 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2082 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2083 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2085 for(s16 y = ystart; y <= yend; y++)
\r
2086 for(s16 z = zstart; z <= zend; z++)
\r
2087 for(s16 x = xstart; x <= xend; x++)
\r
2092 n = client.getNode(v3s16(x,y,z));
\r
2093 if(content_pointable(n.d) == false)
\r
2096 catch(InvalidPositionException &e)
\r
2102 v3f npf = intToFloat(np);
\r
2107 v3s16(0,0,1), // back
\r
2108 v3s16(0,1,0), // top
\r
2109 v3s16(1,0,0), // right
\r
2110 v3s16(0,0,-1), // front
\r
2111 v3s16(0,-1,0), // bottom
\r
2112 v3s16(-1,0,0), // left
\r
2118 if(n.d == CONTENT_TORCH)
\r
2120 v3s16 dir = unpackDir(n.dir);
\r
2121 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2122 dir_f *= BS/2 - BS/6 - BS/20;
\r
2123 v3f cpf = npf + dir_f;
\r
2124 f32 distance = (cpf - camera_position).getLength();
\r
2126 core::aabbox3d<f32> box;
\r
2129 if(dir == v3s16(0,-1,0))
\r
2131 box = core::aabbox3d<f32>(
\r
2132 npf - v3f(BS/6, BS/2, BS/6),
\r
2133 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2137 else if(dir == v3s16(0,1,0))
\r
2139 box = core::aabbox3d<f32>(
\r
2140 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2141 npf + v3f(BS/6, BS/2, BS/6)
\r
2147 box = core::aabbox3d<f32>(
\r
2148 cpf - v3f(BS/6, BS/3, BS/6),
\r
2149 cpf + v3f(BS/6, BS/3, BS/6)
\r
2153 if(distance < mindistance)
\r
2155 if(box.intersectsWithLine(shootline))
\r
2159 neighbourpos = np;
\r
2160 mindistance = distance;
\r
2161 nodefacebox = box;
\r
2170 for(u16 i=0; i<6; i++)
\r
2172 v3f dir_f = v3f(dirs[i].X,
\r
2173 dirs[i].Y, dirs[i].Z);
\r
2174 v3f centerpoint = npf + dir_f * BS/2;
\r
2176 (centerpoint - camera_position).getLength();
\r
2178 if(distance < mindistance)
\r
2180 core::CMatrix4<f32> m;
\r
2181 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2183 // This is the back face
\r
2184 v3f corners[2] = {
\r
2185 v3f(BS/2, BS/2, BS/2),
\r
2186 v3f(-BS/2, -BS/2, BS/2+d)
\r
2189 for(u16 j=0; j<2; j++)
\r
2191 m.rotateVect(corners[j]);
\r
2192 corners[j] += npf;
\r
2195 core::aabbox3d<f32> facebox(corners[0]);
\r
2196 facebox.addInternalPoint(corners[1]);
\r
2198 if(facebox.intersectsWithLine(shootline))
\r
2202 neighbourpos = np + dirs[i];
\r
2203 mindistance = distance;
\r
2204 nodefacebox = facebox;
\r
2206 } // if distance < mindistance
\r
2208 } // regular block
\r
2211 static float nodig_delay_counter = 0.0;
\r
2215 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2217 static float dig_time = 0.0;
\r
2218 static u16 dig_index = 0;
\r
2220 hilightboxes.push_back(nodefacebox);
\r
2222 if(g_input->getLeftReleased())
\r
2224 client.clearTempMod(nodepos);
\r
2228 if(nodig_delay_counter > 0.0)
\r
2230 nodig_delay_counter -= dtime;
\r
2234 if(nodepos != nodepos_old)
\r
2236 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2237 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2239 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2241 client.clearTempMod(nodepos_old);
\r
2246 if(g_input->getLeftClicked() ||
\r
2247 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2249 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2250 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2252 if(g_input->getLeftClicked())
\r
2254 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2256 if(g_input->getLeftState())
\r
2258 MapNode n = client.getNode(nodepos);
\r
2260 // Get tool name. Default is "" = bare hands
\r
2261 std::string toolname = "";
\r
2262 InventoryList *mlist = local_inventory.getList("main");
\r
2265 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2266 if(item && (std::string)item->getName() == "ToolItem")
\r
2268 ToolItem *titem = (ToolItem*)item;
\r
2269 toolname = titem->getToolName();
\r
2273 // Get digging properties for material and tool
\r
2274 u8 material = n.d;
\r
2275 DiggingProperties prop =
\r
2276 getDiggingProperties(material, toolname);
\r
2278 float dig_time_complete = 0.0;
\r
2280 if(prop.diggable == false)
\r
2282 /*dstream<<"Material "<<(int)material
\r
2283 <<" not diggable with \""
\r
2284 <<toolname<<"\""<<std::endl;*/
\r
2285 // I guess nobody will wait for this long
\r
2286 dig_time_complete = 10000000.0;
\r
2290 dig_time_complete = prop.time;
\r
2293 if(dig_time_complete >= 0.001)
\r
2295 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2296 * dig_time/dig_time_complete);
\r
2298 // This is for torches
\r
2301 dig_index = CRACK_ANIMATION_LENGTH;
\r
2304 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2306 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2307 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2311 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2312 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2313 client.clearTempMod(nodepos);
\r
2314 client.removeNode(nodepos);
\r
2318 nodig_delay_counter = dig_time_complete
\r
2319 / (float)CRACK_ANIMATION_LENGTH;
\r
2321 // We don't want a corresponding delay to
\r
2322 // very time consuming nodes
\r
2323 if(nodig_delay_counter > 0.5)
\r
2325 nodig_delay_counter = 0.5;
\r
2327 // We want a slight delay to very little
\r
2328 // time consuming nodes
\r
2329 //float mindelay = 0.15;
\r
2330 float mindelay = 0.20;
\r
2331 if(nodig_delay_counter < mindelay)
\r
2333 nodig_delay_counter = mindelay;
\r
2337 dig_time += dtime;
\r
2341 if(g_input->getRightClicked())
\r
2343 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2344 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2347 nodepos_old = nodepos;
\r
2352 } // selected_object == NULL
\r
2354 g_input->resetLeftClicked();
\r
2355 g_input->resetRightClicked();
\r
2357 if(g_input->getLeftReleased())
\r
2359 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2361 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2363 if(g_input->getRightReleased())
\r
2365 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2369 g_input->resetLeftReleased();
\r
2370 g_input->resetRightReleased();
\r
2373 Calculate stuff for drawing
\r
2376 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2378 u32 daynight_ratio = client.getDayNightRatio();
\r
2379 /*video::SColor bgcolor = video::SColor(
\r
2381 skycolor.getRed() * daynight_ratio / 1000,
\r
2382 skycolor.getGreen() * daynight_ratio / 1000,
\r
2383 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2385 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2386 video::SColor bgcolor = video::SColor(
\r
2388 skycolor.getRed() * l / 255,
\r
2389 skycolor.getGreen() * l / 255,
\r
2390 skycolor.getBlue() * l / 255);
\r
2396 if(g_settings.getBool("enable_fog") == true)
\r
2398 f32 range = draw_control.wanted_range * BS;
\r
2399 if(draw_control.range_all)
\r
2400 range = 100000*BS;
\r
2404 video::EFT_FOG_LINEAR,
\r
2408 false, // pixel fog
\r
2409 false // range fog
\r
2415 Update gui stuff (0ms)
\r
2418 //TimeTaker guiupdatetimer("Gui updating");
\r
2421 wchar_t temptext[150];
\r
2423 static float drawtime_avg = 0;
\r
2424 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2425 static float beginscenetime_avg = 0;
\r
2426 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2427 static float scenetime_avg = 0;
\r
2428 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2429 static float endscenetime_avg = 0;
\r
2430 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2432 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2434 L", R: range_all=%i"
\r
2436 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2438 draw_control.range_all,
\r
2440 beginscenetime_avg,
\r
2445 guitext->setText(temptext);
\r
2449 wchar_t temptext[150];
\r
2450 swprintf(temptext, 150,
\r
2451 L"(% .1f, % .1f, % .1f)"
\r
2452 L" (% .3f < btime_jitter < % .3f"
\r
2453 L", dtime_jitter = % .1f %%"
\r
2454 L", v_range = %.1f)",
\r
2455 player_position.X/BS,
\r
2456 player_position.Y/BS,
\r
2457 player_position.Z/BS,
\r
2458 busytime_jitter1_min_sample,
\r
2459 busytime_jitter1_max_sample,
\r
2460 dtime_jitter1_max_fraction * 100.0,
\r
2461 draw_control.wanted_range
\r
2464 guitext2->setText(temptext);
\r
2468 guitext_info->setText(infotext.c_str());
\r
2472 Get chat messages from client
\r
2475 // Get new messages
\r
2476 std::wstring message;
\r
2477 while(client.getChatMessage(message))
\r
2479 chat_lines.push_back(ChatLine(message));
\r
2480 /*if(chat_lines.size() > 6)
\r
2482 core::list<ChatLine>::Iterator
\r
2483 i = chat_lines.begin();
\r
2484 chat_lines.erase(i);
\r
2487 // Append them to form the whole static text and throw
\r
2488 // it to the gui element
\r
2489 std::wstring whole;
\r
2490 // This will correspond to the line number counted from
\r
2491 // top to bottom, from size-1 to 0
\r
2492 s16 line_number = chat_lines.size();
\r
2493 // Count of messages to be removed from the top
\r
2494 u16 to_be_removed_count = 0;
\r
2495 for(core::list<ChatLine>::Iterator
\r
2496 i = chat_lines.begin();
\r
2497 i != chat_lines.end(); i++)
\r
2499 // After this, line number is valid for this loop
\r
2502 (*i).age += dtime;
\r
2504 This results in a maximum age of 60*6 to the
\r
2505 lowermost line and a maximum of 6 lines
\r
2507 float allowed_age = (6-line_number) * 60.0;
\r
2509 if((*i).age > allowed_age)
\r
2511 to_be_removed_count++;
\r
2514 whole += (*i).text + L'\n';
\r
2516 for(u16 i=0; i<to_be_removed_count; i++)
\r
2518 core::list<ChatLine>::Iterator
\r
2519 it = chat_lines.begin();
\r
2520 chat_lines.erase(it);
\r
2522 chat_guitext->setText(whole.c_str());
\r
2523 // Update gui element size and position
\r
2524 core::rect<s32> rect(
\r
2526 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2527 screensize.X - 10,
\r
2530 chat_guitext->setRelativePosition(rect);
\r
2532 if(chat_lines.size() == 0)
\r
2533 chat_guitext->setVisible(false);
\r
2535 chat_guitext->setVisible(true);
\r
2542 static u16 old_selected_item = 65535;
\r
2543 if(client.getLocalInventoryUpdated()
\r
2544 || g_selected_item != old_selected_item)
\r
2546 old_selected_item = g_selected_item;
\r
2547 //std::cout<<"Updating local inventory"<<std::endl;
\r
2548 client.getLocalInventory(local_inventory);
\r
2549 quick_inventory->setSelection(g_selected_item);
\r
2550 quick_inventory->update();
\r
2554 Send actions returned by the inventory menu
\r
2556 while(inventory_action_queue.size() != 0)
\r
2558 InventoryAction *a = inventory_action_queue.pop_front();
\r
2560 client.sendInventoryAction(a);
\r
2569 TimeTaker drawtimer("Drawing");
\r
2573 TimeTaker timer("beginScene");
\r
2574 driver->beginScene(true, true, bgcolor);
\r
2575 //driver->beginScene(false, true, bgcolor);
\r
2576 beginscenetime = timer.stop(true);
\r
2581 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2584 TimeTaker timer("smgr");
\r
2586 scenetime = timer.stop(true);
\r
2590 //TimeTaker timer9("auxiliary drawings");
\r
2594 //TimeTaker //timer10("//timer10");
\r
2596 video::SMaterial m;
\r
2598 m.Lighting = false;
\r
2599 driver->setMaterial(m);
\r
2601 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2603 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2604 i != hilightboxes.end(); i++)
\r
2606 /*std::cout<<"hilightbox min="
\r
2607 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2609 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2611 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2617 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2618 displaycenter + core::vector2d<s32>(10,0),
\r
2619 video::SColor(255,255,255,255));
\r
2620 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2621 displaycenter + core::vector2d<s32>(0,10),
\r
2622 video::SColor(255,255,255,255));
\r
2627 //TimeTaker //timer11("//timer11");
\r
2633 guienv->drawAll();
\r
2637 TimeTaker timer("endScene");
\r
2638 driver->endScene();
\r
2639 endscenetime = timer.stop(true);
\r
2642 drawtime = drawtimer.stop(true);
\r
2648 static s16 lastFPS = 0;
\r
2649 //u16 fps = driver->getFPS();
\r
2650 u16 fps = (1.0/dtime_avg1);
\r
2652 if (lastFPS != fps)
\r
2654 core::stringw str = L"Minetest [";
\r
2655 str += driver->getName();
\r
2659 device->setWindowCaption(str.c_str());
\r
2665 device->yield();*/
\r
2668 delete quick_inventory;
\r
2670 } // client is deleted at this point
\r
2675 In the end, delete the Irrlicht device.
\r
2680 Update configuration file
\r
2682 /*if(configpath != "")
\r
2684 g_settings.updateConfigFile(configpath.c_str());
\r
2688 catch(con::PeerNotFoundException &e)
\r
2690 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2694 GUIMessageMenu *menu =
\r
2695 new GUIMessageMenu(guienv, guiroot, -1,
\r
2696 &g_active_menu_count,
\r
2697 L"Connection timed out");
\r
2699 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
2701 dstream<<"Created menu"<<std::endl;
\r
2703 while(g_device->run() && menu->getStatus() == false)
\r
2705 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
2706 guienv->drawAll();
\r
2707 driver->endScene();
\r
2710 dstream<<"Dropping menu"<<std::endl;
\r
2716 END_DEBUG_EXCEPTION_HANDLER
\r
2718 debugstreams_deinit();
\r