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 - How would some GTA-style ideas work?
\r
111 - Cars? Stealing? Unlawful stuff and cops? Lots of guns?
\r
115 - Space racer style?
\r
120 Build system / running:
\r
121 -----------------------
\r
123 NOTE: The following fixme is not apparently valid, and it does work.
\r
124 FIXME: Graphical mode seems to segfault with Irrlicht 1.7.1 on 64-bit
\r
126 - http://pastebin.no/32bo
\r
127 - Might be just a bad build, too
\r
128 - Doesn't affect Irrlicht 1.7.2 or 32-bit 1.7.1. (Arch/Debian)
\r
129 - A similar error occurs when getTexture is called from a thread
\r
130 when the texture has not been already loaded from disk:
\r
131 http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=68830
\r
133 FIXME: Some network errors on Windows that cause local game to not work
\r
134 - See siggjen's emails.
\r
136 Networking and serialization:
\r
137 -----------------------------
\r
139 TODO: Get rid of GotSplitPacketException
\r
144 TODO: Add gui option to remove map
\r
146 TODO: Configuration menu, at least for keys
\r
151 TODO: Optimize day/night mesh updating somehow
\r
152 - create copies of all textures for all lighting values and only
\r
153 change texture for material?
\r
154 - Umm... the collecting of the faces is the slow part
\r
155 -> what about just changing the color values of the existing
\r
156 meshbuffers? It should go quite fast.
\r
157 - This is not easy; There'd need to be a buffer somewhere
\r
158 that would contain the night and day lighting values.
\r
160 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
162 - That is >500 vertices
\r
163 - This is not easy; all the MapBlocks close to the player would
\r
164 still need to be drawn separately and combining the blocks
\r
165 would have to happen in a background thread
\r
167 TODO: Make fetching sector's blocks more efficient when rendering
\r
168 sectors that have very large amounts of blocks (on client)
\r
169 - Is this necessary at all?
\r
174 TODO: Make the video backend selectable
\r
179 TODO: Untie client network operations from framerate
\r
180 - Needs some input queues or something
\r
181 - Not really necessary?
\r
183 TODO: Make morning and evening shorter
\r
188 TODO: When player dies, throw items on map
\r
190 TODO: Make an option to the server to disable building and digging near
\r
191 the starting position
\r
193 TODO: Save players with inventories to disk
\r
194 TODO: Players to be saved as text in map/players/<name>
\r
196 TODO: Copy the text of the last picked sign to inventory in creative
\r
199 TODO: Check what goes wrong with caching map to disk (Kray)
\r
202 TODO: When server sees that client is removing an inexistent block or
\r
203 adding a block to an existent position, resend the MapBlock.
\r
208 TODO: Better handling of objects and mobs
\r
210 - There has to be some way to do it with less messy code
\r
211 - Make separate classes for client and server
\r
212 - Client should not discriminate between blocks, server should
\r
213 - Make other players utilize the same framework
\r
214 - This is also needed for objects that don't get sent to client
\r
215 but are used for triggers etc
\r
217 TODO: There has to be some better way to handle static objects than to
\r
218 send them all the time. This affects signs and item objects.
\r
219 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
220 need an additional metadata field for the texts
\r
221 - This is also needed for item container chests
\r
223 Block object server side:
\r
224 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
225 - For all blocks in the buffer, objects are stepped(). This
\r
226 means they are active.
\r
227 - TODO: A global active buffer is needed for the server
\r
228 - TODO: A timestamp to blocks
\r
229 - TODO: All blocks going in and out of the buffer are recorded.
\r
230 - TODO: For outgoing blocks, timestamp is written.
\r
231 - TODO: For incoming blocks, time difference is calculated and
\r
232 objects are stepped according to it.
\r
237 NOTE: There are some lighting-related todos and fixmes in
\r
238 ServerMap::emergeBlock. And there always will be. 8)
\r
240 TODO: Map generator version 2
\r
241 - Create surface areas based on central points; a given point's
\r
242 area type is given by the nearest central point
\r
243 - Separate points for heightmap, caves, plants and minerals?
\r
244 - Flat land, mountains, forest, jungle
\r
246 - There could be a certain height (to which mountains only reach)
\r
247 where some minerals are found
\r
249 TODO: Change AttributeList to split the area into smaller sections so
\r
250 that searching won't be as heavy.
\r
251 TODO: Change AttributeList to be 2D, as it would be too slow to search
\r
252 in 3D fields anyway.
\r
254 FIXME: The new pre-sunlight-propagation code messes up with initial
\r
255 water lighting. Does it any more?
\r
257 TODO: Remove HMParams
\r
262 ======================================================================
\r
267 Setting this to 1 enables a special camera mode that forces
\r
268 the renderers to think that the camera statically points from
\r
269 the starting place to a static direction.
\r
271 This allows one to move around with the player and see what
\r
272 is actually drawn behind solid things and behind the player.
\r
274 #define FIELD_OF_VIEW_TEST 0
\r
278 #pragma message ("Disabling unit tests")
\r
280 #warning "Disabling unit tests"
\r
282 // Disable unit tests
\r
283 #define ENABLE_TESTS 0
\r
285 // Enable unit tests
\r
286 #define ENABLE_TESTS 1
\r
290 #pragma comment(lib, "Irrlicht.lib")
\r
291 //#pragma comment(lib, "jthread.lib")
\r
292 #pragma comment(lib, "zlibwapi.lib")
\r
293 // This would get rid of the console window
\r
294 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
297 #include <iostream>
\r
299 #include <jmutexautolock.h>
\r
300 #include <locale.h>
\r
301 #include "common_irrlicht.h"
\r
304 #include "player.h"
\r
307 #include "environment.h"
\r
308 #include "server.h"
\r
309 #include "client.h"
\r
310 #include "serialization.h"
\r
311 #include "constants.h"
\r
312 #include "strfnd.h"
\r
313 #include "porting.h"
\r
314 #include "irrlichtwrapper.h"
\r
315 #include "gettime.h"
\r
316 #include "porting.h"
\r
317 #include "guiPauseMenu.h"
\r
318 #include "guiInventoryMenu.h"
\r
319 #include "guiTextInputMenu.h"
\r
320 #include "materials.h"
\r
321 #include "guiMessageMenu.h"
\r
322 #include "filesys.h"
\r
323 #include "config.h"
\r
324 #include "guiMainMenu.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
350 gui::IGUIEnvironment* guienv = NULL;
\r
351 gui::IGUIStaticText *guiroot = NULL;
\r
353 class MainMenuManager : public IMenuManager
\r
356 virtual void createdMenu(GUIModalMenu *menu)
\r
358 for(core::list<GUIModalMenu*>::Iterator
\r
359 i = m_stack.begin();
\r
360 i != m_stack.end(); i++)
\r
362 assert(*i != menu);
\r
365 if(m_stack.size() != 0)
\r
366 (*m_stack.getLast())->setVisible(false);
\r
367 m_stack.push_back(menu);
\r
370 virtual void deletingMenu(GUIModalMenu *menu)
\r
372 // Remove all entries if there are duplicates
\r
373 bool removed_entry;
\r
375 removed_entry = false;
\r
376 for(core::list<GUIModalMenu*>::Iterator
\r
377 i = m_stack.begin();
\r
378 i != m_stack.end(); i++)
\r
383 removed_entry = true;
\r
387 }while(removed_entry);
\r
389 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
390 assert(*i == menu);
\r
391 m_stack.erase(i);*/
\r
393 if(m_stack.size() != 0)
\r
394 (*m_stack.getLast())->setVisible(true);
\r
399 return m_stack.size();
\r
402 core::list<GUIModalMenu*> m_stack;
\r
405 MainMenuManager g_menumgr;
\r
407 bool noMenuActive()
\r
409 return (g_menumgr.menuCount() == 0);
\r
412 bool g_disconnect_requested = false;
\r
414 class MainGameCallback : public IGameCallback
\r
417 virtual void exitToOS()
\r
419 g_device->closeDevice();
\r
422 virtual void disconnect()
\r
424 g_disconnect_requested = true;
\r
428 MainGameCallback g_gamecallback;
\r
430 // Inventory actions from the menu are buffered here before sending
\r
431 Queue<InventoryAction*> inventory_action_queue;
\r
432 // This is a copy of the inventory that the client's environment has
\r
433 Inventory local_inventory;
\r
435 u16 g_selected_item = 0;
\r
442 std::ostream *dout_con_ptr = &dummyout;
\r
443 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
444 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
445 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
446 //std::ostream *dout_con_ptr = &dstream;
\r
447 //std::ostream *derr_con_ptr = &dstream;
\r
450 std::ostream *dout_server_ptr = &dstream;
\r
451 std::ostream *derr_server_ptr = &dstream;
\r
454 std::ostream *dout_client_ptr = &dstream;
\r
455 std::ostream *derr_client_ptr = &dstream;
\r
458 gettime.h implementation
\r
464 Use irrlicht because it is more precise than porting.h's
\r
467 if(g_irrlicht == NULL)
\r
469 return g_irrlicht->getTime();
\r
476 struct TextDestSign : public TextDest
\r
478 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
480 m_blockpos = blockpos;
\r
484 void gotText(std::wstring text)
\r
486 std::string ntext = wide_to_narrow(text);
\r
487 dstream<<"Changing text of a sign object: "
\r
488 <<ntext<<std::endl;
\r
489 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
497 struct TextDestChat : public TextDest
\r
499 TextDestChat(Client *client)
\r
503 void gotText(std::wstring text)
\r
505 m_client->sendChatMessage(text);
\r
506 m_client->addChatMessage(text);
\r
512 class MyEventReceiver : public IEventReceiver
\r
515 // This is the one method that we have to implement
\r
516 virtual bool OnEvent(const SEvent& event)
\r
519 React to nothing here if a menu is active
\r
521 if(noMenuActive() == false)
\r
527 // Remember whether each key is down or up
\r
528 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
530 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
532 if(event.KeyInput.PressedDown)
\r
534 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
540 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
542 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
544 dstream<<DTIME<<"MyEventReceiver: "
\r
545 <<"Launching pause menu"<<std::endl;
\r
546 // It will delete itself by itself
\r
547 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
548 &g_menumgr))->drop();
\r
551 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
553 dstream<<DTIME<<"MyEventReceiver: "
\r
554 <<"Launching inventory"<<std::endl;
\r
555 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
556 &local_inventory, &inventory_action_queue,
\r
557 &g_menumgr))->drop();
\r
560 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
562 TextDest *dest = new TextDestChat(g_client);
\r
564 (new GUITextInputMenu(guienv, guiroot, -1,
\r
570 // Material selection
\r
571 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
573 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
576 g_selected_item = 0;
\r
577 dstream<<DTIME<<"Selected item: "
\r
578 <<g_selected_item<<std::endl;
\r
581 // Viewing range selection
\r
582 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
584 if(draw_control.range_all)
\r
586 draw_control.range_all = false;
\r
587 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
591 draw_control.range_all = true;
\r
592 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
596 // Print debug stacks
\r
597 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
599 dstream<<"-----------------------------------------"
\r
601 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
602 dstream<<"-----------------------------------------"
\r
604 debug_stacks_print();
\r
609 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
611 if(noMenuActive() == false)
\r
613 left_active = false;
\r
614 middle_active = false;
\r
615 right_active = false;
\r
619 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
620 left_active = event.MouseInput.isLeftPressed();
\r
621 middle_active = event.MouseInput.isMiddlePressed();
\r
622 right_active = event.MouseInput.isRightPressed();
\r
624 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
626 leftclicked = true;
\r
628 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
630 rightclicked = true;
\r
632 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
634 leftreleased = true;
\r
636 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
638 rightreleased = true;
\r
640 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
642 /*dstream<<"event.MouseInput.Wheel="
\r
643 <<event.MouseInput.Wheel<<std::endl;*/
\r
644 if(event.MouseInput.Wheel < 0)
\r
646 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
649 g_selected_item = 0;
\r
651 else if(event.MouseInput.Wheel > 0)
\r
653 if(g_selected_item > 0)
\r
656 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
665 // This is used to check whether a key is being held down
\r
666 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
668 return keyIsDown[keyCode];
\r
673 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
674 keyIsDown[i] = false;
\r
676 leftclicked = false;
\r
677 rightclicked = false;
\r
678 leftreleased = false;
\r
679 rightreleased = false;
\r
681 left_active = false;
\r
682 middle_active = false;
\r
683 right_active = false;
\r
694 bool rightreleased;
\r
697 bool middle_active;
\r
701 // We use this array to store the current state of each key
\r
702 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
705 IrrlichtDevice *m_device;
\r
714 virtual ~InputHandler()
\r
718 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
720 virtual v2s32 getMousePos() = 0;
\r
721 virtual void setMousePos(s32 x, s32 y) = 0;
\r
723 virtual bool getLeftState() = 0;
\r
724 virtual bool getRightState() = 0;
\r
726 virtual bool getLeftClicked() = 0;
\r
727 virtual bool getRightClicked() = 0;
\r
728 virtual void resetLeftClicked() = 0;
\r
729 virtual void resetRightClicked() = 0;
\r
731 virtual bool getLeftReleased() = 0;
\r
732 virtual bool getRightReleased() = 0;
\r
733 virtual void resetLeftReleased() = 0;
\r
734 virtual void resetRightReleased() = 0;
\r
736 virtual void step(float dtime) {};
\r
738 virtual void clear() {};
\r
741 InputHandler *g_input = NULL;
\r
743 class RealInputHandler : public InputHandler
\r
746 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
748 m_receiver(receiver)
\r
751 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
753 return m_receiver->IsKeyDown(keyCode);
\r
755 virtual v2s32 getMousePos()
\r
757 return m_device->getCursorControl()->getPosition();
\r
759 virtual void setMousePos(s32 x, s32 y)
\r
761 m_device->getCursorControl()->setPosition(x, y);
\r
764 virtual bool getLeftState()
\r
766 return m_receiver->left_active;
\r
768 virtual bool getRightState()
\r
770 return m_receiver->right_active;
\r
773 virtual bool getLeftClicked()
\r
775 return m_receiver->leftclicked;
\r
777 virtual bool getRightClicked()
\r
779 return m_receiver->rightclicked;
\r
781 virtual void resetLeftClicked()
\r
783 m_receiver->leftclicked = false;
\r
785 virtual void resetRightClicked()
\r
787 m_receiver->rightclicked = false;
\r
790 virtual bool getLeftReleased()
\r
792 return m_receiver->leftreleased;
\r
794 virtual bool getRightReleased()
\r
796 return m_receiver->rightreleased;
\r
798 virtual void resetLeftReleased()
\r
800 m_receiver->leftreleased = false;
\r
802 virtual void resetRightReleased()
\r
804 m_receiver->rightreleased = false;
\r
809 resetRightClicked();
\r
810 resetLeftClicked();
\r
813 IrrlichtDevice *m_device;
\r
814 MyEventReceiver *m_receiver;
\r
817 class RandomInputHandler : public InputHandler
\r
820 RandomInputHandler()
\r
822 leftclicked = false;
\r
823 rightclicked = false;
\r
824 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
825 keydown[i] = false;
\r
827 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
829 return keydown[keyCode];
\r
831 virtual v2s32 getMousePos()
\r
835 virtual void setMousePos(s32 x, s32 y)
\r
837 mousepos = v2s32(x,y);
\r
840 virtual bool getLeftState()
\r
844 virtual bool getRightState()
\r
849 virtual bool getLeftClicked()
\r
851 return leftclicked;
\r
853 virtual bool getRightClicked()
\r
855 return rightclicked;
\r
857 virtual void resetLeftClicked()
\r
859 leftclicked = false;
\r
861 virtual void resetRightClicked()
\r
863 rightclicked = false;
\r
866 virtual bool getLeftReleased()
\r
870 virtual bool getRightReleased()
\r
874 virtual void resetLeftReleased()
\r
877 virtual void resetRightReleased()
\r
881 virtual void step(float dtime)
\r
884 static float counter1 = 0;
\r
888 counter1 = 0.1*Rand(1,10);
\r
889 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
890 g_selected_material++;
\r
892 g_selected_material = 0;*/
\r
893 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
896 g_selected_item = 0;
\r
900 static float counter1 = 0;
\r
904 counter1 = 0.1*Rand(1, 40);
\r
905 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
909 static float counter1 = 0;
\r
913 counter1 = 0.1*Rand(1, 40);
\r
914 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
918 static float counter1 = 0;
\r
922 counter1 = 0.1*Rand(1, 40);
\r
923 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
927 static float counter1 = 0;
\r
931 counter1 = 0.1*Rand(1, 40);
\r
932 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
936 static float counter1 = 0;
\r
940 counter1 = 0.1*Rand(1, 20);
\r
941 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
945 static float counter1 = 0;
\r
949 counter1 = 0.1*Rand(1, 30);
\r
950 leftclicked = true;
\r
954 static float counter1 = 0;
\r
958 counter1 = 0.1*Rand(1, 20);
\r
959 rightclicked = true;
\r
962 mousepos += mousespeed;
\r
965 s32 Rand(s32 min, s32 max)
\r
967 return (myrand()%(max-min+1))+min;
\r
970 bool keydown[KEY_KEY_CODES_COUNT];
\r
977 void updateViewingRange(f32 frametime_in, Client *client)
\r
979 if(draw_control.range_all == true)
\r
982 static f32 added_frametime = 0;
\r
983 static s16 added_frames = 0;
\r
985 added_frametime += frametime_in;
\r
988 // Actually this counter kind of sucks because frametime is busytime
\r
989 static f32 counter = 0;
\r
990 counter -= frametime_in;
\r
996 /*dstream<<__FUNCTION_NAME
\r
997 <<": Collected "<<added_frames<<" frames, total of "
\r
998 <<added_frametime<<"s."<<std::endl;*/
\r
1000 /*dstream<<"draw_control.blocks_drawn="
\r
1001 <<draw_control.blocks_drawn
\r
1002 <<", draw_control.blocks_would_have_drawn="
\r
1003 <<draw_control.blocks_would_have_drawn
\r
1006 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1007 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1009 draw_control.wanted_min_range = range_min;
\r
1010 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1012 float block_draw_ratio = 1.0;
\r
1013 if(draw_control.blocks_would_have_drawn != 0)
\r
1015 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1016 / (float)draw_control.blocks_would_have_drawn;
\r
1019 // Calculate the average frametime in the case that all wanted
\r
1020 // blocks had been drawn
\r
1021 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1023 added_frametime = 0.0;
\r
1026 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1027 float wanted_frametime = 1.0 / wanted_fps;
\r
1029 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1030 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1032 // If needed frametime change is very small, just return
\r
1033 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1035 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1039 float range = draw_control.wanted_range;
\r
1040 float new_range = range;
\r
1042 static s16 range_old = 0;
\r
1043 static f32 frametime_old = 0;
\r
1045 float d_range = range - range_old;
\r
1046 f32 d_frametime = frametime - frametime_old;
\r
1047 // A sane default of 30ms per 50 nodes of range
\r
1048 static f32 time_per_range = 30. / 50;
\r
1051 time_per_range = d_frametime / d_range;
\r
1054 // The minimum allowed calculated frametime-range derivative:
\r
1055 // Practically this sets the maximum speed of changing the range.
\r
1056 // The lower this value, the higher the maximum changing speed.
\r
1057 // A low value here results in wobbly range (0.001)
\r
1058 // A high value here results in slow changing range (0.0025)
\r
1059 // SUGG: This could be dynamically adjusted so that when
\r
1060 // the camera is turning, this is lower
\r
1061 //float min_time_per_range = 0.0015;
\r
1062 float min_time_per_range = 0.0010;
\r
1063 //float min_time_per_range = 0.05 / range;
\r
1064 if(time_per_range < min_time_per_range)
\r
1066 time_per_range = min_time_per_range;
\r
1067 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1071 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1074 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1075 // Dampen the change a bit to kill oscillations
\r
1076 //wanted_range_change *= 0.9;
\r
1077 //wanted_range_change *= 0.75;
\r
1078 wanted_range_change *= 0.5;
\r
1079 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1081 // If needed range change is very small, just return
\r
1082 if(fabs(wanted_range_change) < 0.001)
\r
1084 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1088 new_range += wanted_range_change;
\r
1089 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1091 //float new_range_unclamped = new_range;
\r
1092 if(new_range < range_min)
\r
1093 new_range = range_min;
\r
1094 if(new_range > range_max)
\r
1095 new_range = range_max;
\r
1097 /*if(new_range != new_range_unclamped)
\r
1098 dstream<<", clamped to "<<new_range<<std::endl;
\r
1100 dstream<<std::endl;*/
\r
1102 draw_control.wanted_range = new_range;
\r
1104 range_old = new_range;
\r
1105 frametime_old = frametime;
\r
1108 class GUIQuickInventory : public IEventReceiver
\r
1111 GUIQuickInventory(
\r
1112 gui::IGUIEnvironment* env,
\r
1113 gui::IGUIElement* parent,
\r
1116 Inventory *inventory):
\r
1117 m_itemcount(itemcount),
\r
1118 m_inventory(inventory)
\r
1120 core::rect<s32> imgsize(0,0,48,48);
\r
1121 core::rect<s32> textsize(0,0,48,16);
\r
1122 v2s32 spacing(0, 64);
\r
1123 for(s32 i=0; i<m_itemcount; i++)
\r
1125 m_images.push_back(env->addImage(
\r
1126 imgsize + pos + spacing*i
\r
1128 m_images[i]->setScaleImage(true);
\r
1129 m_texts.push_back(env->addStaticText(
\r
1131 textsize + pos + spacing*i,
\r
1134 m_texts[i]->setBackgroundColor(
\r
1135 video::SColor(128,0,0,0));
\r
1136 m_texts[i]->setTextAlignment(
\r
1137 gui::EGUIA_CENTER,
\r
1138 gui::EGUIA_UPPERLEFT);
\r
1142 ~GUIQuickInventory()
\r
1144 for(u32 i=0; i<m_texts.size(); i++)
\r
1146 m_texts[i]->remove();
\r
1148 for(u32 i=0; i<m_images.size(); i++)
\r
1150 m_images[i]->remove();
\r
1154 virtual bool OnEvent(const SEvent& event)
\r
1159 void setSelection(s32 i)
\r
1168 start = m_selection - m_itemcount / 2;
\r
1170 InventoryList *mainlist = m_inventory->getList("main");
\r
1172 for(s32 i=0; i<m_itemcount; i++)
\r
1174 s32 j = i + start;
\r
1176 if(j > (s32)mainlist->getSize() - 1)
\r
1177 j -= mainlist->getSize();
\r
1179 j += mainlist->getSize();
\r
1181 InventoryItem *item = mainlist->getItem(j);
\r
1185 m_images[i]->setImage(NULL);
\r
1188 if(m_selection == j)
\r
1189 swprintf(t, 10, L"<-");
\r
1191 swprintf(t, 10, L"");
\r
1192 m_texts[i]->setText(t);
\r
1194 // The next ifs will segfault with a NULL pointer
\r
1199 m_images[i]->setImage(item->getImage());
\r
1202 if(m_selection == j)
\r
1203 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1205 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1206 m_texts[i]->setText(t);
\r
1212 core::array<gui::IGUIStaticText*> m_texts;
\r
1213 core::array<gui::IGUIImage*> m_images;
\r
1214 Inventory *m_inventory;
\r
1225 ChatLine(const std::wstring &a_text):
\r
1231 std::wstring text;
\r
1234 int main(int argc, char *argv[])
\r
1237 Low-level initialization
\r
1240 bool disable_stderr = false;
\r
1242 disable_stderr = true;
\r
1245 // Initialize debug streams
\r
1246 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1247 // Initialize debug stacks
\r
1248 debug_stacks_init();
\r
1250 DSTACK(__FUNCTION_NAME);
\r
1252 porting::initializePaths();
\r
1253 // Create user data directory
\r
1254 fs::CreateDir(porting::path_userdata);
\r
1256 initializeMaterialProperties();
\r
1258 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1260 // Print startup message
\r
1261 dstream<<DTIME<<"minetest-c55"
\r
1262 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1263 <<", "<<BUILD_INFO
\r
1267 Parse command line
\r
1270 // List all allowed options
\r
1271 core::map<std::string, ValueSpec> allowed_options;
\r
1272 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1273 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1274 "Run server directly"));
\r
1275 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1276 "Load configuration from specified file"));
\r
1277 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1278 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1279 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1280 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1281 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1282 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1284 Settings cmd_args;
\r
1286 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1288 if(ret == false || cmd_args.getFlag("help"))
\r
1290 dstream<<"Allowed options:"<<std::endl;
\r
1291 for(core::map<std::string, ValueSpec>::Iterator
\r
1292 i = allowed_options.getIterator();
\r
1293 i.atEnd() == false; i++)
\r
1295 dstream<<" --"<<i.getNode()->getKey();
\r
1296 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1301 dstream<<" <value>";
\r
1303 dstream<<std::endl;
\r
1305 if(i.getNode()->getValue().help != NULL)
\r
1307 dstream<<" "<<i.getNode()->getValue().help
\r
1312 return cmd_args.getFlag("help") ? 0 : 1;
\r
1317 Basic initialization
\r
1320 // Initialize default settings
\r
1321 set_default_settings();
\r
1323 // Set locale. This is for forcing '.' as the decimal point.
\r
1324 std::locale::global(std::locale("C"));
\r
1325 // This enables printing all characters in bitmap font
\r
1326 setlocale(LC_CTYPE, "en_US");
\r
1328 // Initialize sockets
\r
1330 atexit(sockets_cleanup);
\r
1340 // Path of configuration file in use
\r
1341 std::string configpath = "";
\r
1343 if(cmd_args.exists("config"))
\r
1345 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1348 dstream<<"Could not read configuration from \""
\r
1349 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1352 configpath = cmd_args.get("config");
\r
1356 core::array<std::string> filenames;
\r
1357 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1358 #ifdef RUN_IN_PLACE
\r
1359 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1362 for(u32 i=0; i<filenames.size(); i++)
\r
1364 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1367 configpath = filenames[i];
\r
1373 // Initialize random seed
\r
1380 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1381 || cmd_args.getFlag("enable-unittests") == true)
\r
1386 // Read map parameters from settings
\r
1388 HMParams hm_params;
\r
1389 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1390 hm_params.randmax = g_settings.get("height_randmax");
\r
1391 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1392 hm_params.base = g_settings.get("height_base");*/
\r
1394 MapParams map_params;
\r
1395 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1396 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1404 if(cmd_args.exists("port"))
\r
1405 port = cmd_args.getU16("port");
\r
1406 else if(cmd_args.exists("port"))
\r
1407 port = g_settings.getU16("port");
\r
1410 std::string map_dir = porting::path_userdata+"/map";
\r
1411 if(cmd_args.exists("map-dir"))
\r
1412 map_dir = cmd_args.get("map-dir");
\r
1413 else if(g_settings.exists("map-dir"))
\r
1414 map_dir = g_settings.get("map-dir");
\r
1416 // Run dedicated server if asked to
\r
1417 if(cmd_args.getFlag("server"))
\r
1419 DSTACK("Dedicated server branch");
\r
1422 Server server(map_dir.c_str(), hm_params, map_params);
\r
1423 server.start(port);
\r
1426 dedicated_server_loop(server);
\r
1435 // Address to connect to
\r
1436 std::string address = "";
\r
1438 if(cmd_args.exists("address"))
\r
1440 address = cmd_args.get("address");
\r
1444 address = g_settings.get("address");
\r
1447 std::string playername = g_settings.get("name");
\r
1450 Resolution selection
\r
1453 bool fullscreen = false;
\r
1454 u16 screenW = g_settings.getU16("screenW");
\r
1455 u16 screenH = g_settings.getU16("screenH");
\r
1459 MyEventReceiver receiver;
\r
1461 video::E_DRIVER_TYPE driverType;
\r
1464 //driverType = video::EDT_DIRECT3D9;
\r
1465 driverType = video::EDT_OPENGL;
\r
1467 driverType = video::EDT_OPENGL;
\r
1468 //driverType = video::EDT_BURNINGSVIDEO;
\r
1471 // create device and exit if creation failed
\r
1473 IrrlichtDevice *device;
\r
1474 device = createDevice(driverType,
\r
1475 core::dimension2d<u32>(screenW, screenH),
\r
1476 16, fullscreen, false, false, &receiver);
\r
1479 return 1; // could not create selected driver.
\r
1481 g_device = device;
\r
1482 g_irrlicht = new IrrlichtWrapper(device);
\r
1484 //g_device = device;
\r
1486 device->setResizable(true);
\r
1488 bool random_input = g_settings.getBool("random_input")
\r
1489 || cmd_args.getFlag("random-input");
\r
1491 g_input = new RandomInputHandler();
\r
1493 g_input = new RealInputHandler(device, &receiver);
\r
1496 Continue initialization
\r
1499 video::IVideoDriver* driver = device->getVideoDriver();
\r
1502 This changes the minimum allowed number of vertices in a VBO
\r
1504 //driver->setMinHardwareBufferVertexCount(50);
\r
1506 scene::ISceneManager* smgr = device->getSceneManager();
\r
1508 guienv = device->getGUIEnvironment();
\r
1509 gui::IGUISkin* skin = guienv->getSkin();
\r
1510 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1512 skin->setFont(font);
\r
1514 dstream<<"WARNING: Font file was not found."
\r
1515 " Using default font."<<std::endl;
\r
1516 // If font was not found, this will get us one
\r
1517 font = skin->getFont();
\r
1520 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1521 dstream<<"text_height="<<text_height<<std::endl;
\r
1523 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1524 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1525 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1526 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1527 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1528 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1531 Preload some textures
\r
1534 init_content_inventory_texture_paths();
\r
1535 init_tile_texture_paths();
\r
1536 tile_materials_preload(g_irrlicht);
\r
1543 We need some kind of a root node to be able to add
\r
1544 custom gui elements directly on the screen.
\r
1545 Otherwise they won't be automatically drawn.
\r
1547 guiroot = guienv->addStaticText(L"",
\r
1548 core::rect<s32>(0, 0, 10000, 10000));
\r
1550 // First line of debug text
\r
1551 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1553 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1555 // Second line of debug text
\r
1556 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1558 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1561 // At the middle of the screen
\r
1562 // Object infos are shown in this
\r
1563 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1565 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1569 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1571 core::rect<s32>(0,0,0,0),
\r
1573 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1574 core::list<ChatLine> chat_lines;
\r
1577 If an error occurs, this is set to something and the
\r
1578 menu-game loop is restarted. It is then displayed before
\r
1581 std::wstring error_message = L"";
\r
1586 while(g_device->run())
\r
1589 // This is used for catching disconnects
\r
1594 Out-of-game menu loop
\r
1597 // Wait for proper parameters
\r
1600 // Cursor can be non-visible when coming from the game
\r
1601 device->getCursorControl()->setVisible(true);
\r
1602 // Some stuff are left to scene manager when coming from the game
\r
1603 // (map at least?)
\r
1605 // Reset or hide the debug gui texts
\r
1606 guitext->setText(L"Minetest-c55");
\r
1607 guitext2->setVisible(false);
\r
1608 guitext_info->setVisible(false);
\r
1609 guitext_chat->setVisible(false);
\r
1611 // Initialize menu data
\r
1612 MainMenuData menudata;
\r
1613 menudata.address = narrow_to_wide(address);
\r
1614 menudata.name = narrow_to_wide(playername);
\r
1615 menudata.port = narrow_to_wide(itos(port));
\r
1616 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1618 GUIMainMenu *menu =
\r
1619 new GUIMainMenu(guienv, guiroot, -1,
\r
1620 &g_menumgr, &menudata, &g_gamecallback);
\r
1621 menu->allowFocusRemoval(true);
\r
1623 if(error_message != L"")
\r
1625 GUIMessageMenu *menu2 =
\r
1626 new GUIMessageMenu(guienv, guiroot, -1,
\r
1627 &g_menumgr, error_message.c_str());
\r
1629 error_message = L"";
\r
1632 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1634 dstream<<"Created main menu"<<std::endl;
\r
1636 while(g_device->run())
\r
1638 // Run global IrrlichtWrapper's main thread processing stuff
\r
1639 g_irrlicht->Run();
\r
1641 if(menu->getStatus() == true)
\r
1644 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1645 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1646 guienv->drawAll();
\r
1647 driver->endScene();
\r
1650 // Break out of menu-game loop to shut down cleanly
\r
1651 if(g_device->run() == false)
\r
1654 dstream<<"Dropping main menu"<<std::endl;
\r
1658 playername = wide_to_narrow(menudata.name);
\r
1659 address = wide_to_narrow(menudata.address);
\r
1660 port = stoi(wide_to_narrow(menudata.port));
\r
1661 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1663 // Check for valid parameters, restart menu if invalid.
\r
1664 if(playername == "")
\r
1666 error_message = L"Name required.";
\r
1671 g_settings.set("name", playername);
\r
1672 g_settings.set("address", address);
\r
1673 g_settings.set("port", itos(port));
\r
1674 // Update configuration file
\r
1675 if(configpath != "")
\r
1676 g_settings.updateConfigFile(configpath.c_str());
\r
1678 // Continue to game
\r
1682 // Break out of menu-game loop to shut down cleanly
\r
1683 if(g_device->run() == false)
\r
1687 Make a scope here so that the client and the server and other
\r
1688 stuff gets removed when disconnected or the irrlicht device
\r
1694 Draw "Loading" screen
\r
1696 const wchar_t *text = L"Loading and connecting...";
\r
1697 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1698 core::vector2d<s32> textsize(300, text_height);
\r
1699 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1701 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1702 text, textrect, false, false);
\r
1703 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1705 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1706 guienv->drawAll();
\r
1707 driver->endScene();
\r
1709 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1713 SharedPtr will delete it when it goes out of scope.
\r
1715 SharedPtr<Server> server;
\r
1716 if(address == ""){
\r
1717 server = new Server(map_dir, hm_params, map_params);
\r
1718 server->start(port);
\r
1725 Client client(device, playername.c_str(), draw_control);
\r
1727 g_client = &client;
\r
1729 Address connect_address(0,0,0,0, port);
\r
1732 connect_address.Resolve("localhost");
\r
1734 connect_address.Resolve(address.c_str());
\r
1736 catch(ResolveError &e)
\r
1738 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1740 error_message = L"Couldn't resolve address";
\r
1741 gui_loadingtext->remove();
\r
1745 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1746 client.connect(connect_address);
\r
1749 while(client.connectedAndInitialized() == false)
\r
1752 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1753 guienv->drawAll();
\r
1754 driver->endScene();
\r
1756 // Update client and server
\r
1760 if(server != NULL)
\r
1761 server->step(0.1);
\r
1767 catch(con::PeerNotFoundException &e)
\r
1769 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1771 error_message = L"Connection timed out.";
\r
1772 gui_loadingtext->remove();
\r
1779 /*scene::ISceneNode* skybox;
\r
1780 skybox = smgr->addSkyBoxSceneNode(
\r
1781 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1782 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1783 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1784 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1785 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1786 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1789 Create the camera node
\r
1792 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1793 0, // Camera parent
\r
1794 v3f(BS*100, BS*2, BS*100), // Look from
\r
1795 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1799 if(camera == NULL)
\r
1802 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1804 camera->setFOV(FOV_ANGLE);
\r
1806 // Just so big a value that everything rendered is visible
\r
1807 camera->setFarValue(100000*BS);
\r
1809 f32 camera_yaw = 0; // "right/left"
\r
1810 f32 camera_pitch = 0; // "up/down"
\r
1816 gui_loadingtext->remove();
\r
1819 Add some gui stuff
\r
1822 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1823 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1825 // Test the text input system
\r
1826 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
1828 /*GUIMessageMenu *menu =
\r
1829 new GUIMessageMenu(guienv, guiroot, -1,
\r
1834 // Launch pause menu
\r
1835 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
1836 &g_menumgr))->drop();
\r
1839 guitext2->setVisible(true);
\r
1840 guitext_info->setVisible(true);
\r
1841 guitext_chat->setVisible(true);
\r
1844 Some statistics are collected in these
\r
1847 u32 beginscenetime = 0;
\r
1848 u32 scenetime = 0;
\r
1849 u32 endscenetime = 0;
\r
1852 //throw con::PeerNotFoundException("lol");
\r
1858 bool first_loop_after_window_activation = true;
\r
1860 // Time is in milliseconds
\r
1861 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1862 // NOTE: So we have to use getTime() and call run()s between them
\r
1863 u32 lasttime = device->getTimer()->getTime();
\r
1865 while(device->run())
\r
1867 if(g_disconnect_requested)
\r
1869 g_disconnect_requested = false;
\r
1874 Run global IrrlichtWrapper's main thread processing stuff
\r
1876 g_irrlicht->Run();
\r
1879 Random calculations
\r
1881 v2u32 screensize = driver->getScreenSize();
\r
1882 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1884 // Hilight boxes collected during the loop and displayed
\r
1885 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1888 std::wstring infotext;
\r
1890 //TimeTaker //timer1("//timer1");
\r
1892 // Time of frame without fps limit
\r
1896 // not using getRealTime is necessary for wine
\r
1897 u32 time = device->getTimer()->getTime();
\r
1898 if(time > lasttime)
\r
1899 busytime_u32 = time - lasttime;
\r
1902 busytime = busytime_u32 / 1000.0;
\r
1905 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1907 // Absolutelu necessary for wine!
\r
1914 updateViewingRange(busytime, &client);
\r
1921 float fps_max = g_settings.getFloat("fps_max");
\r
1922 u32 frametime_min = 1000./fps_max;
\r
1924 if(busytime_u32 < frametime_min)
\r
1926 u32 sleeptime = frametime_min - busytime_u32;
\r
1927 device->sleep(sleeptime);
\r
1931 // Absolutelu necessary for wine!
\r
1935 Time difference calculation
\r
1937 f32 dtime; // in seconds
\r
1939 u32 time = device->getTimer()->getTime();
\r
1940 if(time > lasttime)
\r
1941 dtime = (time - lasttime) / 1000.0;
\r
1947 Time average and jitter calculation
\r
1950 static f32 dtime_avg1 = 0.0;
\r
1951 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1952 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1954 static f32 dtime_jitter1_max_sample = 0.0;
\r
1955 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1957 static f32 jitter1_max = 0.0;
\r
1958 static f32 counter = 0.0;
\r
1959 if(dtime_jitter1 > jitter1_max)
\r
1960 jitter1_max = dtime_jitter1;
\r
1965 dtime_jitter1_max_sample = jitter1_max;
\r
1966 dtime_jitter1_max_fraction
\r
1967 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1968 jitter1_max = 0.0;
\r
1971 Control freetime ratio
\r
1973 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1975 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1976 g_freetime_ratio += 0.01;
\r
1980 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1981 g_freetime_ratio -= 0.01;
\r
1987 Busytime average and jitter calculation
\r
1990 static f32 busytime_avg1 = 0.0;
\r
1991 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1992 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1994 static f32 busytime_jitter1_max_sample = 0.0;
\r
1995 static f32 busytime_jitter1_min_sample = 0.0;
\r
1997 static f32 jitter1_max = 0.0;
\r
1998 static f32 jitter1_min = 0.0;
\r
1999 static f32 counter = 0.0;
\r
2000 if(busytime_jitter1 > jitter1_max)
\r
2001 jitter1_max = busytime_jitter1;
\r
2002 if(busytime_jitter1 < jitter1_min)
\r
2003 jitter1_min = busytime_jitter1;
\r
2005 if(counter > 0.0){
\r
2007 busytime_jitter1_max_sample = jitter1_max;
\r
2008 busytime_jitter1_min_sample = jitter1_min;
\r
2009 jitter1_max = 0.0;
\r
2010 jitter1_min = 0.0;
\r
2015 Debug info for client
\r
2018 static float counter = 0.0;
\r
2023 client.printDebugInfo(std::cout);
\r
2028 Input handler step()
\r
2030 g_input->step(dtime);
\r
2033 Player speed control
\r
2042 bool a_superspeed,
\r
2045 PlayerControl control(
\r
2046 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2047 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2048 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2049 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2050 g_input->isKeyDown(irr::KEY_SPACE),
\r
2051 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2055 client.setPlayerControl(control);
\r
2059 Process environment
\r
2063 //TimeTaker timer("client.step(dtime)");
\r
2064 client.step(dtime);
\r
2065 //client.step(dtime_avg1);
\r
2068 if(server != NULL)
\r
2070 //TimeTaker timer("server->step(dtime)");
\r
2071 server->step(dtime);
\r
2074 v3f player_position = client.getPlayerPosition();
\r
2076 //TimeTaker //timer2("//timer2");
\r
2079 Mouse and camera control
\r
2082 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2085 device->getCursorControl()->setVisible(false);
\r
2087 if(first_loop_after_window_activation){
\r
2088 //std::cout<<"window active, first loop"<<std::endl;
\r
2089 first_loop_after_window_activation = false;
\r
2092 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2093 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2094 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2095 camera_yaw -= dx*0.2;
\r
2096 camera_pitch += dy*0.2;
\r
2097 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2098 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2100 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2103 device->getCursorControl()->setVisible(true);
\r
2105 //std::cout<<"window inactive"<<std::endl;
\r
2106 first_loop_after_window_activation = true;
\r
2109 camera_yaw = wrapDegrees(camera_yaw);
\r
2110 camera_pitch = wrapDegrees(camera_pitch);
\r
2112 v3f camera_direction = v3f(0,0,1);
\r
2113 camera_direction.rotateYZBy(camera_pitch);
\r
2114 camera_direction.rotateXZBy(camera_yaw);
\r
2116 // This is at the height of the eyes of the current figure
\r
2117 v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2118 // This is more like in minecraft
\r
2119 //v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2121 camera->setPosition(camera_position);
\r
2122 // *100.0 helps in large map coordinates
\r
2123 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2125 if(FIELD_OF_VIEW_TEST){
\r
2126 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2127 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2130 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2131 //TimeTaker timer("client.updateCamera");
\r
2132 client.updateCamera(camera_position, camera_direction);
\r
2136 //TimeTaker //timer3("//timer3");
\r
2139 Calculate what block is the crosshair pointing to
\r
2142 //u32 t1 = device->getTimer()->getRealTime();
\r
2144 //f32 d = 4; // max. distance
\r
2145 f32 d = 4; // max. distance
\r
2146 core::line3d<f32> shootline(camera_position,
\r
2147 camera_position + camera_direction * BS * (d+1));
\r
2149 MapBlockObject *selected_object = client.getSelectedObject
\r
2150 (d*BS, camera_position, shootline);
\r
2153 If it's pointing to a MapBlockObject
\r
2156 if(selected_object != NULL)
\r
2158 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2160 core::aabbox3d<f32> box_on_map
\r
2161 = selected_object->getSelectionBoxOnMap();
\r
2163 hilightboxes.push_back(box_on_map);
\r
2165 infotext = narrow_to_wide(selected_object->infoText());
\r
2167 if(g_input->getLeftClicked())
\r
2169 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2170 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2171 selected_object->getId(), g_selected_item);
\r
2173 else if(g_input->getRightClicked())
\r
2175 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2177 Check if we want to modify the object ourselves
\r
2179 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2181 dstream<<"Sign object right-clicked"<<std::endl;
\r
2183 if(random_input == false)
\r
2185 // Get a new text for it
\r
2187 TextDest *dest = new TextDestSign(
\r
2188 selected_object->getBlock()->getPos(),
\r
2189 selected_object->getId(),
\r
2192 SignObject *sign_object = (SignObject*)selected_object;
\r
2194 std::wstring wtext =
\r
2195 narrow_to_wide(sign_object->getText());
\r
2197 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2203 Otherwise pass the event to the server as-is
\r
2207 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2208 selected_object->getId(), g_selected_item);
\r
2212 else // selected_object == NULL
\r
2216 Find out which node we are pointing at
\r
2219 bool nodefound = false;
\r
2221 v3s16 neighbourpos;
\r
2222 core::aabbox3d<f32> nodefacebox;
\r
2223 f32 mindistance = BS * 1001;
\r
2225 v3s16 pos_i = floatToInt(player_position);
\r
2227 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2231 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2232 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2233 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2234 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2235 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2236 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2238 for(s16 y = ystart; y <= yend; y++)
\r
2239 for(s16 z = zstart; z <= zend; z++)
\r
2240 for(s16 x = xstart; x <= xend; x++)
\r
2245 n = client.getNode(v3s16(x,y,z));
\r
2246 if(content_pointable(n.d) == false)
\r
2249 catch(InvalidPositionException &e)
\r
2255 v3f npf = intToFloat(np);
\r
2260 v3s16(0,0,1), // back
\r
2261 v3s16(0,1,0), // top
\r
2262 v3s16(1,0,0), // right
\r
2263 v3s16(0,0,-1), // front
\r
2264 v3s16(0,-1,0), // bottom
\r
2265 v3s16(-1,0,0), // left
\r
2271 if(n.d == CONTENT_TORCH)
\r
2273 v3s16 dir = unpackDir(n.dir);
\r
2274 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2275 dir_f *= BS/2 - BS/6 - BS/20;
\r
2276 v3f cpf = npf + dir_f;
\r
2277 f32 distance = (cpf - camera_position).getLength();
\r
2279 core::aabbox3d<f32> box;
\r
2282 if(dir == v3s16(0,-1,0))
\r
2284 box = core::aabbox3d<f32>(
\r
2285 npf - v3f(BS/6, BS/2, BS/6),
\r
2286 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2290 else if(dir == v3s16(0,1,0))
\r
2292 box = core::aabbox3d<f32>(
\r
2293 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2294 npf + v3f(BS/6, BS/2, BS/6)
\r
2300 box = core::aabbox3d<f32>(
\r
2301 cpf - v3f(BS/6, BS/3, BS/6),
\r
2302 cpf + v3f(BS/6, BS/3, BS/6)
\r
2306 if(distance < mindistance)
\r
2308 if(box.intersectsWithLine(shootline))
\r
2312 neighbourpos = np;
\r
2313 mindistance = distance;
\r
2314 nodefacebox = box;
\r
2323 for(u16 i=0; i<6; i++)
\r
2325 v3f dir_f = v3f(dirs[i].X,
\r
2326 dirs[i].Y, dirs[i].Z);
\r
2327 v3f centerpoint = npf + dir_f * BS/2;
\r
2329 (centerpoint - camera_position).getLength();
\r
2331 if(distance < mindistance)
\r
2333 core::CMatrix4<f32> m;
\r
2334 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2336 // This is the back face
\r
2337 v3f corners[2] = {
\r
2338 v3f(BS/2, BS/2, BS/2),
\r
2339 v3f(-BS/2, -BS/2, BS/2+d)
\r
2342 for(u16 j=0; j<2; j++)
\r
2344 m.rotateVect(corners[j]);
\r
2345 corners[j] += npf;
\r
2348 core::aabbox3d<f32> facebox(corners[0]);
\r
2349 facebox.addInternalPoint(corners[1]);
\r
2351 if(facebox.intersectsWithLine(shootline))
\r
2355 neighbourpos = np + dirs[i];
\r
2356 mindistance = distance;
\r
2357 nodefacebox = facebox;
\r
2359 } // if distance < mindistance
\r
2361 } // regular block
\r
2364 static float nodig_delay_counter = 0.0;
\r
2368 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2370 static float dig_time = 0.0;
\r
2371 static u16 dig_index = 0;
\r
2373 hilightboxes.push_back(nodefacebox);
\r
2375 if(g_input->getLeftReleased())
\r
2377 client.clearTempMod(nodepos);
\r
2381 if(nodig_delay_counter > 0.0)
\r
2383 nodig_delay_counter -= dtime;
\r
2387 if(nodepos != nodepos_old)
\r
2389 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2390 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2392 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2394 client.clearTempMod(nodepos_old);
\r
2399 if(g_input->getLeftClicked() ||
\r
2400 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2402 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2403 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2405 if(g_input->getLeftClicked())
\r
2407 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2409 if(g_input->getLeftState())
\r
2411 MapNode n = client.getNode(nodepos);
\r
2413 // Get tool name. Default is "" = bare hands
\r
2414 std::string toolname = "";
\r
2415 InventoryList *mlist = local_inventory.getList("main");
\r
2418 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2419 if(item && (std::string)item->getName() == "ToolItem")
\r
2421 ToolItem *titem = (ToolItem*)item;
\r
2422 toolname = titem->getToolName();
\r
2426 // Get digging properties for material and tool
\r
2427 u8 material = n.d;
\r
2428 DiggingProperties prop =
\r
2429 getDiggingProperties(material, toolname);
\r
2431 float dig_time_complete = 0.0;
\r
2433 if(prop.diggable == false)
\r
2435 /*dstream<<"Material "<<(int)material
\r
2436 <<" not diggable with \""
\r
2437 <<toolname<<"\""<<std::endl;*/
\r
2438 // I guess nobody will wait for this long
\r
2439 dig_time_complete = 10000000.0;
\r
2443 dig_time_complete = prop.time;
\r
2446 if(dig_time_complete >= 0.001)
\r
2448 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2449 * dig_time/dig_time_complete);
\r
2451 // This is for torches
\r
2454 dig_index = CRACK_ANIMATION_LENGTH;
\r
2457 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2459 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2460 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2464 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2465 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2466 client.clearTempMod(nodepos);
\r
2467 client.removeNode(nodepos);
\r
2471 nodig_delay_counter = dig_time_complete
\r
2472 / (float)CRACK_ANIMATION_LENGTH;
\r
2474 // We don't want a corresponding delay to
\r
2475 // very time consuming nodes
\r
2476 if(nodig_delay_counter > 0.5)
\r
2478 nodig_delay_counter = 0.5;
\r
2480 // We want a slight delay to very little
\r
2481 // time consuming nodes
\r
2482 //float mindelay = 0.15;
\r
2483 float mindelay = 0.20;
\r
2484 if(nodig_delay_counter < mindelay)
\r
2486 nodig_delay_counter = mindelay;
\r
2490 dig_time += dtime;
\r
2494 if(g_input->getRightClicked())
\r
2496 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2497 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2500 nodepos_old = nodepos;
\r
2505 } // selected_object == NULL
\r
2507 g_input->resetLeftClicked();
\r
2508 g_input->resetRightClicked();
\r
2510 if(g_input->getLeftReleased())
\r
2512 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2514 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2516 if(g_input->getRightReleased())
\r
2518 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2522 g_input->resetLeftReleased();
\r
2523 g_input->resetRightReleased();
\r
2526 Calculate stuff for drawing
\r
2529 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2531 u32 daynight_ratio = client.getDayNightRatio();
\r
2532 /*video::SColor bgcolor = video::SColor(
\r
2534 skycolor.getRed() * daynight_ratio / 1000,
\r
2535 skycolor.getGreen() * daynight_ratio / 1000,
\r
2536 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2538 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2539 video::SColor bgcolor = video::SColor(
\r
2541 skycolor.getRed() * l / 255,
\r
2542 skycolor.getGreen() * l / 255,
\r
2543 skycolor.getBlue() * l / 255);
\r
2549 if(g_settings.getBool("enable_fog") == true)
\r
2551 f32 range = draw_control.wanted_range * BS;
\r
2552 if(draw_control.range_all)
\r
2553 range = 100000*BS;
\r
2557 video::EFT_FOG_LINEAR,
\r
2561 false, // pixel fog
\r
2562 false // range fog
\r
2568 Update gui stuff (0ms)
\r
2571 //TimeTaker guiupdatetimer("Gui updating");
\r
2574 wchar_t temptext[150];
\r
2576 static float drawtime_avg = 0;
\r
2577 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2578 static float beginscenetime_avg = 0;
\r
2579 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2580 static float scenetime_avg = 0;
\r
2581 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2582 static float endscenetime_avg = 0;
\r
2583 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2585 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2587 L", R: range_all=%i"
\r
2589 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2591 draw_control.range_all,
\r
2593 beginscenetime_avg,
\r
2598 guitext->setText(temptext);
\r
2602 wchar_t temptext[150];
\r
2603 swprintf(temptext, 150,
\r
2604 L"(% .1f, % .1f, % .1f)"
\r
2605 L" (% .3f < btime_jitter < % .3f"
\r
2606 L", dtime_jitter = % .1f %%"
\r
2607 L", v_range = %.1f)",
\r
2608 player_position.X/BS,
\r
2609 player_position.Y/BS,
\r
2610 player_position.Z/BS,
\r
2611 busytime_jitter1_min_sample,
\r
2612 busytime_jitter1_max_sample,
\r
2613 dtime_jitter1_max_fraction * 100.0,
\r
2614 draw_control.wanted_range
\r
2617 guitext2->setText(temptext);
\r
2621 guitext_info->setText(infotext.c_str());
\r
2625 Get chat messages from client
\r
2628 // Get new messages
\r
2629 std::wstring message;
\r
2630 while(client.getChatMessage(message))
\r
2632 chat_lines.push_back(ChatLine(message));
\r
2633 /*if(chat_lines.size() > 6)
\r
2635 core::list<ChatLine>::Iterator
\r
2636 i = chat_lines.begin();
\r
2637 chat_lines.erase(i);
\r
2640 // Append them to form the whole static text and throw
\r
2641 // it to the gui element
\r
2642 std::wstring whole;
\r
2643 // This will correspond to the line number counted from
\r
2644 // top to bottom, from size-1 to 0
\r
2645 s16 line_number = chat_lines.size();
\r
2646 // Count of messages to be removed from the top
\r
2647 u16 to_be_removed_count = 0;
\r
2648 for(core::list<ChatLine>::Iterator
\r
2649 i = chat_lines.begin();
\r
2650 i != chat_lines.end(); i++)
\r
2652 // After this, line number is valid for this loop
\r
2655 (*i).age += dtime;
\r
2657 This results in a maximum age of 60*6 to the
\r
2658 lowermost line and a maximum of 6 lines
\r
2660 float allowed_age = (6-line_number) * 60.0;
\r
2662 if((*i).age > allowed_age)
\r
2664 to_be_removed_count++;
\r
2667 whole += (*i).text + L'\n';
\r
2669 for(u16 i=0; i<to_be_removed_count; i++)
\r
2671 core::list<ChatLine>::Iterator
\r
2672 it = chat_lines.begin();
\r
2673 chat_lines.erase(it);
\r
2675 guitext_chat->setText(whole.c_str());
\r
2676 // Update gui element size and position
\r
2677 core::rect<s32> rect(
\r
2679 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2680 screensize.X - 10,
\r
2683 guitext_chat->setRelativePosition(rect);
\r
2685 if(chat_lines.size() == 0)
\r
2686 guitext_chat->setVisible(false);
\r
2688 guitext_chat->setVisible(true);
\r
2695 static u16 old_selected_item = 65535;
\r
2696 if(client.getLocalInventoryUpdated()
\r
2697 || g_selected_item != old_selected_item)
\r
2699 old_selected_item = g_selected_item;
\r
2700 //std::cout<<"Updating local inventory"<<std::endl;
\r
2701 client.getLocalInventory(local_inventory);
\r
2702 quick_inventory->setSelection(g_selected_item);
\r
2703 quick_inventory->update();
\r
2707 Send actions returned by the inventory menu
\r
2709 while(inventory_action_queue.size() != 0)
\r
2711 InventoryAction *a = inventory_action_queue.pop_front();
\r
2713 client.sendInventoryAction(a);
\r
2722 TimeTaker drawtimer("Drawing");
\r
2726 TimeTaker timer("beginScene");
\r
2727 driver->beginScene(true, true, bgcolor);
\r
2728 //driver->beginScene(false, true, bgcolor);
\r
2729 beginscenetime = timer.stop(true);
\r
2734 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2737 TimeTaker timer("smgr");
\r
2739 scenetime = timer.stop(true);
\r
2743 //TimeTaker timer9("auxiliary drawings");
\r
2747 //TimeTaker //timer10("//timer10");
\r
2749 video::SMaterial m;
\r
2751 m.Lighting = false;
\r
2752 driver->setMaterial(m);
\r
2754 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2756 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2757 i != hilightboxes.end(); i++)
\r
2759 /*std::cout<<"hilightbox min="
\r
2760 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2762 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2764 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2770 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2771 displaycenter + core::vector2d<s32>(10,0),
\r
2772 video::SColor(255,255,255,255));
\r
2773 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2774 displaycenter + core::vector2d<s32>(0,10),
\r
2775 video::SColor(255,255,255,255));
\r
2780 //TimeTaker //timer11("//timer11");
\r
2786 guienv->drawAll();
\r
2790 TimeTaker timer("endScene");
\r
2791 driver->endScene();
\r
2792 endscenetime = timer.stop(true);
\r
2795 drawtime = drawtimer.stop(true);
\r
2801 static s16 lastFPS = 0;
\r
2802 //u16 fps = driver->getFPS();
\r
2803 u16 fps = (1.0/dtime_avg1);
\r
2805 if (lastFPS != fps)
\r
2807 core::stringw str = L"Minetest [";
\r
2808 str += driver->getName();
\r
2812 device->setWindowCaption(str.c_str());
\r
2818 device->yield();*/
\r
2821 delete quick_inventory;
\r
2823 } // client and server are deleted at this point
\r
2826 catch(con::PeerNotFoundException &e)
\r
2828 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2829 error_message = L"Connection timed out.";
\r
2832 } // Menu-game loop
\r
2837 In the end, delete the Irrlicht device.
\r
2842 Update configuration file
\r
2844 /*if(configpath != "")
\r
2846 g_settings.updateConfigFile(configpath.c_str());
\r
2849 END_DEBUG_EXCEPTION_HANDLER
\r
2851 debugstreams_deinit();
\r