Skip to content

Commit b3f3d29

Browse files
Cheater_Detection: update
1 parent 30081b5 commit b3f3d29

6 files changed

Lines changed: 270 additions & 47 deletions

File tree

Cheater_Detection/Core/Evidence_system.lua

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Evidence.Config = {
5555
Evidence_Tolerance = 50, -- Evidence threshold % (0–100) to mark as cheater
5656
MinWeightFloor = 0, -- Cannot decay below this
5757
AutoPriorityThreshold = 12.0, -- Evidence score threshold to trigger SUSPICIOUS flag
58-
ExploitAutoCheaterMin = 70, -- Very high so fake lag alone rarely goes CHEATER
58+
ExploitAutoCheaterMin = 70, -- Per-method % of cap; also marks CHEATER (see isExploitAtAutoCheaterMin)
5959
MethodScoreCaps = {
6060
fake_lag = 120.0, -- ~24 credits at 5 pts (stricter than old 60 cap)
6161
double_tap = 300.0, -- ~10 usages at DT_USAGE_WEIGHT (30) in double_tap.lua
@@ -242,6 +242,50 @@ local function getEvidenceScoreCap(evidence)
242242
return cap
243243
end
244244

245+
-- Cheater line uses strongest active method cap so a little fake_lag weight does not
246+
-- inflate the bar when double_tap is already near cap (sum cap would be 300+120=420).
247+
local function getEvidenceMaxMethodCap(evidence)
248+
local maxCap = 0
249+
if evidence and evidence.Reasons then
250+
for method, reason in pairs(evidence.Reasons) do
251+
if reason.Weight and reason.Weight > 0 then
252+
maxCap = math.max(maxCap, getMethodScoreCap(method))
253+
end
254+
end
255+
end
256+
if maxCap <= 0 then
257+
return 200.0
258+
end
259+
return maxCap
260+
end
261+
262+
local exploitMethodSet = nil
263+
local function isExploitMethod(detectionName)
264+
if not exploitMethodSet then
265+
exploitMethodSet = {}
266+
for _, name in ipairs(Evidence.Config.Categories.Exploit or {}) do
267+
exploitMethodSet[name] = true
268+
end
269+
end
270+
return exploitMethodSet[detectionName] == true
271+
end
272+
273+
local function isExploitAtAutoCheaterMin(evidence)
274+
local minPct = Evidence.Config.ExploitAutoCheaterMin or 70
275+
if not evidence or not evidence.Reasons then
276+
return false
277+
end
278+
for method, reason in pairs(evidence.Reasons) do
279+
if isExploitMethod(method) and reason.Weight and reason.Weight > 0 then
280+
local cap = getMethodScoreCap(method)
281+
if reason.Weight >= cap * (minPct / 100) then
282+
return true
283+
end
284+
end
285+
end
286+
return false
287+
end
288+
245289
local function getEvidencePercent(evidence)
246290
local cap = getEvidenceScoreCap(evidence)
247291
local score = evidence and evidence.TotalScore or 0
@@ -292,6 +336,7 @@ local function syncPlayerStateFromEvidence(steamID, evidence)
292336
local primaryMethod, onlyFakeLag = getPrimaryMethod(evidence)
293337
local shouldMarkSuspicious = evidence.TotalScore >= threshold
294338
local shouldMarkCheater = evidence.TotalScore >= cheaterThreshold
339+
or isExploitAtAutoCheaterMin(evidence)
295340

296341
-- Track active evidence to prevent double-decay in player_cache
297342
if evidence.TotalScore > 0 then
@@ -393,7 +438,7 @@ end
393438

394439
function Evidence.GetCheaterThreshold(evidence)
395440
local pct = getMenuPercent("Evidence_Tolerance", 85)
396-
return getEvidenceScoreCap(evidence) * (pct / 100)
441+
return getEvidenceMaxMethodCap(evidence) * (pct / 100)
397442
end
398443

399444
function Evidence.GetMethodScoreCap(detectionName)

Cheater_Detection/Utils/Commands.lua

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ local SteamHistory = require("Cheater_Detection.Database.SteamHistory")
88
local Config = require("Cheater_Detection.Utils.Config")
99
local Common = require("Cheater_Detection.Utils.Common")
1010
local DoubleTap = require("Cheater_Detection.detectors.double_tap")
11+
local FakeLag = require("Cheater_Detection.detectors.fake_lag")
12+
local DetectionConfig = require("Cheater_Detection.Utils.DetectionConfig")
1113
local PlayerCache = require("Cheater_Detection.Core.player_cache")
1214

1315
local Commands = {}
@@ -241,6 +243,59 @@ local function setupDiagnostics()
241243
printc(200, 200, 200, 255, string.format(" conn block : %s", tostring(stats.connectionBlock)))
242244
printc(200, 200, 200, 255, string.format(" listen bypass: %s", tostring(stats.localListenBypass)))
243245
printc(200, 200, 200, 255, string.format(" model : %s", tostring(stats.model)))
246+
printc(200, 200, 200, 255, string.format(" simtime ring : %d ticks (%d delta pairs)",
247+
stats.simtimeRetention or 0, (stats.simtimeRetention or 1) - 1))
248+
printc(200, 200, 200, 255, string.format(" DT scan : recent=%d watch=%d",
249+
stats.dtRecentScanDeltas or 0, stats.dtWatchingScanDeltas or 0))
250+
end)
251+
252+
Commands.Register("cd_fl_status", function(_args)
253+
local block = FakeLag.GetDetectionBlockReason()
254+
printc(100, 220, 255, 255, "[CD] FakeLag diagnostic:")
255+
printc(200, 200, 200, 255, " min FPS need : >= 40 (smoothed)")
256+
printc(200, 200, 200, 255, string.format(" allowed : %s", tostring(FakeLag.IsDetectionAllowed())))
257+
printc(200, 200, 200, 255, string.format(" block reason : %s", block or "none"))
258+
end)
259+
260+
Commands.Register("cd_simhistory", function(args)
261+
local targets = DetectionConfig.SimtimeScanTargets
262+
local limits = DetectionConfig.GetSimtimeScanLimits()
263+
264+
if not args or not args[1] or args[1] == "" then
265+
printc(100, 220, 255, 255, "[CD] Simtime history (FakeLag + DoubleTap):")
266+
printc(200, 200, 200, 255, string.format(
267+
" retention : %d ticks (max %d simtime delta pairs)",
268+
limits.retentionTicks, limits.maxDeltaPairs))
269+
printc(200, 200, 200, 255, string.format(
270+
" effective scan: FL=%d DT recent=%d DT watch=%d",
271+
limits.flScan, limits.dtRecent, limits.dtWatching))
272+
printc(200, 200, 200, 255, string.format(
273+
" targets : FL=%d DT recent=%d DT watch=%d (clamp to ring)",
274+
targets.FlScanDeltas, targets.DtRecentScanDeltas, targets.DtWatchingScanDeltas))
275+
printc(255, 200, 100, 255, string.format(
276+
" floor: retention >= %d (FL scan %d, DT scan %d)",
277+
DetectionConfig.GetSimtimeMinRetentionTicks(),
278+
targets.FlScanDeltas, targets.DtRecentScanDeltas))
279+
printc(180, 180, 180, 255, " Usage: cd_simhistory <ticks> — default 25; lower will be rejected")
280+
printc(180, 180, 180, 255, " Test 330ms FL + full-rage DT after each change; redeploy not required.")
281+
return
282+
end
283+
284+
local ok, err = DetectionConfig.SetSimtimeRetentionTicks(args[1])
285+
if not ok then
286+
printc(255, 100, 100, 255, "[CD] cd_simhistory: " .. tostring(err))
287+
return
288+
end
289+
290+
limits = DetectionConfig.GetSimtimeScanLimits()
291+
printc(100, 255, 150, 255, string.format(
292+
"[CD] Simtime retention set to %d ticks (ring reset). Scans: FL=%d DT recent=%d watch=%d",
293+
limits.retentionTicks, limits.flScan, limits.dtRecent, limits.dtWatching))
294+
if limits.maxDeltaPairs < DetectionConfig.GetSimtimeMaxScanDeltas() then
295+
printc(255, 100, 100, 255, string.format(
296+
"[CD] WARNING: only %d delta pairs — increase retention to at least %d.",
297+
limits.maxDeltaPairs, DetectionConfig.GetSimtimeMinRetentionTicks()))
298+
end
244299
end)
245300
end
246301

Cheater_Detection/Utils/DetectionConfig.lua

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,23 @@ DetectionConfig.Detectors = {
1717
HistoryManager.Fields.EyePosition,
1818
},
1919
},
20-
-- Simtime + precomputed tick gaps (HistoryManager). FakeLag/DoubleTap only read deltas.
20+
-- Simtime ring: 25 ticks => 24 delta pairs. Scans read only newest N gaps (see GetSimDeltaDeltas).
21+
-- Live tune: cd_simhistory <ticks> (floor = GetSimtimeMinRetentionTicks()).
2122
Simtime = {
22-
retentionTicks = 40,
23+
retentionTicks = 25,
2324
fields = { HistoryManager.Fields.SimulationTime },
2425
},
2526
}
2627

