Validate ranged targets server-side
Some checks failed
build / Linux asan (push) Has been cancelled
build / Linux release (push) Has been cancelled
build / FreeBSD build (push) Has been cancelled

This commit is contained in:
server
2026-04-16 11:59:26 +02:00
parent eea1875d62
commit 2e6140abce
4 changed files with 113 additions and 3 deletions

View File

@@ -21,6 +21,7 @@
#include "sectree.h"
#include "ani.h"
#include "locale_service.h"
#include "skill.h"
int battle_hit(LPCHARACTER ch, LPCHARACTER victim, int & iRetDam);
@@ -60,6 +61,51 @@ bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim)
return battle_distance_valid_by_xy(ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY());
}
int battle_get_ranged_target_max_distance(LPCHARACTER ch, BYTE attackType)
{
int maxDistance = 3500;
if (!ch)
return maxDistance;
if (attackType == 0)
{
maxDistance += MAX(0, ch->GetPoint(POINT_BOW_DISTANCE)) * 100;
return maxDistance;
}
if (attackType > 1)
{
if (CSkillProto* pkSk = CSkillManager::instance().Get(attackType))
{
if (pkSk->dwTargetRange > 0)
return pkSk->dwTargetRange + 300;
}
}
if (!ch->IsPC())
maxDistance = MAX(maxDistance, static_cast<int>(ch->GetMobAttackRange() * 1.5f));
return maxDistance;
}
bool battle_ranged_target_valid(LPCHARACTER ch, LPCHARACTER victim, BYTE attackType, int* outDistance, int* outMaxDistance)
{
if (!ch || !victim)
return false;
const int distance = DISTANCE_APPROX(ch->GetX() - victim->GetX(), ch->GetY() - victim->GetY());
const int maxDistance = battle_get_ranged_target_max_distance(ch, attackType);
if (outDistance)
*outDistance = distance;
if (outMaxDistance)
*outMaxDistance = maxDistance;
return distance <= maxDistance;
}
bool timed_event_cancel(LPCHARACTER ch)
{
if (ch->m_pkTimedEvent)

View File

@@ -24,6 +24,8 @@ extern void battle_end(LPCHARACTER ch);
extern bool battle_distance_valid_by_xy(long x, long y, long tx, long ty);
extern bool battle_distance_valid(LPCHARACTER ch, LPCHARACTER victim);
extern int battle_get_ranged_target_max_distance(LPCHARACTER ch, BYTE attackType);
extern bool battle_ranged_target_valid(LPCHARACTER ch, LPCHARACTER victim, BYTE attackType, int* outDistance = nullptr, int* outMaxDistance = nullptr);
extern int battle_count_attackers(LPCHARACTER ch);
extern void NormalAttackAffect(LPCHARACTER pkAttacker, LPCHARACTER pkVictim);

View File

@@ -2990,10 +2990,23 @@ class CFuncShoot
if (!battle_is_attackable(m_me, pkVictim))
return;
if (m_me->IsNPC())
int rangedDistance = 0;
int rangedLimit = 0;
if (!battle_ranged_target_valid(m_me, pkVictim, m_bType, &rangedDistance, &rangedLimit))
{
if (DISTANCE_APPROX(m_me->GetX() - pkVictim->GetX(), m_me->GetY() - pkVictim->GetY()) > 5000)
return;
if (m_me->IsPC())
{
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"type=%u distance=%d max=%d victim=%u",
m_bType,
rangedDistance,
rangedLimit,
pkVictim->GetVID());
m_me->RecordAntiCheatViolation("RANGED_RANGE", rangedDistance > rangedLimit + 800 ? 18 : 8, szDetail, rangedDistance > rangedLimit + 800);
}
return;
}
LPITEM pkBow, pkArrow;
@@ -3275,6 +3288,12 @@ void CHARACTER::FlyTarget(DWORD dwTargetVID, long x, long y, uint16_t wHeader)
LPCHARACTER pkVictim = CHARACTER_MANAGER::instance().Find(dwTargetVID);
TPacketGCFlyTargeting pack;
if (IsPC() && !pkVictim)
{
sys_log(1, "FlyTarget reject empty player target %s vid %u x %ld y %ld", GetName(), dwTargetVID, x, y);
return;
}
//pack.header = GC::FLY_TARGETING;
pack.header = (wHeader == CG::FLY_TARGETING) ? GC::FLY_TARGETING : GC::ADD_FLY_TARGETING;
pack.length = sizeof(pack);

View File

@@ -2092,6 +2092,49 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
void CInputMain::FlyTarget(LPCHARACTER ch, const char * pcData, uint16_t wHeader)
{
TPacketCGFlyTargeting * p = (TPacketCGFlyTargeting *) pcData;
if (ch && ch->IsPC())
{
LPCHARACTER victim = CHARACTER_MANAGER::instance().Find(p->dwTargetVID);
if (!victim)
{
sys_log(1, "FLY_TARGET_INVALID: %s target=%u missing", ch->GetName(), p->dwTargetVID);
return;
}
if (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));
ch->RecordAntiCheatViolation("FLY_TARGET_INVALID", 4, szDetail);
return;
}
switch (victim->GetCharType())
{
case CHAR_TYPE_WARP:
case CHAR_TYPE_GOTO:
ch->RecordAntiCheatViolation("FLY_TARGET_INVALID", 6, victim->GetName(), true);
return;
}
int distance = 0;
int maxDistance = 0;
if (!battle_ranged_target_valid(ch, victim, 0, &distance, &maxDistance))
{
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"target=%s distance=%d max=%d",
victim->GetName(),
distance,
maxDistance);
ch->RecordAntiCheatViolation("FLY_TARGET_RANGE", distance > maxDistance + 800 ? 12 : 4, szDetail, distance > maxDistance + 800);
return;
}
}
ch->FlyTarget(p->dwTargetVID, p->x, p->y, wHeader);
}