diff --git a/data/sql/anticheat/anticheat.sql b/data/sql/anticheat/anticheat.sql new file mode 100644 index 0000000000..35045946be --- /dev/null +++ b/data/sql/anticheat/anticheat.sql @@ -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=''; \ No newline at end of file diff --git a/modules/azerothshard/src/server/plugins/AzthPlgLoader.cpp b/modules/azerothshard/src/server/plugins/AzthPlgLoader.cpp index 1f41656342..5e07f17512 100644 --- a/modules/azerothshard/src/server/plugins/AzthPlgLoader.cpp +++ b/modules/azerothshard/src/server/plugins/AzthPlgLoader.cpp @@ -12,6 +12,7 @@ // void AddSC_PWS_Transmogrification(); // void AddSC_CS_Transmogrification(); // void AddSC_npc_1v1arena(); +void AddSC_npc_lottery(); void AddAzthScripts() { @@ -24,6 +25,7 @@ void AddAzthScripts() // AddSC_PWS_Transmogrification(); // AddSC_CS_Transmogrification(); // AddSC_npc_1v1arena(); + AddSC_npc_lottery(); } diff --git a/modules/azerothshard/src/server/plugins/npc_lottery.cpp b/modules/azerothshard/src/server/plugins/npc_lottery.cpp new file mode 100644 index 0000000000..2cd9843de9 --- /dev/null +++ b/modules/azerothshard/src/server/plugins/npc_lottery.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2008-2010 TrinityCore + * + * 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 . + */ + +/* ScriptData +SDAuthor: PrinceCreed +SD%Complete: 100% +SDComment: // +SDCategory: Custom +EndScriptData */ + +#include "ScriptPCH.h" +#include "Item.h" + +/*###### +## npc_lotto +######*/ + +#define GOSSIP_BUY_TICKET "Compra un biglietto" +#define TICKET_COST 500000 +#define EVENT_LOTTO 132 +#define MAX_TICKET 5 + +float mol_win[4] = {0.5f, 0.2f, 0.1f, 0.0f}; + +class npc_lotto : public CreatureScript +{ +public: + npc_lotto() : CreatureScript("npc_lotto") { } + + bool OnGossipHello(Player* pPlayer, Creature* creature) + { + if (pPlayer && !pPlayer->IsGameMaster()) + { + QueryResult result = ExtraDatabase.PQuery("SELECT COUNT(*) FROM lotto_tickets where guid=%u", pPlayer->GetGUIDLow()); + if (result) + { + Field *fields = result->Fetch(); + if (fields[0].GetInt32() >= MAX_TICKET) + { + pPlayer->SEND_GOSSIP_MENU(1200006, creature->GetGUID()); + return true; + } + pPlayer->PrepareGossipMenu(creature); + pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BUY_TICKET, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); + } + } + return true; + } + + bool OnGossipSelect(Player* pPlayer, Creature* creature, uint32 /*uiSender*/, uint32 uiAction) + { + pPlayer->PlayerTalkClass->ClearMenus(); + + if (!pPlayer->HasEnoughMoney(TICKET_COST)) + return false; + + switch(uiAction) + { + case GOSSIP_ACTION_INFO_DEF: + pPlayer->ModifyMoney(-TICKET_COST); + QueryResult result = ExtraDatabase.Query("SELECT MAX(id) FROM lotto_tickets"); + if (result) + { + uint32 id = result->Fetch()->GetUInt32(); + ExtraDatabase.PExecute("INSERT INTO lotto_tickets (id,name,guid) VALUES (%u,'%s',%u);", id+1, pPlayer->GetName().c_str(), pPlayer->GetGUIDLow()); + char msg[500]; + sprintf(msg, "Buona fortuna, $N. Il tuo biglietto e' il numero %i", id+1); + creature->MonsterWhisper(msg, pPlayer); + } + break; + } + pPlayer->PlayerTalkClass->SendCloseGossip(); + return true; + } + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_lottoAI (creature); + } + + struct npc_lottoAI : public ScriptedAI + { + npc_lottoAI(Creature* creature) : ScriptedAI(creature) + { + SayTimer = 600*IN_MILLISECONDS; + } + + uint32 SayTimer; + + void UpdateAI( uint32 diff) + { + if (IsEventActive(EVENT_LOTTO)) + { + if (me->IsVisible()) + { + me->SetVisible(false); + QueryResult result = ExtraDatabase.Query("SELECT MAX(id) FROM lotto_tickets"); + uint32 maxTickets = 0; + if (result) + { + maxTickets = result->Fetch()->GetUInt32(); + if (!maxTickets) + return; + } + + result = ExtraDatabase.Query("SELECT name, guid, id FROM `lotto_tickets` ORDER BY RAND() LIMIT 3;"); + uint32 position = 0; + + if (!result) + return; + + do + { + ++position; + + Field *fields = result->Fetch(); + + const char* name = fields[0].GetCString(); + uint32 guid = fields[1].GetUInt32(); + uint32 ticket = fields[2].GetUInt32(); + // uint32 reward = TICKET_COST / (1 << position) * maxTickets; + uint32 reward = TICKET_COST * mol_win[position-1] * maxTickets; + uint32 count = 0; + bool flag = false; + + ExtraDatabase.PExecute("INSERT INTO `lotto_extractions` (winner,guid,position,reward) VALUES ('%s',%u,%u,%u);",name,guid,position,reward); + + // Send reward by mail + //Item* item; + Player *pPlayer = ObjectAccessor::FindPlayer(guid); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + if (position != 4) + { + MailDraft("Premio Lotteria", "Complimenti! Hai vinto alla Lotteria!") + .AddMoney(reward) + .SendMailTo(trans, MailReceiver(pPlayer, GUID_LOPART(guid)), MailSender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM)); + } + + CharacterDatabase.CommitTransaction(trans); + + // Event Message + char msg[500]; + switch (position) + { + case 1: + sprintf(msg, "Il vincitore della Lotteria e' %s che guadagna la bellezza di %i gold con il biglietto %i!", name, reward/10000, ticket); + break; + case 2: + sprintf(msg, "Il secondo premio va a %s che vince %i gold con il biglietto %i!", name, reward/10000, ticket); + break; + case 3: + sprintf(msg, "Mentre il terzo se lo aggiudica %s che vince %i gold con il biglietto %i!", name, reward/10000, ticket); + break; + //case 4: + // sprintf(msg, "E' uscito un premio speciale che se lo aggiudica %s che vince %s con il biglietto %i!", name, item->GetTemplate()->Name1.c_str(), ticket); + // break; + default: + break; + } + + sWorld->SendGlobalText(msg,NULL); + } + while (result->NextRow()); + + // Delete tickets after extraction + ExtraDatabase.PExecute("DELETE FROM lotto_tickets;"); + } + } + else + { + if (!me->IsVisible()) + me->SetVisible(true); + + if (SayTimer <= diff) + { + char msg[500]; + sprintf(msg, "Biglietti della Lotteria! Bastano %u ori per diventare milionari!", TICKET_COST / 10000); + me->MonsterYell(msg, 0, NULL); + QueryResult result = ExtraDatabase.Query("SELECT MAX(id) FROM lotto_tickets"); + if (result) + { + uint32 maxTickets = result->Fetch()->GetUInt32(); + char msg[500]; + sprintf(msg, "Il Primo premio ammonta a %u ori!", uint32(TICKET_COST / 10000 * mol_win[0] * maxTickets)); + if (uint32(TICKET_COST / 10000 * mol_win[0] * maxTickets) > 3000) + me->MonsterYell(msg, LANG_UNIVERSAL, NULL); + } + SayTimer = 600 * IN_MILLISECONDS; + } + else SayTimer -= diff; + } + } + }; + +}; + +void AddSC_npc_lottery() +{ + new npc_lotto(); +} + diff --git a/src/server/game/Anticheat/AntiCheatScripts.cpp b/src/server/game/Anticheat/AntiCheatScripts.cpp new file mode 100644 index 0000000000..44fb5e1af3 --- /dev/null +++ b/src/server/game/Anticheat/AntiCheatScripts.cpp @@ -0,0 +1,14 @@ +#include "AnticheatScripts.h" +#include "AnticheatMgr.h" + +AnticheatScripts::AnticheatScripts(): PlayerScript("AnticheatScripts") {} + +void AnticheatScripts::OnLogout(Player* player) +{ + sAnticheatMgr->HandlePlayerLogout(player); +} + +void AnticheatScripts::OnLogin(Player* player) +{ + sAnticheatMgr->HandlePlayerLogin(player); +} \ No newline at end of file diff --git a/src/server/game/Anticheat/AnticheatData.cpp b/src/server/game/Anticheat/AnticheatData.cpp new file mode 100644 index 0000000000..1a0402f704 --- /dev/null +++ b/src/server/game/Anticheat/AnticheatData.cpp @@ -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.m_positionX = x; + lastMovementInfo.pos.m_positionY = y; + lastMovementInfo.pos.m_positionZ = z; + lastMovementInfo.pos.m_orientation = 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]; +} \ No newline at end of file diff --git a/src/server/game/Anticheat/AnticheatData.h b/src/server/game/Anticheat/AnticheatData.h new file mode 100644 index 0000000000..700ad2df78 --- /dev/null +++ b/src/server/game/Anticheat/AnticheatData.h @@ -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 \ No newline at end of file diff --git a/src/server/game/Anticheat/AnticheatMgr.cpp b/src/server/game/Anticheat/AnticheatMgr.cpp new file mode 100644 index 0000000000..81e5c4edcb --- /dev/null +++ b/src/server/game/Anticheat/AnticheatMgr.cpp @@ -0,0 +1,435 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "AnticheatMgr.h" +#include "AnticheatScripts.h" +#include "MapManager.h" + +#define CLIMB_ANGLE 1.9f + +AnticheatMgr::AnticheatMgr() +{ +} + +AnticheatMgr::~AnticheatMgr() +{ + m_Players.clear(); +} + +void AnticheatMgr::JumpHackDetection(Player* player, MovementInfo /* movementInfo */,uint32 opcode) +{ + if ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & JUMP_HACK_DETECTION) == 0) + return; + + uint32 key = player->GetGUIDLow(); + + if (m_Players[key].GetLastOpcode() == MSG_MOVE_JUMP && opcode == MSG_MOVE_JUMP) + { + BuildReport(player,JUMP_HACK_REPORT); + sLog->outError("AnticheatMgr:: Jump-Hack detected player GUID (low) %u",player->GetGUIDLow()); + } +} + +void AnticheatMgr::WalkOnWaterHackDetection(Player* player, MovementInfo /* movementInfo */) +{ + if ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & WALK_WATER_HACK_DETECTION) == 0) + 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->outError("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 ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & FLY_HACK_DETECTION) == 0) + 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->outError("AnticheatMgr:: Fly-Hack detected player GUID (low) %u",player->GetGUIDLow()); + BuildReport(player,FLY_HACK_REPORT); +} + +void AnticheatMgr::TeleportPlaneHackDetection(Player* player, MovementInfo movementInfo) +{ + if ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & TELEPORT_PLANE_HACK_DETECTION) == 0) + 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->outError("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 (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + 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 ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & CLIMB_HACK_DETECTION) == 0) + 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(&pos); + + 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->outError("AnticheatMgr:: Climb-Hack detected player GUID (low) %u", player->GetGUIDLow()); + BuildReport(player,CLIMB_HACK_REPORT); + } +} + +void AnticheatMgr::SpeedHackDetection(Player* player,MovementInfo movementInfo) +{ + if ((sWorld->getIntConfig(CONFIG_ANTICHEAT_DETECTIONS_ENABLED) & SPEED_HACK_DETECTION) == 0) + 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->outError("AnticheatMgr:: Speed-Hack detected player GUID (low) %u",player->GetGUIDLow()); + } +} + +void AnticheatMgr::StartScripts() +{ + new AnticheatScripts(); +} + +void AnticheatMgr::HandlePlayerLogin(Player* player) +{ + // we must delete this to prevent errors in case of crash + ExtraDatabase.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 = ExtraDatabase.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) + ExtraDatabase.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) +{ + ExtraDatabase.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 (sWorld->getIntConfig(CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT) < m_Players[key].GetTotalReports()) + { + if (!m_Players[key].GetDailyReportState()) + { + ExtraDatabase.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() > sWorld->getIntConfig(CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION)) + { + // 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!. + sObjectAccessor->SaveAllPlayers(); + + QueryResult resultDB = ExtraDatabase.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 = ExtraDatabase.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 = 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); + } + } + ExtraDatabase.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); + } + ExtraDatabase.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); +} \ No newline at end of file diff --git a/src/server/game/Anticheat/AnticheatMgr.h b/src/server/game/Anticheat/AnticheatMgr.h new file mode 100644 index 0000000000..2789ed985d --- /dev/null +++ b/src/server/game/Anticheat/AnticheatMgr.h @@ -0,0 +1,103 @@ +/* + * 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 . + */ + +#ifndef SC_ACMGR_H +#define SC_ACMGR_H + +//#include +#include "Common.h" +#include "SharedDefines.h" +#include "ScriptPCH.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 AnticheatPlayersDataMap; + +class AnticheatMgr +{ +// friend class ACE_Singleton; + 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 StartScripts(); + + 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 \ No newline at end of file diff --git a/src/server/game/Anticheat/AnticheatScripts.h b/src/server/game/Anticheat/AnticheatScripts.h new file mode 100644 index 0000000000..b060370c0c --- /dev/null +++ b/src/server/game/Anticheat/AnticheatScripts.h @@ -0,0 +1,15 @@ +#ifndef SC_ACSCRIPTS_H +#define SC_ACSCRIPTS_H + +#include "ScriptPCH.h" + +class AnticheatScripts: public PlayerScript +{ + public: + AnticheatScripts(); + + void OnLogout(Player* player); + void OnLogin(Player* player); +}; + +#endif \ No newline at end of file diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index 6aa946a3a4..b090afb9f8 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -14,6 +14,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) @@ -68,6 +69,7 @@ set(game_STAT_SRCS ${sources_Achievements} ${sources_Addons} ${sources_AI} + ${sources_Anticheat} ${sources_AuctionHouse} ${sources_Battlefield} ${sources_Battlegrounds} @@ -142,6 +144,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/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index e0a5e0944d..9b548aaeff 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -9080,6 +9080,14 @@ GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry) return NULL; } +//[AZTH] +Player* ObjectMgr::GetPlayerByLowGUID(uint32 lowguid) const +{ + uint64 guid = MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER); + return ObjectAccessor::FindPlayer(guid); +} +//[AZTH] + bool ObjectMgr::IsGameObjectStaticTransport(uint32 entry) { GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index bf7d3a3915..b036c1d8b8 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -666,6 +666,8 @@ class ObjectMgr typedef std::map CharacterConversionMap; + Player* GetPlayerByLowGUID(uint32 lowguid) const; //[AZTH] + GameObjectTemplate const* GetGameObjectTemplate(uint32 entry); bool IsGameObjectStaticTransport(uint32 entry); GameObjectTemplateContainer const* GetGameObjectTemplates() const { return &_gameObjectTemplateStore; } diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index aff9539c7d..41e313961f 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -35,6 +35,7 @@ #include "ArenaSpectator.h" #include "Chat.h" #include "BattlegroundMgr.h" +#include "AnticheatMgr.h" #define MOVEMENT_PACKET_TIME_DELAY 0 @@ -407,6 +408,11 @@ void WorldSession::HandleMovementOpcodes(WorldPacket & recvData) // Dont allow to turn on walking if charming other player if (mover->GetGUID() != _player->GetGUID()) movementInfo.flags &= ~MOVEMENTFLAG_WALKING; + + // [AZTH] Anticheat + if (plrMover) + sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); + // [/AZTH] uint32 mstime = World::GetGameTimeMS(); /*----------------------*/ diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index cdeb7e5c6f..b1eed72c55 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -17,6 +17,7 @@ #include "ScriptLoader.h" #include "ScriptMgr.h" +#include "AnticheatMgr.h" //examples void AddSC_example_creature(); @@ -74,6 +75,7 @@ void AddSC_tele_commandscript(); void AddSC_ticket_commandscript(); void AddSC_titles_commandscript(); void AddSC_wp_commandscript(); +void AddSC_anticheat_commandscript(); #ifdef SCRIPTS //world @@ -594,6 +596,7 @@ void AddScripts() AddSpellScripts(); AddSC_SmartSCripts(); AddCommandScripts(); + sAnticheatMgr->StartScripts(); //[AZTH] Anticheat #ifdef SCRIPTS AddWorldScripts(); AddEventScripts(); @@ -693,6 +696,7 @@ void AddCommandScripts() AddSC_ticket_commandscript(); AddSC_titles_commandscript(); AddSC_wp_commandscript(); + AddSC_anticheat_commandscript(); } void AddWorldScripts() diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index f026cba9b4..350ecf6132 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -87,6 +87,7 @@ #include "WhoListCache.h" #include "AsyncAuctionListing.h" #include "SavingSystem.h" +#include "AnticheatMgr.h" ACE_Atomic_Op World::m_stopEvent = false; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -1197,6 +1198,13 @@ void World::LoadConfigSettings(bool reload) // Dungeon finder m_int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetIntDefault("DungeonFinder.OptionsMask", 3); + //[AZTH] ANTICHEAT + m_bool_configs[CONFIG_ANTICHEAT_ENABLE] = sConfigMgr->GetBoolDefault("Anticheat.Enable", true); + m_int_configs[CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION] = sConfigMgr->GetIntDefault("Anticheat.ReportsForIngameWarnings", 70); + m_int_configs[CONFIG_ANTICHEAT_DETECTIONS_ENABLED] = sConfigMgr->GetIntDefault("Anticheat.DetectionsEnabled", 31); + m_int_configs[CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT] = sConfigMgr->GetIntDefault("Anticheat.MaxReportsForDailyReport", 70); + //[AZTH] + // DBC_ItemAttributes m_bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = sConfigMgr->GetBoolDefault("DBC.EnforceItemAttributes", true); @@ -2818,6 +2826,7 @@ void World::ResetDailyQuests() // change available dailies sPoolMgr->ChangeDailyQuests(); + sAnticheatMgr->ResetDailyReportStates(); } void World::LoadDBAllowedSecurityLevel() diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 378bdfce09..06064dae3a 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -156,6 +156,7 @@ enum WorldBoolConfigs CONFIG_QUEST_IGNORE_AUTO_ACCEPT, CONFIG_QUEST_IGNORE_AUTO_COMPLETE, CONFIG_WARDEN_ENABLED, + CONFIG_ANTICHEAT_ENABLE, BOOL_CONFIG_VALUE_COUNT }; @@ -317,6 +318,9 @@ enum WorldIntConfigs CONFIG_WARDEN_NUM_MEM_CHECKS, CONFIG_WARDEN_NUM_OTHER_CHECKS, CONFIG_BIRTHDAY_TIME, + CONFIG_ANTICHEAT_REPORTS_INGAME_NOTIFICATION, + CONFIG_ANTICHEAT_MAX_REPORTS_FOR_DAILY_REPORT, + CONFIG_ANTICHEAT_DETECTIONS_ENABLED, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 77e147b49d..1fd7a62921 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -73,6 +73,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 0000000000..a13cb77fac --- /dev/null +++ b/src/server/scripts/Commands/cs_anticheat.cpp @@ -0,0 +1,262 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +#include "Language.h" +#include "ScriptMgr.h" +#include "ObjectMgr.h" +#include "Chat.h" +#include "AnticheatMgr.h" + +class anticheat_commandscript : public CommandScript +{ +public: + anticheat_commandscript() : CommandScript("anticheat_commandscript") { } + + ChatCommand* GetCommands() const + { + static ChatCommand anticheatCommandTable[] = + { + { "global", SEC_GAMEMASTER, true, &HandleAntiCheatGlobalCommand, "", NULL }, + { "player", SEC_GAMEMASTER, true, &HandleAntiCheatPlayerCommand, "", NULL }, + { "delete", SEC_ADMINISTRATOR, true, &HandleAntiCheatDeleteCommand, "", NULL }, + { "handle", SEC_ADMINISTRATOR, true, &HandleAntiCheatHandleCommand, "", NULL }, + { "jail", SEC_GAMEMASTER, true, &HandleAnticheatJailCommand, "", NULL }, + { "warn", SEC_GAMEMASTER, true, &HandleAnticheatWarnCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand commandTable[] = + { + { "anticheat", SEC_GAMEMASTER, true, NULL, "", anticheatCommandTable}, + { NULL, 0, false, NULL, "", NULL } + }; + + return commandTable; + } + + static bool HandleAnticheatWarnCommand(ChatHandler* handler, const char* args) + { + if (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* pTarget = NULL; + + std::string strCommand; + + char* command = strtok((char*)args, " "); + + if (command) + { + strCommand = command; + normalizePlayerName(strCommand); + + pTarget = sObjectAccessor->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 (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + return false; + + Player* pTarget = NULL; + + std::string strCommand; + + char* command = strtok((char*)args, " "); + + if (command) + { + strCommand = command; + normalizePlayerName(strCommand); + + pTarget = sObjectAccessor->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); + + WorldLocation loc; + + // the player should be already there, but no :( + // pTarget->GetPosition(&loc); + + loc.m_mapId = 1; + loc.m_positionX = 16226.5f; + loc.m_positionY = 16403.6f; + loc.m_positionZ = -64.5f; + loc.m_orientation = 3.2f; + + pTarget->SetHomebind(loc,876); + return true; + } + + static bool HandleAntiCheatDeleteCommand(ChatHandler* handler, const char* args) + { + if (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + 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 = sObjectAccessor->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 (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + return false; + + std::string strCommand; + + char* command = strtok((char*)args, " "); + + uint32 guid = 0; + Player* player = NULL; + + if (command) + { + strCommand = command; + + normalizePlayerName(strCommand); + player = sObjectAccessor->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!"); + } + + return true; + } + + static bool HandleAntiCheatGlobalCommand(ChatHandler* handler, const char* /* args */) + { + if (!sWorld->getBoolConfig(CONFIG_ANTICHEAT_ENABLE)) + { + handler->PSendSysMessage("The Anticheat System is disabled."); + return true; + } + + sAnticheatMgr->AnticheatGlobalCommand(handler); + + return true; + } +}; + +void AddSC_anticheat_commandscript() +{ + new anticheat_commandscript(); +} \ No newline at end of file diff --git a/src/server/shared/Database/DatabaseEnv.h b/src/server/shared/Database/DatabaseEnv.h index cd45b97a1f..e29a899e36 100644 --- a/src/server/shared/Database/DatabaseEnv.h +++ b/src/server/shared/Database/DatabaseEnv.h @@ -37,10 +37,12 @@ #include "Implementation/LoginDatabase.h" #include "Implementation/CharacterDatabase.h" #include "Implementation/WorldDatabase.h" +#include "Implementation/ExtraDatabase.h" extern WorldDatabaseWorkerPool WorldDatabase; extern CharacterDatabaseWorkerPool CharacterDatabase; extern LoginDatabaseWorkerPool LoginDatabase; +extern ExtraDatabaseWorkerPool ExtraDatabase; #endif diff --git a/src/server/shared/Database/Implementation/ExtraDatabase.cpp b/src/server/shared/Database/Implementation/ExtraDatabase.cpp new file mode 100644 index 0000000000..dcb661cc52 --- /dev/null +++ b/src/server/shared/Database/Implementation/ExtraDatabase.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008-2010 TrinityCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "ExtraDatabase.h" + +void ExtraDatabaseConnection::DoPrepareStatements() +{ + if (!m_reconnecting) + m_stmts.resize(MAX_EXTRADATABASE_STATEMENTS); + + PrepareStatement(EXTRA_ADD_ITEMSTAT, "INSERT INTO item_stats (guid, item, state, group_guid) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(EXTRA_GET_EXTERNAL_MAIL, "SELECT id, receiver, subject, message, money, item, item_count FROM mail_external ORDER BY id ASC", CONNECTION_SYNCH); + PrepareStatement(EXTRA_DEL_EXTERNAL_MAIL, "DELETE FROM mail_external WHERE id = ?", CONNECTION_ASYNC); +} diff --git a/src/server/shared/Database/Implementation/ExtraDatabase.h b/src/server/shared/Database/Implementation/ExtraDatabase.h new file mode 100644 index 0000000000..269ed9425e --- /dev/null +++ b/src/server/shared/Database/Implementation/ExtraDatabase.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2010 TrinityCore + * + * 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 . + */ + +#ifndef _EXTRADATABASE_H +#define _EXTRADATABASE_H + +#include "DatabaseWorkerPool.h" +#include "MySQLConnection.h" + +class ExtraDatabaseConnection : public MySQLConnection +{ + public: + //- Constructors for sync and async connections + ExtraDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) {} + ExtraDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) {} + + + //- Loads databasetype specific prepared statements + void DoPrepareStatements(); +}; + +typedef DatabaseWorkerPool ExtraDatabaseWorkerPool; + +enum ExtraDatabaseStatements +{ + /* Naming standard for defines: + {DB}_{SET/DEL/ADD/REP}_{Summary of data changed} + When updating more than one field, consider looking at the calling function + name for a suiting suffix. + */ + EXTRA_ADD_ITEMSTAT, + EXTRA_GET_EXTERNAL_MAIL, + EXTRA_DEL_EXTERNAL_MAIL, + MAX_EXTRADATABASE_STATEMENTS, +}; + +#endif diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 0a00f1d1b7..db452167ee 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -52,6 +52,7 @@ int m_ServiceStatus = -1; WorldDatabaseWorkerPool WorldDatabase; ///< Accessor to the world database CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the character database LoginDatabaseWorkerPool LoginDatabase; ///< Accessor to the realm/login database +ExtraDatabaseWorkerPool ExtraDatabase; ///< Accessor to the realm/login database uint32 realmID; ///< Id of the realm diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index d4250493af..fc942c7b31 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -90,6 +90,7 @@ LogsDir = "" LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" WorldDatabaseInfo = "127.0.0.1;3306;trinity;trinity;world" CharacterDatabaseInfo = "127.0.0.1;3306;trinity;trinity;characters" +ExtraDatabaseInfo = "127.0.0.1;3306;trinity;trinity;extra" # # LoginDatabase.WorkerThreads @@ -105,6 +106,7 @@ CharacterDatabaseInfo = "127.0.0.1;3306;trinity;trinity;characters" LoginDatabase.WorkerThreads = 1 WorldDatabase.WorkerThreads = 1 CharacterDatabase.WorkerThreads = 1 +ExtraDatabase.WorkerThreads = 1 # # LoginDatabase.SynchThreads @@ -118,6 +120,7 @@ CharacterDatabase.WorkerThreads = 1 LoginDatabase.SynchThreads = 1 WorldDatabase.SynchThreads = 1 CharacterDatabase.SynchThreads = 2 +ExtraDatabase.SynchThreads = 1 # # MaxPingTime @@ -3158,3 +3161,40 @@ Transmogrification.SetCopperCost = 0 # ################################################################################################### +# ANTICHEAT +# +# Anticheat.Enable +# Description: Enables or disables the Anticheat System functionality +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Anticheat.Enable = 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 = 15 + +# Anticheat.DetectionsEnabled +# Description: It represents which detections are enabled. +# +# 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 +# +# Default: 31 + +Anticheat.DetectionsEnabled = 31 + +# 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 = 30 + +# +################################################################################################### \ No newline at end of file