diff options
author | Perttu Ahola <celeron55@gmail.com> | 2013-08-03 23:16:37 +0300 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2013-08-03 23:16:37 +0300 |
commit | 742614180cbbe598694a48bd9eb6f7b97a762243 (patch) | |
tree | 5b5033b1d719911ae863ca99d304ebdcca3220f2 | |
parent | bc5db9b0273d43fffad93cf67cf2bfdb714441a9 (diff) | |
download | minetest-742614180cbbe598694a48bd9eb6f7b97a762243.tar.gz minetest-742614180cbbe598694a48bd9eb6f7b97a762243.tar.bz2 minetest-742614180cbbe598694a48bd9eb6f7b97a762243.zip |
Fix anticheat
-rw-r--r-- | src/content_sao.cpp | 129 | ||||
-rw-r--r-- | src/content_sao.h | 38 | ||||
-rw-r--r-- | src/environment.cpp | 3 | ||||
-rw-r--r-- | src/environment.h | 6 | ||||
-rw-r--r-- | src/server.cpp | 40 |
5 files changed, 146 insertions, 70 deletions
diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 90c4fa69c..8d46d4237 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -934,7 +934,6 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, m_peer_id(peer_id_), m_inventory(NULL), m_last_good_position(0,0,0), - m_last_good_position_age(0), m_time_from_last_punch(0), m_nocheat_dig_pos(32767, 32767, 32767), m_nocheat_dig_time(0), @@ -1002,7 +1001,6 @@ void PlayerSAO::addedToEnvironment(u32 dtime_s) m_player->setPlayerSAO(this); m_player->peer_id = m_peer_id; m_last_good_position = m_player->getPosition(); - m_last_good_position_age = 0.0; } // Called before removing from environment @@ -1106,6 +1104,19 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_moved = true; } + //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl; + + // Set lag pool maximums based on estimated lag + const float LAG_POOL_MIN = 5.0; + float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; + if(lag_pool_max < LAG_POOL_MIN) + lag_pool_max = LAG_POOL_MIN; + m_dig_pool.setMax(lag_pool_max); + m_move_pool.setMax(lag_pool_max); + + // Increment cheat prevention timers + m_dig_pool.add(dtime); + m_move_pool.add(dtime); m_time_from_last_punch += dtime; m_nocheat_dig_time += dtime; @@ -1115,66 +1126,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) { v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); m_last_good_position = pos; - m_last_good_position_age = 0; m_player->setPosition(pos); } - else - { - if(m_is_singleplayer || g_settings->getBool("disable_anticheat")) - { - m_last_good_position = m_player->getPosition(); - m_last_good_position_age = 0; - } - else - { - /* - Check player movements - - NOTE: Actually the server should handle player physics like the - client does and compare player's position to what is calculated - on our side. This is required when eg. players fly due to an - explosion. Altough a node-based alternative might be possible - too, and much more lightweight. - */ - - float player_max_speed = 0; - float player_max_speed_up = 0; - if(m_privs.count("fast") != 0){ - // Fast speed - player_max_speed = BS * 20; - player_max_speed_up = BS * 20; - } else { - // Normal speed - player_max_speed = BS * 4.0; - player_max_speed_up = BS * 4.0; - } - // Tolerance - player_max_speed *= 2.5; - player_max_speed_up *= 2.5; - - m_last_good_position_age += dtime; - if(m_last_good_position_age >= 1.0){ - float age = m_last_good_position_age; - v3f diff = (m_player->getPosition() - m_last_good_position); - float d_vert = diff.Y; - diff.Y = 0; - float d_horiz = diff.getLength(); - /*infostream<<m_player->getName()<<"'s horizontal speed is " - <<(d_horiz/age)<<std::endl;*/ - if(d_horiz <= age * player_max_speed && - (d_vert < 0 || d_vert < age * player_max_speed_up)){ - m_last_good_position = m_player->getPosition(); - } else { - actionstream<<"Player "<<m_player->getName() - <<" moved too fast; resetting position" - <<std::endl; - m_player->setPosition(m_last_good_position); - m_moved = true; - } - m_last_good_position_age = 0; - } - } - } if(send_recommended == false) return; @@ -1267,7 +1220,6 @@ void PlayerSAO::setPos(v3f pos) m_player->setPosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; - m_last_good_position_age = 0; // Force position change on client m_moved = true; } @@ -1279,7 +1231,6 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) m_player->setPosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; - m_last_good_position_age = 0; // Force position change on client m_moved = true; } @@ -1503,6 +1454,59 @@ std::string PlayerSAO::getPropertyPacket() return gob_cmd_set_properties(m_prop); } +void PlayerSAO::checkMovementCheat() +{ + if(isAttached() || m_is_singleplayer || + g_settings->getBool("disable_anticheat")) + { + m_last_good_position = m_player->getPosition(); + } + else + { + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. + */ + + float player_max_speed = 0; + float player_max_speed_up = 0; + if(m_privs.count("fast") != 0){ + // Fast speed + player_max_speed = m_player->movement_speed_fast; + player_max_speed_up = m_player->movement_speed_fast; + } else { + // Normal speed + player_max_speed = m_player->movement_speed_walk; + player_max_speed_up = m_player->movement_speed_walk; + } + // Tolerance. With the lag pool we shouldn't need it. + //player_max_speed *= 2.5; + //player_max_speed_up *= 2.5; + + v3f diff = (m_player->getPosition() - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + float required_time = d_horiz/player_max_speed; + if(d_vert > 0 && d_vert/player_max_speed > required_time) + required_time = d_vert/player_max_speed; + if(m_move_pool.grab(required_time)){ + m_last_good_position = m_player->getPosition(); + } else { + actionstream<<"Player "<<m_player->getName() + <<" moved too fast; resetting position" + <<std::endl; + m_player->setPosition(m_last_good_position); + m_moved = true; + } + } +} + bool PlayerSAO::getCollisionBox(aabb3f *toset) { //update collision box *toset = m_player->getCollisionbox(); @@ -1516,3 +1520,4 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) { bool PlayerSAO::collideWithObjects(){ return true; } + diff --git a/src/content_sao.h b/src/content_sao.h index 140211cf6..9640e5f08 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -122,6 +122,36 @@ private: PlayerSAO needs some internals exposed. */ +class LagPool +{ + float pool; + float max; +public: + LagPool(): pool(15), max(15) + {} + void setMax(float new_max) + { + max = new_max; + if(pool > new_max) + pool = new_max; + } + void add(float dtime) + { + pool -= dtime; + if(pool < 0) + pool = 0; + } + bool grab(float dtime) + { + if(dtime <= 0) + return true; + if(pool + dtime > max) + return false; + pool += dtime; + return true; + } +}; + class PlayerSAO : public ServerActiveObject { public: @@ -228,6 +258,11 @@ public: { m_nocheat_dig_pos = v3s16(32767, 32767, 32767); } + LagPool& getDigPool() + { + return m_dig_pool; + } + void checkMovementCheat(); // Other @@ -249,8 +284,9 @@ private: Inventory *m_inventory; // Cheat prevention + LagPool m_dig_pool; + LagPool m_move_pool; v3f m_last_good_position; - float m_last_good_position_age; float m_time_from_last_punch; v3s16 m_nocheat_dig_pos; float m_nocheat_dig_time; diff --git a/src/environment.cpp b/src/environment.cpp index 57fdfd7e5..63718f3fc 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -332,7 +332,8 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface, m_active_block_interval_overload_skip(0), m_game_time(0), m_game_time_fraction_counter(0), - m_recommended_send_interval(0.1) + m_recommended_send_interval(0.1), + m_max_lag_estimate(0.1) { } diff --git a/src/environment.h b/src/environment.h index 8fc5611e4..b59ce83c1 100644 --- a/src/environment.h +++ b/src/environment.h @@ -304,6 +304,9 @@ public: bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0); u32 getGameTime() { return m_game_time; } + + void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } + float getMaxLagEstimate() { return m_max_lag_estimate; } private: /* @@ -378,6 +381,9 @@ private: std::list<ABMWithState> m_abms; // An interval for generally sending object positions and stuff float m_recommended_send_interval; + // Estimate for general maximum lag as determined by server. + // Can raise to high values like 15s with eg. map generation mods. + float m_max_lag_estimate; }; #ifndef SERVER diff --git a/src/server.cpp b/src/server.cpp index 8c67846c5..cdd42c2e9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1090,6 +1090,16 @@ void Server::AsyncRunStep() { JMutexAutoLock lock(m_env_mutex); + // Figure out and report maximum lag to environment + float max_lag = m_env->getMaxLagEstimate(); + max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes) + if(dtime > max_lag){ + if(dtime > 0.1 && dtime > max_lag * 2.0) + infostream<<"Server: Maximum lag peaked to "<<dtime + <<" s"<<std::endl; + max_lag = dtime; + } + m_env->reportMaxLagEstimate(max_lag); // Step environment ScopeProfiler sp(g_profiler, "SEnv step"); ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG); @@ -2241,6 +2251,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) player->control.LMB = (bool)(keyPressed&128); player->control.RMB = (bool)(keyPressed&256); + playersao->checkMovementCheat(); + /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to " <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")" <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/ @@ -2953,13 +2965,27 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<std::endl; is_valid_dig = false; } - // If time is considerably too short, ignore dig - // Check time only for medium and slow timed digs - if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){ + // Check digging time + // If already invalidated, we don't have to + if(!is_valid_dig){ + // Well not our problem then + } + // Clean and long dig + else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){ + // All is good, but grab time from pool; don't care if + // it's actually available + playersao->getDigPool().grab(params.time); + } + // Short or laggy dig + // Try getting the time from pool + else if(playersao->getDigPool().grab(params.time)){ + // All is good + } + // Dig not possible + else{ infostream<<"Server: NoCheat: "<<player->getName() - <<" completed digging " - <<PP(p_under)<<" in "<<nocheat_t<<"s; expected " - <<params.time<<"s; not digging."<<std::endl; + <<" completed digging "<<PP(p_under) + <<"too fast; not digging."<<std::endl; is_valid_dig = false; } } @@ -4617,6 +4643,8 @@ std::wstring Server::getStatusString() os<<L"version="<<narrow_to_wide(VERSION_STRING); // Uptime os<<L", uptime="<<m_uptime.get(); + // Max lag estimate + os<<L", max_lag="<<m_env->getMaxLagEstimate(); // Information about clients std::map<u16, RemoteClient*>::iterator i; bool first; |