2728
DetectionConfig.DefaultRetentionTicks = 24
2829

30+
-- Per-detector scan depths (clamped to retention - 1). Ring sized for DT (24), not FL (20).
31+
DetectionConfig.SimtimeScanTargets = {
32+
FlScanDeltas = 20, -- rhythm/sustained/choke-hold windows <= 20
33+
DtRecentScanDeltas = 24, -- DT band [24,34); one gap can be 24t
34+
DtWatchingScanDeltas = 24,
35+
}
36+
2937
function DetectionConfig.GetRetentionTicks()
3038
local maxTicks = DetectionConfig.DefaultRetentionTicks
3139
for _, spec in pairs(DetectionConfig.Detectors) do
@@ -36,6 +44,68 @@ function DetectionConfig.GetRetentionTicks()
3644
return maxTicks
3745
end
3846

47+
function DetectionConfig.GetSimtimeRetentionTicks()
48+
local spec = DetectionConfig.Detectors.Simtime
49+
return spec and spec.retentionTicks or DetectionConfig.DefaultRetentionTicks
50+
end
51+
52+
function DetectionConfig.GetSimtimeMaxDeltaPairs()
53+
return math.max(0, DetectionConfig.GetSimtimeRetentionTicks() - 1)
54+
end
55+
56+
--- Smallest ring that fits all configured simtime scan depths.
57+
function DetectionConfig.GetSimtimeMinRetentionTicks()
58+
local targets = DetectionConfig.SimtimeScanTargets
59+
return math.max(
60+
targets.FlScanDeltas + 1,
61+
targets.DtRecentScanDeltas + 1,
62+
targets.DtWatchingScanDeltas + 1)
63+
end
64+
65+
function DetectionConfig.GetSimtimeMaxScanDeltas()
66+
local targets = DetectionConfig.SimtimeScanTargets
67+
return math.max(
68+
targets.FlScanDeltas,
69+
targets.DtRecentScanDeltas,
70+
targets.DtWatchingScanDeltas)
71+
end
72+
73+
--- Effective scan windows after clamping to ring size (FakeLag / DoubleTap).
74+
function DetectionConfig.GetSimtimeScanLimits()
75+
local targets = DetectionConfig.SimtimeScanTargets
76+
local maxPairs = DetectionConfig.GetSimtimeMaxDeltaPairs()
77+
local function clampScan(requested)
78+
return math.min(math.max(1, requested), maxPairs)
79+
end
80+
return {
81+
retentionTicks = DetectionConfig.GetSimtimeRetentionTicks(),
82+
maxDeltaPairs = maxPairs,
83+
flScan = clampScan(targets.FlScanDeltas),
84+
dtRecent = clampScan(targets.DtRecentScanDeltas),
85+
dtWatching = clampScan(targets.DtWatchingScanDeltas),
86+
}
87+
end
88+
89+
function DetectionConfig.SetSimtimeRetentionTicks(ticks)
90+
local targets = DetectionConfig.SimtimeScanTargets
91+
local minRetention = DetectionConfig.GetSimtimeMinRetentionTicks()
92+
local maxScan = DetectionConfig.GetSimtimeMaxScanDeltas()
93+
local n = math.floor(tonumber(ticks) or 0)
94+
if n < minRetention then
95+
return false, string.format(
96+
"retention must be >= %d (FL scan %d, DT scan %d)",
97+
minRetention, targets.FlScanDeltas, targets.DtRecentScanDeltas)
98+
end
99+
if (n - 1) < maxScan then
100+
return false, string.format(
101+
"retention %d only gives %d pairs; need >= %d for max scan depth %d",
102+
n, n - 1, minRetention, maxScan)
103+
end
104+
DetectionConfig.Detectors.Simtime.retentionTicks = n
105+
DetectionConfig.RegisterWithHistoryManager()
106+
return true, nil
107+
end
108+
39109
function DetectionConfig.GetActiveFields()
40110
local fields = {}
41111
for _, spec in pairs(DetectionConfig.Detectors) do

