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
156 - Actually if FastFaces would be stored, they could
\r
159 FEATURE: Combine MapBlock's face caches to so big pieces that VBO
\r
161 - That is >500 vertices
\r
162 - This is not easy; all the MapBlocks close to the player would
\r
163 still need to be drawn separately and combining the blocks
\r
164 would have to happen in a background thread
\r
166 TODO: Make fetching sector's blocks more efficient when rendering
\r
167 sectors that have very large amounts of blocks (on client)
\r
168 - Is this necessary at all?
\r
170 TODO: Flowing water animation
\r
175 TODO: Make the video backend selectable
\r
180 TODO: Untie client network operations from framerate
\r
181 - Needs some input queues or something
\r
182 - Not really necessary?
\r
184 TODO: Make morning and evening shorter
\r
186 TODO: Don't update all meshes always on single node changes, but
\r
187 check which ones should be updated
\r
188 - implement Map::updateNodeMeshes()
\r
193 TODO: When player dies, throw items on map
\r
195 TODO: Make an option to the server to disable building and digging near
\r
196 the starting position
\r
198 TODO: Save players with inventories to disk
\r
199 TODO: Players to be saved as text in map/players/<name>
\r
201 TODO: Copy the text of the last picked sign to inventory in creative
\r
204 TODO: Check what goes wrong with caching map to disk (Kray)
\r
207 TODO: When server sees that client is removing an inexistent block or
\r
208 adding a block to an existent position, resend the MapBlock.
\r
213 TODO: Better handling of objects and mobs
\r
215 - There has to be some way to do it with less messy code
\r
216 - Make separate classes for client and server
\r
217 - Client should not discriminate between blocks, server should
\r
218 - Make other players utilize the same framework
\r
219 - This is also needed for objects that don't get sent to client
\r
220 but are used for triggers etc
\r
222 TODO: There has to be some better way to handle static objects than to
\r
223 send them all the time. This affects signs and item objects.
\r
224 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
225 need an additional metadata field for the texts
\r
226 - This is also needed for item container chests
\r
228 Block object server side:
\r
229 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
230 - For all blocks in the buffer, objects are stepped(). This
\r
231 means they are active.
\r
232 - TODO: A global active buffer is needed for the server
\r
233 - TODO: A timestamp to blocks
\r
234 - TODO: All blocks going in and out of the buffer are recorded.
\r
235 - TODO: For outgoing blocks, timestamp is written.
\r
236 - TODO: For incoming blocks, time difference is calculated and
\r
237 objects are stepped according to it.
\r
242 NOTE: There are some lighting-related todos and fixmes in
\r
243 ServerMap::emergeBlock. And there always will be. 8)
\r
245 FEATURE: Map generator version 2
\r
246 - Create surface areas based on central points; a given point's
\r
247 area type is given by the nearest central point
\r
248 - Separate points for heightmap, caves, plants and minerals?
\r
249 - Flat land, mountains, forest, jungle
\r
251 - There could be a certain height (to which mountains only reach)
\r
252 where some minerals are found
\r
253 - Create a system that allows a huge amount of different "map
\r
254 generator modules/filters"
\r
256 FEATURE: The map could be generated procedually:
\r
257 - This would need the map to be generated in larger pieces
\r
258 - How large? How do they connect to each other?
\r
259 * Make the stone level with a heightmap
\r
260 * Carve out stuff in the stone
\r
261 * Dump dirt all around, and simulate it falling off steep
\r
263 * Erosion simulation at map generation time
\r
264 - Simulate water flows, which would carve out dirt fast and
\r
265 then turn stone into gravel and sand and relocate it.
\r
266 - How about relocating minerals, too? Coal and gold in
\r
267 downstream sand and gravel would be kind of cool
\r
268 - This would need a better way of handling minerals, mainly
\r
269 to have mineral content as a separate field. the first
\r
270 parameter field is free for this.
\r
271 - Simulate rock falling from cliffs when water has removed
\r
272 enough solid rock from the bottom
\r
274 TODO: Mineral and ground material properties
\r
275 - This way mineral ground toughness can be calculated with just
\r
276 some formula, as well as tool strengths
\r
278 TODO: Change AttributeList to split the area into smaller sections so
\r
279 that searching won't be as heavy.
\r
281 TODO: Remove HMParams
\r
283 TODO: Flowing water to actually contain flow direction information
\r
285 TODO: Remove duplicate lighting implementation from Map (leave
\r
286 VoxelManipulator, which is faster)
\r
288 FIXME: The new texture stuff is slow on wine
\r
289 - Actually it is not too slow; updating excess amount of meshes
\r
290 when making footprints is too slow. It has to be fixed.
\r
291 -> implement Map::updateNodeMeshes()
\r
296 ======================================================================
\r
301 Setting this to 1 enables a special camera mode that forces
\r
302 the renderers to think that the camera statically points from
\r
303 the starting place to a static direction.
\r
305 This allows one to move around with the player and see what
\r
306 is actually drawn behind solid things and behind the player.
\r
308 #define FIELD_OF_VIEW_TEST 0
\r
312 #pragma message ("Disabling unit tests")
\r
314 #warning "Disabling unit tests"
\r
316 // Disable unit tests
\r
317 #define ENABLE_TESTS 0
\r
319 // Enable unit tests
\r
320 #define ENABLE_TESTS 1
\r
324 #pragma comment(lib, "Irrlicht.lib")
\r
325 //#pragma comment(lib, "jthread.lib")
\r
326 #pragma comment(lib, "zlibwapi.lib")
\r
327 #pragma comment(lib, "Shell32.lib")
\r
328 // This would get rid of the console window
\r
329 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
332 #include <iostream>
\r
334 #include <jmutexautolock.h>
\r
335 #include <locale.h>
\r
336 #include "common_irrlicht.h"
\r
339 #include "player.h"
\r
342 #include "environment.h"
\r
343 #include "server.h"
\r
344 #include "client.h"
\r
345 #include "serialization.h"
\r
346 #include "constants.h"
\r
347 #include "strfnd.h"
\r
348 #include "porting.h"
\r
349 #include "irrlichtwrapper.h"
\r
350 #include "gettime.h"
\r
351 #include "porting.h"
\r
352 #include "guiPauseMenu.h"
\r
353 #include "guiInventoryMenu.h"
\r
354 #include "guiTextInputMenu.h"
\r
355 #include "materials.h"
\r
356 #include "guiMessageMenu.h"
\r
357 #include "filesys.h"
\r
358 #include "config.h"
\r
359 #include "guiMainMenu.h"
\r
361 IrrlichtWrapper *g_irrlicht;
\r
363 MapDrawControl draw_control;
\r
367 These are loaded from the config file.
\r
370 Settings g_settings;
\r
372 extern void set_default_settings();
\r
378 IrrlichtDevice *g_device = NULL;
\r
379 Client *g_client = NULL;
\r
385 gui::IGUIEnvironment* guienv = NULL;
\r
386 gui::IGUIStaticText *guiroot = NULL;
\r
388 class MainMenuManager : public IMenuManager
\r
391 virtual void createdMenu(GUIModalMenu *menu)
\r
393 for(core::list<GUIModalMenu*>::Iterator
\r
394 i = m_stack.begin();
\r
395 i != m_stack.end(); i++)
\r
397 assert(*i != menu);
\r
400 if(m_stack.size() != 0)
\r
401 (*m_stack.getLast())->setVisible(false);
\r
402 m_stack.push_back(menu);
\r
405 virtual void deletingMenu(GUIModalMenu *menu)
\r
407 // Remove all entries if there are duplicates
\r
408 bool removed_entry;
\r
410 removed_entry = false;
\r
411 for(core::list<GUIModalMenu*>::Iterator
\r
412 i = m_stack.begin();
\r
413 i != m_stack.end(); i++)
\r
418 removed_entry = true;
\r
422 }while(removed_entry);
\r
424 /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
\r
425 assert(*i == menu);
\r
426 m_stack.erase(i);*/
\r
428 if(m_stack.size() != 0)
\r
429 (*m_stack.getLast())->setVisible(true);
\r
434 return m_stack.size();
\r
437 core::list<GUIModalMenu*> m_stack;
\r
440 MainMenuManager g_menumgr;
\r
442 bool noMenuActive()
\r
444 return (g_menumgr.menuCount() == 0);
\r
447 bool g_disconnect_requested = false;
\r
449 class MainGameCallback : public IGameCallback
\r
452 virtual void exitToOS()
\r
454 g_device->closeDevice();
\r
457 virtual void disconnect()
\r
459 g_disconnect_requested = true;
\r
463 MainGameCallback g_gamecallback;
\r
465 // Inventory actions from the menu are buffered here before sending
\r
466 Queue<InventoryAction*> inventory_action_queue;
\r
467 // This is a copy of the inventory that the client's environment has
\r
468 Inventory local_inventory;
\r
470 u16 g_selected_item = 0;
\r
477 std::ostream *dout_con_ptr = &dummyout;
\r
478 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
479 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
480 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
481 //std::ostream *dout_con_ptr = &dstream;
\r
482 //std::ostream *derr_con_ptr = &dstream;
\r
485 std::ostream *dout_server_ptr = &dstream;
\r
486 std::ostream *derr_server_ptr = &dstream;
\r
489 std::ostream *dout_client_ptr = &dstream;
\r
490 std::ostream *derr_client_ptr = &dstream;
\r
493 gettime.h implementation
\r
499 Use irrlicht because it is more precise than porting.h's
\r
502 if(g_irrlicht == NULL)
\r
504 return g_irrlicht->getTime();
\r
511 struct TextDestSign : public TextDest
\r
513 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
515 m_blockpos = blockpos;
\r
519 void gotText(std::wstring text)
\r
521 std::string ntext = wide_to_narrow(text);
\r
522 dstream<<"Changing text of a sign object: "
\r
523 <<ntext<<std::endl;
\r
524 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
532 struct TextDestChat : public TextDest
\r
534 TextDestChat(Client *client)
\r
538 void gotText(std::wstring text)
\r
540 m_client->sendChatMessage(text);
\r
541 m_client->addChatMessage(text);
\r
547 class MyEventReceiver : public IEventReceiver
\r
550 // This is the one method that we have to implement
\r
551 virtual bool OnEvent(const SEvent& event)
\r
554 React to nothing here if a menu is active
\r
556 if(noMenuActive() == false)
\r
562 // Remember whether each key is down or up
\r
563 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
565 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
567 if(event.KeyInput.PressedDown)
\r
569 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
575 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
577 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
579 dstream<<DTIME<<"MyEventReceiver: "
\r
580 <<"Launching pause menu"<<std::endl;
\r
581 // It will delete itself by itself
\r
582 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
583 &g_menumgr))->drop();
\r
586 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
588 dstream<<DTIME<<"MyEventReceiver: "
\r
589 <<"Launching inventory"<<std::endl;
\r
590 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
591 &local_inventory, &inventory_action_queue,
\r
592 &g_menumgr))->drop();
\r
595 if(event.KeyInput.Key == irr::KEY_KEY_T)
\r
597 TextDest *dest = new TextDestChat(g_client);
\r
599 (new GUITextInputMenu(guienv, guiroot, -1,
\r
605 // Material selection
\r
606 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
608 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
611 g_selected_item = 0;
\r
612 dstream<<DTIME<<"Selected item: "
\r
613 <<g_selected_item<<std::endl;
\r
616 // Viewing range selection
\r
617 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
619 if(draw_control.range_all)
\r
621 draw_control.range_all = false;
\r
622 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
626 draw_control.range_all = true;
\r
627 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
631 // Print debug stacks
\r
632 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
634 dstream<<"-----------------------------------------"
\r
636 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
637 dstream<<"-----------------------------------------"
\r
639 debug_stacks_print();
\r
644 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
646 if(noMenuActive() == false)
\r
648 left_active = false;
\r
649 middle_active = false;
\r
650 right_active = false;
\r
654 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
655 left_active = event.MouseInput.isLeftPressed();
\r
656 middle_active = event.MouseInput.isMiddlePressed();
\r
657 right_active = event.MouseInput.isRightPressed();
\r
659 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
661 leftclicked = true;
\r
663 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
665 rightclicked = true;
\r
667 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
669 leftreleased = true;
\r
671 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
673 rightreleased = true;
\r
675 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
677 /*dstream<<"event.MouseInput.Wheel="
\r
678 <<event.MouseInput.Wheel<<std::endl;*/
\r
679 if(event.MouseInput.Wheel < 0)
\r
681 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
684 g_selected_item = 0;
\r
686 else if(event.MouseInput.Wheel > 0)
\r
688 if(g_selected_item > 0)
\r
691 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
700 // This is used to check whether a key is being held down
\r
701 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
703 return keyIsDown[keyCode];
\r
708 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
709 keyIsDown[i] = false;
\r
711 leftclicked = false;
\r
712 rightclicked = false;
\r
713 leftreleased = false;
\r
714 rightreleased = false;
\r
716 left_active = false;
\r
717 middle_active = false;
\r
718 right_active = false;
\r
729 bool rightreleased;
\r
732 bool middle_active;
\r
736 // We use this array to store the current state of each key
\r
737 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
740 IrrlichtDevice *m_device;
\r
749 virtual ~InputHandler()
\r
753 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
755 virtual v2s32 getMousePos() = 0;
\r
756 virtual void setMousePos(s32 x, s32 y) = 0;
\r
758 virtual bool getLeftState() = 0;
\r
759 virtual bool getRightState() = 0;
\r
761 virtual bool getLeftClicked() = 0;
\r
762 virtual bool getRightClicked() = 0;
\r
763 virtual void resetLeftClicked() = 0;
\r
764 virtual void resetRightClicked() = 0;
\r
766 virtual bool getLeftReleased() = 0;
\r
767 virtual bool getRightReleased() = 0;
\r
768 virtual void resetLeftReleased() = 0;
\r
769 virtual void resetRightReleased() = 0;
\r
771 virtual void step(float dtime) {};
\r
773 virtual void clear() {};
\r
776 InputHandler *g_input = NULL;
\r
778 class RealInputHandler : public InputHandler
\r
781 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
783 m_receiver(receiver)
\r
786 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
788 return m_receiver->IsKeyDown(keyCode);
\r
790 virtual v2s32 getMousePos()
\r
792 return m_device->getCursorControl()->getPosition();
\r
794 virtual void setMousePos(s32 x, s32 y)
\r
796 m_device->getCursorControl()->setPosition(x, y);
\r
799 virtual bool getLeftState()
\r
801 return m_receiver->left_active;
\r
803 virtual bool getRightState()
\r
805 return m_receiver->right_active;
\r
808 virtual bool getLeftClicked()
\r
810 return m_receiver->leftclicked;
\r
812 virtual bool getRightClicked()
\r
814 return m_receiver->rightclicked;
\r
816 virtual void resetLeftClicked()
\r
818 m_receiver->leftclicked = false;
\r
820 virtual void resetRightClicked()
\r
822 m_receiver->rightclicked = false;
\r
825 virtual bool getLeftReleased()
\r
827 return m_receiver->leftreleased;
\r
829 virtual bool getRightReleased()
\r
831 return m_receiver->rightreleased;
\r
833 virtual void resetLeftReleased()
\r
835 m_receiver->leftreleased = false;
\r
837 virtual void resetRightReleased()
\r
839 m_receiver->rightreleased = false;
\r
844 resetRightClicked();
\r
845 resetLeftClicked();
\r
848 IrrlichtDevice *m_device;
\r
849 MyEventReceiver *m_receiver;
\r
852 class RandomInputHandler : public InputHandler
\r
855 RandomInputHandler()
\r
857 leftclicked = false;
\r
858 rightclicked = false;
\r
859 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
860 keydown[i] = false;
\r
862 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
864 return keydown[keyCode];
\r
866 virtual v2s32 getMousePos()
\r
870 virtual void setMousePos(s32 x, s32 y)
\r
872 mousepos = v2s32(x,y);
\r
875 virtual bool getLeftState()
\r
879 virtual bool getRightState()
\r
884 virtual bool getLeftClicked()
\r
886 return leftclicked;
\r
888 virtual bool getRightClicked()
\r
890 return rightclicked;
\r
892 virtual void resetLeftClicked()
\r
894 leftclicked = false;
\r
896 virtual void resetRightClicked()
\r
898 rightclicked = false;
\r
901 virtual bool getLeftReleased()
\r
905 virtual bool getRightReleased()
\r
909 virtual void resetLeftReleased()
\r
912 virtual void resetRightReleased()
\r
916 virtual void step(float dtime)
\r
919 static float counter1 = 0;
\r
923 counter1 = 0.1*Rand(1,10);
\r
924 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
925 g_selected_material++;
\r
927 g_selected_material = 0;*/
\r
928 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
931 g_selected_item = 0;
\r
935 static float counter1 = 0;
\r
939 counter1 = 0.1*Rand(1, 40);
\r
940 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
944 static float counter1 = 0;
\r
948 counter1 = 0.1*Rand(1, 40);
\r
949 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
953 static float counter1 = 0;
\r
957 counter1 = 0.1*Rand(1, 40);
\r
958 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
962 static float counter1 = 0;
\r
966 counter1 = 0.1*Rand(1, 40);
\r
967 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
971 static float counter1 = 0;
\r
975 counter1 = 0.1*Rand(1, 20);
\r
976 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
980 static float counter1 = 0;
\r
984 counter1 = 0.1*Rand(1, 30);
\r
985 leftclicked = true;
\r
989 static float counter1 = 0;
\r
993 counter1 = 0.1*Rand(1, 20);
\r
994 rightclicked = true;
\r
997 mousepos += mousespeed;
\r
1000 s32 Rand(s32 min, s32 max)
\r
1002 return (myrand()%(max-min+1))+min;
\r
1005 bool keydown[KEY_KEY_CODES_COUNT];
\r
1009 bool rightclicked;
\r
1012 void updateViewingRange(f32 frametime_in, Client *client)
\r
1014 if(draw_control.range_all == true)
\r
1017 static f32 added_frametime = 0;
\r
1018 static s16 added_frames = 0;
\r
1020 added_frametime += frametime_in;
\r
1021 added_frames += 1;
\r
1023 // Actually this counter kind of sucks because frametime is busytime
\r
1024 static f32 counter = 0;
\r
1025 counter -= frametime_in;
\r
1031 /*dstream<<__FUNCTION_NAME
\r
1032 <<": Collected "<<added_frames<<" frames, total of "
\r
1033 <<added_frametime<<"s."<<std::endl;*/
\r
1035 /*dstream<<"draw_control.blocks_drawn="
\r
1036 <<draw_control.blocks_drawn
\r
1037 <<", draw_control.blocks_would_have_drawn="
\r
1038 <<draw_control.blocks_would_have_drawn
\r
1041 float range_min = g_settings.getS16("viewing_range_nodes_min");
\r
1042 float range_max = g_settings.getS16("viewing_range_nodes_max");
\r
1044 draw_control.wanted_min_range = range_min;
\r
1045 draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
\r
1047 float block_draw_ratio = 1.0;
\r
1048 if(draw_control.blocks_would_have_drawn != 0)
\r
1050 block_draw_ratio = (float)draw_control.blocks_drawn
\r
1051 / (float)draw_control.blocks_would_have_drawn;
\r
1054 // Calculate the average frametime in the case that all wanted
\r
1055 // blocks had been drawn
\r
1056 f32 frametime = added_frametime / added_frames / block_draw_ratio;
\r
1058 added_frametime = 0.0;
\r
1061 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
1062 float wanted_frametime = 1.0 / wanted_fps;
\r
1064 f32 wanted_frametime_change = wanted_frametime - frametime;
\r
1065 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
\r
1067 // If needed frametime change is very small, just return
\r
1068 if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
\r
1070 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
\r
1074 float range = draw_control.wanted_range;
\r
1075 float new_range = range;
\r
1077 static s16 range_old = 0;
\r
1078 static f32 frametime_old = 0;
\r
1080 float d_range = range - range_old;
\r
1081 f32 d_frametime = frametime - frametime_old;
\r
1082 // A sane default of 30ms per 50 nodes of range
\r
1083 static f32 time_per_range = 30. / 50;
\r
1086 time_per_range = d_frametime / d_range;
\r
1089 // The minimum allowed calculated frametime-range derivative:
\r
1090 // Practically this sets the maximum speed of changing the range.
\r
1091 // The lower this value, the higher the maximum changing speed.
\r
1092 // A low value here results in wobbly range (0.001)
\r
1093 // A high value here results in slow changing range (0.0025)
\r
1094 // SUGG: This could be dynamically adjusted so that when
\r
1095 // the camera is turning, this is lower
\r
1096 //float min_time_per_range = 0.0015;
\r
1097 float min_time_per_range = 0.0010;
\r
1098 //float min_time_per_range = 0.05 / range;
\r
1099 if(time_per_range < min_time_per_range)
\r
1101 time_per_range = min_time_per_range;
\r
1102 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
\r
1106 //dstream<<"time_per_range="<<time_per_range<<std::endl;
\r
1109 f32 wanted_range_change = wanted_frametime_change / time_per_range;
\r
1110 // Dampen the change a bit to kill oscillations
\r
1111 //wanted_range_change *= 0.9;
\r
1112 //wanted_range_change *= 0.75;
\r
1113 wanted_range_change *= 0.5;
\r
1114 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
\r
1116 // If needed range change is very small, just return
\r
1117 if(fabs(wanted_range_change) < 0.001)
\r
1119 //dstream<<"ignoring small wanted_range_change"<<std::endl;
\r
1123 new_range += wanted_range_change;
\r
1124 //dstream<<"new_range="<<new_range/*<<std::endl*/;
\r
1126 //float new_range_unclamped = new_range;
\r
1127 if(new_range < range_min)
\r
1128 new_range = range_min;
\r
1129 if(new_range > range_max)
\r
1130 new_range = range_max;
\r
1132 /*if(new_range != new_range_unclamped)
\r
1133 dstream<<", clamped to "<<new_range<<std::endl;
\r
1135 dstream<<std::endl;*/
\r
1137 draw_control.wanted_range = new_range;
\r
1139 range_old = new_range;
\r
1140 frametime_old = frametime;
\r
1143 class GUIQuickInventory : public IEventReceiver
\r
1146 GUIQuickInventory(
\r
1147 gui::IGUIEnvironment* env,
\r
1148 gui::IGUIElement* parent,
\r
1151 Inventory *inventory):
\r
1152 m_itemcount(itemcount),
\r
1153 m_inventory(inventory)
\r
1155 core::rect<s32> imgsize(0,0,48,48);
\r
1156 core::rect<s32> textsize(0,0,48,16);
\r
1157 v2s32 spacing(0, 64);
\r
1158 for(s32 i=0; i<m_itemcount; i++)
\r
1160 m_images.push_back(env->addImage(
\r
1161 imgsize + pos + spacing*i
\r
1163 m_images[i]->setScaleImage(true);
\r
1164 m_texts.push_back(env->addStaticText(
\r
1166 textsize + pos + spacing*i,
\r
1169 m_texts[i]->setBackgroundColor(
\r
1170 video::SColor(128,0,0,0));
\r
1171 m_texts[i]->setTextAlignment(
\r
1172 gui::EGUIA_CENTER,
\r
1173 gui::EGUIA_UPPERLEFT);
\r
1177 ~GUIQuickInventory()
\r
1179 for(u32 i=0; i<m_texts.size(); i++)
\r
1181 m_texts[i]->remove();
\r
1183 for(u32 i=0; i<m_images.size(); i++)
\r
1185 m_images[i]->remove();
\r
1189 virtual bool OnEvent(const SEvent& event)
\r
1194 void setSelection(s32 i)
\r
1203 start = m_selection - m_itemcount / 2;
\r
1205 InventoryList *mainlist = m_inventory->getList("main");
\r
1207 for(s32 i=0; i<m_itemcount; i++)
\r
1209 s32 j = i + start;
\r
1211 if(j > (s32)mainlist->getSize() - 1)
\r
1212 j -= mainlist->getSize();
\r
1214 j += mainlist->getSize();
\r
1216 InventoryItem *item = mainlist->getItem(j);
\r
1220 m_images[i]->setImage(NULL);
\r
1223 if(m_selection == j)
\r
1224 swprintf(t, 10, L"<-");
\r
1226 swprintf(t, 10, L"");
\r
1227 m_texts[i]->setText(t);
\r
1229 // The next ifs will segfault with a NULL pointer
\r
1234 m_images[i]->setImage(item->getImage());
\r
1237 if(m_selection == j)
\r
1238 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
1240 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
1241 m_texts[i]->setText(t);
\r
1247 core::array<gui::IGUIStaticText*> m_texts;
\r
1248 core::array<gui::IGUIImage*> m_images;
\r
1249 Inventory *m_inventory;
\r
1260 ChatLine(const std::wstring &a_text):
\r
1266 std::wstring text;
\r
1269 int main(int argc, char *argv[])
\r
1272 Low-level initialization
\r
1275 bool disable_stderr = false;
\r
1277 disable_stderr = true;
\r
1280 // Initialize debug streams
\r
1281 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1282 // Initialize debug stacks
\r
1283 debug_stacks_init();
\r
1285 DSTACK(__FUNCTION_NAME);
\r
1287 porting::initializePaths();
\r
1288 // Create user data directory
\r
1289 fs::CreateDir(porting::path_userdata);
\r
1291 // C-style stuff initialization
\r
1292 initializeMaterialProperties();
\r
1296 BEGIN_DEBUG_EXCEPTION_HANDLER
\r
1298 // Print startup message
\r
1299 dstream<<DTIME<<"minetest-c55"
\r
1300 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1301 <<", "<<BUILD_INFO
\r
1305 Parse command line
\r
1308 // List all allowed options
\r
1309 core::map<std::string, ValueSpec> allowed_options;
\r
1310 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1311 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1312 "Run server directly"));
\r
1313 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1314 "Load configuration from specified file"));
\r
1315 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1316 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1317 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1318 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1319 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1320 allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING));
\r
1322 Settings cmd_args;
\r
1324 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1326 if(ret == false || cmd_args.getFlag("help"))
\r
1328 dstream<<"Allowed options:"<<std::endl;
\r
1329 for(core::map<std::string, ValueSpec>::Iterator
\r
1330 i = allowed_options.getIterator();
\r
1331 i.atEnd() == false; i++)
\r
1333 dstream<<" --"<<i.getNode()->getKey();
\r
1334 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1339 dstream<<" <value>";
\r
1341 dstream<<std::endl;
\r
1343 if(i.getNode()->getValue().help != NULL)
\r
1345 dstream<<" "<<i.getNode()->getValue().help
\r
1350 return cmd_args.getFlag("help") ? 0 : 1;
\r
1355 Basic initialization
\r
1358 // Initialize default settings
\r
1359 set_default_settings();
\r
1361 // Set locale. This is for forcing '.' as the decimal point.
\r
1362 std::locale::global(std::locale("C"));
\r
1363 // This enables printing all characters in bitmap font
\r
1364 setlocale(LC_CTYPE, "en_US");
\r
1366 // Initialize sockets
\r
1368 atexit(sockets_cleanup);
\r
1378 // Path of configuration file in use
\r
1379 std::string configpath = "";
\r
1381 if(cmd_args.exists("config"))
\r
1383 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1386 dstream<<"Could not read configuration from \""
\r
1387 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1390 configpath = cmd_args.get("config");
\r
1394 core::array<std::string> filenames;
\r
1395 filenames.push_back(porting::path_userdata + "/minetest.conf");
\r
1396 #ifdef RUN_IN_PLACE
\r
1397 filenames.push_back(porting::path_userdata + "/../minetest.conf");
\r
1400 for(u32 i=0; i<filenames.size(); i++)
\r
1402 bool r = g_settings.readConfigFile(filenames[i].c_str());
\r
1405 configpath = filenames[i];
\r
1411 // Initialize random seed
\r
1418 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1419 || cmd_args.getFlag("enable-unittests") == true)
\r
1424 // Read map parameters from settings
\r
1426 HMParams hm_params;
\r
1427 /*hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1428 hm_params.randmax = g_settings.get("height_randmax");
\r
1429 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1430 hm_params.base = g_settings.get("height_base");*/
\r
1432 MapParams map_params;
\r
1433 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1434 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1442 if(cmd_args.exists("port"))
\r
1443 port = cmd_args.getU16("port");
\r
1444 else if(cmd_args.exists("port"))
\r
1445 port = g_settings.getU16("port");
\r
1448 std::string map_dir = porting::path_userdata+"/map";
\r
1449 if(cmd_args.exists("map-dir"))
\r
1450 map_dir = cmd_args.get("map-dir");
\r
1451 else if(g_settings.exists("map-dir"))
\r
1452 map_dir = g_settings.get("map-dir");
\r
1454 // Run dedicated server if asked to
\r
1455 if(cmd_args.getFlag("server"))
\r
1457 DSTACK("Dedicated server branch");
\r
1460 Server server(map_dir.c_str(), hm_params, map_params);
\r
1461 server.start(port);
\r
1464 dedicated_server_loop(server);
\r
1473 // Address to connect to
\r
1474 std::string address = "";
\r
1476 if(cmd_args.exists("address"))
\r
1478 address = cmd_args.get("address");
\r
1482 address = g_settings.get("address");
\r
1485 std::string playername = g_settings.get("name");
\r
1488 Resolution selection
\r
1491 bool fullscreen = false;
\r
1492 u16 screenW = g_settings.getU16("screenW");
\r
1493 u16 screenH = g_settings.getU16("screenH");
\r
1497 MyEventReceiver receiver;
\r
1499 video::E_DRIVER_TYPE driverType;
\r
1502 //driverType = video::EDT_DIRECT3D9;
\r
1503 driverType = video::EDT_OPENGL;
\r
1505 driverType = video::EDT_OPENGL;
\r
1506 //driverType = video::EDT_BURNINGSVIDEO;
\r
1509 // create device and exit if creation failed
\r
1511 IrrlichtDevice *device;
\r
1512 device = createDevice(driverType,
\r
1513 core::dimension2d<u32>(screenW, screenH),
\r
1514 16, fullscreen, false, false, &receiver);
\r
1517 return 1; // could not create selected driver.
\r
1519 g_device = device;
\r
1520 g_irrlicht = new IrrlichtWrapper(device);
\r
1522 //g_device = device;
\r
1524 device->setResizable(true);
\r
1526 bool random_input = g_settings.getBool("random_input")
\r
1527 || cmd_args.getFlag("random-input");
\r
1529 g_input = new RandomInputHandler();
\r
1531 g_input = new RealInputHandler(device, &receiver);
\r
1534 Continue initialization
\r
1537 video::IVideoDriver* driver = device->getVideoDriver();
\r
1540 This changes the minimum allowed number of vertices in a VBO
\r
1542 //driver->setMinHardwareBufferVertexCount(50);
\r
1544 scene::ISceneManager* smgr = device->getSceneManager();
\r
1546 guienv = device->getGUIEnvironment();
\r
1547 gui::IGUISkin* skin = guienv->getSkin();
\r
1548 gui::IGUIFont* font = guienv->getFont(porting::getDataPath("fontlucida.png").c_str());
\r
1550 skin->setFont(font);
\r
1552 dstream<<"WARNING: Font file was not found."
\r
1553 " Using default font."<<std::endl;
\r
1554 // If font was not found, this will get us one
\r
1555 font = skin->getFont();
\r
1558 u32 text_height = font->getDimension(L"Hello, world!").Height;
\r
1559 dstream<<"text_height="<<text_height<<std::endl;
\r
1561 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1562 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1563 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1564 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1565 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1566 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1569 Preload some textures
\r
1572 init_content_inventory_texture_paths();
\r
1573 //init_tile_textures();
\r
1580 We need some kind of a root node to be able to add
\r
1581 custom gui elements directly on the screen.
\r
1582 Otherwise they won't be automatically drawn.
\r
1584 guiroot = guienv->addStaticText(L"",
\r
1585 core::rect<s32>(0, 0, 10000, 10000));
\r
1587 // First line of debug text
\r
1588 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1590 core::rect<s32>(5, 5, 795, 5+text_height),
\r
1592 // Second line of debug text
\r
1593 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1595 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
\r
1598 // At the middle of the screen
\r
1599 // Object infos are shown in this
\r
1600 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1602 core::rect<s32>(100, 70, 100+400, 70+(text_height+5)),
\r
1606 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
\r
1608 core::rect<s32>(0,0,0,0),
\r
1610 guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
\r
1611 core::list<ChatLine> chat_lines;
\r
1614 If an error occurs, this is set to something and the
\r
1615 menu-game loop is restarted. It is then displayed before
\r
1618 std::wstring error_message = L"";
\r
1623 while(g_device->run())
\r
1626 // This is used for catching disconnects
\r
1631 Out-of-game menu loop.
\r
1633 Loop quits when menu returns proper parameters.
\r
1637 // Cursor can be non-visible when coming from the game
\r
1638 device->getCursorControl()->setVisible(true);
\r
1639 // Some stuff are left to scene manager when coming from the game
\r
1640 // (map at least?)
\r
1642 // Reset or hide the debug gui texts
\r
1643 guitext->setText(L"Minetest-c55");
\r
1644 guitext2->setVisible(false);
\r
1645 guitext_info->setVisible(false);
\r
1646 guitext_chat->setVisible(false);
\r
1648 // Initialize menu data
\r
1649 MainMenuData menudata;
\r
1650 menudata.address = narrow_to_wide(address);
\r
1651 menudata.name = narrow_to_wide(playername);
\r
1652 menudata.port = narrow_to_wide(itos(port));
\r
1653 menudata.creative_mode = g_settings.getBool("creative_mode");
\r
1655 GUIMainMenu *menu =
\r
1656 new GUIMainMenu(guienv, guiroot, -1,
\r
1657 &g_menumgr, &menudata, &g_gamecallback);
\r
1658 menu->allowFocusRemoval(true);
\r
1660 if(error_message != L"")
\r
1662 GUIMessageMenu *menu2 =
\r
1663 new GUIMessageMenu(guienv, guiroot, -1,
\r
1664 &g_menumgr, error_message.c_str());
\r
1666 error_message = L"";
\r
1669 video::IVideoDriver* driver = g_device->getVideoDriver();
\r
1671 dstream<<"Created main menu"<<std::endl;
\r
1673 while(g_device->run())
\r
1675 // Run global IrrlichtWrapper's main thread processing stuff
\r
1676 g_irrlicht->Run();
\r
1678 if(menu->getStatus() == true)
\r
1681 //driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1682 driver->beginScene(true, true, video::SColor(255,128,128,128));
\r
1683 guienv->drawAll();
\r
1684 driver->endScene();
\r
1687 // Break out of menu-game loop to shut down cleanly
\r
1688 if(g_device->run() == false)
\r
1691 dstream<<"Dropping main menu"<<std::endl;
\r
1695 // Delete map if requested
\r
1696 if(menudata.delete_map)
\r
1698 bool r = fs::RecursiveDeleteContent(map_dir);
\r
1700 error_message = L"Delete failed";
\r
1704 playername = wide_to_narrow(menudata.name);
\r
1705 address = wide_to_narrow(menudata.address);
\r
1706 port = stoi(wide_to_narrow(menudata.port));
\r
1707 g_settings.set("creative_mode", itos(menudata.creative_mode));
\r
1709 // Check for valid parameters, restart menu if invalid.
\r
1710 if(playername == "")
\r
1712 error_message = L"Name required.";
\r
1717 g_settings.set("name", playername);
\r
1718 g_settings.set("address", address);
\r
1719 g_settings.set("port", itos(port));
\r
1720 // Update configuration file
\r
1721 if(configpath != "")
\r
1722 g_settings.updateConfigFile(configpath.c_str());
\r
1724 // Continue to game
\r
1728 // Break out of menu-game loop to shut down cleanly
\r
1729 if(g_device->run() == false)
\r
1733 Make a scope here so that the client and the server and other
\r
1734 stuff gets removed when disconnected or the irrlicht device
\r
1740 Draw "Loading" screen
\r
1742 const wchar_t *text = L"Loading and connecting...";
\r
1743 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1744 core::vector2d<s32> textsize(300, text_height);
\r
1745 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1747 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1748 text, textrect, false, false);
\r
1749 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1751 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1752 guienv->drawAll();
\r
1753 driver->endScene();
\r
1755 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1759 SharedPtr will delete it when it goes out of scope.
\r
1761 SharedPtr<Server> server;
\r
1762 if(address == ""){
\r
1763 server = new Server(map_dir, hm_params, map_params);
\r
1764 server->start(port);
\r
1771 Client client(device, playername.c_str(), draw_control);
\r
1773 g_client = &client;
\r
1775 Address connect_address(0,0,0,0, port);
\r
1778 connect_address.Resolve("localhost");
\r
1780 connect_address.Resolve(address.c_str());
\r
1782 catch(ResolveError &e)
\r
1784 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1786 error_message = L"Couldn't resolve address";
\r
1787 gui_loadingtext->remove();
\r
1791 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1792 client.connect(connect_address);
\r
1795 while(client.connectedAndInitialized() == false)
\r
1798 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1799 guienv->drawAll();
\r
1800 driver->endScene();
\r
1802 // Update client and server
\r
1806 if(server != NULL)
\r
1807 server->step(0.1);
\r
1813 catch(con::PeerNotFoundException &e)
\r
1815 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1817 error_message = L"Connection timed out.";
\r
1818 gui_loadingtext->remove();
\r
1825 /*scene::ISceneNode* skybox;
\r
1826 skybox = smgr->addSkyBoxSceneNode(
\r
1827 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
\r
1828 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
\r
1829 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1830 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1831 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
\r
1832 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
\r
1835 Create the camera node
\r
1838 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1839 0, // Camera parent
\r
1840 v3f(BS*100, BS*2, BS*100), // Look from
\r
1841 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1845 if(camera == NULL)
\r
1848 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1850 camera->setFOV(FOV_ANGLE);
\r
1852 // Just so big a value that everything rendered is visible
\r
1853 camera->setFarValue(100000*BS);
\r
1855 f32 camera_yaw = 0; // "right/left"
\r
1856 f32 camera_pitch = 0; // "up/down"
\r
1862 gui_loadingtext->remove();
\r
1865 Add some gui stuff
\r
1868 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1869 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1871 // Test the text input system
\r
1872 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
\r
1874 /*GUIMessageMenu *menu =
\r
1875 new GUIMessageMenu(guienv, guiroot, -1,
\r
1880 // Launch pause menu
\r
1881 (new GUIPauseMenu(guienv, guiroot, -1, &g_gamecallback,
\r
1882 &g_menumgr))->drop();
\r
1885 guitext2->setVisible(true);
\r
1886 guitext_info->setVisible(true);
\r
1887 guitext_chat->setVisible(true);
\r
1890 Some statistics are collected in these
\r
1893 u32 beginscenetime = 0;
\r
1894 u32 scenetime = 0;
\r
1895 u32 endscenetime = 0;
\r
1898 //throw con::PeerNotFoundException("lol");
\r
1904 bool first_loop_after_window_activation = true;
\r
1906 // Time is in milliseconds
\r
1907 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
\r
1908 // NOTE: So we have to use getTime() and call run()s between them
\r
1909 u32 lasttime = device->getTimer()->getTime();
\r
1911 while(device->run())
\r
1913 if(g_disconnect_requested)
\r
1915 g_disconnect_requested = false;
\r
1920 Run global IrrlichtWrapper's main thread processing stuff
\r
1922 g_irrlicht->Run();
\r
1925 Random calculations
\r
1927 v2u32 screensize = driver->getScreenSize();
\r
1928 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1930 // Hilight boxes collected during the loop and displayed
\r
1931 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1934 std::wstring infotext;
\r
1936 //TimeTaker //timer1("//timer1");
\r
1938 // Time of frame without fps limit
\r
1942 // not using getRealTime is necessary for wine
\r
1943 u32 time = device->getTimer()->getTime();
\r
1944 if(time > lasttime)
\r
1945 busytime_u32 = time - lasttime;
\r
1948 busytime = busytime_u32 / 1000.0;
\r
1951 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1953 // Absolutelu necessary for wine!
\r
1960 updateViewingRange(busytime, &client);
\r
1967 float fps_max = g_settings.getFloat("fps_max");
\r
1968 u32 frametime_min = 1000./fps_max;
\r
1970 if(busytime_u32 < frametime_min)
\r
1972 u32 sleeptime = frametime_min - busytime_u32;
\r
1973 device->sleep(sleeptime);
\r
1977 // Absolutelu necessary for wine!
\r
1981 Time difference calculation
\r
1983 f32 dtime; // in seconds
\r
1985 u32 time = device->getTimer()->getTime();
\r
1986 if(time > lasttime)
\r
1987 dtime = (time - lasttime) / 1000.0;
\r
1993 Time average and jitter calculation
\r
1996 static f32 dtime_avg1 = 0.0;
\r
1997 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1998 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
2000 static f32 dtime_jitter1_max_sample = 0.0;
\r
2001 static f32 dtime_jitter1_max_fraction = 0.0;
\r
2003 static f32 jitter1_max = 0.0;
\r
2004 static f32 counter = 0.0;
\r
2005 if(dtime_jitter1 > jitter1_max)
\r
2006 jitter1_max = dtime_jitter1;
\r
2011 dtime_jitter1_max_sample = jitter1_max;
\r
2012 dtime_jitter1_max_fraction
\r
2013 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
2014 jitter1_max = 0.0;
\r
2017 Control freetime ratio
\r
2019 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
2021 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
2022 g_freetime_ratio += 0.01;
\r
2026 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
2027 g_freetime_ratio -= 0.01;
\r
2033 Busytime average and jitter calculation
\r
2036 static f32 busytime_avg1 = 0.0;
\r
2037 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
2038 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
2040 static f32 busytime_jitter1_max_sample = 0.0;
\r
2041 static f32 busytime_jitter1_min_sample = 0.0;
\r
2043 static f32 jitter1_max = 0.0;
\r
2044 static f32 jitter1_min = 0.0;
\r
2045 static f32 counter = 0.0;
\r
2046 if(busytime_jitter1 > jitter1_max)
\r
2047 jitter1_max = busytime_jitter1;
\r
2048 if(busytime_jitter1 < jitter1_min)
\r
2049 jitter1_min = busytime_jitter1;
\r
2051 if(counter > 0.0){
\r
2053 busytime_jitter1_max_sample = jitter1_max;
\r
2054 busytime_jitter1_min_sample = jitter1_min;
\r
2055 jitter1_max = 0.0;
\r
2056 jitter1_min = 0.0;
\r
2061 Debug info for client
\r
2064 static float counter = 0.0;
\r
2069 client.printDebugInfo(std::cout);
\r
2074 Input handler step()
\r
2076 g_input->step(dtime);
\r
2079 Player speed control
\r
2088 bool a_superspeed,
\r
2091 PlayerControl control(
\r
2092 g_input->isKeyDown(irr::KEY_KEY_W),
\r
2093 g_input->isKeyDown(irr::KEY_KEY_S),
\r
2094 g_input->isKeyDown(irr::KEY_KEY_A),
\r
2095 g_input->isKeyDown(irr::KEY_KEY_D),
\r
2096 g_input->isKeyDown(irr::KEY_SPACE),
\r
2097 g_input->isKeyDown(irr::KEY_KEY_2),
\r
2101 client.setPlayerControl(control);
\r
2105 Process environment
\r
2109 //TimeTaker timer("client.step(dtime)");
\r
2110 client.step(dtime);
\r
2111 //client.step(dtime_avg1);
\r
2114 if(server != NULL)
\r
2116 //TimeTaker timer("server->step(dtime)");
\r
2117 server->step(dtime);
\r
2120 v3f player_position = client.getPlayerPosition();
\r
2122 //TimeTaker //timer2("//timer2");
\r
2125 Mouse and camera control
\r
2128 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
2131 device->getCursorControl()->setVisible(false);
\r
2133 if(first_loop_after_window_activation){
\r
2134 //std::cout<<"window active, first loop"<<std::endl;
\r
2135 first_loop_after_window_activation = false;
\r
2138 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
2139 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
2140 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
2141 camera_yaw -= dx*0.2;
\r
2142 camera_pitch += dy*0.2;
\r
2143 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
2144 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
2146 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
2149 device->getCursorControl()->setVisible(true);
\r
2151 //std::cout<<"window inactive"<<std::endl;
\r
2152 first_loop_after_window_activation = true;
\r
2155 camera_yaw = wrapDegrees(camera_yaw);
\r
2156 camera_pitch = wrapDegrees(camera_pitch);
\r
2158 v3f camera_direction = v3f(0,0,1);
\r
2159 camera_direction.rotateYZBy(camera_pitch);
\r
2160 camera_direction.rotateXZBy(camera_yaw);
\r
2162 // This is at the height of the eyes of the current figure
\r
2163 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
\r
2164 // This is more like in minecraft
\r
2165 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
\r
2167 camera->setPosition(camera_position);
\r
2168 // *100.0 helps in large map coordinates
\r
2169 camera->setTarget(camera_position + camera_direction * 100.0);
\r
2171 if(FIELD_OF_VIEW_TEST){
\r
2172 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2173 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
2176 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
2177 //TimeTaker timer("client.updateCamera");
\r
2178 client.updateCamera(camera_position, camera_direction);
\r
2182 //TimeTaker //timer3("//timer3");
\r
2185 Calculate what block is the crosshair pointing to
\r
2188 //u32 t1 = device->getTimer()->getRealTime();
\r
2190 //f32 d = 4; // max. distance
\r
2191 f32 d = 4; // max. distance
\r
2192 core::line3d<f32> shootline(camera_position,
\r
2193 camera_position + camera_direction * BS * (d+1));
\r
2195 MapBlockObject *selected_object = client.getSelectedObject
\r
2196 (d*BS, camera_position, shootline);
\r
2199 If it's pointing to a MapBlockObject
\r
2202 if(selected_object != NULL)
\r
2204 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
2206 core::aabbox3d<f32> box_on_map
\r
2207 = selected_object->getSelectionBoxOnMap();
\r
2209 hilightboxes.push_back(box_on_map);
\r
2211 infotext = narrow_to_wide(selected_object->infoText());
\r
2213 if(g_input->getLeftClicked())
\r
2215 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
2216 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
2217 selected_object->getId(), g_selected_item);
\r
2219 else if(g_input->getRightClicked())
\r
2221 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
2223 Check if we want to modify the object ourselves
\r
2225 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
2227 dstream<<"Sign object right-clicked"<<std::endl;
\r
2229 if(random_input == false)
\r
2231 // Get a new text for it
\r
2233 TextDest *dest = new TextDestSign(
\r
2234 selected_object->getBlock()->getPos(),
\r
2235 selected_object->getId(),
\r
2238 SignObject *sign_object = (SignObject*)selected_object;
\r
2240 std::wstring wtext =
\r
2241 narrow_to_wide(sign_object->getText());
\r
2243 (new GUITextInputMenu(guienv, guiroot, -1,
\r
2249 Otherwise pass the event to the server as-is
\r
2253 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
2254 selected_object->getId(), g_selected_item);
\r
2258 else // selected_object == NULL
\r
2262 Find out which node we are pointing at
\r
2265 bool nodefound = false;
\r
2267 v3s16 neighbourpos;
\r
2268 core::aabbox3d<f32> nodefacebox;
\r
2269 f32 mindistance = BS * 1001;
\r
2271 v3s16 pos_i = floatToInt(player_position);
\r
2273 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
2277 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
2278 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
2279 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
2280 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
2281 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
2282 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
2284 for(s16 y = ystart; y <= yend; y++)
\r
2285 for(s16 z = zstart; z <= zend; z++)
\r
2286 for(s16 x = xstart; x <= xend; x++)
\r
2291 n = client.getNode(v3s16(x,y,z));
\r
2292 if(content_pointable(n.d) == false)
\r
2295 catch(InvalidPositionException &e)
\r
2301 v3f npf = intToFloat(np);
\r
2306 v3s16(0,0,1), // back
\r
2307 v3s16(0,1,0), // top
\r
2308 v3s16(1,0,0), // right
\r
2309 v3s16(0,0,-1), // front
\r
2310 v3s16(0,-1,0), // bottom
\r
2311 v3s16(-1,0,0), // left
\r
2317 if(n.d == CONTENT_TORCH)
\r
2319 v3s16 dir = unpackDir(n.dir);
\r
2320 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
2321 dir_f *= BS/2 - BS/6 - BS/20;
\r
2322 v3f cpf = npf + dir_f;
\r
2323 f32 distance = (cpf - camera_position).getLength();
\r
2325 core::aabbox3d<f32> box;
\r
2328 if(dir == v3s16(0,-1,0))
\r
2330 box = core::aabbox3d<f32>(
\r
2331 npf - v3f(BS/6, BS/2, BS/6),
\r
2332 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
2336 else if(dir == v3s16(0,1,0))
\r
2338 box = core::aabbox3d<f32>(
\r
2339 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
2340 npf + v3f(BS/6, BS/2, BS/6)
\r
2346 box = core::aabbox3d<f32>(
\r
2347 cpf - v3f(BS/6, BS/3, BS/6),
\r
2348 cpf + v3f(BS/6, BS/3, BS/6)
\r
2352 if(distance < mindistance)
\r
2354 if(box.intersectsWithLine(shootline))
\r
2358 neighbourpos = np;
\r
2359 mindistance = distance;
\r
2360 nodefacebox = box;
\r
2369 for(u16 i=0; i<6; i++)
\r
2371 v3f dir_f = v3f(dirs[i].X,
\r
2372 dirs[i].Y, dirs[i].Z);
\r
2373 v3f centerpoint = npf + dir_f * BS/2;
\r
2375 (centerpoint - camera_position).getLength();
\r
2377 if(distance < mindistance)
\r
2379 core::CMatrix4<f32> m;
\r
2380 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2382 // This is the back face
\r
2383 v3f corners[2] = {
\r
2384 v3f(BS/2, BS/2, BS/2),
\r
2385 v3f(-BS/2, -BS/2, BS/2+d)
\r
2388 for(u16 j=0; j<2; j++)
\r
2390 m.rotateVect(corners[j]);
\r
2391 corners[j] += npf;
\r
2394 core::aabbox3d<f32> facebox(corners[0]);
\r
2395 facebox.addInternalPoint(corners[1]);
\r
2397 if(facebox.intersectsWithLine(shootline))
\r
2401 neighbourpos = np + dirs[i];
\r
2402 mindistance = distance;
\r
2403 nodefacebox = facebox;
\r
2405 } // if distance < mindistance
\r
2407 } // regular block
\r
2410 static float nodig_delay_counter = 0.0;
\r
2414 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2416 static float dig_time = 0.0;
\r
2417 static u16 dig_index = 0;
\r
2419 // Visualize selection
\r
2421 const float d = 0.502;
\r
2422 core::aabbox3d<f32> nodebox(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
\r
2423 v3f nodepos_f = intToFloat(nodepos);
\r
2424 //v3f nodepos_f(nodepos.X*BS, nodepos.Y*BS, nodepos.Z*BS);
\r
2425 nodebox.MinEdge += nodepos_f;
\r
2426 nodebox.MaxEdge += nodepos_f;
\r
2427 hilightboxes.push_back(nodebox);
\r
2429 //hilightboxes.push_back(nodefacebox);
\r
2433 if(g_input->getLeftReleased())
\r
2435 client.clearTempMod(nodepos);
\r
2439 if(nodig_delay_counter > 0.0)
\r
2441 nodig_delay_counter -= dtime;
\r
2445 if(nodepos != nodepos_old)
\r
2447 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2448 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2450 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2452 client.clearTempMod(nodepos_old);
\r
2457 if(g_input->getLeftClicked() ||
\r
2458 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2460 dstream<<DTIME<<"Started digging"<<std::endl;
\r
2461 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2463 if(g_input->getLeftClicked())
\r
2465 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2467 if(g_input->getLeftState())
\r
2469 MapNode n = client.getNode(nodepos);
\r
2471 // Get tool name. Default is "" = bare hands
\r
2472 std::string toolname = "";
\r
2473 InventoryList *mlist = local_inventory.getList("main");
\r
2476 InventoryItem *item = mlist->getItem(g_selected_item);
\r
2477 if(item && (std::string)item->getName() == "ToolItem")
\r
2479 ToolItem *titem = (ToolItem*)item;
\r
2480 toolname = titem->getToolName();
\r
2484 // Get digging properties for material and tool
\r
2485 u8 material = n.d;
\r
2486 DiggingProperties prop =
\r
2487 getDiggingProperties(material, toolname);
\r
2489 float dig_time_complete = 0.0;
\r
2491 if(prop.diggable == false)
\r
2493 /*dstream<<"Material "<<(int)material
\r
2494 <<" not diggable with \""
\r
2495 <<toolname<<"\""<<std::endl;*/
\r
2496 // I guess nobody will wait for this long
\r
2497 dig_time_complete = 10000000.0;
\r
2501 dig_time_complete = prop.time;
\r
2504 if(dig_time_complete >= 0.001)
\r
2506 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2507 * dig_time/dig_time_complete);
\r
2509 // This is for torches
\r
2512 dig_index = CRACK_ANIMATION_LENGTH;
\r
2515 if(dig_index < CRACK_ANIMATION_LENGTH)
\r
2517 //TimeTaker timer("client.setTempMod");
\r
2518 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2519 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2523 dstream<<DTIME<<"Digging completed"<<std::endl;
\r
2524 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
\r
2525 client.clearTempMod(nodepos);
\r
2526 client.removeNode(nodepos);
\r
2530 nodig_delay_counter = dig_time_complete
\r
2531 / (float)CRACK_ANIMATION_LENGTH;
\r
2533 // We don't want a corresponding delay to
\r
2534 // very time consuming nodes
\r
2535 if(nodig_delay_counter > 0.5)
\r
2537 nodig_delay_counter = 0.5;
\r
2539 // We want a slight delay to very little
\r
2540 // time consuming nodes
\r
2541 //float mindelay = 0.15;
\r
2542 float mindelay = 0.20;
\r
2543 if(nodig_delay_counter < mindelay)
\r
2545 nodig_delay_counter = mindelay;
\r
2549 dig_time += dtime;
\r
2553 if(g_input->getRightClicked())
\r
2555 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2556 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2559 nodepos_old = nodepos;
\r
2564 } // selected_object == NULL
\r
2566 g_input->resetLeftClicked();
\r
2567 g_input->resetRightClicked();
\r
2569 if(g_input->getLeftReleased())
\r
2571 std::cout<<DTIME<<"Left button released (stopped digging)"
\r
2573 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2575 if(g_input->getRightReleased())
\r
2577 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2581 g_input->resetLeftReleased();
\r
2582 g_input->resetRightReleased();
\r
2585 Calculate stuff for drawing
\r
2588 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2590 u32 daynight_ratio = client.getDayNightRatio();
\r
2591 /*video::SColor bgcolor = video::SColor(
\r
2593 skycolor.getRed() * daynight_ratio / 1000,
\r
2594 skycolor.getGreen() * daynight_ratio / 1000,
\r
2595 skycolor.getBlue() * daynight_ratio / 1000);*/
\r
2597 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
\r
2598 video::SColor bgcolor = video::SColor(
\r
2600 skycolor.getRed() * l / 255,
\r
2601 skycolor.getGreen() * l / 255,
\r
2602 skycolor.getBlue() * l / 255);
\r
2608 if(g_settings.getBool("enable_fog") == true)
\r
2610 f32 range = draw_control.wanted_range * BS;
\r
2611 if(draw_control.range_all)
\r
2612 range = 100000*BS;
\r
2616 video::EFT_FOG_LINEAR,
\r
2620 false, // pixel fog
\r
2621 false // range fog
\r
2627 Update gui stuff (0ms)
\r
2630 //TimeTaker guiupdatetimer("Gui updating");
\r
2633 wchar_t temptext[150];
\r
2635 static float drawtime_avg = 0;
\r
2636 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
\r
2637 static float beginscenetime_avg = 0;
\r
2638 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
\r
2639 static float scenetime_avg = 0;
\r
2640 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
\r
2641 static float endscenetime_avg = 0;
\r
2642 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
\r
2644 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2646 L", R: range_all=%i"
\r
2648 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2650 draw_control.range_all,
\r
2652 beginscenetime_avg,
\r
2657 guitext->setText(temptext);
\r
2661 wchar_t temptext[150];
\r
2662 swprintf(temptext, 150,
\r
2663 L"(% .1f, % .1f, % .1f)"
\r
2664 L" (% .3f < btime_jitter < % .3f"
\r
2665 L", dtime_jitter = % .1f %%"
\r
2666 L", v_range = %.1f)",
\r
2667 player_position.X/BS,
\r
2668 player_position.Y/BS,
\r
2669 player_position.Z/BS,
\r
2670 busytime_jitter1_min_sample,
\r
2671 busytime_jitter1_max_sample,
\r
2672 dtime_jitter1_max_fraction * 100.0,
\r
2673 draw_control.wanted_range
\r
2676 guitext2->setText(temptext);
\r
2680 guitext_info->setText(infotext.c_str());
\r
2684 Get chat messages from client
\r
2687 // Get new messages
\r
2688 std::wstring message;
\r
2689 while(client.getChatMessage(message))
\r
2691 chat_lines.push_back(ChatLine(message));
\r
2692 /*if(chat_lines.size() > 6)
\r
2694 core::list<ChatLine>::Iterator
\r
2695 i = chat_lines.begin();
\r
2696 chat_lines.erase(i);
\r
2699 // Append them to form the whole static text and throw
\r
2700 // it to the gui element
\r
2701 std::wstring whole;
\r
2702 // This will correspond to the line number counted from
\r
2703 // top to bottom, from size-1 to 0
\r
2704 s16 line_number = chat_lines.size();
\r
2705 // Count of messages to be removed from the top
\r
2706 u16 to_be_removed_count = 0;
\r
2707 for(core::list<ChatLine>::Iterator
\r
2708 i = chat_lines.begin();
\r
2709 i != chat_lines.end(); i++)
\r
2711 // After this, line number is valid for this loop
\r
2714 (*i).age += dtime;
\r
2716 This results in a maximum age of 60*6 to the
\r
2717 lowermost line and a maximum of 6 lines
\r
2719 float allowed_age = (6-line_number) * 60.0;
\r
2721 if((*i).age > allowed_age)
\r
2723 to_be_removed_count++;
\r
2726 whole += (*i).text + L'\n';
\r
2728 for(u16 i=0; i<to_be_removed_count; i++)
\r
2730 core::list<ChatLine>::Iterator
\r
2731 it = chat_lines.begin();
\r
2732 chat_lines.erase(it);
\r
2734 guitext_chat->setText(whole.c_str());
\r
2735 // Update gui element size and position
\r
2736 core::rect<s32> rect(
\r
2738 screensize.Y - 10 - text_height*chat_lines.size(),
\r
2739 screensize.X - 10,
\r
2742 guitext_chat->setRelativePosition(rect);
\r
2744 if(chat_lines.size() == 0)
\r
2745 guitext_chat->setVisible(false);
\r
2747 guitext_chat->setVisible(true);
\r
2754 static u16 old_selected_item = 65535;
\r
2755 if(client.getLocalInventoryUpdated()
\r
2756 || g_selected_item != old_selected_item)
\r
2758 old_selected_item = g_selected_item;
\r
2759 //std::cout<<"Updating local inventory"<<std::endl;
\r
2760 client.getLocalInventory(local_inventory);
\r
2761 quick_inventory->setSelection(g_selected_item);
\r
2762 quick_inventory->update();
\r
2766 Send actions returned by the inventory menu
\r
2768 while(inventory_action_queue.size() != 0)
\r
2770 InventoryAction *a = inventory_action_queue.pop_front();
\r
2772 client.sendInventoryAction(a);
\r
2781 TimeTaker drawtimer("Drawing");
\r
2785 TimeTaker timer("beginScene");
\r
2786 driver->beginScene(true, true, bgcolor);
\r
2787 //driver->beginScene(false, true, bgcolor);
\r
2788 beginscenetime = timer.stop(true);
\r
2793 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2796 TimeTaker timer("smgr");
\r
2798 scenetime = timer.stop(true);
\r
2802 //TimeTaker timer9("auxiliary drawings");
\r
2806 //TimeTaker //timer10("//timer10");
\r
2808 video::SMaterial m;
\r
2809 //m.Thickness = 10;
\r
2811 m.Lighting = false;
\r
2812 driver->setMaterial(m);
\r
2814 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2816 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2817 i != hilightboxes.end(); i++)
\r
2819 /*std::cout<<"hilightbox min="
\r
2820 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2822 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2824 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2830 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2831 displaycenter + core::vector2d<s32>(10,0),
\r
2832 video::SColor(255,255,255,255));
\r
2833 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2834 displaycenter + core::vector2d<s32>(0,10),
\r
2835 video::SColor(255,255,255,255));
\r
2840 //TimeTaker //timer11("//timer11");
\r
2846 guienv->drawAll();
\r
2850 TimeTaker timer("endScene");
\r
2851 driver->endScene();
\r
2852 endscenetime = timer.stop(true);
\r
2855 drawtime = drawtimer.stop(true);
\r
2861 static s16 lastFPS = 0;
\r
2862 //u16 fps = driver->getFPS();
\r
2863 u16 fps = (1.0/dtime_avg1);
\r
2865 if (lastFPS != fps)
\r
2867 core::stringw str = L"Minetest [";
\r
2868 str += driver->getName();
\r
2872 device->setWindowCaption(str.c_str());
\r
2878 device->yield();*/
\r
2881 delete quick_inventory;
\r
2883 } // client and server are deleted at this point
\r
2886 catch(con::PeerNotFoundException &e)
\r
2888 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2889 error_message = L"Connection timed out.";
\r
2892 } // Menu-game loop
\r
2897 In the end, delete the Irrlicht device.
\r
2902 Update configuration file
\r
2904 /*if(configpath != "")
\r
2906 g_settings.updateConfigFile(configpath.c_str());
\r
2909 END_DEBUG_EXCEPTION_HANDLER
\r
2911 debugstreams_deinit();
\r