Add configurable anti-cheat gates
This commit is contained in:
@@ -190,6 +190,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
|
||||
|
||||
// 거리 체크
|
||||
int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY());
|
||||
const bool enforceCombatValidation = AntiCheatCombatValidationEnabled();
|
||||
|
||||
if (!victim->IsBuilding())
|
||||
{
|
||||
@@ -207,7 +208,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
|
||||
max = MAX(300, (int) (victim->GetMobAttackRange() * 1.15f));
|
||||
}
|
||||
|
||||
if (distance > max)
|
||||
if (enforceCombatValidation && distance > max)
|
||||
{
|
||||
if (ch->IsPC() && distance > max + 200)
|
||||
{
|
||||
@@ -229,7 +230,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
|
||||
}
|
||||
|
||||
float rotDelta = 0.0f;
|
||||
if (!battle_melee_angle_valid(ch, victim, distance, max, &rotDelta))
|
||||
if (enforceCombatValidation && !battle_melee_angle_valid(ch, victim, distance, max, &rotDelta))
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail,
|
||||
@@ -263,7 +264,7 @@ int battle_melee_attack(LPCHARACTER ch, LPCHARACTER victim)
|
||||
int dam;
|
||||
int ret = battle_hit(ch, victim, dam);
|
||||
|
||||
if ((ret == BATTLE_DAMAGE || ret == BATTLE_DEAD) && ch->IsPC() && victim->IsPC())
|
||||
if (enforceCombatValidation && (ret == BATTLE_DAMAGE || ret == BATTLE_DEAD) && ch->IsPC() && victim->IsPC())
|
||||
victim->LockSyncOwner(450);
|
||||
|
||||
return (ret);
|
||||
@@ -882,6 +883,9 @@ void SET_ATTACKED_TIME(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)
|
||||
|
||||
bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)
|
||||
{
|
||||
if (!AntiCheatCombatValidationEnabled())
|
||||
return false;
|
||||
|
||||
// 2013 09 11 CYH debugging log
|
||||
/*sys_log(0, "%s attack test log! time (delta, limit)=(%u, %u). ch->m_kAttackLog.dwvID(%u) victim->GetVID(%u)",
|
||||
ch->GetName(),
|
||||
|
||||
@@ -4125,21 +4125,24 @@ void CHARACTER::mining_cancel()
|
||||
void CHARACTER::mining(LPCHARACTER chLoad)
|
||||
{
|
||||
const DWORD dwNow = get_dword_time();
|
||||
if (0 == m_dwMiningWindowStart || dwNow - m_dwMiningWindowStart >= kActionWindowMs)
|
||||
if (AntiCheatActionValidationEnabled() && (0 == m_dwMiningWindowStart || dwNow - m_dwMiningWindowStart >= kActionWindowMs))
|
||||
{
|
||||
m_dwMiningWindowStart = dwNow;
|
||||
m_iMiningWindowCount = 0;
|
||||
}
|
||||
|
||||
++m_iMiningWindowCount;
|
||||
if (m_iMiningWindowCount > kMiningRateWarnThreshold)
|
||||
if (AntiCheatActionValidationEnabled())
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "window=1s count=%d target=%u", m_iMiningWindowCount, chLoad ? chLoad->GetVID() : 0);
|
||||
RecordAntiCheatViolation("MINING_RATE", MIN(8, 1 + (m_iMiningWindowCount - kMiningRateWarnThreshold) / 2), szDetail, m_iMiningWindowCount > kMiningRateBlockThreshold);
|
||||
++m_iMiningWindowCount;
|
||||
if (m_iMiningWindowCount > kMiningRateWarnThreshold)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "window=1s count=%d target=%u", m_iMiningWindowCount, chLoad ? chLoad->GetVID() : 0);
|
||||
RecordAntiCheatViolation("MINING_RATE", MIN(8, 1 + (m_iMiningWindowCount - kMiningRateWarnThreshold) / 2), szDetail, m_iMiningWindowCount > kMiningRateBlockThreshold);
|
||||
|
||||
if (m_iMiningWindowCount > kMiningRateBlockThreshold)
|
||||
return;
|
||||
if (m_iMiningWindowCount > kMiningRateBlockThreshold)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pkMiningEvent)
|
||||
@@ -4148,7 +4151,7 @@ void CHARACTER::mining(LPCHARACTER chLoad)
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDead() || !CanMove() || !CanHandleItem())
|
||||
if (AntiCheatActionValidationEnabled() && (IsDead() || !CanMove() || !CanHandleItem()))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -4168,7 +4171,7 @@ void CHARACTER::mining(LPCHARACTER chLoad)
|
||||
if (mining::GetRawOreFromLoad(chLoad->GetRaceNum()) == 0)
|
||||
return;
|
||||
|
||||
if (chLoad == this || chLoad->GetMapIndex() != GetMapIndex())
|
||||
if (AntiCheatActionValidationEnabled() && (chLoad == this || chLoad->GetMapIndex() != GetMapIndex()))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -4182,7 +4185,7 @@ void CHARACTER::mining(LPCHARACTER chLoad)
|
||||
}
|
||||
|
||||
const int iDistance = DISTANCE_APPROX(GetX() - chLoad->GetX(), GetY() - chLoad->GetY());
|
||||
if (iDistance > kMiningStartMaxDistance)
|
||||
if (AntiCheatActionValidationEnabled() && iDistance > kMiningStartMaxDistance)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "target=%u distance=%d max=%d", chLoad->GetVID(), iDistance, kMiningStartMaxDistance);
|
||||
@@ -4217,21 +4220,24 @@ void CHARACTER::mining(LPCHARACTER chLoad)
|
||||
void CHARACTER::fishing()
|
||||
{
|
||||
const DWORD dwNow = get_dword_time();
|
||||
if (0 == m_dwFishingWindowStart || dwNow - m_dwFishingWindowStart >= kActionWindowMs)
|
||||
if (AntiCheatActionValidationEnabled() && (0 == m_dwFishingWindowStart || dwNow - m_dwFishingWindowStart >= kActionWindowMs))
|
||||
{
|
||||
m_dwFishingWindowStart = dwNow;
|
||||
m_iFishingWindowCount = 0;
|
||||
}
|
||||
|
||||
++m_iFishingWindowCount;
|
||||
if (m_iFishingWindowCount > kFishingRateWarnThreshold)
|
||||
if (AntiCheatActionValidationEnabled())
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "window=1s count=%d active=%d", m_iFishingWindowCount, m_pkFishingEvent ? 1 : 0);
|
||||
RecordAntiCheatViolation("FISHING_RATE", MIN(8, 1 + (m_iFishingWindowCount - kFishingRateWarnThreshold) / 2), szDetail, m_iFishingWindowCount > kFishingRateBlockThreshold);
|
||||
++m_iFishingWindowCount;
|
||||
if (m_iFishingWindowCount > kFishingRateWarnThreshold)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "window=1s count=%d active=%d", m_iFishingWindowCount, m_pkFishingEvent ? 1 : 0);
|
||||
RecordAntiCheatViolation("FISHING_RATE", MIN(8, 1 + (m_iFishingWindowCount - kFishingRateWarnThreshold) / 2), szDetail, m_iFishingWindowCount > kFishingRateBlockThreshold);
|
||||
|
||||
if (m_iFishingWindowCount > kFishingRateBlockThreshold)
|
||||
return;
|
||||
if (m_iFishingWindowCount > kFishingRateBlockThreshold)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pkFishingEvent)
|
||||
@@ -4240,7 +4246,7 @@ void CHARACTER::fishing()
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDead() || !CanMove() || !CanHandleItem())
|
||||
if (AntiCheatActionValidationEnabled() && (IsDead() || !CanMove() || !CanHandleItem()))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -4295,7 +4301,7 @@ void CHARACTER::fishing()
|
||||
|
||||
void CHARACTER::fishing_take()
|
||||
{
|
||||
if (!CanMove() || !CanHandleItem())
|
||||
if (AntiCheatActionValidationEnabled() && (!CanMove() || !CanHandleItem()))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -4309,7 +4315,7 @@ void CHARACTER::fishing_take()
|
||||
}
|
||||
|
||||
const int iMovedDistance = DISTANCE_APPROX(GetX() - m_lFishingStartX, GetY() - m_lFishingStartY);
|
||||
if (iMovedDistance > kFishingTakeMaxMoveDistance)
|
||||
if (AntiCheatActionValidationEnabled() && iMovedDistance > kFishingTakeMaxMoveDistance)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "distance=%d max=%d", iMovedDistance, kFishingTakeMaxMoveDistance);
|
||||
@@ -7258,6 +7264,13 @@ EVENTFUNC(check_speedhack_event)
|
||||
if (NULL == ch || ch->IsNPC())
|
||||
return 0;
|
||||
|
||||
if (!AntiCheatCombatValidationEnabled())
|
||||
{
|
||||
ch->m_speed_hack_count = 0;
|
||||
ch->ResetComboHackCount();
|
||||
return PASSES_PER_SEC(60);
|
||||
}
|
||||
|
||||
if (IS_SPEED_HACK_PLAYER(ch))
|
||||
{
|
||||
// write hack log
|
||||
@@ -7402,6 +7415,9 @@ BYTE CHARACTER::GetComboIndex() const
|
||||
|
||||
void CHARACTER::IncreaseComboHackCount(int k)
|
||||
{
|
||||
if (!AntiCheatCombatValidationEnabled())
|
||||
return;
|
||||
|
||||
m_iComboHackCount += k;
|
||||
|
||||
if (m_iComboHackCount >= 10)
|
||||
@@ -7447,6 +7463,9 @@ void CHARACTER::DecayAntiCheatScore(DWORD dwNow)
|
||||
|
||||
void CHARACTER::RecordAntiCheatViolation(const char* category, int score, const char* detail, bool forcePersist)
|
||||
{
|
||||
if (!AntiCheatEnabled())
|
||||
return;
|
||||
|
||||
const DWORD dwNow = get_dword_time();
|
||||
DecayAntiCheatScore(dwNow);
|
||||
|
||||
@@ -7475,7 +7494,7 @@ void CHARACTER::RecordAntiCheatViolation(const char* category, int score, const
|
||||
m_dwLastAntiCheatPersistTime = dwNow;
|
||||
}
|
||||
|
||||
if (GetDesc() && m_iAntiCheatScore >= kAntiCheatDisconnectScore)
|
||||
if (AntiCheatDisconnectEnabled() && GetDesc() && m_iAntiCheatScore >= kAntiCheatDisconnectScore)
|
||||
{
|
||||
if (GetDesc()->DelayedDisconnect(number(3, 8)))
|
||||
sys_log(0, "ANTI_CHEAT_DISCONNECT: %s score=%d", GetName(), m_iAntiCheatScore);
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace
|
||||
|
||||
void ObserveCombatTargetPattern(LPCHARACTER ch, LPCHARACTER victim, DWORD now)
|
||||
{
|
||||
if (!ch || !victim || !ch->IsPC())
|
||||
if (!AntiCheatBehaviorTelemetryEnabled() || !ch || !victim || !ch->IsPC())
|
||||
return;
|
||||
|
||||
if (!victim->IsMonster() && !victim->IsStone())
|
||||
@@ -365,7 +365,7 @@ bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType)
|
||||
{
|
||||
if (IsPC() == true)
|
||||
{
|
||||
if (dwCurrentTime - m_dwLastSkillTime > 1500)
|
||||
if (AntiCheatCombatValidationEnabled() && dwCurrentTime - m_dwLastSkillTime > 1500)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "skill=%u delta=%u victim=%s", bType, dwCurrentTime - m_dwLastSkillTime, pkVictim ? pkVictim->GetName() : "-");
|
||||
@@ -3108,7 +3108,8 @@ class CFuncShoot
|
||||
|
||||
int rangedDistance = 0;
|
||||
int rangedLimit = 0;
|
||||
if (!battle_ranged_target_valid(m_me, pkVictim, m_bType, &rangedDistance, &rangedLimit))
|
||||
if (AntiCheatCombatValidationEnabled() &&
|
||||
!battle_ranged_target_valid(m_me, pkVictim, m_bType, &rangedDistance, &rangedLimit))
|
||||
{
|
||||
if (m_me->IsPC())
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "char.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "item.h"
|
||||
#include "desc.h"
|
||||
#include "DragonSoul.h"
|
||||
@@ -154,6 +155,9 @@ bool CHARACTER::DragonSoul_RefineWindow_CanRefine()
|
||||
if (NULL == m_pointsInstant.m_pDragonSoulRefineWindowOpener)
|
||||
return false;
|
||||
|
||||
if (!AntiCheatActionValidationEnabled())
|
||||
return true;
|
||||
|
||||
LPENTITY pOpener = m_pointsInstant.m_pDragonSoulRefineWindowOpener;
|
||||
if (!pOpener->IsType(ENTITY_CHARACTER))
|
||||
{
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace
|
||||
if (!ch)
|
||||
return false;
|
||||
|
||||
if (!AntiCheatActionValidationEnabled())
|
||||
return true;
|
||||
|
||||
if (0 == dwRefineNpcVID)
|
||||
{
|
||||
char szDetail[128];
|
||||
@@ -114,7 +117,7 @@ namespace
|
||||
|
||||
void ObservePickupPattern(LPCHARACTER ch, DWORD now, long x, long y)
|
||||
{
|
||||
if (!ch)
|
||||
if (!AntiCheatBehaviorTelemetryEnabled() || !ch)
|
||||
return;
|
||||
|
||||
if (0 == ch->m_dwPickupPatternWindowStart || now - ch->m_dwPickupPatternWindowStart >= kPickupPatternWindowMs)
|
||||
@@ -5995,22 +5998,25 @@ bool CHARACTER::PickupItem(DWORD dwVID)
|
||||
const long lPickupX = GetX();
|
||||
const long lPickupY = GetY();
|
||||
|
||||
if (m_dwPickupWindowStart == 0 || dwNow - m_dwPickupWindowStart >= 1000)
|
||||
if (AntiCheatActionValidationEnabled() && (m_dwPickupWindowStart == 0 || dwNow - m_dwPickupWindowStart >= 1000))
|
||||
{
|
||||
m_dwPickupWindowStart = dwNow;
|
||||
m_iPickupWindowCount = 0;
|
||||
}
|
||||
|
||||
++m_iPickupWindowCount;
|
||||
|
||||
if (m_iPickupWindowCount > 25)
|
||||
if (AntiCheatActionValidationEnabled())
|
||||
{
|
||||
char szDetail[96];
|
||||
snprintf(szDetail, sizeof(szDetail), "count=%d window_ms=%u", m_iPickupWindowCount, dwNow - m_dwPickupWindowStart);
|
||||
RecordAntiCheatViolation("PICKUP_RATE", MIN(8, 1 + (m_iPickupWindowCount - 25) / 5), szDetail, m_iPickupWindowCount > 45);
|
||||
++m_iPickupWindowCount;
|
||||
|
||||
if (m_iPickupWindowCount > 45)
|
||||
return false;
|
||||
if (m_iPickupWindowCount > 25)
|
||||
{
|
||||
char szDetail[96];
|
||||
snprintf(szDetail, sizeof(szDetail), "count=%d window_ms=%u", m_iPickupWindowCount, dwNow - m_dwPickupWindowStart);
|
||||
RecordAntiCheatViolation("PICKUP_RATE", MIN(8, 1 + (m_iPickupWindowCount - 25) / 5), szDetail, m_iPickupWindowCount > 45);
|
||||
|
||||
if (m_iPickupWindowCount > 45)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LPITEM item = ITEM_MANAGER::instance().FindByVID(dwVID);
|
||||
@@ -6023,7 +6029,7 @@ bool CHARACTER::PickupItem(DWORD dwVID)
|
||||
|
||||
const int iDist = DISTANCE_APPROX(item->GetX() - GetX(), item->GetY() - GetY());
|
||||
|
||||
if (!item->DistanceValid(this))
|
||||
if (AntiCheatActionValidationEnabled() && !item->DistanceValid(this))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "item=%u dist=%d owner=%u", item->GetVID(), iDist, item->GetOwnershipPID());
|
||||
@@ -6141,9 +6147,12 @@ bool CHARACTER::PickupItem(DWORD dwVID)
|
||||
|
||||
if (!owner)
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail, sizeof(szDetail), "item=%u owner_pid=%u party=%d", item->GetVID(), item->GetOwnershipPID(), GetParty() ? 1 : 0);
|
||||
RecordAntiCheatViolation("PICKUP_OWNERSHIP", 6, szDetail, true);
|
||||
if (AntiCheatActionValidationEnabled())
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail, sizeof(szDetail), "item=%u owner_pid=%u party=%d", item->GetVID(), item->GetOwnershipPID(), GetParty() ? 1 : 0);
|
||||
RecordAntiCheatViolation("PICKUP_OWNERSHIP", 6, szDetail, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6202,9 +6211,12 @@ bool CHARACTER::PickupItem(DWORD dwVID)
|
||||
return true;
|
||||
}
|
||||
|
||||
char szDetail[160];
|
||||
snprintf(szDetail, sizeof(szDetail), "item=%u owner_pid=%u party=%d", item->GetVID(), item->GetOwnershipPID(), GetParty() ? 1 : 0);
|
||||
RecordAntiCheatViolation("PICKUP_OWNERSHIP", 4, szDetail);
|
||||
if (AntiCheatActionValidationEnabled())
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail, sizeof(szDetail), "item=%u owner_pid=%u party=%d", item->GetVID(), item->GetOwnershipPID(), GetParty() ? 1 : 0);
|
||||
RecordAntiCheatViolation("PICKUP_OWNERSHIP", 4, szDetail);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -98,6 +98,12 @@ unsigned int g_uiSpamBlockScore = 100; // 기본 100점
|
||||
unsigned int g_uiSpamReloadCycle = 60 * 10; // 기본 10분
|
||||
|
||||
bool g_bCheckMultiHack = true;
|
||||
bool g_bAntiCheatEnable = true;
|
||||
bool g_bAntiCheatDisconnect = true;
|
||||
bool g_bAntiCheatCombatValidation = true;
|
||||
bool g_bAntiCheatMovementValidation = true;
|
||||
bool g_bAntiCheatActionValidation = true;
|
||||
bool g_bAntiCheatBehaviorTelemetry = true;
|
||||
|
||||
int g_iSpamBlockMaxLevel = 10;
|
||||
|
||||
@@ -137,6 +143,36 @@ bool map_allow_find(int index)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AntiCheatEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable;
|
||||
}
|
||||
|
||||
bool AntiCheatDisconnectEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable && g_bAntiCheatDisconnect;
|
||||
}
|
||||
|
||||
bool AntiCheatCombatValidationEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable && g_bAntiCheatCombatValidation;
|
||||
}
|
||||
|
||||
bool AntiCheatMovementValidationEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable && g_bAntiCheatMovementValidation;
|
||||
}
|
||||
|
||||
bool AntiCheatActionValidationEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable && g_bAntiCheatActionValidation;
|
||||
}
|
||||
|
||||
bool AntiCheatBehaviorTelemetryEnabled()
|
||||
{
|
||||
return g_bAntiCheatEnable && g_bAntiCheatBehaviorTelemetry;
|
||||
}
|
||||
|
||||
void map_allow_log()
|
||||
{
|
||||
std::set<int>::iterator i;
|
||||
@@ -782,6 +818,36 @@ void config_init(const string& st_localeServiceName)
|
||||
str_to_number(g_bCheckMultiHack, value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_enable")
|
||||
{
|
||||
g_bAntiCheatEnable = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_disconnect")
|
||||
{
|
||||
g_bAntiCheatDisconnect = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_combat_validation")
|
||||
{
|
||||
g_bAntiCheatCombatValidation = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_movement_validation")
|
||||
{
|
||||
g_bAntiCheatMovementValidation = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_action_validation")
|
||||
{
|
||||
g_bAntiCheatActionValidation = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("anti_cheat_behavior_telemetry")
|
||||
{
|
||||
g_bAntiCheatBehaviorTelemetry = is_string_true(value_string);
|
||||
}
|
||||
|
||||
TOKEN("spam_block_max_level")
|
||||
{
|
||||
str_to_number(g_iSpamBlockMaxLevel, value_string);
|
||||
|
||||
@@ -98,6 +98,12 @@ extern int VIEW_BONUS_RANGE;
|
||||
extern bool g_bCheckMultiHack;
|
||||
extern bool g_protectNormalPlayer; // 범법자가 "평화모드" 인 일반유저를 공격하지 못함
|
||||
extern bool g_noticeBattleZone; // 중립지대에 입장하면 안내메세지를 알려줌
|
||||
extern bool g_bAntiCheatEnable;
|
||||
extern bool g_bAntiCheatDisconnect;
|
||||
extern bool g_bAntiCheatCombatValidation;
|
||||
extern bool g_bAntiCheatMovementValidation;
|
||||
extern bool g_bAntiCheatActionValidation;
|
||||
extern bool g_bAntiCheatBehaviorTelemetry;
|
||||
|
||||
extern DWORD g_GoldDropTimeLimitValue;
|
||||
|
||||
@@ -105,5 +111,11 @@ extern int gPlayerMaxLevel;
|
||||
|
||||
extern bool g_BlockCharCreation;
|
||||
|
||||
#endif /* __INC_METIN_II_GAME_CONFIG_H__ */
|
||||
extern bool AntiCheatEnabled();
|
||||
extern bool AntiCheatDisconnectEnabled();
|
||||
extern bool AntiCheatCombatValidationEnabled();
|
||||
extern bool AntiCheatMovementValidationEnabled();
|
||||
extern bool AntiCheatActionValidationEnabled();
|
||||
extern bool AntiCheatBehaviorTelemetryEnabled();
|
||||
|
||||
#endif /* __INC_METIN_II_GAME_CONFIG_H__ */
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "constants.h"
|
||||
#include "utils.h"
|
||||
#include "log.h"
|
||||
#include "config.h"
|
||||
#include "char.h"
|
||||
#include "locale_service.h"
|
||||
#include "item.h"
|
||||
@@ -188,6 +189,9 @@ static bool FN_validate_cube_npc(LPCHARACTER ch, const char* action)
|
||||
if (!ch || !ch->IsCubeOpen())
|
||||
return false;
|
||||
|
||||
if (!AntiCheatActionValidationEnabled())
|
||||
return true;
|
||||
|
||||
LPCHARACTER npc = ch->GetCubeNpc();
|
||||
if (NULL == npc)
|
||||
{
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace fishing
|
||||
|
||||
void ObserveFishingReaction(LPCHARACTER ch, int reactionMs, bool success)
|
||||
{
|
||||
if (!ch || reactionMs <= 0 || reactionMs > 6000)
|
||||
if (!AntiCheatBehaviorTelemetryEnabled() || !ch || reactionMs <= 0 || reactionMs > 6000)
|
||||
return;
|
||||
|
||||
const DWORD now = get_dword_time();
|
||||
|
||||
@@ -1340,6 +1340,9 @@ DWORD ClacValidComboInterval( LPCHARACTER ch, BYTE bArg )
|
||||
|
||||
bool CheckComboHack(LPCHARACTER ch, BYTE bArg, DWORD dwTime, bool CheckSpeedHack)
|
||||
{
|
||||
if (!AntiCheatCombatValidationEnabled())
|
||||
return false;
|
||||
|
||||
// 죽거나 기절 상태에서는 공격할 수 없으므로, skip한다.
|
||||
// 이렇게 하지 말고, CHRACTER::CanMove()에
|
||||
// if (IsStun() || IsDead()) return false;
|
||||
@@ -1558,8 +1561,10 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
|
||||
return;
|
||||
|
||||
struct command_move * pinfo = (struct command_move *) data;
|
||||
const bool enforceMovementValidation = AntiCheatMovementValidationEnabled();
|
||||
const bool enforceCombatValidation = AntiCheatCombatValidationEnabled();
|
||||
|
||||
if (pinfo->bFunc >= FUNC_MAX_NUM && !(pinfo->bFunc & 0x80))
|
||||
if (enforceMovementValidation && pinfo->bFunc >= FUNC_MAX_NUM && !(pinfo->bFunc & 0x80))
|
||||
{
|
||||
char szDetail[64];
|
||||
snprintf(szDetail, sizeof(szDetail), "func=%u arg=%u", pinfo->bFunc, pinfo->bArg);
|
||||
@@ -1582,7 +1587,12 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
|
||||
|
||||
const float fDist = DISTANCE_SQRT((ch->GetX() - pinfo->lX) / 100, (ch->GetY() - pinfo->lY) / 100);
|
||||
|
||||
if (ch->IsPC() && ch->IsSyncOwnerLocked() && ch->GetSyncOwner() && ch->GetSyncOwner() != ch && fDist > 2.5f)
|
||||
if (enforceMovementValidation &&
|
||||
ch->IsPC() &&
|
||||
ch->IsSyncOwnerLocked() &&
|
||||
ch->GetSyncOwner() &&
|
||||
ch->GetSyncOwner() != ch &&
|
||||
fDist > 2.5f)
|
||||
{
|
||||
char szDetail[192];
|
||||
snprintf(szDetail,
|
||||
@@ -1609,7 +1619,8 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
|
||||
|
||||
// if (!test_server) //2012.05.15 김용욱 : 테섭에서 (무적상태로) 다수 몬스터 상대로 다운되면서 공격시 콤보핵으로 죽는 문제가 있었다.
|
||||
{
|
||||
if (((false == ch->IsRiding() && fDist > 25) || fDist > 40) && OXEVENT_MAP_INDEX != ch->GetMapIndex())
|
||||
if (enforceMovementValidation &&
|
||||
(((false == ch->IsRiding() && fDist > 25) || fDist > 40) && OXEVENT_MAP_INDEX != ch->GetMapIndex()))
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail,
|
||||
@@ -1661,7 +1672,7 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
|
||||
//
|
||||
// 콤보핵 및 스피드핵 체크
|
||||
//
|
||||
if (pinfo->bFunc == FUNC_COMBO && g_bCheckMultiHack)
|
||||
if (pinfo->bFunc == FUNC_COMBO && g_bCheckMultiHack && enforceCombatValidation)
|
||||
{
|
||||
CheckComboHack(ch, pinfo->bArg, pinfo->dwTime, CheckSpeedHack); // 콤보 체크
|
||||
}
|
||||
@@ -1686,7 +1697,7 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
|
||||
const int MASK_SKILL_MOTION = 0x7F;
|
||||
unsigned int motion = pinfo->bFunc & MASK_SKILL_MOTION;
|
||||
|
||||
if (!ch->IsUsableSkillMotion(motion))
|
||||
if (enforceCombatValidation && !ch->IsUsableSkillMotion(motion))
|
||||
{
|
||||
const char* name = ch->GetName();
|
||||
unsigned int job = ch->GetJob();
|
||||
@@ -1875,6 +1886,7 @@ void CInputMain::Attack(LPCHARACTER ch, const uint16_t header, const char* data)
|
||||
int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiBytes)
|
||||
{
|
||||
const TPacketCGSyncPosition* pinfo = reinterpret_cast<const TPacketCGSyncPosition*>( c_pcData );
|
||||
const bool enforceMovementValidation = AntiCheatMovementValidationEnabled();
|
||||
|
||||
if (uiBytes < pinfo->length)
|
||||
return -1;
|
||||
@@ -1901,7 +1913,7 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
|
||||
|
||||
static const int nCountLimit = 16;
|
||||
|
||||
if( iCount > nCountLimit )
|
||||
if( enforceMovementValidation && iCount > nCountLimit )
|
||||
{
|
||||
//LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
|
||||
char szDetail[64];
|
||||
@@ -1939,7 +1951,7 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
|
||||
continue;
|
||||
}
|
||||
|
||||
if (victim->IsSyncOwnerLocked() && !victim->IsSyncOwnerLockedFor(ch))
|
||||
if (enforceMovementValidation && victim->IsSyncOwnerLocked() && !victim->IsSyncOwnerLockedFor(ch))
|
||||
{
|
||||
char szDetail[192];
|
||||
snprintf(szDetail,
|
||||
@@ -1965,7 +1977,7 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
|
||||
// 2500 : 스킬 proto에서 가장 사거리가 긴 스킬의 사거리, 또는 활의 사거리
|
||||
// a = POINT_BOW_DISTANCE 값... 인데 실제로 사용하는 값인지는 잘 모르겠음. 아이템이나 포션, 스킬, 퀘스트에는 없는데...
|
||||
// 그래도 혹시나 하는 마음에 버퍼로 사용할 겸해서 1000.f 로 둠...
|
||||
if (fDistWithSyncOwner > fLimitDistWithSyncOwner)
|
||||
if (enforceMovementValidation && fDistWithSyncOwner > fLimitDistWithSyncOwner)
|
||||
{
|
||||
// g_iSyncHackLimitCount번 까지는 봐줌.
|
||||
if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
|
||||
@@ -2011,7 +2023,7 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
|
||||
|
||||
// SyncPosition을 악용하여 타유저를 이상한 곳으로 보내는 핵 방어하기 위하여,
|
||||
// 같은 유저를 g_lValidSyncInterval ms 이내에 다시 SyncPosition하려고 하면 핵으로 간주.
|
||||
if (tvDiff->tv_sec == 0 && tvDiff->tv_usec < g_lValidSyncInterval)
|
||||
if (enforceMovementValidation && tvDiff->tv_sec == 0 && tvDiff->tv_usec < g_lValidSyncInterval)
|
||||
{
|
||||
// g_iSyncHackLimitCount번 까지는 봐줌.
|
||||
if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
|
||||
@@ -2049,7 +2061,7 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( fDist > 25.0f )
|
||||
else if( enforceMovementValidation && fDist > 25.0f )
|
||||
{
|
||||
char szDetail[192];
|
||||
snprintf(szDetail,
|
||||
@@ -2103,7 +2115,7 @@ void CInputMain::FlyTarget(LPCHARACTER ch, const char * pcData, uint16_t wHeader
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == victim)
|
||||
if (AntiCheatCombatValidationEnabled() && ch == victim)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "target=%u x=%ld y=%ld", p->dwTargetVID, static_cast<long>(p->x), static_cast<long>(p->y));
|
||||
@@ -2115,13 +2127,17 @@ void CInputMain::FlyTarget(LPCHARACTER ch, const char * pcData, uint16_t wHeader
|
||||
{
|
||||
case CHAR_TYPE_WARP:
|
||||
case CHAR_TYPE_GOTO:
|
||||
ch->RecordAntiCheatViolation("FLY_TARGET_INVALID", 6, victim->GetName(), true);
|
||||
return;
|
||||
if (AntiCheatCombatValidationEnabled())
|
||||
{
|
||||
ch->RecordAntiCheatViolation("FLY_TARGET_INVALID", 6, victim->GetName(), true);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int distance = 0;
|
||||
int maxDistance = 0;
|
||||
if (!battle_ranged_target_valid(ch, victim, 0, &distance, &maxDistance))
|
||||
if (AntiCheatCombatValidationEnabled() && !battle_ranged_target_valid(ch, victim, 0, &distance, &maxDistance))
|
||||
{
|
||||
char szDetail[160];
|
||||
snprintf(szDetail,
|
||||
|
||||
@@ -223,6 +223,16 @@ namespace
|
||||
g_wAuthMasterPort,
|
||||
test_server
|
||||
);
|
||||
|
||||
sys_log(0,
|
||||
"[STARTUP] anti_cheat enabled=%s disconnect=%s combat=%s movement=%s action=%s telemetry=%s",
|
||||
BoolState(AntiCheatEnabled()),
|
||||
BoolState(AntiCheatDisconnectEnabled()),
|
||||
BoolState(AntiCheatCombatValidationEnabled()),
|
||||
BoolState(AntiCheatMovementValidationEnabled()),
|
||||
BoolState(AntiCheatActionValidationEnabled()),
|
||||
BoolState(AntiCheatBehaviorTelemetryEnabled())
|
||||
);
|
||||
}
|
||||
|
||||
struct SendDisconnectFunc
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace mining
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (load->GetMapIndex() != ch->GetMapIndex())
|
||||
if (AntiCheatActionValidationEnabled() && load->GetMapIndex() != ch->GetMapIndex())
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -381,7 +381,7 @@ namespace mining
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ch->CanMove() || !ch->CanHandleItem())
|
||||
if (AntiCheatActionValidationEnabled() && (!ch->CanMove() || !ch->CanHandleItem()))
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail,
|
||||
@@ -395,7 +395,7 @@ namespace mining
|
||||
}
|
||||
|
||||
const int iDistance = DISTANCE_APPROX(ch->GetX() - load->GetX(), ch->GetY() - load->GetY());
|
||||
if (iDistance > MINING_COMPLETE_MAX_DISTANCE)
|
||||
if (AntiCheatActionValidationEnabled() && iDistance > MINING_COMPLETE_MAX_DISTANCE)
|
||||
{
|
||||
char szDetail[128];
|
||||
snprintf(szDetail, sizeof(szDetail), "target=%u distance=%d max=%d", load->GetVID(), iDistance, MINING_COMPLETE_MAX_DISTANCE);
|
||||
|
||||
Reference in New Issue
Block a user