Add combat target reacquire telemetry
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 14:31:37 +02:00
parent c4e16dd6a5
commit acf7038abb
3 changed files with 136 additions and 0 deletions

View File

@@ -161,6 +161,16 @@ void CHARACTER::Initialize()
m_iFishingPatternTightSequence = 0;
m_dwMiningWindowStart = 0;
m_iMiningWindowCount = 0;
m_dwCombatPatternWindowStart = 0;
m_dwLastCombatPatternTime = 0;
m_dwLastCombatPatternVictimVID = 0;
m_lLastCombatPatternVictimX = 0;
m_lLastCombatPatternVictimY = 0;
m_iCombatPatternSampleCount = 0;
m_iCombatPatternMinIntervalMs = 0;
m_iCombatPatternMaxIntervalMs = 0;
m_iCombatPatternMinStep = 0;
m_iCombatPatternMaxStep = 0;
m_iLastPMPulse = 0;
m_iPMCounter = 0;

View File

@@ -2073,6 +2073,16 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
int m_iFishingPatternTightSequence;
DWORD m_dwMiningWindowStart;
int m_iMiningWindowCount;
DWORD m_dwCombatPatternWindowStart;
DWORD m_dwLastCombatPatternTime;
DWORD m_dwLastCombatPatternVictimVID;
long m_lLastCombatPatternVictimX;
long m_lLastCombatPatternVictimY;
int m_iCombatPatternSampleCount;
int m_iCombatPatternMinIntervalMs;
int m_iCombatPatternMaxIntervalMs;
int m_iCombatPatternMinStep;
int m_iCombatPatternMaxStep;
void ClearPMCounter(void) { m_iPMCounter = 0; }
void IncreasePMCounter(void) { m_iPMCounter++; }
void SetLastPMPulse(void);

View File

@@ -41,6 +41,119 @@
#include <random>
#include <algorithm>
namespace
{
constexpr DWORD kCombatPatternWindowMs = 10 * 60 * 1000;
constexpr int kCombatPatternMinSamples = 10;
constexpr int kCombatPatternMaxIntervalMs = 8000;
constexpr int kCombatPatternMaxIntervalSpreadMs = 280;
constexpr int kCombatPatternMaxStepSpread = 480;
constexpr int kCombatPatternMinRouteStep = 250;
void ResetCombatPatternWindow(LPCHARACTER ch, LPCHARACTER victim, DWORD now)
{
if (!ch)
return;
ch->m_dwCombatPatternWindowStart = now;
ch->m_dwLastCombatPatternTime = now;
ch->m_dwLastCombatPatternVictimVID = victim ? victim->GetVID() : 0;
ch->m_lLastCombatPatternVictimX = victim ? victim->GetX() : 0;
ch->m_lLastCombatPatternVictimY = victim ? victim->GetY() : 0;
ch->m_iCombatPatternSampleCount = 0;
ch->m_iCombatPatternMinIntervalMs = 0;
ch->m_iCombatPatternMaxIntervalMs = 0;
ch->m_iCombatPatternMinStep = 0;
ch->m_iCombatPatternMaxStep = 0;
}
void ObserveCombatTargetPattern(LPCHARACTER ch, LPCHARACTER victim, DWORD now)
{
if (!ch || !victim || !ch->IsPC())
return;
if (!victim->IsMonster() && !victim->IsStone())
return;
if (0 == ch->m_dwCombatPatternWindowStart || now - ch->m_dwCombatPatternWindowStart >= kCombatPatternWindowMs)
{
ResetCombatPatternWindow(ch, victim, now);
return;
}
if (0 == ch->m_dwLastCombatPatternVictimVID)
{
ResetCombatPatternWindow(ch, victim, now);
return;
}
if (ch->m_dwLastCombatPatternVictimVID == victim->GetVID())
{
ch->m_dwLastCombatPatternTime = now;
ch->m_lLastCombatPatternVictimX = victim->GetX();
ch->m_lLastCombatPatternVictimY = victim->GetY();
return;
}
const int iIntervalMs = static_cast<int>(now - ch->m_dwLastCombatPatternTime);
const int iStep = DISTANCE_APPROX(victim->GetX() - ch->m_lLastCombatPatternVictimX,
victim->GetY() - ch->m_lLastCombatPatternVictimY);
ch->m_dwLastCombatPatternTime = now;
ch->m_dwLastCombatPatternVictimVID = victim->GetVID();
ch->m_lLastCombatPatternVictimX = victim->GetX();
ch->m_lLastCombatPatternVictimY = victim->GetY();
if (iIntervalMs <= 0 || iIntervalMs > kCombatPatternMaxIntervalMs)
{
ResetCombatPatternWindow(ch, victim, now);
return;
}
if (0 == ch->m_iCombatPatternSampleCount)
{
ch->m_iCombatPatternSampleCount = 1;
ch->m_iCombatPatternMinIntervalMs = iIntervalMs;
ch->m_iCombatPatternMaxIntervalMs = iIntervalMs;
ch->m_iCombatPatternMinStep = iStep;
ch->m_iCombatPatternMaxStep = iStep;
return;
}
++ch->m_iCombatPatternSampleCount;
ch->m_iCombatPatternMinIntervalMs = MIN(ch->m_iCombatPatternMinIntervalMs, iIntervalMs);
ch->m_iCombatPatternMaxIntervalMs = MAX(ch->m_iCombatPatternMaxIntervalMs, iIntervalMs);
ch->m_iCombatPatternMinStep = MIN(ch->m_iCombatPatternMinStep, iStep);
ch->m_iCombatPatternMaxStep = MAX(ch->m_iCombatPatternMaxStep, iStep);
if (ch->m_iCombatPatternSampleCount < kCombatPatternMinSamples)
return;
const int iIntervalSpread = ch->m_iCombatPatternMaxIntervalMs - ch->m_iCombatPatternMinIntervalMs;
const int iStepSpread = ch->m_iCombatPatternMaxStep - ch->m_iCombatPatternMinStep;
if (iIntervalSpread > kCombatPatternMaxIntervalSpreadMs ||
iStepSpread > kCombatPatternMaxStepSpread ||
ch->m_iCombatPatternMaxStep < kCombatPatternMinRouteStep)
return;
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"samples=%d interval=%d..%d step=%d..%d target=%u",
ch->m_iCombatPatternSampleCount,
ch->m_iCombatPatternMinIntervalMs,
ch->m_iCombatPatternMaxIntervalMs,
ch->m_iCombatPatternMinStep,
ch->m_iCombatPatternMaxStep,
victim->GetVID());
ch->RecordAntiCheatViolation("TARGET_REACQUIRE_PATTERN",
iIntervalSpread <= 140 && iStepSpread <= 280 ? 2 : 1,
szDetail);
ResetCombatPatternWindow(ch, victim, now);
}
}
DWORD AdjustExpByLevel(const LPCHARACTER ch, const DWORD exp)
{
if (PLAYER_EXP_TABLE_MAX < ch->GetLevel())
@@ -271,6 +384,9 @@ bool CHARACTER::Attack(LPCHARACTER pkVictim, BYTE bType)
// sys_log(0, "%s Attack %s type %u ret %d", GetName(), pkVictim->GetName(), bType, iRet);
if (iRet == BATTLE_DAMAGE || iRet == BATTLE_DEAD)
{
if (IsPC() && 0 == bType)
ObserveCombatTargetPattern(this, pkVictim, dwCurrentTime);
OnMove(true);
pkVictim->OnMove();