mirror of
https://github.com/azerothcore/mod-anticheat.git
synced 2026-01-13 00:58:35 +00:00
fix(): False positives against speed hacks and countermeasures (#114)
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user