11--[[ detectors/bhop.lua
22 Detects scripted bunnyhops by counting consecutive "perfect" jumps.
33 Tracks ground contact ticks on landing, then scores on ground→air transition.
4+ Only runs while airborne or in the brief post-landing ground window (no idle ground work).
45]]
56
67local Constants = require (" Cheater_Detection.Core.constants" )
@@ -24,6 +25,11 @@ local function isBhopEnabled()
2425 return adv and adv .Bhop == true
2526end
2627
28+ local function clearBhopState (id )
29+ playerData [id ] = nil
30+ end
31+
32+ --- Airborne this tick, or still counting landing ticks after air (inAir / groundTicks only set during chains).
2733function Bhop .HasWork (playerState )
2834 if not isBhopEnabled () then
2935 return false
@@ -34,10 +40,18 @@ function Bhop.HasWork(playerState)
3440 if (playerState .flags & Constants .Flags .CHEATER ) ~= 0 then
3541 return false
3642 end
37- if playerState .pdata and (playerState .pdata .isDormant or not playerState .pdata .isAlive ) then
43+ local pdata = playerState .pdata
44+ if not pdata or pdata .isDormant or not pdata .isAlive then
45+ return false
46+ end
47+ if pdata .onGround == nil then
3848 return false
3949 end
40- return true
50+ if not pdata .onGround then
51+ return true
52+ end
53+ local data = playerData [playerState .id ]
54+ return data ~= nil and (data .inAir == true or data .groundTicks ~= nil )
4155end
4256
4357function Bhop .ProcessPlayer (playerState )
@@ -51,22 +65,44 @@ function Bhop.ProcessPlayer(playerState)
5165
5266 local id = playerState .id
5367 local pdata = playerState .pdata
54-
5568 local onGround = pdata .onGround
5669
57- if onGround == nil then
70+ if id == PlayerCache . GetLocalID () and not Common . IsDebugEnabled () then
5871 return
5972 end
6073
61- if id == PlayerCache .GetLocalID () and not Common .IsDebugEnabled () then
74+ if onGround then
75+ local data = playerData [id ]
76+ if not data then
77+ return
78+ end
79+
80+ if data .inAir then
81+ data .inAir = nil
82+ data .groundTicks = 1
83+ return
84+ end
85+
86+ if not data .groundTicks then
87+ return
88+ end
89+
90+ data .groundTicks = data .groundTicks + 1
91+ if data .groundTicks > MAX_GROUND_TICKS then
92+ clearBhopState (id )
93+ end
6294 return
6395 end
6496
97+ -- Airborne
6598 local data = playerData [id ]
99+ if data and data .inAir then
100+ return
101+ end
102+
103+ local groundTicks = (data and data .groundTicks ) or 0
66104 if not data then
67105 data = {
68- wasOnGround = false ,
69- groundTicks = 0 ,
70106 consecutivePerfects = 0 ,
71107 lastJumpTime = nil ,
72108 }
@@ -78,44 +114,37 @@ function Bhop.ProcessPlayer(playerState)
78114 data .consecutivePerfects = 0
79115 end
80116
81- if onGround then
82- data .groundTicks = data .groundTicks + 1
83- data .wasOnGround = true
84- else
85- if data .wasOnGround then
86- data .lastJumpTime = now
87- if data .groundTicks >= 0 and data .groundTicks <= MAX_GROUND_TICKS then
88- data .consecutivePerfects = data .consecutivePerfects + 1
89-
90- if data .consecutivePerfects >= Constants .BHOP_MIN_CONSECUTIVE_SUCCESS then
91- local lastEvidence = evidenceCooldowns [id ] or 0
92- if (now - lastEvidence ) >= BHOP_EVIDENCE_COOLDOWN_S then
93- Evidence .AddEvidence (id , " bhop" , BHOP_EVIDENCE_WEIGHT )
94- evidenceCooldowns [id ] = now
95- if Common .IsDebugEnabled () then
96- print (string.format (
97- " [Bhop] %s perfect jump #%d (ground_ticks=%d) evidence +%.1f" ,
98- id , data .consecutivePerfects , data .groundTicks , BHOP_EVIDENCE_WEIGHT ))
99- end
100- end
117+ data .lastJumpTime = now
118+ if groundTicks >= 0 and groundTicks <= MAX_GROUND_TICKS then
119+ data .consecutivePerfects = data .consecutivePerfects + 1
120+
121+ if data .consecutivePerfects >= Constants .BHOP_MIN_CONSECUTIVE_SUCCESS then
122+ local lastEvidence = evidenceCooldowns [id ] or 0
123+ if (now - lastEvidence ) >= BHOP_EVIDENCE_COOLDOWN_S then
124+ Evidence .AddEvidence (id , " bhop" , BHOP_EVIDENCE_WEIGHT )
125+ evidenceCooldowns [id ] = now
126+ if Common .IsDebugEnabled () then
127+ print (string.format (
128+ " [Bhop] %s perfect jump #%d (ground_ticks=%d) evidence +%.1f" ,
129+ id , data .consecutivePerfects , groundTicks , BHOP_EVIDENCE_WEIGHT ))
101130 end
102- else
103- data .consecutivePerfects = 0
104131 end
105-
106- data .wasOnGround = false
107- data .groundTicks = 0
108132 end
133+ else
134+ data .consecutivePerfects = 0
109135 end
136+
137+ data .groundTicks = nil
138+ data .inAir = true
110139end
111140
112141Events .Subscribe (" OnPlayerDisconnect" , function (id )
113- playerData [ id ] = nil
142+ clearBhopState ( id )
114143 evidenceCooldowns [id ] = nil
115144end )
116145
117146Events .Subscribe (" OnPlayerRemoved" , function (id )
118- playerData [ id ] = nil
147+ clearBhopState ( id )
119148 evidenceCooldowns [id ] = nil
120149end )
121150
0 commit comments