Add pickup route 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:18:50 +02:00
parent 563e898942
commit c4e16dd6a5
3 changed files with 107 additions and 0 deletions

View File

@@ -140,6 +140,15 @@ void CHARACTER::Initialize()
LastDropTime = 0;
m_dwPickupWindowStart = 0;
m_iPickupWindowCount = 0;
m_dwPickupPatternWindowStart = 0;
m_dwLastPickupPatternTime = 0;
m_lLastPickupPatternX = 0;
m_lLastPickupPatternY = 0;
m_iPickupPatternSampleCount = 0;
m_iPickupPatternMinIntervalMs = 0;
m_iPickupPatternMaxIntervalMs = 0;
m_iPickupPatternMinStep = 0;
m_iPickupPatternMaxStep = 0;
m_dwFishingWindowStart = 0;
m_iFishingWindowCount = 0;
m_lFishingStartX = 0;

View File

@@ -2052,6 +2052,15 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
int CountDrops;
DWORD m_dwPickupWindowStart;
int m_iPickupWindowCount;
DWORD m_dwPickupPatternWindowStart;
DWORD m_dwLastPickupPatternTime;
long m_lLastPickupPatternX;
long m_lLastPickupPatternY;
int m_iPickupPatternSampleCount;
int m_iPickupPatternMinIntervalMs;
int m_iPickupPatternMaxIntervalMs;
int m_iPickupPatternMinStep;
int m_iPickupPatternMaxStep;
DWORD m_dwFishingWindowStart;
int m_iFishingWindowCount;
long m_lFishingStartX;

View File

@@ -46,6 +46,12 @@
namespace
{
constexpr int kRefineNpcMaxDistance = 2000;
constexpr DWORD kPickupPatternWindowMs = 8 * 60 * 1000;
constexpr int kPickupPatternMinSamples = 12;
constexpr int kPickupPatternMaxIntervalSpreadMs = 180;
constexpr int kPickupPatternMaxStepSpread = 220;
constexpr int kPickupPatternMinRouteStep = 250;
constexpr int kPickupPatternMaxIntervalMs = 15000;
bool ValidateStoredRefineNpc(LPCHARACTER ch, DWORD dwRefineNpcVID, const char* action)
{
@@ -92,6 +98,84 @@ namespace
return true;
}
void ResetPickupPatternWindow(LPCHARACTER ch, DWORD now, long x, long y)
{
ch->m_dwPickupPatternWindowStart = now;
ch->m_dwLastPickupPatternTime = now;
ch->m_lLastPickupPatternX = x;
ch->m_lLastPickupPatternY = y;
ch->m_iPickupPatternSampleCount = 0;
ch->m_iPickupPatternMinIntervalMs = 0;
ch->m_iPickupPatternMaxIntervalMs = 0;
ch->m_iPickupPatternMinStep = 0;
ch->m_iPickupPatternMaxStep = 0;
}
void ObservePickupPattern(LPCHARACTER ch, DWORD now, long x, long y)
{
if (!ch)
return;
if (0 == ch->m_dwPickupPatternWindowStart || now - ch->m_dwPickupPatternWindowStart >= kPickupPatternWindowMs)
{
ResetPickupPatternWindow(ch, now, x, y);
return;
}
const int iIntervalMs = static_cast<int>(now - ch->m_dwLastPickupPatternTime);
const int iStep = DISTANCE_APPROX(x - ch->m_lLastPickupPatternX, y - ch->m_lLastPickupPatternY);
ch->m_dwLastPickupPatternTime = now;
ch->m_lLastPickupPatternX = x;
ch->m_lLastPickupPatternY = y;
if (iIntervalMs <= 0 || iIntervalMs > kPickupPatternMaxIntervalMs)
{
ResetPickupPatternWindow(ch, now, x, y);
return;
}
if (0 == ch->m_iPickupPatternSampleCount)
{
ch->m_iPickupPatternSampleCount = 1;
ch->m_iPickupPatternMinIntervalMs = iIntervalMs;
ch->m_iPickupPatternMaxIntervalMs = iIntervalMs;
ch->m_iPickupPatternMinStep = iStep;
ch->m_iPickupPatternMaxStep = iStep;
return;
}
++ch->m_iPickupPatternSampleCount;
ch->m_iPickupPatternMinIntervalMs = MIN(ch->m_iPickupPatternMinIntervalMs, iIntervalMs);
ch->m_iPickupPatternMaxIntervalMs = MAX(ch->m_iPickupPatternMaxIntervalMs, iIntervalMs);
ch->m_iPickupPatternMinStep = MIN(ch->m_iPickupPatternMinStep, iStep);
ch->m_iPickupPatternMaxStep = MAX(ch->m_iPickupPatternMaxStep, iStep);
if (ch->m_iPickupPatternSampleCount < kPickupPatternMinSamples)
return;
const int iIntervalSpread = ch->m_iPickupPatternMaxIntervalMs - ch->m_iPickupPatternMinIntervalMs;
const int iStepSpread = ch->m_iPickupPatternMaxStep - ch->m_iPickupPatternMinStep;
if (iIntervalSpread > kPickupPatternMaxIntervalSpreadMs || iStepSpread > kPickupPatternMaxStepSpread || ch->m_iPickupPatternMaxStep < kPickupPatternMinRouteStep)
return;
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"samples=%d interval=%d..%d step=%d..%d",
ch->m_iPickupPatternSampleCount,
ch->m_iPickupPatternMinIntervalMs,
ch->m_iPickupPatternMaxIntervalMs,
ch->m_iPickupPatternMinStep,
ch->m_iPickupPatternMaxStep);
ch->RecordAntiCheatViolation("PICKUP_PATTERN",
iIntervalSpread <= 100 && iStepSpread <= 140 ? 4 : 2,
szDetail,
iIntervalSpread <= 100 && iStepSpread <= 140);
ResetPickupPatternWindow(ch, now, x, y);
}
}
const int ITEM_BROKEN_METIN_VNUM = 28960;
@@ -5908,6 +5992,8 @@ bool CHARACTER::PickupItem(DWORD dwVID)
return false;
const DWORD dwNow = get_dword_time();
const long lPickupX = GetX();
const long lPickupY = GetY();
if (m_dwPickupWindowStart == 0 || dwNow - m_dwPickupWindowStart >= 1000)
{
@@ -5990,6 +6076,7 @@ bool CHARACTER::PickupItem(DWORD dwVID)
if (bCount == 0)
{
ItemGetPacket(item2->GetVnum(), item2->GetCount());
ObservePickupPattern(this, dwNow, lPickupX, lPickupY);
M2_DESTROY_ITEM(item);
if (item2->GetType() == ITEM_QUEST)
quest::CQuestManager::instance().PickupItem (GetPlayerID(), item2);
@@ -6038,6 +6125,7 @@ bool CHARACTER::PickupItem(DWORD dwVID)
}
//Motion(MOTION_PICKUP);
ObservePickupPattern(this, dwNow, lPickupX, lPickupY);
return true;
}
else if (item->HasOwnership())
@@ -6110,6 +6198,7 @@ bool CHARACTER::PickupItem(DWORD dwVID)
if (item->GetType() == ITEM_QUEST)
quest::CQuestManager::instance().PickupItem (owner->GetPlayerID(), item);
ObservePickupPattern(this, dwNow, lPickupX, lPickupY);
return true;
}