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
95 SUGG: Precalculate lighting translation table at runtime (at startup)
\r
97 SUGG: A version number to blocks, which increments when the block is
\r
98 modified (node add/remove, water update, lighting update)
\r
99 - This can then be used to make sure the most recent version of
\r
100 a block has been sent to client
\r
102 TODO: Combine MapBlock's face caches to so big pieces that VBO
\r
104 - That is >500 vertices
\r
106 TODO: Better dungeons
\r
111 TODO: Moving players more smoothly. Calculate moving animation
\r
112 in a way that doesn't make the player jump to the right place
\r
113 immediately when the server sends a new position
\r
115 TODO: There are some lighting-related todos and fixmes in
\r
116 ServerMap::emergeBlock
\r
118 TODO: Proper handling of spawning place (try to find something that
\r
119 is not in the middle of an ocean (some land to stand on at
\r
120 least) and save it in map config.
\r
122 TODO: Make the amount of blocks sending to client and the total
\r
123 amount of blocks dynamically limited. Transferring blocks is the
\r
124 main network eater of this system, so it is the one that has
\r
125 to be throttled so that RTTs stay low.
\r
127 TODO: Server to load starting inventory from disk
\r
129 TODO: Players to only be hidden when the client quits.
\r
130 TODO: - Players to be saved on disk, with inventory
\r
131 TODO: Players to be saved as text in map/players/<name>
\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: Untie client network operations from framerate
\r
153 - Needs some input queues or something
\r
155 TODO: Get rid of GotSplitPacketException
\r
157 TODO: Check what goes wrong with caching map to disk (Kray)
\r
159 TODO: Remove LazyMeshUpdater. It is not used as supposed.
\r
161 TODO: TOSERVER_LEAVE
\r
163 TODO: Better handling of objects and mobs
\r
164 - Update brightness according to day-night blended light of node
\r
169 ======================================================================
\r
171 TODO: Get rid of g_irrlicht for server build
\r
173 TODO: Implement getGlobalTime for server build
\r
174 - It is needed for controlling the time used for flowing water
\r
176 ======================================================================
\r
181 Setting this to 1 enables a special camera mode that forces
\r
182 the renderers to think that the camera statically points from
\r
183 the starting place to a static direction.
\r
185 This allows one to move around with the player and see what
\r
186 is actually drawn behind solid things and behind the player.
\r
188 #define FIELD_OF_VIEW_TEST 0
\r
190 #ifdef UNITTEST_DISABLE
\r
192 #pragma message ("Disabling unit tests")
\r
194 #warning "Disabling unit tests"
\r
196 // Disable unit tests
\r
197 #define ENABLE_TESTS 0
\r
199 // Enable unit tests
\r
200 #define ENABLE_TESTS 1
\r
204 #pragma comment(lib, "Irrlicht.lib")
\r
205 #pragma comment(lib, "jthread.lib")
\r
206 #pragma comment(lib, "zlibwapi.lib")
\r
207 // This would get rid of the console window
\r
208 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
\r
212 #define WIN32_LEAN_AND_MEAN
\r
213 #include <windows.h>
\r
214 #define sleep_ms(x) Sleep(x)
\r
216 #include <unistd.h>
\r
217 #define sleep_ms(x) usleep(x*1000)
\r
220 #include <iostream>
\r
222 #include <jmutexautolock.h>
\r
223 #include <locale.h>
\r
224 #include "common_irrlicht.h"
\r
227 #include "player.h"
\r
230 #include "environment.h"
\r
231 #include "server.h"
\r
232 #include "client.h"
\r
233 #include "serialization.h"
\r
234 #include "constants.h"
\r
235 #include "strfnd.h"
\r
236 #include "porting.h"
\r
237 #include "guiPauseMenu.h"
\r
238 #include "irrlichtwrapper.h"
\r
239 #include "gettime.h"
\r
241 IrrlichtWrapper *g_irrlicht;
\r
243 // All range-related stuff below is locked behind this
\r
244 JMutex g_range_mutex;
\r
246 // Blocks are viewed in this range from the player
\r
247 s16 g_viewing_range_nodes = 60;
\r
248 //s16 g_viewing_range_nodes = 0;
\r
250 // This is updated by the client's fetchBlocks routine
\r
251 //s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
\r
253 // If true, the preceding value has no meaning and all blocks
\r
254 // already existing in memory are drawn
\r
255 bool g_viewing_range_all = false;
\r
257 // This is the freetime ratio imposed by the dynamic viewing
\r
258 // range changing code.
\r
259 // It is controlled by the main loop to the smallest value that
\r
260 // inhibits glitches (dtime jitter) in the main loop.
\r
261 //float g_freetime_ratio = FREETIME_RATIO_MAX;
\r
265 These are loaded from the config file.
\r
268 Settings g_settings;
\r
270 extern void set_default_settings();
\r
276 //u16 g_selected_material = 0;
\r
277 u16 g_selected_item = 0;
\r
279 bool g_esc_pressed = false;
\r
281 std::wstring g_text_buffer;
\r
282 bool g_text_buffer_accepted = false;
\r
284 // When true, the mouse and keyboard are grabbed
\r
285 bool g_game_focused = true;
\r
292 std::ostream *dout_con_ptr = &dummyout;
\r
293 std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
294 //std::ostream *dout_con_ptr = &dstream_no_stderr;
\r
295 //std::ostream *derr_con_ptr = &dstream_no_stderr;
\r
296 //std::ostream *dout_con_ptr = &dstream;
\r
297 //std::ostream *derr_con_ptr = &dstream;
\r
300 std::ostream *dout_server_ptr = &dstream;
\r
301 std::ostream *derr_server_ptr = &dstream;
\r
304 std::ostream *dout_client_ptr = &dstream;
\r
305 std::ostream *derr_client_ptr = &dstream;
\r
308 gettime.h implementation
\r
314 Use irrlicht because it is more precise than porting.h's
\r
317 if(g_irrlicht == NULL)
\r
319 return g_irrlicht->getTime();
\r
322 class MyEventReceiver : public IEventReceiver
\r
325 // This is the one method that we have to implement
\r
326 virtual bool OnEvent(const SEvent& event)
\r
328 // Remember whether each key is down or up
\r
329 if(event.EventType == irr::EET_KEY_INPUT_EVENT)
\r
331 keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
\r
333 if(event.KeyInput.PressedDown)
\r
335 //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
\r
336 if(g_game_focused == false)
\r
338 s16 key = event.KeyInput.Key;
\r
339 if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
\r
341 g_text_buffer_accepted = true;
\r
343 else if(key == irr::KEY_BACK)
\r
345 if(g_text_buffer.size() > 0)
\r
346 g_text_buffer = g_text_buffer.substr
\r
347 (0, g_text_buffer.size()-1);
\r
351 wchar_t wc = event.KeyInput.Char;
\r
353 g_text_buffer += wc;
\r
357 if(event.KeyInput.Key == irr::KEY_ESCAPE)
\r
359 //TODO: Not used anymore?
\r
360 if(g_game_focused == true)
\r
362 dstream<<DTIME<<"ESC pressed"<<std::endl;
\r
363 g_esc_pressed = true;
\r
367 // Material selection
\r
368 if(event.KeyInput.Key == irr::KEY_KEY_F)
\r
370 if(g_game_focused == true)
\r
372 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
375 g_selected_item = 0;
\r
376 dstream<<DTIME<<"Selected item: "
\r
377 <<g_selected_item<<std::endl;
\r
381 // Viewing range selection
\r
382 if(event.KeyInput.Key == irr::KEY_KEY_R
\r
385 JMutexAutoLock lock(g_range_mutex);
\r
386 if(g_viewing_range_all)
\r
388 g_viewing_range_all = false;
\r
389 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
\r
393 g_viewing_range_all = true;
\r
394 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
\r
398 // Print debug stacks
\r
399 if(event.KeyInput.Key == irr::KEY_KEY_P
\r
402 dstream<<"-----------------------------------------"
\r
404 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
\r
405 dstream<<"-----------------------------------------"
\r
407 debug_stacks_print();
\r
412 if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
\r
414 left_active = event.MouseInput.isLeftPressed();
\r
415 middle_active = event.MouseInput.isMiddlePressed();
\r
416 right_active = event.MouseInput.isRightPressed();
\r
418 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
\r
420 leftclicked = true;
\r
422 if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
\r
424 rightclicked = true;
\r
426 if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
\r
428 leftreleased = true;
\r
430 if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
\r
432 rightreleased = true;
\r
434 if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
\r
436 /*dstream<<"event.MouseInput.Wheel="
\r
437 <<event.MouseInput.Wheel<<std::endl;*/
\r
438 if(event.MouseInput.Wheel < 0)
\r
440 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
443 g_selected_item = 0;
\r
445 else if(event.MouseInput.Wheel > 0)
\r
447 if(g_selected_item > 0)
\r
450 g_selected_item = PLAYER_INVENTORY_SIZE-1;
\r
458 // This is used to check whether a key is being held down
\r
459 virtual bool IsKeyDown(EKEY_CODE keyCode) const
\r
461 return keyIsDown[keyCode];
\r
466 for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
467 keyIsDown[i] = false;
\r
468 leftclicked = false;
\r
469 rightclicked = false;
\r
470 leftreleased = false;
\r
471 rightreleased = false;
\r
473 left_active = false;
\r
474 middle_active = false;
\r
475 right_active = false;
\r
481 bool rightreleased;
\r
484 bool middle_active;
\r
488 // We use this array to store the current state of each key
\r
489 bool keyIsDown[KEY_KEY_CODES_COUNT];
\r
500 virtual ~InputHandler()
\r
504 virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
\r
506 virtual v2s32 getMousePos() = 0;
\r
507 virtual void setMousePos(s32 x, s32 y) = 0;
\r
509 virtual bool getLeftState() = 0;
\r
510 virtual bool getRightState() = 0;
\r
512 virtual bool getLeftClicked() = 0;
\r
513 virtual bool getRightClicked() = 0;
\r
514 virtual void resetLeftClicked() = 0;
\r
515 virtual void resetRightClicked() = 0;
\r
517 virtual bool getLeftReleased() = 0;
\r
518 virtual bool getRightReleased() = 0;
\r
519 virtual void resetLeftReleased() = 0;
\r
520 virtual void resetRightReleased() = 0;
\r
522 virtual void step(float dtime) {};
\r
524 virtual void clear() {};
\r
527 InputHandler *g_input = NULL;
\r
532 g_game_focused = true;
\r
537 g_game_focused = false;
\r
540 class RealInputHandler : public InputHandler
\r
543 RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
\r
545 m_receiver(receiver)
\r
548 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
550 return m_receiver->IsKeyDown(keyCode);
\r
552 virtual v2s32 getMousePos()
\r
554 return m_device->getCursorControl()->getPosition();
\r
556 virtual void setMousePos(s32 x, s32 y)
\r
558 m_device->getCursorControl()->setPosition(x, y);
\r
561 virtual bool getLeftState()
\r
563 return m_receiver->left_active;
\r
565 virtual bool getRightState()
\r
567 return m_receiver->right_active;
\r
570 virtual bool getLeftClicked()
\r
572 if(g_game_focused == false)
\r
574 return m_receiver->leftclicked;
\r
576 virtual bool getRightClicked()
\r
578 if(g_game_focused == false)
\r
580 return m_receiver->rightclicked;
\r
582 virtual void resetLeftClicked()
\r
584 m_receiver->leftclicked = false;
\r
586 virtual void resetRightClicked()
\r
588 m_receiver->rightclicked = false;
\r
591 virtual bool getLeftReleased()
\r
593 if(g_game_focused == false)
\r
595 return m_receiver->leftreleased;
\r
597 virtual bool getRightReleased()
\r
599 if(g_game_focused == false)
\r
601 return m_receiver->rightreleased;
\r
603 virtual void resetLeftReleased()
\r
605 m_receiver->leftreleased = false;
\r
607 virtual void resetRightReleased()
\r
609 m_receiver->rightreleased = false;
\r
614 resetRightClicked();
\r
615 resetLeftClicked();
\r
618 IrrlichtDevice *m_device;
\r
619 MyEventReceiver *m_receiver;
\r
622 class RandomInputHandler : public InputHandler
\r
625 RandomInputHandler()
\r
627 leftclicked = false;
\r
628 rightclicked = false;
\r
629 for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
\r
630 keydown[i] = false;
\r
632 virtual bool isKeyDown(EKEY_CODE keyCode)
\r
634 return keydown[keyCode];
\r
636 virtual v2s32 getMousePos()
\r
640 virtual void setMousePos(s32 x, s32 y)
\r
642 mousepos = v2s32(x,y);
\r
645 virtual bool getLeftState()
\r
649 virtual bool getRightState()
\r
654 virtual bool getLeftClicked()
\r
656 return leftclicked;
\r
658 virtual bool getRightClicked()
\r
660 return rightclicked;
\r
662 virtual void resetLeftClicked()
\r
664 leftclicked = false;
\r
666 virtual void resetRightClicked()
\r
668 rightclicked = false;
\r
671 virtual bool getLeftReleased()
\r
675 virtual bool getRightReleased()
\r
679 virtual void resetLeftReleased()
\r
682 virtual void resetRightReleased()
\r
686 virtual void step(float dtime)
\r
689 static float counter1 = 0;
\r
693 counter1 = 0.1*Rand(1,10);
\r
694 /*if(g_selected_material < USEFUL_CONTENT_COUNT-1)
\r
695 g_selected_material++;
\r
697 g_selected_material = 0;*/
\r
698 if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
\r
701 g_selected_item = 0;
\r
705 static float counter1 = 0;
\r
709 counter1 = 0.1*Rand(1, 40);
\r
710 keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
\r
714 static float counter1 = 0;
\r
718 counter1 = 0.1*Rand(1, 40);
\r
719 keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
\r
723 static float counter1 = 0;
\r
727 counter1 = 0.1*Rand(1, 40);
\r
728 keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
\r
732 static float counter1 = 0;
\r
736 counter1 = 0.1*Rand(1, 40);
\r
737 keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
\r
741 static float counter1 = 0;
\r
745 counter1 = 0.1*Rand(1, 20);
\r
746 mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
\r
750 static float counter1 = 0;
\r
754 counter1 = 0.1*Rand(1, 30);
\r
755 leftclicked = true;
\r
759 static float counter1 = 0;
\r
763 counter1 = 0.1*Rand(1, 20);
\r
764 rightclicked = true;
\r
767 mousepos += mousespeed;
\r
770 s32 Rand(s32 min, s32 max)
\r
772 return (rand()%(max-min+1))+min;
\r
775 bool keydown[KEY_KEY_CODES_COUNT];
\r
782 void updateViewingRange(f32 frametime, Client *client)
\r
784 // Range_all messes up frametime_avg
\r
785 if(g_viewing_range_all == true)
\r
788 float wanted_fps = g_settings.getFloat("wanted_fps");
\r
790 // Initialize to the target value
\r
791 static float frametime_avg = 1.0/wanted_fps;
\r
792 //frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
\r
793 frametime_avg = frametime_avg * 0.7 + frametime * 0.3;
\r
795 static f32 counter = 0;
\r
797 counter -= frametime;
\r
800 //counter = 1.0; //seconds
\r
801 counter = 0.5; //seconds
\r
803 //float freetime_ratio = 0.2;
\r
804 //float freetime_ratio = 0.4;
\r
805 float freetime_ratio = FREETIME_RATIO;
\r
807 float frametime_wanted = (1.0/(wanted_fps/(1.0-freetime_ratio)));
\r
809 float fraction = sqrt(frametime_avg / frametime_wanted);
\r
811 /*float fraction = sqrt(frametime_avg / frametime_wanted) / 2.0
\r
812 + frametime_avg / frametime_wanted / 2.0;*/
\r
814 //float fraction = frametime_avg / frametime_wanted;
\r
816 static bool fraction_is_good = false;
\r
818 //float fraction_good_threshold = 0.1;
\r
819 //float fraction_bad_threshold = 0.25;
\r
820 float fraction_good_threshold = 0.075;
\r
821 float fraction_bad_threshold = 0.125;
\r
822 float fraction_limit;
\r
823 // Use high limit if fraction is good AND the fraction would
\r
824 // lower the range. We want to keep the range fairly high.
\r
825 if(fraction_is_good && fraction > 1.0)
\r
826 fraction_limit = fraction_bad_threshold;
\r
828 fraction_limit = fraction_good_threshold;
\r
830 if(fabs(fraction - 1.0) < fraction_limit)
\r
832 fraction_is_good = true;
\r
837 fraction_is_good = false;
\r
840 //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
\r
841 //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
\r
842 /*dstream<<"fetching="<<client->isFetchingBlocks()
\r
843 <<" faction = "<<fraction<<std::endl;*/
\r
845 JMutexAutoLock lock(g_range_mutex);
\r
847 s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
\r
848 s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
\r
850 s16 n = (float)g_viewing_range_nodes / fraction;
\r
851 if(n < viewing_range_nodes_min)
\r
852 n = viewing_range_nodes_min;
\r
853 if(n > viewing_range_nodes_max)
\r
854 n = viewing_range_nodes_max;
\r
856 bool can_change = true;
\r
858 if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
\r
859 can_change = false;
\r
862 g_viewing_range_nodes = n;
\r
864 /*dstream<<"g_viewing_range_nodes = "
\r
865 <<g_viewing_range_nodes<<std::endl;*/
\r
868 class GUIQuickInventory : public IEventReceiver
\r
872 gui::IGUIEnvironment* env,
\r
873 gui::IGUIElement* parent,
\r
876 Inventory *inventory):
\r
877 m_itemcount(itemcount),
\r
878 m_inventory(inventory)
\r
880 core::rect<s32> imgsize(0,0,48,48);
\r
881 core::rect<s32> textsize(0,0,48,16);
\r
882 v2s32 spacing(0, 64);
\r
883 for(s32 i=0; i<m_itemcount; i++)
\r
885 m_images.push_back(env->addImage(
\r
886 imgsize + pos + spacing*i
\r
888 m_images[i]->setScaleImage(true);
\r
889 m_texts.push_back(env->addStaticText(
\r
891 textsize + pos + spacing*i,
\r
894 m_texts[i]->setBackgroundColor(
\r
895 video::SColor(128,0,0,0));
\r
896 m_texts[i]->setTextAlignment(
\r
898 gui::EGUIA_UPPERLEFT);
\r
902 virtual bool OnEvent(const SEvent& event)
\r
907 void setSelection(s32 i)
\r
916 start = m_selection - m_itemcount / 2;
\r
918 for(s32 i=0; i<m_itemcount; i++)
\r
922 if(j > (s32)m_inventory->getSize() - 1)
\r
923 j -= m_inventory->getSize();
\r
925 j += m_inventory->getSize();
\r
927 InventoryItem *item = m_inventory->getItem(j);
\r
931 m_images[i]->setImage(NULL);
\r
934 if(m_selection == j)
\r
935 swprintf(t, 10, L"<-");
\r
937 swprintf(t, 10, L"");
\r
938 m_texts[i]->setText(t);
\r
940 // The next ifs will segfault with a NULL pointer
\r
945 m_images[i]->setImage(item->getImage());
\r
948 if(m_selection == j)
\r
949 swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
\r
951 swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
\r
952 m_texts[i]->setText(t);
\r
958 core::array<gui::IGUIStaticText*> m_texts;
\r
959 core::array<gui::IGUIImage*> m_images;
\r
960 Inventory *m_inventory;
\r
964 int main(int argc, char *argv[])
\r
967 Low-level initialization
\r
970 bool disable_stderr = false;
\r
972 disable_stderr = true;
\r
975 // Initialize debug streams
\r
976 debugstreams_init(disable_stderr, DEBUGFILE);
\r
977 // Initialize debug stacks
\r
978 debug_stacks_init();
\r
980 DSTACK(__FUNCTION_NAME);
\r
989 // List all allowed options
\r
990 core::map<std::string, ValueSpec> allowed_options;
\r
991 allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));
\r
992 allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,
\r
993 "Run server directly"));
\r
994 allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,
\r
995 "Load configuration from specified file"));
\r
996 allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));
\r
997 allowed_options.insert("address", ValueSpec(VALUETYPE_STRING));
\r
998 allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG));
\r
999 allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1000 allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG));
\r
1002 Settings cmd_args;
\r
1004 bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);
\r
1006 if(ret == false || cmd_args.getFlag("help"))
\r
1008 dstream<<"Allowed options:"<<std::endl;
\r
1009 for(core::map<std::string, ValueSpec>::Iterator
\r
1010 i = allowed_options.getIterator();
\r
1011 i.atEnd() == false; i++)
\r
1013 dstream<<" --"<<i.getNode()->getKey();
\r
1014 if(i.getNode()->getValue().type == VALUETYPE_FLAG)
\r
1019 dstream<<" <value>";
\r
1021 dstream<<std::endl;
\r
1023 if(i.getNode()->getValue().help != NULL)
\r
1025 dstream<<" "<<i.getNode()->getValue().help
\r
1030 return cmd_args.getFlag("help") ? 0 : 1;
\r
1035 Basic initialization
\r
1038 // Initialize default settings
\r
1039 set_default_settings();
\r
1041 // Print startup message
\r
1042 dstream<<DTIME<<"minetest-c55"
\r
1043 " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
\r
1044 <<", ENABLE_TESTS="<<ENABLE_TESTS
\r
1047 // Set locale. This is for forcing '.' as the decimal point.
\r
1048 std::locale::global(std::locale("C"));
\r
1049 // This enables printing all characters in bitmap font
\r
1050 setlocale(LC_CTYPE, "en_US");
\r
1052 // Initialize sockets
\r
1054 atexit(sockets_cleanup);
\r
1064 // Path of configuration file in use
\r
1065 std::string configpath = "";
\r
1067 if(cmd_args.exists("config"))
\r
1069 bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());
\r
1072 dstream<<"Could not read configuration from \""
\r
1073 <<cmd_args.get("config")<<"\""<<std::endl;
\r
1076 configpath = cmd_args.get("config");
\r
1080 const char *filenames[2] =
\r
1082 "../minetest.conf",
\r
1083 "../../minetest.conf"
\r
1086 for(u32 i=0; i<2; i++)
\r
1088 bool r = g_settings.readConfigFile(filenames[i]);
\r
1091 configpath = filenames[i];
\r
1097 // Initialize random seed
\r
1103 if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
\r
1104 || cmd_args.getFlag("enable-unittests") == true)
\r
1110 Global range mutex
\r
1112 g_range_mutex.Init();
\r
1113 assert(g_range_mutex.IsInitialized());
\r
1115 // Read map parameters from settings
\r
1117 HMParams hm_params;
\r
1118 hm_params.blocksize = g_settings.getU16("heightmap_blocksize");
\r
1119 hm_params.randmax = g_settings.get("height_randmax");
\r
1120 hm_params.randfactor = g_settings.get("height_randfactor");
\r
1121 hm_params.base = g_settings.get("height_base");
\r
1123 MapParams map_params;
\r
1124 map_params.plants_amount = g_settings.getFloat("plants_amount");
\r
1125 map_params.ravines_amount = g_settings.getFloat("ravines_amount");
\r
1131 std::cout<<std::endl<<std::endl;
\r
1134 <<" .__ __ __ "<<std::endl
\r
1135 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
\r
1136 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
\r
1137 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
\r
1138 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
\r
1139 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl
\r
1141 <<"Now with more waterish water!"
\r
1144 std::cout<<std::endl;
\r
1145 //char templine[100];
\r
1149 if(cmd_args.exists("port"))
\r
1151 port = cmd_args.getU16("port");
\r
1155 port = g_settings.getU16Ask("port", "Port", 30000);
\r
1156 std::cout<<"-> "<<port<<std::endl;
\r
1159 if(cmd_args.getFlag("server"))
\r
1161 DSTACK("Dedicated server branch");
\r
1163 std::cout<<std::endl;
\r
1164 std::cout<<"========================"<<std::endl;
\r
1165 std::cout<<"Running dedicated server"<<std::endl;
\r
1166 std::cout<<"========================"<<std::endl;
\r
1167 std::cout<<std::endl;
\r
1169 Server server("../map", hm_params, map_params);
\r
1170 server.start(port);
\r
1174 // This is kind of a hack but can be done like this
\r
1175 // because server.step() is very light
\r
1177 server.step(0.030);
\r
1179 static int counter = 0;
\r
1185 core::list<PlayerInfo> list = server.getPlayerInfo();
\r
1186 core::list<PlayerInfo>::Iterator i;
\r
1187 static u32 sum_old = 0;
\r
1188 u32 sum = PIChecksum(list);
\r
1189 if(sum != sum_old)
\r
1191 std::cout<<DTIME<<"Player info:"<<std::endl;
\r
1192 for(i=list.begin(); i!=list.end(); i++)
\r
1194 i->PrintLine(&std::cout);
\r
1204 bool hosting = false;
\r
1205 char connect_name[100] = "";
\r
1207 if(cmd_args.exists("address"))
\r
1209 snprintf(connect_name, 100, "%s", cmd_args.get("address").c_str());
\r
1211 else if(g_settings.get("address") != "" && is_yes(g_settings.get("host_game")) == false)
\r
1213 std::cout<<g_settings.get("address")<<std::endl;
\r
1214 snprintf(connect_name, 100, "%s", g_settings.get("address").c_str());
\r
1218 std::cout<<"Address to connect to [empty = host a game]: ";
\r
1219 std::cin.getline(connect_name, 100);
\r
1222 if(connect_name[0] == 0){
\r
1223 snprintf(connect_name, 100, "127.0.0.1");
\r
1228 std::cout<<"> Hosting game"<<std::endl;
\r
1230 std::cout<<"> Connecting to "<<connect_name<<std::endl;
\r
1232 char playername[PLAYERNAME_SIZE] = "";
\r
1233 if(g_settings.get("name") != "")
\r
1235 snprintf(playername, PLAYERNAME_SIZE, "%s", g_settings.get("name").c_str());
\r
1239 std::cout<<"Name of player: ";
\r
1240 std::cin.getline(playername, PLAYERNAME_SIZE);
\r
1242 std::cout<<"-> \""<<playername<<"\""<<std::endl;
\r
1245 Resolution selection
\r
1248 bool fullscreen = false;
\r
1249 u16 screenW = atoi(g_settings.get("screenW").c_str());
\r
1250 u16 screenH = atoi(g_settings.get("screenH").c_str());
\r
1254 MyEventReceiver receiver;
\r
1256 video::E_DRIVER_TYPE driverType;
\r
1259 //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
\r
1260 driverType = video::EDT_OPENGL;
\r
1262 driverType = video::EDT_OPENGL;
\r
1265 // create device and exit if creation failed
\r
1267 IrrlichtDevice *device;
\r
1268 device = createDevice(driverType,
\r
1269 core::dimension2d<u32>(screenW, screenH),
\r
1270 16, fullscreen, false, false, &receiver);
\r
1273 return 1; // could not create selected driver.
\r
1275 g_irrlicht = new IrrlichtWrapper(device);
\r
1277 //g_device = device;
\r
1279 device->setResizable(true);
\r
1281 bool random_input = g_settings.getBool("random_input")
\r
1282 || cmd_args.getFlag("random-input");
\r
1284 g_input = new RandomInputHandler();
\r
1286 g_input = new RealInputHandler(device, &receiver);
\r
1289 Continue initialization
\r
1292 video::IVideoDriver* driver = device->getVideoDriver();
\r
1295 This changes the minimum allowed number of vertices in a VBO
\r
1297 //driver->setMinHardwareBufferVertexCount(1);
\r
1299 scene::ISceneManager* smgr = device->getSceneManager();
\r
1302 guiPauseMenu pauseMenu(device, &receiver);
\r
1304 gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
\r
1305 gui::IGUISkin* skin = guienv->getSkin();
\r
1306 gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
\r
1308 skin->setFont(font);
\r
1309 //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
\r
1310 skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
\r
1311 //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
\r
1312 //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
\r
1313 skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
\r
1314 skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
\r
1316 const wchar_t *text = L"Loading and connecting...";
\r
1317 core::vector2d<s32> center(screenW/2, screenH/2);
\r
1318 core::dimension2d<u32> textd = font->getDimension(text);
\r
1319 std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
\r
1320 // Have to add a bit to disable the text from word wrapping
\r
1321 //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
\r
1322 core::vector2d<s32> textsize(300, textd.Height);
\r
1323 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
\r
1325 gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
\r
1326 text, textrect, false, false);
\r
1327 gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
\r
1329 driver->beginScene(true, true, video::SColor(255,0,0,0));
\r
1330 guienv->drawAll();
\r
1331 driver->endScene();
\r
1334 Preload some random textures that are used in threads
\r
1337 g_texturecache.set("torch", driver->getTexture("../data/torch.png"));
\r
1338 g_texturecache.set("torch_on_floor", driver->getTexture("../data/torch_on_floor.png"));
\r
1339 g_texturecache.set("torch_on_ceiling", driver->getTexture("../data/torch_on_ceiling.png"));
\r
1340 g_texturecache.set("crack", driver->getTexture("../data/crack.png"));
\r
1343 Load tile textures
\r
1345 for(s32 i=0; i<TILES_COUNT; i++)
\r
1347 if(g_tile_texture_names[i] == NULL)
\r
1349 std::string name = g_tile_texture_names[i];
\r
1350 std::string filename;
\r
1351 filename += "../data/";
\r
1353 filename += ".png";
\r
1354 g_texturecache.set(name, driver->getTexture(filename.c_str()));
\r
1359 //tile_materials_preload(g_texturecache);
\r
1360 tile_materials_preload(g_irrlicht);
\r
1361 //tile_materials_init();
\r
1364 Make a scope here for the client so that it gets removed
\r
1365 before the irrlicht device
\r
1369 std::cout<<DTIME<<"Creating server and client"<<std::endl;
\r
1374 SharedPtr<Server> server;
\r
1376 server = new Server("../map", hm_params, map_params);
\r
1377 server->start(port);
\r
1384 Client client(device, playername,
\r
1386 g_viewing_range_nodes,
\r
1387 g_viewing_range_all);
\r
1389 Address connect_address(0,0,0,0, port);
\r
1391 connect_address.Resolve(connect_name);
\r
1393 catch(ResolveError &e)
\r
1395 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
\r
1399 std::cout<<DTIME<<"Connecting to server..."<<std::endl;
\r
1400 client.connect(connect_address);
\r
1403 while(client.connectedAndInitialized() == false)
\r
1406 if(server != NULL){
\r
1407 server->step(0.1);
\r
1412 catch(con::PeerNotFoundException &e)
\r
1414 std::cout<<DTIME<<"Timed out."<<std::endl;
\r
1421 /*scene::ISceneNode* skybox;
\r
1422 skybox = smgr->addSkyBoxSceneNode(
\r
1423 driver->getTexture("../data/skybox2.png"),
\r
1424 driver->getTexture("../data/skybox3.png"),
\r
1425 driver->getTexture("../data/skybox1.png"),
\r
1426 driver->getTexture("../data/skybox1.png"),
\r
1427 driver->getTexture("../data/skybox1.png"),
\r
1428 driver->getTexture("../data/skybox1.png"));*/
\r
1431 Create the camera node
\r
1434 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
\r
1435 0, // Camera parent
\r
1436 v3f(BS*100, BS*2, BS*100), // Look from
\r
1437 v3f(BS*100+1, BS*2, BS*100), // Look to
\r
1441 if(camera == NULL)
\r
1444 video::SColor skycolor = video::SColor(255,90,140,200);
\r
1446 camera->setFOV(FOV_ANGLE);
\r
1448 // Just so big a value that everything rendered is visible
\r
1449 camera->setFarValue(100000*BS);
\r
1451 f32 camera_yaw = 0; // "right/left"
\r
1452 f32 camera_pitch = 0; // "up/down"
\r
1458 gui_loadingtext->remove();
\r
1460 pauseMenu.setVisible(true);
\r
1463 Add some gui stuff
\r
1466 // First line of debug text
\r
1467 gui::IGUIStaticText *guitext = guienv->addStaticText(
\r
1469 core::rect<s32>(5, 5, 795, 5+textsize.Y),
\r
1471 // Second line of debug text
\r
1472 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
\r
1474 core::rect<s32>(5, 5+(textsize.Y+5)*1, 795, (5+textsize.Y)*2),
\r
1477 // At the middle of the screen
\r
1478 // Object infos are shown in this
\r
1479 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
\r
1481 core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
\r
1484 // This is a copy of the inventory that the client's environment has
\r
1485 Inventory local_inventory(PLAYER_INVENTORY_SIZE);
\r
1487 GUIQuickInventory *quick_inventory = new GUIQuickInventory
\r
1488 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
\r
1491 Some statistics are collected in these
\r
1494 u32 beginscenetime = 0;
\r
1495 u32 scenetime = 0;
\r
1496 u32 endscenetime = 0;
\r
1504 virtual void sendText(std::string text) = 0;
\r
1507 struct TextDestSign : public TextDest
\r
1509 TextDestSign(v3s16 blockpos, s16 id, Client *client)
\r
1511 m_blockpos = blockpos;
\r
1513 m_client = client;
\r
1515 void sendText(std::string text)
\r
1517 dstream<<"Changing text of a sign object: "
\r
1518 <<text<<std::endl;
\r
1519 m_client->sendSignText(m_blockpos, m_id, text);
\r
1527 TextDest *textbuf_dest = NULL;
\r
1529 //gui::IGUIWindow* input_window = NULL;
\r
1530 gui::IGUIStaticText* input_guitext = NULL;
\r
1541 bool first_loop_after_window_activation = true;
\r
1543 // Time is in milliseconds
\r
1544 // NOTE: getRealTime() without run()s causes strange problems in wine
\r
1545 // NOTE: Have to call run() between calls of this to update the timer
\r
1546 u32 lasttime = device->getTimer()->getTime();
\r
1548 while(device->run())
\r
1551 Run global IrrlichtWrapper's main thread processing stuff
\r
1553 g_irrlicht->Run();
\r
1556 Random calculations
\r
1558 v2u32 screensize = driver->getScreenSize();
\r
1559 core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
\r
1561 // Hilight boxes collected during the loop and displayed
\r
1562 core::list< core::aabbox3d<f32> > hilightboxes;
\r
1565 std::wstring infotext;
\r
1567 //TimeTaker //timer1("//timer1");
\r
1569 // Time of frame without fps limit
\r
1573 // not using getRealTime is necessary for wine
\r
1574 u32 time = device->getTimer()->getTime();
\r
1575 if(time > lasttime)
\r
1576 busytime_u32 = time - lasttime;
\r
1579 busytime = busytime_u32 / 1000.0;
\r
1582 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
\r
1584 // Absolutelu necessary for wine!
\r
1591 //updateViewingRange(dtime, &client);
\r
1592 updateViewingRange(busytime, &client);
\r
1599 float fps_max = g_settings.getFloat("fps_max");
\r
1600 u32 frametime_min = 1000./fps_max;
\r
1602 if(busytime_u32 < frametime_min)
\r
1604 u32 sleeptime = frametime_min - busytime_u32;
\r
1605 device->sleep(sleeptime);
\r
1609 // Absolutelu necessary for wine!
\r
1613 Time difference calculation
\r
1615 f32 dtime; // in seconds
\r
1617 u32 time = device->getTimer()->getTime();
\r
1618 if(time > lasttime)
\r
1619 dtime = (time - lasttime) / 1000.0;
\r
1625 Time average and jitter calculation
\r
1628 static f32 dtime_avg1 = 0.0;
\r
1629 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
\r
1630 f32 dtime_jitter1 = dtime - dtime_avg1;
\r
1632 static f32 dtime_jitter1_max_sample = 0.0;
\r
1633 static f32 dtime_jitter1_max_fraction = 0.0;
\r
1635 static f32 jitter1_max = 0.0;
\r
1636 static f32 counter = 0.0;
\r
1637 if(dtime_jitter1 > jitter1_max)
\r
1638 jitter1_max = dtime_jitter1;
\r
1643 dtime_jitter1_max_sample = jitter1_max;
\r
1644 dtime_jitter1_max_fraction
\r
1645 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
\r
1646 jitter1_max = 0.0;
\r
1649 Control freetime ratio
\r
1651 /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
\r
1653 if(g_freetime_ratio < FREETIME_RATIO_MAX)
\r
1654 g_freetime_ratio += 0.01;
\r
1658 if(g_freetime_ratio > FREETIME_RATIO_MIN)
\r
1659 g_freetime_ratio -= 0.01;
\r
1665 Busytime average and jitter calculation
\r
1668 static f32 busytime_avg1 = 0.0;
\r
1669 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
\r
1670 f32 busytime_jitter1 = busytime - busytime_avg1;
\r
1672 static f32 busytime_jitter1_max_sample = 0.0;
\r
1673 static f32 busytime_jitter1_min_sample = 0.0;
\r
1675 static f32 jitter1_max = 0.0;
\r
1676 static f32 jitter1_min = 0.0;
\r
1677 static f32 counter = 0.0;
\r
1678 if(busytime_jitter1 > jitter1_max)
\r
1679 jitter1_max = busytime_jitter1;
\r
1680 if(busytime_jitter1 < jitter1_min)
\r
1681 jitter1_min = busytime_jitter1;
\r
1683 if(counter > 0.0){
\r
1685 busytime_jitter1_max_sample = jitter1_max;
\r
1686 busytime_jitter1_min_sample = jitter1_min;
\r
1687 jitter1_max = 0.0;
\r
1688 jitter1_min = 0.0;
\r
1693 Debug info for client
\r
1696 static float counter = 0.0;
\r
1701 client.printDebugInfo(std::cout);
\r
1706 Input handler step()
\r
1708 g_input->step(dtime);
\r
1713 /*if(g_esc_pressed)
\r
1719 Player speed control
\r
1722 if(g_game_focused)
\r
1729 bool a_superspeed,
\r
1732 PlayerControl control(
\r
1733 g_input->isKeyDown(irr::KEY_KEY_W),
\r
1734 g_input->isKeyDown(irr::KEY_KEY_S),
\r
1735 g_input->isKeyDown(irr::KEY_KEY_A),
\r
1736 g_input->isKeyDown(irr::KEY_KEY_D),
\r
1737 g_input->isKeyDown(irr::KEY_SPACE),
\r
1738 g_input->isKeyDown(irr::KEY_KEY_2),
\r
1742 client.setPlayerControl(control);
\r
1746 // Set every key to inactive
\r
1747 PlayerControl control;
\r
1748 client.setPlayerControl(control);
\r
1753 Process environment
\r
1757 //TimeTaker timer("client.step(dtime)");
\r
1758 client.step(dtime);
\r
1759 //client.step(dtime_avg1);
\r
1762 if(server != NULL)
\r
1764 //TimeTaker timer("server->step(dtime)");
\r
1765 server->step(dtime);
\r
1768 v3f player_position = client.getPlayerPosition();
\r
1770 //TimeTaker //timer2("//timer2");
\r
1773 Mouse and camera control
\r
1776 if((device->isWindowActive() && g_game_focused && !pauseMenu.isVisible())
\r
1780 device->getCursorControl()->setVisible(false);
\r
1782 if(first_loop_after_window_activation){
\r
1783 //std::cout<<"window active, first loop"<<std::endl;
\r
1784 first_loop_after_window_activation = false;
\r
1787 s32 dx = g_input->getMousePos().X - displaycenter.X;
\r
1788 s32 dy = g_input->getMousePos().Y - displaycenter.Y;
\r
1789 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
\r
1790 camera_yaw -= dx*0.2;
\r
1791 camera_pitch += dy*0.2;
\r
1792 if(camera_pitch < -89.5) camera_pitch = -89.5;
\r
1793 if(camera_pitch > 89.5) camera_pitch = 89.5;
\r
1795 g_input->setMousePos(displaycenter.X, displaycenter.Y);
\r
1798 device->getCursorControl()->setVisible(true);
\r
1800 //std::cout<<"window inactive"<<std::endl;
\r
1801 first_loop_after_window_activation = true;
\r
1804 camera_yaw = wrapDegrees(camera_yaw);
\r
1805 camera_pitch = wrapDegrees(camera_pitch);
\r
1807 v3f camera_direction = v3f(0,0,1);
\r
1808 camera_direction.rotateYZBy(camera_pitch);
\r
1809 camera_direction.rotateXZBy(camera_yaw);
\r
1811 v3f camera_position =
\r
1812 player_position + v3f(0, BS+BS/2, 0);
\r
1814 camera->setPosition(camera_position);
\r
1815 // *100.0 helps in large map coordinates
\r
1816 camera->setTarget(camera_position + camera_direction * 100.0);
\r
1818 if(FIELD_OF_VIEW_TEST){
\r
1819 //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1820 client.updateCamera(v3f(0,0,0), v3f(0,0,1));
\r
1823 //client.m_env.getMap().updateCamera(camera_position, camera_direction);
\r
1824 //TimeTaker timer("client.updateCamera");
\r
1825 client.updateCamera(camera_position, camera_direction);
\r
1829 //TimeTaker //timer3("//timer3");
\r
1832 Calculate what block is the crosshair pointing to
\r
1835 //u32 t1 = device->getTimer()->getRealTime();
\r
1837 //f32 d = 4; // max. distance
\r
1838 f32 d = 4; // max. distance
\r
1839 core::line3d<f32> shootline(camera_position,
\r
1840 camera_position + camera_direction * BS * (d+1));
\r
1842 MapBlockObject *selected_object = client.getSelectedObject
\r
1843 (d*BS, camera_position, shootline);
\r
1845 if(selected_object != NULL)
\r
1847 //dstream<<"Client returned selected_object != NULL"<<std::endl;
\r
1849 core::aabbox3d<f32> box_on_map
\r
1850 = selected_object->getSelectionBoxOnMap();
\r
1852 hilightboxes.push_back(box_on_map);
\r
1854 infotext = narrow_to_wide(selected_object->infoText());
\r
1856 if(g_input->getLeftClicked())
\r
1858 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
\r
1859 client.clickObject(0, selected_object->getBlock()->getPos(),
\r
1860 selected_object->getId(), g_selected_item);
\r
1862 else if(g_input->getRightClicked())
\r
1864 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
\r
1866 Check if we want to modify the object ourselves
\r
1868 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
\r
1870 dstream<<"Sign object right-clicked"<<std::endl;
\r
1874 input_guitext = guienv->addStaticText(L"",
\r
1875 core::rect<s32>(150,100,350,120),
\r
1877 false, // wordwrap?
\r
1880 input_guitext->setDrawBackground(true);
\r
1884 g_text_buffer = L"ASD LOL 8)";
\r
1885 g_text_buffer_accepted = true;
\r
1889 g_text_buffer = L"";
\r
1890 g_text_buffer_accepted = false;
\r
1893 textbuf_dest = new TextDestSign(
\r
1894 selected_object->getBlock()->getPos(),
\r
1895 selected_object->getId(),
\r
1899 Otherwise pass the event to the server as-is
\r
1903 client.clickObject(1, selected_object->getBlock()->getPos(),
\r
1904 selected_object->getId(), g_selected_item);
\r
1908 else // selected_object == NULL
\r
1912 Find out which node we are pointing at
\r
1915 bool nodefound = false;
\r
1917 v3s16 neighbourpos;
\r
1918 core::aabbox3d<f32> nodefacebox;
\r
1919 f32 mindistance = BS * 1001;
\r
1921 v3s16 pos_i = floatToInt(player_position);
\r
1923 /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
\r
1927 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
\r
1928 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
\r
1929 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
\r
1930 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
\r
1931 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
\r
1932 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
\r
1934 for(s16 y = ystart; y <= yend; y++)
\r
1935 for(s16 z = zstart; z <= zend; z++)
\r
1936 for(s16 x = xstart; x <= xend; x++)
\r
1941 n = client.getNode(v3s16(x,y,z));
\r
1942 if(content_pointable(n.d) == false)
\r
1945 catch(InvalidPositionException &e)
\r
1951 v3f npf = intToFloat(np);
\r
1956 v3s16(0,0,1), // back
\r
1957 v3s16(0,1,0), // top
\r
1958 v3s16(1,0,0), // right
\r
1959 v3s16(0,0,-1), // front
\r
1960 v3s16(0,-1,0), // bottom
\r
1961 v3s16(-1,0,0), // left
\r
1967 if(n.d == CONTENT_TORCH)
\r
1969 v3s16 dir = unpackDir(n.dir);
\r
1970 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
\r
1971 dir_f *= BS/2 - BS/6 - BS/20;
\r
1972 v3f cpf = npf + dir_f;
\r
1973 f32 distance = (cpf - camera_position).getLength();
\r
1975 core::aabbox3d<f32> box;
\r
1978 if(dir == v3s16(0,-1,0))
\r
1980 box = core::aabbox3d<f32>(
\r
1981 npf - v3f(BS/6, BS/2, BS/6),
\r
1982 npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
\r
1986 else if(dir == v3s16(0,1,0))
\r
1988 box = core::aabbox3d<f32>(
\r
1989 npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
\r
1990 npf + v3f(BS/6, BS/2, BS/6)
\r
1996 box = core::aabbox3d<f32>(
\r
1997 cpf - v3f(BS/6, BS/3, BS/6),
\r
1998 cpf + v3f(BS/6, BS/3, BS/6)
\r
2002 if(distance < mindistance)
\r
2004 if(box.intersectsWithLine(shootline))
\r
2008 neighbourpos = np;
\r
2009 mindistance = distance;
\r
2010 nodefacebox = box;
\r
2019 for(u16 i=0; i<6; i++)
\r
2021 v3f dir_f = v3f(dirs[i].X,
\r
2022 dirs[i].Y, dirs[i].Z);
\r
2023 v3f centerpoint = npf + dir_f * BS/2;
\r
2025 (centerpoint - camera_position).getLength();
\r
2027 if(distance < mindistance)
\r
2029 core::CMatrix4<f32> m;
\r
2030 m.buildRotateFromTo(v3f(0,0,1), dir_f);
\r
2032 // This is the back face
\r
2033 v3f corners[2] = {
\r
2034 v3f(BS/2, BS/2, BS/2),
\r
2035 v3f(-BS/2, -BS/2, BS/2+d)
\r
2038 for(u16 j=0; j<2; j++)
\r
2040 m.rotateVect(corners[j]);
\r
2041 corners[j] += npf;
\r
2044 core::aabbox3d<f32> facebox(corners[0]);
\r
2045 facebox.addInternalPoint(corners[1]);
\r
2047 if(facebox.intersectsWithLine(shootline))
\r
2051 neighbourpos = np + dirs[i];
\r
2052 mindistance = distance;
\r
2053 nodefacebox = facebox;
\r
2055 } // if distance < mindistance
\r
2057 } // regular block
\r
2062 static v3s16 nodepos_old(-32768,-32768,-32768);
\r
2064 static float dig_time = 0.0;
\r
2066 if(nodepos != nodepos_old)
\r
2068 std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
\r
2069 <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
\r
2071 if(nodepos_old != v3s16(-32768,-32768,-32768))
\r
2073 client.clearTempMod(nodepos_old);
\r
2078 hilightboxes.push_back(nodefacebox);
\r
2080 if(g_input->getLeftReleased())
\r
2082 client.clearTempMod(nodepos);
\r
2085 if(g_input->getLeftClicked() ||
\r
2086 (g_input->getLeftState() && nodepos != nodepos_old))
\r
2088 std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
\r
2089 client.groundAction(0, nodepos, neighbourpos, g_selected_item);
\r
2091 if(g_input->getLeftClicked())
\r
2093 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
\r
2095 if(g_input->getLeftState())
\r
2097 dig_time += dtime;
\r
2099 float dig_time_complete = 0.5;
\r
2100 MapNode n = client.getNode(nodepos);
\r
2101 if(n.d == CONTENT_STONE)
\r
2102 dig_time_complete = 1.5;
\r
2104 u16 dig_index = (u16)(3.99*dig_time/dig_time_complete);
\r
2105 if(dig_time > 0.125)
\r
2107 //dstream<<"dig_index="<<dig_index<<std::endl;
\r
2108 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
\r
2112 if(g_input->getRightClicked())
\r
2114 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
\r
2115 client.groundAction(1, nodepos, neighbourpos, g_selected_item);
\r
2118 nodepos_old = nodepos;
\r
2123 } // selected_object == NULL
\r
2125 g_input->resetLeftClicked();
\r
2126 g_input->resetRightClicked();
\r
2128 if(g_input->getLeftReleased())
\r
2130 std::cout<<DTIME<<"Left released"<<std::endl;
\r
2131 client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
\r
2133 if(g_input->getRightReleased())
\r
2135 //std::cout<<DTIME<<"Right released"<<std::endl;
\r
2139 g_input->resetLeftReleased();
\r
2140 g_input->resetRightReleased();
\r
2143 Calculate stuff for drawing
\r
2146 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
\r
2148 u32 daynight_ratio = client.getDayNightRatio();
\r
2149 video::SColor bgcolor = video::SColor(
\r
2151 skycolor.getRed() * daynight_ratio / 1000,
\r
2152 skycolor.getGreen() * daynight_ratio / 1000,
\r
2153 skycolor.getBlue() * daynight_ratio / 1000);
\r
2159 if(g_settings.getBool("enable_fog") == true)
\r
2161 f32 range = g_viewing_range_nodes * BS;
\r
2162 if(g_viewing_range_all)
\r
2163 range = 100000*BS;
\r
2167 video::EFT_FOG_LINEAR,
\r
2171 false, // pixel fog
\r
2172 false // range fog
\r
2178 Update gui stuff (0ms)
\r
2181 //TimeTaker guiupdatetimer("Gui updating");
\r
2184 wchar_t temptext[150];
\r
2186 static float drawtime_avg = 0;
\r
2187 drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
\r
2188 static float beginscenetime_avg = 0;
\r
2189 beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
\r
2190 static float scenetime_avg = 0;
\r
2191 scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
\r
2192 static float endscenetime_avg = 0;
\r
2193 endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
\r
2195 swprintf(temptext, 150, L"Minetest-c55 ("
\r
2197 L", R: range_all=%i"
\r
2199 L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
\r
2201 g_viewing_range_all,
\r
2203 beginscenetime_avg,
\r
2208 guitext->setText(temptext);
\r
2212 wchar_t temptext[150];
\r
2213 swprintf(temptext, 150,
\r
2214 L"(% .1f, % .1f, % .1f)"
\r
2215 L" (% .3f < btime_jitter < % .3f"
\r
2216 L", dtime_jitter = % .1f %%)",
\r
2217 player_position.X/BS,
\r
2218 player_position.Y/BS,
\r
2219 player_position.Z/BS,
\r
2220 busytime_jitter1_min_sample,
\r
2221 busytime_jitter1_max_sample,
\r
2222 dtime_jitter1_max_fraction * 100.0
\r
2225 guitext2->setText(temptext);
\r
2229 /*wchar_t temptext[100];
\r
2230 swprintf(temptext, 100,
\r
2231 SWPRINTF_CHARSTRING,
\r
2232 infotext.substr(0,99).c_str()
\r
2235 guitext_info->setText(temptext);*/
\r
2237 guitext_info->setText(infotext.c_str());
\r
2244 static u16 old_selected_item = 65535;
\r
2245 if(client.getLocalInventoryUpdated()
\r
2246 || g_selected_item != old_selected_item)
\r
2248 old_selected_item = g_selected_item;
\r
2249 //std::cout<<"Updating local inventory"<<std::endl;
\r
2250 client.getLocalInventory(local_inventory);
\r
2251 quick_inventory->setSelection(g_selected_item);
\r
2252 quick_inventory->update();
\r
2255 if(input_guitext != NULL)
\r
2257 /*wchar_t temptext[100];
\r
2258 swprintf(temptext, 100,
\r
2259 SWPRINTF_CHARSTRING,
\r
2260 g_text_buffer.substr(0,99).c_str()
\r
2262 input_guitext->setText(g_text_buffer.c_str());
\r
2268 if(input_guitext != NULL && g_text_buffer_accepted)
\r
2270 input_guitext->remove();
\r
2271 input_guitext = NULL;
\r
2273 if(textbuf_dest != NULL)
\r
2275 std::string text = wide_to_narrow(g_text_buffer);
\r
2276 dstream<<"Sending text: "<<text<<std::endl;
\r
2277 textbuf_dest->sendText(text);
\r
2278 delete textbuf_dest;
\r
2279 textbuf_dest = NULL;
\r
2285 //guiupdatetimer.stop();
\r
2291 TimeTaker drawtimer("Drawing");
\r
2295 TimeTaker timer("beginScene");
\r
2296 driver->beginScene(true, true, bgcolor);
\r
2297 //driver->beginScene(false, true, bgcolor);
\r
2298 beginscenetime = timer.stop(true);
\r
2303 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
\r
2306 TimeTaker timer("smgr");
\r
2308 scenetime = timer.stop(true);
\r
2312 //TimeTaker timer9("auxiliary drawings");
\r
2315 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
\r
2316 displaycenter + core::vector2d<s32>(10,0),
\r
2317 video::SColor(255,255,255,255));
\r
2318 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
\r
2319 displaycenter + core::vector2d<s32>(0,10),
\r
2320 video::SColor(255,255,255,255));
\r
2323 //TimeTaker //timer10("//timer10");
\r
2325 video::SMaterial m;
\r
2327 m.Lighting = false;
\r
2328 driver->setMaterial(m);
\r
2330 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
\r
2332 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
\r
2333 i != hilightboxes.end(); i++)
\r
2335 /*std::cout<<"hilightbox min="
\r
2336 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
\r
2338 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
\r
2340 driver->draw3DBox(*i, video::SColor(255,0,0,0));
\r
2346 //TimeTaker //timer11("//timer11");
\r
2352 guienv->drawAll();
\r
2356 TimeTaker timer("endScene");
\r
2357 driver->endScene();
\r
2358 endscenetime = timer.stop(true);
\r
2361 drawtime = drawtimer.stop(true);
\r
2367 static s16 lastFPS = 0;
\r
2368 //u16 fps = driver->getFPS();
\r
2369 u16 fps = (1.0/dtime_avg1);
\r
2371 if (lastFPS != fps)
\r
2373 core::stringw str = L"Minetest [";
\r
2374 str += driver->getName();
\r
2378 device->setWindowCaption(str.c_str());
\r
2384 device->yield();*/
\r
2387 delete quick_inventory;
\r
2389 } // client is deleted at this point
\r
2394 In the end, delete the Irrlicht device.
\r
2399 Update configuration file
\r
2401 /*if(configpath != "")
\r
2403 g_settings.updateConfigFile(configpath.c_str());
\r
2407 catch(con::PeerNotFoundException &e)
\r
2409 dstream<<DTIME<<"Connection timed out."<<std::endl;
\r
2411 #if CATCH_UNHANDLED_EXCEPTIONS
\r
2413 This is what has to be done in every thread to get suitable debug info
\r
2415 catch(std::exception &e)
\r
2417 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
\r
2418 <<e.what()<<std::endl;
\r
2423 debugstreams_deinit();
\r