From 563e8989427d71c58276c6452d7766554d17d624 Mon Sep 17 00:00:00 2001 From: server Date: Thu, 16 Apr 2026 12:50:36 +0200 Subject: [PATCH] Add fishing bot-pattern telemetry --- src/game/char.cpp | 6 +++++ src/game/char.h | 6 +++++ src/game/fishing.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/src/game/char.cpp b/src/game/char.cpp index a3f0538..94e124e 100644 --- a/src/game/char.cpp +++ b/src/game/char.cpp @@ -144,6 +144,12 @@ void CHARACTER::Initialize() m_iFishingWindowCount = 0; m_lFishingStartX = 0; m_lFishingStartY = 0; + m_dwFishingPatternWindowStart = 0; + m_iFishingPatternSampleCount = 0; + m_iFishingPatternMinReactionMs = 0; + m_iFishingPatternMaxReactionMs = 0; + m_iFishingPatternLastReactionMs = 0; + m_iFishingPatternTightSequence = 0; m_dwMiningWindowStart = 0; m_iMiningWindowCount = 0; diff --git a/src/game/char.h b/src/game/char.h index 9a7b56c..c4f143f 100644 --- a/src/game/char.h +++ b/src/game/char.h @@ -2056,6 +2056,12 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider int m_iFishingWindowCount; long m_lFishingStartX; long m_lFishingStartY; + DWORD m_dwFishingPatternWindowStart; + int m_iFishingPatternSampleCount; + int m_iFishingPatternMinReactionMs; + int m_iFishingPatternMaxReactionMs; + int m_iFishingPatternLastReactionMs; + int m_iFishingPatternTightSequence; DWORD m_dwMiningWindowStart; int m_iMiningWindowCount; void ClearPMCounter(void) { m_iPMCounter = 0; } diff --git a/src/game/fishing.cpp b/src/game/fishing.cpp index a3cd256..b909895 100644 --- a/src/game/fishing.cpp +++ b/src/game/fishing.cpp @@ -69,6 +69,68 @@ namespace fishing FISHING_LIMIT_APPEAR, }; + namespace + { + constexpr DWORD kFishingPatternWindowMs = 12 * 60 * 1000; + constexpr int kFishingPatternMinSamples = 8; + constexpr int kFishingPatternMaxSpreadMs = 120; + constexpr int kFishingPatternTightDeltaMs = 80; + + void ResetFishingPatternWindow(LPCHARACTER ch, DWORD now, int reactionMs) + { + ch->m_dwFishingPatternWindowStart = now; + ch->m_iFishingPatternSampleCount = 1; + ch->m_iFishingPatternMinReactionMs = reactionMs; + ch->m_iFishingPatternMaxReactionMs = reactionMs; + ch->m_iFishingPatternLastReactionMs = reactionMs; + ch->m_iFishingPatternTightSequence = 1; + } + + void ObserveFishingReaction(LPCHARACTER ch, int reactionMs, bool success) + { + if (!ch || reactionMs <= 0 || reactionMs > 6000) + return; + + const DWORD now = get_dword_time(); + if (0 == ch->m_dwFishingPatternWindowStart || now - ch->m_dwFishingPatternWindowStart >= kFishingPatternWindowMs) + { + ResetFishingPatternWindow(ch, now, reactionMs); + return; + } + + ++ch->m_iFishingPatternSampleCount; + ch->m_iFishingPatternMinReactionMs = MIN(ch->m_iFishingPatternMinReactionMs, reactionMs); + ch->m_iFishingPatternMaxReactionMs = MAX(ch->m_iFishingPatternMaxReactionMs, reactionMs); + + if (abs(reactionMs - ch->m_iFishingPatternLastReactionMs) <= kFishingPatternTightDeltaMs) + ++ch->m_iFishingPatternTightSequence; + else + ch->m_iFishingPatternTightSequence = 1; + + ch->m_iFishingPatternLastReactionMs = reactionMs; + + const int spread = ch->m_iFishingPatternMaxReactionMs - ch->m_iFishingPatternMinReactionMs; + if (ch->m_iFishingPatternSampleCount < kFishingPatternMinSamples || spread > kFishingPatternMaxSpreadMs) + return; + + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "samples=%d spread=%d last=%d tight_seq=%d success=%d", + ch->m_iFishingPatternSampleCount, + spread, + reactionMs, + ch->m_iFishingPatternTightSequence, + success ? 1 : 0); + ch->RecordAntiCheatViolation("FISHING_PATTERN", + ch->m_iFishingPatternTightSequence >= 5 ? 4 : 2, + szDetail, + spread <= 80); + + ResetFishingPatternWindow(ch, now, reactionMs); + } + } + int aFishingTime[FISHING_TIME_COUNT][MAX_FISHING_TIME_COUNT] = { { 0, 0, 0, 0, 0, 2, 4, 8, 12, 16, 20, 22, 25, 30, 50, 80, 50, 30, 25, 22, 20, 16, 12, 8, 4, 2, 0, 0, 0, 0, 0 }, @@ -618,6 +680,7 @@ void Take(fishing_event_info* info, LPCHARACTER ch) long ms = (long) ((get_dword_time() - info->hang_time)); DWORD item_vnum = 0; int ret = Compute(info->fish_id, ms, &item_vnum, GetFishingLevel(ch)); + ObserveFishingReaction(ch, ms, ret == 0); //if (test_server) //ch->ChatPacket(CHAT_TYPE_INFO, "%d ms", ms);