fix(): False positives against speed hacks and countermeasures (#114)

This commit is contained in:
Synful-Syn
2023-06-07 14:21:13 -04:00
committed by GitHub
parent 00e1399838
commit 3177f5deca
4 changed files with 107 additions and 27 deletions

View File

@@ -27,6 +27,7 @@
AnticheatData::AnticheatData() AnticheatData::AnticheatData()
{ {
lastOpcode = 0; lastOpcode = 0;
lastSpeedRate = 0.0f;
totalReports = 0; totalReports = 0;
for (uint8 i = 0; i < MAX_REPORT_TYPES; i++) for (uint8 i = 0; i < MAX_REPORT_TYPES; i++)
{ {
@@ -34,15 +35,23 @@ AnticheatData::AnticheatData()
tempReports[i] = 0; tempReports[i] = 0;
tempReportsTimer[i] = 0; tempReportsTimer[i] = 0;
} }
average = 0; average = 0.0f;
creationTime = 0; creationTime = 0;
hasDailyReport = false; hasDailyReport = false;
justUsedMovementSpell = false;
} }
AnticheatData::~AnticheatData() AnticheatData::~AnticheatData()
{ {
} }
void AnticheatData::SetLastInformations(MovementInfo movementInfo, uint32 opcode, float speedRate)
{
SetLastMovementInfo(movementInfo);
SetLastOpcode(opcode);
SetLastSpeedRate(speedRate);
}
void AnticheatData::SetDailyReportState(bool b) void AnticheatData::SetDailyReportState(bool b)
{ {
hasDailyReport = b; hasDailyReport = b;

View File

@@ -35,12 +35,17 @@ public:
AnticheatData(); AnticheatData();
~AnticheatData(); ~AnticheatData();
void SetLastInformations(MovementInfo movementInfo, uint32 opcode, float speedRate);
void SetLastOpcode(uint32 opcode); void SetLastOpcode(uint32 opcode);
uint32 GetLastOpcode() const; uint32 GetLastOpcode() const;
const MovementInfo& GetLastMovementInfo() const; const MovementInfo& GetLastMovementInfo() const;
void SetLastMovementInfo(MovementInfo& moveInfo); void SetLastMovementInfo(MovementInfo& moveInfo);
[[nodiscard]] float GetLastSpeedRate() const { return lastSpeedRate; }
void SetLastSpeedRate(float speedRate) { lastSpeedRate = speedRate; }
void SetPosition(float x, float y, float z, float o); void SetPosition(float x, float y, float z, float o);
uint32 GetTotalReports() const; uint32 GetTotalReports() const;
@@ -63,9 +68,13 @@ public:
void SetDailyReportState(bool b); void SetDailyReportState(bool b);
bool GetDailyReportState(); bool GetDailyReportState();
[[nodiscard]] bool GetJustUsedMovementSpell() const { return justUsedMovementSpell; }
void SetJustUsedMovementSpell(bool value) { justUsedMovementSpell = value; }
private: private:
uint32 lastOpcode; uint32 lastOpcode;
MovementInfo lastMovementInfo; MovementInfo lastMovementInfo;
float lastSpeedRate;
uint32 totalReports; uint32 totalReports;
uint32 typeReports[MAX_REPORT_TYPES]; uint32 typeReports[MAX_REPORT_TYPES];
float average; float average;
@@ -73,6 +82,7 @@ private:
uint32 tempReports[MAX_REPORT_TYPES]; uint32 tempReports[MAX_REPORT_TYPES];
uint32 tempReportsTimer[MAX_REPORT_TYPES]; uint32 tempReportsTimer[MAX_REPORT_TYPES];
bool hasDailyReport; bool hasDailyReport;
bool justUsedMovementSpell;
}; };
#endif #endif

View File

@@ -41,6 +41,12 @@ constexpr auto ALLOWED_ACK_LAG = 2000;
enum Spells : uint32 enum Spells : uint32
{ {
BLINK = 1953,
BLINK_COOLDOWN_REDUCTION = 23025, // Reduces Blink cooldown by 2 seconds.
GLYPH_OF_BLINK = 56365, // Increases Blink distance by 5 yards.
SHADOWSTEP = 36554,
FILTHY_TRICKS_RANK_1 = 58414, // Reduces Shadowstep cooldown by 5 seconds.
FILTHY_TRICKS_RANK_2 = 58415, // Reduces Shadowstep cooldown by 10 seconds.
SHACKLES = 38505, SHACKLES = 38505,
LFG_SPELL_DUNGEON_DESERTER = 71041, LFG_SPELL_DUNGEON_DESERTER = 71041,
BG_SPELL_DESERTER = 26013, BG_SPELL_DESERTER = 26013,
@@ -93,8 +99,7 @@ void AnticheatMgr::StartHackDetection(Player* player, MovementInfo movementInfo,
if (player->IsInFlight() || player->GetTransport() || player->GetVehicle()) if (player->IsInFlight() || player->GetTransport() || player->GetVehicle())
{ {
m_Players[key].SetLastMovementInfo(movementInfo); m_Players[key].SetLastInformations(movementInfo, opcode, GetPlayerCurrentSpeedRate(player));
m_Players[key].SetLastOpcode(opcode);
return; return;
} }
@@ -127,8 +132,57 @@ void AnticheatMgr::StartHackDetection(Player* player, MovementInfo movementInfo,
BGStartExploit(player, movementInfo); BGStartExploit(player, movementInfo);
} }
} }
m_Players[key].SetLastMovementInfo(movementInfo); m_Players[key].SetLastInformations(movementInfo, opcode, GetPlayerCurrentSpeedRate(player));
m_Players[key].SetLastOpcode(opcode); }
uint32 AnticheatMgr::GetTeleportSkillCooldownDurationInMS(Player* player) const
{
switch (player->getClass())
{
case CLASS_ROGUE:
if (player->HasAura(FILTHY_TRICKS_RANK_2))
return 20000u;
else if (player->HasAura(FILTHY_TRICKS_RANK_1))
return 25000u;
return 30000u;
case CLASS_MAGE:
if (player->HasAura(BLINK_COOLDOWN_REDUCTION)) // Bonus from Vanilla/Early TBC pvp gear.
return 13000u;
return 15000u;
default:
return 0u;
}
}
float AnticheatMgr::GetTeleportSkillDistanceInYards(Player* player) const
{
switch (player->getClass())
{
case CLASS_ROGUE: // The rogue's teleport spell is Shadowstep.
return 25.0f; // Synful-Syn: Help needed! At least, 25 yards adjustment is better than nothing!
// The spell can be casted at a maximum of 25 yards from the middle of the ennemy and teleports the player a short distance behind the target which might be over 25 yards, especially when the target is facing the rogue.
// Using Shadowstep on Onyxia at as far as I could moved me by 44 yards. Doing it on a blood elf in duel moved me 29 yards.
case CLASS_MAGE: // The mage's teleport spell is Blink.
if (player->HasAura(GLYPH_OF_BLINK))
return 25.1f; // Includes a 0.1 miscalculation margin.
return 20.1f; // Includes a 0.1 miscalculation margin.
default:
return 0.0f;
}
}
// Get how many yards the player can move in a second.
float AnticheatMgr::GetPlayerCurrentSpeedRate(Player* player) const
{
// we need to know HOW is the player moving
// TO-DO: Should we check the incoming movement flags?
if (player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
return player->GetSpeed(MOVE_SWIM);
else if (player->IsFlying())
return player->GetSpeed(MOVE_FLIGHT);
else if (player->HasUnitMovementFlag(MOVEMENTFLAG_WALKING))
return player->GetSpeed(MOVE_WALK);
return player->GetSpeed(MOVE_RUN);
} }
void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo movementInfo) void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo movementInfo)
@@ -179,32 +233,20 @@ void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo movementInfo)
} }
} }
uint32 distance2D = (uint32)movementInfo.pos.GetExactDist2d(&m_Players[key].GetLastMovementInfo().pos); float distance2D = movementInfo.pos.GetExactDist2d(&m_Players[key].GetLastMovementInfo().pos);
// We don't need to check for a speedhack if the player hasn't moved // We don't need to check for a speedhack if the player hasn't moved
// This is necessary since MovementHandler fires if you rotate the camera in place // This is necessary since MovementHandler fires if you rotate the camera in place
if (!distance2D) if (!distance2D)
return; return;
// we need to know HOW is the player moving
// TO-DO: Should we check the incoming movement flags?
UnitMoveType moveType;
if (player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
moveType = MOVE_SWIM;
else if (player->IsFlying())
moveType = MOVE_FLIGHT;
else if (player->HasUnitMovementFlag(MOVEMENTFLAG_WALKING))
moveType = MOVE_WALK;
else
moveType = MOVE_RUN;
// how many yards the player can do in one sec.
// We remove the added speed for jumping because otherwise permanently jumping doubles your allowed speed
uint32 speedRate = (uint32)(player->GetSpeed(moveType));
// how long the player took to move to here. // how long the player took to move to here.
uint32 timeDiff = getMSTimeDiff(m_Players[key].GetLastMovementInfo().time, movementInfo.time); uint32 timeDiff = getMSTimeDiff(m_Players[key].GetLastMovementInfo().time, movementInfo.time);
float speedRate = GetPlayerCurrentSpeedRate(player);
if (timeDiff <= ALLOWED_ACK_LAG)
speedRate = std::max(speedRate, m_Players[key].GetLastSpeedRate()); // The player might have been moving with a previously faster speed. This should help mitigate a false positive from loosing a speed increase buff.
if (int32(timeDiff) < 0 && sConfigMgr->GetOption<bool>("Anticheat.CM.TIMEMANIPULATION", true)) if (int32(timeDiff) < 0 && sConfigMgr->GetOption<bool>("Anticheat.CM.TIMEMANIPULATION", true))
{ {
if (sConfigMgr->GetOption<bool>("Anticheat.CM.WriteLog", true)) if (sConfigMgr->GetOption<bool>("Anticheat.CM.WriteLog", true))
@@ -257,19 +299,36 @@ void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo movementInfo)
BuildReport(player, COUNTER_MEASURES_REPORT); BuildReport(player, COUNTER_MEASURES_REPORT);
} }
// Adjust distance from Blink/Shadowstep.
if (player->HasAura(BLINK) || player->HasAura(SHADOWSTEP))
{
// Only adjust the travelled distance if the player previously didn't use a movement spell or didn't move at all since they previously used the movement spell.
if (!m_Players[key].GetJustUsedMovementSpell() || timeDiff >= GetTeleportSkillCooldownDurationInMS(player))
{
m_Players[key].SetJustUsedMovementSpell(true);
distance2D = std::max(distance2D - GetTeleportSkillDistanceInYards(player), 0.0f);
}
}
else
{
m_Players[key].SetJustUsedMovementSpell(false);
}
// this is the distance doable by the player in 1 sec, using the time done to move to this point. // this is the distance doable by the player in 1 sec, using the time done to move to this point.
uint32 clientSpeedRate = distance2D * 1000 / timeDiff; float clientSpeedRate = 0.0f;
if (float floatTimeDiff = float(timeDiff))
clientSpeedRate = distance2D * 1000.0f / floatTimeDiff;
// we create a diff speed in uint32 for further precision checking to avoid legit fall and slide // we create a diff speed in uint32 for further precision checking to avoid legit fall and slide
uint32 diffspeed = clientSpeedRate - speedRate; float diffspeed = clientSpeedRate - speedRate;
// create a conf to establish a speed limit tolerance over server rate set speed // create a conf to establish a speed limit tolerance over server rate set speed
// this is done so we can ignore minor violations that are not false positives such as going 1 or 2 over the speed limit // this is done so we can ignore minor violations that are not false positives such as going 1 or 2 over the speed limit
_assignedspeeddiff = sConfigMgr->GetOption<uint32>("Anticheat.SpeedLimitTolerance", 0); float assignedspeeddiff = sConfigMgr->GetOption<float>("Anticheat.SpeedLimitTolerance", 0.0f);
// We did the (uint32) cast to accept a margin of tolerance for seasonal spells and buffs such as sugar rush // We did the (uint32) cast to accept a margin of tolerance for seasonal spells and buffs such as sugar rush
// We check the last MovementInfo for the falling flag since falling down a hill and sliding a bit triggered a false positive // We check the last MovementInfo for the falling flag since falling down a hill and sliding a bit triggered a false positive
if ((diffspeed >= _assignedspeeddiff) && !m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_FALLING)) if ((diffspeed >= assignedspeeddiff) && !m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_FALLING))
{ {
if (clientSpeedRate > speedRate * 1.05f) if (clientSpeedRate > speedRate * 1.05f)
{ {

View File

@@ -131,9 +131,11 @@ class AnticheatMgr
void BGStartExploit(Player* player, MovementInfo movementInfo); void BGStartExploit(Player* player, MovementInfo movementInfo);
void BuildReport(Player* player, ReportTypes reportType); void BuildReport(Player* player, ReportTypes reportType);
bool MustCheckTempReports(ReportTypes type); bool MustCheckTempReports(ReportTypes type);
[[nodiscard]] uint32 GetTeleportSkillCooldownDurationInMS(Player* player) const;
[[nodiscard]] float GetTeleportSkillDistanceInYards(Player* player) const;
[[nodiscard]] float GetPlayerCurrentSpeedRate(Player* player) const;
uint32 _counter = 0; uint32 _counter = 0;
uint32 _alertFrequency = 0; uint32 _alertFrequency = 0;
uint32 _assignedspeeddiff = 0;
uint32 _updateCheckTimer = 4000; uint32 _updateCheckTimer = 4000;
uint32 m_MapId; uint32 m_MapId;
std::array<Position, PVP_TEAMS_COUNT> _startPosition; std::array<Position, PVP_TEAMS_COUNT> _startPosition;