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
27 NOTE: iostream.imbue(std::locale("C")) is very slow
\r
28 NOTE: Global locale is now set at initialization
\r
30 SUGG: Fix address to be ipv6 compatible
\r
32 FIXME: When a new sector is generated, it may change the ground level
\r
33 of it's and it's neighbors border that two blocks that are
\r
34 above and below each other and that are generated before and
\r
35 after the sector heightmap generation (order doesn't matter),
\r
36 can have a small gap between each other at the border.
\r
37 SUGGESTION: Use same technique for sector heightmaps as what we're
\r
38 using for UnlimitedHeightmap? (getting all neighbors
\r
41 SUGG: Transfer more blocks in a single packet
\r
42 SUGG: A blockdata combiner class, to which blocks are added and at
\r
43 destruction it sends all the stuff in as few packets as possible.
\r
45 SUGG: If player is on ground, mainly fetch ground-level blocks
\r
46 SUGG: Fetch stuff mainly from the viewing direction
\r
48 SUGG: Expose Connection's seqnums and ACKs to server and client.
\r
49 - This enables saving many packets and making a faster connection
\r
50 - This also enables server to check if client has received the
\r
51 most recent block sent, for example.
\r
52 SUGG: Add a sane bandwidth throttling system to Connection
\r
54 SUGG: More fine-grained control of client's dumping of blocks from
\r
56 - ...What does this mean in the first place?
\r
58 SUGG: A map editing mode (similar to dedicated server mode)
\r
60 SUGG: Add a time value to the param of footstepped grass and check it
\r
61 against a global timer when a block is accessed, to make old
\r
64 SUGG: Make a copy of close-range environment on client for showing
\r
65 on screen, with minimal mutexes to slow down the main loop
\r
67 SUGG: Make a PACKET_COMBINED which contains many subpackets. Utilize
\r
68 it by sending more stuff in a single packet.
\r
69 - Add a packet queue to RemoteClient, from which packets will be
\r
70 combined with object data packets
\r
71 - This is not exactly trivial: the object data packets are
\r
72 sometimes very big by themselves
\r
74 SUGG: Split MapBlockObject serialization to to-client and to-disk
\r
75 - This will allow saving ages of rats on disk but not sending
\r
78 SUGG: Implement lighting using VoxelManipulator
\r
79 - Would it be significantly faster?
\r
81 FIXME: Rats somehow go underground sometimes (you can see it in water)
\r
82 - Does their position get saved to a border value or something?
\r
83 - Does this happen anymore?
\r
85 SUGG: MovingObject::move and Player::move are basically the same.
\r
88 SUGG: Implement a "Fast check queue" (a queue with a map for checking
\r
89 if something is already in it)
\r
90 - Use it in active block queue in water flowing
\r
92 SUGG: Signs could be done in the same way as torches. For this, blocks
\r
93 need an additional metadata field for the texts
\r
94 - This is also needed for item container chests
\r
96 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
98 SUGG: A version number to blocks, which increments when the block is
\r
99 modified (node add/remove, water update, lighting update)
\r
100 - This can then be used to make sure the most recent version of
\r
101 a block has been sent to client
\r
103 SUGG: Make the amount of blocks sending to client and the total
\r
104 amount of blocks dynamically limited. Transferring blocks is the
\r
105 main network eater of this system, so it is the one that has
\r
106 to be throttled so that RTTs stay low.
\r
108 TODO: Untie client network operations from framerate
\r
109 - Needs some input queues or something
\r
110 - Not really necessary?
\r
112 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
114 - That is >500 vertices
\r
116 TODO: Better dungeons
\r
119 TODO: Startup and configuration menu
\r
121 TODO: There are some lighting-related todos and fixmes in
\r
122 ServerMap::emergeBlock
\r
124 TODO: Proper handling of spawning place (try to find something that
\r
125 is not in the middle of an ocean (some land to stand on at
\r
126 least) and save it in map config.
\r
128 TODO: Players to only be hidden when the client quits.
\r
129 TODO: - Players to be saved on disk, with inventory
\r
130 TODO: Players to be saved as text in map/players/<name>
\r
131 TODO: Player inventory to be saved on disk
\r
133 TODO: Make fetching sector's blocks more efficient when rendering
\r
134 sectors that have very large amounts of blocks (on client)
\r
136 TODO: Make the video backend selectable
\r
138 Block object server side:
\r
139 - A "near blocks" buffer, in which some nearby blocks are stored.
\r
140 - For all blocks in the buffer, objects are stepped(). This
\r
141 means they are active.
\r
142 - TODO: A global active buffer is needed for the server
\r
143 - TODO: A timestamp to blocks
\r
144 - TODO: All blocks going in and out of the buffer are recorded.
\r
145 - TODO: For outgoing blocks, timestamp is written.
\r
146 - TODO: For incoming blocks, time difference is calculated and
\r
147 objects are stepped according to it.
\r
149 TODO: Copy the text of the last picked sign to inventory in creative
\r
152 TODO: Get rid of GotSplitPacketException
\r
154 TODO: Check what goes wrong with caching map to disk (Kray)
\r
156 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
158 TODO: TOSERVER_LEAVE
\r
160 TODO: Better handling of objects and mobs
\r
162 - There has to be some way to do it with less spaghetti code
\r
163 - Make separate classes for client and server
\r
164 - Client should not discriminate between blocks, server should
\r
165 - Make other players utilize the same framework
\r
167 TODO: Draw big amounts of torches better (that is, throw them in the
\r
168 same meshbuffer (can the meshcollector class be used?))
\r
171 ======================================================================
\r
173 TODO: Convert the text input system to use a modal menu... or something
\r
175 ======================================================================
\r
180 Setting this to 1 enables a special camera mode that forces
\r
181 the renderers to think that the camera statically points from
\r
182 the starting place to a static direction.
\r
184 This allows one to move around with the player and see what
\r
185 is actually drawn behind solid things and behind the player.
\r
187 #define FIELD_OF_VIEW_TEST 0
\r
189 #ifdef UNITTEST_DISABLE
\r
191 #pragma message ("Disabling unit tests")
\r
193 #warning "Disabling unit tests"
\r
195 // Disable unit tests
\r
196 #define ENABLE_TESTS 0
\r
198 // Enable unit tests
\r
199 #define ENABLE_TESTS 1
\r
203 #pragma comment(lib, "Irrlicht.lib")
\r
204 #pragma comment(lib, "jthread.lib")
\r
205 #pragma comment(lib, "zlibwapi.lib")
\r
206 // This would get rid of the console window
\r
207 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
210 #include <iostream>
\r
212 #include <jmutexautolock.h>
\r
213 #include <locale.h>
\r
214 #include "common_irrlicht.h"
\r
217 #include "player.h"
\r
220 #include "environment.h"
\r
221 #include "server.h"
\r
222 #include "client.h"
\r
223 #include "serialization.h"
\r
224 #include "constants.h"
\r
225 #include "strfnd.h"
\r
226 #include "porting.h"
\r
227 #include "irrlichtwrapper.h"
\r
228 #include "gettime.h"
\r
229 #include "porting.h"
\r
230 #include "guiPauseMenu.h"
\r
231 #include "guiInventoryMenu.h"
\r
232 #include "guiTextInputMenu.h"
\r
234 IrrlichtWrapper *g_irrlicht;
\r
236 // All range-related stuff below is locked behind this
\r
237 JMutex g_range_mutex;
\r
239 // Blocks are viewed in this range from the player
\r
240 s16 g_viewing_range_nodes = 60;
\r
241 //s16 g_viewing_range_nodes = 0;
\r
243 // This is updated by the client's fetchBlocks routine
\r
244 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
246 // If true, the preceding value has no meaning and all blocks
\r
247 // already existing in memory are drawn
\r
248 bool g_viewing_range_all = false;
\r
250 // This is the freetime ratio imposed by the dynamic viewing
\r
251 // range changing code.
\r
252 // It is controlled by the main loop to the smallest value that
\r
253 // inhibits glitches (dtime jitter) in the main loop.
\r
254 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
258 These are loaded from the config file.
\r
261 Settings g_settings;
\r
263 extern void set_default_settings();
\r
269 //u16 g_selected_material = 0;
\r
270 u16 g_selected_item = 0;
\r
272 IrrlichtDevice *g_device = NULL;
\r
277 gui::IGUIEnvironment* guienv = NULL;
\r
278 gui::IGUIStaticText *guiroot = NULL;
\r
279 int g_active_menu_count = 0;
\r
281 bool noMenuActive()
\r
283 return (g_active_menu_count == 0);
\r
286 // Inventory actions from the menu are buffered here before sending
\r
287 Queue<InventoryAction*> inventory_action_queue;
\r
288 // This is a copy of the inventory that the client's environment has
\r
289 Inventory local_inventory;
\r
296 std::ostream *dout_con_ptr = &dummyout;
\r
297 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
298 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
299 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
300 //std::ostream *dout_con_ptr = &dstream;
\r
301 //std::ostream *derr_con_ptr = &dstream;
\r
304 std::ostream *dout_server_ptr = &dstream;
\r
305 std::ostream *derr_server_ptr = &dstream;
\r
308 std::ostream *dout_client_ptr = &dstream;
\r
309 std::ostream *derr_client_ptr = &dstream;
\r
312 gettime.h implementation
\r
318 Use irrlicht because it is more precise than porting.h's
\r
321 if(g_irrlicht == NULL)
\r
323 return g_irrlicht->getTime();
\r
326 class MyEventReceiver : public IEventReceiver
\r
329 // This is the one method that we have to implement
\r
330 virtual bool OnEvent(const SEvent& event)
\r
333 React to nothing here if a menu is active
\r
335 if(noMenuActive() == false)
\r
341 // Remember whether each key is down or up
\r
342 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
344 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
346 if(event.KeyInput.PressedDown)
\r
348 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
354 if(guienv != NULL && guiroot != NULL && g_device != NULL)
\r
356 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
358 dstream<<DTIME<<"MyEventReceiver: "
\r
359 <<"Launching pause menu"<<std::endl;
\r
360 // It will delete itself by itself
\r
361 (new GUIPauseMenu(guienv, guiroot, -1, g_device,
\r
362 &g_active_menu_count))->drop();
\r
365 if(event.KeyInput.Key == irr::KEY_KEY_I)
\r
367 dstream<<DTIME<<"MyEventReceiver: "
\r
368 <<"Launching inventory"<<std::endl;
\r
369 (new GUIInventoryMenu(guienv, guiroot, -1,
\r
370 &local_inventory, &inventory_action_queue,
\r
371 &g_active_menu_count))->drop();
\r
376 // Material selection
\r
377 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
379 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
382 g_selected_item = 0;
\r
383 dstream<<DTIME<<"Selected item: "
\r
384 <<g_selected_item<<std::endl;
\r
387 // Viewing range selection
\r
388 if(event.KeyInput.Key == irr::KEY_KEY_R)
\r
390 JMutexAutoLock lock(g_range_mutex);
\r
391 if(g_viewing_range_all)
\r
393 g_viewing_range_all = false;
\r
394 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
398 g_viewing_range_all = true;
\r
399 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
403 // Print debug stacks
\r
404 if(event.KeyInput.Key == irr::KEY_KEY_P)
\r
406 dstream<<"-----------------------------------------"
\r
408 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
409 dstream<<"-----------------------------------------"
\r
411 debug_stacks_print();
\r
416 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
418 if(noMenuActive() == false)
\r
420 left_active = false;
\r
421 middle_active = false;
\r
422 right_active = false;
\r
426 //dstream<<"MyEventReceiver: mouse input"<<std::endl;
\r
427 left_active = event.MouseInput.isLeftPressed();
\r
428 middle_active = event.MouseInput.isMiddlePressed();
\r
429 right_active = event.MouseInput.isRightPressed();
\r
431 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
433 leftclicked = true;
\r
435 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
437 rightclicked = true;
\r
439 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
441 leftreleased = true;
\r
443 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
445 rightreleased = true;
\r
447 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
449 /*dstream<<"event.MouseInput.Wheel="
\r
450 <<event.MouseInput.Wheel<<std::endl;*/
\r
451 if(event.MouseInput.Wheel < 0)
\r
453 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
456 g_selected_item = 0;
\r
458 else if(event.MouseInput.Wheel > 0)
\r
460 if(g_selected_item > 0)
\r
463 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
472 // This is used to check whether a key is being held down
\r
473 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
475 return keyIsDown[keyCode];
\r
480 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
481 keyIsDown[i] = false;
\r
483 leftclicked = false;
\r
484 rightclicked = false;
\r
485 leftreleased = false;
\r
486 rightreleased = false;
\r
488 left_active = false;
\r
489 middle_active = false;
\r
490 right_active = false;
\r
501 bool rightreleased;
\r
504 bool middle_active;
\r
508 // We use this array to store the current state of each key
\r
509 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
512 IrrlichtDevice *m_device;
\r
521 virtual ~InputHandler()
\r
525 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
527 virtual v2s32 getMousePos() = 0;
\r
528 virtual void setMousePos(s32 x, s32 y) = 0;
\r
530 virtual bool getLeftState() = 0;
\r
531 virtual bool getRightState() = 0;
\r
533 virtual bool getLeftClicked() = 0;
\r
534 virtual bool getRightClicked() = 0;
\r
535 virtual void resetLeftClicked() = 0;
\r
536 virtual void resetRightClicked() = 0;
\r
538 virtual bool getLeftReleased() = 0;
\r
539 virtual bool getRightReleased() = 0;
\r
540 virtual void resetLeftReleased() = 0;
\r
541 virtual void resetRightReleased() = 0;
\r
543 virtual void step(float dtime) {};
\r
545 virtual void clear() {};
\r
548 InputHandler *g_input = NULL;
\r
550 class RealInputHandler : public InputHandler
\r
553 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
555 m_receiver(receiver)
\r
558 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
560 return m_receiver->IsKeyDown(keyCode);
\r
562 virtual v2s32 getMousePos()
\r
564 return m_device->getCursorControl()->getPosition();
\r
566 virtual void setMousePos(s32 x, s32 y)
\r
568 m_device->getCursorControl()->setPosition(x, y);
\r
571 virtual bool getLeftState()
\r
573 return m_receiver->left_active;
\r
575 virtual bool getRightState()
\r
577 return m_receiver->right_active;
\r
580 virtual bool getLeftClicked()
\r
582 return m_receiver->leftclicked;
\r
584 virtual bool getRightClicked()
\r
586 return m_receiver->rightclicked;
\r
588 virtual void resetLeftClicked()
\r
590 m_receiver->leftclicked = false;
\r
592 virtual void resetRightClicked()
\r
594 m_receiver->rightclicked = false;
\r
597 virtual bool getLeftReleased()
\r
599 return m_receiver->leftreleased;
\r
601 virtual bool getRightReleased()
\r
603 return m_receiver->rightreleased;
\r
605 virtual void resetLeftReleased()
\r
607 m_receiver->leftreleased = false;
\r
609 virtual void resetRightReleased()
\r
611 m_receiver->rightreleased = false;
\r
616 resetRightClicked();
\r
617 resetLeftClicked();
\r
620 IrrlichtDevice *m_device;
\r
621 MyEventReceiver *m_receiver;
\r
624 class RandomInputHandler : public InputHandler
\r
627 RandomInputHandler()
\r
629 leftclicked = false;
\r
630 rightclicked = false;
\r
631 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
632 keydown[i] = false;
\r
634 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
636 return keydown[keyCode];
\r
638 virtual v2s32 getMousePos()
\r
642 virtual void setMousePos(s32 x, s32 y)
\r
644 mousepos = v2s32(x,y);
\r
647 virtual bool getLeftState()
\r
651 virtual bool getRightState()
\r
656 virtual bool getLeftClicked()
\r
658 return leftclicked;
\r
660 virtual bool getRightClicked()
\r
662 return rightclicked;
\r
664 virtual void resetLeftClicked()
\r
666 leftclicked = false;
\r
668 virtual void resetRightClicked()
\r
670 rightclicked = false;
\r
673 virtual bool getLeftReleased()
\r
677 virtual bool getRightReleased()
\r
681 virtual void resetLeftReleased()
\r
684 virtual void resetRightReleased()
\r
688 virtual void step(float dtime)
\r
691 static float counter1 = 0;
\r
695 counter1 = 0.1*Rand(1,10);
\r
696 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
697 g_selected_material++;
\r
699 g_selected_material = 0;*/
\r
700 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
703 g_selected_item = 0;
\r
707 static float counter1 = 0;
\r
711 counter1 = 0.1*Rand(1, 40);
\r
712 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
716 static float counter1 = 0;
\r
720 counter1 = 0.1*Rand(1, 40);
\r
721 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
725 static float counter1 = 0;
\r
729 counter1 = 0.1*Rand(1, 40);
\r
730 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
734 static float counter1 = 0;
\r
738 counter1 = 0.1*Rand(1, 40);
\r
739 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
743 static float counter1 = 0;
\r
747 counter1 = 0.1*Rand(1, 20);
\r
748 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
752 static float counter1 = 0;
\r
756 counter1 = 0.1*Rand(1, 30);
\r
757 leftclicked = true;
\r
761 static float counter1 = 0;
\r
765 counter1 = 0.1*Rand(1, 20);
\r
766 rightclicked = true;
\r
769 mousepos += mousespeed;
\r
772 s32 Rand(s32 min, s32 max)
\r
774 return (rand()%(max-min+1))+min;
\r
777 bool keydown[KEY_KEY_CODES_COUNT];
\r
784 void updateViewingRange(f32 frametime, Client *client)
\r
786 // Range_all messes up frametime_avg
\r
787 if(g_viewing_range_all == true)
\r
790 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
792 // Initialize to the target value
\r
793 static float frametime_avg = 1.0/wanted_fps;
\r
794 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
795 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
797 static f32 counter = 0;
\r
799 counter -= frametime;
\r
802 //counter = 1.0; //seconds
\r
803 counter = 0.5; //seconds
\r
805 //float freetime_ratio = 0.2;
\r
806 //float freetime_ratio = 0.4;
\r
807 float freetime_ratio = FREETIME_RATIO;
\r
809 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
811 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
813 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
814 + frametime_avg / frametime_wanted / 2.0;*/
\r
816 //float fraction = frametime_avg / frametime_wanted;
\r
818 static bool fraction_is_good = false;
\r
820 //float fraction_good_threshold = 0.1;
\r
821 //float fraction_bad_threshold = 0.25;
\r
822 float fraction_good_threshold = 0.075;
\r
823 float fraction_bad_threshold = 0.125;
\r
824 float fraction_limit;
\r
825 // Use high limit if fraction is good AND the fraction would
\r
826 // lower the range. We want to keep the range fairly high.
\r
827 if(fraction_is_good && fraction > 1.0)
\r
828 fraction_limit = fraction_bad_threshold;
\r
830 fraction_limit = fraction_good_threshold;
\r
832 if(fabs(fraction - 1.0) < fraction_limit)
\r
834 fraction_is_good = true;
\r
839 fraction_is_good = false;
\r
842 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
843 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
844 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
845 <<" faction = "<<fraction<<std::endl;*/
\r
847 JMutexAutoLock lock(g_range_mutex);
\r
849 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
850 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
852 s16 n = (float)g_viewing_range_nodes / fraction;
\r
853 if(n < viewing_range_nodes_min)
\r
854 n = viewing_range_nodes_min;
\r
855 if(n > viewing_range_nodes_max)
\r
856 n = viewing_range_nodes_max;
\r
858 bool can_change = true;
\r
860 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
861 can_change = false;
\r
864 g_viewing_range_nodes = n;
\r
866 /*dstream<<"g_viewing_range_nodes = "
\r
867 <<g_viewing_range_nodes<<std::endl;*/
\r
870 class GUIQuickInventory : public IEventReceiver
\r
874 gui::IGUIEnvironment* env,
\r
875 gui::IGUIElement* parent,
\r
878 Inventory *inventory):
\r
879 m_itemcount(itemcount),
\r
880 m_inventory(inventory)
\r
882 core::rect<s32> imgsize(0,0,48,48);
\r
883 core::rect<s32> textsize(0,0,48,16);
\r
884 v2s32 spacing(0, 64);
\r
885 for(s32 i=0; i<m_itemcount; i++)
\r
887 m_images.push_back(env->addImage(
\r
888 imgsize + pos + spacing*i
\r
890 m_images[i]->setScaleImage(true);
\r
891 m_texts.push_back(env->addStaticText(
\r
893 textsize + pos + spacing*i,
\r
896 m_texts[i]->setBackgroundColor(
\r
897 video::SColor(128,0,0,0));
\r
898 m_texts[i]->setTextAlignment(
\r
900 gui::EGUIA_UPPERLEFT);
\r
904 virtual bool OnEvent(const SEvent& event)
\r
909 void setSelection(s32 i)
\r
918 start = m_selection - m_itemcount / 2;
\r
920 InventoryList *mainlist = m_inventory->getList("main");
\r
922 for(s32 i=0; i<m_itemcount; i++)
\r
926 if(j > (s32)mainlist->getSize() - 1)
\r
927 j -= mainlist->getSize();
\r
929 j += mainlist->getSize();
\r
931 InventoryItem *item = mainlist->getItem(j);
\r
935 m_images[i]->setImage(NULL);
\r
938 if(m_selection == j)
\r
939 swprintf(t, 10, L"<-");
\r
941 swprintf(t, 10, L"");
\r
942 m_texts[i]->setText(t);
\r
944 // The next ifs will segfault with a NULL pointer
\r
949 m_images[i]->setImage(item->getImage());
\r
952 if(m_selection == j)
\r
953 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
955 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
956 m_texts[i]->setText(t);
\r
962 core::array<gui::IGUIStaticText*> m_texts;
\r
963 core::array<gui::IGUIImage*> m_images;
\r
964 Inventory *m_inventory;
\r
972 struct TextDestSign : public TextDest
\r
974 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
976 m_blockpos = blockpos;
\r
980 void gotText(std::wstring text)
\r
982 std::string ntext = wide_to_narrow(text);
\r
983 dstream<<"Changing text of a sign object: "
\r
984 <<ntext<<std::endl;
\r
985 m_client->sendSignText(m_blockpos, m_id, ntext);
\r
993 int main(int argc, char *argv[])
\r
996 Low-level initialization
\r
999 bool disable_stderr = false;
\r
1001 disable_stderr = true;
\r
1004 // Initialize debug streams
\r
1005 debugstreams_init(disable_stderr, DEBUGFILE);
\r
1006 // Initialize debug stacks
\r
1007 debug_stacks_init();
\r
1009 DSTACK(__FUNCTION_NAME);
\r
1015 Parse command line
\r
1018 // List all allowed options
\r
1019 core::map<std::string, ValueSpec> allowed_options;
\r
1020 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
1021 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
1022 "Run server directly"));
\r
1023 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
1024 "Load configuration from specified file"));
\r
1025 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
1026 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
1027 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
1028 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1029 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1031 Settings cmd_args;
\r
1033 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1035 if(ret == false || cmd_args.getFlag("help"))
\r
1037 dstream<<"Allowed options:"<<std::endl;
\r
1038 for(core::map<std::string, ValueSpec>::Iterator
\r
1039 i = allowed_options.getIterator();
\r
1040 i.atEnd() == false; i++)
\r
1042 dstream<<" --"<<i.getNode()->getKey();
\r
1043 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1048 dstream<<" <value>";
\r
1050 dstream<<std::endl;
\r
1052 if(i.getNode()->getValue().help != NULL)
\r
1054 dstream<<" "<<i.getNode()->getValue().help
\r
1059 return cmd_args.getFlag("help") ? 0 : 1;
\r
1064 Basic initialization
\r
1067 // Initialize default settings
\r
1068 set_default_settings();
\r
1070 // Print startup message
\r
1071 dstream<<DTIME<<"minetest-c55"
\r
1072 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1073 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1076 // Set locale. This is for forcing '.' as the decimal point.
\r
1077 std::locale::global(std::locale("C"));
\r
1078 // This enables printing all characters in bitmap font
\r
1079 setlocale(LC_CTYPE, "en_US");
\r
1081 // Initialize sockets
\r
1083 atexit(sockets_cleanup);
\r
1093 // Path of configuration file in use
\r
1094 std::string configpath = "";
\r
1096 if(cmd_args.exists("config"))
\r
1098 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1101 dstream<<"Could not read configuration from \""
\r
1102 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1105 configpath = cmd_args.get("config");
\r
1109 const char *filenames[2] =
\r
1111 "../minetest.conf",
\r
1112 "../../minetest.conf"
\r
1115 for(u32 i=0; i<2; i++)
\r
1117 bool r = g_settings.readConfigFile(filenames[i]);
\r
1120 configpath = filenames[i];
\r
1126 // Initialize random seed
\r
1132 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1133 || cmd_args.getFlag("enable-unittests") == true)
\r
1139 Global range mutex
\r
1141 g_range_mutex.Init();
\r
1142 assert(g_range_mutex.IsInitialized());
\r
1144 // Read map parameters from settings
\r
1146 HMParams hm_params;
\r
1147 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1148 hm_params.randmax = g_settings.get("height_randmax");
\r
1149 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1150 hm_params.base = g_settings.get("height_base");
\r
1152 MapParams map_params;
\r
1153 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1154 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1160 std::cout<<std::endl<<std::endl;
\r
1163 <<" .__ __ __ "<<std::endl
\r
1164 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1165 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1166 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1167 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1168 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1171 std::cout<<std::endl;
\r
1172 //char templine[100];
\r
1176 if(cmd_args.exists("port"))
\r
1178 port = cmd_args.getU16("port");
\r
1182 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1183 std::cout<<"-> "<<port<<std::endl;
\r
1186 if(cmd_args.getFlag("server"))
\r
1188 DSTACK("Dedicated server branch");
\r
1190 std::cout<<std::endl;
\r
1191 std::cout<<"========================"<<std::endl;
\r
1192 std::cout<<"Running dedicated server"<<std::endl;
\r
1193 std::cout<<"========================"<<std::endl;
\r
1194 std::cout<<std::endl;
\r
1196 Server server("../map", hm_params, map_params);
\r
1197 server.start(port);
\r
1201 // This is kind of a hack but can be done like this
\r
1202 // because server.step() is very light
\r
1204 server.step(0.030);
\r
1206 static int counter = 0;
\r
1212 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1213 core::list<PlayerInfo>::Iterator i;
\r
1214 static u32 sum_old = 0;
\r
1215 u32 sum = PIChecksum(list);
\r
1216 if(sum != sum_old)
\r
1218 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1219 for(i=list.begin(); i!=list.end(); i++)
\r
1221 i->PrintLine(&std::cout);
\r
1231 bool hosting = false;
\r
1232 char connect_name[100] = "";
\r
1234 if(cmd_args.exists("address"))
\r
1236 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1238 else if(is_yes(g_settings.get("host_game")) == false)
\r
1240 if(g_settings.get("address") != "")
\r
1242 std::cout<<g_settings.get("address")<<std::endl;
\r
1243 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1247 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1248 std::cin.getline(connect_name, 100);
\r
1252 if(connect_name[0] == 0){
\r
1253 snprintf(connect_name, 100, "127.0.0.1");
\r
1258 std::cout<<"> Hosting game"<<std::endl;
\r
1260 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1262 char playername[PLAYERNAME_SIZE] = "";
\r
1263 if(g_settings.get("name") != "")
\r
1265 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1269 std::cout<<"Name of player: ";
\r
1270 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1272 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1275 Resolution selection
\r
1278 bool fullscreen = false;
\r
1279 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1280 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1284 MyEventReceiver receiver;
\r
1286 video::E_DRIVER_TYPE driverType;
\r
1289 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1290 driverType = video::EDT_OPENGL;
\r
1292 driverType = video::EDT_OPENGL;
\r
1295 // create device and exit if creation failed
\r
1297 IrrlichtDevice *device;
\r
1298 device = createDevice(driverType,
\r
1299 core::dimension2d<u32>(screenW, screenH),
\r
1300 16, fullscreen, false, false, &receiver);
\r
1303 return 1; // could not create selected driver.
\r
1305 g_device = device;
\r
1306 g_irrlicht = new IrrlichtWrapper(device);
\r
1308 //g_device = device;
\r
1310 device->setResizable(true);
\r
1312 bool random_input = g_settings.getBool("random_input")
\r
1313 || cmd_args.getFlag("random-input");
\r
1315 g_input = new RandomInputHandler();
\r
1317 g_input = new RealInputHandler(device, &receiver);
\r
1320 Continue initialization
\r
1323 video::IVideoDriver* driver = device->getVideoDriver();
\r
1326 This changes the minimum allowed number of vertices in a VBO
\r
1328 //driver->setMinHardwareBufferVertexCount(1);
\r
1330 scene::ISceneManager* smgr = device->getSceneManager();
\r
1332 guienv = device->getGUIEnvironment();
\r
1333 gui::IGUISkin* skin = guienv->getSkin();
\r
1334 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1336 skin->setFont(font);
\r
1337 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1338 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1339 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1340 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1341 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1342 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1344 const wchar_t *text = L"Loading and connecting...";
\r
1345 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1346 core::dimension2d<u32> textd = font->getDimension(text);
\r
1347 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1348 // Have to add a bit to disable the text from word wrapping
\r
1349 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1350 core::vector2d<s32> textsize(300, textd.Height);
\r
1351 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1353 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1354 text, textrect, false, false);
\r
1355 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1357 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1358 guienv->drawAll();
\r
1359 driver->endScene();
\r
1362 Preload some textures
\r
1365 tile_materials_preload(g_irrlicht);
\r
1368 Make a scope here for the client so that it gets removed
\r
1369 before the irrlicht device
\r
1373 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1378 SharedPtr<Server> server;
\r
1380 server = new Server("../map", hm_params, map_params);
\r
1381 server->start(port);
\r
1388 Client client(device, playername,
\r
1390 g_viewing_range_nodes,
\r
1391 g_viewing_range_all);
\r
1393 Address connect_address(0,0,0,0, port);
\r
1395 connect_address.Resolve(connect_name);
\r
1397 catch(ResolveError &e)
\r
1399 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1403 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1404 client.connect(connect_address);
\r
1407 while(client.connectedAndInitialized() == false)
\r
1410 if(server != NULL){
\r
1411 server->step(0.1);
\r
1416 catch(con::PeerNotFoundException &e)
\r
1418 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1425 /*scene::ISceneNode* skybox;
\r
1426 skybox = smgr->addSkyBoxSceneNode(
\r
1427 driver->getTexture("../data/skybox2.png"),
\r
1428 driver->getTexture("../data/skybox3.png"),
\r
1429 driver->getTexture("../data/skybox1.png"),
\r
1430 driver->getTexture("../data/skybox1.png"),
\r
1431 driver->getTexture("../data/skybox1.png"),
\r
1432 driver->getTexture("../data/skybox1.png"));*/
\r
1435 Create the camera node
\r
1438 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1439 0, // Camera parent
\r
1440 v3f(BS*100, BS*2, BS*100), // Look from
\r
1441 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1445 if(camera == NULL)
\r
1448 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1450 camera->setFOV(FOV_ANGLE);
\r
1452 // Just so big a value that everything rendered is visible
\r
1453 camera->setFarValue(100000*BS);
\r
1455 f32 camera_yaw = 0; // "right/left"
\r
1456 f32 camera_pitch = 0; // "up/down"
\r
1462 gui_loadingtext->remove();
\r
1465 Add some gui stuff
\r
1468 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1469 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1472 We need some kind of a root node to be able to add
\r
1473 custom elements directly on the screen.
\r
1474 Otherwise they won't be automatically drawn.
\r
1476 guiroot = guienv->addStaticText(L"",
\r
1477 core::rect<s32>(0, 0, 10000, 10000));
\r
1479 // Test the text input system
\r
1480 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_active_menu_count,
\r
1483 // First line of debug text
\r
1484 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1486 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1488 // Second line of debug text
\r
1489 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1491 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1494 // At the middle of the screen
\r
1495 // Object infos are shown in this
\r
1496 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1498 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1502 Some statistics are collected in these
\r
1505 u32 beginscenetime = 0;
\r
1506 u32 scenetime = 0;
\r
1507 u32 endscenetime = 0;
\r
1513 bool first_loop_after_window_activation = true;
\r
1515 // Time is in milliseconds
\r
1516 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1517 // NOTE: Have to call run() between calls of this to update the timer
\r
1518 u32 lasttime = device->getTimer()->getTime();
\r
1520 while(device->run())
\r
1523 Run global IrrlichtWrapper's main thread processing stuff
\r
1525 g_irrlicht->Run();
\r
1528 Random calculations
\r
1530 v2u32 screensize = driver->getScreenSize();
\r
1531 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1533 // Hilight boxes collected during the loop and displayed
\r
1534 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1537 std::wstring infotext;
\r
1539 //TimeTaker //timer1("//timer1");
\r
1541 // Time of frame without fps limit
\r
1545 // not using getRealTime is necessary for wine
\r
1546 u32 time = device->getTimer()->getTime();
\r
1547 if(time > lasttime)
\r
1548 busytime_u32 = time - lasttime;
\r
1551 busytime = busytime_u32 / 1000.0;
\r
1554 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1556 // Absolutelu necessary for wine!
\r
1563 updateViewingRange(busytime, &client);
\r
1570 float fps_max = g_settings.getFloat("fps_max");
\r
1571 u32 frametime_min = 1000./fps_max;
\r
1573 if(busytime_u32 < frametime_min)
\r
1575 u32 sleeptime = frametime_min - busytime_u32;
\r
1576 device->sleep(sleeptime);
\r
1580 // Absolutelu necessary for wine!
\r
1584 Time difference calculation
\r
1586 f32 dtime; // in seconds
\r
1588 u32 time = device->getTimer()->getTime();
\r
1589 if(time > lasttime)
\r
1590 dtime = (time - lasttime) / 1000.0;
\r
1596 Time average and jitter calculation
\r
1599 static f32 dtime_avg1 = 0.0;
\r
1600 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1601 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1603 static f32 dtime_jitter1_max_sample = 0.0;
\r
1604 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1606 static f32 jitter1_max = 0.0;
\r
1607 static f32 counter = 0.0;
\r
1608 if(dtime_jitter1 > jitter1_max)
\r
1609 jitter1_max = dtime_jitter1;
\r
1614 dtime_jitter1_max_sample = jitter1_max;
\r
1615 dtime_jitter1_max_fraction
\r
1616 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1617 jitter1_max = 0.0;
\r
1620 Control freetime ratio
\r
1622 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1624 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1625 g_freetime_ratio += 0.01;
\r
1629 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1630 g_freetime_ratio -= 0.01;
\r
1636 Busytime average and jitter calculation
\r
1639 static f32 busytime_avg1 = 0.0;
\r
1640 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1641 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1643 static f32 busytime_jitter1_max_sample = 0.0;
\r
1644 static f32 busytime_jitter1_min_sample = 0.0;
\r
1646 static f32 jitter1_max = 0.0;
\r
1647 static f32 jitter1_min = 0.0;
\r
1648 static f32 counter = 0.0;
\r
1649 if(busytime_jitter1 > jitter1_max)
\r
1650 jitter1_max = busytime_jitter1;
\r
1651 if(busytime_jitter1 < jitter1_min)
\r
1652 jitter1_min = busytime_jitter1;
\r
1654 if(counter > 0.0){
\r
1656 busytime_jitter1_max_sample = jitter1_max;
\r
1657 busytime_jitter1_min_sample = jitter1_min;
\r
1658 jitter1_max = 0.0;
\r
1659 jitter1_min = 0.0;
\r
1664 Debug info for client
\r
1667 static float counter = 0.0;
\r
1672 client.printDebugInfo(std::cout);
\r
1677 Input handler step()
\r
1679 g_input->step(dtime);
\r
1682 Player speed control
\r
1691 bool a_superspeed,
\r
1694 PlayerControl control(
\r
1695 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1696 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1697 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1698 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1699 g_input->isKeyDown(irr::KEY_SPACE),
\r
1700 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1704 client.setPlayerControl(control);
\r
1708 Process environment
\r
1712 //TimeTaker timer("client.step(dtime)");
\r
1713 client.step(dtime);
\r
1714 //client.step(dtime_avg1);
\r
1717 if(server != NULL)
\r
1719 //TimeTaker timer("server->step(dtime)");
\r
1720 server->step(dtime);
\r
1723 v3f player_position = client.getPlayerPosition();
\r
1725 //TimeTaker //timer2("//timer2");
\r
1728 Mouse and camera control
\r
1731 if((device->isWindowActive() && noMenuActive()) || random_input)
\r
1734 device->getCursorControl()->setVisible(false);
\r
1736 if(first_loop_after_window_activation){
\r
1737 //std::cout<<"window active, first loop"<<std::endl;
\r
1738 first_loop_after_window_activation = false;
\r
1741 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1742 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1743 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1744 camera_yaw -= dx*0.2;
\r
1745 camera_pitch += dy*0.2;
\r
1746 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1747 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1749 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1752 device->getCursorControl()->setVisible(true);
\r
1754 //std::cout<<"window inactive"<<std::endl;
\r
1755 first_loop_after_window_activation = true;
\r
1758 camera_yaw = wrapDegrees(camera_yaw);
\r
1759 camera_pitch = wrapDegrees(camera_pitch);
\r
1761 v3f camera_direction = v3f(0,0,1);
\r
1762 camera_direction.rotateYZBy(camera_pitch);
\r
1763 camera_direction.rotateXZBy(camera_yaw);
\r
1765 v3f camera_position =
\r
1766 player_position + v3f(0, BS+BS/2, 0);
\r
1768 camera->setPosition(camera_position);
\r
1769 // *100.0 helps in large map coordinates
\r
1770 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1772 if(FIELD_OF_VIEW_TEST){
\r
1773 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1774 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1777 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1778 //TimeTaker timer("client.updateCamera");
\r
1779 client.updateCamera(camera_position, camera_direction);
\r
1783 //TimeTaker //timer3("//timer3");
\r
1786 Calculate what block is the crosshair pointing to
\r
1789 //u32 t1 = device->getTimer()->getRealTime();
\r
1791 //f32 d = 4; // max. distance
\r
1792 f32 d = 4; // max. distance
\r
1793 core::line3d<f32> shootline(camera_position,
\r
1794 camera_position + camera_direction * BS * (d+1));
\r
1796 MapBlockObject *selected_object = client.getSelectedObject
\r
1797 (d*BS, camera_position, shootline);
\r
1799 if(selected_object != NULL)
\r
1801 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1803 core::aabbox3d<f32> box_on_map
\r
1804 = selected_object->getSelectionBoxOnMap();
\r
1806 hilightboxes.push_back(box_on_map);
\r
1808 infotext = narrow_to_wide(selected_object->infoText());
\r
1810 if(g_input->getLeftClicked())
\r
1812 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1813 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1814 selected_object->getId(), g_selected_item);
\r
1816 else if(g_input->getRightClicked())
\r
1818 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1820 Check if we want to modify the object ourselves
\r
1822 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1824 dstream<<"Sign object right-clicked"<<std::endl;
\r
1826 if(random_input == false)
\r
1828 // Get a new text for it
\r
1830 TextDest *dest = new TextDestSign(
\r
1831 selected_object->getBlock()->getPos(),
\r
1832 selected_object->getId(),
\r
1835 SignObject *sign_object = (SignObject*)selected_object;
\r
1837 std::wstring wtext =
\r
1838 narrow_to_wide(sign_object->getText());
\r
1840 (new GUITextInputMenu(guienv, guiroot, -1,
\r
1841 &g_active_menu_count, dest,
\r
1846 Otherwise pass the event to the server as-is
\r
1850 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1851 selected_object->getId(), g_selected_item);
\r
1855 else // selected_object == NULL
\r
1859 Find out which node we are pointing at
\r
1862 bool nodefound = false;
\r
1864 v3s16 neighbourpos;
\r
1865 core::aabbox3d<f32> nodefacebox;
\r
1866 f32 mindistance = BS * 1001;
\r
1868 v3s16 pos_i = floatToInt(player_position);
\r
1870 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1874 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1875 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1876 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1877 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1878 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1879 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1881 for(s16 y = ystart; y <= yend; y++)
\r
1882 for(s16 z = zstart; z <= zend; z++)
\r
1883 for(s16 x = xstart; x <= xend; x++)
\r
1888 n = client.getNode(v3s16(x,y,z));
\r
1889 if(content_pointable(n.d) == false)
\r
1892 catch(InvalidPositionException &e)
\r
1898 v3f npf = intToFloat(np);
\r
1903 v3s16(0,0,1), // back
\r
1904 v3s16(0,1,0), // top
\r
1905 v3s16(1,0,0), // right
\r
1906 v3s16(0,0,-1), // front
\r
1907 v3s16(0,-1,0), // bottom
\r
1908 v3s16(-1,0,0), // left
\r
1914 if(n.d == CONTENT_TORCH)
\r
1916 v3s16 dir = unpackDir(n.dir);
\r
1917 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1918 dir_f *= BS/2 - BS/6 - BS/20;
\r
1919 v3f cpf = npf + dir_f;
\r
1920 f32 distance = (cpf - camera_position).getLength();
\r
1922 core::aabbox3d<f32> box;
\r
1925 if(dir == v3s16(0,-1,0))
\r
1927 box = core::aabbox3d<f32>(
\r
1928 npf - v3f(BS/6, BS/2, BS/6),
\r
1929 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
1933 else if(dir == v3s16(0,1,0))
\r
1935 box = core::aabbox3d<f32>(
\r
1936 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
1937 npf + v3f(BS/6, BS/2, BS/6)
\r
1943 box = core::aabbox3d<f32>(
\r
1944 cpf - v3f(BS/6, BS/3, BS/6),
\r
1945 cpf + v3f(BS/6, BS/3, BS/6)
\r
1949 if(distance < mindistance)
\r
1951 if(box.intersectsWithLine(shootline))
\r
1955 neighbourpos = np;
\r
1956 mindistance = distance;
\r
1957 nodefacebox = box;
\r
1966 for(u16 i=0; i<6; i++)
\r
1968 v3f dir_f = v3f(dirs[i].X,
\r
1969 dirs[i].Y, dirs[i].Z);
\r
1970 v3f centerpoint = npf + dir_f * BS/2;
\r
1972 (centerpoint - camera_position).getLength();
\r
1974 if(distance < mindistance)
\r
1976 core::CMatrix4<f32> m;
\r
1977 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
1979 // This is the back face
\r
1980 v3f corners[2] = {
\r
1981 v3f(BS/2, BS/2, BS/2),
\r
1982 v3f(-BS/2, -BS/2, BS/2+d)
\r
1985 for(u16 j=0; j<2; j++)
\r
1987 m.rotateVect(corners[j]);
\r
1988 corners[j] += npf;
\r
1991 core::aabbox3d<f32> facebox(corners[0]);
\r
1992 facebox.addInternalPoint(corners[1]);
\r
1994 if(facebox.intersectsWithLine(shootline))
\r
1998 neighbourpos = np + dirs[i];
\r
1999 mindistance = distance;
\r
2000 nodefacebox = facebox;
\r
2002 } // if distance < mindistance
\r
2004 } // regular block
\r
2009 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2011 static float dig_time = 0.0;
\r
2012 static u16 dig_index = 0;
\r
2014 if(nodepos != nodepos_old)
\r
2016 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2017 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2019 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2021 client.clearTempMod(nodepos_old);
\r
2026 hilightboxes.push_back(nodefacebox);
\r
2028 if(g_input->getLeftReleased())
\r
2030 client.clearTempMod(nodepos);
\r
2033 if(g_input->getLeftClicked() ||
\r
2034 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2036 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2037 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2039 if(g_input->getLeftClicked())
\r
2041 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2043 if(g_input->getLeftState())
\r
2045 float dig_time_complete = 0.5;
\r
2046 MapNode n = client.getNode(nodepos);
\r
2047 if(n.d == CONTENT_STONE)
\r
2048 dig_time_complete = 1.5;
\r
2050 float dig_time_complete0 = dig_time_complete+client.getAvgRtt()*2;
\r
2051 if(dig_time_complete0 < 0.0)
\r
2052 dig_time_complete0 = 0.0;
\r
2054 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
\r
2055 * dig_time/dig_time_complete0);
\r
2057 if(dig_time > 0.125 && dig_index < CRACK_ANIMATION_LENGTH)
\r
2059 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2060 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2063 dig_time += dtime;
\r
2066 if(g_input->getRightClicked())
\r
2068 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2069 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2072 nodepos_old = nodepos;
\r
2077 } // selected_object == NULL
\r
2079 g_input->resetLeftClicked();
\r
2080 g_input->resetRightClicked();
\r
2082 if(g_input->getLeftReleased())
\r
2084 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2085 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2087 if(g_input->getRightReleased())
\r
2089 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2093 g_input->resetLeftReleased();
\r
2094 g_input->resetRightReleased();
\r
2097 Calculate stuff for drawing
\r
2100 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2102 u32 daynight_ratio = client.getDayNightRatio();
\r
2103 video::SColor bgcolor = video::SColor(
\r
2105 skycolor.getRed() * daynight_ratio / 1000,
\r
2106 skycolor.getGreen() * daynight_ratio / 1000,
\r
2107 skycolor.getBlue() * daynight_ratio / 1000);
\r
2113 if(g_settings.getBool("enable_fog") == true)
\r
2115 f32 range = g_viewing_range_nodes * BS;
\r
2116 if(g_viewing_range_all)
\r
2117 range = 100000*BS;
\r
2121 video::EFT_FOG_LINEAR,
\r
2125 false, // pixel fog
\r
2126 false // range fog
\r
2132 Update gui stuff (0ms)
\r
2135 //TimeTaker guiupdatetimer("Gui updating");
\r
2138 wchar_t temptext[150];
\r
2140 static float drawtime_avg = 0;
\r
2141 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2142 static float beginscenetime_avg = 0;
\r
2143 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2144 static float scenetime_avg = 0;
\r
2145 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2146 static float endscenetime_avg = 0;
\r
2147 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2149 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2151 L", R: range_all=%i"
\r
2153 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2155 g_viewing_range_all,
\r
2157 beginscenetime_avg,
\r
2162 guitext->setText(temptext);
\r
2166 wchar_t temptext[150];
\r
2167 swprintf(temptext, 150,
\r
2168 L"(% .1f, % .1f, % .1f)"
\r
2169 L" (% .3f < btime_jitter < % .3f"
\r
2170 L", dtime_jitter = % .1f %%)",
\r
2171 player_position.X/BS,
\r
2172 player_position.Y/BS,
\r
2173 player_position.Z/BS,
\r
2174 busytime_jitter1_min_sample,
\r
2175 busytime_jitter1_max_sample,
\r
2176 dtime_jitter1_max_fraction * 100.0
\r
2179 guitext2->setText(temptext);
\r
2183 /*wchar_t temptext[100];
\r
2184 swprintf(temptext, 100,
\r
2185 SWPRINTF_CHARSTRING,
\r
2186 infotext.substr(0,99).c_str()
\r
2189 guitext_info->setText(temptext);*/
\r
2191 guitext_info->setText(infotext.c_str());
\r
2198 static u16 old_selected_item = 65535;
\r
2199 if(client.getLocalInventoryUpdated()
\r
2200 || g_selected_item != old_selected_item)
\r
2202 old_selected_item = g_selected_item;
\r
2203 //std::cout<<"Updating local inventory"<<std::endl;
\r
2204 client.getLocalInventory(local_inventory);
\r
2205 quick_inventory->setSelection(g_selected_item);
\r
2206 quick_inventory->update();
\r
2210 Send actions returned by the inventory menu
\r
2212 while(inventory_action_queue.size() != 0)
\r
2214 InventoryAction *a = inventory_action_queue.pop_front();
\r
2216 client.sendInventoryAction(a);
\r
2225 TimeTaker drawtimer("Drawing");
\r
2229 TimeTaker timer("beginScene");
\r
2230 driver->beginScene(true, true, bgcolor);
\r
2231 //driver->beginScene(false, true, bgcolor);
\r
2232 beginscenetime = timer.stop(true);
\r
2237 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2240 TimeTaker timer("smgr");
\r
2242 scenetime = timer.stop(true);
\r
2246 //TimeTaker timer9("auxiliary drawings");
\r
2249 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2250 displaycenter + core::vector2d<s32>(10,0),
\r
2251 video::SColor(255,255,255,255));
\r
2252 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2253 displaycenter + core::vector2d<s32>(0,10),
\r
2254 video::SColor(255,255,255,255));
\r
2257 //TimeTaker //timer10("//timer10");
\r
2259 video::SMaterial m;
\r
2261 m.Lighting = false;
\r
2262 driver->setMaterial(m);
\r
2264 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2266 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2267 i != hilightboxes.end(); i++)
\r
2269 /*std::cout<<"hilightbox min="
\r
2270 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2272 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2274 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2280 //TimeTaker //timer11("//timer11");
\r
2286 guienv->drawAll();
\r
2290 TimeTaker timer("endScene");
\r
2291 driver->endScene();
\r
2292 endscenetime = timer.stop(true);
\r
2295 drawtime = drawtimer.stop(true);
\r
2301 static s16 lastFPS = 0;
\r
2302 //u16 fps = driver->getFPS();
\r
2303 u16 fps = (1.0/dtime_avg1);
\r
2305 if (lastFPS != fps)
\r
2307 core::stringw str = L"Minetest [";
\r
2308 str += driver->getName();
\r
2312 device->setWindowCaption(str.c_str());
\r
2318 device->yield();*/
\r
2321 delete quick_inventory;
\r
2323 } // client is deleted at this point
\r
2328 In the end, delete the Irrlicht device.
\r
2333 Update configuration file
\r
2335 /*if(configpath != "")
\r
2337 g_settings.updateConfigFile(configpath.c_str());
\r
2341 catch(con::PeerNotFoundException &e)
\r
2343 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2345 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2347 This is what has to be done in every thread to get suitable debug info
\r
2349 catch(std::exception &e)
\r
2351 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2352 <<e.what()<<std::endl;
\r
2357 debugstreams_deinit();
\r