summaryrefslogtreecommitdiff
path: root/sourcemod/scripting/gokz-anticheat/bhop_tracking.sp
diff options
context:
space:
mode:
authoraura <nw@moneybot.cc>2026-02-17 23:42:09 +0100
committeraura <nw@moneybot.cc>2026-02-17 23:42:09 +0100
commit5e2eb7d67ae933b7566f1944d0bb7744da03d586 (patch)
tree054acff1113270a9cd07933df760f3768c1b6853 /sourcemod/scripting/gokz-anticheat/bhop_tracking.sp
parent341db13a008dc12bb22ceb50452d93d01476308c (diff)
move source stuff to its own folder
Diffstat (limited to 'sourcemod/scripting/gokz-anticheat/bhop_tracking.sp')
-rw-r--r--sourcemod/scripting/gokz-anticheat/bhop_tracking.sp336
1 files changed, 0 insertions, 336 deletions
diff --git a/sourcemod/scripting/gokz-anticheat/bhop_tracking.sp b/sourcemod/scripting/gokz-anticheat/bhop_tracking.sp
deleted file mode 100644
index 5607b07..0000000
--- a/sourcemod/scripting/gokz-anticheat/bhop_tracking.sp
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- Track player's jump inputs and whether they hit perfect
- bunnyhops for a number of their recent bunnyhops.
-*/
-
-
-
-// =====[ PUBLIC ]=====
-
-void PrintBhopCheckToChat(int client, int target)
-{
- GOKZ_PrintToChat(client, true,
- "{lime}%N {grey}[{lime}%d%%%% {grey}%t | {lime}%.2f {grey}%t]",
- target,
- RoundFloat(GOKZ_AC_GetPerfRatio(target, 20) * 100.0),
- "Perfs",
- GOKZ_AC_GetAverageJumpInputs(target, 20),
- "Average");
- GOKZ_PrintToChat(client, false,
- " {grey}%t - %s",
- "Pattern",
- GenerateScrollPattern(target, 20));
-}
-
-void PrintBhopCheckToConsole(int client, int target)
-{
- PrintToConsole(client,
- "%N [%d%% %t | %.2f %t]\n %t - %s",
- target,
- RoundFloat(GOKZ_AC_GetPerfRatio(target, 20) * 100.0),
- "Perfs",
- GOKZ_AC_GetAverageJumpInputs(target, 20),
- "Average",
- "Pattern",
- GenerateScrollPattern(target, 20, false));
-}
-
-// Generate 'scroll pattern'
-char[] GenerateScrollPattern(int client, int sampleSize = AC_MAX_BHOP_SAMPLES, bool colours = true)
-{
- char report[512];
- int maxIndex = IntMin(gI_BhopCount[client], sampleSize);
- bool[] perfs = new bool[maxIndex];
- GOKZ_AC_GetHitPerf(client, perfs, maxIndex);
- int[] jumpInputs = new int[maxIndex];
- GOKZ_AC_GetJumpInputs(client, jumpInputs, maxIndex);
-
- for (int i = 0; i < maxIndex; i++)
- {
- if (colours)
- {
- Format(report, sizeof(report), "%s%s%d ",
- report,
- perfs[i] ? "{green}" : "{default}",
- jumpInputs[i]);
- }
- else
- {
- Format(report, sizeof(report), "%s%d%s ",
- report,
- jumpInputs[i],
- perfs[i] ? "*" : "");
- }
- }
-
- TrimString(report);
-
- return report;
-}
-
-// Generate 'scroll pattern' report showing pre and post inputs instead
-char[] GenerateScrollPatternEx(int client, int sampleSize = AC_MAX_BHOP_SAMPLES)
-{
- char report[512];
- int maxIndex = IntMin(gI_BhopCount[client], sampleSize);
- bool[] perfs = new bool[maxIndex];
- GOKZ_AC_GetHitPerf(client, perfs, maxIndex);
- int[] jumpInputs = new int[maxIndex];
- GOKZ_AC_GetJumpInputs(client, jumpInputs, maxIndex);
- int[] preJumpInputs = new int[maxIndex];
- GOKZ_AC_GetPreJumpInputs(client, preJumpInputs, maxIndex);
- int[] postJumpInputs = new int[maxIndex];
- GOKZ_AC_GetPostJumpInputs(client, postJumpInputs, maxIndex);
-
- for (int i = 0; i < maxIndex; i++)
- {
- Format(report, sizeof(report), "%s(%d%s%d)",
- report,
- preJumpInputs[i],
- perfs[i] ? "*" : " ",
- postJumpInputs[i]);
- }
-
- TrimString(report);
-
- return report;
-}
-
-
-
-// =====[ EVENTS ]=====
-
-void OnClientPutInServer_BhopTracking(int client)
-{
- ResetBhopStats(client);
-}
-
-void OnPlayerRunCmdPost_BhopTracking(int client, int buttons, int cmdnum)
-{
- if (gCV_sv_autobunnyhopping.BoolValue)
- {
- return;
- }
-
- int nextIndex = NextIndex(gI_BhopIndex[client], AC_MAX_BHOP_SAMPLES);
-
- // Record buttons BEFORE checking for bhop
- RecordButtons(client, buttons);
-
- // If bhop was last tick, then record the pre bhop inputs.
- // Require two times the button sample size since the last
- // takeoff to avoid pre and post bhop input overlap.
- if (HitBhop(client, cmdnum)
- && cmdnum >= gI_BhopLastTakeoffCmdnum[client] + AC_MAX_BUTTON_SAMPLES * 2
- && gB_LastLandingWasValid[client])
- {
- gB_BhopHitPerf[client][nextIndex] = Movement_GetHitPerf(client);
- gI_BhopPreJumpInputs[client][nextIndex] = CountJumpInputs(client);
- gI_BhopLastRecordedBhopCmdnum[client] = cmdnum;
- gB_BhopPostJumpInputsPending[client] = true;
- gB_BindExceptionPending[client] = false;
- gB_BindExceptionPostPending[client] = false;
- }
-
- // Bind exception
- if (gB_BindExceptionPending[client] && cmdnum > Movement_GetLandingCmdNum(client) + AC_MAX_BHOP_GROUND_TICKS)
- {
- gB_BhopHitPerf[client][nextIndex] = false;
- gI_BhopPreJumpInputs[client][nextIndex] = -1; // Special value for binded jumps
- gI_BhopLastRecordedBhopCmdnum[client] = cmdnum;
- gB_BhopPostJumpInputsPending[client] = true;
- gB_BindExceptionPending[client] = false;
- gB_BindExceptionPostPending[client] = true;
- }
-
- // Record post bhop inputs once enough ticks have passed
- if (gB_BhopPostJumpInputsPending[client] && cmdnum == gI_BhopLastRecordedBhopCmdnum[client] + AC_MAX_BUTTON_SAMPLES)
- {
- gI_BhopPostJumpInputs[client][nextIndex] = CountJumpInputs(client);
- gB_BhopPostJumpInputsPending[client] = false;
- gI_BhopIndex[client] = nextIndex;
- gI_BhopCount[client]++;
- CheckForBhopMacro(client);
- gB_BindExceptionPostPending[client] = false;
- }
-
- // Record last jump takeoff time
- if (JustJumped(client, cmdnum))
- {
- gI_BhopLastTakeoffCmdnum[client] = cmdnum;
- gB_BindExceptionPending[client] = false;
- if (gB_BindExceptionPostPending[client])
- {
- gB_BhopPostJumpInputsPending[client] = false;
- gB_BindExceptionPostPending[client] = false;
- }
- }
-
- if (JustLanded(client, cmdnum))
- {
- // These conditions exist to reduce false positives.
-
- // Telehopping is when the player bunnyhops out of a teleport that has a
- // destination very close to the ground. This will, more than usual,
- // result in a perfect bunnyhop. This is alleviated by checking if the
- // player's origin was affected by a teleport last tick.
-
- // When a player is pressing up against a slope but not ascending it (e.g.
- // palm trees on kz_adv_cursedjourney), they will switch between on ground
- // and off ground frequently, which means that if they manage to jump, the
- // jump will be recorded as a perfect bunnyhop. To ignore this, we check
- // the jump is more than 1 tick duration.
-
- gB_LastLandingWasValid[client] = cmdnum - gI_LastOriginTeleportCmdNum[client] > 1
- && cmdnum - Movement_GetTakeoffCmdNum(client) > 1;
-
- // You can still crouch-bind VNL jumps and some people just don't know that
- // it doesn't work with the other modes in GOKZ. This can cause false positives
- // if the player uses the bind for bhops and mostly presses it too early or
- // exactly on time rather than too late. This is supposed to reduce those by
- // detecting jumps where you don't get a bhop and have exactly one jump input
- // before landing and none after landing. We require the one input to be right
- // before the jump to make it a lot harder to fake a binded jump when doing
- // a regular longjump.
- gB_BindExceptionPending[client] = (CountJumpInputs(client, AC_BINDEXCEPTION_SAMPLES) == 1 && CountJumpInputs(client, AC_MAX_BUTTON_SAMPLES) == 1);
- gB_BindExceptionPostPending[client] = false;
- }
-}
-
-
-
-// =====[ PRIVATE ]=====
-
-static void CheckForBhopMacro(int client)
-{
- if (GOKZ_AC_GetPerfCount(client, 19) == 19)
- {
- SuspectPlayer(client, ACReason_BhopHack, "High perf ratio", GenerateBhopBanStats(client, 19));
- }
- else if (GOKZ_AC_GetPerfCount(client, 30) >= 28)
- {
- SuspectPlayer(client, ACReason_BhopHack, "High perf ratio", GenerateBhopBanStats(client, 30));
- }
- else if (GOKZ_AC_GetPerfCount(client, 20) >= 16 && GOKZ_AC_GetAverageJumpInputs(client, 20) <= 2.0 + EPSILON)
- {
- SuspectPlayer(client, ACReason_BhopHack, "1's or 2's scroll pattern", GenerateBhopBanStats(client, 20));
- }
- else if (gI_BhopCount[client] >= 20 && GOKZ_AC_GetPerfCount(client, 20) >= 8
- && GOKZ_AC_GetAverageJumpInputs(client, 20) >= 19.0 - EPSILON)
- {
- SuspectPlayer(client, ACReason_BhopMacro, "High scroll pattern", GenerateBhopBanStats(client, 20));
- }
- else if (GOKZ_AC_GetPerfCount(client, 30) >= 10 && CheckForRepeatingJumpInputsCount(client, 25, 30) >= 14)
- {
- SuspectPlayer(client, ACReason_BhopMacro, "Repeating scroll pattern", GenerateBhopBanStats(client, 30));
- }
-}
-
-static char[] GenerateBhopBanStats(int client, int sampleSize)
-{
- char stats[512];
- FormatEx(stats, sizeof(stats),
- "Perfs: %d/%d, Average: %.2f, Scroll pattern: %s",
- GOKZ_AC_GetPerfCount(client, sampleSize),
- IntMin(gI_BhopCount[client], sampleSize),
- GOKZ_AC_GetAverageJumpInputs(client, sampleSize),
- GenerateScrollPatternEx(client, sampleSize));
- return stats;
-}
-
-/**
- * Returns -1, or the repeating input count if there if there is
- * an input count that repeats for more than the provided ratio.
- *
- * @param client Client index.
- * @param threshold Minimum frequency to be considered 'repeating'.
- * @param sampleSize Maximum recent bhop samples to include in calculation.
- * @return The repeating input, or else -1.
- */
-static int CheckForRepeatingJumpInputsCount(int client, int threshold, int sampleSize = AC_MAX_BHOP_SAMPLES)
-{
- int maxIndex = IntMin(gI_BhopCount[client], sampleSize);
- int[] jumpInputs = new int[maxIndex];
- GOKZ_AC_GetJumpInputs(client, jumpInputs, maxIndex);
- int maxJumpInputs = AC_MAX_BUTTON_SAMPLES + 1;
- int[] jumpInputsFrequency = new int[maxJumpInputs];
-
- // Count up all the in jump patterns
- for (int i = 0; i < maxIndex; i++)
- {
- // -1 is a binded jump, those are excluded
- if (jumpInputs[i] != -1)
- {
- jumpInputsFrequency[jumpInputs[i]]++;
- }
- }
-
- // Returns i if the given number of the sample size has the same jump input count
- for (int i = 1; i < maxJumpInputs; i++)
- {
- if (jumpInputsFrequency[i] >= threshold)
- {
- return i;
- }
- }
-
- return -1; // -1 if no repeating jump input found
-}
-
-// Reset the tracked bhop stats of the client
-static void ResetBhopStats(int client)
-{
- gI_ButtonCount[client] = 0;
- gI_ButtonsIndex[client] = 0;
- gI_BhopCount[client] = 0;
- gI_BhopIndex[client] = 0;
- gI_BhopLastTakeoffCmdnum[client] = 0;
- gI_BhopLastRecordedBhopCmdnum[client] = 0;
- gB_BhopPostJumpInputsPending[client] = false;
- gB_LastLandingWasValid[client] = false;
- gB_BindExceptionPending[client] = false;
- gB_BindExceptionPostPending[client] = false;
-}
-
-// Returns true if ther was a jump last tick and was within a number of ticks after landing
-static bool HitBhop(int client, int cmdnum)
-{
- return JustJumped(client, cmdnum) && Movement_GetTakeoffCmdNum(client) - Movement_GetLandingCmdNum(client) <= AC_MAX_BHOP_GROUND_TICKS;
-}
-
-static bool JustJumped(int client, int cmdnum)
-{
- return Movement_GetJumped(client) && Movement_GetTakeoffCmdNum(client) == cmdnum;
-}
-
-static bool JustLanded(int client, int cmdnum)
-{
- return Movement_GetLandingCmdNum(client) == cmdnum;
-}
-
-// Records current button inputs
-static void RecordButtons(int client, int buttons)
-{
- gI_ButtonsIndex[client] = NextIndex(gI_ButtonsIndex[client], AC_MAX_BUTTON_SAMPLES);
- gI_Buttons[client][gI_ButtonsIndex[client]] = buttons;
- gI_ButtonCount[client]++;
-}
-
-// Counts the number of times buttons went from !IN_JUMP to IN_JUMP
-static int CountJumpInputs(int client, int sampleSize = AC_MAX_BUTTON_SAMPLES)
-{
- int[] recentButtons = new int[sampleSize];
- SortByRecent(gI_Buttons[client], AC_MAX_BUTTON_SAMPLES, recentButtons, sampleSize, gI_ButtonsIndex[client]);
- int maxIndex = IntMin(gI_ButtonCount[client], sampleSize);
- int jumps = 0;
-
- for (int i = 0; i < maxIndex - 1; i++)
- {
- // If buttons went from !IN_JUMP to IN_JUMP
- if (!(recentButtons[i + 1] & IN_JUMP) && recentButtons[i] & IN_JUMP)
- {
- jumps++;
- }
- }
- return jumps;
-} \ No newline at end of file