aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2013-08-03 23:16:37 +0300
committerPerttu Ahola <celeron55@gmail.com>2013-08-03 23:16:37 +0300
commit742614180cbbe598694a48bd9eb6f7b97a762243 (patch)
tree5b5033b1d719911ae863ca99d304ebdcca3220f2 /src
parentbc5db9b0273d43fffad93cf67cf2bfdb714441a9 (diff)
downloadminetest-742614180cbbe598694a48bd9eb6f7b97a762243.tar.gz
minetest-742614180cbbe598694a48bd9eb6f7b97a762243.tar.bz2
minetest-742614180cbbe598694a48bd9eb6f7b97a762243.zip
Fix anticheat
Diffstat (limited to 'src')
-rw-r--r--src/content_sao.cpp129
-rw-r--r--src/content_sao.h38
-rw-r--r--src/environment.cpp3
-rw-r--r--src/environment.h6
-rw-r--r--src/server.cpp40
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;