/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "AnticheatMgr.h"
#include "Log.h"
#include "MapMgr.h"
#include "Player.h"
#include "Configuration/Config.h"
#define CLIMB_ANGLE 1.87f
#define LANG_ANTICHEAT_ALERT 30087
#define LANG_ANTICHEAT_TELEPORT 30088
#define LANG_ANTICHEAT_IGNORECONTROL 30089
AnticheatMgr::AnticheatMgr()
{
}
AnticheatMgr::~AnticheatMgr()
{
m_Players.clear();
}
void AnticheatMgr::JumpHackDetection(Player* player, MovementInfo /* movementInfo */, uint32 opcode)
{
if (!sConfigMgr->GetOption("Anticheat.DetectJumpHack", true))
return;
ObjectGuid key = player->GetGUID();
if (m_Players[key].GetLastOpcode() == MSG_MOVE_JUMP && opcode == MSG_MOVE_JUMP)
{
BuildReport(player, JUMP_HACK_REPORT);
LOG_INFO("module", "AnticheatMgr:: Jump-Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
}
void AnticheatMgr::WalkOnWaterHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetOption("Anticheat.DetectWaterWalkHack", true))
return;
// ghost can water walk
if (player->HasAuraType(SPELL_AURA_GHOST))
return;
// Prevents the False Positive for water walking when you ressurrect.
// Aura 15007 (Resurrection sickness) is given while dead before returning back to life.
if (player->HasAuraType(SPELL_AURA_GHOST) && player->HasAura(15007))
return;
ObjectGuid key = player->GetGUID();
/* Thanks to @LilleCarl */
if (m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && movementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING))
{
if (player->HasAuraType(SPELL_AURA_WATER_WALK) || player->HasAuraType(SPELL_AURA_FEATHER_FALL) ||
player->HasAuraType(SPELL_AURA_SAFE_FALL))
{
return;
}
}
else if (!m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && !movementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING))
{
return;
}
if (sConfigMgr->GetOption("Anticheat.KickPlayerWaterWalkHack", false))
{
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Walk on Water - Hack detected and counteracted by kicking player {} ({})", player->GetName(), player->GetGUID().ToString());
}
player->GetSession()->KickPlayer(true);
if (sConfigMgr->GetOption("Anticheat.AnnounceKick", true))
{
std::string plr = player->GetName();
std::string tag_colour = "7bbef7";
std::string plr_colour = "ff0000";
std::ostringstream stream;
stream << "|CFF" << plr_colour << "[AntiCheat]|r|CFF" << tag_colour <<
" Player |r|cff" << plr_colour << plr << "|r|cff" << tag_colour <<
" has been kicked.|r";
sWorld->SendServerMessage(SERVER_MSG_STRING, stream.str().c_str());
}
}
else if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Walk on Water - Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, WALK_WATER_HACK_REPORT);
}
void AnticheatMgr::FlyHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetOption("Anticheat.DetectFlyHack", true))
{
return;
}
if (player->HasAuraType(SPELL_AURA_FLY) || player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || player->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED))//overkill but wth
{
return;
}
/*Thanks to @LilleCarl for info to check extra flag*/
bool stricterChecks = true;
if (sConfigMgr->GetOption("Anticheat.StricterFlyHackCheck", false))
{
stricterChecks = !(movementInfo.HasMovementFlag(MOVEMENTFLAG_ASCENDING) && !player->IsInWater());
}
if (!movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY) && !movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING) && stricterChecks)
{
return;
}
if (sConfigMgr->GetOption("Anticheat.KickPlayerFlyHack", false))
{
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Fly-Hack detected and counteracted by kicking player {} ({})", player->GetName(), player->GetGUID().ToString());
}
player->GetSession()->KickPlayer(true);
if (sConfigMgr->GetOption("Anticheat.AnnounceKick", true))
{
std::string plr = player->GetName();
std::string tag_colour = "7bbef7";
std::string plr_colour = "ff0000";
std::ostringstream stream;
stream << "|CFF" << plr_colour << "[AntiCheat]|r|CFF" << tag_colour <<
" Player |r|cff" << plr_colour << plr << "|r|cff" << tag_colour <<
" has been kicked.|r";
sWorld->SendServerMessage(SERVER_MSG_STRING, stream.str().c_str());
}
}
else if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Fly-Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, FLY_HACK_REPORT);
}
void AnticheatMgr::TeleportPlaneHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetOption("Anticheat.DetectTelePlaneHack", true))
return;
ObjectGuid key = player->GetGUID();
if (m_Players[key].GetLastMovementInfo().pos.GetPositionZ() != 0 ||
movementInfo.pos.GetPositionZ() != 0)
return;
if (movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING))
return;
float x, y, z;
player->GetPosition(x, y, z);
float ground_Z = player->GetMap()->GetHeight(x, y, z);
float z_diff = fabs(ground_Z - z);
// we are not really walking there
if (z_diff > 1.0f)
{
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Teleport To Plane - Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, TELEPORT_PLANE_HACK_REPORT);
}
}
void AnticheatMgr::IgnoreControlHackDetection(Player* player, MovementInfo movementInfo)
{
float x, y;
player->GetPosition(x, y);
ObjectGuid key = player->GetGUID();
if (sConfigMgr->GetOption("Anticheat.IgnoreControlHack", true))
{
if (player->HasUnitState(UNIT_STATE_ROOT) && !player->GetVehicle())
{
bool unrestricted = movementInfo.pos.GetPositionX() != x || movementInfo.pos.GetPositionY() != y;
if (unrestricted)
{
if (m_Players[key].GetTotalReports() > sConfigMgr->GetOption("Anticheat.ReportsForIngameWarnings", 70))
{
// display warning at the center of the screen, hacky way?
std::string str = "";
str = "|cFFFFFC00[Playername:|cFF00FFFF[|cFF60FF00" + std::string(player->GetName().c_str()) + "|cFF00FFFF] Possible Ignore Control Hack Detected!";
WorldPacket data(SMSG_NOTIFICATION, (str.size() + 1));
data << str;
sWorld->SendGlobalGMMessage(&data);
// need better way to limit chat spam
if (m_Players[key].GetTotalReports() >= sConfigMgr->GetOption("Anticheat.ReportinChat.Min", 70) && m_Players[key].GetTotalReports() <= sConfigMgr->GetOption("Anticheat.ReportinChat.Max", 80))
{
sWorld->SendGMText(LANG_ANTICHEAT_IGNORECONTROL, player->GetName().c_str());
}
}
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Ignore Control - Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, IGNORE_CONTROL_REPORT);
}
}
}
}
void AnticheatMgr::TeleportHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetOption("Anticheat.DetectTelePortHack", true))
return;
ObjectGuid key = player->GetGUID();
if (m_Players[key].GetLastMovementInfo().pos.GetPositionX() == movementInfo.pos.GetPositionX())
return;
float lastX = m_Players[key].GetLastMovementInfo().pos.GetPositionX();
float newX = movementInfo.pos.GetPositionX();
float lastY = m_Players[key].GetLastMovementInfo().pos.GetPositionY();
float newY = movementInfo.pos.GetPositionY();
float xDiff = fabs(lastX - newX);
float yDiff = fabs(lastY - newY);
if ((xDiff >= 50.0f || yDiff >= 50.0f) && !player->CanTeleport())
{
if (m_Players[key].GetTotalReports() > sConfigMgr->GetOption("Anticheat.ReportsForIngameWarnings", 70))
{
// display warning at the center of the screen, hacky way?
std::string str = "";
str = "|cFFFFFC00[Playername:|cFF00FFFF[|cFF60FF00" + std::string(player->GetName().c_str()) + "|cFF00FFFF] Possible Teleport Hack Detected!";
WorldPacket data(SMSG_NOTIFICATION, (str.size() + 1));
data << str;
sWorld->SendGlobalGMMessage(&data);
// need better way to limit chat spam
if (m_Players[key].GetTotalReports() >= sConfigMgr->GetOption("Anticheat.ReportinChat.Min", 70) && m_Players[key].GetTotalReports() <= sConfigMgr->GetOption("Anticheat.ReportinChat.Max", 80))
{
sWorld->SendGMText(LANG_ANTICHEAT_TELEPORT, player->GetName().c_str());
}
}
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Teleport-Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, TELEPORT_HACK_REPORT);
}
else if (player->CanTeleport())
player->SetCanTeleport(false);
}
void AnticheatMgr::StartHackDetection(Player* player, MovementInfo movementInfo, uint32 opcode)
{
if (!sConfigMgr->GetOption("Anticheat.Enabled", 0))
return;
if (player->IsGameMaster())
return;
ObjectGuid key = player->GetGUID();
if (player->IsInFlight() || player->GetTransport() || player->GetVehicle())
{
m_Players[key].SetLastMovementInfo(movementInfo);
m_Players[key].SetLastOpcode(opcode);
return;
}
SpeedHackDetection(player, movementInfo);
FlyHackDetection(player, movementInfo);
WalkOnWaterHackDetection(player, movementInfo);
JumpHackDetection(player, movementInfo, opcode);
TeleportPlaneHackDetection(player, movementInfo);
ClimbHackDetection(player, movementInfo, opcode);
TeleportHackDetection(player, movementInfo);
IgnoreControlHackDetection(player, movementInfo);
m_Players[key].SetLastMovementInfo(movementInfo);
m_Players[key].SetLastOpcode(opcode);
}
// basic detection
void AnticheatMgr::ClimbHackDetection(Player* player, MovementInfo movementInfo, uint32 opcode)
{
if (!sConfigMgr->GetOption("Anticheat.DetectClimbHack", false))
return;
ObjectGuid key = player->GetGUID();
if (opcode != MSG_MOVE_HEARTBEAT ||
m_Players[key].GetLastOpcode() != MSG_MOVE_HEARTBEAT)
return;
// in this case we don't care if they are "legal" flags, they are handled in another parts of the Anticheat Manager.
if (player->IsInWater() ||
player->IsFlying() ||
player->IsFalling())
return;
Position playerPos = player->GetPosition();
float deltaZ = fabs(playerPos.GetPositionZ() - movementInfo.pos.GetPositionZ());
float deltaXY = movementInfo.pos.GetExactDist2d(&playerPos);
float angle = Position::NormalizeOrientation(tan(deltaZ / deltaXY));
if (angle > CLIMB_ANGLE)
{
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Climb-Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, CLIMB_HACK_REPORT);
}
}
void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetOption("Anticheat.DetectSpeedHack", true))
return;
ObjectGuid key = player->GetGUID();
// We also must check the map because the movementFlag can be modified by the client.
// If we just check the flag, they could always add that flag and always skip the speed hacking detection.
if (m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && player->GetMapId())
switch (player->GetMapId())
{
case 369: //Transport: DEEPRUN TRAM
case 607: //Transport: Strands of the Ancients
case 582: //Transport: Rut'theran to Auberdine
case 584: //Transport: Menethil to Theramore
case 586: //Transport: Exodar to Auberdine
case 587: //Transport: Feathermoon Ferry
case 588: //Transport: Menethil to Auberdine
case 589: //Transport: Orgrimmar to Grom'Gol
case 590: //Transport: Grom'Gol to Undercity
case 591: //Transport: Undercity to Orgrimmar
case 592: //Transport: Borean Tundra Test
case 593: //Transport: Booty Bay to Ratchet
case 594: //Transport: Howling Fjord Sister Mercy (Quest)
case 596: //Transport: Naglfar
case 610: //Transport: Tirisfal to Vengeance Landing
case 612: //Transport: Menethil to Valgarde
case 613: //Transport: Orgrimmar to Warsong Hold
case 614: //Transport: Stormwind to Valiance Keep
case 620: //Transport: Moa'ki to Unu'pe
case 621: //Transport: Moa'ki to Kamagua
case 622: //Transport: Orgrim's Hammer
case 623: //Transport: The Skybreaker
case 641: //Transport: Alliance Airship BG
case 642: //Transport: Horde Airship BG
case 647: //Transport: Orgrimmar to Thunder Bluff
case 672: //Transport: The Skybreaker (Icecrown Citadel Raid)
case 673: //Transport: Orgrim's Hammer (Icecrown Citadel Raid)
case 712: //Transport: The Skybreaker (IC Dungeon)
case 713: //Transport: Orgrim's Hammer (IC Dungeon)
case 718: //Transport: The Mighty Wind (Icecrown Citadel Raid)
return;
break;
}
uint32 distance2D = (uint32)movementInfo.pos.GetExactDist2d(&m_Players[key].GetLastMovementInfo().pos);
uint8 moveType = 0;
// we need to know HOW is the player moving
// TO-DO: Should we check the incoming movement flags?
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.
uint32 speedRate = (uint32)(player->GetSpeed(UnitMoveType(moveType)) + movementInfo.jump.xyspeed);
// how long the player took to move to here.
uint32 timeDiff = getMSTimeDiff(m_Players[key].GetLastMovementInfo().time, movementInfo.time);
if (int32(timeDiff) < 0)
{
BuildReport(player, SPEED_HACK_REPORT);
timeDiff = 1;
}
if (!timeDiff)
timeDiff = 1;
// Rouge Class Killing Spree causes false flags so we add the exception here
if (player->getClass() == CLASS_ROGUE && player->GetAura(51690))
{
return;
}
// Mage Class Blink causes false flags so we add the exception here
if (player->getClass() == CLASS_MAGE && player->GetAura(1953))
{
return;
}
// 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;
// we did the (uint32) cast to accept a margin of tolerance
if (clientSpeedRate > speedRate * 1.25f)
{
if (sConfigMgr->GetOption("Anticheat.WriteLog", true))
{
LOG_INFO("module", "AnticheatMgr:: Speed-Hack detected player {} ({})", player->GetName(), player->GetGUID().ToString());
}
BuildReport(player, SPEED_HACK_REPORT);
}
}
void AnticheatMgr::HandlePlayerLogin(Player* player)
{
// we must delete this to prevent errors in case of crash
CharacterDatabase.Execute("DELETE FROM players_reports_status WHERE guid={}", player->GetGUID().GetCounter());
// we initialize the pos of lastMovementPosition var.
m_Players[player->GetGUID()].SetPosition(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation());
QueryResult resultDB = CharacterDatabase.Query("SELECT * FROM daily_players_reports WHERE guid={};", player->GetGUID().GetCounter());
if (resultDB)
m_Players[player->GetGUID()].SetDailyReportState(true);
}
void AnticheatMgr::HandlePlayerLogout(Player* player)
{
// TO-DO Make a table that stores the cheaters of the day, with more detailed information.
// We must also delete it at logout to prevent have data of offline players in the db when we query the database (IE: The GM Command)
CharacterDatabase.Execute("DELETE FROM players_reports_status WHERE guid={}", player->GetGUID().GetCounter());
// Delete not needed data from the memory.
m_Players.erase(player->GetGUID());
}
void AnticheatMgr::SavePlayerData(Player* player)
{// 1 2 3 4 5 6 7 8 9 10
CharacterDatabase.Execute("REPLACE INTO players_reports_status (guid,average,total_reports,speed_reports,fly_reports,jump_reports,waterwalk_reports,teleportplane_reports,climb_reports,creation_time) VALUES ({},{},{},{},{},{},{},{},{},{});", player->GetGUID().GetCounter(), m_Players[player->GetGUID()].GetAverage(), m_Players[player->GetGUID()].GetTotalReports(), m_Players[player->GetGUID()].GetTypeReports(SPEED_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(FLY_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(JUMP_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(WALK_WATER_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(CLIMB_HACK_REPORT), m_Players[player->GetGUID()].GetCreationTime());
}
uint32 AnticheatMgr::GetTotalReports(ObjectGuid guid)
{
return m_Players[guid].GetTotalReports();
}
float AnticheatMgr::GetAverage(ObjectGuid guid)
{
return m_Players[guid].GetAverage();
}
uint32 AnticheatMgr::GetTypeReports(ObjectGuid guid, uint8 type)
{
return m_Players[guid].GetTypeReports(type);
}
bool AnticheatMgr::MustCheckTempReports(uint8 type)
{
if (type == JUMP_HACK_REPORT)
return false;
if (type == TELEPORT_HACK_REPORT)
return false;
if (type == IGNORE_CONTROL_REPORT)
return false;
return true;
}
void AnticheatMgr::BuildReport(Player* player, uint16 reportType)
{
ObjectGuid key = player->GetGUID();
if (MustCheckTempReports(reportType))
{
uint32 actualTime = getMSTime();
if (!m_Players[key].GetTempReportsTimer(reportType))
m_Players[key].SetTempReportsTimer(actualTime, reportType);
if (getMSTimeDiff(m_Players[key].GetTempReportsTimer(reportType), actualTime) < 3000)
{
m_Players[key].SetTempReports(m_Players[key].GetTempReports(reportType) + 1, reportType);
if (m_Players[key].GetTempReports(reportType) < 3)
return;
}
else
{
m_Players[key].SetTempReportsTimer(actualTime, reportType);
m_Players[key].SetTempReports(1, reportType);
return;
}
}
// generating creationTime for average calculation
if (!m_Players[key].GetTotalReports())
m_Players[key].SetCreationTime(getMSTime());
// increasing total_reports
m_Players[key].SetTotalReports(m_Players[key].GetTotalReports() + 1);
// increasing specific cheat report
m_Players[key].SetTypeReports(reportType, m_Players[key].GetTypeReports(reportType) + 1);
// diff time for average calculation
uint32 diffTime = getMSTimeDiff(m_Players[key].GetCreationTime(), getMSTime()) / IN_MILLISECONDS;
if (diffTime > 0)
{
// Average == Reports per second
float average = float(m_Players[key].GetTotalReports()) / float(diffTime);
m_Players[key].SetAverage(average);
}
if (sConfigMgr->GetOption("Anticheat.MaxReportsForDailyReport", 70) < m_Players[key].GetTotalReports())
{
if (!m_Players[key].GetDailyReportState())
{// 1 2 3 4 5 6 7 8 9 10
CharacterDatabase.Execute("REPLACE INTO daily_players_reports (guid,average,total_reports,speed_reports,fly_reports,jump_reports,waterwalk_reports,teleportplane_reports,climb_reports,creation_time) VALUES ({},{},{},{},{},{},{},{},{},{});", player->GetGUID().GetCounter(), m_Players[player->GetGUID()].GetAverage(), m_Players[player->GetGUID()].GetTotalReports(), m_Players[player->GetGUID()].GetTypeReports(SPEED_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(FLY_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(JUMP_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(WALK_WATER_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT), m_Players[player->GetGUID()].GetTypeReports(CLIMB_HACK_REPORT), m_Players[player->GetGUID()].GetCreationTime());
m_Players[key].SetDailyReportState(true);
}
}
if (m_Players[key].GetTotalReports() > sConfigMgr->GetOption("Anticheat.ReportsForIngameWarnings", 70))
{
// display warning at the center of the screen, hacky way?
std::string str = "";
str = "|cFFFFFC00[Playername:|cFF00FFFF[|cFF60FF00" + std::string(player->GetName().c_str()) + "|cFF00FFFF] Possible cheater!";
WorldPacket data(SMSG_NOTIFICATION, (str.size() + 1));
data << str;
sWorld->SendGlobalGMMessage(&data);
// need better way to limit chat spam
if (m_Players[key].GetTotalReports() >= sConfigMgr->GetOption("Anticheat.ReportinChat.Min", 70) && m_Players[key].GetTotalReports() <= sConfigMgr->GetOption("Anticheat.ReportinChat.Max", 80))
{
sWorld->SendGMText(LANG_ANTICHEAT_ALERT, player->GetName().c_str(), player->GetName().c_str());
}
}
}
void AnticheatMgr::AnticheatGlobalCommand(ChatHandler* handler)
{
// MySQL will sort all for us, anyway this is not the best way we must only save the anticheat data not whole player's data!.
ObjectAccessor::SaveAllPlayers();
QueryResult resultDB = CharacterDatabase.Query("SELECT guid,average,total_reports FROM players_reports_status WHERE total_reports != 0 ORDER BY average ASC LIMIT 3;");
if (!resultDB)
{
handler->PSendSysMessage("No players found.");
return;
}
else
{
handler->SendSysMessage("=============================");
handler->PSendSysMessage("Players with the lowest averages:");
do
{
Field* fieldsDB = resultDB->Fetch();
ObjectGuid guid = ObjectGuid::Create(fieldsDB[0].Get());
float average = fieldsDB[1].Get();
uint32 total_reports = fieldsDB[2].Get();
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
handler->PSendSysMessage("Player: %s Average: %f Total Reports: %u", player->GetName().c_str(), average, total_reports);
} while (resultDB->NextRow());
}
resultDB = CharacterDatabase.Query("SELECT guid,average,total_reports FROM players_reports_status WHERE total_reports != 0 ORDER BY total_reports DESC LIMIT 3;");
// this should never happen
if (!resultDB)
{
handler->PSendSysMessage("No players found.");
return;
}
else
{
handler->PSendSysMessage("=============================");
handler->PSendSysMessage("Players with the more reports:");
do
{
Field* fieldsDB = resultDB->Fetch();
ObjectGuid guid = ObjectGuid::Create(fieldsDB[0].Get());
float average = fieldsDB[1].Get();
uint32 total_reports = fieldsDB[2].Get();
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
handler->PSendSysMessage("Player: %s Total Reports: %u Average: %f", player->GetName().c_str(), total_reports, average);
} while (resultDB->NextRow());
}
}
void AnticheatMgr::AnticheatDeleteCommand(ObjectGuid guid)
{
if (!guid)
{
for (AnticheatPlayersDataMap::iterator it = m_Players.begin(); it != m_Players.end(); ++it)
{
(*it).second.SetTotalReports(0);
(*it).second.SetAverage(0);
(*it).second.SetCreationTime(0);
for (uint8 i = 0; i < MAX_REPORT_TYPES; i++)
{
(*it).second.SetTempReports(0, i);
(*it).second.SetTempReportsTimer(0, i);
(*it).second.SetTypeReports(i, 0);
}
}
CharacterDatabase.Execute("DELETE FROM players_reports_status;");
}
else
{
m_Players[guid].SetTotalReports(0);
m_Players[guid].SetAverage(0);
m_Players[guid].SetCreationTime(0);
for (uint8 i = 0; i < MAX_REPORT_TYPES; i++)
{
m_Players[guid].SetTempReports(0, i);
m_Players[guid].SetTempReportsTimer(0, i);
m_Players[guid].SetTypeReports(i, 0);
}
CharacterDatabase.Execute("DELETE FROM players_reports_status WHERE guid={};", guid.GetCounter());
}
}
void AnticheatMgr::ResetDailyReportStates()
{
for (AnticheatPlayersDataMap::iterator it = m_Players.begin(); it != m_Players.end(); ++it)
m_Players[(*it).first].SetDailyReportState(false);
}