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: MovingObject::move and Player::move are basically the same.
\r
82 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
83 if something is already in it)
\r
84 - Use it in active block queue in water flowing
\r
86 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
87 - This is not doable because it is currently hand-made and not
\r
88 based on some mathematical function.
\r
90 SUGG: A version number to blocks, which increments when the block is
\r
91 modified (node add/remove, water update, lighting update)
\r
92 - This can then be used to make sure the most recent version of
\r
93 a block has been sent to client
\r
95 SUGG: Make the amount of blocks sending to client and the total
\r
96 amount of blocks dynamically limited. Transferring blocks is the
\r
97 main network eater of this system, so it is the one that has
\r
98 to be throttled so that RTTs stay low.
\r
100 SUGG: Meshes of blocks could be split into 6 meshes facing into
\r
101 different directions and then only those drawn that need to be
\r
102 - Also an 1-dimensional tile map would be nice probably
\r
107 - How would some GTA-style ideas work?
\r
108 - Cars? Stealing? Unlawful stuff and cops? Lots of guns?
\r
112 - Space racer style?
\r
117 Build system / running:
\r
118 -----------------------
\r
120 NOTE: The following fixme is not apparently valid, and it does work.
\r
121 FIXME: Graphical mode seems to segfault with Irrlicht 1.7.1 on 64-bit
\r
123 - http://pastebin.no/32bo
\r
124 - Might be just a bad build, too
\r
125 - Doesn't affect Irrlicht 1.7.2 or 32-bit 1.7.1. (Arch/Debian)
\r
126 - A similar error occurs when getTexture is called from a thread
\r
127 when the texture has not been already loaded from disk:
\r
128 http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=68830
\r
130 FIXME: Some network errors on Windows that cause local game to not work
\r
131 - See siggjen's emails.
\r
133 Networking and serialization:
\r
134 -----------------------------
\r
136 TODO: Get rid of GotSplitPacketException
\r
141 TODO: Add gui option to remove map
\r
143 TODO: Configuration menu, at least for keys
\r
148 TODO: Optimize day/night mesh updating somehow
\r
149 - create copies of all textures for all lighting values and only
\r
150 change texture for material?
\r
151 - Umm... the collecting of the faces is the slow part
\r
152 -> what about just changing the color values of the existing
\r
153 meshbuffers? It should go quite fast.
\r
154 - This is not easy; There'd need to be a buffer somewhere
\r
155 that would contain the night and day lighting values.
\r
157 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
159 - That is >500 vertices
\r
160 - This is not easy; all the MapBlocks close to the player would
\r
161 still need to be drawn separately and combining the blocks
\r
162 would have to happen in a background thread
\r
164 TODO: Make fetching sector's blocks more efficient when rendering
\r
165 sectors that have very large amounts of blocks (on client)
\r
166 - Is this necessary at all?
\r
168 TODO: Flowing water animation
\r
173 TODO: Make the video backend selectable
\r
178 TODO: Untie client network operations from framerate
\r
179 - Needs some input queues or something
\r
180 - Not really necessary?
\r
182 TODO: Make morning and evening shorter
\r
187 TODO: When player dies, throw items on map
\r
189 TODO: Make an option to the server to disable building and digging near
\r
190 the starting position
\r
192 TODO: Save players with inventories to disk
\r
193 TODO: Players to be saved as text in map/players/<name>
\r
195 TODO: Copy the text of the last picked sign to inventory in creative
\r
198 TODO: Check what goes wrong with caching map to disk (Kray)
\r
201 TODO: When server sees that client is removing an inexistent block or
\r
202 adding a block to an existent position, resend the MapBlock.
\r
207 TODO: Better handling of objects and mobs
\r
209 - There has to be some way to do it with less messy code
\r
210 - Make separate classes for client and server
\r
211 - Client should not discriminate between blocks, server should
\r
212 - Make other players utilize the same framework
\r
213 - This is also needed for objects that don't get sent to client
\r
214 but are used for triggers etc
\r
216 TODO: There has to be some better way to handle static objects than to
\r
217 send them all the time. This affects signs and item objects.
\r
218 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
219 need an additional metadata field for the texts
\r
220 - This is also needed for item container chests
\r
222 Block object server side:
\r
223 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
224 - For all blocks in the buffer, objects are stepped(). This
\r
225 means they are active.
\r
226 - TODO: A global active buffer is needed for the server
\r
227 - TODO: A timestamp to blocks
\r
228 - TODO: All blocks going in and out of the buffer are recorded.
\r
229 - TODO: For outgoing blocks, timestamp is written.
\r
230 - TODO: For incoming blocks, time difference is calculated and
\r
231 objects are stepped according to it.
\r
236 NOTE: There are some lighting-related todos and fixmes in
\r
237 ServerMap::emergeBlock. And there always will be. 8)
\r
239 FEATURE: Map generator version 2
\r
240 - Create surface areas based on central points; a given point's
\r
241 area type is given by the nearest central point
\r
242 - Separate points for heightmap, caves, plants and minerals?
\r
243 - Flat land, mountains, forest, jungle
\r
245 - There could be a certain height (to which mountains only reach)
\r
246 where some minerals are found
\r
247 - Create a system that allows a huge amount of different "map
\r
248 generator modules/filters"
\r
250 FEATURE: The map could be generated procedually:
\r
251 - This would need the map to be generated in larger pieces
\r
252 - How large? How do they connect to each other?
\r
253 * Make the stone level with a heightmap
\r
254 * Carve out stuff in the stone
\r
255 * Dump dirt all around, and simulate it falling off steep
\r
257 * Erosion simulation at map generation time
\r
258 - Simulate water flows, which would carve out dirt fast and
\r
259 then turn stone into gravel and sand and relocate it.
\r
260 - How about relocating minerals, too? Coal and gold in
\r
261 downstream sand and gravel would be kind of cool
\r
262 - This would need a better way of handling minerals, mainly
\r
263 to have mineral content as a separate field
\r
264 - Simulate rock falling from cliffs when water has removed
\r
265 enough solid rock from the bottom
\r
267 TODO: Change AttributeList to split the area into smaller sections so
\r
268 that searching won't be as heavy.
\r
270 TODO: Remove HMParams
\r
272 TODO: Flowing water to actually contain flow direction information
\r
274 TODO: Remove duplicate lighting implementation from Map (leave
\r
275 VoxelManipulator, which is faster)
\r
280 ======================================================================
\r
285 Setting this to 1 enables a special camera mode that forces
\r
286 the renderers to think that the camera statically points from
\r
287 the starting place to a static direction.
\r
289 This allows one to move around with the player and see what
\r
290 is actually drawn behind solid things and behind the player.
\r
292 #define FIELD_OF_VIEW_TEST 0
\r
296 #pragma message ("Disabling unit tests")
\r
298 #warning "Disabling unit tests"
\r
300 // Disable unit tests
\r
301 #define ENABLE_TESTS 0
\r
303 // Enable unit tests
\r
304 #define ENABLE_TESTS 1
\r
308 #pragma comment(lib, "Irrlicht.lib")
\r
309 //#pragma comment(lib, "jthread.lib")
\r
310 #pragma comment(lib, "zlibwapi.lib")
\r
311 // This would get rid of the console window
\r
312 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
315 #include <iostream>
\r
317 #include <jmutexautolock.h>
\r
318 #include <locale.h>
\r
319 #include "common_irrlicht.h"
\r
322 #include "player.h"
\r
325 #include "environment.h"
\r
326 #include "server.h"
\r
327 #include "client.h"
\r
328 #include "serialization.h"
\r
329 #include "constants.h"
\r
330 #include "strfnd.h"
\r
331 #include "porting.h"
\r
332 #include "irrlichtwrapper.h"
\r
333 #include "gettime.h"
\r
334 #include "porting.h"
\r
335 #include "guiPauseMenu.h"
\r
336 #include "guiInventoryMenu.h"
\r
337 #include "guiTextInputMenu.h"
\r
338 #include "materials.h"
\r
339 #include "guiMessageMenu.h"
\r
340 #include "filesys.h"
\r
341 #include "config.h"
\r
342 #include "guiMainMenu.h"
\r
344 IrrlichtWrapper *g_irrlicht;
\r
346 MapDrawControl draw_control;
\r
350 These are loaded from the config file.
\r
353 Settings g_settings;
\r
355 extern void set_default_settings();
\r
361 IrrlichtDevice *g_device = NULL;
\r
362 Client *g_client = NULL;
\r
368 gui::IGUIEnvironment* guienv = NULL;
\r
369 gui::IGUIStaticText *guiroot = NULL;
\r
371 class MainMenuManager : public IMenuManager
\r
374 virtual void createdMenu(GUIModalMenu *menu)
\r
376 for(core::list<GUIModalMenu*>::Iterator
\r
377 i = m_stack.begin();
\r
378 i != m_stack.end(); i++)
\r
380 assert(*i != menu);
\r
383 if(m_stack.size() != 0)
\r
384 (*m_stack.getLast())->setVisible(false);
\r
385 m_stack.push_back(menu);
\r
388 virtual void deletingMenu(GUIModalMenu *menu)
\r
390 // Remove all entries if there are duplicates
\r
391 bool removed_entry;
\r
393 removed_entry = false;
\r
394 for(core::list<GUIModalMenu*>::Iterator
\r
395 i = m_stack.begin();
\r
396 i != m_stack.end(); i++)
\r
401 removed_entry = true;
\r
405 }while(removed_entry);
\r
407 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
408 assert(*i == menu);
\r
409 m_stack.erase(i);*/
\r
411 if(m_stack.size() != 0)
\r
412 (*m_stack.getLast())->setVisible(true);
\r
417 return m_stack.size();
\r
420 core::list<GUIModalMenu*> m_stack;
\r
423 MainMenuManager g_menumgr;
\r
425 bool noMenuActive()
\r
427 return (g_menumgr.menuCount() == 0);
\r
430 bool g_disconnect_requested = false;
\r
432 class MainGameCallback : public IGameCallback
\r
435 virtual void exitToOS()
\r
437 g_device->closeDevice();
\r
440 virtual void disconnect()
\r
442 g_disconnect_requested = true;
\r
446 MainGameCallback g_gamecallback;
\r
448 // Inventory actions from the menu are buffered here before sending
\r
449 Queue<InventoryAction*> inventory_action_queue;
\r
450 // This is a copy of the inventory that the client's environment has
\r
451 Inventory local_inventory;
\r
453 u16 g_selected_item = 0;
\r
460 std::ostream *dout_con_ptr = &dummyout;
\r
461 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
462 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
463 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
464 //std::ostream *dout_con_ptr = &dstream;
\r
465 //std::ostream *derr_con_ptr = &dstream;
\r
468 std::ostream *dout_server_ptr = &dstream;
\r
469 std::ostream *derr_server_ptr = &dstream;
\r
472 std::ostream *dout_client_ptr = &dstream;
\r
473 std::ostream *derr_client_ptr = &dstream;
\r
476 gettime.h implementation
\r
482 Use irrlicht because it is more precise than porting.h's
\r
485 if(g_irrlicht == NULL)
\r
487 return g_irrlicht->getTime();
\r
494 struct TextDestSign : public TextDest
\r
496 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
498 m_blockpos = blockpos;
\r
502 void gotText(std::wstring text)
\r
504 std::string ntext = wide_to_narrow(text);
\r
505 dstream<<"Changing text of a sign object: "
\r
506 <<ntext<<std::endl;
\r
507 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
515 struct TextDestChat : public TextDest
\r
517 TextDestChat(Client *client)
\r
521 void gotText(std::wstring text)
\r
523 m_client->sendChatMessage(text);
\r
524 m_client->addChatMessage(text);
\r
530 class MyEventReceiver : public IEventReceiver
\r
533 // This is the one method that we have to implement
\r
534 virtual bool OnEvent(const SEvent& event)
\r
537 React to nothing here if a menu is active
\r
539 if(noMenuActive() == false)
\r
545 // Remember whether each key is down or up
\r
546 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
548 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
550 if(event.KeyInput.PressedDown)
\r
552 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
558 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
560 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
562 dstream<<DTIME<<"MyEventReceiver: "
\r
563 <<"Launching pause menu"<<std::endl;
\r
564 // It will delete itself by itself
\r
565 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
566 &g_menumgr))->drop();
\r
569 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
571 dstream<<DTIME<<"MyEventReceiver: "
\r
572 <<"Launching inventory"<<std::endl;
\r
573 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
574 &local_inventory, &inventory_action_queue,
\r
575 &g_menumgr))->drop();
\r
578 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
580 TextDest *dest = new TextDestChat(g_client);
\r
582 (new GUITextInputMenu(guienv, guiroot, -1,
\r
588 // Material selection
\r
589 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
591 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
594 g_selected_item = 0;
\r
595 dstream<<DTIME<<"Selected item: "
\r
596 <<g_selected_item<<std::endl;
\r
599 // Viewing range selection
\r
600 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
602 if(draw_control.range_all)
\r
604 draw_control.range_all = false;
\r
605 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
609 draw_control.range_all = true;
\r
610 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
614 // Print debug stacks
\r
615 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
617 dstream<<"-----------------------------------------"
\r
619 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
620 dstream<<"-----------------------------------------"
\r
622 debug_stacks_print();
\r
627 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
629 if(noMenuActive() == false)
\r
631 left_active = false;
\r
632 middle_active = false;
\r
633 right_active = false;
\r
637 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
638 left_active = event.MouseInput.isLeftPressed();
\r
639 middle_active = event.MouseInput.isMiddlePressed();
\r
640 right_active = event.MouseInput.isRightPressed();
\r
642 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
644 leftclicked = true;
\r
646 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
648 rightclicked = true;
\r
650 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
652 leftreleased = true;
\r
654 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
656 rightreleased = true;
\r
658 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
660 /*dstream<<"event.MouseInput.Wheel="
\r
661 <<event.MouseInput.Wheel<<std::endl;*/
\r
662 if(event.MouseInput.Wheel < 0)
\r
664 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
667 g_selected_item = 0;
\r
669 else if(event.MouseInput.Wheel > 0)
\r
671 if(g_selected_item > 0)
\r
674 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
683 // This is used to check whether a key is being held down
\r
684 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
686 return keyIsDown[keyCode];
\r
691 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
692 keyIsDown[i] = false;
\r
694 leftclicked = false;
\r
695 rightclicked = false;
\r
696 leftreleased = false;
\r
697 rightreleased = false;
\r
699 left_active = false;
\r
700 middle_active = false;
\r
701 right_active = false;
\r
712 bool rightreleased;
\r
715 bool middle_active;
\r
719 // We use this array to store the current state of each key
\r
720 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
723 IrrlichtDevice *m_device;
\r
732 virtual ~InputHandler()
\r
736 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
738 virtual v2s32 getMousePos() = 0;
\r
739 virtual void setMousePos(s32 x, s32 y) = 0;
\r
741 virtual bool getLeftState() = 0;
\r
742 virtual bool getRightState() = 0;
\r
744 virtual bool getLeftClicked() = 0;
\r
745 virtual bool getRightClicked() = 0;
\r
746 virtual void resetLeftClicked() = 0;
\r
747 virtual void resetRightClicked() = 0;
\r
749 virtual bool getLeftReleased() = 0;
\r
750 virtual bool getRightReleased() = 0;
\r
751 virtual void resetLeftReleased() = 0;
\r
752 virtual void resetRightReleased() = 0;
\r
754 virtual void step(float dtime) {};
\r
756 virtual void clear() {};
\r
759 InputHandler *g_input = NULL;
\r
761 class RealInputHandler : public InputHandler
\r
764 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
766 m_receiver(receiver)
\r
769 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
771 return m_receiver->IsKeyDown(keyCode);
\r
773 virtual v2s32 getMousePos()
\r
775 return m_device->getCursorControl()->getPosition();
\r
777 virtual void setMousePos(s32 x, s32 y)
\r
779 m_device->getCursorControl()->setPosition(x, y);
\r
782 virtual bool getLeftState()
\r
784 return m_receiver->left_active;
\r
786 virtual bool getRightState()
\r
788 return m_receiver->right_active;
\r
791 virtual bool getLeftClicked()
\r
793 return m_receiver->leftclicked;
\r
795 virtual bool getRightClicked()
\r
797 return m_receiver->rightclicked;
\r
799 virtual void resetLeftClicked()
\r
801 m_receiver->leftclicked = false;
\r
803 virtual void resetRightClicked()
\r
805 m_receiver->rightclicked = false;
\r
808 virtual bool getLeftReleased()
\r
810 return m_receiver->leftreleased;
\r
812 virtual bool getRightReleased()
\r
814 return m_receiver->rightreleased;
\r
816 virtual void resetLeftReleased()
\r
818 m_receiver->leftreleased = false;
\r
820 virtual void resetRightReleased()
\r
822 m_receiver->rightreleased = false;
\r
827 resetRightClicked();
\r
828 resetLeftClicked();
\r
831 IrrlichtDevice *m_device;
\r
832 MyEventReceiver *m_receiver;
\r
835 class RandomInputHandler : public InputHandler
\r
838 RandomInputHandler()
\r
840 leftclicked = false;
\r
841 rightclicked = false;
\r
842 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
843 keydown[i] = false;
\r
845 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
847 return keydown[keyCode];
\r
849 virtual v2s32 getMousePos()
\r
853 virtual void setMousePos(s32 x, s32 y)
\r
855 mousepos = v2s32(x,y);
\r
858 virtual bool getLeftState()
\r
862 virtual bool getRightState()
\r
867 virtual bool getLeftClicked()
\r
869 return leftclicked;
\r
871 virtual bool getRightClicked()
\r
873 return rightclicked;
\r
875 virtual void resetLeftClicked()
\r
877 leftclicked = false;
\r
879 virtual void resetRightClicked()
\r
881 rightclicked = false;
\r
884 virtual bool getLeftReleased()
\r
888 virtual bool getRightReleased()
\r
892 virtual void resetLeftReleased()
\r
895 virtual void resetRightReleased()
\r
899 virtual void step(float dtime)
\r
902 static float counter1 = 0;
\r
906 counter1 = 0.1*Rand(1,10);
\r
907 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
908 g_selected_material++;
\r
910 g_selected_material = 0;*/
\r
911 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
914 g_selected_item = 0;
\r
918 static float counter1 = 0;
\r
922 counter1 = 0.1*Rand(1, 40);
\r
923 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
927 static float counter1 = 0;
\r
931 counter1 = 0.1*Rand(1, 40);
\r
932 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
936 static float counter1 = 0;
\r
940 counter1 = 0.1*Rand(1, 40);
\r
941 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
945 static float counter1 = 0;
\r
949 counter1 = 0.1*Rand(1, 40);
\r
950 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
954 static float counter1 = 0;
\r
958 counter1 = 0.1*Rand(1, 20);
\r
959 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
963 static float counter1 = 0;
\r
967 counter1 = 0.1*Rand(1, 30);
\r
968 leftclicked = true;
\r
972 static float counter1 = 0;
\r
976 counter1 = 0.1*Rand(1, 20);
\r
977 rightclicked = true;
\r
980 mousepos += mousespeed;
\r
983 s32 Rand(s32 min, s32 max)
\r
985 return (myrand()%(max-min+1))+min;
\r
988 bool keydown[KEY_KEY_CODES_COUNT];
\r
995 void updateViewingRange(f32 frametime_in, Client *client)
\r
997 if(draw_control.range_all == true)
\r
1000 static f32 added_frametime = 0;
\r
1001 static s16 added_frames = 0;
\r
1003 added_frametime += frametime_in;
\r
1004 added_frames += 1;
\r
1006 // Actually this counter kind of sucks because frametime is busytime
\r
1007 static f32 counter = 0;
\r
1008 counter -= frametime_in;
\r
1014 /*dstream<<__FUNCTION_NAME
\r
1015 <<": Collected "<<added_frames<<" frames, total of "
\r
1016 <<added_frametime<<"s."<<std::endl;*/
\r
1018 /*dstream<<"draw_control.blocks_drawn="
\r
1019 <<draw_control.blocks_drawn
\r
1020 <<", draw_control.blocks_would_have_drawn="
\r
1021 <<draw_control.blocks_would_have_drawn
\r
1024 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1025 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1027 draw_control.wanted_min_range = range_min;
\r
1028 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1030 float block_draw_ratio = 1.0;
\r
1031 if(draw_control.blocks_would_have_drawn != 0)
\r
1033 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1034 / (float)draw_control.blocks_would_have_drawn;
\r
1037 // Calculate the average frametime in the case that all wanted
\r
1038 // blocks had been drawn
\r
1039 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1041 added_frametime = 0.0;
\r
1044 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1045 float wanted_frametime = 1.0 / wanted_fps;
\r
1047 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1048 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1050 // If needed frametime change is very small, just return
\r
1051 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1053 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1057 float range = draw_control.wanted_range;
\r
1058 float new_range = range;
\r
1060 static s16 range_old = 0;
\r
1061 static f32 frametime_old = 0;
\r
1063 float d_range = range - range_old;
\r
1064 f32 d_frametime = frametime - frametime_old;
\r
1065 // A sane default of 30ms per 50 nodes of range
\r
1066 static f32 time_per_range = 30. / 50;
\r
1069 time_per_range = d_frametime / d_range;
\r
1072 // The minimum allowed calculated frametime-range derivative:
\r
1073 // Practically this sets the maximum speed of changing the range.
\r
1074 // The lower this value, the higher the maximum changing speed.
\r
1075 // A low value here results in wobbly range (0.001)
\r
1076 // A high value here results in slow changing range (0.0025)
\r
1077 // SUGG: This could be dynamically adjusted so that when
\r
1078 // the camera is turning, this is lower
\r
1079 //float min_time_per_range = 0.0015;
\r
1080 float min_time_per_range = 0.0010;
\r
1081 //float min_time_per_range = 0.05 / range;
\r
1082 if(time_per_range < min_time_per_range)
\r
1084 time_per_range = min_time_per_range;
\r
1085 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1089 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1092 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1093 // Dampen the change a bit to kill oscillations
\r
1094 //wanted_range_change *= 0.9;
\r
1095 //wanted_range_change *= 0.75;
\r
1096 wanted_range_change *= 0.5;
\r
1097 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1099 // If needed range change is very small, just return
\r
1100 if(fabs(wanted_range_change) < 0.001)
\r
1102 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1106 new_range += wanted_range_change;
\r
1107 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1109 //float new_range_unclamped = new_range;
\r
1110 if(new_range < range_min)
\r
1111 new_range = range_min;
\r
1112 if(new_range > range_max)
\r
1113 new_range = range_max;
\r
1115 /*if(new_range != new_range_unclamped)
\r
1116 dstream<<", clamped to "<<new_range<<std::endl;
\r
1118 dstream<<std::endl;*/
\r
1120 draw_control.wanted_range = new_range;
\r
1122 range_old = new_range;
\r
1123 frametime_old = frametime;
\r
1126 class GUIQuickInventory : public IEventReceiver
\r
1129 GUIQuickInventory(
\r
1130 gui::IGUIEnvironment* env,
\r
1131 gui::IGUIElement* parent,
\r
1134 Inventory *inventory):
\r
1135 m_itemcount(itemcount),
\r
1136 m_inventory(inventory)
\r
1138 core::rect<s32> imgsize(0,0,48,48);
\r
1139 core::rect<s32> textsize(0,0,48,16);
\r
1140 v2s32 spacing(0, 64);
\r
1141 for(s32 i=0; i<m_itemcount; i++)
\r
1143 m_images.push_back(env->addImage(
\r
1144 imgsize + pos + spacing*i
\r
1146 m_images[i]->setScaleImage(true);
\r
1147 m_texts.push_back(env->addStaticText(
\r
1149 textsize + pos + spacing*i,
\r
1152 m_texts[i]->setBackgroundColor(
\r
1153 video::SColor(128,0,0,0));
\r
1154 m_texts[i]->setTextAlignment(
\r
1155 gui::EGUIA_CENTER,
\r
1156 gui::EGUIA_UPPERLEFT);
\r
1160 ~GUIQuickInventory()
\r
1162 for(u32 i=0; i<m_texts.size(); i++)
\r
1164 m_texts[i]->remove();
\r
1166 for(u32 i=0; i<m_images.size(); i++)
\r
1168 m_images[i]->remove();
\r
1172 virtual bool OnEvent(const SEvent& event)
\r
1177 void setSelection(s32 i)
\r
1186 start = m_selection - m_itemcount / 2;
\r
1188 InventoryList *mainlist = m_inventory->getList("main");
\r
1190 for(s32 i=0; i<m_itemcount; i++)
\r
1192 s32 j = i + start;
\r
1194 if(j > (s32)mainlist->getSize() - 1)
\r
1195 j -= mainlist->getSize();
\r
1197 j += mainlist->getSize();
\r
1199 InventoryItem *item = mainlist->getItem(j);
\r
1203 m_images[i]->setImage(NULL);
\r
1206 if(m_selection == j)
\r
1207 swprintf(t, 10, L"<-");
\r
1209 swprintf(t, 10, L"");
\r
1210 m_texts[i]->setText(t);
\r
1212 // The next ifs will segfault with a NULL pointer
\r
1217 m_images[i]->setImage(item->getImage());
\r
1220 if(m_selection == j)
\r
1221 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1223 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1224 m_texts[i]->setText(t);
\r
1230 core::array<gui::IGUIStaticText*> m_texts;
\r
1231 core::array<gui::IGUIImage*> m_images;
\r
1232 Inventory *m_inventory;
\r
1243 ChatLine(const std::wstring &a_text):
\r
1249 std::wstring text;
\r
1252 int main(int argc, char *argv[])
\r
1255 Low-level initialization
\r
1258 bool disable_stderr = false;
\r
1260 disable_stderr = true;
\r
1263 // Initialize debug streams
\r
1264 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1265 // Initialize debug stacks
\r
1266 debug_stacks_init();
\r
1268 DSTACK(__FUNCTION_NAME);
\r
1270 porting::initializePaths();
\r
1271 // Create user data directory
\r
1272 fs::CreateDir(porting::path_userdata);
\r
1274 initializeMaterialProperties();
\r
1276 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1278 // Print startup message
\r
1279 dstream<<DTIME<<"minetest-c55"
\r
1280 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1281 <<", "<<BUILD_INFO
\r
1285 Parse command line
\r
1288 // List all allowed options
\r
1289 core::map<std::string, ValueSpec> allowed_options;
\r
1290 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1291 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1292 "Run server directly"));
\r
1293 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1294 "Load configuration from specified file"));
\r
1295 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1296 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1297 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1298 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1299 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1300 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1302 Settings cmd_args;
\r
1304 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1306 if(ret == false || cmd_args.getFlag("help"))
\r
1308 dstream<<"Allowed options:"<<std::endl;
\r
1309 for(core::map<std::string, ValueSpec>::Iterator
\r
1310 i = allowed_options.getIterator();
\r
1311 i.atEnd() == false; i++)
\r
1313 dstream<<" --"<<i.getNode()->getKey();
\r
1314 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1319 dstream<<" <value>";
\r
1321 dstream<<std::endl;
\r
1323 if(i.getNode()->getValue().help != NULL)
\r
1325 dstream<<" "<<i.getNode()->getValue().help
\r
1330 return cmd_args.getFlag("help") ? 0 : 1;
\r
1335 Basic initialization
\r
1338 // Initialize default settings
\r
1339 set_default_settings();
\r
1341 // Set locale. This is for forcing '.' as the decimal point.
\r
1342 std::locale::global(std::locale("C"));
\r
1343 // This enables printing all characters in bitmap font
\r
1344 setlocale(LC_CTYPE, "en_US");
\r
1346 // Initialize sockets
\r
1348 atexit(sockets_cleanup);
\r
1358 // Path of configuration file in use
\r
1359 std::string configpath = "";
\r
1361 if(cmd_args.exists("config"))
\r
1363 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1366 dstream<<"Could not read configuration from \""
\r
1367 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1370 configpath = cmd_args.get("config");
\r
1374 core::array<std::string> filenames;
\r
1375 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1376 #ifdef RUN_IN_PLACE
\r
1377 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1380 for(u32 i=0; i<filenames.size(); i++)
\r
1382 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1385 configpath = filenames[i];
\r
1391 // Initialize random seed
\r
1398 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1399 || cmd_args.getFlag("enable-unittests") == true)
\r
1404 // Read map parameters from settings
\r
1406 HMParams hm_params;
\r
1407 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1408 hm_params.randmax = g_settings.get("height_randmax");
\r
1409 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1410 hm_params.base = g_settings.get("height_base");*/
\r
1412 MapParams map_params;
\r
1413 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1414 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1422 if(cmd_args.exists("port"))
\r
1423 port = cmd_args.getU16("port");
\r
1424 else if(cmd_args.exists("port"))
\r
1425 port = g_settings.getU16("port");
\r
1428 std::string map_dir = porting::path_userdata+"/map";
\r
1429 if(cmd_args.exists("map-dir"))
\r
1430 map_dir = cmd_args.get("map-dir");
\r
1431 else if(g_settings.exists("map-dir"))
\r
1432 map_dir = g_settings.get("map-dir");
\r
1434 // Run dedicated server if asked to
\r
1435 if(cmd_args.getFlag("server"))
\r
1437 DSTACK("Dedicated server branch");
\r
1440 Server server(map_dir.c_str(), hm_params, map_params);
\r
1441 server.start(port);
\r
1444 dedicated_server_loop(server);
\r
1453 // Address to connect to
\r
1454 std::string address = "";
\r
1456 if(cmd_args.exists("address"))
\r
1458 address = cmd_args.get("address");
\r
1462 address = g_settings.get("address");
\r
1465 std::string playername = g_settings.get("name");
\r
1468 Resolution selection
\r
1471 bool fullscreen = false;
\r
1472 u16 screenW = g_settings.getU16("screenW");
\r
1473 u16 screenH = g_settings.getU16("screenH");
\r
1477 MyEventReceiver receiver;
\r
1479 video::E_DRIVER_TYPE driverType;
\r
1482 //driverType = video::EDT_DIRECT3D9;
\r
1483 driverType = video::EDT_OPENGL;
\r
1485 driverType = video::EDT_OPENGL;
\r
1486 //driverType = video::EDT_BURNINGSVIDEO;
\r
1489 // create device and exit if creation failed
\r
1491 IrrlichtDevice *device;
\r
1492 device = createDevice(driverType,
\r
1493 core::dimension2d<u32>(screenW, screenH),
\r
1494 16, fullscreen, false, false, &receiver);
\r
1497 return 1; // could not create selected driver.
\r
1499 g_device = device;
\r
1500 g_irrlicht = new IrrlichtWrapper(device);
\r
1502 //g_device = device;
\r
1504 device->setResizable(true);
\r
1506 bool random_input = g_settings.getBool("random_input")
\r
1507 || cmd_args.getFlag("random-input");
\r
1509 g_input = new RandomInputHandler();
\r
1511 g_input = new RealInputHandler(device, &receiver);
\r
1514 Continue initialization
\r
1517 video::IVideoDriver* driver = device->getVideoDriver();
\r
1520 This changes the minimum allowed number of vertices in a VBO
\r
1522 //driver->setMinHardwareBufferVertexCount(50);
\r
1524 scene::ISceneManager* smgr = device->getSceneManager();
\r
1526 guienv = device->getGUIEnvironment();
\r
1527 gui::IGUISkin* skin = guienv->getSkin();
\r
1528 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1530 skin->setFont(font);
\r
1532 dstream<<"WARNING: Font file was not found."
\r
1533 " Using default font."<<std::endl;
\r
1534 // If font was not found, this will get us one
\r
1535 font = skin->getFont();
\r
1538 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1539 dstream<<"text_height="<<text_height<<std::endl;
\r
1541 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1542 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1543 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1544 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1545 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1546 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1549 Preload some textures
\r
1552 init_content_inventory_texture_paths();
\r
1553 init_tile_textures();
\r
1560 We need some kind of a root node to be able to add
\r
1561 custom gui elements directly on the screen.
\r
1562 Otherwise they won't be automatically drawn.
\r
1564 guiroot = guienv->addStaticText(L"",
\r
1565 core::rect<s32>(0, 0, 10000, 10000));
\r
1567 // First line of debug text
\r
1568 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1570 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1572 // Second line of debug text
\r
1573 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1575 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1578 // At the middle of the screen
\r
1579 // Object infos are shown in this
\r
1580 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1582 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1586 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1588 core::rect<s32>(0,0,0,0),
\r
1590 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1591 core::list<ChatLine> chat_lines;
\r
1594 If an error occurs, this is set to something and the
\r
1595 menu-game loop is restarted. It is then displayed before
\r
1598 std::wstring error_message = L"";
\r
1603 while(g_device->run())
\r
1606 // This is used for catching disconnects
\r
1611 Out-of-game menu loop
\r
1614 // Wait for proper parameters
\r
1617 // Cursor can be non-visible when coming from the game
\r
1618 device->getCursorControl()->setVisible(true);
\r
1619 // Some stuff are left to scene manager when coming from the game
\r
1620 // (map at least?)
\r
1622 // Reset or hide the debug gui texts
\r
1623 guitext->setText(L"Minetest-c55");
\r
1624 guitext2->setVisible(false);
\r
1625 guitext_info->setVisible(false);
\r
1626 guitext_chat->setVisible(false);
\r
1628 // Initialize menu data
\r
1629 MainMenuData menudata;
\r
1630 menudata.address = narrow_to_wide(address);
\r
1631 menudata.name = narrow_to_wide(playername);
\r
1632 menudata.port = narrow_to_wide(itos(port));
\r
1633 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1635 GUIMainMenu *menu =
\r
1636 new GUIMainMenu(guienv, guiroot, -1,
\r
1637 &g_menumgr, &menudata, &g_gamecallback);
\r
1638 menu->allowFocusRemoval(true);
\r
1640 if(error_message != L"")
\r
1642 GUIMessageMenu *menu2 =
\r
1643 new GUIMessageMenu(guienv, guiroot, -1,
\r
1644 &g_menumgr, error_message.c_str());
\r
1646 error_message = L"";
\r
1649 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1651 dstream<<"Created main menu"<<std::endl;
\r
1653 while(g_device->run())
\r
1655 // Run global IrrlichtWrapper's main thread processing stuff
\r
1656 g_irrlicht->Run();
\r
1658 if(menu->getStatus() == true)
\r
1661 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1662 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1663 guienv->drawAll();
\r
1664 driver->endScene();
\r
1667 // Break out of menu-game loop to shut down cleanly
\r
1668 if(g_device->run() == false)
\r
1671 dstream<<"Dropping main menu"<<std::endl;
\r
1675 playername = wide_to_narrow(menudata.name);
\r
1676 address = wide_to_narrow(menudata.address);
\r
1677 port = stoi(wide_to_narrow(menudata.port));
\r
1678 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1680 // Check for valid parameters, restart menu if invalid.
\r
1681 if(playername == "")
\r
1683 error_message = L"Name required.";
\r
1688 g_settings.set("name", playername);
\r
1689 g_settings.set("address", address);
\r
1690 g_settings.set("port", itos(port));
\r
1691 // Update configuration file
\r
1692 if(configpath != "")
\r
1693 g_settings.updateConfigFile(configpath.c_str());
\r
1695 // Continue to game
\r
1699 // Break out of menu-game loop to shut down cleanly
\r
1700 if(g_device->run() == false)
\r
1704 Make a scope here so that the client and the server and other
\r
1705 stuff gets removed when disconnected or the irrlicht device
\r
1711 Draw "Loading" screen
\r
1713 const wchar_t *text = L"Loading and connecting...";
\r
1714 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1715 core::vector2d<s32> textsize(300, text_height);
\r
1716 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1718 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1719 text, textrect, false, false);
\r
1720 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1722 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1723 guienv->drawAll();
\r
1724 driver->endScene();
\r
1726 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1730 SharedPtr will delete it when it goes out of scope.
\r
1732 SharedPtr<Server> server;
\r
1733 if(address == ""){
\r
1734 server = new Server(map_dir, hm_params, map_params);
\r
1735 server->start(port);
\r
1742 Client client(device, playername.c_str(), draw_control);
\r
1744 g_client = &client;
\r
1746 Address connect_address(0,0,0,0, port);
\r
1749 connect_address.Resolve("localhost");
\r
1751 connect_address.Resolve(address.c_str());
\r
1753 catch(ResolveError &e)
\r
1755 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1757 error_message = L"Couldn't resolve address";
\r
1758 gui_loadingtext->remove();
\r
1762 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1763 client.connect(connect_address);
\r
1766 while(client.connectedAndInitialized() == false)
\r
1769 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1770 guienv->drawAll();
\r
1771 driver->endScene();
\r
1773 // Update client and server
\r
1777 if(server != NULL)
\r
1778 server->step(0.1);
\r
1784 catch(con::PeerNotFoundException &e)
\r
1786 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1788 error_message = L"Connection timed out.";
\r
1789 gui_loadingtext->remove();
\r
1796 /*scene::ISceneNode* skybox;
\r
1797 skybox = smgr->addSkyBoxSceneNode(
\r
1798 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1799 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1800 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1801 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1802 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1803 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1806 Create the camera node
\r
1809 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1810 0, // Camera parent
\r
1811 v3f(BS*100, BS*2, BS*100), // Look from
\r
1812 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1816 if(camera == NULL)
\r
1819 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1821 camera->setFOV(FOV_ANGLE);
\r
1823 // Just so big a value that everything rendered is visible
\r
1824 camera->setFarValue(100000*BS);
\r
1826 f32 camera_yaw = 0; // "right/left"
\r
1827 f32 camera_pitch = 0; // "up/down"
\r
1833 gui_loadingtext->remove();
\r
1836 Add some gui stuff
\r
1839 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1840 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1842 // Test the text input system
\r
1843 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
1845 /*GUIMessageMenu *menu =
\r
1846 new GUIMessageMenu(guienv, guiroot, -1,
\r
1851 // Launch pause menu
\r
1852 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
1853 &g_menumgr))->drop();
\r
1856 guitext2->setVisible(true);
\r
1857 guitext_info->setVisible(true);
\r
1858 guitext_chat->setVisible(true);
\r
1861 Some statistics are collected in these
\r
1864 u32 beginscenetime = 0;
\r
1865 u32 scenetime = 0;
\r
1866 u32 endscenetime = 0;
\r
1869 //throw con::PeerNotFoundException("lol");
\r
1875 bool first_loop_after_window_activation = true;
\r
1877 // Time is in milliseconds
\r
1878 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1879 // NOTE: So we have to use getTime() and call run()s between them
\r
1880 u32 lasttime = device->getTimer()->getTime();
\r
1882 while(device->run())
\r
1884 if(g_disconnect_requested)
\r
1886 g_disconnect_requested = false;
\r
1891 Run global IrrlichtWrapper's main thread processing stuff
\r
1893 g_irrlicht->Run();
\r
1896 Random calculations
\r
1898 v2u32 screensize = driver->getScreenSize();
\r
1899 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1901 // Hilight boxes collected during the loop and displayed
\r
1902 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1905 std::wstring infotext;
\r
1907 //TimeTaker //timer1("//timer1");
\r
1909 // Time of frame without fps limit
\r
1913 // not using getRealTime is necessary for wine
\r
1914 u32 time = device->getTimer()->getTime();
\r
1915 if(time > lasttime)
\r
1916 busytime_u32 = time - lasttime;
\r
1919 busytime = busytime_u32 / 1000.0;
\r
1922 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1924 // Absolutelu necessary for wine!
\r
1931 updateViewingRange(busytime, &client);
\r
1938 float fps_max = g_settings.getFloat("fps_max");
\r
1939 u32 frametime_min = 1000./fps_max;
\r
1941 if(busytime_u32 < frametime_min)
\r
1943 u32 sleeptime = frametime_min - busytime_u32;
\r
1944 device->sleep(sleeptime);
\r
1948 // Absolutelu necessary for wine!
\r
1952 Time difference calculation
\r
1954 f32 dtime; // in seconds
\r
1956 u32 time = device->getTimer()->getTime();
\r
1957 if(time > lasttime)
\r
1958 dtime = (time - lasttime) / 1000.0;
\r
1964 Time average and jitter calculation
\r
1967 static f32 dtime_avg1 = 0.0;
\r
1968 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1969 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1971 static f32 dtime_jitter1_max_sample = 0.0;
\r
1972 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1974 static f32 jitter1_max = 0.0;
\r
1975 static f32 counter = 0.0;
\r
1976 if(dtime_jitter1 > jitter1_max)
\r
1977 jitter1_max = dtime_jitter1;
\r
1982 dtime_jitter1_max_sample = jitter1_max;
\r
1983 dtime_jitter1_max_fraction
\r
1984 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1985 jitter1_max = 0.0;
\r
1988 Control freetime ratio
\r
1990 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1992 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1993 g_freetime_ratio += 0.01;
\r
1997 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1998 g_freetime_ratio -= 0.01;
\r
2004 Busytime average and jitter calculation
\r
2007 static f32 busytime_avg1 = 0.0;
\r
2008 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2009 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2011 static f32 busytime_jitter1_max_sample = 0.0;
\r
2012 static f32 busytime_jitter1_min_sample = 0.0;
\r
2014 static f32 jitter1_max = 0.0;
\r
2015 static f32 jitter1_min = 0.0;
\r
2016 static f32 counter = 0.0;
\r
2017 if(busytime_jitter1 > jitter1_max)
\r
2018 jitter1_max = busytime_jitter1;
\r
2019 if(busytime_jitter1 < jitter1_min)
\r
2020 jitter1_min = busytime_jitter1;
\r
2022 if(counter > 0.0){
\r
2024 busytime_jitter1_max_sample = jitter1_max;
\r
2025 busytime_jitter1_min_sample = jitter1_min;
\r
2026 jitter1_max = 0.0;
\r
2027 jitter1_min = 0.0;
\r
2032 Debug info for client
\r
2035 static float counter = 0.0;
\r
2040 client.printDebugInfo(std::cout);
\r
2045 Input handler step()
\r
2047 g_input->step(dtime);
\r
2050 Player speed control
\r
2059 bool a_superspeed,
\r
2062 PlayerControl control(
\r
2063 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2064 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2065 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2066 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2067 g_input->isKeyDown(irr::KEY_SPACE),
\r
2068 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2072 client.setPlayerControl(control);
\r
2076 Process environment
\r
2080 //TimeTaker timer("client.step(dtime)");
\r
2081 client.step(dtime);
\r
2082 //client.step(dtime_avg1);
\r
2085 if(server != NULL)
\r
2087 //TimeTaker timer("server->step(dtime)");
\r
2088 server->step(dtime);
\r
2091 v3f player_position = client.getPlayerPosition();
\r
2093 //TimeTaker //timer2("//timer2");
\r
2096 Mouse and camera control
\r
2099 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2102 device->getCursorControl()->setVisible(false);
\r
2104 if(first_loop_after_window_activation){
\r
2105 //std::cout<<"window active, first loop"<<std::endl;
\r
2106 first_loop_after_window_activation = false;
\r
2109 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2110 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2111 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2112 camera_yaw -= dx*0.2;
\r
2113 camera_pitch += dy*0.2;
\r
2114 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2115 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2117 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2120 device->getCursorControl()->setVisible(true);
\r
2122 //std::cout<<"window inactive"<<std::endl;
\r
2123 first_loop_after_window_activation = true;
\r
2126 camera_yaw = wrapDegrees(camera_yaw);
\r
2127 camera_pitch = wrapDegrees(camera_pitch);
\r
2129 v3f camera_direction = v3f(0,0,1);
\r
2130 camera_direction.rotateYZBy(camera_pitch);
\r
2131 camera_direction.rotateXZBy(camera_yaw);
\r
2133 // This is at the height of the eyes of the current figure
\r
2134 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2135 // This is more like in minecraft
\r
2136 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2138 camera->setPosition(camera_position);
\r
2139 // *100.0 helps in large map coordinates
\r
2140 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2142 if(FIELD_OF_VIEW_TEST){
\r
2143 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2144 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2147 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2148 //TimeTaker timer("client.updateCamera");
\r
2149 client.updateCamera(camera_position, camera_direction);
\r
2153 //TimeTaker //timer3("//timer3");
\r
2156 Calculate what block is the crosshair pointing to
\r
2159 //u32 t1 = device->getTimer()->getRealTime();
\r
2161 //f32 d = 4; // max. distance
\r
2162 f32 d = 4; // max. distance
\r
2163 core::line3d<f32> shootline(camera_position,
\r
2164 camera_position + camera_direction * BS * (d+1));
\r
2166 MapBlockObject *selected_object = client.getSelectedObject
\r
2167 (d*BS, camera_position, shootline);
\r
2170 If it's pointing to a MapBlockObject
\r
2173 if(selected_object != NULL)
\r
2175 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2177 core::aabbox3d<f32> box_on_map
\r
2178 = selected_object->getSelectionBoxOnMap();
\r
2180 hilightboxes.push_back(box_on_map);
\r
2182 infotext = narrow_to_wide(selected_object->infoText());
\r
2184 if(g_input->getLeftClicked())
\r
2186 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2187 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2188 selected_object->getId(), g_selected_item);
\r
2190 else if(g_input->getRightClicked())
\r
2192 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2194 Check if we want to modify the object ourselves
\r
2196 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2198 dstream<<"Sign object right-clicked"<<std::endl;
\r
2200 if(random_input == false)
\r
2202 // Get a new text for it
\r
2204 TextDest *dest = new TextDestSign(
\r
2205 selected_object->getBlock()->getPos(),
\r
2206 selected_object->getId(),
\r
2209 SignObject *sign_object = (SignObject*)selected_object;
\r
2211 std::wstring wtext =
\r
2212 narrow_to_wide(sign_object->getText());
\r
2214 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2220 Otherwise pass the event to the server as-is
\r
2224 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2225 selected_object->getId(), g_selected_item);
\r
2229 else // selected_object == NULL
\r
2233 Find out which node we are pointing at
\r
2236 bool nodefound = false;
\r
2238 v3s16 neighbourpos;
\r
2239 core::aabbox3d<f32> nodefacebox;
\r
2240 f32 mindistance = BS * 1001;
\r
2242 v3s16 pos_i = floatToInt(player_position);
\r
2244 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2248 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2249 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2250 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2251 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2252 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2253 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2255 for(s16 y = ystart; y <= yend; y++)
\r
2256 for(s16 z = zstart; z <= zend; z++)
\r
2257 for(s16 x = xstart; x <= xend; x++)
\r
2262 n = client.getNode(v3s16(x,y,z));
\r
2263 if(content_pointable(n.d) == false)
\r
2266 catch(InvalidPositionException &e)
\r
2272 v3f npf = intToFloat(np);
\r
2277 v3s16(0,0,1), // back
\r
2278 v3s16(0,1,0), // top
\r
2279 v3s16(1,0,0), // right
\r
2280 v3s16(0,0,-1), // front
\r
2281 v3s16(0,-1,0), // bottom
\r
2282 v3s16(-1,0,0), // left
\r
2288 if(n.d == CONTENT_TORCH)
\r
2290 v3s16 dir = unpackDir(n.dir);
\r
2291 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2292 dir_f *= BS/2 - BS/6 - BS/20;
\r
2293 v3f cpf = npf + dir_f;
\r
2294 f32 distance = (cpf - camera_position).getLength();
\r
2296 core::aabbox3d<f32> box;
\r
2299 if(dir == v3s16(0,-1,0))
\r
2301 box = core::aabbox3d<f32>(
\r
2302 npf - v3f(BS/6, BS/2, BS/6),
\r
2303 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2307 else if(dir == v3s16(0,1,0))
\r
2309 box = core::aabbox3d<f32>(
\r
2310 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2311 npf + v3f(BS/6, BS/2, BS/6)
\r
2317 box = core::aabbox3d<f32>(
\r
2318 cpf - v3f(BS/6, BS/3, BS/6),
\r
2319 cpf + v3f(BS/6, BS/3, BS/6)
\r
2323 if(distance < mindistance)
\r
2325 if(box.intersectsWithLine(shootline))
\r
2329 neighbourpos = np;
\r
2330 mindistance = distance;
\r
2331 nodefacebox = box;
\r
2340 for(u16 i=0; i<6; i++)
\r
2342 v3f dir_f = v3f(dirs[i].X,
\r
2343 dirs[i].Y, dirs[i].Z);
\r
2344 v3f centerpoint = npf + dir_f * BS/2;
\r
2346 (centerpoint - camera_position).getLength();
\r
2348 if(distance < mindistance)
\r
2350 core::CMatrix4<f32> m;
\r
2351 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2353 // This is the back face
\r
2354 v3f corners[2] = {
\r
2355 v3f(BS/2, BS/2, BS/2),
\r
2356 v3f(-BS/2, -BS/2, BS/2+d)
\r
2359 for(u16 j=0; j<2; j++)
\r
2361 m.rotateVect(corners[j]);
\r
2362 corners[j] += npf;
\r
2365 core::aabbox3d<f32> facebox(corners[0]);
\r
2366 facebox.addInternalPoint(corners[1]);
\r
2368 if(facebox.intersectsWithLine(shootline))
\r
2372 neighbourpos = np + dirs[i];
\r
2373 mindistance = distance;
\r
2374 nodefacebox = facebox;
\r
2376 } // if distance < mindistance
\r
2378 } // regular block
\r
2381 static float nodig_delay_counter = 0.0;
\r
2385 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2387 static float dig_time = 0.0;
\r
2388 static u16 dig_index = 0;
\r
2390 hilightboxes.push_back(nodefacebox);
\r
2392 if(g_input->getLeftReleased())
\r
2394 client.clearTempMod(nodepos);
\r
2398 if(nodig_delay_counter > 0.0)
\r
2400 nodig_delay_counter -= dtime;
\r
2404 if(nodepos != nodepos_old)
\r
2406 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2407 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2409 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2411 client.clearTempMod(nodepos_old);
\r
2416 if(g_input->getLeftClicked() ||
\r
2417 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2419 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2420 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2422 if(g_input->getLeftClicked())
\r
2424 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2426 if(g_input->getLeftState())
\r
2428 MapNode n = client.getNode(nodepos);
\r
2430 // Get tool name. Default is "" = bare hands
\r
2431 std::string toolname = "";
\r
2432 InventoryList *mlist = local_inventory.getList("main");
\r
2435 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2436 if(item && (std::string)item->getName() == "ToolItem")
\r
2438 ToolItem *titem = (ToolItem*)item;
\r
2439 toolname = titem->getToolName();
\r
2443 // Get digging properties for material and tool
\r
2444 u8 material = n.d;
\r
2445 DiggingProperties prop =
\r
2446 getDiggingProperties(material, toolname);
\r
2448 float dig_time_complete = 0.0;
\r
2450 if(prop.diggable == false)
\r
2452 /*dstream<<"Material "<<(int)material
\r
2453 <<" not diggable with \""
\r
2454 <<toolname<<"\""<<std::endl;*/
\r
2455 // I guess nobody will wait for this long
\r
2456 dig_time_complete = 10000000.0;
\r
2460 dig_time_complete = prop.time;
\r
2463 if(dig_time_complete >= 0.001)
\r
2465 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2466 * dig_time/dig_time_complete);
\r
2468 // This is for torches
\r
2471 dig_index = CRACK_ANIMATION_LENGTH;
\r
2474 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2476 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2477 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2481 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2482 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2483 client.clearTempMod(nodepos);
\r
2484 client.removeNode(nodepos);
\r
2488 nodig_delay_counter = dig_time_complete
\r
2489 / (float)CRACK_ANIMATION_LENGTH;
\r
2491 // We don't want a corresponding delay to
\r
2492 // very time consuming nodes
\r
2493 if(nodig_delay_counter > 0.5)
\r
2495 nodig_delay_counter = 0.5;
\r
2497 // We want a slight delay to very little
\r
2498 // time consuming nodes
\r
2499 //float mindelay = 0.15;
\r
2500 float mindelay = 0.20;
\r
2501 if(nodig_delay_counter < mindelay)
\r
2503 nodig_delay_counter = mindelay;
\r
2507 dig_time += dtime;
\r
2511 if(g_input->getRightClicked())
\r
2513 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2514 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2517 nodepos_old = nodepos;
\r
2522 } // selected_object == NULL
\r
2524 g_input->resetLeftClicked();
\r
2525 g_input->resetRightClicked();
\r
2527 if(g_input->getLeftReleased())
\r
2529 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2531 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2533 if(g_input->getRightReleased())
\r
2535 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2539 g_input->resetLeftReleased();
\r
2540 g_input->resetRightReleased();
\r
2543 Calculate stuff for drawing
\r
2546 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2548 u32 daynight_ratio = client.getDayNightRatio();
\r
2549 /*video::SColor bgcolor = video::SColor(
\r
2551 skycolor.getRed() * daynight_ratio / 1000,
\r
2552 skycolor.getGreen() * daynight_ratio / 1000,
\r
2553 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2555 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2556 video::SColor bgcolor = video::SColor(
\r
2558 skycolor.getRed() * l / 255,
\r
2559 skycolor.getGreen() * l / 255,
\r
2560 skycolor.getBlue() * l / 255);
\r
2566 if(g_settings.getBool("enable_fog") == true)
\r
2568 f32 range = draw_control.wanted_range * BS;
\r
2569 if(draw_control.range_all)
\r
2570 range = 100000*BS;
\r
2574 video::EFT_FOG_LINEAR,
\r
2578 false, // pixel fog
\r
2579 false // range fog
\r
2585 Update gui stuff (0ms)
\r
2588 //TimeTaker guiupdatetimer("Gui updating");
\r
2591 wchar_t temptext[150];
\r
2593 static float drawtime_avg = 0;
\r
2594 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2595 static float beginscenetime_avg = 0;
\r
2596 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2597 static float scenetime_avg = 0;
\r
2598 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2599 static float endscenetime_avg = 0;
\r
2600 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2602 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2604 L", R: range_all=%i"
\r
2606 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2608 draw_control.range_all,
\r
2610 beginscenetime_avg,
\r
2615 guitext->setText(temptext);
\r
2619 wchar_t temptext[150];
\r
2620 swprintf(temptext, 150,
\r
2621 L"(% .1f, % .1f, % .1f)"
\r
2622 L" (% .3f < btime_jitter < % .3f"
\r
2623 L", dtime_jitter = % .1f %%"
\r
2624 L", v_range = %.1f)",
\r
2625 player_position.X/BS,
\r
2626 player_position.Y/BS,
\r
2627 player_position.Z/BS,
\r
2628 busytime_jitter1_min_sample,
\r
2629 busytime_jitter1_max_sample,
\r
2630 dtime_jitter1_max_fraction * 100.0,
\r
2631 draw_control.wanted_range
\r
2634 guitext2->setText(temptext);
\r
2638 guitext_info->setText(infotext.c_str());
\r
2642 Get chat messages from client
\r
2645 // Get new messages
\r
2646 std::wstring message;
\r
2647 while(client.getChatMessage(message))
\r
2649 chat_lines.push_back(ChatLine(message));
\r
2650 /*if(chat_lines.size() > 6)
\r
2652 core::list<ChatLine>::Iterator
\r
2653 i = chat_lines.begin();
\r
2654 chat_lines.erase(i);
\r
2657 // Append them to form the whole static text and throw
\r
2658 // it to the gui element
\r
2659 std::wstring whole;
\r
2660 // This will correspond to the line number counted from
\r
2661 // top to bottom, from size-1 to 0
\r
2662 s16 line_number = chat_lines.size();
\r
2663 // Count of messages to be removed from the top
\r
2664 u16 to_be_removed_count = 0;
\r
2665 for(core::list<ChatLine>::Iterator
\r
2666 i = chat_lines.begin();
\r
2667 i != chat_lines.end(); i++)
\r
2669 // After this, line number is valid for this loop
\r
2672 (*i).age += dtime;
\r
2674 This results in a maximum age of 60*6 to the
\r
2675 lowermost line and a maximum of 6 lines
\r
2677 float allowed_age = (6-line_number) * 60.0;
\r
2679 if((*i).age > allowed_age)
\r
2681 to_be_removed_count++;
\r
2684 whole += (*i).text + L'\n';
\r
2686 for(u16 i=0; i<to_be_removed_count; i++)
\r
2688 core::list<ChatLine>::Iterator
\r
2689 it = chat_lines.begin();
\r
2690 chat_lines.erase(it);
\r
2692 guitext_chat->setText(whole.c_str());
\r
2693 // Update gui element size and position
\r
2694 core::rect<s32> rect(
\r
2696 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2697 screensize.X - 10,
\r
2700 guitext_chat->setRelativePosition(rect);
\r
2702 if(chat_lines.size() == 0)
\r
2703 guitext_chat->setVisible(false);
\r
2705 guitext_chat->setVisible(true);
\r
2712 static u16 old_selected_item = 65535;
\r
2713 if(client.getLocalInventoryUpdated()
\r
2714 || g_selected_item != old_selected_item)
\r
2716 old_selected_item = g_selected_item;
\r
2717 //std::cout<<"Updating local inventory"<<std::endl;
\r
2718 client.getLocalInventory(local_inventory);
\r
2719 quick_inventory->setSelection(g_selected_item);
\r
2720 quick_inventory->update();
\r
2724 Send actions returned by the inventory menu
\r
2726 while(inventory_action_queue.size() != 0)
\r
2728 InventoryAction *a = inventory_action_queue.pop_front();
\r
2730 client.sendInventoryAction(a);
\r
2739 TimeTaker drawtimer("Drawing");
\r
2743 TimeTaker timer("beginScene");
\r
2744 driver->beginScene(true, true, bgcolor);
\r
2745 //driver->beginScene(false, true, bgcolor);
\r
2746 beginscenetime = timer.stop(true);
\r
2751 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2754 TimeTaker timer("smgr");
\r
2756 scenetime = timer.stop(true);
\r
2760 //TimeTaker timer9("auxiliary drawings");
\r
2764 //TimeTaker //timer10("//timer10");
\r
2766 video::SMaterial m;
\r
2767 //m.Thickness = 10;
\r
2769 m.Lighting = false;
\r
2770 driver->setMaterial(m);
\r
2772 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2774 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2775 i != hilightboxes.end(); i++)
\r
2777 /*std::cout<<"hilightbox min="
\r
2778 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2780 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2782 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2788 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2789 displaycenter + core::vector2d<s32>(10,0),
\r
2790 video::SColor(255,255,255,255));
\r
2791 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2792 displaycenter + core::vector2d<s32>(0,10),
\r
2793 video::SColor(255,255,255,255));
\r
2798 //TimeTaker //timer11("//timer11");
\r
2804 guienv->drawAll();
\r
2808 TimeTaker timer("endScene");
\r
2809 driver->endScene();
\r
2810 endscenetime = timer.stop(true);
\r
2813 drawtime = drawtimer.stop(true);
\r
2819 static s16 lastFPS = 0;
\r
2820 //u16 fps = driver->getFPS();
\r
2821 u16 fps = (1.0/dtime_avg1);
\r
2823 if (lastFPS != fps)
\r
2825 core::stringw str = L"Minetest [";
\r
2826 str += driver->getName();
\r
2830 device->setWindowCaption(str.c_str());
\r
2836 device->yield();*/
\r
2839 delete quick_inventory;
\r
2841 } // client and server are deleted at this point
\r
2844 catch(con::PeerNotFoundException &e)
\r
2846 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2847 error_message = L"Connection timed out.";
\r
2850 } // Menu-game loop
\r
2855 In the end, delete the Irrlicht device.
\r
2860 Update configuration file
\r
2862 /*if(configpath != "")
\r
2864 g_settings.updateConfigFile(configpath.c_str());
\r
2867 END_DEBUG_EXCEPTION_HANDLER
\r
2869 debugstreams_deinit();
\r