Initial upload of AnticheatModule

This commit is contained in:
3ndos
2016-12-05 11:39:52 -05:00
commit 05231e0c2c
15 changed files with 1181 additions and 0 deletions

10
CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
AC_ADD_SCRIPT("${CMAKE_CURRENT_LIST_DIR}/src/PassiveAnticheat.cpp")
AC_ADD_SCRIPT("${CMAKE_CURRENT_LIST_DIR}/src/AnticheatMgr.cpp")
AC_ADD_SCRIPT("${CMAKE_CURRENT_LIST_DIR}/src/AnticheatScripts.cpp")
AC_ADD_SCRIPT("${CMAKE_CURRENT_LIST_DIR}/src/AnticheatData.cpp")
AC_ADD_SCRIPT("${CMAKE_CURRENT_LIST_DIR}/src/cs_anticheat.cpp")
AC_ADD_SCRIPT_LOADER("PassiveAnticheat" "${CMAKE_CURRENT_LIST_DIR}/src/loader.h")
CU_ADD_HOOK(AFTER_WORLDSERVER_CMAKE "${CMAKE_CURRENT_LIST_DIR}/src/cmake/after_ws_install.cmake")

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
Currently this only works with the <b>moreScriptHooks</b> Branch:
https://github.com/3ndos/azerothcore-wotlk/tree/moreScriptHooks
<b>Make sure to support the main project:
https://github.com/azerothcore/azerothcore-wotlk/</b>
# AnticheatModule
* <b>!!BEFORE RUNNING!!:</b> Execute the included "conf/SQL/charactersdb_anticheat.sql" file on your characters database. This creates a necessary table for this module.
* This is a port of the PassiveAnticheat Script from lordpsyan's repo to azerothcore.

40
conf/Anticheat.conf.dist Normal file
View File

@@ -0,0 +1,40 @@
[worldserver]
#
###################################################################################################
###################################################################################################
# Anticheat.Enable
# Description: Enables or disables the Anticheat System functionality
# Default: 1 - (Enabled)
# 0 - (Disabled)
Anticheat.Enabled = 1
# Anticheat.ReportsForIngameWarnings
# Description: How many reports the player must have to notify to GameMasters ingame when he generates a new report.
# Default: 70
Anticheat.ReportsForIngameWarnings = 70
# Anticheat.LoginMessage
# Description: Enable login message "This server is running an Anticheat module."
# Default: 1
Anticheat.LoginMessage = 1
# Anticheat.Detect
# Description: It represents which detections are enabled.
# Default 1 (climbhack = 0 by default)
Anticheat.DetectFlyHack = 1
Anticheat.DetectWaterWalk = 1
Anticheat.DetectJumpHack = 1
Anticheat.DetectTelePlaneHack = 1
Anticheat.DetectSpeedHack = 1
Anticheat.DetectClimbHack = 0
# Anticheat.MaxReportsForDailyReport
# Description: How many reports must the player have to make a report that it is in DB for a day (not only during the player's session).
# Default: 70
Anticheat.MaxReportsForDailyReport = 70

View File

