mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 01:53:45 +01:00
Fix anticheat
This commit is contained in:
parent
bc5db9b027
commit
742614180c
@ -934,7 +934,6 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
|
|||||||
m_peer_id(peer_id_),
|
m_peer_id(peer_id_),
|
||||||
m_inventory(NULL),
|
m_inventory(NULL),
|
||||||
m_last_good_position(0,0,0),
|
m_last_good_position(0,0,0),
|
||||||
m_last_good_position_age(0),
|
|
||||||
m_time_from_last_punch(0),
|
m_time_from_last_punch(0),
|
||||||
m_nocheat_dig_pos(32767, 32767, 32767),
|
m_nocheat_dig_pos(32767, 32767, 32767),
|
||||||
m_nocheat_dig_time(0),
|
m_nocheat_dig_time(0),
|
||||||
@ -1002,7 +1001,6 @@ void PlayerSAO::addedToEnvironment(u32 dtime_s)
|
|||||||
m_player->setPlayerSAO(this);
|
m_player->setPlayerSAO(this);
|
||||||
m_player->peer_id = m_peer_id;
|
m_player->peer_id = m_peer_id;
|
||||||
m_last_good_position = m_player->getPosition();
|
m_last_good_position = m_player->getPosition();
|
||||||
m_last_good_position_age = 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called before removing from environment
|
// Called before removing from environment
|
||||||
@ -1106,6 +1104,19 @@ void PlayerSAO::step(float dtime, bool send_recommended)
|
|||||||
m_moved = true;
|
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_time_from_last_punch += dtime;
|
||||||
m_nocheat_dig_time += 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();
|
v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
|
||||||
m_last_good_position = pos;
|
m_last_good_position = pos;
|
||||||
m_last_good_position_age = 0;
|
|
||||||
m_player->setPosition(pos);
|
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)
|
if(send_recommended == false)
|
||||||
return;
|
return;
|
||||||
@ -1267,7 +1220,6 @@ void PlayerSAO::setPos(v3f pos)
|
|||||||
m_player->setPosition(pos);
|
m_player->setPosition(pos);
|
||||||
// Movement caused by this command is always valid
|
// Movement caused by this command is always valid
|
||||||
m_last_good_position = pos;
|
m_last_good_position = pos;
|
||||||
m_last_good_position_age = 0;
|
|
||||||
// Force position change on client
|
// Force position change on client
|
||||||
m_moved = true;
|
m_moved = true;
|
||||||
}
|
}
|
||||||
@ -1279,7 +1231,6 @@ void PlayerSAO::moveTo(v3f pos, bool continuous)
|
|||||||
m_player->setPosition(pos);
|
m_player->setPosition(pos);
|
||||||
// Movement caused by this command is always valid
|
// Movement caused by this command is always valid
|
||||||
m_last_good_position = pos;
|
m_last_good_position = pos;
|
||||||
m_last_good_position_age = 0;
|
|
||||||
// Force position change on client
|
// Force position change on client
|
||||||
m_moved = true;
|
m_moved = true;
|
||||||
}
|
}
|
||||||
@ -1503,6 +1454,59 @@ std::string PlayerSAO::getPropertyPacket()
|
|||||||
return gob_cmd_set_properties(m_prop);
|
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) {
|
bool PlayerSAO::getCollisionBox(aabb3f *toset) {
|
||||||
//update collision box
|
//update collision box
|
||||||
*toset = m_player->getCollisionbox();
|
*toset = m_player->getCollisionbox();
|
||||||
@ -1516,3 +1520,4 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) {
|
|||||||
bool PlayerSAO::collideWithObjects(){
|
bool PlayerSAO::collideWithObjects(){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,36 @@ private:
|
|||||||
PlayerSAO needs some internals exposed.
|
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
|
class PlayerSAO : public ServerActiveObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -228,6 +258,11 @@ public:
|
|||||||
{
|
{
|
||||||
m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
|
m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
|
||||||
}
|
}
|
||||||
|
LagPool& getDigPool()
|
||||||
|
{
|
||||||
|
return m_dig_pool;
|
||||||
|
}
|
||||||
|
void checkMovementCheat();
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
|
|
||||||
@ -249,8 +284,9 @@ private:
|
|||||||
Inventory *m_inventory;
|
Inventory *m_inventory;
|
||||||
|
|
||||||
// Cheat prevention
|
// Cheat prevention
|
||||||
|
LagPool m_dig_pool;
|
||||||
|
LagPool m_move_pool;
|
||||||
v3f m_last_good_position;
|
v3f m_last_good_position;
|
||||||
float m_last_good_position_age;
|
|
||||||
float m_time_from_last_punch;
|
float m_time_from_last_punch;
|
||||||
v3s16 m_nocheat_dig_pos;
|
v3s16 m_nocheat_dig_pos;
|
||||||
float m_nocheat_dig_time;
|
float m_nocheat_dig_time;
|
||||||
|
@ -332,7 +332,8 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
|
|||||||
m_active_block_interval_overload_skip(0),
|
m_active_block_interval_overload_skip(0),
|
||||||
m_game_time(0),
|
m_game_time(0),
|
||||||
m_game_time_fraction_counter(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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +304,9 @@ public:
|
|||||||
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
|
bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0);
|
||||||
|
|
||||||
u32 getGameTime() { return m_game_time; }
|
u32 getGameTime() { return m_game_time; }
|
||||||
|
|
||||||
|
void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; }
|
||||||
|
float getMaxLagEstimate() { return m_max_lag_estimate; }
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -378,6 +381,9 @@ private:
|
|||||||
std::list<ABMWithState> m_abms;
|
std::list<ABMWithState> m_abms;
|
||||||
// An interval for generally sending object positions and stuff
|
// An interval for generally sending object positions and stuff
|
||||||
float m_recommended_send_interval;
|
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
|
#ifndef SERVER
|
||||||
|
@ -1090,6 +1090,16 @@ void Server::AsyncRunStep()
|
|||||||
|
|
||||||
{
|
{
|
||||||
JMutexAutoLock lock(m_env_mutex);
|
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
|
// Step environment
|
||||||
ScopeProfiler sp(g_profiler, "SEnv step");
|
ScopeProfiler sp(g_profiler, "SEnv step");
|
||||||
ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
|
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.LMB = (bool)(keyPressed&128);
|
||||||
player->control.RMB = (bool)(keyPressed&256);
|
player->control.RMB = (bool)(keyPressed&256);
|
||||||
|
|
||||||
|
playersao->checkMovementCheat();
|
||||||
|
|
||||||
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
|
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
|
||||||
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
|
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
|
||||||
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
|
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
|
||||||
@ -2953,13 +2965,27 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
<<std::endl;
|
<<std::endl;
|
||||||
is_valid_dig = false;
|
is_valid_dig = false;
|
||||||
}
|
}
|
||||||
// If time is considerably too short, ignore dig
|
// Check digging time
|
||||||
// Check time only for medium and slow timed digs
|
// If already invalidated, we don't have to
|
||||||
if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
|
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()
|
infostream<<"Server: NoCheat: "<<player->getName()
|
||||||
<<" completed digging "
|
<<" completed digging "<<PP(p_under)
|
||||||
<<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
|
<<"too fast; not digging."<<std::endl;
|
||||||
<<params.time<<"s; not digging."<<std::endl;
|
|
||||||
is_valid_dig = false;
|
is_valid_dig = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4617,6 +4643,8 @@ std::wstring Server::getStatusString()
|
|||||||
os<<L"version="<<narrow_to_wide(VERSION_STRING);
|
os<<L"version="<<narrow_to_wide(VERSION_STRING);
|
||||||
// Uptime
|
// Uptime
|
||||||
os<<L", uptime="<<m_uptime.get();
|
os<<L", uptime="<<m_uptime.get();
|
||||||
|
// Max lag estimate
|
||||||
|
os<<L", max_lag="<<m_env->getMaxLagEstimate();
|
||||||
// Information about clients
|
// Information about clients
|
||||||
std::map<u16, RemoteClient*>::iterator i;
|
std::map<u16, RemoteClient*>::iterator i;
|
||||||
bool first;
|
bool first;
|
||||||
|
Loading…
Reference in New Issue
Block a user