Cheater_Detection/Utils/HistoryManager.lua

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,8 @@ function HistoryManager.ClearPlayer(steamID)
350350
_storedFieldsByPlayer[id] = nil
351351
end
352352

353-
--- Newest-first simtime gaps in ticks (computed once when simtime is stored).
353+
--- Newest-first simtime gaps in ticks (precomputed on each simtime store).
354+
--- Only walks the newest maxDeltas gaps (ring may retain more ticks for other detectors).
354355
---@return table buf Reused per-player buffer (do not hold across ticks)
355356
---@return number count Dense entries in buf[1..count]
356357
---@return number sumTicks Sum of buf[1..count]
@@ -365,13 +366,18 @@ function HistoryManager.GetSimDeltaDeltas(history, maxDeltas)
365366
history._simDeltaBuf = buf
366367
end
367368

368-
local n = 0
369-
local sum = 0
370369
local maxPairs = history._count - 1
371370
local limit = math.min(maxDeltas or maxPairs, maxPairs)
371+
local head = history._head
372+
local cap = maxRetentionTicks
373+
local n = 0
374+
local sum = 0
372375
for offset = 0, limit - 1 do
373-
local record = HistoryManager.GetRecordAt(history, offset)
374-
local tickDelta = record and record._sim_delta_ticks
376+
local idx = head - offset
377+
if idx < 1 then
378+
idx = idx + cap
379+
end
380+
local tickDelta = history[idx]._sim_delta_ticks
375381
if tickDelta ~= nil then
376382
n = n + 1
377383
buf[n] = tickDelta

0 commit comments

Comments
 (0)