diff options
-rw-r--r-- | minetest.conf.example | 11 | ||||
-rw-r--r-- | src/defaultsettings.cpp | 3 | ||||
-rw-r--r-- | src/game.cpp | 19 | ||||
-rw-r--r-- | src/main.cpp | 33 | ||||
-rw-r--r-- | src/main.h | 4 | ||||
-rw-r--r-- | src/map.cpp | 6 | ||||
-rw-r--r-- | src/mapblock.cpp | 4 | ||||
-rw-r--r-- | src/profiler.h | 131 | ||||
-rw-r--r-- | src/server.cpp | 185 | ||||
-rw-r--r-- | src/server.h | 11 | ||||
-rw-r--r-- | src/utility.h | 3 |
11 files changed, 355 insertions, 55 deletions
diff --git a/minetest.conf.example b/minetest.conf.example index 1a1dbe0fc..743186853 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -92,10 +92,6 @@ # Server side stuff # -# Set to true to enable experimental features -# (varies from version to version, see wiki) -#enable_experimental = false - # Map directory (everything in the world is stored here) #map-dir = /home/palle/custom_map @@ -112,6 +108,13 @@ # Gives some stuff to players at the beginning #give_initial_stuff = false +# Set to true to enable experimental features +# (varies from version to version, see wiki) +#enable_experimental = false + +# Profiler data print interval. 0 = disable. +#profiler_print_interval = 10 + # Player and object positions are sent at intervals specified by this #objectdata_inverval = 0.2 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6fcdc1dbb..73f22a7f7 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -74,12 +74,13 @@ void set_default_settings() g_settings.setDefault("give_initial_stuff", "false"); g_settings.setDefault("default_password", ""); g_settings.setDefault("default_privs", "build, shout"); + g_settings.setDefault("profiler_print_interval", "0"); g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); //g_settings.setDefault("max_simultaneous_block_sends_per_client", "2"); - g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); + g_settings.setDefault("max_simultaneous_block_sends_server_total", "8"); g_settings.setDefault("max_block_send_distance", "8"); g_settings.setDefault("max_block_generate_distance", "8"); g_settings.setDefault("time_send_interval", "20"); diff --git a/src/game.cpp b/src/game.cpp index 6932b45f1..e8e6cb71f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -903,6 +903,10 @@ void the_game( bool first_loop_after_window_activation = true; + // TODO: Convert the static interval timers to these + // Interval limiter for profiler + IntervalLimiter m_profiler_interval; + // Time is in milliseconds // NOTE: getRealTime() causes strange problems in wine (imprecision?) // NOTE: So we have to use getTime() and call run()s between them @@ -1090,6 +1094,21 @@ void the_game( } /* + Profiler + */ + float profiler_print_interval = + g_settings.getFloat("profiler_print_interval"); + if(profiler_print_interval != 0) + { + if(m_profiler_interval.step(0.030, profiler_print_interval)) + { + dstream<<"Profiler:"<<std::endl; + g_profiler.print(dstream); + g_profiler.clear(); + } + } + + /* Direct handling of user input */ diff --git a/src/main.cpp b/src/main.cpp index 3d342e596..a739d71bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -215,6 +215,8 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop FIXME: The new optimized map sending doesn't sometimes send enough blocks
from big caves and such
+* Take player's walking direction into account in GetNextBlocks
+
Environment:
------------
@@ -308,6 +310,10 @@ Making it more portable: Stuff to do before release:
---------------------------
+Fixes to the current release:
+-----------------------------
+- Make AuthManager to save only when data has changed
+
Stuff to do after release:
---------------------------
- Make sure server handles removing grass when a block is placed (etc)
@@ -386,6 +392,9 @@ Settings g_settings; // This is located in defaultsettings.cpp
extern void set_default_settings();
+// Global profiler
+Profiler g_profiler;
+
/*
Random stuff
*/
@@ -436,7 +445,14 @@ std::ostream *derr_client_ptr = &dstream; class TimeGetter
{
public:
- TimeGetter(IrrlichtDevice *device):
+ virtual u32 getTime() = 0;
+};
+
+// A precise irrlicht one
+class IrrlichtTimeGetter: public TimeGetter
+{
+public:
+ IrrlichtTimeGetter(IrrlichtDevice *device):
m_device(device)
{}
u32 getTime()
@@ -448,8 +464,18 @@ public: private:
IrrlichtDevice *m_device;
};
+// Not so precise one which works without irrlicht
+class SimpleTimeGetter: public TimeGetter
+{
+public:
+ u32 getTime()
+ {
+ return porting::getTimeMs();
+ }
+};
// A pointer to a global instance of the time getter
+// TODO: why?
TimeGetter *g_timegetter = NULL;
u32 getTimeMs()
@@ -1208,6 +1234,9 @@ int main(int argc, char *argv[]) {
DSTACK("Dedicated server branch");
+ // Create time getter
+ g_timegetter = new SimpleTimeGetter();
+
// Create server
Server server(map_dir.c_str());
server.start(port);
@@ -1290,7 +1319,7 @@ int main(int argc, char *argv[]) device = device;
// Create time getter
- g_timegetter = new TimeGetter(device);
+ g_timegetter = new IrrlichtTimeGetter(device);
// Create game callback for menus
g_gamecallback = new MainGameCallback(device);
diff --git a/src/main.h b/src/main.h index fcf150f87..450525c26 100644 --- a/src/main.h +++ b/src/main.h @@ -28,6 +28,10 @@ extern Settings g_settings; #include "tile.h" extern ITextureSource *g_texturesource; +// Global profiler +#include "profiler.h" +extern Profiler g_profiler; + // Debug streams #include <fstream> diff --git a/src/map.cpp b/src/map.cpp index a49de3c46..f9809e9fd 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5692,9 +5692,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) p_nodes_min.Y / MAP_BLOCKSIZE - 1, p_nodes_min.Z / MAP_BLOCKSIZE - 1); v3s16 p_blocks_max( - p_nodes_max.X / MAP_BLOCKSIZE + 1, - p_nodes_max.Y / MAP_BLOCKSIZE + 1, - p_nodes_max.Z / MAP_BLOCKSIZE + 1); + p_nodes_max.X / MAP_BLOCKSIZE, + p_nodes_max.Y / MAP_BLOCKSIZE, + p_nodes_max.Z / MAP_BLOCKSIZE); u32 vertex_count = 0; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index c448ef236..b2f972dde 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -540,10 +540,10 @@ void updateFastFaceRow( v3s16 p_next; - bool next_makes_face; + bool next_makes_face = false; v3s16 next_p_corrected; v3s16 next_face_dir_corrected; - u8 next_lights[4]; + u8 next_lights[4] = {0,0,0,0}; TileSpec next_tile; // If at last position, there is nothing to compare to and diff --git a/src/profiler.h b/src/profiler.h new file mode 100644 index 000000000..a30e34a7c --- /dev/null +++ b/src/profiler.h @@ -0,0 +1,131 @@ +/* +Minetest-c55 +Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef PROFILER_HEADER +#define PROFILER_HEADER + +#include "common_irrlicht.h" +#include <string> +#include "utility.h" +#include <jmutex.h> +#include <jmutexautolock.h> + +/* + Time profiler +*/ + +class Profiler +{ +public: + Profiler() + { + m_mutex.Init(); + } + + void add(const std::string &name, u32 duration) + { + JMutexAutoLock lock(m_mutex); + core::map<std::string, u32>::Node *n = m_data.find(name); + if(n == NULL) + { + m_data[name] = duration; + } + else + { + n->setValue(n->getValue()+duration); + } + } + + void clear() + { + JMutexAutoLock lock(m_mutex); + for(core::map<std::string, u32>::Iterator + i = m_data.getIterator(); + i.atEnd() == false; i++) + { + i.getNode()->setValue(0); + } + } + + void print(std::ostream &o) + { + JMutexAutoLock lock(m_mutex); + for(core::map<std::string, u32>::Iterator + i = m_data.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + o<<name<<": "; + s32 clampsize = 40; + s32 space = clampsize-name.size(); + for(s32 j=0; j<space; j++) + { + if(j%2 == 0 && j < space - 1) + o<<"-"; + else + o<<" "; + } + o<<i.getNode()->getValue(); + o<<std::endl; + } + } + +private: + JMutex m_mutex; + core::map<std::string, u32> m_data; +}; + +class ScopeProfiler +{ +public: + ScopeProfiler(Profiler *profiler, const std::string &name): + m_profiler(profiler), + m_name(name), + m_timer(NULL) + { + if(m_profiler) + m_timer = new TimeTaker(m_name.c_str()); + } + // name is copied + ScopeProfiler(Profiler *profiler, const char *name): + m_profiler(profiler), + m_name(name), + m_timer(NULL) + { + if(m_profiler) + m_timer = new TimeTaker(m_name.c_str()); + } + ~ScopeProfiler() + { + if(m_timer) + { + u32 duration = m_timer->stop(true); + if(m_profiler) + m_profiler->add(m_name, duration); + delete m_timer; + } + } +private: + Profiler *m_profiler; + std::string m_name; + TimeTaker *m_timer; +}; + +#endif + diff --git a/src/server.cpp b/src/server.cpp index 56874c46c..6f1f1e6e8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -312,11 +312,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/ // Increment timers - m_nearest_unsent_reset_timer += dtime; m_nothing_to_send_pause_timer -= dtime; if(m_nothing_to_send_pause_timer >= 0) + { + // Keep this reset + m_nearest_unsent_reset_timer = 0; return; + } // Won't send anything if already sending if(m_blocks_sending.size() >= g_settings.getU16 @@ -326,14 +329,21 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, return; } + //TimeTaker timer("RemoteClient::GetNextBlocks"); + Player *player = server->m_env.getPlayer(peer_id); assert(player != NULL); v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); + v3f playerspeeddir(0,0,0); + if(playerspeed.getLength() > 1.0*BS) + playerspeeddir = playerspeed / playerspeed.getLength(); + // Predict to next block + v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - v3s16 center_nodepos = floatToInt(playerpos, BS); + v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); @@ -344,6 +354,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); + /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<"," + <<camera_dir.Z<<")"<<std::endl;*/ + /* Get the starting value of the block finder radius. */ @@ -356,11 +369,18 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /*dstream<<"m_nearest_unsent_reset_timer=" <<m_nearest_unsent_reset_timer<<std::endl;*/ - if(m_nearest_unsent_reset_timer > 5.0) + + // This has to be incremented only when the nothing to send pause + // is not active + m_nearest_unsent_reset_timer += dtime; + + // Reset periodically to avoid possible bugs or other mishaps + if(m_nearest_unsent_reset_timer > 10.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; - //dstream<<"Resetting m_nearest_unsent_d"<<std::endl; + dstream<<"Resetting m_nearest_unsent_d for " + <<server->getPlayerName(peer_id)<<std::endl; } //s16 last_nearest_unsent_d = m_nearest_unsent_d; @@ -402,11 +422,22 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, s16 d_max = g_settings.getS16("max_block_send_distance"); s16 d_max_gen = g_settings.getS16("max_block_generate_distance"); + // Don't loop very much at a time + if(d_max > d_start+1) + d_max = d_start+1; + /*if(d_max_gen > d_start+2) + d_max_gen = d_start+2;*/ + //dstream<<"Starting from "<<d_start<<std::endl; bool sending_something = false; - for(s16 d = d_start; d <= d_max; d++) + bool no_blocks_found_for_sending = true; + + bool queue_is_full = false; + + s16 d; + for(d = d_start; d <= d_max; d++) { //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl; @@ -451,7 +482,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, // Don't select too many blocks for sending if(num_blocks_selected >= max_simul_dynamic) - goto queue_full; + { + queue_is_full = true; + goto queue_full_break; + } // Don't send blocks that are currently being transferred if(m_blocks_sending.find(p) != NULL) @@ -513,6 +547,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } #endif + //dstream<<"d="<<d<<std::endl; + /* Don't generate or send if not in sight */ @@ -527,7 +563,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ { if(m_blocks_sent.find(p) != NULL) + { continue; + } } /* @@ -539,11 +577,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, bool block_is_invalid = false; if(block != NULL) { + // Block is dummy if data doesn't exist. + // It means it has been not found from disk and not generated if(block->isDummy()) { surely_not_found_on_disk = true; } - + + // Block is valid if lighting is up-to-date and data exists if(block->isValid() == false) { block_is_invalid = true; @@ -564,8 +605,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, If block is not close, don't send it unless it is near ground level. - Block is not near ground level if night-time mesh - doesn't differ from day-time mesh. + Block is near ground level if night-time mesh + differs from day-time mesh. */ if(d > 3) { @@ -586,14 +627,16 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } /* - Record the lowest d from which a a block has been + Record the lowest d from which a block has been found being not sent and possibly to exist */ - if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d) + if(no_blocks_found_for_sending) { if(generate == true) new_nearest_unsent_d = d; } + + no_blocks_found_for_sending = false; /* Add inexistent block to emerge queue. @@ -633,20 +676,30 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, sending_something = true; } } -queue_full: +queue_full_break: - if(new_nearest_unsent_d != -1) + //dstream<<"Stopped at "<<d<<std::endl; + + if(no_blocks_found_for_sending) { - m_nearest_unsent_d = new_nearest_unsent_d; + if(queue_is_full == false) + new_nearest_unsent_d = d; } + if(new_nearest_unsent_d != -1) + m_nearest_unsent_d = new_nearest_unsent_d; + if(sending_something == false) { m_nothing_to_send_counter++; - if(m_nothing_to_send_counter >= 3) + if((s16)m_nothing_to_send_counter >= + g_settings.getS16("max_block_send_distance")) { // Pause time in seconds - m_nothing_to_send_pause_timer = 2.0; + m_nothing_to_send_pause_timer = 1.0; + dstream<<"nothing to send to " + <<server->getPlayerName(peer_id) + <<" (d="<<d<<")"<<std::endl; } } else @@ -1130,8 +1183,12 @@ void Server::AsyncRunStep() dtime = m_step_dtime; } - // Send blocks to clients - SendBlocks(dtime); + { + ScopeProfiler sp(&g_profiler, "Server: selecting and sending " + "blocks to clients"); + // Send blocks to clients + SendBlocks(dtime); + } if(dtime < 0.001) return; @@ -1196,12 +1253,14 @@ void Server::AsyncRunStep() { // Process connection's timeouts JMutexAutoLock lock2(m_con_mutex); + ScopeProfiler sp(&g_profiler, "Server: connection timeout processing"); m_con.RunTimeouts(dtime); } { // This has to be called so that the client list gets synced // with the peer list of the connection + ScopeProfiler sp(&g_profiler, "Server: peer change handling"); handlePeerChanges(); } @@ -1209,6 +1268,7 @@ void Server::AsyncRunStep() // Step environment // This also runs Map's timers JMutexAutoLock lock(m_env_mutex); + ScopeProfiler sp(&g_profiler, "Server: environment step"); m_env.step(dtime); } @@ -1225,7 +1285,9 @@ void Server::AsyncRunStep() m_liquid_transform_timer -= 1.00; JMutexAutoLock lock(m_env_mutex); - + + ScopeProfiler sp(&g_profiler, "Server: liquid transform"); + core::map<v3s16, MapBlock*> modified_blocks; m_env.getMap().transformLiquids(modified_blocks); #if 0 @@ -1298,10 +1360,11 @@ void Server::AsyncRunStep() */ { //dstream<<"Server: Checking added and deleted active objects"<<std::endl; - JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + + ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects"); + // Radius inside which objects are active s16 radius = 32; @@ -1446,6 +1509,8 @@ void Server::AsyncRunStep() JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); + ScopeProfiler sp(&g_profiler, "Server: sending object messages"); + // Key = object id // Value = data sent by object core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages; @@ -1598,6 +1663,9 @@ void Server::AsyncRunStep() { JMutexAutoLock lock1(m_env_mutex); JMutexAutoLock lock2(m_con_mutex); + + ScopeProfiler sp(&g_profiler, "Server: sending mbo positions"); + SendObjectData(counter); counter = 0.0; @@ -1612,7 +1680,9 @@ void Server::AsyncRunStep() JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - + + ScopeProfiler sp(&g_profiler, "Server: stepping node metadata"); + core::map<v3s16, MapBlock*> changed_blocks; m_env.getMap().nodeMetadataStep(dtime, changed_blocks); @@ -1655,6 +1725,8 @@ void Server::AsyncRunStep() { counter = 0.0; + ScopeProfiler sp(&g_profiler, "Server: saving stuff"); + // Auth stuff m_authmanager.save(); @@ -3646,20 +3718,24 @@ void Server::SendBlocks(float dtime) core::array<PrioritySortedBlockTransfer> queue; s32 total_sending = 0; - - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending"); - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); + for(core::map<u16, RemoteClient*>::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + + total_sending += client->SendingCount(); + + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + client->GetNextBlocks(this, dtime, queue); + } } // Sort. @@ -4531,25 +4607,48 @@ void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - std::cout<<DTIME<<std::endl; - std::cout<<"========================"<<std::endl; - std::cout<<"Running dedicated server"<<std::endl; - std::cout<<"========================"<<std::endl; - std::cout<<std::endl; + dstream<<DTIME<<std::endl; + dstream<<"========================"<<std::endl; + dstream<<"Running dedicated server"<<std::endl; + dstream<<"========================"<<std::endl; + dstream<<std::endl; + + IntervalLimiter m_profiler_interval; for(;;) { // This is kind of a hack but can be done like this // because server.step() is very light - sleep_ms(30); + { + ScopeProfiler sp(&g_profiler, "dedicated server sleep"); + sleep_ms(30); + } server.step(0.030); if(server.getShutdownRequested() || kill) { - std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl; + dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl; break; } + /* + Profiler + */ + float profiler_print_interval = + g_settings.getFloat("profiler_print_interval"); + if(profiler_print_interval != 0) + { + if(m_profiler_interval.step(0.030, profiler_print_interval)) + { + dstream<<"Profiler:"<<std::endl; + g_profiler.print(dstream); + g_profiler.clear(); + } + } + + /* + Player info + */ static int counter = 0; counter--; if(counter <= 0) @@ -4562,10 +4661,10 @@ void dedicated_server_loop(Server &server, bool &kill) u32 sum = PIChecksum(list); if(sum != sum_old) { - std::cout<<DTIME<<"Player info:"<<std::endl; + dstream<<DTIME<<"Player info:"<<std::endl; for(i=list.begin(); i!=list.end(); i++) { - i->PrintLine(&std::cout); + i->PrintLine(&dstream); } } sum_old = sum; diff --git a/src/server.h b/src/server.h index 7b73e476c..6bee10685 100644 --- a/src/server.h +++ b/src/server.h @@ -500,6 +500,15 @@ private: // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id); + // When called, environment mutex should be locked + std::string getPlayerName(u16 peer_id) + { + Player *player = m_env.getPlayer(peer_id); + if(player == NULL) + return "[id="+itos(peer_id); + return player->getName(); + } + /* Get a player from memory or creates one. If player is already connected, return NULL @@ -627,6 +636,8 @@ private: */ u16 m_ignore_map_edit_events_peer_id; + Profiler *m_profiler; + friend class EmergeThread; friend class RemoteClient; }; diff --git a/src/utility.h b/src/utility.h index f18d31278..534aea483 100644 --- a/src/utility.h +++ b/src/utility.h @@ -244,6 +244,9 @@ inline f32 readF1000(std::istream &is) { char buf[2]; is.read(buf, 2); + // TODO: verify if this gets rid of the valgrind warning + //if(is.gcount() != 2) + // return 0; return readF1000((u8*)buf); } |