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 TODO: Remove HMParams
\r
257 TODO: Flowing water to actually contain flow direction information
\r
259 TODO: Remove duplicate lighting implementation from Map (leave
\r
265 ======================================================================
\r
270 Setting this to 1 enables a special camera mode that forces
\r
271 the renderers to think that the camera statically points from
\r
272 the starting place to a static direction.
\r
274 This allows one to move around with the player and see what
\r
275 is actually drawn behind solid things and behind the player.
\r
277 #define FIELD_OF_VIEW_TEST 0
\r
281 #pragma message ("Disabling unit tests")
\r
283 #warning "Disabling unit tests"
\r
285 // Disable unit tests
\r
286 #define ENABLE_TESTS 0
\r
288 // Enable unit tests
\r
289 #define ENABLE_TESTS 1
\r
293 #pragma comment(lib, "Irrlicht.lib")
\r
294 //#pragma comment(lib, "jthread.lib")
\r
295 #pragma comment(lib, "zlibwapi.lib")
\r
296 // This would get rid of the console window
\r
297 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
300 #include <iostream>
\r
302 #include <jmutexautolock.h>
\r
303 #include <locale.h>
\r
304 #include "common_irrlicht.h"
\r
307 #include "player.h"
\r
310 #include "environment.h"
\r
311 #include "server.h"
\r
312 #include "client.h"
\r
313 #include "serialization.h"
\r
314 #include "constants.h"
\r
315 #include "strfnd.h"
\r
316 #include "porting.h"
\r
317 #include "irrlichtwrapper.h"
\r
318 #include "gettime.h"
\r
319 #include "porting.h"
\r
320 #include "guiPauseMenu.h"
\r
321 #include "guiInventoryMenu.h"
\r
322 #include "guiTextInputMenu.h"
\r
323 #include "materials.h"
\r
324 #include "guiMessageMenu.h"
\r
325 #include "filesys.h"
\r
326 #include "config.h"
\r
327 #include "guiMainMenu.h"
\r
329 IrrlichtWrapper *g_irrlicht;
\r
331 MapDrawControl draw_control;
\r
335 These are loaded from the config file.
\r
338 Settings g_settings;
\r
340 extern void set_default_settings();
\r
346 IrrlichtDevice *g_device = NULL;
\r
347 Client *g_client = NULL;
\r
353 gui::IGUIEnvironment* guienv = NULL;
\r
354 gui::IGUIStaticText *guiroot = NULL;
\r
356 class MainMenuManager : public IMenuManager
\r
359 virtual void createdMenu(GUIModalMenu *menu)
\r
361 for(core::list<GUIModalMenu*>::Iterator
\r
362 i = m_stack.begin();
\r
363 i != m_stack.end(); i++)
\r
365 assert(*i != menu);
\r
368 if(m_stack.size() != 0)
\r
369 (*m_stack.getLast())->setVisible(false);
\r
370 m_stack.push_back(menu);
\r
373 virtual void deletingMenu(GUIModalMenu *menu)
\r
375 // Remove all entries if there are duplicates
\r
376 bool removed_entry;
\r
378 removed_entry = false;
\r
379 for(core::list<GUIModalMenu*>::Iterator
\r
380 i = m_stack.begin();
\r
381 i != m_stack.end(); i++)
\r
386 removed_entry = true;
\r
390 }while(removed_entry);
\r
392 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
393 assert(*i == menu);
\r
394 m_stack.erase(i);*/
\r
396 if(m_stack.size() != 0)
\r
397 (*m_stack.getLast())->setVisible(true);
\r
402 return m_stack.size();
\r
405 core::list<GUIModalMenu*> m_stack;
\r
408 MainMenuManager g_menumgr;
\r
410 bool noMenuActive()
\r
412 return (g_menumgr.menuCount() == 0);
\r
415 bool g_disconnect_requested = false;
\r
417 class MainGameCallback : public IGameCallback
\r
420 virtual void exitToOS()
\r
422 g_device->closeDevice();
\r
425 virtual void disconnect()
\r
427 g_disconnect_requested = true;
\r
431 MainGameCallback g_gamecallback;
\r
433 // Inventory actions from the menu are buffered here before sending
\r
434 Queue<InventoryAction*> inventory_action_queue;
\r
435 // This is a copy of the inventory that the client's environment has
\r
436 Inventory local_inventory;
\r
438 u16 g_selected_item = 0;
\r
445 std::ostream *dout_con_ptr = &dummyout;
\r
446 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
447 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
448 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
449 //std::ostream *dout_con_ptr = &dstream;
\r
450 //std::ostream *derr_con_ptr = &dstream;
\r
453 std::ostream *dout_server_ptr = &dstream;
\r
454 std::ostream *derr_server_ptr = &dstream;
\r
457 std::ostream *dout_client_ptr = &dstream;
\r
458 std::ostream *derr_client_ptr = &dstream;
\r
461 gettime.h implementation
\r
467 Use irrlicht because it is more precise than porting.h's
\r
470 if(g_irrlicht == NULL)
\r
472 return g_irrlicht->getTime();
\r
479 struct TextDestSign : public TextDest
\r
481 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
483 m_blockpos = blockpos;
\r
487 void gotText(std::wstring text)
\r
489 std::string ntext = wide_to_narrow(text);
\r
490 dstream<<"Changing text of a sign object: "
\r
491 <<ntext<<std::endl;
\r
492 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
500 struct TextDestChat : public TextDest
\r
502 TextDestChat(Client *client)
\r
506 void gotText(std::wstring text)
\r
508 m_client->sendChatMessage(text);
\r
509 m_client->addChatMessage(text);
\r
515 class MyEventReceiver : public IEventReceiver
\r
518 // This is the one method that we have to implement
\r
519 virtual bool OnEvent(const SEvent& event)
\r
522 React to nothing here if a menu is active
\r
524 if(noMenuActive() == false)
\r
530 // Remember whether each key is down or up
\r
531 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
533 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
535 if(event.KeyInput.PressedDown)
\r
537 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
543 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
545 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
547 dstream<<DTIME<<"MyEventReceiver: "
\r
548 <<"Launching pause menu"<<std::endl;
\r
549 // It will delete itself by itself
\r
550 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
551 &g_menumgr))->drop();
\r
554 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
556 dstream<<DTIME<<"MyEventReceiver: "
\r
557 <<"Launching inventory"<<std::endl;
\r
558 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
559 &local_inventory, &inventory_action_queue,
\r
560 &g_menumgr))->drop();
\r
563 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
565 TextDest *dest = new TextDestChat(g_client);
\r
567 (new GUITextInputMenu(guienv, guiroot, -1,
\r
573 // Material selection
\r
574 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
576 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
579 g_selected_item = 0;
\r
580 dstream<<DTIME<<"Selected item: "
\r
581 <<g_selected_item<<std::endl;
\r
584 // Viewing range selection
\r
585 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
587 if(draw_control.range_all)
\r
589 draw_control.range_all = false;
\r
590 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
594 draw_control.range_all = true;
\r
595 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
599 // Print debug stacks
\r
600 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
602 dstream<<"-----------------------------------------"
\r
604 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
605 dstream<<"-----------------------------------------"
\r
607 debug_stacks_print();
\r
612 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
614 if(noMenuActive() == false)
\r
616 left_active = false;
\r
617 middle_active = false;
\r
618 right_active = false;
\r
622 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
623 left_active = event.MouseInput.isLeftPressed();
\r
624 middle_active = event.MouseInput.isMiddlePressed();
\r
625 right_active = event.MouseInput.isRightPressed();
\r
627 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
629 leftclicked = true;
\r
631 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
633 rightclicked = true;
\r
635 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
637 leftreleased = true;
\r
639 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
641 rightreleased = true;
\r
643 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
645 /*dstream<<"event.MouseInput.Wheel="
\r
646 <<event.MouseInput.Wheel<<std::endl;*/
\r
647 if(event.MouseInput.Wheel < 0)
\r
649 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
652 g_selected_item = 0;
\r
654 else if(event.MouseInput.Wheel > 0)
\r
656 if(g_selected_item > 0)
\r
659 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
668 // This is used to check whether a key is being held down
\r
669 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
671 return keyIsDown[keyCode];
\r
676 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
677 keyIsDown[i] = false;
\r
679 leftclicked = false;
\r
680 rightclicked = false;
\r
681 leftreleased = false;
\r
682 rightreleased = false;
\r
684 left_active = false;
\r
685 middle_active = false;
\r
686 right_active = false;
\r
697 bool rightreleased;
\r
700 bool middle_active;
\r
704 // We use this array to store the current state of each key
\r
705 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
708 IrrlichtDevice *m_device;
\r
717 virtual ~InputHandler()
\r
721 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
723 virtual v2s32 getMousePos() = 0;
\r
724 virtual void setMousePos(s32 x, s32 y) = 0;
\r
726 virtual bool getLeftState() = 0;
\r
727 virtual bool getRightState() = 0;
\r
729 virtual bool getLeftClicked() = 0;
\r
730 virtual bool getRightClicked() = 0;
\r
731 virtual void resetLeftClicked() = 0;
\r
732 virtual void resetRightClicked() = 0;
\r
734 virtual bool getLeftReleased() = 0;
\r
735 virtual bool getRightReleased() = 0;
\r
736 virtual void resetLeftReleased() = 0;
\r
737 virtual void resetRightReleased() = 0;
\r
739 virtual void step(float dtime) {};
\r
741 virtual void clear() {};
\r
744 InputHandler *g_input = NULL;
\r
746 class RealInputHandler : public InputHandler
\r
749 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
751 m_receiver(receiver)
\r
754 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
756 return m_receiver->IsKeyDown(keyCode);
\r
758 virtual v2s32 getMousePos()
\r
760 return m_device->getCursorControl()->getPosition();
\r
762 virtual void setMousePos(s32 x, s32 y)
\r
764 m_device->getCursorControl()->setPosition(x, y);
\r
767 virtual bool getLeftState()
\r
769 return m_receiver->left_active;
\r
771 virtual bool getRightState()
\r
773 return m_receiver->right_active;
\r
776 virtual bool getLeftClicked()
\r
778 return m_receiver->leftclicked;
\r
780 virtual bool getRightClicked()
\r
782 return m_receiver->rightclicked;
\r
784 virtual void resetLeftClicked()
\r
786 m_receiver->leftclicked = false;
\r
788 virtual void resetRightClicked()
\r
790 m_receiver->rightclicked = false;
\r
793 virtual bool getLeftReleased()
\r
795 return m_receiver->leftreleased;
\r
797 virtual bool getRightReleased()
\r
799 return m_receiver->rightreleased;
\r
801 virtual void resetLeftReleased()
\r
803 m_receiver->leftreleased = false;
\r
805 virtual void resetRightReleased()
\r
807 m_receiver->rightreleased = false;
\r
812 resetRightClicked();
\r
813 resetLeftClicked();
\r
816 IrrlichtDevice *m_device;
\r
817 MyEventReceiver *m_receiver;
\r
820 class RandomInputHandler : public InputHandler
\r
823 RandomInputHandler()
\r
825 leftclicked = false;
\r
826 rightclicked = false;
\r
827 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
828 keydown[i] = false;
\r
830 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
832 return keydown[keyCode];
\r
834 virtual v2s32 getMousePos()
\r
838 virtual void setMousePos(s32 x, s32 y)
\r
840 mousepos = v2s32(x,y);
\r
843 virtual bool getLeftState()
\r
847 virtual bool getRightState()
\r
852 virtual bool getLeftClicked()
\r
854 return leftclicked;
\r
856 virtual bool getRightClicked()
\r
858 return rightclicked;
\r
860 virtual void resetLeftClicked()
\r
862 leftclicked = false;
\r
864 virtual void resetRightClicked()
\r
866 rightclicked = false;
\r
869 virtual bool getLeftReleased()
\r
873 virtual bool getRightReleased()
\r
877 virtual void resetLeftReleased()
\r
880 virtual void resetRightReleased()
\r
884 virtual void step(float dtime)
\r
887 static float counter1 = 0;
\r
891 counter1 = 0.1*Rand(1,10);
\r
892 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
893 g_selected_material++;
\r
895 g_selected_material = 0;*/
\r
896 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
899 g_selected_item = 0;
\r
903 static float counter1 = 0;
\r
907 counter1 = 0.1*Rand(1, 40);
\r
908 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
912 static float counter1 = 0;
\r
916 counter1 = 0.1*Rand(1, 40);
\r
917 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
921 static float counter1 = 0;
\r
925 counter1 = 0.1*Rand(1, 40);
\r
926 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
930 static float counter1 = 0;
\r
934 counter1 = 0.1*Rand(1, 40);
\r
935 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
939 static float counter1 = 0;
\r
943 counter1 = 0.1*Rand(1, 20);
\r
944 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
948 static float counter1 = 0;
\r
952 counter1 = 0.1*Rand(1, 30);
\r
953 leftclicked = true;
\r
957 static float counter1 = 0;
\r
961 counter1 = 0.1*Rand(1, 20);
\r
962 rightclicked = true;
\r
965 mousepos += mousespeed;
\r
968 s32 Rand(s32 min, s32 max)
\r
970 return (myrand()%(max-min+1))+min;
\r
973 bool keydown[KEY_KEY_CODES_COUNT];
\r
980 void updateViewingRange(f32 frametime_in, Client *client)
\r
982 if(draw_control.range_all == true)
\r
985 static f32 added_frametime = 0;
\r
986 static s16 added_frames = 0;
\r
988 added_frametime += frametime_in;
\r
991 // Actually this counter kind of sucks because frametime is busytime
\r
992 static f32 counter = 0;
\r
993 counter -= frametime_in;
\r
999 /*dstream<<__FUNCTION_NAME
\r
1000 <<": Collected "<<added_frames<<" frames, total of "
\r
1001 <<added_frametime<<"s."<<std::endl;*/
\r
1003 /*dstream<<"draw_control.blocks_drawn="
\r
1004 <<draw_control.blocks_drawn
\r
1005 <<", draw_control.blocks_would_have_drawn="
\r
1006 <<draw_control.blocks_would_have_drawn
\r
1009 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1010 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1012 draw_control.wanted_min_range = range_min;
\r
1013 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1015 float block_draw_ratio = 1.0;
\r
1016 if(draw_control.blocks_would_have_drawn != 0)
\r
1018 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1019 / (float)draw_control.blocks_would_have_drawn;
\r
1022 // Calculate the average frametime in the case that all wanted
\r
1023 // blocks had been drawn
\r
1024 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1026 added_frametime = 0.0;
\r
1029 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1030 float wanted_frametime = 1.0 / wanted_fps;
\r
1032 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1033 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1035 // If needed frametime change is very small, just return
\r
1036 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1038 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1042 float range = draw_control.wanted_range;
\r
1043 float new_range = range;
\r
1045 static s16 range_old = 0;
\r
1046 static f32 frametime_old = 0;
\r
1048 float d_range = range - range_old;
\r
1049 f32 d_frametime = frametime - frametime_old;
\r
1050 // A sane default of 30ms per 50 nodes of range
\r
1051 static f32 time_per_range = 30. / 50;
\r
1054 time_per_range = d_frametime / d_range;
\r
1057 // The minimum allowed calculated frametime-range derivative:
\r
1058 // Practically this sets the maximum speed of changing the range.
\r
1059 // The lower this value, the higher the maximum changing speed.
\r
1060 // A low value here results in wobbly range (0.001)
\r
1061 // A high value here results in slow changing range (0.0025)
\r
1062 // SUGG: This could be dynamically adjusted so that when
\r
1063 // the camera is turning, this is lower
\r
1064 //float min_time_per_range = 0.0015;
\r
1065 float min_time_per_range = 0.0010;
\r
1066 //float min_time_per_range = 0.05 / range;
\r
1067 if(time_per_range < min_time_per_range)
\r
1069 time_per_range = min_time_per_range;
\r
1070 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1074 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1077 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1078 // Dampen the change a bit to kill oscillations
\r
1079 //wanted_range_change *= 0.9;
\r
1080 //wanted_range_change *= 0.75;
\r
1081 wanted_range_change *= 0.5;
\r
1082 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1084 // If needed range change is very small, just return
\r
1085 if(fabs(wanted_range_change) < 0.001)
\r
1087 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1091 new_range += wanted_range_change;
\r
1092 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1094 //float new_range_unclamped = new_range;
\r
1095 if(new_range < range_min)
\r
1096 new_range = range_min;
\r
1097 if(new_range > range_max)
\r
1098 new_range = range_max;
\r
1100 /*if(new_range != new_range_unclamped)
\r
1101 dstream<<", clamped to "<<new_range<<std::endl;
\r
1103 dstream<<std::endl;*/
\r
1105 draw_control.wanted_range = new_range;
\r
1107 range_old = new_range;
\r
1108 frametime_old = frametime;
\r
1111 class GUIQuickInventory : public IEventReceiver
\r
1114 GUIQuickInventory(
\r
1115 gui::IGUIEnvironment* env,
\r
1116 gui::IGUIElement* parent,
\r
1119 Inventory *inventory):
\r
1120 m_itemcount(itemcount),
\r
1121 m_inventory(inventory)
\r
1123 core::rect<s32> imgsize(0,0,48,48);
\r
1124 core::rect<s32> textsize(0,0,48,16);
\r
1125 v2s32 spacing(0, 64);
\r
1126 for(s32 i=0; i<m_itemcount; i++)
\r
1128 m_images.push_back(env->addImage(
\r
1129 imgsize + pos + spacing*i
\r
1131 m_images[i]->setScaleImage(true);
\r
1132 m_texts.push_back(env->addStaticText(
\r
1134 textsize + pos + spacing*i,
\r
1137 m_texts[i]->setBackgroundColor(
\r
1138 video::SColor(128,0,0,0));
\r
1139 m_texts[i]->setTextAlignment(
\r
1140 gui::EGUIA_CENTER,
\r
1141 gui::EGUIA_UPPERLEFT);
\r
1145 ~GUIQuickInventory()
\r
1147 for(u32 i=0; i<m_texts.size(); i++)
\r
1149 m_texts[i]->remove();
\r
1151 for(u32 i=0; i<m_images.size(); i++)
\r
1153 m_images[i]->remove();
\r
1157 virtual bool OnEvent(const SEvent& event)
\r
1162 void setSelection(s32 i)
\r
1171 start = m_selection - m_itemcount / 2;
\r
1173 InventoryList *mainlist = m_inventory->getList("main");
\r
1175 for(s32 i=0; i<m_itemcount; i++)
\r
1177 s32 j = i + start;
\r
1179 if(j > (s32)mainlist->getSize() - 1)
\r
1180 j -= mainlist->getSize();
\r
1182 j += mainlist->getSize();
\r
1184 InventoryItem *item = mainlist->getItem(j);
\r
1188 m_images[i]->setImage(NULL);
\r
1191 if(m_selection == j)
\r
1192 swprintf(t, 10, L"<-");
\r
1194 swprintf(t, 10, L"");
\r
1195 m_texts[i]->setText(t);
\r
1197 // The next ifs will segfault with a NULL pointer
\r
1202 m_images[i]->setImage(item->getImage());
\r
1205 if(m_selection == j)
\r
1206 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1208 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1209 m_texts[i]->setText(t);
\r
1215 core::array<gui::IGUIStaticText*> m_texts;
\r
1216 core::array<gui::IGUIImage*> m_images;
\r
1217 Inventory *m_inventory;
\r
1228 ChatLine(const std::wstring &a_text):
\r
1234 std::wstring text;
\r
1237 int main(int argc, char *argv[])
\r
1240 Low-level initialization
\r
1243 bool disable_stderr = false;
\r
1245 disable_stderr = true;
\r
1248 // Initialize debug streams
\r
1249 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1250 // Initialize debug stacks
\r
1251 debug_stacks_init();
\r
1253 DSTACK(__FUNCTION_NAME);
\r
1255 porting::initializePaths();
\r
1256 // Create user data directory
\r
1257 fs::CreateDir(porting::path_userdata);
\r
1259 initializeMaterialProperties();
\r
1261 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1263 // Print startup message
\r
1264 dstream<<DTIME<<"minetest-c55"
\r
1265 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1266 <<", "<<BUILD_INFO
\r
1270 Parse command line
\r
1273 // List all allowed options
\r
1274 core::map<std::string, ValueSpec> allowed_options;
\r
1275 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1276 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1277 "Run server directly"));
\r
1278 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1279 "Load configuration from specified file"));
\r
1280 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1281 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1282 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1283 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1284 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1285 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1287 Settings cmd_args;
\r
1289 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1291 if(ret == false || cmd_args.getFlag("help"))
\r
1293 dstream<<"Allowed options:"<<std::endl;
\r
1294 for(core::map<std::string, ValueSpec>::Iterator
\r
1295 i = allowed_options.getIterator();
\r
1296 i.atEnd() == false; i++)
\r
1298 dstream<<" --"<<i.getNode()->getKey();
\r
1299 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1304 dstream<<" <value>";
\r
1306 dstream<<std::endl;
\r
1308 if(i.getNode()->getValue().help != NULL)
\r
1310 dstream<<" "<<i.getNode()->getValue().help
\r
1315 return cmd_args.getFlag("help") ? 0 : 1;
\r
1320 Basic initialization
\r
1323 // Initialize default settings
\r
1324 set_default_settings();
\r
1326 // Set locale. This is for forcing '.' as the decimal point.
\r
1327 std::locale::global(std::locale("C"));
\r
1328 // This enables printing all characters in bitmap font
\r
1329 setlocale(LC_CTYPE, "en_US");
\r
1331 // Initialize sockets
\r
1333 atexit(sockets_cleanup);
\r
1343 // Path of configuration file in use
\r
1344 std::string configpath = "";
\r
1346 if(cmd_args.exists("config"))
\r
1348 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1351 dstream<<"Could not read configuration from \""
\r
1352 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1355 configpath = cmd_args.get("config");
\r
1359 core::array<std::string> filenames;
\r
1360 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1361 #ifdef RUN_IN_PLACE
\r
1362 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1365 for(u32 i=0; i<filenames.size(); i++)
\r
1367 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1370 configpath = filenames[i];
\r
1376 // Initialize random seed
\r
1383 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1384 || cmd_args.getFlag("enable-unittests") == true)
\r
1389 // Read map parameters from settings
\r
1391 HMParams hm_params;
\r
1392 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1393 hm_params.randmax = g_settings.get("height_randmax");
\r
1394 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1395 hm_params.base = g_settings.get("height_base");*/
\r
1397 MapParams map_params;
\r
1398 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1399 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1407 if(cmd_args.exists("port"))
\r
1408 port = cmd_args.getU16("port");
\r
1409 else if(cmd_args.exists("port"))
\r
1410 port = g_settings.getU16("port");
\r
1413 std::string map_dir = porting::path_userdata+"/map";
\r
1414 if(cmd_args.exists("map-dir"))
\r
1415 map_dir = cmd_args.get("map-dir");
\r
1416 else if(g_settings.exists("map-dir"))
\r
1417 map_dir = g_settings.get("map-dir");
\r
1419 // Run dedicated server if asked to
\r
1420 if(cmd_args.getFlag("server"))
\r
1422 DSTACK("Dedicated server branch");
\r
1425 Server server(map_dir.c_str(), hm_params, map_params);
\r
1426 server.start(port);
\r
1429 dedicated_server_loop(server);
\r
1438 // Address to connect to
\r
1439 std::string address = "";
\r
1441 if(cmd_args.exists("address"))
\r
1443 address = cmd_args.get("address");
\r
1447 address = g_settings.get("address");
\r
1450 std::string playername = g_settings.get("name");
\r
1453 Resolution selection
\r
1456 bool fullscreen = false;
\r
1457 u16 screenW = g_settings.getU16("screenW");
\r
1458 u16 screenH = g_settings.getU16("screenH");
\r
1462 MyEventReceiver receiver;
\r
1464 video::E_DRIVER_TYPE driverType;
\r
1467 //driverType = video::EDT_DIRECT3D9;
\r
1468 driverType = video::EDT_OPENGL;
\r
1470 driverType = video::EDT_OPENGL;
\r
1471 //driverType = video::EDT_BURNINGSVIDEO;
\r
1474 // create device and exit if creation failed
\r
1476 IrrlichtDevice *device;
\r
1477 device = createDevice(driverType,
\r
1478 core::dimension2d<u32>(screenW, screenH),
\r
1479 16, fullscreen, false, false, &receiver);
\r
1482 return 1; // could not create selected driver.
\r
1484 g_device = device;
\r
1485 g_irrlicht = new IrrlichtWrapper(device);
\r
1487 //g_device = device;
\r
1489 device->setResizable(true);
\r
1491 bool random_input = g_settings.getBool("random_input")
\r
1492 || cmd_args.getFlag("random-input");
\r
1494 g_input = new RandomInputHandler();
\r
1496 g_input = new RealInputHandler(device, &receiver);
\r
1499 Continue initialization
\r
1502 video::IVideoDriver* driver = device->getVideoDriver();
\r
1505 This changes the minimum allowed number of vertices in a VBO
\r
1507 //driver->setMinHardwareBufferVertexCount(50);
\r
1509 scene::ISceneManager* smgr = device->getSceneManager();
\r
1511 guienv = device->getGUIEnvironment();
\r
1512 gui::IGUISkin* skin = guienv->getSkin();
\r
1513 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1515 skin->setFont(font);
\r
1517 dstream<<"WARNING: Font file was not found."
\r
1518 " Using default font."<<std::endl;
\r
1519 // If font was not found, this will get us one
\r
1520 font = skin->getFont();
\r
1523 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1524 dstream<<"text_height="<<text_height<<std::endl;
\r
1526 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1527 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1528 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1529 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1530 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1531 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1534 Preload some textures
\r
1537 init_content_inventory_texture_paths();
\r
1538 init_tile_textures();
\r
1545 We need some kind of a root node to be able to add
\r
1546 custom gui elements directly on the screen.
\r
1547 Otherwise they won't be automatically drawn.
\r
1549 guiroot = guienv->addStaticText(L"",
\r
1550 core::rect<s32>(0, 0, 10000, 10000));
\r
1552 // First line of debug text
\r
1553 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1555 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1557 // Second line of debug text
\r
1558 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1560 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1563 // At the middle of the screen
\r
1564 // Object infos are shown in this
\r
1565 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1567 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1571 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1573 core::rect<s32>(0,0,0,0),
\r
1575 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1576 core::list<ChatLine> chat_lines;
\r
1579 If an error occurs, this is set to something and the
\r
1580 menu-game loop is restarted. It is then displayed before
\r
1583 std::wstring error_message = L"";
\r
1588 while(g_device->run())
\r
1591 // This is used for catching disconnects
\r
1596 Out-of-game menu loop
\r
1599 // Wait for proper parameters
\r
1602 // Cursor can be non-visible when coming from the game
\r
1603 device->getCursorControl()->setVisible(true);
\r
1604 // Some stuff are left to scene manager when coming from the game
\r
1605 // (map at least?)
\r
1607 // Reset or hide the debug gui texts
\r
1608 guitext->setText(L"Minetest-c55");
\r
1609 guitext2->setVisible(false);
\r
1610 guitext_info->setVisible(false);
\r
1611 guitext_chat->setVisible(false);
\r
1613 // Initialize menu data
\r
1614 MainMenuData menudata;
\r
1615 menudata.address = narrow_to_wide(address);
\r
1616 menudata.name = narrow_to_wide(playername);
\r
1617 menudata.port = narrow_to_wide(itos(port));
\r
1618 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1620 GUIMainMenu *menu =
\r
1621 new GUIMainMenu(guienv, guiroot, -1,
\r
1622 &g_menumgr, &menudata, &g_gamecallback);
\r
1623 menu->allowFocusRemoval(true);
\r
1625 if(error_message != L"")
\r
1627 GUIMessageMenu *menu2 =
\r
1628 new GUIMessageMenu(guienv, guiroot, -1,
\r
1629 &g_menumgr, error_message.c_str());
\r
1631 error_message = L"";
\r
1634 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1636 dstream<<"Created main menu"<<std::endl;
\r
1638 while(g_device->run())
\r
1640 // Run global IrrlichtWrapper's main thread processing stuff
\r
1641 g_irrlicht->Run();
\r
1643 if(menu->getStatus() == true)
\r
1646 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1647 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1648 guienv->drawAll();
\r
1649 driver->endScene();
\r
1652 // Break out of menu-game loop to shut down cleanly
\r
1653 if(g_device->run() == false)
\r
1656 dstream<<"Dropping main menu"<<std::endl;
\r
1660 playername = wide_to_narrow(menudata.name);
\r
1661 address = wide_to_narrow(menudata.address);
\r
1662 port = stoi(wide_to_narrow(menudata.port));
\r
1663 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1665 // Check for valid parameters, restart menu if invalid.
\r
1666 if(playername == "")
\r
1668 error_message = L"Name required.";
\r
1673 g_settings.set("name", playername);
\r
1674 g_settings.set("address", address);
\r
1675 g_settings.set("port", itos(port));
\r
1676 // Update configuration file
\r
1677 if(configpath != "")
\r
1678 g_settings.updateConfigFile(configpath.c_str());
\r
1680 // Continue to game
\r
1684 // Break out of menu-game loop to shut down cleanly
\r
1685 if(g_device->run() == false)
\r
1689 Make a scope here so that the client and the server and other
\r
1690 stuff gets removed when disconnected or the irrlicht device
\r
1696 Draw "Loading" screen
\r
1698 const wchar_t *text = L"Loading and connecting...";
\r
1699 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1700 core::vector2d<s32> textsize(300, text_height);
\r
1701 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1703 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1704 text, textrect, false, false);
\r
1705 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1707 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1708 guienv->drawAll();
\r
1709 driver->endScene();
\r
1711 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1715 SharedPtr will delete it when it goes out of scope.
\r
1717 SharedPtr<Server> server;
\r
1718 if(address == ""){
\r
1719 server = new Server(map_dir, hm_params, map_params);
\r
1720 server->start(port);
\r
1727 Client client(device, playername.c_str(), draw_control);
\r
1729 g_client = &client;
\r
1731 Address connect_address(0,0,0,0, port);
\r
1734 connect_address.Resolve("localhost");
\r
1736 connect_address.Resolve(address.c_str());
\r
1738 catch(ResolveError &e)
\r
1740 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1742 error_message = L"Couldn't resolve address";
\r
1743 gui_loadingtext->remove();
\r
1747 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1748 client.connect(connect_address);
\r
1751 while(client.connectedAndInitialized() == false)
\r
1754 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1755 guienv->drawAll();
\r
1756 driver->endScene();
\r
1758 // Update client and server
\r
1762 if(server != NULL)
\r
1763 server->step(0.1);
\r
1769 catch(con::PeerNotFoundException &e)
\r
1771 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1773 error_message = L"Connection timed out.";
\r
1774 gui_loadingtext->remove();
\r
1781 /*scene::ISceneNode* skybox;
\r
1782 skybox = smgr->addSkyBoxSceneNode(
\r
1783 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1784 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1785 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1786 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1787 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1788 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1791 Create the camera node
\r
1794 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1795 0, // Camera parent
\r
1796 v3f(BS*100, BS*2, BS*100), // Look from
\r
1797 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1801 if(camera == NULL)
\r
1804 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1806 camera->setFOV(FOV_ANGLE);
\r
1808 // Just so big a value that everything rendered is visible
\r
1809 camera->setFarValue(100000*BS);
\r
1811 f32 camera_yaw = 0; // "right/left"
\r
1812 f32 camera_pitch = 0; // "up/down"
\r
1818 gui_loadingtext->remove();
\r
1821 Add some gui stuff
\r
1824 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1825 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1827 // Test the text input system
\r
1828 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
1830 /*GUIMessageMenu *menu =
\r
1831 new GUIMessageMenu(guienv, guiroot, -1,
\r
1836 // Launch pause menu
\r
1837 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
1838 &g_menumgr))->drop();
\r
1841 guitext2->setVisible(true);
\r
1842 guitext_info->setVisible(true);
\r
1843 guitext_chat->setVisible(true);
\r
1846 Some statistics are collected in these
\r
1849 u32 beginscenetime = 0;
\r
1850 u32 scenetime = 0;
\r
1851 u32 endscenetime = 0;
\r
1854 //throw con::PeerNotFoundException("lol");
\r
1860 bool first_loop_after_window_activation = true;
\r
1862 // Time is in milliseconds
\r
1863 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1864 // NOTE: So we have to use getTime() and call run()s between them
\r
1865 u32 lasttime = device->getTimer()->getTime();
\r
1867 while(device->run())
\r
1869 if(g_disconnect_requested)
\r
1871 g_disconnect_requested = false;
\r
1876 Run global IrrlichtWrapper's main thread processing stuff
\r
1878 g_irrlicht->Run();
\r
1881 Random calculations
\r
1883 v2u32 screensize = driver->getScreenSize();
\r
1884 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1886 // Hilight boxes collected during the loop and displayed
\r
1887 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1890 std::wstring infotext;
\r
1892 //TimeTaker //timer1("//timer1");
\r
1894 // Time of frame without fps limit
\r
1898 // not using getRealTime is necessary for wine
\r
1899 u32 time = device->getTimer()->getTime();
\r
1900 if(time > lasttime)
\r
1901 busytime_u32 = time - lasttime;
\r
1904 busytime = busytime_u32 / 1000.0;
\r
1907 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1909 // Absolutelu necessary for wine!
\r
1916 updateViewingRange(busytime, &client);
\r
1923 float fps_max = g_settings.getFloat("fps_max");
\r
1924 u32 frametime_min = 1000./fps_max;
\r
1926 if(busytime_u32 < frametime_min)
\r
1928 u32 sleeptime = frametime_min - busytime_u32;
\r
1929 device->sleep(sleeptime);
\r
1933 // Absolutelu necessary for wine!
\r
1937 Time difference calculation
\r
1939 f32 dtime; // in seconds
\r
1941 u32 time = device->getTimer()->getTime();
\r
1942 if(time > lasttime)
\r
1943 dtime = (time - lasttime) / 1000.0;
\r
1949 Time average and jitter calculation
\r
1952 static f32 dtime_avg1 = 0.0;
\r
1953 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1954 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1956 static f32 dtime_jitter1_max_sample = 0.0;
\r
1957 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1959 static f32 jitter1_max = 0.0;
\r
1960 static f32 counter = 0.0;
\r
1961 if(dtime_jitter1 > jitter1_max)
\r
1962 jitter1_max = dtime_jitter1;
\r
1967 dtime_jitter1_max_sample = jitter1_max;
\r
1968 dtime_jitter1_max_fraction
\r
1969 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1970 jitter1_max = 0.0;
\r
1973 Control freetime ratio
\r
1975 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1977 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1978 g_freetime_ratio += 0.01;
\r
1982 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1983 g_freetime_ratio -= 0.01;
\r
1989 Busytime average and jitter calculation
\r
1992 static f32 busytime_avg1 = 0.0;
\r
1993 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1994 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1996 static f32 busytime_jitter1_max_sample = 0.0;
\r
1997 static f32 busytime_jitter1_min_sample = 0.0;
\r
1999 static f32 jitter1_max = 0.0;
\r
2000 static f32 jitter1_min = 0.0;
\r
2001 static f32 counter = 0.0;
\r
2002 if(busytime_jitter1 > jitter1_max)
\r
2003 jitter1_max = busytime_jitter1;
\r
2004 if(busytime_jitter1 < jitter1_min)
\r
2005 jitter1_min = busytime_jitter1;
\r
2007 if(counter > 0.0){
\r
2009 busytime_jitter1_max_sample = jitter1_max;
\r
2010 busytime_jitter1_min_sample = jitter1_min;
\r
2011 jitter1_max = 0.0;
\r
2012 jitter1_min = 0.0;
\r
2017 Debug info for client
\r
2020 static float counter = 0.0;
\r
2025 client.printDebugInfo(std::cout);
\r
2030 Input handler step()
\r
2032 g_input->step(dtime);
\r
2035 Player speed control
\r
2044 bool a_superspeed,
\r
2047 PlayerControl control(
\r
2048 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2049 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2050 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2051 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2052 g_input->isKeyDown(irr::KEY_SPACE),
\r
2053 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2057 client.setPlayerControl(control);
\r
2061 Process environment
\r
2065 //TimeTaker timer("client.step(dtime)");
\r
2066 client.step(dtime);
\r
2067 //client.step(dtime_avg1);
\r
2070 if(server != NULL)
\r
2072 //TimeTaker timer("server->step(dtime)");
\r
2073 server->step(dtime);
\r
2076 v3f player_position = client.getPlayerPosition();
\r
2078 //TimeTaker //timer2("//timer2");
\r
2081 Mouse and camera control
\r
2084 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2087 device->getCursorControl()->setVisible(false);
\r
2089 if(first_loop_after_window_activation){
\r
2090 //std::cout<<"window active, first loop"<<std::endl;
\r
2091 first_loop_after_window_activation = false;
\r
2094 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2095 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2096 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2097 camera_yaw -= dx*0.2;
\r
2098 camera_pitch += dy*0.2;
\r
2099 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2100 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2102 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2105 device->getCursorControl()->setVisible(true);
\r
2107 //std::cout<<"window inactive"<<std::endl;
\r
2108 first_loop_after_window_activation = true;
\r
2111 camera_yaw = wrapDegrees(camera_yaw);
\r
2112 camera_pitch = wrapDegrees(camera_pitch);
\r
2114 v3f camera_direction = v3f(0,0,1);
\r
2115 camera_direction.rotateYZBy(camera_pitch);
\r
2116 camera_direction.rotateXZBy(camera_yaw);
\r
2118 // This is at the height of the eyes of the current figure
\r
2119 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2120 // This is more like in minecraft
\r
2121 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2123 camera->setPosition(camera_position);
\r
2124 // *100.0 helps in large map coordinates
\r
2125 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2127 if(FIELD_OF_VIEW_TEST){
\r
2128 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2129 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2132 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2133 //TimeTaker timer("client.updateCamera");
\r
2134 client.updateCamera(camera_position, camera_direction);
\r
2138 //TimeTaker //timer3("//timer3");
\r
2141 Calculate what block is the crosshair pointing to
\r
2144 //u32 t1 = device->getTimer()->getRealTime();
\r
2146 //f32 d = 4; // max. distance
\r
2147 f32 d = 4; // max. distance
\r
2148 core::line3d<f32> shootline(camera_position,
\r
2149 camera_position + camera_direction * BS * (d+1));
\r
2151 MapBlockObject *selected_object = client.getSelectedObject
\r
2152 (d*BS, camera_position, shootline);
\r
2155 If it's pointing to a MapBlockObject
\r
2158 if(selected_object != NULL)
\r
2160 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2162 core::aabbox3d<f32> box_on_map
\r
2163 = selected_object->getSelectionBoxOnMap();
\r
2165 hilightboxes.push_back(box_on_map);
\r
2167 infotext = narrow_to_wide(selected_object->infoText());
\r
2169 if(g_input->getLeftClicked())
\r
2171 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2172 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2173 selected_object->getId(), g_selected_item);
\r
2175 else if(g_input->getRightClicked())
\r
2177 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2179 Check if we want to modify the object ourselves
\r
2181 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2183 dstream<<"Sign object right-clicked"<<std::endl;
\r
2185 if(random_input == false)
\r
2187 // Get a new text for it
\r
2189 TextDest *dest = new TextDestSign(
\r
2190 selected_object->getBlock()->getPos(),
\r
2191 selected_object->getId(),
\r
2194 SignObject *sign_object = (SignObject*)selected_object;
\r
2196 std::wstring wtext =
\r
2197 narrow_to_wide(sign_object->getText());
\r
2199 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2205 Otherwise pass the event to the server as-is
\r
2209 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2210 selected_object->getId(), g_selected_item);
\r
2214 else // selected_object == NULL
\r
2218 Find out which node we are pointing at
\r
2221 bool nodefound = false;
\r
2223 v3s16 neighbourpos;
\r
2224 core::aabbox3d<f32> nodefacebox;
\r
2225 f32 mindistance = BS * 1001;
\r
2227 v3s16 pos_i = floatToInt(player_position);
\r
2229 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2233 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2234 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2235 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2236 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2237 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2238 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2240 for(s16 y = ystart; y <= yend; y++)
\r
2241 for(s16 z = zstart; z <= zend; z++)
\r
2242 for(s16 x = xstart; x <= xend; x++)
\r
2247 n = client.getNode(v3s16(x,y,z));
\r
2248 if(content_pointable(n.d) == false)
\r
2251 catch(InvalidPositionException &e)
\r
2257 v3f npf = intToFloat(np);
\r
2262 v3s16(0,0,1), // back
\r
2263 v3s16(0,1,0), // top
\r
2264 v3s16(1,0,0), // right
\r
2265 v3s16(0,0,-1), // front
\r
2266 v3s16(0,-1,0), // bottom
\r
2267 v3s16(-1,0,0), // left
\r
2273 if(n.d == CONTENT_TORCH)
\r
2275 v3s16 dir = unpackDir(n.dir);
\r
2276 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2277 dir_f *= BS/2 - BS/6 - BS/20;
\r
2278 v3f cpf = npf + dir_f;
\r
2279 f32 distance = (cpf - camera_position).getLength();
\r
2281 core::aabbox3d<f32> box;
\r
2284 if(dir == v3s16(0,-1,0))
\r
2286 box = core::aabbox3d<f32>(
\r
2287 npf - v3f(BS/6, BS/2, BS/6),
\r
2288 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2292 else if(dir == v3s16(0,1,0))
\r
2294 box = core::aabbox3d<f32>(
\r
2295 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2296 npf + v3f(BS/6, BS/2, BS/6)
\r
2302 box = core::aabbox3d<f32>(
\r
2303 cpf - v3f(BS/6, BS/3, BS/6),
\r
2304 cpf + v3f(BS/6, BS/3, BS/6)
\r
2308 if(distance < mindistance)
\r
2310 if(box.intersectsWithLine(shootline))
\r
2314 neighbourpos = np;
\r
2315 mindistance = distance;
\r
2316 nodefacebox = box;
\r
2325 for(u16 i=0; i<6; i++)
\r
2327 v3f dir_f = v3f(dirs[i].X,
\r
2328 dirs[i].Y, dirs[i].Z);
\r
2329 v3f centerpoint = npf + dir_f * BS/2;
\r
2331 (centerpoint - camera_position).getLength();
\r
2333 if(distance < mindistance)
\r
2335 core::CMatrix4<f32> m;
\r
2336 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2338 // This is the back face
\r
2339 v3f corners[2] = {
\r
2340 v3f(BS/2, BS/2, BS/2),
\r
2341 v3f(-BS/2, -BS/2, BS/2+d)
\r
2344 for(u16 j=0; j<2; j++)
\r
2346 m.rotateVect(corners[j]);
\r
2347 corners[j] += npf;
\r
2350 core::aabbox3d<f32> facebox(corners[0]);
\r
2351 facebox.addInternalPoint(corners[1]);
\r
2353 if(facebox.intersectsWithLine(shootline))
\r
2357 neighbourpos = np + dirs[i];
\r
2358 mindistance = distance;
\r
2359 nodefacebox = facebox;
\r
2361 } // if distance < mindistance
\r
2363 } // regular block
\r
2366 static float nodig_delay_counter = 0.0;
\r
2370 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2372 static float dig_time = 0.0;
\r
2373 static u16 dig_index = 0;
\r
2375 hilightboxes.push_back(nodefacebox);
\r
2377 if(g_input->getLeftReleased())
\r
2379 client.clearTempMod(nodepos);
\r
2383 if(nodig_delay_counter > 0.0)
\r
2385 nodig_delay_counter -= dtime;
\r
2389 if(nodepos != nodepos_old)
\r
2391 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2392 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2394 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2396 client.clearTempMod(nodepos_old);
\r
2401 if(g_input->getLeftClicked() ||
\r
2402 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2404 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2405 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2407 if(g_input->getLeftClicked())
\r
2409 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2411 if(g_input->getLeftState())
\r
2413 MapNode n = client.getNode(nodepos);
\r
2415 // Get tool name. Default is "" = bare hands
\r
2416 std::string toolname = "";
\r
2417 InventoryList *mlist = local_inventory.getList("main");
\r
2420 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2421 if(item && (std::string)item->getName() == "ToolItem")
\r
2423 ToolItem *titem = (ToolItem*)item;
\r
2424 toolname = titem->getToolName();
\r
2428 // Get digging properties for material and tool
\r
2429 u8 material = n.d;
\r
2430 DiggingProperties prop =
\r
2431 getDiggingProperties(material, toolname);
\r
2433 float dig_time_complete = 0.0;
\r
2435 if(prop.diggable == false)
\r
2437 /*dstream<<"Material "<<(int)material
\r
2438 <<" not diggable with \""
\r
2439 <<toolname<<"\""<<std::endl;*/
\r
2440 // I guess nobody will wait for this long
\r
2441 dig_time_complete = 10000000.0;
\r
2445 dig_time_complete = prop.time;
\r
2448 if(dig_time_complete >= 0.001)
\r
2450 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2451 * dig_time/dig_time_complete);
\r
2453 // This is for torches
\r
2456 dig_index = CRACK_ANIMATION_LENGTH;
\r
2459 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2461 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2462 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2466 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2467 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2468 client.clearTempMod(nodepos);
\r
2469 client.removeNode(nodepos);
\r
2473 nodig_delay_counter = dig_time_complete
\r
2474 / (float)CRACK_ANIMATION_LENGTH;
\r
2476 // We don't want a corresponding delay to
\r
2477 // very time consuming nodes
\r
2478 if(nodig_delay_counter > 0.5)
\r
2480 nodig_delay_counter = 0.5;
\r
2482 // We want a slight delay to very little
\r
2483 // time consuming nodes
\r
2484 //float mindelay = 0.15;
\r
2485 float mindelay = 0.20;
\r
2486 if(nodig_delay_counter < mindelay)
\r
2488 nodig_delay_counter = mindelay;
\r
2492 dig_time += dtime;
\r
2496 if(g_input->getRightClicked())
\r
2498 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2499 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2502 nodepos_old = nodepos;
\r
2507 } // selected_object == NULL
\r
2509 g_input->resetLeftClicked();
\r
2510 g_input->resetRightClicked();
\r
2512 if(g_input->getLeftReleased())
\r
2514 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2516 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2518 if(g_input->getRightReleased())
\r
2520 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2524 g_input->resetLeftReleased();
\r
2525 g_input->resetRightReleased();
\r
2528 Calculate stuff for drawing
\r
2531 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2533 u32 daynight_ratio = client.getDayNightRatio();
\r
2534 /*video::SColor bgcolor = video::SColor(
\r
2536 skycolor.getRed() * daynight_ratio / 1000,
\r
2537 skycolor.getGreen() * daynight_ratio / 1000,
\r
2538 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2540 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2541 video::SColor bgcolor = video::SColor(
\r
2543 skycolor.getRed() * l / 255,
\r
2544 skycolor.getGreen() * l / 255,
\r
2545 skycolor.getBlue() * l / 255);
\r
2551 if(g_settings.getBool("enable_fog") == true)
\r
2553 f32 range = draw_control.wanted_range * BS;
\r
2554 if(draw_control.range_all)
\r
2555 range = 100000*BS;
\r
2559 video::EFT_FOG_LINEAR,
\r
2563 false, // pixel fog
\r
2564 false // range fog
\r
2570 Update gui stuff (0ms)
\r
2573 //TimeTaker guiupdatetimer("Gui updating");
\r
2576 wchar_t temptext[150];
\r
2578 static float drawtime_avg = 0;
\r
2579 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2580 static float beginscenetime_avg = 0;
\r
2581 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2582 static float scenetime_avg = 0;
\r
2583 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2584 static float endscenetime_avg = 0;
\r
2585 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2587 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2589 L", R: range_all=%i"
\r
2591 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2593 draw_control.range_all,
\r
2595 beginscenetime_avg,
\r
2600 guitext->setText(temptext);
\r
2604 wchar_t temptext[150];
\r
2605 swprintf(temptext, 150,
\r
2606 L"(% .1f, % .1f, % .1f)"
\r
2607 L" (% .3f < btime_jitter < % .3f"
\r
2608 L", dtime_jitter = % .1f %%"
\r
2609 L", v_range = %.1f)",
\r
2610 player_position.X/BS,
\r
2611 player_position.Y/BS,
\r
2612 player_position.Z/BS,
\r
2613 busytime_jitter1_min_sample,
\r
2614 busytime_jitter1_max_sample,
\r
2615 dtime_jitter1_max_fraction * 100.0,
\r
2616 draw_control.wanted_range
\r
2619 guitext2->setText(temptext);
\r
2623 guitext_info->setText(infotext.c_str());
\r
2627 Get chat messages from client
\r
2630 // Get new messages
\r
2631 std::wstring message;
\r
2632 while(client.getChatMessage(message))
\r
2634 chat_lines.push_back(ChatLine(message));
\r
2635 /*if(chat_lines.size() > 6)
\r
2637 core::list<ChatLine>::Iterator
\r
2638 i = chat_lines.begin();
\r
2639 chat_lines.erase(i);
\r
2642 // Append them to form the whole static text and throw
\r
2643 // it to the gui element
\r
2644 std::wstring whole;
\r
2645 // This will correspond to the line number counted from
\r
2646 // top to bottom, from size-1 to 0
\r
2647 s16 line_number = chat_lines.size();
\r
2648 // Count of messages to be removed from the top
\r
2649 u16 to_be_removed_count = 0;
\r
2650 for(core::list<ChatLine>::Iterator
\r
2651 i = chat_lines.begin();
\r
2652 i != chat_lines.end(); i++)
\r
2654 // After this, line number is valid for this loop
\r
2657 (*i).age += dtime;
\r
2659 This results in a maximum age of 60*6 to the
\r
2660 lowermost line and a maximum of 6 lines
\r
2662 float allowed_age = (6-line_number) * 60.0;
\r
2664 if((*i).age > allowed_age)
\r
2666 to_be_removed_count++;
\r
2669 whole += (*i).text + L'\n';
\r
2671 for(u16 i=0; i<to_be_removed_count; i++)
\r
2673 core::list<ChatLine>::Iterator
\r
2674 it = chat_lines.begin();
\r
2675 chat_lines.erase(it);
\r
2677 guitext_chat->setText(whole.c_str());
\r
2678 // Update gui element size and position
\r
2679 core::rect<s32> rect(
\r
2681 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2682 screensize.X - 10,
\r
2685 guitext_chat->setRelativePosition(rect);
\r
2687 if(chat_lines.size() == 0)
\r
2688 guitext_chat->setVisible(false);
\r
2690 guitext_chat->setVisible(true);
\r
2697 static u16 old_selected_item = 65535;
\r
2698 if(client.getLocalInventoryUpdated()
\r
2699 || g_selected_item != old_selected_item)
\r
2701 old_selected_item = g_selected_item;
\r
2702 //std::cout<<"Updating local inventory"<<std::endl;
\r
2703 client.getLocalInventory(local_inventory);
\r
2704 quick_inventory->setSelection(g_selected_item);
\r
2705 quick_inventory->update();
\r
2709 Send actions returned by the inventory menu
\r
2711 while(inventory_action_queue.size() != 0)
\r
2713 InventoryAction *a = inventory_action_queue.pop_front();
\r
2715 client.sendInventoryAction(a);
\r
2724 TimeTaker drawtimer("Drawing");
\r
2728 TimeTaker timer("beginScene");
\r
2729 driver->beginScene(true, true, bgcolor);
\r
2730 //driver->beginScene(false, true, bgcolor);
\r
2731 beginscenetime = timer.stop(true);
\r
2736 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2739 TimeTaker timer("smgr");
\r
2741 scenetime = timer.stop(true);
\r
2745 //TimeTaker timer9("auxiliary drawings");
\r
2749 //TimeTaker //timer10("//timer10");
\r
2751 video::SMaterial m;
\r
2752 //m.Thickness = 10;
\r
2754 m.Lighting = false;
\r
2755 driver->setMaterial(m);
\r
2757 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2759 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2760 i != hilightboxes.end(); i++)
\r
2762 /*std::cout<<"hilightbox min="
\r
2763 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2765 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2767 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2773 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2774 displaycenter + core::vector2d<s32>(10,0),
\r
2775 video::SColor(255,255,255,255));
\r
2776 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2777 displaycenter + core::vector2d<s32>(0,10),
\r
2778 video::SColor(255,255,255,255));
\r
2783 //TimeTaker //timer11("//timer11");
\r
2789 guienv->drawAll();
\r
2793 TimeTaker timer("endScene");
\r
2794 driver->endScene();
\r
2795 endscenetime = timer.stop(true);
\r
2798 drawtime = drawtimer.stop(true);
\r
2804 static s16 lastFPS = 0;
\r
2805 //u16 fps = driver->getFPS();
\r
2806 u16 fps = (1.0/dtime_avg1);
\r
2808 if (lastFPS != fps)
\r
2810 core::stringw str = L"Minetest [";
\r
2811 str += driver->getName();
\r
2815 device->setWindowCaption(str.c_str());
\r
2821 device->yield();*/
\r
2824 delete quick_inventory;
\r
2826 } // client and server are deleted at this point
\r
2829 catch(con::PeerNotFoundException &e)
\r
2831 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2832 error_message = L"Connection timed out.";
\r
2835 } // Menu-game loop
\r
2840 In the end, delete the Irrlicht device.
\r
2845 Update configuration file
\r
2847 /*if(configpath != "")
\r
2849 g_settings.updateConfigFile(configpath.c_str());
\r
2852 END_DEBUG_EXCEPTION_HANDLER
\r
2854 debugstreams_deinit();
\r