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 TODO: 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 TODO: Change AttributeList to split the area into smaller sections so
\r
251 that searching won't be as heavy.
\r
252 TODO: Change AttributeList to be 2D, as it would be too slow to search
\r
253 in 3D fields anyway.
\r
255 FIXME: The new pre-sunlight-propagation code messes up with initial
\r
256 water lighting. Does it any more?
\r
258 TODO: Remove HMParams
\r
260 TODO: Flowing water to actually contain flow direction information
\r
262 TODO: Faster lighting using VoxelManipulator
\r
267 ======================================================================
\r
272 Setting this to 1 enables a special camera mode that forces
\r
273 the renderers to think that the camera statically points from
\r
274 the starting place to a static direction.
\r
276 This allows one to move around with the player and see what
\r
277 is actually drawn behind solid things and behind the player.
\r
279 #define FIELD_OF_VIEW_TEST 0
\r
283 #pragma message ("Disabling unit tests")
\r
285 #warning "Disabling unit tests"
\r
287 // Disable unit tests
\r
288 #define ENABLE_TESTS 0
\r
290 // Enable unit tests
\r
291 #define ENABLE_TESTS 1
\r
295 #pragma comment(lib, "Irrlicht.lib")
\r
296 //#pragma comment(lib, "jthread.lib")
\r
297 #pragma comment(lib, "zlibwapi.lib")
\r
298 // This would get rid of the console window
\r
299 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
302 #include <iostream>
\r
304 #include <jmutexautolock.h>
\r
305 #include <locale.h>
\r
306 #include "common_irrlicht.h"
\r
309 #include "player.h"
\r
312 #include "environment.h"
\r
313 #include "server.h"
\r
314 #include "client.h"
\r
315 #include "serialization.h"
\r
316 #include "constants.h"
\r
317 #include "strfnd.h"
\r
318 #include "porting.h"
\r
319 #include "irrlichtwrapper.h"
\r
320 #include "gettime.h"
\r
321 #include "porting.h"
\r
322 #include "guiPauseMenu.h"
\r
323 #include "guiInventoryMenu.h"
\r
324 #include "guiTextInputMenu.h"
\r
325 #include "materials.h"
\r
326 #include "guiMessageMenu.h"
\r
327 #include "filesys.h"
\r
328 #include "config.h"
\r
329 #include "guiMainMenu.h"
\r
331 IrrlichtWrapper *g_irrlicht;
\r
333 MapDrawControl draw_control;
\r
337 These are loaded from the config file.
\r
340 Settings g_settings;
\r
342 extern void set_default_settings();
\r
348 IrrlichtDevice *g_device = NULL;
\r
349 Client *g_client = NULL;
\r
355 gui::IGUIEnvironment* guienv = NULL;
\r
356 gui::IGUIStaticText *guiroot = NULL;
\r
358 class MainMenuManager : public IMenuManager
\r
361 virtual void createdMenu(GUIModalMenu *menu)
\r
363 for(core::list<GUIModalMenu*>::Iterator
\r
364 i = m_stack.begin();
\r
365 i != m_stack.end(); i++)
\r
367 assert(*i != menu);
\r
370 if(m_stack.size() != 0)
\r
371 (*m_stack.getLast())->setVisible(false);
\r
372 m_stack.push_back(menu);
\r
375 virtual void deletingMenu(GUIModalMenu *menu)
\r
377 // Remove all entries if there are duplicates
\r
378 bool removed_entry;
\r
380 removed_entry = false;
\r
381 for(core::list<GUIModalMenu*>::Iterator
\r
382 i = m_stack.begin();
\r
383 i != m_stack.end(); i++)
\r
388 removed_entry = true;
\r
392 }while(removed_entry);
\r
394 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
395 assert(*i == menu);
\r
396 m_stack.erase(i);*/
\r
398 if(m_stack.size() != 0)
\r
399 (*m_stack.getLast())->setVisible(true);
\r
404 return m_stack.size();
\r
407 core::list<GUIModalMenu*> m_stack;
\r
410 MainMenuManager g_menumgr;
\r
412 bool noMenuActive()
\r
414 return (g_menumgr.menuCount() == 0);
\r
417 bool g_disconnect_requested = false;
\r
419 class MainGameCallback : public IGameCallback
\r
422 virtual void exitToOS()
\r
424 g_device->closeDevice();
\r
427 virtual void disconnect()
\r
429 g_disconnect_requested = true;
\r
433 MainGameCallback g_gamecallback;
\r
435 // Inventory actions from the menu are buffered here before sending
\r
436 Queue<InventoryAction*> inventory_action_queue;
\r
437 // This is a copy of the inventory that the client's environment has
\r
438 Inventory local_inventory;
\r
440 u16 g_selected_item = 0;
\r
447 std::ostream *dout_con_ptr = &dummyout;
\r
448 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
449 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
450 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
451 //std::ostream *dout_con_ptr = &dstream;
\r
452 //std::ostream *derr_con_ptr = &dstream;
\r
455 std::ostream *dout_server_ptr = &dstream;
\r
456 std::ostream *derr_server_ptr = &dstream;
\r
459 std::ostream *dout_client_ptr = &dstream;
\r
460 std::ostream *derr_client_ptr = &dstream;
\r
463 gettime.h implementation
\r
469 Use irrlicht because it is more precise than porting.h's
\r
472 if(g_irrlicht == NULL)
\r
474 return g_irrlicht->getTime();
\r
481 struct TextDestSign : public TextDest
\r
483 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
485 m_blockpos = blockpos;
\r
489 void gotText(std::wstring text)
\r
491 std::string ntext = wide_to_narrow(text);
\r
492 dstream<<"Changing text of a sign object: "
\r
493 <<ntext<<std::endl;
\r
494 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
502 struct TextDestChat : public TextDest
\r
504 TextDestChat(Client *client)
\r
508 void gotText(std::wstring text)
\r
510 m_client->sendChatMessage(text);
\r
511 m_client->addChatMessage(text);
\r
517 class MyEventReceiver : public IEventReceiver
\r
520 // This is the one method that we have to implement
\r
521 virtual bool OnEvent(const SEvent& event)
\r
524 React to nothing here if a menu is active
\r
526 if(noMenuActive() == false)
\r
532 // Remember whether each key is down or up
\r
533 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
535 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
537 if(event.KeyInput.PressedDown)
\r
539 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
545 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
547 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
549 dstream<<DTIME<<"MyEventReceiver: "
\r
550 <<"Launching pause menu"<<std::endl;
\r
551 // It will delete itself by itself
\r
552 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
553 &g_menumgr))->drop();
\r
556 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
558 dstream<<DTIME<<"MyEventReceiver: "
\r
559 <<"Launching inventory"<<std::endl;
\r
560 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
561 &local_inventory, &inventory_action_queue,
\r
562 &g_menumgr))->drop();
\r
565 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
567 TextDest *dest = new TextDestChat(g_client);
\r
569 (new GUITextInputMenu(guienv, guiroot, -1,
\r
575 // Material selection
\r
576 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
578 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
581 g_selected_item = 0;
\r
582 dstream<<DTIME<<"Selected item: "
\r
583 <<g_selected_item<<std::endl;
\r
586 // Viewing range selection
\r
587 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
589 if(draw_control.range_all)
\r
591 draw_control.range_all = false;
\r
592 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
596 draw_control.range_all = true;
\r
597 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
601 // Print debug stacks
\r
602 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
604 dstream<<"-----------------------------------------"
\r
606 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
607 dstream<<"-----------------------------------------"
\r
609 debug_stacks_print();
\r
614 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
616 if(noMenuActive() == false)
\r
618 left_active = false;
\r
619 middle_active = false;
\r
620 right_active = false;
\r
624 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
625 left_active = event.MouseInput.isLeftPressed();
\r
626 middle_active = event.MouseInput.isMiddlePressed();
\r
627 right_active = event.MouseInput.isRightPressed();
\r
629 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
631 leftclicked = true;
\r
633 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
635 rightclicked = true;
\r
637 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
639 leftreleased = true;
\r
641 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
643 rightreleased = true;
\r
645 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
647 /*dstream<<"event.MouseInput.Wheel="
\r
648 <<event.MouseInput.Wheel<<std::endl;*/
\r
649 if(event.MouseInput.Wheel < 0)
\r
651 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
654 g_selected_item = 0;
\r
656 else if(event.MouseInput.Wheel > 0)
\r
658 if(g_selected_item > 0)
\r
661 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
670 // This is used to check whether a key is being held down
\r
671 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
673 return keyIsDown[keyCode];
\r
678 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
679 keyIsDown[i] = false;
\r
681 leftclicked = false;
\r
682 rightclicked = false;
\r
683 leftreleased = false;
\r
684 rightreleased = false;
\r
686 left_active = false;
\r
687 middle_active = false;
\r
688 right_active = false;
\r
699 bool rightreleased;
\r
702 bool middle_active;
\r
706 // We use this array to store the current state of each key
\r
707 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
710 IrrlichtDevice *m_device;
\r
719 virtual ~InputHandler()
\r
723 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
725 virtual v2s32 getMousePos() = 0;
\r
726 virtual void setMousePos(s32 x, s32 y) = 0;
\r
728 virtual bool getLeftState() = 0;
\r
729 virtual bool getRightState() = 0;
\r
731 virtual bool getLeftClicked() = 0;
\r
732 virtual bool getRightClicked() = 0;
\r
733 virtual void resetLeftClicked() = 0;
\r
734 virtual void resetRightClicked() = 0;
\r
736 virtual bool getLeftReleased() = 0;
\r
737 virtual bool getRightReleased() = 0;
\r
738 virtual void resetLeftReleased() = 0;
\r
739 virtual void resetRightReleased() = 0;
\r
741 virtual void step(float dtime) {};
\r
743 virtual void clear() {};
\r
746 InputHandler *g_input = NULL;
\r
748 class RealInputHandler : public InputHandler
\r
751 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
753 m_receiver(receiver)
\r
756 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
758 return m_receiver->IsKeyDown(keyCode);
\r
760 virtual v2s32 getMousePos()
\r
762 return m_device->getCursorControl()->getPosition();
\r
764 virtual void setMousePos(s32 x, s32 y)
\r
766 m_device->getCursorControl()->setPosition(x, y);
\r
769 virtual bool getLeftState()
\r
771 return m_receiver->left_active;
\r
773 virtual bool getRightState()
\r
775 return m_receiver->right_active;
\r
778 virtual bool getLeftClicked()
\r
780 return m_receiver->leftclicked;
\r
782 virtual bool getRightClicked()
\r
784 return m_receiver->rightclicked;
\r
786 virtual void resetLeftClicked()
\r
788 m_receiver->leftclicked = false;
\r
790 virtual void resetRightClicked()
\r
792 m_receiver->rightclicked = false;
\r
795 virtual bool getLeftReleased()
\r
797 return m_receiver->leftreleased;
\r
799 virtual bool getRightReleased()
\r
801 return m_receiver->rightreleased;
\r
803 virtual void resetLeftReleased()
\r
805 m_receiver->leftreleased = false;
\r
807 virtual void resetRightReleased()
\r
809 m_receiver->rightreleased = false;
\r
814 resetRightClicked();
\r
815 resetLeftClicked();
\r
818 IrrlichtDevice *m_device;
\r
819 MyEventReceiver *m_receiver;
\r
822 class RandomInputHandler : public InputHandler
\r
825 RandomInputHandler()
\r
827 leftclicked = false;
\r
828 rightclicked = false;
\r
829 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
830 keydown[i] = false;
\r
832 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
834 return keydown[keyCode];
\r
836 virtual v2s32 getMousePos()
\r
840 virtual void setMousePos(s32 x, s32 y)
\r
842 mousepos = v2s32(x,y);
\r
845 virtual bool getLeftState()
\r
849 virtual bool getRightState()
\r
854 virtual bool getLeftClicked()
\r
856 return leftclicked;
\r
858 virtual bool getRightClicked()
\r
860 return rightclicked;
\r
862 virtual void resetLeftClicked()
\r
864 leftclicked = false;
\r
866 virtual void resetRightClicked()
\r
868 rightclicked = false;
\r
871 virtual bool getLeftReleased()
\r
875 virtual bool getRightReleased()
\r
879 virtual void resetLeftReleased()
\r
882 virtual void resetRightReleased()
\r
886 virtual void step(float dtime)
\r
889 static float counter1 = 0;
\r
893 counter1 = 0.1*Rand(1,10);
\r
894 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
895 g_selected_material++;
\r
897 g_selected_material = 0;*/
\r
898 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
901 g_selected_item = 0;
\r
905 static float counter1 = 0;
\r
909 counter1 = 0.1*Rand(1, 40);
\r
910 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
914 static float counter1 = 0;
\r
918 counter1 = 0.1*Rand(1, 40);
\r
919 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
923 static float counter1 = 0;
\r
927 counter1 = 0.1*Rand(1, 40);
\r
928 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
932 static float counter1 = 0;
\r
936 counter1 = 0.1*Rand(1, 40);
\r
937 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
941 static float counter1 = 0;
\r
945 counter1 = 0.1*Rand(1, 20);
\r
946 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
950 static float counter1 = 0;
\r
954 counter1 = 0.1*Rand(1, 30);
\r
955 leftclicked = true;
\r
959 static float counter1 = 0;
\r
963 counter1 = 0.1*Rand(1, 20);
\r
964 rightclicked = true;
\r
967 mousepos += mousespeed;
\r
970 s32 Rand(s32 min, s32 max)
\r
972 return (myrand()%(max-min+1))+min;
\r
975 bool keydown[KEY_KEY_CODES_COUNT];
\r
982 void updateViewingRange(f32 frametime_in, Client *client)
\r
984 if(draw_control.range_all == true)
\r
987 static f32 added_frametime = 0;
\r
988 static s16 added_frames = 0;
\r
990 added_frametime += frametime_in;
\r
993 // Actually this counter kind of sucks because frametime is busytime
\r
994 static f32 counter = 0;
\r
995 counter -= frametime_in;
\r
1001 /*dstream<<__FUNCTION_NAME
\r
1002 <<": Collected "<<added_frames<<" frames, total of "
\r
1003 <<added_frametime<<"s."<<std::endl;*/
\r
1005 /*dstream<<"draw_control.blocks_drawn="
\r
1006 <<draw_control.blocks_drawn
\r
1007 <<", draw_control.blocks_would_have_drawn="
\r
1008 <<draw_control.blocks_would_have_drawn
\r
1011 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1012 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1014 draw_control.wanted_min_range = range_min;
\r
1015 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1017 float block_draw_ratio = 1.0;
\r
1018 if(draw_control.blocks_would_have_drawn != 0)
\r
1020 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1021 / (float)draw_control.blocks_would_have_drawn;
\r
1024 // Calculate the average frametime in the case that all wanted
\r
1025 // blocks had been drawn
\r
1026 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1028 added_frametime = 0.0;
\r
1031 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1032 float wanted_frametime = 1.0 / wanted_fps;
\r
1034 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1035 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1037 // If needed frametime change is very small, just return
\r
1038 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1040 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1044 float range = draw_control.wanted_range;
\r
1045 float new_range = range;
\r
1047 static s16 range_old = 0;
\r
1048 static f32 frametime_old = 0;
\r
1050 float d_range = range - range_old;
\r
1051 f32 d_frametime = frametime - frametime_old;
\r
1052 // A sane default of 30ms per 50 nodes of range
\r
1053 static f32 time_per_range = 30. / 50;
\r
1056 time_per_range = d_frametime / d_range;
\r
1059 // The minimum allowed calculated frametime-range derivative:
\r
1060 // Practically this sets the maximum speed of changing the range.
\r
1061 // The lower this value, the higher the maximum changing speed.
\r
1062 // A low value here results in wobbly range (0.001)
\r
1063 // A high value here results in slow changing range (0.0025)
\r
1064 // SUGG: This could be dynamically adjusted so that when
\r
1065 // the camera is turning, this is lower
\r
1066 //float min_time_per_range = 0.0015;
\r
1067 float min_time_per_range = 0.0010;
\r
1068 //float min_time_per_range = 0.05 / range;
\r
1069 if(time_per_range < min_time_per_range)
\r
1071 time_per_range = min_time_per_range;
\r
1072 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1076 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1079 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1080 // Dampen the change a bit to kill oscillations
\r
1081 //wanted_range_change *= 0.9;
\r
1082 //wanted_range_change *= 0.75;
\r
1083 wanted_range_change *= 0.5;
\r
1084 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1086 // If needed range change is very small, just return
\r
1087 if(fabs(wanted_range_change) < 0.001)
\r
1089 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1093 new_range += wanted_range_change;
\r
1094 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1096 //float new_range_unclamped = new_range;
\r
1097 if(new_range < range_min)
\r
1098 new_range = range_min;
\r
1099 if(new_range > range_max)
\r
1100 new_range = range_max;
\r
1102 /*if(new_range != new_range_unclamped)
\r
1103 dstream<<", clamped to "<<new_range<<std::endl;
\r
1105 dstream<<std::endl;*/
\r
1107 draw_control.wanted_range = new_range;
\r
1109 range_old = new_range;
\r
1110 frametime_old = frametime;
\r
1113 class GUIQuickInventory : public IEventReceiver
\r
1116 GUIQuickInventory(
\r
1117 gui::IGUIEnvironment* env,
\r
1118 gui::IGUIElement* parent,
\r
1121 Inventory *inventory):
\r
1122 m_itemcount(itemcount),
\r
1123 m_inventory(inventory)
\r
1125 core::rect<s32> imgsize(0,0,48,48);
\r
1126 core::rect<s32> textsize(0,0,48,16);
\r
1127 v2s32 spacing(0, 64);
\r
1128 for(s32 i=0; i<m_itemcount; i++)
\r
1130 m_images.push_back(env->addImage(
\r
1131 imgsize + pos + spacing*i
\r
1133 m_images[i]->setScaleImage(true);
\r
1134 m_texts.push_back(env->addStaticText(
\r
1136 textsize + pos + spacing*i,
\r
1139 m_texts[i]->setBackgroundColor(
\r
1140 video::SColor(128,0,0,0));
\r
1141 m_texts[i]->setTextAlignment(
\r
1142 gui::EGUIA_CENTER,
\r
1143 gui::EGUIA_UPPERLEFT);
\r
1147 ~GUIQuickInventory()
\r
1149 for(u32 i=0; i<m_texts.size(); i++)
\r
1151 m_texts[i]->remove();
\r
1153 for(u32 i=0; i<m_images.size(); i++)
\r
1155 m_images[i]->remove();
\r
1159 virtual bool OnEvent(const SEvent& event)
\r
1164 void setSelection(s32 i)
\r
1173 start = m_selection - m_itemcount / 2;
\r
1175 InventoryList *mainlist = m_inventory->getList("main");
\r
1177 for(s32 i=0; i<m_itemcount; i++)
\r
1179 s32 j = i + start;
\r
1181 if(j > (s32)mainlist->getSize() - 1)
\r
1182 j -= mainlist->getSize();
\r
1184 j += mainlist->getSize();
\r
1186 InventoryItem *item = mainlist->getItem(j);
\r
1190 m_images[i]->setImage(NULL);
\r
1193 if(m_selection == j)
\r
1194 swprintf(t, 10, L"<-");
\r
1196 swprintf(t, 10, L"");
\r
1197 m_texts[i]->setText(t);
\r
1199 // The next ifs will segfault with a NULL pointer
\r
1204 m_images[i]->setImage(item->getImage());
\r
1207 if(m_selection == j)
\r
1208 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1210 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1211 m_texts[i]->setText(t);
\r
1217 core::array<gui::IGUIStaticText*> m_texts;
\r
1218 core::array<gui::IGUIImage*> m_images;
\r
1219 Inventory *m_inventory;
\r
1230 ChatLine(const std::wstring &a_text):
\r
1236 std::wstring text;
\r
1239 int main(int argc, char *argv[])
\r
1242 Low-level initialization
\r
1245 bool disable_stderr = false;
\r
1247 disable_stderr = true;
\r
1250 // Initialize debug streams
\r
1251 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1252 // Initialize debug stacks
\r
1253 debug_stacks_init();
\r
1255 DSTACK(__FUNCTION_NAME);
\r
1257 porting::initializePaths();
\r
1258 // Create user data directory
\r
1259 fs::CreateDir(porting::path_userdata);
\r
1261 initializeMaterialProperties();
\r
1263 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1265 // Print startup message
\r
1266 dstream<<DTIME<<"minetest-c55"
\r
1267 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1268 <<", "<<BUILD_INFO
\r
1272 Parse command line
\r
1275 // List all allowed options
\r
1276 core::map<std::string, ValueSpec> allowed_options;
\r
1277 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1278 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1279 "Run server directly"));
\r
1280 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1281 "Load configuration from specified file"));
\r
1282 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1283 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1284 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1285 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1286 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1287 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1289 Settings cmd_args;
\r
1291 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1293 if(ret == false || cmd_args.getFlag("help"))
\r
1295 dstream<<"Allowed options:"<<std::endl;
\r
1296 for(core::map<std::string, ValueSpec>::Iterator
\r
1297 i = allowed_options.getIterator();
\r
1298 i.atEnd() == false; i++)
\r
1300 dstream<<" --"<<i.getNode()->getKey();
\r
1301 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1306 dstream<<" <value>";
\r
1308 dstream<<std::endl;
\r
1310 if(i.getNode()->getValue().help != NULL)
\r
1312 dstream<<" "<<i.getNode()->getValue().help
\r
1317 return cmd_args.getFlag("help") ? 0 : 1;
\r
1322 Basic initialization
\r
1325 // Initialize default settings
\r
1326 set_default_settings();
\r
1328 // Set locale. This is for forcing '.' as the decimal point.
\r
1329 std::locale::global(std::locale("C"));
\r
1330 // This enables printing all characters in bitmap font
\r
1331 setlocale(LC_CTYPE, "en_US");
\r
1333 // Initialize sockets
\r
1335 atexit(sockets_cleanup);
\r
1345 // Path of configuration file in use
\r
1346 std::string configpath = "";
\r
1348 if(cmd_args.exists("config"))
\r
1350 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1353 dstream<<"Could not read configuration from \""
\r
1354 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1357 configpath = cmd_args.get("config");
\r
1361 core::array<std::string> filenames;
\r
1362 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1363 #ifdef RUN_IN_PLACE
\r
1364 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1367 for(u32 i=0; i<filenames.size(); i++)
\r
1369 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1372 configpath = filenames[i];
\r
1378 // Initialize random seed
\r
1385 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1386 || cmd_args.getFlag("enable-unittests") == true)
\r
1391 // Read map parameters from settings
\r
1393 HMParams hm_params;
\r
1394 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1395 hm_params.randmax = g_settings.get("height_randmax");
\r
1396 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1397 hm_params.base = g_settings.get("height_base");*/
\r
1399 MapParams map_params;
\r
1400 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1401 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1409 if(cmd_args.exists("port"))
\r
1410 port = cmd_args.getU16("port");
\r
1411 else if(cmd_args.exists("port"))
\r
1412 port = g_settings.getU16("port");
\r
1415 std::string map_dir = porting::path_userdata+"/map";
\r
1416 if(cmd_args.exists("map-dir"))
\r
1417 map_dir = cmd_args.get("map-dir");
\r
1418 else if(g_settings.exists("map-dir"))
\r
1419 map_dir = g_settings.get("map-dir");
\r
1421 // Run dedicated server if asked to
\r
1422 if(cmd_args.getFlag("server"))
\r
1424 DSTACK("Dedicated server branch");
\r
1427 Server server(map_dir.c_str(), hm_params, map_params);
\r
1428 server.start(port);
\r
1431 dedicated_server_loop(server);
\r
1440 // Address to connect to
\r
1441 std::string address = "";
\r
1443 if(cmd_args.exists("address"))
\r
1445 address = cmd_args.get("address");
\r
1449 address = g_settings.get("address");
\r
1452 std::string playername = g_settings.get("name");
\r
1455 Resolution selection
\r
1458 bool fullscreen = false;
\r
1459 u16 screenW = g_settings.getU16("screenW");
\r
1460 u16 screenH = g_settings.getU16("screenH");
\r
1464 MyEventReceiver receiver;
\r
1466 video::E_DRIVER_TYPE driverType;
\r
1469 //driverType = video::EDT_DIRECT3D9;
\r
1470 driverType = video::EDT_OPENGL;
\r
1472 driverType = video::EDT_OPENGL;
\r
1473 //driverType = video::EDT_BURNINGSVIDEO;
\r
1476 // create device and exit if creation failed
\r
1478 IrrlichtDevice *device;
\r
1479 device = createDevice(driverType,
\r
1480 core::dimension2d<u32>(screenW, screenH),
\r
1481 16, fullscreen, false, false, &receiver);
\r
1484 return 1; // could not create selected driver.
\r
1486 g_device = device;
\r
1487 g_irrlicht = new IrrlichtWrapper(device);
\r
1489 //g_device = device;
\r
1491 device->setResizable(true);
\r
1493 bool random_input = g_settings.getBool("random_input")
\r
1494 || cmd_args.getFlag("random-input");
\r
1496 g_input = new RandomInputHandler();
\r
1498 g_input = new RealInputHandler(device, &receiver);
\r
1501 Continue initialization
\r
1504 video::IVideoDriver* driver = device->getVideoDriver();
\r
1507 This changes the minimum allowed number of vertices in a VBO
\r
1509 //driver->setMinHardwareBufferVertexCount(50);
\r
1511 scene::ISceneManager* smgr = device->getSceneManager();
\r
1513 guienv = device->getGUIEnvironment();
\r
1514 gui::IGUISkin* skin = guienv->getSkin();
\r
1515 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1517 skin->setFont(font);
\r
1519 dstream<<"WARNING: Font file was not found."
\r
1520 " Using default font."<<std::endl;
\r
1521 // If font was not found, this will get us one
\r
1522 font = skin->getFont();
\r
1525 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1526 dstream<<"text_height="<<text_height<<std::endl;
\r
1528 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1529 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1530 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1531 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1532 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1533 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1536 Preload some textures
\r
1539 init_content_inventory_texture_paths();
\r
1540 init_tile_textures();
\r
1547 We need some kind of a root node to be able to add
\r
1548 custom gui elements directly on the screen.
\r
1549 Otherwise they won't be automatically drawn.
\r
1551 guiroot = guienv->addStaticText(L"",
\r
1552 core::rect<s32>(0, 0, 10000, 10000));
\r
1554 // First line of debug text
\r
1555 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1557 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1559 // Second line of debug text
\r
1560 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1562 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1565 // At the middle of the screen
\r
1566 // Object infos are shown in this
\r
1567 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1569 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1573 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1575 core::rect<s32>(0,0,0,0),
\r
1577 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1578 core::list<ChatLine> chat_lines;
\r
1581 If an error occurs, this is set to something and the
\r
1582 menu-game loop is restarted. It is then displayed before
\r
1585 std::wstring error_message = L"";
\r
1590 while(g_device->run())
\r
1593 // This is used for catching disconnects
\r
1598 Out-of-game menu loop
\r
1601 // Wait for proper parameters
\r
1604 // Cursor can be non-visible when coming from the game
\r
1605 device->getCursorControl()->setVisible(true);
\r
1606 // Some stuff are left to scene manager when coming from the game
\r
1607 // (map at least?)
\r
1609 // Reset or hide the debug gui texts
\r
1610 guitext->setText(L"Minetest-c55");
\r
1611 guitext2->setVisible(false);
\r
1612 guitext_info->setVisible(false);
\r
1613 guitext_chat->setVisible(false);
\r
1615 // Initialize menu data
\r
1616 MainMenuData menudata;
\r
1617 menudata.address = narrow_to_wide(address);
\r
1618 menudata.name = narrow_to_wide(playername);
\r
1619 menudata.port = narrow_to_wide(itos(port));
\r
1620 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1622 GUIMainMenu *menu =
\r
1623 new GUIMainMenu(guienv, guiroot, -1,
\r
1624 &g_menumgr, &menudata, &g_gamecallback);
\r
1625 menu->allowFocusRemoval(true);
\r
1627 if(error_message != L"")
\r
1629 GUIMessageMenu *menu2 =
\r
1630 new GUIMessageMenu(guienv, guiroot, -1,
\r
1631 &g_menumgr, error_message.c_str());
\r
1633 error_message = L"";
\r
1636 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1638 dstream<<"Created main menu"<<std::endl;
\r
1640 while(g_device->run())
\r
1642 // Run global IrrlichtWrapper's main thread processing stuff
\r
1643 g_irrlicht->Run();
\r
1645 if(menu->getStatus() == true)
\r
1648 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1649 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1650 guienv->drawAll();
\r
1651 driver->endScene();
\r
1654 // Break out of menu-game loop to shut down cleanly
\r
1655 if(g_device->run() == false)
\r
1658 dstream<<"Dropping main menu"<<std::endl;
\r
1662 playername = wide_to_narrow(menudata.name);
\r
1663 address = wide_to_narrow(menudata.address);
\r
1664 port = stoi(wide_to_narrow(menudata.port));
\r
1665 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1667 // Check for valid parameters, restart menu if invalid.
\r
1668 if(playername == "")
\r
1670 error_message = L"Name required.";
\r
1675 g_settings.set("name", playername);
\r
1676 g_settings.set("address", address);
\r
1677 g_settings.set("port", itos(port));
\r
1678 // Update configuration file
\r
1679 if(configpath != "")
\r
1680 g_settings.updateConfigFile(configpath.c_str());
\r
1682 // Continue to game
\r
1686 // Break out of menu-game loop to shut down cleanly
\r
1687 if(g_device->run() == false)
\r
1691 Make a scope here so that the client and the server and other
\r
1692 stuff gets removed when disconnected or the irrlicht device
\r
1698 Draw "Loading" screen
\r
1700 const wchar_t *text = L"Loading and connecting...";
\r
1701 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1702 core::vector2d<s32> textsize(300, text_height);
\r
1703 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1705 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1706 text, textrect, false, false);
\r
1707 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1709 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1710 guienv->drawAll();
\r
1711 driver->endScene();
\r
1713 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1717 SharedPtr will delete it when it goes out of scope.
\r
1719 SharedPtr<Server> server;
\r
1720 if(address == ""){
\r
1721 server = new Server(map_dir, hm_params, map_params);
\r
1722 server->start(port);
\r
1729 Client client(device, playername.c_str(), draw_control);
\r
1731 g_client = &client;
\r
1733 Address connect_address(0,0,0,0, port);
\r
1736 connect_address.Resolve("localhost");
\r
1738 connect_address.Resolve(address.c_str());
\r
1740 catch(ResolveError &e)
\r
1742 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1744 error_message = L"Couldn't resolve address";
\r
1745 gui_loadingtext->remove();
\r
1749 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1750 client.connect(connect_address);
\r
1753 while(client.connectedAndInitialized() == false)
\r
1756 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1757 guienv->drawAll();
\r
1758 driver->endScene();
\r
1760 // Update client and server
\r
1764 if(server != NULL)
\r
1765 server->step(0.1);
\r
1771 catch(con::PeerNotFoundException &e)
\r
1773 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1775 error_message = L"Connection timed out.";
\r
1776 gui_loadingtext->remove();
\r
1783 /*scene::ISceneNode* skybox;
\r
1784 skybox = smgr->addSkyBoxSceneNode(
\r
1785 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1786 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1787 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1788 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1789 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1790 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1793 Create the camera node
\r
1796 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1797 0, // Camera parent
\r
1798 v3f(BS*100, BS*2, BS*100), // Look from
\r
1799 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1803 if(camera == NULL)
\r
1806 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1808 camera->setFOV(FOV_ANGLE);
\r
1810 // Just so big a value that everything rendered is visible
\r
1811 camera->setFarValue(100000*BS);
\r
1813 f32 camera_yaw = 0; // "right/left"
\r
1814 f32 camera_pitch = 0; // "up/down"
\r
1820 gui_loadingtext->remove();
\r
1823 Add some gui stuff
\r
1826 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1827 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1829 // Test the text input system
\r
1830 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
1832 /*GUIMessageMenu *menu =
\r
1833 new GUIMessageMenu(guienv, guiroot, -1,
\r
1838 // Launch pause menu
\r
1839 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
1840 &g_menumgr))->drop();
\r
1843 guitext2->setVisible(true);
\r
1844 guitext_info->setVisible(true);
\r
1845 guitext_chat->setVisible(true);
\r
1848 Some statistics are collected in these
\r
1851 u32 beginscenetime = 0;
\r
1852 u32 scenetime = 0;
\r
1853 u32 endscenetime = 0;
\r
1856 //throw con::PeerNotFoundException("lol");
\r
1862 bool first_loop_after_window_activation = true;
\r
1864 // Time is in milliseconds
\r
1865 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1866 // NOTE: So we have to use getTime() and call run()s between them
\r
1867 u32 lasttime = device->getTimer()->getTime();
\r
1869 while(device->run())
\r
1871 if(g_disconnect_requested)
\r
1873 g_disconnect_requested = false;
\r
1878 Run global IrrlichtWrapper's main thread processing stuff
\r
1880 g_irrlicht->Run();
\r
1883 Random calculations
\r
1885 v2u32 screensize = driver->getScreenSize();
\r
1886 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1888 // Hilight boxes collected during the loop and displayed
\r
1889 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1892 std::wstring infotext;
\r
1894 //TimeTaker //timer1("//timer1");
\r
1896 // Time of frame without fps limit
\r
1900 // not using getRealTime is necessary for wine
\r
1901 u32 time = device->getTimer()->getTime();
\r
1902 if(time > lasttime)
\r
1903 busytime_u32 = time - lasttime;
\r
1906 busytime = busytime_u32 / 1000.0;
\r
1909 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1911 // Absolutelu necessary for wine!
\r
1918 updateViewingRange(busytime, &client);
\r
1925 float fps_max = g_settings.getFloat("fps_max");
\r
1926 u32 frametime_min = 1000./fps_max;
\r
1928 if(busytime_u32 < frametime_min)
\r
1930 u32 sleeptime = frametime_min - busytime_u32;
\r
1931 device->sleep(sleeptime);
\r
1935 // Absolutelu necessary for wine!
\r
1939 Time difference calculation
\r
1941 f32 dtime; // in seconds
\r
1943 u32 time = device->getTimer()->getTime();
\r
1944 if(time > lasttime)
\r
1945 dtime = (time - lasttime) / 1000.0;
\r
1951 Time average and jitter calculation
\r
1954 static f32 dtime_avg1 = 0.0;
\r
1955 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1956 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1958 static f32 dtime_jitter1_max_sample = 0.0;
\r
1959 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1961 static f32 jitter1_max = 0.0;
\r
1962 static f32 counter = 0.0;
\r
1963 if(dtime_jitter1 > jitter1_max)
\r
1964 jitter1_max = dtime_jitter1;
\r
1969 dtime_jitter1_max_sample = jitter1_max;
\r
1970 dtime_jitter1_max_fraction
\r
1971 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1972 jitter1_max = 0.0;
\r
1975 Control freetime ratio
\r
1977 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1979 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1980 g_freetime_ratio += 0.01;
\r
1984 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1985 g_freetime_ratio -= 0.01;
\r
1991 Busytime average and jitter calculation
\r
1994 static f32 busytime_avg1 = 0.0;
\r
1995 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1996 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1998 static f32 busytime_jitter1_max_sample = 0.0;
\r
1999 static f32 busytime_jitter1_min_sample = 0.0;
\r
2001 static f32 jitter1_max = 0.0;
\r
2002 static f32 jitter1_min = 0.0;
\r
2003 static f32 counter = 0.0;
\r
2004 if(busytime_jitter1 > jitter1_max)
\r
2005 jitter1_max = busytime_jitter1;
\r
2006 if(busytime_jitter1 < jitter1_min)
\r
2007 jitter1_min = busytime_jitter1;
\r
2009 if(counter > 0.0){
\r
2011 busytime_jitter1_max_sample = jitter1_max;
\r
2012 busytime_jitter1_min_sample = jitter1_min;
\r
2013 jitter1_max = 0.0;
\r
2014 jitter1_min = 0.0;
\r
2019 Debug info for client
\r
2022 static float counter = 0.0;
\r
2027 client.printDebugInfo(std::cout);
\r
2032 Input handler step()
\r
2034 g_input->step(dtime);
\r
2037 Player speed control
\r
2046 bool a_superspeed,
\r
2049 PlayerControl control(
\r
2050 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2051 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2052 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2053 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2054 g_input->isKeyDown(irr::KEY_SPACE),
\r
2055 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2059 client.setPlayerControl(control);
\r
2063 Process environment
\r
2067 //TimeTaker timer("client.step(dtime)");
\r
2068 client.step(dtime);
\r
2069 //client.step(dtime_avg1);
\r
2072 if(server != NULL)
\r
2074 //TimeTaker timer("server->step(dtime)");
\r
2075 server->step(dtime);
\r
2078 v3f player_position = client.getPlayerPosition();
\r
2080 //TimeTaker //timer2("//timer2");
\r
2083 Mouse and camera control
\r
2086 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2089 device->getCursorControl()->setVisible(false);
\r
2091 if(first_loop_after_window_activation){
\r
2092 //std::cout<<"window active, first loop"<<std::endl;
\r
2093 first_loop_after_window_activation = false;
\r
2096 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2097 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2098 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2099 camera_yaw -= dx*0.2;
\r
2100 camera_pitch += dy*0.2;
\r
2101 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2102 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2104 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2107 device->getCursorControl()->setVisible(true);
\r
2109 //std::cout<<"window inactive"<<std::endl;
\r
2110 first_loop_after_window_activation = true;
\r
2113 camera_yaw = wrapDegrees(camera_yaw);
\r
2114 camera_pitch = wrapDegrees(camera_pitch);
\r
2116 v3f camera_direction = v3f(0,0,1);
\r
2117 camera_direction.rotateYZBy(camera_pitch);
\r
2118 camera_direction.rotateXZBy(camera_yaw);
\r
2120 // This is at the height of the eyes of the current figure
\r
2121 v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2122 // This is more like in minecraft
\r
2123 //v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2125 camera->setPosition(camera_position);
\r
2126 // *100.0 helps in large map coordinates
\r
2127 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2129 if(FIELD_OF_VIEW_TEST){
\r
2130 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2131 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2134 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2135 //TimeTaker timer("client.updateCamera");
\r
2136 client.updateCamera(camera_position, camera_direction);
\r
2140 //TimeTaker //timer3("//timer3");
\r
2143 Calculate what block is the crosshair pointing to
\r
2146 //u32 t1 = device->getTimer()->getRealTime();
\r
2148 //f32 d = 4; // max. distance
\r
2149 f32 d = 4; // max. distance
\r
2150 core::line3d<f32> shootline(camera_position,
\r
2151 camera_position + camera_direction * BS * (d+1));
\r
2153 MapBlockObject *selected_object = client.getSelectedObject
\r
2154 (d*BS, camera_position, shootline);
\r
2157 If it's pointing to a MapBlockObject
\r
2160 if(selected_object != NULL)
\r
2162 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2164 core::aabbox3d<f32> box_on_map
\r
2165 = selected_object->getSelectionBoxOnMap();
\r
2167 hilightboxes.push_back(box_on_map);
\r
2169 infotext = narrow_to_wide(selected_object->infoText());
\r
2171 if(g_input->getLeftClicked())
\r
2173 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2174 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2175 selected_object->getId(), g_selected_item);
\r
2177 else if(g_input->getRightClicked())
\r
2179 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2181 Check if we want to modify the object ourselves
\r
2183 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2185 dstream<<"Sign object right-clicked"<<std::endl;
\r
2187 if(random_input == false)
\r
2189 // Get a new text for it
\r
2191 TextDest *dest = new TextDestSign(
\r
2192 selected_object->getBlock()->getPos(),
\r
2193 selected_object->getId(),
\r
2196 SignObject *sign_object = (SignObject*)selected_object;
\r
2198 std::wstring wtext =
\r
2199 narrow_to_wide(sign_object->getText());
\r
2201 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2207 Otherwise pass the event to the server as-is
\r
2211 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2212 selected_object->getId(), g_selected_item);
\r
2216 else // selected_object == NULL
\r
2220 Find out which node we are pointing at
\r
2223 bool nodefound = false;
\r
2225 v3s16 neighbourpos;
\r
2226 core::aabbox3d<f32> nodefacebox;
\r
2227 f32 mindistance = BS * 1001;
\r
2229 v3s16 pos_i = floatToInt(player_position);
\r
2231 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2235 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2236 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2237 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2238 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2239 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2240 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2242 for(s16 y = ystart; y <= yend; y++)
\r
2243 for(s16 z = zstart; z <= zend; z++)
\r
2244 for(s16 x = xstart; x <= xend; x++)
\r
2249 n = client.getNode(v3s16(x,y,z));
\r
2250 if(content_pointable(n.d) == false)
\r
2253 catch(InvalidPositionException &e)
\r
2259 v3f npf = intToFloat(np);
\r
2264 v3s16(0,0,1), // back
\r
2265 v3s16(0,1,0), // top
\r
2266 v3s16(1,0,0), // right
\r
2267 v3s16(0,0,-1), // front
\r
2268 v3s16(0,-1,0), // bottom
\r
2269 v3s16(-1,0,0), // left
\r
2275 if(n.d == CONTENT_TORCH)
\r
2277 v3s16 dir = unpackDir(n.dir);
\r
2278 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2279 dir_f *= BS/2 - BS/6 - BS/20;
\r
2280 v3f cpf = npf + dir_f;
\r
2281 f32 distance = (cpf - camera_position).getLength();
\r
2283 core::aabbox3d<f32> box;
\r
2286 if(dir == v3s16(0,-1,0))
\r
2288 box = core::aabbox3d<f32>(
\r
2289 npf - v3f(BS/6, BS/2, BS/6),
\r
2290 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2294 else if(dir == v3s16(0,1,0))
\r
2296 box = core::aabbox3d<f32>(
\r
2297 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2298 npf + v3f(BS/6, BS/2, BS/6)
\r
2304 box = core::aabbox3d<f32>(
\r
2305 cpf - v3f(BS/6, BS/3, BS/6),
\r
2306 cpf + v3f(BS/6, BS/3, BS/6)
\r
2310 if(distance < mindistance)
\r
2312 if(box.intersectsWithLine(shootline))
\r
2316 neighbourpos = np;
\r
2317 mindistance = distance;
\r
2318 nodefacebox = box;
\r
2327 for(u16 i=0; i<6; i++)
\r
2329 v3f dir_f = v3f(dirs[i].X,
\r
2330 dirs[i].Y, dirs[i].Z);
\r
2331 v3f centerpoint = npf + dir_f * BS/2;
\r
2333 (centerpoint - camera_position).getLength();
\r
2335 if(distance < mindistance)
\r
2337 core::CMatrix4<f32> m;
\r
2338 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2340 // This is the back face
\r
2341 v3f corners[2] = {
\r
2342 v3f(BS/2, BS/2, BS/2),
\r
2343 v3f(-BS/2, -BS/2, BS/2+d)
\r
2346 for(u16 j=0; j<2; j++)
\r
2348 m.rotateVect(corners[j]);
\r
2349 corners[j] += npf;
\r
2352 core::aabbox3d<f32> facebox(corners[0]);
\r
2353 facebox.addInternalPoint(corners[1]);
\r
2355 if(facebox.intersectsWithLine(shootline))
\r
2359 neighbourpos = np + dirs[i];
\r
2360 mindistance = distance;
\r
2361 nodefacebox = facebox;
\r
2363 } // if distance < mindistance
\r
2365 } // regular block
\r
2368 static float nodig_delay_counter = 0.0;
\r
2372 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2374 static float dig_time = 0.0;
\r
2375 static u16 dig_index = 0;
\r
2377 hilightboxes.push_back(nodefacebox);
\r
2379 if(g_input->getLeftReleased())
\r
2381 client.clearTempMod(nodepos);
\r
2385 if(nodig_delay_counter > 0.0)
\r
2387 nodig_delay_counter -= dtime;
\r
2391 if(nodepos != nodepos_old)
\r
2393 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2394 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2396 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2398 client.clearTempMod(nodepos_old);
\r
2403 if(g_input->getLeftClicked() ||
\r
2404 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2406 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2407 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2409 if(g_input->getLeftClicked())
\r
2411 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2413 if(g_input->getLeftState())
\r
2415 MapNode n = client.getNode(nodepos);
\r
2417 // Get tool name. Default is "" = bare hands
\r
2418 std::string toolname = "";
\r
2419 InventoryList *mlist = local_inventory.getList("main");
\r
2422 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2423 if(item && (std::string)item->getName() == "ToolItem")
\r
2425 ToolItem *titem = (ToolItem*)item;
\r
2426 toolname = titem->getToolName();
\r
2430 // Get digging properties for material and tool
\r
2431 u8 material = n.d;
\r
2432 DiggingProperties prop =
\r
2433 getDiggingProperties(material, toolname);
\r
2435 float dig_time_complete = 0.0;
\r
2437 if(prop.diggable == false)
\r
2439 /*dstream<<"Material "<<(int)material
\r
2440 <<" not diggable with \""
\r
2441 <<toolname<<"\""<<std::endl;*/
\r
2442 // I guess nobody will wait for this long
\r
2443 dig_time_complete = 10000000.0;
\r
2447 dig_time_complete = prop.time;
\r
2450 if(dig_time_complete >= 0.001)
\r
2452 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2453 * dig_time/dig_time_complete);
\r
2455 // This is for torches
\r
2458 dig_index = CRACK_ANIMATION_LENGTH;
\r
2461 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2463 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2464 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2468 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2469 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2470 client.clearTempMod(nodepos);
\r
2471 client.removeNode(nodepos);
\r
2475 nodig_delay_counter = dig_time_complete
\r
2476 / (float)CRACK_ANIMATION_LENGTH;
\r
2478 // We don't want a corresponding delay to
\r
2479 // very time consuming nodes
\r
2480 if(nodig_delay_counter > 0.5)
\r
2482 nodig_delay_counter = 0.5;
\r
2484 // We want a slight delay to very little
\r
2485 // time consuming nodes
\r
2486 //float mindelay = 0.15;
\r
2487 float mindelay = 0.20;
\r
2488 if(nodig_delay_counter < mindelay)
\r
2490 nodig_delay_counter = mindelay;
\r
2494 dig_time += dtime;
\r
2498 if(g_input->getRightClicked())
\r
2500 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2501 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2504 nodepos_old = nodepos;
\r
2509 } // selected_object == NULL
\r
2511 g_input->resetLeftClicked();
\r
2512 g_input->resetRightClicked();
\r
2514 if(g_input->getLeftReleased())
\r
2516 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2518 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2520 if(g_input->getRightReleased())
\r
2522 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2526 g_input->resetLeftReleased();
\r
2527 g_input->resetRightReleased();
\r
2530 Calculate stuff for drawing
\r
2533 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2535 u32 daynight_ratio = client.getDayNightRatio();
\r
2536 /*video::SColor bgcolor = video::SColor(
\r
2538 skycolor.getRed() * daynight_ratio / 1000,
\r
2539 skycolor.getGreen() * daynight_ratio / 1000,
\r
2540 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2542 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2543 video::SColor bgcolor = video::SColor(
\r
2545 skycolor.getRed() * l / 255,
\r
2546 skycolor.getGreen() * l / 255,
\r
2547 skycolor.getBlue() * l / 255);
\r
2553 if(g_settings.getBool("enable_fog") == true)
\r
2555 f32 range = draw_control.wanted_range * BS;
\r
2556 if(draw_control.range_all)
\r
2557 range = 100000*BS;
\r
2561 video::EFT_FOG_LINEAR,
\r
2565 false, // pixel fog
\r
2566 false // range fog
\r
2572 Update gui stuff (0ms)
\r
2575 //TimeTaker guiupdatetimer("Gui updating");
\r
2578 wchar_t temptext[150];
\r
2580 static float drawtime_avg = 0;
\r
2581 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2582 static float beginscenetime_avg = 0;
\r
2583 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2584 static float scenetime_avg = 0;
\r
2585 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2586 static float endscenetime_avg = 0;
\r
2587 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2589 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2591 L", R: range_all=%i"
\r
2593 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2595 draw_control.range_all,
\r
2597 beginscenetime_avg,
\r
2602 guitext->setText(temptext);
\r
2606 wchar_t temptext[150];
\r
2607 swprintf(temptext, 150,
\r
2608 L"(% .1f, % .1f, % .1f)"
\r
2609 L" (% .3f < btime_jitter < % .3f"
\r
2610 L", dtime_jitter = % .1f %%"
\r
2611 L", v_range = %.1f)",
\r
2612 player_position.X/BS,
\r
2613 player_position.Y/BS,
\r
2614 player_position.Z/BS,
\r
2615 busytime_jitter1_min_sample,
\r
2616 busytime_jitter1_max_sample,
\r
2617 dtime_jitter1_max_fraction * 100.0,
\r
2618 draw_control.wanted_range
\r
2621 guitext2->setText(temptext);
\r
2625 guitext_info->setText(infotext.c_str());
\r
2629 Get chat messages from client
\r
2632 // Get new messages
\r
2633 std::wstring message;
\r
2634 while(client.getChatMessage(message))
\r
2636 chat_lines.push_back(ChatLine(message));
\r
2637 /*if(chat_lines.size() > 6)
\r
2639 core::list<ChatLine>::Iterator
\r
2640 i = chat_lines.begin();
\r
2641 chat_lines.erase(i);
\r
2644 // Append them to form the whole static text and throw
\r
2645 // it to the gui element
\r
2646 std::wstring whole;
\r
2647 // This will correspond to the line number counted from
\r
2648 // top to bottom, from size-1 to 0
\r
2649 s16 line_number = chat_lines.size();
\r
2650 // Count of messages to be removed from the top
\r
2651 u16 to_be_removed_count = 0;
\r
2652 for(core::list<ChatLine>::Iterator
\r
2653 i = chat_lines.begin();
\r
2654 i != chat_lines.end(); i++)
\r
2656 // After this, line number is valid for this loop
\r
2659 (*i).age += dtime;
\r
2661 This results in a maximum age of 60*6 to the
\r
2662 lowermost line and a maximum of 6 lines
\r
2664 float allowed_age = (6-line_number) * 60.0;
\r
2666 if((*i).age > allowed_age)
\r
2668 to_be_removed_count++;
\r
2671 whole += (*i).text + L'\n';
\r
2673 for(u16 i=0; i<to_be_removed_count; i++)
\r
2675 core::list<ChatLine>::Iterator
\r
2676 it = chat_lines.begin();
\r
2677 chat_lines.erase(it);
\r
2679 guitext_chat->setText(whole.c_str());
\r
2680 // Update gui element size and position
\r
2681 core::rect<s32> rect(
\r
2683 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2684 screensize.X - 10,
\r
2687 guitext_chat->setRelativePosition(rect);
\r
2689 if(chat_lines.size() == 0)
\r
2690 guitext_chat->setVisible(false);
\r
2692 guitext_chat->setVisible(true);
\r
2699 static u16 old_selected_item = 65535;
\r
2700 if(client.getLocalInventoryUpdated()
\r
2701 || g_selected_item != old_selected_item)
\r
2703 old_selected_item = g_selected_item;
\r
2704 //std::cout<<"Updating local inventory"<<std::endl;
\r
2705 client.getLocalInventory(local_inventory);
\r
2706 quick_inventory->setSelection(g_selected_item);
\r
2707 quick_inventory->update();
\r
2711 Send actions returned by the inventory menu
\r
2713 while(inventory_action_queue.size() != 0)
\r
2715 InventoryAction *a = inventory_action_queue.pop_front();
\r
2717 client.sendInventoryAction(a);
\r
2726 TimeTaker drawtimer("Drawing");
\r
2730 TimeTaker timer("beginScene");
\r
2731 driver->beginScene(true, true, bgcolor);
\r
2732 //driver->beginScene(false, true, bgcolor);
\r
2733 beginscenetime = timer.stop(true);
\r
2738 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2741 TimeTaker timer("smgr");
\r
2743 scenetime = timer.stop(true);
\r
2747 //TimeTaker timer9("auxiliary drawings");
\r
2751 //TimeTaker //timer10("//timer10");
\r
2753 video::SMaterial m;
\r
2755 m.Lighting = false;
\r
2756 driver->setMaterial(m);
\r
2758 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2760 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2761 i != hilightboxes.end(); i++)
\r
2763 /*std::cout<<"hilightbox min="
\r
2764 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2766 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2768 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2774 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2775 displaycenter + core::vector2d<s32>(10,0),
\r
2776 video::SColor(255,255,255,255));
\r
2777 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2778 displaycenter + core::vector2d<s32>(0,10),
\r
2779 video::SColor(255,255,255,255));
\r
2784 //TimeTaker //timer11("//timer11");
\r
2790 guienv->drawAll();
\r
2794 TimeTaker timer("endScene");
\r
2795 driver->endScene();
\r
2796 endscenetime = timer.stop(true);
\r
2799 drawtime = drawtimer.stop(true);
\r
2805 static s16 lastFPS = 0;
\r
2806 //u16 fps = driver->getFPS();
\r
2807 u16 fps = (1.0/dtime_avg1);
\r
2809 if (lastFPS != fps)
\r
2811 core::stringw str = L"Minetest [";
\r
2812 str += driver->getName();
\r
2816 device->setWindowCaption(str.c_str());
\r
2822 device->yield();*/
\r
2825 delete quick_inventory;
\r
2827 } // client and server are deleted at this point
\r
2830 catch(con::PeerNotFoundException &e)
\r
2832 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2833 error_message = L"Connection timed out.";
\r
2836 } // Menu-game loop
\r
2841 In the end, delete the Irrlicht device.
\r
2846 Update configuration file
\r
2848 /*if(configpath != "")
\r
2850 g_settings.updateConfigFile(configpath.c_str());
\r
2853 END_DEBUG_EXCEPTION_HANDLER
\r
2855 debugstreams_deinit();
\r