Skip to content

Commit

Permalink
Fix anticheat
Browse files Browse the repository at this point in the history
  • Loading branch information
celeron55 committed Aug 3, 2013
1 parent bc5db9b commit 7426141
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 70 deletions.
129 changes: 67 additions & 62 deletions src/content_sao.cpp
Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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();
Expand All @@ -1516,3 +1520,4 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) {
bool PlayerSAO::collideWithObjects(){
return true;
}

38 changes: 37 additions & 1 deletion src/content_sao.h
Expand Up @@ -122,6 +122,36 @@ class LuaEntitySAO : public ServerActiveObject
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:
Expand Down Expand Up @@ -228,6 +258,11 @@ class PlayerSAO : public ServerActiveObject
{
m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
}
LagPool& getDigPool()
{
return m_dig_pool;
}
void checkMovementCheat();

// Other

Expand All @@ -249,8 +284,9 @@ class PlayerSAO : public ServerActiveObject
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;
Expand Down
3 changes: 2 additions & 1 deletion src/environment.cpp
Expand Up @@ -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)
{
}

Expand Down
6 changes: 6 additions & 0 deletions src/environment.h
Expand Up @@ -304,6 +304,9 @@ class ServerEnvironment : public Environment
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:

/*
Expand Down Expand Up @@ -378,6 +381,9 @@ class ServerEnvironment : public Environment
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
Expand Down
40 changes: 34 additions & 6 deletions src/server.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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;*/
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 7426141

Please sign in to comment.