@@ -0,0 +1,30 @@
DROP TABLE IF EXISTS `players_reports_status`;
CREATE TABLE `players_reports_status` (
`guid` int(10) unsigned NOT NULL DEFAULT '0',
`creation_time` int(10) unsigned NOT NULL DEFAULT '0',
`average` float NOT NULL DEFAULT '0',
`total_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`speed_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`fly_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`jump_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`waterwalk_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`teleportplane_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`climb_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
DROP TABLE IF EXISTS `daily_players_reports`;
CREATE TABLE `daily_players_reports` (
`guid` int(10) unsigned NOT NULL DEFAULT '0',
`creation_time` int(10) unsigned NOT NULL DEFAULT '0',
`average` float NOT NULL DEFAULT '0',
`total_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`speed_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`fly_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`jump_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`waterwalk_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`teleportplane_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
`climb_reports` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';

118
src/AnticheatData.cpp Normal file
View File

@@ -0,0 +1,118 @@
#include "AnticheatData.h"
AnticheatData::AnticheatData()
{
lastOpcode = 0;
totalReports = 0;
for (uint8 i = 0; i < MAX_REPORT_TYPES; i++)
{
typeReports[i] = 0;
tempReports[i] = 0;
tempReportsTimer[i] = 0;
}
average = 0;
creationTime = 0;
hasDailyReport = false;
}
AnticheatData::~AnticheatData()
{
}
void AnticheatData::SetDailyReportState(bool b)
{
hasDailyReport = b;
}
bool AnticheatData::GetDailyReportState()
{
return hasDailyReport;
}
void AnticheatData::SetLastOpcode(uint32 opcode)
{
lastOpcode = opcode;
}
void AnticheatData::SetPosition(float x, float y, float z, float o)
{
lastMovementInfo.pos = { x, y, z, o };
}
uint32 AnticheatData::GetLastOpcode() const
{
return lastOpcode;
}
const MovementInfo& AnticheatData::GetLastMovementInfo() const
{
return lastMovementInfo;
}
void AnticheatData::SetLastMovementInfo(MovementInfo& moveInfo)
{
lastMovementInfo = moveInfo;
}
uint32 AnticheatData::GetTotalReports() const
{
return totalReports;
}
void AnticheatData::SetTotalReports(uint32 _totalReports)
{
totalReports = _totalReports;
}
void AnticheatData::SetTypeReports(uint32 type, uint32 amount)
{
typeReports[type] = amount;
}
uint32 AnticheatData::GetTypeReports(uint32 type) const
{
return typeReports[type];
}
float AnticheatData::GetAverage() const
{
return average;
}
void AnticheatData::SetAverage(float _average)
{
average = _average;
}
uint32 AnticheatData::GetCreationTime() const
{
return creationTime;
}
void AnticheatData::SetCreationTime(uint32 _creationTime)
{
creationTime = _creationTime;
}
void AnticheatData::SetTempReports(uint32 amount, uint8 type)
{
tempReports[type] = amount;
}
uint32 AnticheatData::GetTempReports(uint8 type)
{
return tempReports[type];
}
void AnticheatData::SetTempReportsTimer(uint32 time, uint8 type)
{
tempReportsTimer[type] = time;
}
uint32 AnticheatData::GetTempReportsTimer(uint8 type)
{
return tempReportsTimer[type];
}

63
src/AnticheatData.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef SC_ACDATA_H
#define SC_ACDATA_H
#include "AnticheatMgr.h"
#define MAX_REPORT_TYPES 6
class AnticheatData
{
public:
AnticheatData();
~AnticheatData();
void SetLastOpcode(uint32 opcode);
uint32 GetLastOpcode() const;
const MovementInfo& GetLastMovementInfo() const;
void SetLastMovementInfo(MovementInfo& moveInfo);
void SetPosition(float x, float y, float z, float o);
/*
bool GetDisableACCheck() const;
void SetDisableACCheck(bool check);
uint32 GetDisableACTimer() const;
void SetDisableACTimer(uint32 timer);*/
uint32 GetTotalReports() const;
void SetTotalReports(uint32 _totalReports);
uint32 GetTypeReports(uint32 type) const;
void SetTypeReports(uint32 type, uint32 amount);
float GetAverage() const;
void SetAverage(float _average);
uint32 GetCreationTime() const;
void SetCreationTime(uint32 creationTime);
void SetTempReports(uint32 amount, uint8 type);
uint32 GetTempReports(uint8 type);
void SetTempReportsTimer(uint32 time, uint8 type);
uint32 GetTempReportsTimer(uint8 type);
void SetDailyReportState(bool b);
bool GetDailyReportState();
private:
uint32 lastOpcode;
MovementInfo lastMovementInfo;
//bool disableACCheck;
//uint32 disableACCheckTimer;
uint32 totalReports;
uint32 typeReports[MAX_REPORT_TYPES];
float average;
uint32 creationTime;
uint32 tempReports[MAX_REPORT_TYPES];
uint32 tempReportsTimer[MAX_REPORT_TYPES];
bool hasDailyReport;
};
#endif

430
src/AnticheatMgr.cpp Normal file
View File

@@ -0,0 +1,430 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "AnticheatMgr.h"
#include "MapManager.h"
#include "Player.h"
#include "Configuration\Config.h"
#define CLIMB_ANGLE 1.9f
AnticheatMgr::AnticheatMgr()
{
}
AnticheatMgr::~AnticheatMgr()
{
m_Players.clear();
}
void AnticheatMgr::JumpHackDetection(Player* player, MovementInfo /* movementInfo */,uint32 opcode)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectJumpHack", true))
return;
uint32 key = player->GetGUIDLow();
if (m_Players[key].GetLastOpcode() == MSG_MOVE_JUMP && opcode == MSG_MOVE_JUMP)
{
BuildReport(player,JUMP_HACK_REPORT);
sLog->outString( "AnticheatMgr:: Jump-Hack detected player GUID (low) %u",player->GetGUIDLow());
}
}
void AnticheatMgr::WalkOnWaterHackDetection(Player* player, MovementInfo /* movementInfo */)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectWaterWalk", true))
return;
uint32 key = player->GetGUIDLow();
if (!m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_WATERWALKING))
return;
// if we are a ghost we can walk on water
if (!player->IsAlive())
return;
if (player->HasAuraType(SPELL_AURA_FEATHER_FALL) ||
player->HasAuraType(SPELL_AURA_SAFE_FALL) ||
player->HasAuraType(SPELL_AURA_WATER_WALK))
return;
sLog->outString( "AnticheatMgr:: Walk on Water - Hack detected player GUID (low) %u",player->GetGUIDLow());
BuildReport(player,WALK_WATER_HACK_REPORT);
}
void AnticheatMgr::FlyHackDetection(Player* player, MovementInfo /* movementInfo */)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectFlyHack", true))
return;
uint32 key = player->GetGUIDLow();
if (!m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_FLYING))
return;
if (player->HasAuraType(SPELL_AURA_FLY) ||
player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) ||
player->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED))
return;
sLog->outString( "AnticheatMgr:: Fly-Hack detected player GUID (low) %u",player->GetGUIDLow());
BuildReport(player,FLY_HACK_REPORT);
}
void AnticheatMgr::TeleportPlaneHackDetection(Player* player, MovementInfo movementInfo)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectTelePlaneHack", true))
return;
uint32 key = player->GetGUIDLow();
if (m_Players[key].GetLastMovementInfo().pos.GetPositionZ() != 0 ||
movementInfo.pos.GetPositionZ() != 0)
return;
if (movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING))
return;
// DEAD_FALLING was deprecated
//if (player->getDeathState() == DEAD_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)
{
sLog->outString( "AnticheatMgr:: Teleport To Plane - Hack detected player GUID (low) %u",player->GetGUIDLow());
BuildReport(player,TELEPORT_PLANE_HACK_REPORT);
}
}
void AnticheatMgr::StartHackDetection(Player* player, MovementInfo movementInfo, uint32 opcode)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
return;
if (player->IsGameMaster())
return;
uint32 key = player->GetGUIDLow();
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);
m_Players[key].SetLastMovementInfo(movementInfo);
m_Players[key].SetLastOpcode(opcode);
}
// basic detection
void AnticheatMgr::ClimbHackDetection(Player *player, MovementInfo movementInfo, uint32 opcode)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectClimbHack", false))
return;
uint32 key = player->GetGUIDLow();
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;
// Position pos = 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)
{
sLog->outString( "AnticheatMgr:: Climb-Hack detected player GUID (low) %u", player->GetGUIDLow());
BuildReport(player,CLIMB_HACK_REPORT);
}
}
void AnticheatMgr::SpeedHackDetection(Player* player,MovementInfo movementInfo)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.DetectSpeedHack", true))
return;
uint32 key = player->GetGUIDLow();
// 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.
// 369 == DEEPRUN TRAM
if (m_Players[key].GetLastMovementInfo().HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && player->GetMapId() == 369)
return;
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 (!timeDiff)
timeDiff = 1;
// 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)
{
BuildReport(player,SPEED_HACK_REPORT);
sLog->outString( "AnticheatMgr:: Speed-Hack detected player GUID (low) %u",player->GetGUIDLow());
}
}
void AnticheatMgr::HandlePlayerLogin(Player* player)
{
// we must delete this to prevent errors in case of crash
CharacterDatabase.PExecute("DELETE FROM players_reports_status WHERE guid=%u",player->GetGUIDLow());
// we initialize the pos of lastMovementPosition var.
m_Players[player->GetGUIDLow()].SetPosition(player->GetPositionX(),player->GetPositionY(),player->GetPositionZ(),player->GetOrientation());
QueryResult resultDB = CharacterDatabase.PQuery("SELECT * FROM daily_players_reports WHERE guid=%u;",player->GetGUIDLow());
if (resultDB)
m_Players[player->GetGUIDLow()].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.PExecute("DELETE FROM players_reports_status WHERE guid=%u",player->GetGUIDLow());
// Delete not needed data from the memory.
m_Players.erase(player->GetGUIDLow());
}
void AnticheatMgr::SavePlayerData(Player* player)
{
CharacterDatabase.PExecute("REPLACE INTO players_reports_status (guid,average,total_reports,speed_reports,fly_reports,jump_reports,waterwalk_reports,teleportplane_reports,climb_reports,creation_time) VALUES (%u,%f,%u,%u,%u,%u,%u,%u,%u,%u);",player->GetGUIDLow(),m_Players[player->GetGUIDLow()].GetAverage(),m_Players[player->GetGUIDLow()].GetTotalReports(), m_Players[player->GetGUIDLow()].GetTypeReports(SPEED_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(FLY_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(JUMP_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(WALK_WATER_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(CLIMB_HACK_REPORT),m_Players[player->GetGUIDLow()].GetCreationTime());
}
uint32 AnticheatMgr::GetTotalReports(uint32 lowGUID)
{
return m_Players[lowGUID].GetTotalReports();
}
float AnticheatMgr::GetAverage(uint32 lowGUID)
{
return m_Players[lowGUID].GetAverage();
}
uint32 AnticheatMgr::GetTypeReports(uint32 lowGUID, uint8 type)
{
return m_Players[lowGUID].GetTypeReports(type);
}
bool AnticheatMgr::MustCheckTempReports(uint8 type)
{
if (type == JUMP_HACK_REPORT)
return false;
return true;
}
void AnticheatMgr::BuildReport(Player* player,uint8 reportType)
{
uint32 key = player->GetGUIDLow();
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 ((uint32)sConfigMgr->GetIntDefault("Anticheat.MaxReportsForDailyReport", 70) < m_Players[key].GetTotalReports())
{
if (!m_Players[key].GetDailyReportState())
{
CharacterDatabase.PExecute("REPLACE INTO daily_players_reports (guid,average,total_reports,speed_reports,fly_reports,jump_reports,waterwalk_reports,teleportplane_reports,climb_reports,creation_time) VALUES (%u,%f,%u,%u,%u,%u,%u,%u,%u,%u);",player->GetGUIDLow(),m_Players[player->GetGUIDLow()].GetAverage(),m_Players[player->GetGUIDLow()].GetTotalReports(), m_Players[player->GetGUIDLow()].GetTypeReports(SPEED_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(FLY_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(JUMP_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(WALK_WATER_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT),m_Players[player->GetGUIDLow()].GetTypeReports(CLIMB_HACK_REPORT),m_Players[player->GetGUIDLow()].GetCreationTime());
m_Players[key].SetDailyReportState(true);
}
}
if (m_Players[key].GetTotalReports() > (uint32)sConfigMgr->GetIntDefault("Anticheat.ReportsForIngameWarnings", 70))
{
// display warning at the center of the screen, hacky way?
std::string str = "";
str = "|cFFFFFC00[AC]|cFF00FFFF[|cFF60FF00" + std::string(player->GetName().c_str()) + "|cFF00FFFF] Possible cheater!";
WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
data << str;
sWorld->SendGlobalGMMessage(&data);
}
}
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();
uint32 guid = fieldsDB[0].GetUInt32();
float average = fieldsDB[1].GetFloat();
uint32 total_reports = fieldsDB[2].GetUInt32();
if (Player* player = sObjectMgr->GetPlayerByLowGUID(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();
uint32 guid = fieldsDB[0].GetUInt32();
float average = fieldsDB[1].GetFloat();
uint32 total_reports = fieldsDB[2].GetUInt32();
if (Player* player = sObjectMgr->GetPlayerByLowGUID(guid))
handler->PSendSysMessage("Player: %s Total Reports: %u Average: %f",player->GetName().c_str(),total_reports,average);
} while (resultDB->NextRow());
}
}
void AnticheatMgr::AnticheatDeleteCommand(uint32 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.PExecute("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.PExecute("DELETE FROM players_reports_status WHERE guid=%u;",guid);
}
}
void AnticheatMgr::ResetDailyReportStates()
{
for (AnticheatPlayersDataMap::iterator it = m_Players.begin(); it != m_Players.end(); ++it)
m_Players[(*it).first].SetDailyReportState(false);
}

101
src/AnticheatMgr.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef SC_ACMGR_H
#define SC_ACMGR_H
//#include <ace/Singleton.h>
#include "Common.h"
#include "SharedDefines.h"
#include "ScriptMgr.h"
#include "AnticheatData.h"
#include "Chat.h"
class Player;
class AnticheatData;
enum ReportTypes
{
SPEED_HACK_REPORT = 0,
FLY_HACK_REPORT,
WALK_WATER_HACK_REPORT,
JUMP_HACK_REPORT,
TELEPORT_PLANE_HACK_REPORT,
CLIMB_HACK_REPORT,
// MAX_REPORT_TYPES
};
enum DetectionTypes
{
SPEED_HACK_DETECTION = 1,
FLY_HACK_DETECTION = 2,
WALK_WATER_HACK_DETECTION = 4,
JUMP_HACK_DETECTION = 8,
TELEPORT_PLANE_HACK_DETECTION = 16,
CLIMB_HACK_DETECTION = 32
};
// GUIDLow is the key.
typedef std::map<uint32, AnticheatData> AnticheatPlayersDataMap;
class AnticheatMgr
{
// friend class ACE_Singleton<AnticheatMgr, ACE_Null_Mutex>;
AnticheatMgr();
~AnticheatMgr();
public:
static AnticheatMgr* instance()
{
static AnticheatMgr* instance = new AnticheatMgr();
return instance;
}
void StartHackDetection(Player* player, MovementInfo movementInfo, uint32 opcode);
void DeletePlayerReport(Player* player, bool login);
void DeletePlayerData(Player* player);
void CreatePlayerData(Player* player);
void SavePlayerData(Player* player);
void HandlePlayerLogin(Player* player);
void HandlePlayerLogout(Player* player);
uint32 GetTotalReports(uint32 lowGUID);
float GetAverage(uint32 lowGUID);
uint32 GetTypeReports(uint32 lowGUID, uint8 type);
void AnticheatGlobalCommand(ChatHandler* handler);
void AnticheatDeleteCommand(uint32 guid);
void ResetDailyReportStates();
private:
void SpeedHackDetection(Player* player, MovementInfo movementInfo);
void FlyHackDetection(Player* player, MovementInfo movementInfo);
void WalkOnWaterHackDetection(Player* player, MovementInfo movementInfo);
void JumpHackDetection(Player* player, MovementInfo movementInfo,uint32 opcode);
void TeleportPlaneHackDetection(Player* player, MovementInfo);
void ClimbHackDetection(Player* player,MovementInfo movementInfo,uint32 opcode);
void BuildReport(Player* player,uint8 reportType);
bool MustCheckTempReports(uint8 type);
AnticheatPlayersDataMap m_Players; ///< Player data
};
#define sAnticheatMgr AnticheatMgr::instance()
#endif

93
src/AnticheatScripts.cpp Normal file
View File

@@ -0,0 +1,93 @@
#include "Configuration/Config.h"
#include "AnticheatMgr.h"
#include "Object.h"
int64 resetTime = 0;
int64 lastIterationPlayer = sWorld->GetUptime() + 30;//TODO: change 30 secs static to a configurable option
class AnticheatPlayerScript : public PlayerScript
{
public:
AnticheatPlayerScript()
: PlayerScript("AnticheatPlayerScript")
{
}
void OnLogout(Player* player) override
{
sAnticheatMgr->HandlePlayerLogout(player);
}
void OnLogin(Player* player) override
{
sAnticheatMgr->HandlePlayerLogin(player);
if(sConfigMgr->GetBoolDefault("Anticheat.LoginMessage", true))
ChatHandler(player->GetSession()).PSendSysMessage("This server is running an Anticheat Module.");
}
};
class AnticheatWorldScript : public WorldScript
{
public:
AnticheatWorldScript()
: WorldScript("AnticheatWorldScript")
{
}
void OnUpdate(uint32 diff) override
{
if (sWorld->GetGameTime() > resetTime)
{
sLog->outString( "Anticheat: Resetting daily report states.");
sAnticheatMgr->ResetDailyReportStates();
UpdateReportResetTime();
sLog->outString( "Anticheat: Next daily report reset: %u", resetTime);
}
if (sWorld->GetUptime() > lastIterationPlayer)
{
lastIterationPlayer = sWorld->GetUptime() + 30;//TODO: change 30 secs static to a configurable option
sLog->outString( "Saving reports for %u players.", sWorld->GetPlayerCount());
for (SessionMap::const_iterator itr = sWorld->GetAllSessions().begin(); itr != sWorld->GetAllSessions().end(); ++itr)
if (Player* plr = itr->second->GetPlayer())
sAnticheatMgr->SavePlayerData(plr);
}
}
void OnBeforeConfigLoad(bool reload) override
{
/* from skeleton module */
if (!reload) {
std::string conf_path = _CONF_DIR;
std::string cfg_file = conf_path + "/Anticheat.conf";
if (WIN32)
cfg_file = "Anticheat.conf";
std::string cfg_def_file = cfg_file + ".dist";
sConfigMgr->LoadMore(cfg_def_file.c_str());
sConfigMgr->LoadMore(cfg_file.c_str());
}
/* end from skeleton module */
}
void OnAfterConfigLoad(bool reload) override
{
sLog->outString("AnticheatModule Loaded.");
}
void UpdateReportResetTime()
{
resetTime = sWorld->GetNextTimeWithDayAndHour(-1, 6);
}
};
class AnticheatMovementHandlerScript : public MovementHandlerScript
{
public:
AnticheatMovementHandlerScript()
: MovementHandlerScript("AnticheatMovementHandlerScript")
{
}
void AnticheatMovementHandlerScript::OnPlayerMove(Player* player, MovementInfo mi, uint32 opcode) override
{
sAnticheatMgr->StartHackDetection(player, mi, opcode);
}
};
void startAnticheatScripts()
{
new AnticheatWorldScript();
new AnticheatPlayerScript();
new AnticheatMovementHandlerScript();
}

1
src/AnticheatScripts.h Normal file
View File

@@ -0,0 +1 @@
void startAnticheatScripts();

8
src/PassiveAnticheat.cpp Normal file
View File

@@ -0,0 +1,8 @@
#include "cs_anticheat.h"
#include "AnticheatScripts.h"
void AddPassiveAnticheatScripts()
{
startAnticheatScripts();
AddSC_anticheat_commandscript();
}

View File

@@ -0,0 +1,15 @@
if( WIN32 )
if ( MSVC )
add_custom_command(TARGET worldserver
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/modules/AnticheatModule/conf/Anticheat.conf.dist" ${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/
)
elseif ( MINGW )
add_custom_command(TARGET worldserver
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/modules/AnticheatModule/conf/Anticheat.conf.dist" ${CMAKE_BINARY_DIR}/bin/
)
endif()
endif()
install(FILES "${CMAKE_SOURCE_DIR}/modules/AnticheatModule/conf/Anticheat.conf.dist" DESTINATION ${CONF_DIR})

263
src/cs_anticheat.cpp Normal file
View File

@@ -0,0 +1,263 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Language.h"
#include "ScriptMgr.h"
#include "ObjectMgr.h"
#include "Chat.h"
#include "AnticheatMgr.h"
#include "Configuration\Config.h"
class anticheat_commandscript : public CommandScript
{
public:
anticheat_commandscript() : CommandScript("anticheat_commandscript") { }
std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> anticheatCommandTable =
{
{ "global", SEC_GAMEMASTER, true, &HandleAntiCheatGlobalCommand, "" },
{ "player", SEC_GAMEMASTER, true, &HandleAntiCheatPlayerCommand, "" },
{ "delete", SEC_ADMINISTRATOR, true, &HandleAntiCheatDeleteCommand, "" },
{ "handle", SEC_ADMINISTRATOR, true, &HandleAntiCheatHandleCommand, "" },
{ "jail", SEC_GAMEMASTER, true, &HandleAnticheatJailCommand, "" },
{ "warn", SEC_GAMEMASTER, true, &HandleAnticheatWarnCommand, "" },
// { NULL, 0, false, NULL, "", NULL }
};
static std::vector<ChatCommand> commandTable =
{
{ "anticheat", SEC_GAMEMASTER, true, NULL, "", anticheatCommandTable},
// { NULL, 0, false, NULL, "", NULL }
};
return commandTable;
}
static bool HandleAnticheatWarnCommand(ChatHandler* handler, const char* args)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
return false;
Player* pTarget = NULL;
std::string strCommand;
char* command = strtok((char*)args, " ");
if (command)
{
strCommand = command;
normalizePlayerName(strCommand);
pTarget = ObjectAccessor::FindPlayerByName(strCommand.c_str()); // get player by name
}else
pTarget = handler->getSelectedPlayer();
if (!pTarget)
return false;
WorldPacket data;
// need copy to prevent corruption by strtok call in LineFromMessage original string
char* buf = strdup("The anticheat system has reported several times that you may be cheating. You will be monitored to confirm if this is accurate.");
char* pos = buf;
while (char* line = handler->LineFromMessage(pos))
{
handler->BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line);
pTarget->GetSession()->SendPacket(&data);
}
free(buf);
return true;
}
static bool HandleAnticheatJailCommand(ChatHandler* handler, const char* args)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
return false;
Player* pTarget = NULL;
std::string strCommand;
char* command = strtok((char*)args, " ");
if (command)
{
strCommand = command;
normalizePlayerName(strCommand);
pTarget = ObjectAccessor::FindPlayerByName(strCommand.c_str()); // get player by name
}else
pTarget = handler->getSelectedPlayer();
if (!pTarget)
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (pTarget == handler->GetSession()->GetPlayer())
return false;
// teleport both to jail.
pTarget->TeleportTo(1,16226.5f,16403.6f,-64.5f,3.2f);
handler->GetSession()->GetPlayer()->TeleportTo(1,16226.5f,16403.6f,-64.5f,3.2f);
// the player should be already there, but no :(
// pTarget->GetPosition(&loc);
WorldLocation loc;
loc = WorldLocation(1, 16226.5f, 16403.6f, -64.5f, 3.2f);
pTarget->SetHomebind(loc, 876);
pTarget->SetHomebind(loc,876);
return true;
}
static bool HandleAntiCheatDeleteCommand(ChatHandler* handler, const char* args)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
return false;
std::string strCommand;
char* command = strtok((char*)args, " "); // get entered name
if (!command)
return true;
strCommand = command;
if (strCommand.compare("deleteall") == 0)
sAnticheatMgr->AnticheatDeleteCommand(0);
else
{
normalizePlayerName(strCommand);
Player* player = ObjectAccessor::FindPlayerByName(strCommand.c_str()); // get player by name
if (!player)
handler->PSendSysMessage("Player doesn't exist");
else
sAnticheatMgr->AnticheatDeleteCommand(player->GetGUIDLow());
}
return true;
}
static bool HandleAntiCheatPlayerCommand(ChatHandler* handler, const char* args)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
return false;
std::string strCommand;
char* command = strtok((char*)args, " ");
uint32 guid = 0;
Player* player = NULL;
if (command)
{
strCommand = command;
normalizePlayerName(strCommand);
player = ObjectAccessor::FindPlayerByName(strCommand.c_str()); // get player by name
if (player)
guid = player->GetGUIDLow();
}else
{
player = handler->getSelectedPlayer();
if (player)
guid = player->GetGUIDLow();
}
if (!guid)
{
handler->PSendSysMessage("There is no player.");
return true;
}
float average = sAnticheatMgr->GetAverage(guid);
uint32 total_reports = sAnticheatMgr->GetTotalReports(guid);
uint32 speed_reports = sAnticheatMgr->GetTypeReports(guid,0);
uint32 fly_reports = sAnticheatMgr->GetTypeReports(guid,1);
uint32 jump_reports = sAnticheatMgr->GetTypeReports(guid,3);
uint32 waterwalk_reports = sAnticheatMgr->GetTypeReports(guid,2);
uint32 teleportplane_reports = sAnticheatMgr->GetTypeReports(guid,4);
uint32 climb_reports = sAnticheatMgr->GetTypeReports(guid,5);
handler->PSendSysMessage("Information about player %s",player->GetName().c_str());
handler->PSendSysMessage("Average: %f || Total Reports: %u ",average,total_reports);
handler->PSendSysMessage("Speed Reports: %u || Fly Reports: %u || Jump Reports: %u ",speed_reports,fly_reports,jump_reports);
handler->PSendSysMessage("Walk On Water Reports: %u || Teleport To Plane Reports: %u",waterwalk_reports,teleportplane_reports);
handler->PSendSysMessage("Climb Reports: %u", climb_reports);
return true;
}
static bool HandleAntiCheatHandleCommand(ChatHandler* handler, const char* args)
{
/* std::string strCommand;
char* command = strtok((char*)args, " ");
if (!command)
return true;
if (!handler->GetSession()->GetPlayer())
return true;
strCommand = command;
if (strCommand.compare("on") == 0)
{
sWorld->setBoolConfig(CONFIG_ANTICHEAT_ENABLE,true);
handler->SendSysMessage("The Anticheat System is now: Enabled!");
}
else if (strCommand.compare("off") == 0)
{
sWorld->setBoolConfig(CONFIG_ANTICHEAT_ENABLE,false);
handler->SendSysMessage("The Anticheat System is now: Disabled!");
}*/
handler->PSendSysMessage("Please change value in config file and reload to disable/enable anticheat system.");
return true;
}
static bool HandleAntiCheatGlobalCommand(ChatHandler* handler, const char* /* args */)
{
if (!sConfigMgr->GetBoolDefault("Anticheat.Enabled", 0))
{
handler->PSendSysMessage("The Anticheat System is disabled.");
return true;
}
sAnticheatMgr->AnticheatGlobalCommand(handler);
return true;
}
};
void AddSC_anticheat_commandscript()
{
new anticheat_commandscript();
}

1
src/cs_anticheat.h Normal file
View File

@@ -0,0 +1 @@
void AddSC_anticheat_commandscript();

1
src/loader.h Normal file
View File

@@ -0,0 +1 @@
void AddPassiveAnticheatScripts();