diff --git a/sql/updates/characters/2023_11_12_00_characters.sql b/sql/updates/characters/2023_11_12_00_characters.sql new file mode 100644 index 00000000000..2eb4278f14e --- /dev/null +++ b/sql/updates/characters/2023_11_12_00_characters.sql @@ -0,0 +1,38 @@ +DROP TABLE `daily_players_reports`; +CREATE TABLE IF NOT EXISTS `daily_players_reports` ( + `guid` int unsigned NOT NULL DEFAULT 0, + `creation_time` int unsigned NOT NULL DEFAULT 0, + `average` float NOT NULL DEFAULT 0, + `total_reports` bigint unsigned NOT NULL DEFAULT 0, + `speed_reports` bigint unsigned NOT NULL DEFAULT 0, + `fly_reports` bigint unsigned NOT NULL DEFAULT 0, + `jump_reports` bigint unsigned NOT NULL DEFAULT 0, + `waterwalk_reports` bigint unsigned NOT NULL DEFAULT 0, + `teleportplane_reports` bigint unsigned NOT NULL DEFAULT 0, + `climb_reports` bigint unsigned NOT NULL DEFAULT 0, + `time_maniputation_reports` bigint unsigned NOT NULL DEFAULT 0, + `gravity_reports` bigint unsigned NOT NULL DEFAULT 0, + `teleport_reports` bigint unsigned NOT NULL DEFAULT 0, + `bg_start_reports` bigint unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +DROP TABLE `players_reports_status`; +CREATE TABLE IF NOT EXISTS `players_reports_status` ( + `guid` int unsigned NOT NULL DEFAULT 0, + `creation_time` int unsigned NOT NULL DEFAULT 0, + `average` float NOT NULL DEFAULT 0, + `total_reports` bigint unsigned NOT NULL DEFAULT 0, + `speed_reports` bigint unsigned NOT NULL DEFAULT 0, + `fly_reports` bigint unsigned NOT NULL DEFAULT 0, + `jump_reports` bigint unsigned NOT NULL DEFAULT 0, + `waterwalk_reports` bigint unsigned NOT NULL DEFAULT 0, + `teleportplane_reports` bigint unsigned NOT NULL DEFAULT 0, + `climb_reports` bigint unsigned NOT NULL DEFAULT 0, + `time_maniputation_reports` bigint unsigned NOT NULL DEFAULT 0, + `gravity_reports` bigint unsigned NOT NULL DEFAULT 0, + `teleport_reports` bigint unsigned NOT NULL DEFAULT 0, + `bg_start_reports` bigint unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + diff --git a/sql/updates/world/2023_11_22_world.sql b/sql/updates/world/2023_11_22_world.sql new file mode 100644 index 00000000000..b97a874fad0 --- /dev/null +++ b/sql/updates/world/2023_11_22_world.sql @@ -0,0 +1,3 @@ +DELETE FROM `skyfire_string` WHERE `entry`=11002; +INSERT INTO `skyfire_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`, `content_loc9`, `content_loc10`, `content_loc11`)VALUES +(11002, '|cFFFFBF00[%s]:|cFFFFFFFF|Hplayer:%s|h[%s]|h|cFF00FFFF potential |r|cFFFFFF00%s|r%s x %u|r', null, null, null, null, null, null, null, null, null, null, null); diff --git a/src/server/game/Anticheat/AnticheatMgr.cpp b/src/server/game/Anticheat/AnticheatMgr.cpp new file mode 100644 index 00000000000..cc8dbd3c9c4 --- /dev/null +++ b/src/server/game/Anticheat/AnticheatMgr.cpp @@ -0,0 +1,1119 @@ +/* +* This file is part of Project SkyFire https://www.projectskyfire.org. +* See LICENSE.md file for Copyright information +*/ + +#include "AnticheatMgr.h" +#include "Battleground.h" +#include "Chat.h" +#include "Language.h" +#include "MapManager.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "SpellAuras.h" +#include "World.h" +#include "WorldSession.h" + +enum Spells +{ + FREEZE = 9454, + LFG_SPELL_DUNGEON_DESERTER = 71041, + BG_SPELL_DESERTER = 26013, + SILENCED = 23207, + RESURRECTION_SICKNESS = 15007, + SLOWDOWN = 61458 +}; + +AnticheatData::AnticheatData() +{ + lastOpcode = 0; + totalReports = 0; + for (uint8 i = 0; i < MAX_REPORT_TYPES; i++) + { + typeReports[i] = 0; + tempReports[i] = 0; + tempReportsTimer[i] = 0; + reportAnnounceCount[i] = 0; + reportAnnounceCooldown[i] = 0; + } + average = 0; + creationTime = 0; + hasDailyReport = false; +} + +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::SetDailyReportState(bool b) +{ + hasDailyReport = b; +} + +bool AnticheatData::GetDailyReportState() +{ + return hasDailyReport; +} + +void AnticheatData::Reset() +{ + totalReports = 0; + average = 0; + creationTime = 0; + for (uint8 i = 0; i < MAX_REPORT_TYPES; i++) + { + tempReports[i] = 0; + tempReportsTimer[i] = 0; + typeReports[i] = 0; + } +} + +void AnticheatData::SetPosition(float x, float y, float z, float o) +{ + lastMovementInfo.pos.m_positionX = x; + lastMovementInfo.pos.m_positionY = y; + lastMovementInfo.pos.m_positionZ = z; + lastMovementInfo.pos.m_orientation = o; +} + +class AnticheatScript : public PlayerScript +{ +public: + AnticheatScript() : PlayerScript("AnticheatScripts") { } + + void OnLogout(Player* player) override + { + sAnticheatMgr->HandlePlayerLogout(player); + } + + void OnLogin(Player* player, bool /*loginFirst*/ ) override + { + sAnticheatMgr->HandlePlayerLogin(player); + } +}; + +void AddSC_Anticheat() +{ + new AnticheatScript(); +} + +AnticheatMgr::AnticheatMgr() +{ +} + +AnticheatMgr::~AnticheatMgr() +{ +} + +void AnticheatMgr::StartHackDetection(Player* player, MovementInfo& movementInfo, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + return; + + if (player->IsGameMaster()) + return; + + AnticheatData* data = GetDataFor(player); + if (!data) + return; + + if (player->IsInFlight() || player->GetTransport() || player->GetVehicle()) + { + data->lastMovementInfo = movementInfo; + data->lastOpcode = opcode; + return; + } + + SpeedHackDetection(player, movementInfo, *data); + FlyHackDetection(player, movementInfo, *data); + if (!player->HasAtLoginFlag(AT_LOGIN_FIRST)) + { + TeleportHackDetection(player, movementInfo, *data); + } + if (movementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING)) + { + WalkOnWaterHackDetection(player, movementInfo, *data); + } + JumpHackDetection(player, movementInfo, *data, opcode); + TeleportPlaneHackDetection(player, movementInfo, *data, opcode); + ClimbHackDetection(player, movementInfo, *data, opcode); + GravityHackDetection(player, movementInfo, *data, opcode); + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + BGStartExploit(player, movementInfo, *data, opcode); + } + } + data->lastMovementInfo = movementInfo; + data->lastOpcode = opcode; +} + +void AnticheatMgr::MapSpeedTeleExemption(Player* player, MovementInfo& movementInfo) +{ + // The anticheat is disabled on transports, so we need to be sure that the player is indeed on a transport. + GameObject* transportGobj = player->GetMap()->GetGameObject(movementInfo.transport.guid); + float maxDist2d = 70.0f; // Transports usually dont go far away. + if (player->GetMapId() == 369) // Deeprun tram + { + maxDist2d = 3000.0f; + } + if (transportGobj && (transportGobj->IsTransport() || transportGobj->IsWithinDist(player, maxDist2d, false))) + { + return; + } +} + +void AnticheatMgr::BGLogger(Player* player, AnticheatData& data) +{ + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: BG START - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, BG_START_HACK_REPORT); +} + +void AnticheatMgr::SpeedHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_SPEEDHACK_ENABLE)) + return; + + if (player) + { + MapSpeedTeleExemption(player, movementInfo); + } + + if (player->HasAura(1850) /*Dash*/ || player->HasAuraType(SPELL_AURA_FEATHER_FALL) || player->HasAuraType(SPELL_AURA_SAFE_FALL)) + { + return; + } + + if (player->IsInWater() && player->HasAura(57856) /*Glyph of Aquatic Form*/) + { + return; + } + + float distance2D = movementInfo.pos.GetExactDist2d(&data.lastMovementInfo.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. + float speedRate = player->GetSpeed(UnitMoveType(moveType)) + movementInfo.jump.xyspeed; + + // how long the player took to move to here. + uint32 timeDiff = getMSTimeDiff(data.lastMovementInfo.time, movementInfo.time); + + if (int32(timeDiff) < 0) + { + uint32 latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Time Manipulation- Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress(), goXYZ.c_str()); + BuildReport(player, data, TIME_MANIPULATION_REPORT); + timeDiff = 1; + } + + if (!timeDiff) + timeDiff = 1; + + // this is the distance doable by the player in 1 sec, using the time done to move to this point. + float clientSpeedRate = distance2D * 1000 / timeDiff; + + // we create a diff speed in uint32 for further precision checking to avoid legit fall and slide + uint32 diffspeed = clientSpeedRate - speedRate; + + // 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 + uint32 _assignedspeeddiff = sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_SPEED_LIMIT_TOLERANCE); + + // 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 + if (!movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING)) + { + if (diffspeed >= _assignedspeeddiff) + { + // we did the (uint32) cast to accept a margin of tolerance + if (clientSpeedRate >= _assignedspeeddiff + speedRate) + { + uint32 _combined = _assignedspeeddiff + speedRate; + BuildReport(player, data, SPEED_HACK_REPORT); + uint32 latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Speed - Hack detected player %s - (GUID %u) - (Speed Movement at %u above allowed Server Set rate %u.)- Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), diffspeed, _combined, latency, player->GetSession()->GetRemoteAddress(), goXYZ.c_str()); + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_SPEEDHACK)) + { + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s SPEED HACK REVERTED", player->GetName().c_str()); + if (Aura* slowcheater = player->AddAura(SLOWDOWN, player))// SLOWDOWN + { + slowcheater->SetDuration(1000); + } + } + } + } + } +} + +void AnticheatMgr::FlyHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_FLYHACK_ENABLE)) + return; + + if (!data.lastMovementInfo.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; + + /*Thanks to @LilleCarl for info to check extra flag*/ + bool stricterChecks = true; + stricterChecks = !(movementInfo.HasMovementFlag(MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING) && !player->IsInWater()); + + if (!movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY) && !movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING) && stricterChecks) + { + return; + } + + uint32 latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Fly - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, FLY_HACK_REPORT); + + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_FLYHACK)) + { + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s FLY HACK REVERTED", player->GetName().c_str()); + // Drop them with a op code set if they use a exploit or hack app + WorldPacket cheater(SMSG_MOVE_UNSET_CAN_FLY, 8 + 4); + cheater.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY); + + player->WriteMovementInfo(cheater); + player->SendMessageToSet(&cheater, true); + } +} + +void AnticheatMgr::TeleportHackDetection(Player* player, MovementInfo movementInfo, AnticheatData& data) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_TELEPORTHACK_ENABLE)) + return; + + float lastX = data.lastMovementInfo.pos.GetPositionX(); + float newX = movementInfo.pos.GetPositionX(); + + float lastY = data.lastMovementInfo.pos.GetPositionY(); + float newY = movementInfo.pos.GetPositionY(); + + float lastZ = data.lastMovementInfo.pos.GetPositionZ(); + float newZ = movementInfo.pos.GetPositionZ(); + + float xDiff = fabs(lastX - newX); + float yDiff = fabs(lastY - newY); + + if (player->IsFalling() || (player->IsFalling() && player->IsMounted())) + return; + + if (player) + { + MapSpeedTeleExemption(player, movementInfo); + } + + /* Please work */ + if ((xDiff >= 50.0f || yDiff >= 50.0f) && !player->CanTeleport() && !player->IsBeingTeleported())// teleport helpers in play + { + uint32 latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Teleport - Hack detected player %s - (GUID %u) - X-Diff: %f - Y-Diff: %f - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), xDiff, yDiff, latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, TELEPORT_HACK_REPORT); + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_TELEPORT)) + { + player->TeleportTo(player->GetMapId(), lastX, lastY, lastZ, player->GetOrientation()); + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s TELEPORT HACK REVERTED PLAYER BACK", player->GetName().c_str()); + } + } + else if (player->CanTeleport())// if we hit the teleport helpers in the source then we return it to false + player->SetCanTeleport(false); +} + +void AnticheatMgr::WalkOnWaterHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_WATERWALKHACK_ENABLE)) + return; + + if (!data.lastMovementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING)) + return; + + if (player->IsFlying()) + return; + + // ghost can water walk + if (player->HasAuraType(SPELL_AURA_GHOST)) + return; + + // if we are a ghost we can walk on water + if (!player->IsAlive()) + return; + + // if the player previous movement and current movement is water walking then we do a follow up check + if (data.lastMovementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && movementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING)) + { // if player has the following auras then we return + if (player->HasAuraType(SPELL_AURA_WATER_WALK) || player->HasAuraType(SPELL_AURA_FEATHER_FALL) || + player->HasAuraType(SPELL_AURA_SAFE_FALL)) + { + return; + } + } + else if (!data.lastMovementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && !movementInfo.HasMovementFlag(MOVEMENTFLAG_WATERWALKING)) + { + //Boomer Review Time: + //Return stops code execution of the entire function + return; + } + + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Walk on Water - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, WALK_WATER_HACK_REPORT); + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_WATERHACK)) + { + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s WATER WALKING HACK REVERTED", player->GetName().c_str()); + player->GetMotionMaster()->MoveFallPlayer(); + } +} + +void AnticheatMgr::JumpHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_JUMPHACK_ENABLE)) + return; + + const float ground_Z = movementInfo.pos.GetPositionZ() - player->GetMap()->GetHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()); + + const bool no_fly_auras = !(player->HasAuraType(SPELL_AURA_FLY) || player->HasAuraType(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED) + || player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || player->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) + || player->HasAuraType(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS)); + const bool no_fly_flags = ((movementInfo.flags & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING)) == 0); + const bool no_swim_in_water = !player->IsInWater(); + const bool no_swim_above_water = movementInfo.pos.GetPositionZ() - 7.0f >= player->GetMap()->GetWaterLevel(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()); + const bool no_swim_water = no_swim_in_water && no_swim_above_water; + + if (data.lastOpcode == MSG_MOVE_JUMP && opcode == MSG_MOVE_JUMP) + { + BuildReport(player, data, JUMP_HACK_REPORT); + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Jump-Hack detected player %s - Latency: %u ms - IP: %u - Cheat Flagged at: %s", player->GetName().c_str(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_JUMPHACK)) + { + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s JUMP HACK REVERTED", player->GetName().c_str()); + player->GetMotionMaster()->MoveFallPlayer(); + } + } + else if (no_fly_auras && no_fly_flags && no_swim_water) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ADV_JUMPHACK_ENABLE)) + return; + + if (data.lastOpcode == MSG_MOVE_JUMP && !player->IsFalling()) + return; + + float distance2D = movementInfo.pos.GetExactDist2d(&data.lastMovementInfo.pos); + + // This is necessary since MovementHandler fires if you rotate the camera in place + if (!distance2D) + return; + + // The anticheat is disabled on transports, so we need to be sure that the player is indeed on a transport. + GameObject* transportGobj = player->GetMap()->GetGameObject(movementInfo.transport.guid); + if (transportGobj && transportGobj->IsTransport()) + { + return; + } + + if (!player->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && movementInfo.jump.zspeed < -10.0f) + return; + + if (player->HasAuraType(SPELL_AURA_WATER_WALK) || player->HasAuraType(SPELL_AURA_FEATHER_FALL) || + player->HasAuraType(SPELL_AURA_SAFE_FALL)) + { + return; + } + + // We exempt select areas found in 335 to prevent false hack hits + if (player->GetAreaId()) + { + switch (player->GetAreaId()) + { + case 4273: //Celestial Planetarium Observer Battle has a narrow path that false flags + case 6724: //Backbreaker Bay + case 6725: //Greymist Firth + return; + default: + break; + } + } + + if (ground_Z > 5.0f && movementInfo.pos.GetPositionZ() >= player->GetPositionZ()) + { + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Advanced Jump-Hack detected player %s - Latency: %u ms - IP: %u - Cheat Flagged at: %s", player->GetName().c_str(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, JUMP_HACK_REPORT); + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_ADVJUMPHACK)) + { + SF_LOG_INFO("anticheat", "ANTICHEAT COUNTER MEASURE:: %s ADVANCE JUMP HACK REVERTED", player->GetName().c_str()); + player->GetMotionMaster()->MoveFallPlayer(); + } + } + } +} + +void AnticheatMgr::TeleportPlaneHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_TELEPANEHACK_ENABLE)) + return; + + + if (player->HasAuraType(SPELL_AURA_WATER_WALK) || player->HasAuraType(SPELL_AURA_WATER_BREATHING) || player->HasAuraType(SPELL_AURA_GHOST)) + return; + + ZLiquidStatus result = LIQUID_MAP_ABOVE_WATER; + float distance2D = movementInfo.pos.GetExactDist2d(&data.lastMovementInfo.pos); + + // We don't need to check for a water walking hack if the player hasn't moved + // This is necessary since MovementHandler fires if you rotate the camera in place + if (!distance2D) + return; + + if (data.lastOpcode == MSG_MOVE_JUMP) + return; + + if (opcode == MSG_MOVE_FALL_LAND) + return; + + if (player && result == LIQUID_MAP_ABOVE_WATER) + return; + + if (movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_SWIMMING)) + return; + + // If he is flying we dont need to check + if (movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING)) + return; + + // We exempt select areas found in 335 to prevent false hack hits + if (player->GetAreaId()) + { + switch (player->GetAreaId()) + { + case 4273: //Celestial Planetarium Observer Battle has a narrow path that false flags + return; + default: + break; + } + } + + float pos_z = player->GetPositionZ(); + float groundZ = player->GetMap()->GetHeight(player->GetPositionX(), player->GetPositionY(), MAX_HEIGHT); + float floorZ = player->GetMap()->GetHeight(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + + // we are not really walking there + if (groundZ == floorZ && (fabs(groundZ - pos_z) > 2.0f || fabs(groundZ - pos_z) < -1.0f)) + { + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Teleport To Plane - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, TELEPORT_PLANE_HACK_REPORT); + } +} + +// basic detection +void AnticheatMgr::ClimbHackDetection(Player *player, MovementInfo &movementInfo, AnticheatData& data, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CLIMBHACK_ENABLE)) + 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; + + // If the player jumped, we dont want to check for climb hack + // This can lead to false positives for climbing game objects legit + if (opcode == MSG_MOVE_JUMP) + return; + + if (player->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) + return; + + Position playerPos; + player->GetPosition(&playerPos); + + float deltaZ = fabs(playerPos.GetPositionZ() - movementInfo.pos.GetPositionZ()); + float tanangle = movementInfo.pos.GetExactDist2d(&playerPos) / deltaZ; + + if (!player->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING | MOVEMENTFLAG_SWIMMING)) + { + if (movementInfo.pos.GetPositionZ() > playerPos.GetPositionZ() && + deltaZ > 1.87f && tanangle < 0.57735026919f) // 30 degrees + { + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Climb - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, CLIMB_HACK_REPORT); + } + } +} + +void AnticheatMgr::GravityHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_GRAVITY_ENABLE)) + return; + + if (player->HasAuraType(SPELL_AURA_FEATHER_FALL)) + { + return; + } + + if (data.lastOpcode == MSG_MOVE_JUMP) + { + if (!player->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && movementInfo.jump.zspeed < -10.0f) + { + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: Gravity - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, GRAVITY_HACK_REPORT); + } + } +} + +void AnticheatMgr::BGStartExploit(Player* player, MovementInfo movementInfo, AnticheatData& data, uint32 opcode) +{ + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_BG_START_HACK_ENABLE)) + return; + + switch (player->GetMapId()) + { + case 30: // Alterac Valley + { + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 770.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() > 940.31f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < -525.0f)) + { + uint32 latency = 0; + latency = player->GetSession()->GetLatency(); + std::string goXYZ = ".go xyz " + std::to_string(player->GetPositionX()) + " " + std::to_string(player->GetPositionY()) + " " + std::to_string(player->GetPositionZ() + 1.0f) + " " + std::to_string(player->GetMap()->GetId()) + " " + std::to_string(player->GetOrientation()); + SF_LOG_INFO("anticheat", "Anticheat Manager:: BG START - Hack detected player %s - (GUID %u) - Latency: %u ms - IP: %s - Cheat Flagged at: %s", player->GetName().c_str(), player->GetGUID(), latency, player->GetSession()->GetRemoteAddress().c_str(), goXYZ.c_str()); + BuildReport(player, data, BG_START_HACK_REPORT); + } + if ((player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > -535.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionX() > -1283.33f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() < -716.0f)) + { + BGLogger(player, data); + } + } + } + break; + } + case 489: // Warsong Gulch + { + // Only way to get this high is with engineering items malfunction. + if (!(movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING_FAR) || data.lastOpcode == MSG_MOVE_JUMP) && movementInfo.pos.GetPositionZ() > 380.0f) + { + BGLogger(player, data); + } + + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 1490.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() > 1500.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < 1450.0f)) + { + BGLogger(player, data); + } + if ((player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionX() > 957.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() < 1416.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > 1466.0f)) + { + BGLogger(player, data); + } + } + } + break; + } + case 529: // Arathi Basin + { + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 1270.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < 1258.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() > 1361.0f)) + { + BGLogger(player, data); + } + if ((player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionX() > 730.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > 724.8f)) + { + BGLogger(player, data); + } + } + } + break; + } + case 566: // Eye of the Storm + { + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 2512.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() > 1610.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < 1584.0f)) + { + BGLogger(player, data); + } + if ((player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionX() > 1816.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > 1554.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() < 1526.0f)) + { + BGLogger(player, data); + } + } + } + break; + } + case 628: // Island Of Conquest + { + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() > 412.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < -911.0f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() > -760.0f)) + { + BGLogger(player, data); + } + if ((player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionX() < 1147.8f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() < -855.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > -676.0f)) + { + BGLogger(player, data); + } + } + } + break; + } + case 726: // Twin Peaks + { + if (Battleground* bg = player->GetBattleground()) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + // Outside of starting area before BG has started. + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 2075.00f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() < 154.30f) || + (player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionY() > 222.80f)) + { + BGLogger(player, data); + } + if ((player->GetTeamId() == TEAM_ALLIANCE && movementInfo.pos.GetPositionX() < 314.00f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() < 314.0f) || + (player->GetTeamId() == TEAM_HORDE && movementInfo.pos.GetPositionY() > 380.0f)) + { + BGLogger(player, data); + } + } + } + break; + } + return; + default: + break; + } +} + +void AnticheatMgr::SetAllowedMovement(Player* player, bool) +{ + player->SetCanTeleport(true); +} + +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. + _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) + _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. + _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,time_maniputation_reports, gravity_reports, teleport_reports, bg_start_reports, creation_time) VALUES (%u,%f,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u);",player->GetGUIDLow(),_players[player->GetGUIDLow()].GetAverage(),_players[player->GetGUIDLow()].GetTotalReports(), _players[player->GetGUIDLow()].GetTypeReports(SPEED_HACK_REPORT),_players[player->GetGUIDLow()].GetTypeReports(FLY_HACK_REPORT),_players[player->GetGUIDLow()].GetTypeReports(JUMP_HACK_REPORT),_players[player->GetGUIDLow()].GetTypeReports(WALK_WATER_HACK_REPORT),_players[player->GetGUIDLow()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT),_players[player->GetGUIDLow()].GetTypeReports(CLIMB_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TIME_MANIPULATION_REPORT), _players[player->GetGUIDLow()].GetTypeReports(GRAVITY_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TELEPORT_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(BG_START_HACK_REPORT), _players[player->GetGUIDLow()].GetCreationTime()); +} + +void AnticheatMgr::SavePlayerDataDaily(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,time_maniputation_reports, gravity_reports, teleport_reports, bg_start_reports, creation_time) VALUES (%u,%f,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u);", player->GetGUIDLow(), _players[player->GetGUIDLow()].GetAverage(), _players[player->GetGUIDLow()].GetTotalReports(), _players[player->GetGUIDLow()].GetTypeReports(SPEED_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(FLY_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(JUMP_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(WALK_WATER_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(CLIMB_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TIME_MANIPULATION_REPORT), _players[player->GetGUIDLow()].GetTypeReports(GRAVITY_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TELEPORT_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(BG_START_HACK_REPORT), _players[player->GetGUIDLow()].GetCreationTime()); +} + +void AnticheatMgr::OnPlayerMove(Player* player, MovementInfo &mi, uint32 opcode) +{ + if (!AccountMgr::IsAdminAccount(player->GetSession()->GetSecurity()) || sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE_ON_GM)) + sAnticheatMgr->StartHackDetection(player, mi, opcode); +} + +uint32 AnticheatMgr::GetTotalReports(uint32 lowGUID) +{ + AnticheatPlayersDataMap::iterator iter = _players.find(lowGUID); + if (iter == _players.end()) + return 0; + + return iter->second.totalReports; +} + +float AnticheatMgr::GetAverage(uint32 lowGUID) +{ + AnticheatPlayersDataMap::iterator iter = _players.find(lowGUID); + if (iter == _players.end()) + return 0.0f; + + return iter->second.average; +} + +uint32 AnticheatMgr::GetTypeReports(uint32 lowGUID, uint8 type) +{ + AnticheatPlayersDataMap::iterator iter = _players.find(lowGUID); + if (iter == _players.end()) + return 0; + + return iter->second.typeReports[type]; +} + +bool AnticheatMgr::MustCheckTempReports(uint8 type) +{ + if (type == JUMP_HACK_REPORT) + return false; + + if (type == GRAVITY_HACK_REPORT) + return false; + + if (type == TELEPORT_HACK_REPORT) + return false; + + if (type == BG_START_HACK_REPORT) + return false; + + return true; +} + +void AnticheatMgr::BuildReport(Player* player, AnticheatData& data, uint8 reportType) +{ + uint32 key = player->GetGUIDLow(); + + if (MustCheckTempReports(reportType)) + { + uint32 actualTime = getMSTime(); + + if (!data.tempReportsTimer[reportType]) + data.tempReportsTimer[reportType] = actualTime; + + if (getMSTimeDiff(data.tempReportsTimer[reportType], actualTime) < 3000) + { + data.tempReports[reportType] += 1; + + if (data.tempReports[reportType] < 3) + return; + } + else + { + data.tempReportsTimer[reportType] = actualTime; + data.tempReports[reportType] = 1; + return; + } + } + + // generating creationTime for average calculation + if (!data.totalReports) + data.creationTime = getMSTime(); + + // increasing total_reports + data.totalReports += 1; + // increasing specific cheat report + data.typeReports[reportType] += 1; + + // diff time for average calculation + uint32 diffTime = getMSTimeDiff(data.creationTime, getMSTime()) / IN_MILLISECONDS; + + if (diffTime > 0) + { + // Average == Reports per second + float average = float(data.totalReports) / float(diffTime); + data.average = average; + } + + if (sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT) < data.GetTotalReports()) + { + if (!data.GetDailyReportState()) + { + CharacterDatabase.PExecute("REPLACE INTO daily_players_reports (guid,average,total_reports,speed_reports,fly_reports,jump_reports,waterwalk_reports,teleportplane_reports,climb_reports,time_maniputation_reports, gravity_reports, teleport_reports, bg_start_reports, creation_time) VALUES (%u,%f,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u);", player->GetGUIDLow(), _players[player->GetGUIDLow()].GetAverage(), _players[player->GetGUIDLow()].GetTotalReports(), _players[player->GetGUIDLow()].GetTypeReports(SPEED_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(FLY_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(JUMP_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(WALK_WATER_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TELEPORT_PLANE_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(CLIMB_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TIME_MANIPULATION_REPORT), _players[player->GetGUIDLow()].GetTypeReports(GRAVITY_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(TELEPORT_HACK_REPORT), _players[player->GetGUIDLow()].GetTypeReports(BG_START_HACK_REPORT), _players[player->GetGUIDLow()].GetCreationTime()); + data.SetDailyReportState(true); + } + } + + // Send warning to ingame GMs + if (data.totalReports > sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION)) + { + uint32& count = data.reportAnnounceCount[reportType]; + ++count; + time_t now = time(NULL); + if (data.reportAnnounceCooldown[reportType] < now) + data.reportAnnounceCooldown[reportType] = now + 3; + else + return; + + const char* reportTypeName; + switch (reportType) + { + case SPEED_HACK_REPORT: reportTypeName = "SpeedHack"; break; + case FLY_HACK_REPORT: reportTypeName = "FlyHack"; break; + case WALK_WATER_HACK_REPORT: reportTypeName = "WaterWalkHack"; break; + case JUMP_HACK_REPORT: reportTypeName = "JumpHack"; break; + case TELEPORT_PLANE_HACK_REPORT: reportTypeName = "TeleportPlaneHack"; break; + case CLIMB_HACK_REPORT: reportTypeName = "ClimbHack"; break; + case TIME_MANIPULATION_REPORT: reportTypeName = "TimeManipulation"; break; + case GRAVITY_HACK_REPORT: reportTypeName = "GravityHack"; break; + case TELEPORT_HACK_REPORT: reportTypeName = "TeleportHack"; break; + case BG_START_HACK_REPORT: reportTypeName = "BGStartHack"; break; + default: reportTypeName = "?"; break; + } + SF_LOG_DEBUG("network", "Anticheat Alert: %s detected as possible cheater. HackType: %u.", player->GetName().c_str(), reportType); + sWorld->SendGMText(LANG_CHEATER_CHATLOG, "Anticheat Alert", player->GetName().c_str(), player->GetName().c_str(), reportTypeName, count < 10 ? "|cFF00FF00" : count < 20 ? "|cFFFF8000" : "|cFFFF0000", count); + count = 0; + } + // Automatic Moderation, not recommended but not hated + // Auto Kick + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOKICK_ENABLE)) + { + if (data.totalReports > sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_KICKS)) + { + SF_LOG_INFO("anticheat", "AnticheatMgr:: Reports reached assigned threshhold and counteracted by kicking player %s (GUID %u)", player->GetName().c_str(), player->GetGUID()); + // kick offender when reports are reached for automatic moderation + player->GetSession()->KickPlayer(); + } + } + // Auto Ban + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOBAN_ENABLE)) + { + if (data.totalReports > sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_BANS)) + { + SF_LOG_INFO("anticheat", "AnticheatMgr:: Reports reached assigned threshhold and counteracted by banning player %s (GUID %u)", player->GetName().c_str(), player->GetGUID()); + //Auto Ban the Offender Indefinately their whole account. + std::string accountName; + AccountMgr::GetName(player->GetSession()->GetAccountId(), accountName); + sWorld->BanAccount(BAN_ACCOUNT, accountName, "0s", "Anticheat module Auto Banned Account for Reach Cheat Threshhold", "Server"); + } + } + //Auto Jail + if (sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOJAIL_ENABLE)) + { + if (data.totalReports > sWorld->getIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_JAILS)) + { + SF_LOG_INFO("anticheat", "AnticheatMgr:: Reports reached assigned threshhold and counteracted by jailing player %s (GUID %u)", player->GetName().c_str(), player->GetGUID()); + player->CastSpell(player, FREEZE);// freeze him in place to ensure no exploit happens for jail break attempt + if (Aura* dungdesert = player->AddAura(LFG_SPELL_DUNGEON_DESERTER,player))// LFG_SPELL_DUNGEON_DESERTER + { + dungdesert->SetDuration(-1); + } + if (Aura* bgdesert = player->AddAura(BG_SPELL_DESERTER, player))// BG_SPELL_DESERTER + { + bgdesert->SetDuration(-1); + } + if (Aura* silent = player->AddAura(SILENCED, player))// SILENCED + { + silent->SetDuration(-1); + } + // This is where they end up going shaw shank redemption style + WorldLocation loc = WorldLocation(1, 16226.5f, 16403.6f, -64.5f, 3.2f);// GM Jail Location + player->SetHomebind(loc, 876);// GM Jail Homebind location + player->TeleportTo(loc);// we defined loc as the jail location so we tele them there + + } + } +} + +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!. + for (SessionMap::const_iterator itr = sWorld->GetAllSessions().begin(); itr != sWorld->GetAllSessions().end(); ++itr) + if (Player* plr = itr->second->GetPlayer()) + { + sAnticheatMgr->SavePlayerData(plr); + sAnticheatMgr->SavePlayerDataDaily(plr); + } + + 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->SendSysMessage("============================="); + 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 = _players.begin(); it != _players.end(); ++it) + (*it).second.Reset(); + + CharacterDatabase.PExecute("DELETE FROM players_reports_status;"); + } + else + { + AnticheatPlayersDataMap::iterator iter = _players.find(guid); + if (iter == _players.end()) + return; + + iter->second.Reset(); + CharacterDatabase.PExecute("DELETE FROM players_reports_status WHERE guid=%u;", guid); + } +} + +void AnticheatMgr::AnticheatPurgeCommand(ChatHandler* /*handler*/) +{ + CharacterDatabase.Execute("TRUNCATE TABLE daily_players_reports;"); +} + +void AnticheatMgr::ResetDailyReportStates() +{ + for (AnticheatPlayersDataMap::iterator it = _players.begin(); it != _players.end(); ++it) + it->second.hasDailyReport = false; +} + +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]; +} + +AnticheatData* AnticheatMgr::GetDataFor(Player* player) +{ + AnticheatPlayersDataMap::iterator iter = _players.find(player->GetGUIDLow()); + if (iter == _players.end()) + { + return NULL; + } + + return &iter->second; +} diff --git a/src/server/game/Anticheat/AnticheatMgr.h b/src/server/game/Anticheat/AnticheatMgr.h new file mode 100644 index 00000000000..07d3b138e8a --- /dev/null +++ b/src/server/game/Anticheat/AnticheatMgr.h @@ -0,0 +1,117 @@ +/* +* This file is part of Project SkyFire https://www.projectskyfire.org. +* See LICENSE.md file for Copyright information +*/ + +#ifndef SC_ACMGR_H +#define SC_ACMGR_H + +#include "Common.h" +#include "SharedDefines.h" +#include +#include "WorldSession.h" + +class Player; +class ChatHandler; + +enum ReportTypes +{ + SPEED_HACK_REPORT = 0, + FLY_HACK_REPORT = 1, + WALK_WATER_HACK_REPORT = 2, + JUMP_HACK_REPORT = 3, + TELEPORT_PLANE_HACK_REPORT = 4, + CLIMB_HACK_REPORT = 5, + TIME_MANIPULATION_REPORT = 6, + GRAVITY_HACK_REPORT = 7, + TELEPORT_HACK_REPORT = 8, + BG_START_HACK_REPORT = 9, + MAX_REPORT_TYPES +}; + +struct AnticheatData +{ + AnticheatData(); + + void SetPosition(float x, float y, float z, float o); + void Reset(); + + uint32 lastOpcode; + MovementInfo lastMovementInfo; + uint32 totalReports; + float average; + uint32 creationTime; + bool hasDailyReport; + + 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); + + uint32 typeReports[MAX_REPORT_TYPES]; + uint32 tempReports[MAX_REPORT_TYPES]; + uint32 tempReportsTimer[MAX_REPORT_TYPES]; + + uint32 reportAnnounceCount[MAX_REPORT_TYPES]; + time_t reportAnnounceCooldown[MAX_REPORT_TYPES]; + void SetDailyReportState(bool b); + bool GetDailyReportState(); +}; + +// GUIDLow is the key. +typedef std::unordered_map AnticheatPlayersDataMap; + +class AnticheatMgr +{ + AnticheatMgr(); + ~AnticheatMgr(); + + public: + friend class ACE_Singleton; + + void SetAllowedMovement(Player* player, bool); + void StartHackDetection(Player* player, MovementInfo &movementInfo, uint32 opcode); + void SavePlayerData(Player* player); + void SavePlayerDataDaily(Player* player); + void HandlePlayerLogin(Player* player); + void HandlePlayerLogout(Player* player); + + void OnPlayerMove(Player* player, MovementInfo &mi, uint32 opcode); + uint32 GetTotalReports(uint32 lowGUID); + float GetAverage(uint32 lowGUID); + uint32 GetTypeReports(uint32 lowGUID, uint8 type); + AnticheatData* GetDataFor(Player* player); + + void AnticheatGlobalCommand(ChatHandler* handler); + void AnticheatDeleteCommand(uint32 guid); + void AnticheatPurgeCommand(ChatHandler* handler); + void ResetDailyReportStates(); + + private: + void MapSpeedTeleExemption(Player* player, MovementInfo& movementInfo); + void BGLogger(Player* player, AnticheatData& data); + void SpeedHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data); + void FlyHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data); + void TeleportHackDetection(Player* player, MovementInfo movementInfo, AnticheatData& data); + void WalkOnWaterHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data); + void JumpHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data, uint32 opcode); + void TeleportPlaneHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data, uint32 opcode); + void ClimbHackDetection(Player* player, MovementInfo &movementInfo, AnticheatData& data, uint32 opcode); + void GravityHackDetection(Player* player, MovementInfo& movementInfo, AnticheatData& data, uint32 opcode); + void BGStartExploit(Player* player, MovementInfo movementInfo, AnticheatData& data, uint32 opcode); + void BuildReport(Player* player, AnticheatData& data, uint8 reportType); + + bool MustCheckTempReports(uint8 type); + AnticheatPlayersDataMap _players; +}; + +#define sAnticheatMgr ACE_Singleton::instance() + +#endif diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index cf0ffdb456e..9ed7f7f6587 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -7,6 +7,7 @@ file(GLOB_RECURSE sources_Accounts Accounts/*.cpp Accounts/*.h) file(GLOB_RECURSE sources_Achievements Achievements/*.cpp Achievements/*.h) file(GLOB_RECURSE sources_Addons Addons/*.cpp Addons/*.h) file(GLOB_RECURSE sources_AI AI/*.cpp AI/*.h) +file(GLOB_RECURSE sources_Anticheat Anticheat/*.cpp Anticheat/*.h) file(GLOB_RECURSE sources_AuctionHouse AuctionHouse/*.cpp AuctionHouse/*.h) file(GLOB_RECURSE sources_Battlefield Battlefield/*.cpp Battlefield/*.h) file(GLOB_RECURSE sources_Battlegrounds Battlegrounds/*.cpp Battlegrounds/*.h) @@ -53,6 +54,7 @@ source_group(Accounts FILES ${sources_Accounts}) source_group(Achievements FILES ${sources_Achievements}) source_group(Addons FILES ${sources_Addons}) source_group(AI FILES ${sources_AI}) +source_group(Anticheat FILES ${sources_Anticheat}) source_group(AuctionHouse FILES ${sources_AuctionHouse}) source_group(Battlefield FILES ${sources_Battlefield}) source_group(Battlegrounds FILES ${sources_Battlegrounds}) @@ -106,6 +108,7 @@ set(game_STAT_SRCS ${sources_Achievements} ${sources_Addons} ${sources_AI} + ${sources_Anticheat} ${sources_AuctionHouse} ${sources_Battlefield} ${sources_Battlegrounds} @@ -180,6 +183,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/AI/CoreAI ${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI ${CMAKE_CURRENT_SOURCE_DIR}/AI/SmartScripts + ${CMAKE_CURRENT_SOURCE_DIR}/Anticheat ${CMAKE_CURRENT_SOURCE_DIR}/AuctionHouse ${CMAKE_CURRENT_SOURCE_DIR}/Battlefield ${CMAKE_CURRENT_SOURCE_DIR}/Battlefield/Zones diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a799c234c98..e52f62f2633 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -6,6 +6,7 @@ #include "Player.h" #include "AccountMgr.h" #include "AchievementMgr.h" +#include "AnticheatMgr.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "Battlefield.h" @@ -570,7 +571,7 @@ Player::Player(WorldSession* session): Unit(true) m_bCanDelayTeleport = false; m_bHasDelayedTeleport = false; m_teleport_options = 0; - + m_canTeleport = false; m_trade = NULL; m_cinematic = 0; @@ -2151,6 +2152,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing if (!GetSession()->PlayerLogout()) { + SetCanTeleport(true); Position oldPos; GetPosition(&oldPos); Relocate(x, y, z, orientation); @@ -2266,6 +2268,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if (!GetSession()->PlayerLogout()) { + SetCanTeleport(true); WorldPacket data(SMSG_NEW_WORLD, 4 + 4 + 4 + 4 + 4); data << float(m_teleport_dest.GetPositionX()); data << uint32(mapid); @@ -20154,6 +20157,9 @@ void Player::SaveToDB(bool create /*=false*/) if (m_session->isLogingOut() || !sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_STATS_SAVE_ONLY_ON_LOGOUT)) _SaveStats(trans); + // we save the data here to prevent spamming + sAnticheatMgr->SavePlayerData(this); + CharacterDatabase.CommitTransaction(trans); // save pet (hunter pet level and experience and all type pets health/mana). diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index bb53b1e8644..27e740d0993 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -3003,8 +3003,9 @@ class Player : public Unit, public GridObject // Set map to player and add reference void SetMap(Map* map) override; void ResetMap() override; - + bool CanTeleport() { return m_canTeleport; } bool isAllowedToLoot(const Creature* creature); + void SetCanTeleport(bool value) { m_canTeleport = value; } DeclinedName const* GetDeclinedNames() const { @@ -3526,6 +3527,7 @@ class Player : public Unit, public GridObject uint32 m_DelayedOperations; bool m_bCanDelayTeleport; bool m_bHasDelayedTeleport; + bool m_canTeleport; // Temporary removed pet cache uint32 m_temporaryUnsummonedPetNumber; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0c04d3ec923..bbf1a20ff4b 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15850,6 +15850,10 @@ void Unit::ExitVehicle(Position const* /*exitPosition*/) return; GetVehicleBase()->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, GetGUID()); + if (Player* player = ToPlayer()) + { + player->SetCanTeleport(true); + } //! The following call would not even be executed successfully as the //! SPELL_AURA_CONTROL_VEHICLE unapply handler already calls _ExitVehicle without //! specifying an exitposition. The subsequent call below would return on if (!m_vehicle). @@ -15908,7 +15912,10 @@ void Unit::_ExitVehicle(Position const* exitPosition) init.Launch(); if (player) + { + player->SetCanTeleport(true); player->ResummonPetTemporaryUnSummonedIfAny(); + } if (vehicle->GetBase()->HasUnitTypeMask(UNIT_MASK_MINION) && vehicle->GetBase()->GetTypeId() == TypeID::TYPEID_UNIT) if (((Minion*) vehicle->GetBase())->GetOwner() == this) @@ -16220,6 +16227,7 @@ void Unit::SendTeleportPacket(Position& pos) if (GetTypeId() == TypeID::TYPEID_PLAYER) { + ToPlayer()->SetCanTeleport(true); WorldPacket data2(SMSG_MOVE_TELEPORT, 1 + 8 + 1 + 8 + 1 + 4 + 4 + 4 + 4); WriteMovementInfo(data2); ToPlayer()->SendDirectMessage(&data2); // Send the SMSG_MOVE_TELEPORT packet to self. diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index cd6c32fcefc..5e3ddc23d25 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -19,6 +19,7 @@ #include "ObjectMgr.h" #include "MovementStructures.h" #include "BattlePetMgr.h" +#include "AnticheatMgr.h" #define MOVEMENT_PACKET_TIME_DELAY 0 @@ -364,6 +365,11 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; + if (plrMover) + { + sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); + } + /* process position-change */ movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; @@ -576,7 +582,7 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) return; _player->m_movementInfo = movementInfo; - + _player->SetCanTeleport(true); WorldPacket data(SMSG_MOVE_UPDATE_KNOCK_BACK, 66); _player->WriteMovementInfo(data); _player->SendMessageToSet(&data, false); diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index f2cac49c952..27cc6702179 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -135,6 +135,9 @@ void WorldSession::SendTaxiMenu(Creature* unit) void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode) { + // add anticheat helper here to avoid false hits if relog during flight path travel + GetPlayer()->SetCanTeleport(true); + // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); @@ -309,6 +312,8 @@ void WorldSession::HandleActivateTaxiOpcode(WorldPacket& recvData) recvData.ReadGuidMask(guid, 4, 0, 1, 2, 5, 6, 7, 3); recvData.ReadGuidBytes(guid, 1, 0, 6, 5, 2, 4, 3, 7); + GetPlayer()->SetCanTeleport(true); + SF_LOG_DEBUG("network", "WORLD: Received CMSG_ACTIVATE_TAXI from %d to %d", nodes[0], nodes[1]); Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); if (!npc) diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index e754bfaeb36..4af0b7bb114 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1230,6 +1230,7 @@ enum SkyFireStrings // Use for custom patches 11000-11999 LANG_AUTO_BROADCAST = 11000, LANG_INVALID_REALMID = 11001, + LANG_CHEATER_CHATLOG = 11002, // NOT RESERVED IDS 12015-1999999999 LANG_BG_TP_START_TWO_MINUTES = 12000, diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 9b779a6b8a8..7f3001ed680 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -390,6 +390,30 @@ void MotionMaster::MoveFall(uint32 id /*=0*/) Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); } +void MotionMaster::MoveFallPlayer(uint32 id /*=0*/) +{ + // use larger distance for vmap height search than in most other cases + float tz = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ(), true, MAX_FALL_DISTANCE); + if (tz <= INVALID_HEIGHT) + { + SF_LOG_DEBUG("misc", "MotionMaster::MoveFall: unable retrive a proper height at map %u (x: %f, y: %f, z: %f).", + _owner->GetMap()->GetId(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); + return; + } + // Abort too if the ground is very near + if (fabs(_owner->GetPositionZ() - tz) < 0.1f) + return; + if (_owner->GetTypeId() == TypeID::TYPEID_PLAYER) + { + _owner->ToPlayer()->SetFallInformation(0, _owner->GetPositionZ()); + } + Movement::MoveSplineInit init(_owner); + init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false); + init.SetFall(); + init.Launch(); + Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); +} + void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath) { if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE) diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 269a699ae52..7a28191f97a 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -149,7 +149,7 @@ class MotionMaster //: private std::stack { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); }; void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP); void MoveFall(uint32 id = 0); - + void MoveFallPlayer(uint32 id = 0); void MoveSeekAssistance(float x, float y, float z); void MoveSeekAssistanceDistract(uint32 timer); void MoveTaxiFlight(uint32 path, uint32 pathnode); diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 1c4744c26bb..5f94f5d9b57 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -28,6 +28,7 @@ void AddSC_SmartSCripts(); //Commands void AddSC_account_commandscript(); void AddSC_achievement_commandscript(); +void AddSC_anticheat_commandscript(); void AddSC_ban_commandscript(); void AddSC_bf_commandscript(); void AddSC_cast_commandscript(); @@ -681,6 +682,7 @@ void AddCommandScripts() { AddSC_account_commandscript(); AddSC_achievement_commandscript(); + AddSC_anticheat_commandscript(); AddSC_ban_commandscript(); AddSC_bf_commandscript(); AddSC_cast_commandscript(); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 0a624130d10..c6e47e8859a 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1734,6 +1734,11 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo case FORM_FLIGHT: case FORM_MOONKIN: { + if (Player* player = target->ToPlayer()) + { + player->SetCanTeleport(true); + } + // remove movement affects target->RemoveMovementImpairingAuras(); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 48b2ce15180..9770c43d194 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -44,6 +44,7 @@ #include "DB2Stores.h" #include "Battlefield.h" #include "BattlefieldMgr.h" +#include "AnticheatMgr.h" extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -5947,6 +5948,11 @@ SpellCastResult Spell::CheckCast(bool strict) else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) return SpellCastResult::SPELL_FAILED_NOPATH; } + if (Player* player = m_caster->ToPlayer()) + { + // To prevent false positives in the Anticheat system + sAnticheatMgr->SetAllowedMovement(player, true); + } break; } case SPELL_EFFECT_SKINNING: diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 5b378f07bcb..543bc18cf05 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -921,6 +921,11 @@ void Spell::EffectJumpDest(SpellEffIndex effIndex) float speedXY, speedZ; CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(x, y), speedXY, speedZ); m_caster->GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); + + if (Player* player = m_caster->ToPlayer()) + { + player->SetCanTeleport(true); + } } void Spell::CalculateJumpSpeeds(uint8 i, float dist, float & speedXY, float & speedZ) @@ -949,6 +954,11 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) return; } + if (Player* player = m_caster->ToPlayer()) + { + player->SetCanTeleport(true); + } + // Init dest coordinates uint32 mapid = destTarget->GetMapId(); if (mapid == MAPID_INVALID) @@ -5096,6 +5106,10 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) return; } } + if (Player* player = m_caster->ToPlayer()) + { + player->SetCanTeleport(true); + } } void Spell::EffectProspecting(SpellEffIndex /*effIndex*/) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 217b320c325..e4206b915ba 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -72,6 +72,7 @@ #include "BattlefieldMgr.h" #include "TransportMgr.h" #include "BattlePetMgr.h" +#include "AnticheatMgr.h" ACE_Atomic_Op World::m_stopEvent = false; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -1346,6 +1347,36 @@ void World::LoadConfigSettings(bool reload) setIntConfig(WorldIntConfigs::CONFIG_PACKET_SPOOF_BANDURATION, sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400)); + // Anticheat Stuff + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.Enable", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE_ON_GM, sConfigMgr->GetBoolDefault("Anticheat.EnabledOnGmAccounts", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_LUABLOCKER_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.LUAblocker", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_SPEEDHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectSpeedHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_FLYHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectFlyHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_WATERWALKHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectWaterWalkHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_JUMPHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectJumpHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ADV_JUMPHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.StricterDetectJumpHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_TELEPANEHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectTelePlaneHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CLIMBHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectClimbHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_GRAVITY_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectGravityHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_TELEPORTHACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectTelePortHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_BG_START_HACK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.DetectBGStartHack", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_FLYHACK, sConfigMgr->GetBoolDefault("Anticheat.CM.FLYHACK", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_WATERHACK, sConfigMgr->GetBoolDefault("Anticheat.CM.WATERHACK", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_SPEEDHACK, sConfigMgr->GetBoolDefault("Anticheat.CM.SPEEDHACK", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_JUMPHACK, sConfigMgr->GetBoolDefault("Anticheat.CM.JUMPHACK", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_ADVJUMPHACK, sConfigMgr->GetBoolDefault("Anticheat.CM.ADVJUMPHACK", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_CM_TELEPORT, sConfigMgr->GetBoolDefault("Anticheat.CM.Teleport", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOKICK_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.KickPlayer", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOBAN_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.BanPlayer", false)); + SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_AUTOJAIL_ENABLE, sConfigMgr->GetBoolDefault("Anticheat.JailPlayer", false)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION, sConfigMgr->GetIntDefault("Anticheat.Reports.InGame", 70)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT, sConfigMgr->GetIntDefault("Anticheat.Reports.InGame.Max", 70)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_SPEED_LIMIT_TOLERANCE, sConfigMgr->GetIntDefault("Anticheat.SpeedLimitTolerance", 10)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_BANS, sConfigMgr->GetIntDefault("Anticheat.ReportsForBan", 70)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_KICKS, sConfigMgr->GetIntDefault("Anticheat.ReportsForKick", 70)); + setIntConfig(WorldIntConfigs::CONFIG_ANTICHEAT_MAX_REPORTS_FOR_JAILS, sConfigMgr->GetIntDefault("Anticheat.ReportsForJail", 70)); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); @@ -3046,6 +3077,8 @@ void World::ResetDailyQuests() // change available dailies sPoolMgr->ChangeDailyQuests(); + + sAnticheatMgr->ResetDailyReportStates(); } void World::ResetCurrencyWeekCap() diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index d42b4bba7cb..d5d5b7a18e7 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -165,6 +165,30 @@ enum class WorldBoolConfigs CONFIG_TICKETS_GM_ENABLED, CONFIG_TICKETS_FEEDBACK_SYSTEM_ENABLED, CONFIG_BOOST_NEW_ACCOUNT, + // Anticheat + CONFIG_ANTICHEAT_ENABLE, + CONFIG_ANTICHEAT_ENABLE_ON_GM, + CONFIG_LUABLOCKER_ENABLE, + CONFIG_ANTICHEAT_SPEEDHACK_ENABLE, + CONFIG_ANTICHEAT_FLYHACK_ENABLE, + CONFIG_ANTICHEAT_WATERWALKHACK_ENABLE, + CONFIG_ANTICHEAT_JUMPHACK_ENABLE, + CONFIG_ANTICHEAT_ADV_JUMPHACK_ENABLE, + CONFIG_ANTICHEAT_TELEPANEHACK_ENABLE, + CONFIG_ANTICHEAT_GRAVITY_ENABLE, + CONFIG_ANTICHEAT_TELEPORTHACK_ENABLE, + CONFIG_ANTICHEAT_CLIMBHACK_ENABLE, + CONFIG_ANTICHEAT_BG_START_HACK_ENABLE, + CONFIG_ANTICHEAT_CM_SPEEDHACK, + CONFIG_ANTICHEAT_CM_FLYHACK, + CONFIG_ANTICHEAT_CM_WATERHACK, + CONFIG_ANTICHEAT_CM_JUMPHACK, + CONFIG_ANTICHEAT_CM_ADVJUMPHACK, + CONFIG_ANTICHEAT_CM_TELEPORT, + CONFIG_ANTICHEAT_AUTOKICK_ENABLE, + CONFIG_ANTICHEAT_AUTOBAN_ENABLE, + CONFIG_ANTICHEAT_AUTOJAIL_ENABLE, + BOOL_CONFIG_VALUE_COUNT }; @@ -359,6 +383,14 @@ enum class WorldIntConfigs CONFIG_BLACK_MARKET_AUCTION_DELAY_MOD, CONFIG_BOOST_START_MONEY, CONFIG_BOOST_START_LEVEL, + // Anticheat + CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION, + CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT, + CONFIG_ANTICHEAT_SPEED_LIMIT_TOLERANCE, + CONFIG_ANTICHEAT_MAX_REPORTS_FOR_BANS, + CONFIG_ANTICHEAT_MAX_REPORTS_FOR_KICKS, + CONFIG_ANTICHEAT_MAX_REPORTS_FOR_JAILS, + INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 3ac24d8f530..30fc8811eb8 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -103,6 +103,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/server/game/AI/CoreAI ${CMAKE_SOURCE_DIR}/src/server/game/AI/ScriptedAI ${CMAKE_SOURCE_DIR}/src/server/game/AI/SmartScripts + ${CMAKE_SOURCE_DIR}/src/server/game/Anticheat ${CMAKE_SOURCE_DIR}/src/server/game/AuctionHouse ${CMAKE_SOURCE_DIR}/src/server/game/Battlefield ${CMAKE_SOURCE_DIR}/src/server/game/Battlefield/Zones diff --git a/src/server/scripts/Commands/cs_anticheat.cpp b/src/server/scripts/Commands/cs_anticheat.cpp new file mode 100644 index 00000000000..4c83685f669 --- /dev/null +++ b/src/server/scripts/Commands/cs_anticheat.cpp @@ -0,0 +1,269 @@ +/* +* This file is part of Project SkyFire https://www.projectskyfire.org. +* See LICENSE.md file for Copyright information +*/ + +#include "Chat.h" +#include "Language.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "SpellAuras.h" +#include "AnticheatMgr.h" + +enum Spells +{ + FREEZE = 9454, + LFG_SPELL_DUNGEON_DESERTER = 71041, + BG_SPELL_DESERTER = 26013, + SILENCED = 23207 +}; + +class anticheat_commandscript : public CommandScript +{ +public: + anticheat_commandscript() : CommandScript("anticheat_commandscript") { } + + std::vector GetCommands() const override + { + static std::vector anticheatCommandTable = + { + { "global", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAntiCheatGlobalCommand, "", }, + { "player", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAntiCheatPlayerCommand, "", }, + { "delete", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAntiCheatDeleteCommand, "", }, + { "purge", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAntiCheatPurgeCommand, "", }, + { "handle", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAntiCheatHandleCommand, "", }, + { "jail", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAnticheatJailCommand, "", }, + { "parole", rbac::RBAC_PERM_COMMAND_GM, true, &HandleAnticheatParoleCommand, "", }, + }; + + static std::vector commandTable = + { + { "anticheat", rbac::RBAC_PERM_COMMAND_GM, true, NULL, "", anticheatCommandTable }, + }; + return commandTable; + } + static bool HandleAntiCheatGlobalCommand(ChatHandler* handler, const char* args) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + { + handler->PSendSysMessage("The Anticheat System is disabled."); + return true; + } + + sAnticheatMgr->AnticheatGlobalCommand(handler); + + return true; + } + static bool HandleAntiCheatPlayerCommand(ChatHandler* handler, char const* args) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + uint32 guid = target->GetGUIDLow(); + + 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 waterwalk_reports = sAnticheatMgr->GetTypeReports(guid, 2); + uint32 jump_reports = sAnticheatMgr->GetTypeReports(guid, 3); + uint32 teleportplane_reports = sAnticheatMgr->GetTypeReports(guid, 4); + uint32 climb_reports = sAnticheatMgr->GetTypeReports(guid, 5); + uint32 time_reports = sAnticheatMgr->GetTypeReports(guid, 6); + uint32 gravity_reports = sAnticheatMgr->GetTypeReports(guid, 7); + uint32 teleport_reports = sAnticheatMgr->GetTypeReports(guid, 8); + uint32 bgstart_reports = sAnticheatMgr->GetTypeReports(guid, 9); + + uint32 latency = 0; + latency = target->GetSession()->GetLatency(); + + // account ban info + QueryResult resultADB = LoginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate, banreason, bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC", target->GetSession()->GetAccountId()); + // character ban info + QueryResult resultCDB = CharacterDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate, banreason, bannedby FROM character_banned WHERE guid = '%u' ORDER BY bandate ASC", target->GetGUID()); + // 0 1 2 3 + QueryResult resultLDB = CharacterDatabase.PQuery("SELECT accountId, type, time, data FROM account_data WHERE `data` LIKE '%%CastSpellByName%%' AND accountId = '%u'", target->GetSession()->GetAccountId()); + + handler->PSendSysMessage("-----------------------------------------------------------------"); + handler->PSendSysMessage("Information about player %s", target->GetName().c_str()); + handler->PSendSysMessage("IP Address: %s || Latency %u ms", target->GetSession()->GetRemoteAddress().c_str(), latency); + if (resultADB) + { + do + { + Field* fields = resultADB->Fetch(); + std::string startbanEnd = TimeToTimestampStr(fields[3].GetUInt64()); + std::string bannedReason = fields[4].GetString(); + std::string bannedBy = fields[5].GetString(); + handler->PSendSysMessage("Account Previously Banned: Yes"); + handler->PSendSysMessage("Ban Ended: %s", startbanEnd.c_str()); + handler->PSendSysMessage("Ban by: %s || Ban Reason: %s", bannedBy.c_str(), bannedReason.c_str()); + } while (resultADB->NextRow()); + } + if (!resultADB) + { + handler->PSendSysMessage("Account Previously Banned: No"); + } + if (resultCDB) + { + do + { + Field* fields = resultCDB->Fetch(); + std::string startbanEnd = TimeToTimestampStr(fields[3].GetUInt64()); + std::string bannedReason = fields[4].GetString(); + std::string bannedBy = fields[5].GetString(); + handler->PSendSysMessage("Character Previously Banned: Yes"); + handler->PSendSysMessage("Ban Ended: %s", startbanEnd.c_str()); + handler->PSendSysMessage("Ban by: %s || Ban Reason: %s", bannedBy.c_str(), bannedReason.c_str()); + } while (resultCDB->NextRow()); + } + if (!resultCDB) + { + handler->PSendSysMessage("Character Previously Banned: No"); + } + if (resultLDB) + { + do + { + handler->PSendSysMessage("Macro Requiring Lua unlock Detected: Yes"); + } while (resultLDB->NextRow()); + } + if (!resultLDB) + { + handler->PSendSysMessage("Macro Requiring Lua unlock Detected: No"); + } + 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("Teleport Reports: %u || Climb Reports: %u", teleport_reports, climb_reports); + handler->PSendSysMessage("Time Manipuilation Reports: %u || Gravity Reports: %u", time_reports, gravity_reports); + handler->PSendSysMessage("Battle Ground Start Reports: %u", bgstart_reports); + return true; + } + static bool HandleAntiCheatDeleteCommand(ChatHandler* handler, char const* args) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + sAnticheatMgr->AnticheatDeleteCommand(target->GetGUID()); + handler->PSendSysMessage("Anticheat players_reports_status deleted for player %s", target->GetName().c_str()); + return true; + } + static bool HandleAntiCheatPurgeCommand(ChatHandler* handler, const char* args) + { + // For the sins I am about to commit, may CTHULHU forgive me + // this will purge the daily_player_reports which is the cumlative statistics of auto reports + sAnticheatMgr->AnticheatPurgeCommand(handler); + handler->PSendSysMessage("The Anticheat daily_player_reports has been purged."); + return true; + } + static bool HandleAntiCheatHandleCommand(ChatHandler* handler, const char* args) + { + if (!handler->GetSession() && !handler->GetSession()->GetPlayer()) + return false; + + std::string argstr = (char*)args; + + if (argstr == "on") + { + sWorld->SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE, true); + handler->SendSysMessage("The Anticheat System is now: Enabled!"); + return true; + } + if (argstr == "off") + { + sWorld->SetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE, false); + handler->SendSysMessage("The Anticheat System is now: Disabled!"); + return true; + } + + std::string strACModIs = sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE) ? "enabled" : "disabled"; + handler->PSendSysMessage("Anticheat Manager is now %s.", strACModIs.c_str()); + + return true; + } + static bool HandleAnticheatJailCommand(ChatHandler* handler, char const* args) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + target->CastSpell(target, FREEZE);// Freeze him in place to ensure no exploit happens for jail break attempt + + if (Aura* dungdesert = target->AddAura(LFG_SPELL_DUNGEON_DESERTER, target))// LFG_SPELL_DUNGEON_DESERTER + { + dungdesert->SetDuration(-1); + } + if (Aura* bgdesert = target->AddAura(BG_SPELL_DESERTER, target))// BG_SPELL_DESERTER + { + bgdesert->SetDuration(-1); + } + if (Aura* silent = target->AddAura(SILENCED, target))// SILENCED + { + silent->SetDuration(-1); + } + + WorldLocation jailLoc = WorldLocation(1, 16226.5f, 16403.6f, -64.5f, 3.2f);// GM Jail Location + target->SetHomebind(jailLoc, 876);// GM Jail Homebind location + target->TeleportTo(jailLoc); + + return true; + } + static bool HandleAnticheatParoleCommand(ChatHandler* handler, char const* args) + { + if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + WorldLocation Aloc = WorldLocation(0, -8833.37f, 628.62f, 94.00f, 1.06f);// Stormwind + WorldLocation Hloc = WorldLocation(1, 1569.59f, -4397.63f, 16.06f, 0.54f);// Orgrimmar + WorldLocation Nloc = WorldLocation(860, 1470.96f, 3466.06f, 181.63f, 2.78f);//Shang Xi Training Grounds + + if (target->getRace() == RACE_PANDAREN_NEUTRAL) + { + target->TeleportTo(860, 1470.96f, 3466.06f, 181.63f, 2.78f);//Shang Xi Training Grounds + target->SetHomebind(Nloc, 5834);// Shang Xi Training Grounds Homebind location + } + if (target->GetTeamId() == TEAM_ALLIANCE && target->getRace() != RACE_PANDAREN_NEUTRAL) + { + target->TeleportTo(0, -8833.37f, 628.62f, 94.00f, 1.06f);//Stormwind + target->SetHomebind(Aloc, 1519);// Stormwind Homebind location + } + if (target->GetTeamId() == TEAM_HORDE && target->getRace() != RACE_PANDAREN_NEUTRAL) + { + target->TeleportTo(1, 1569.59f, -4397.63f, 16.06f, 0.54f);//Orgrimmar + target->SetHomebind(Hloc, 1653);// Orgrimmar Homebind location + } + + target->RemoveAura(FREEZE);// remove shackles + target->RemoveAura(LFG_SPELL_DUNGEON_DESERTER);// LFG_SPELL_DUNGEON_DESERTER + target->RemoveAura(BG_SPELL_DESERTER);// BG_SPELL_DESERTER + target->RemoveAura(SILENCED);// SILENCED + sAnticheatMgr->AnticheatDeleteCommand(target->GetGUID());// deletes auto reports on player + return true; + } +}; + +void AddSC_anticheat_commandscript() +{ + new anticheat_commandscript(); +} diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 20141ffd9c7..01d30fdca1e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3110,3 +3110,140 @@ Boost.StartLevel = 90 # ################################################################################################### + +################################################################################################### +# ANTICHEAT +# +# Anticheat.Enable +# Description: Enables or disables the Anticheat System functionality +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Anticheat.Enable = 1 + +# Anticheat.EnabledOnGmAccounts +# Description: Enables detection for GM accounts +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Anticheat.EnabledOnGmAccounts = 0 + +# Anticheat REPORTING +# Description: Alerts GMs that are on line once reports hit designated threshhold and stops after desiganted threshhold +# Default: 70 - (Alerts at and Stops Alerting at 70 report thresh hold) +# 0 - (Disabled) + +Anticheat.Reports.InGame = 70 +Anticheat.Reports.InGame.Max = 70 + +# Anticheat.Detect +# Description: It represents which detections are enabled (ClimbHack disabled by default). +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Anticheat.DetectFlyHack = 0 +Anticheat.DetectWaterWalkHack = 0 +Anticheat.DetectJumpHack = 0 +Anticheat.StricterDetectJumpHack = 0 +Anticheat.DetectTelePlaneHack = 0 +Anticheat.DetectSpeedHack = 0 +Anticheat.DetectClimbHack = 0 +Anticheat.DetectGravityHack = 0 +Anticheat.DetectTelePortHack = 0 + +# ANTICHEAT COUNTER MEASURE +# +# Anticheat Counter Measures +# +# Anticheat.CM.Teleport +# +# Description: Sends player back to last gps position if player cheats and teleports. +# +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +Anticheat.CM.Teleport = 0 +# +# Anticheat.CM.FLYHACK +# +# Description: Sets player back to the ground if fly hack is detected +# +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +Anticheat.CM.FLYHACK = 0 +# +# Anticheat.CM.WATERHACK +# +# Description: Sets player to fall if water hack is detected +# +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +Anticheat.CM.WATERHACK = 0 +# +# Anticheat.CM.JUMPHACK +# Anticheat.CM.ADVJUMPHACK +# Description: Sets player back to the ground if jump hack is detected +# +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +Anticheat.CM.JUMPHACK = 0 +Anticheat.CM.ADVJUMPHACK = 0 +# +# Anticheat.CM.SPEEDHACK +# +# Description: Sets player back to allowed server rate speed when speed hack is detected +# Default: 0 - (Disabled) +# 1 - (Enabled) +# +Anticheat.CM.SPEEDHACK = 0 + +# Automatic Moderation Features +# +# Anticheat.KickPlayer +# Anticheat.ReportsForKick +# +# Description: Enables and Auto kick when reports reach threshhold +# Default: 0 - (Disabled) +# 1 - (Enabled) +# Default: 70 - (Kicks at 70 auto reports) +# + +Anticheat.KickPlayer = 0 +Anticheat.ReportsForKick = 75 + +# Anticheat.BanPlayer +# Anticheat.ReportsForBan +# +# Description: Enables and Auto ban when reports reach threshhold +# Default: 0 - (Disabled) +# 1 - (Enabled) +# Default: 70 - (Bans at 70 auto reports) +# + +Anticheat.BanPlayer = 0 +Anticheat.ReportsForBan = 70 + +# Anticheat.JailPlayer +# Anticheat.ReportsForJail +# +# Description: Enables and Auto Jail when reports reach threshhold +# Default: 0 - (Disabled) +# 1 - (Enabled) +# Default: 70 - (Jails at 70 auto reports) +# + +Anticheat.JailPlayer = 0 +Anticheat.ReportsForJail = 70 + +# Anticheat Logging\Appenders +# Currently spams into its own file +# If you want world console spam just use Logger.anticheat=3,Console Server Anticheat + +Appender.Anticheat=2,3,15,anticheat.log,a +Logger.anticheat=3,Server Anticheat + +# +###################################################################################################