Manual/Anticheat/Anticheat · Technical
v1.0.364 notes · build 2026-11-07 12:10 UTC

Anticheat · Technical

digs: wire loophole · module anatomy (redacted) · rhythm tell · confrontation · rules
#anticheat #digs #treasure-claim

The problem

Digs pour out resources, speedups, gems, upgrade ore and skill medals. Past a mid-season plateau none of that moves an account — every S3+ bottleneck (valor badges, blueprints, oil) is capped elsewhere — so the auto user gains almost nothing. What the dig economy actually produces is discontent: members who play by hand, watch the broadcast, tap within two seconds, and lose the dig for the tenth night running stop showing up for the alliance at all. The dig is small; the leak of trust is not.

The loophole on the wire

The whole dig race happens client-side, by design:

  1. Dig/treasure events are ordinary detect-events with server-side unlock times. The client learns state through the point-detail channel and the event polls — nothing special, nothing gated.
  2. Claiming is a single ordinary command the server takes from whoever sends it first — detect.event.claim.treasure (uuid, serverId), the identical request a finger-tap produces. See wire protocol for the envelope and world map for the tile/point plumbing behind it.
  3. The server never questions claim timing — no reaction floor, no rate-shape check, no challenge. Ordering is the only law.

So the winner is decided by (prediction → dispatch) latency. A human pays 200–600 ms of visual reaction plus UI traversal. A third-party client that already lives inside the game's own Lua VM pays single-digit milliseconds — and, as it turns out, usually fires before a human could even see the unlock.

How the tool does it (module anatomy, redacted)

Recovered from the tool's per-session Lua delivery (the g_CloudLua_Treasure* claim-module family). Structure verbatim, operational parts redacted:

  • Tick source: the game's own timer entry (CS.GameEntry.Timer) plus a point-detail manager scan — the module doesn't react to a broadcast, it tracks each dig's unlock countdown and acts on predicted unlock.
  • Operator config: read through a fallback pattern from runtime globals with compiled-in defaults — lead_ms (dispatch lead, default 120, clamped ±1000; negative = pre-fire before unlock), pre_fire (default 2.0 s), kb_mode. The "delay after unlock" operators set in the UI lands here.
  • Per-dig state keyed by uuid, anchored to server time; claim retry with a negative_checks / 6 loop at ≥20 s spacing; min claim interval KB4_KHOANG_MIN.
  • The rhythm tables — baked, not configurable: KB4_NHIP = 0.100 (claim beat), KB4_LECH = {-0.150, -0.050, +0.050, +0.150} (per-shot offsets), KB4_SO_PHAT = 4 (shots per burst), an advanced table (KB4_NANGCAO, 5 shots, 0.000–0.400), plus an operator calibration bias read from FunFly/kb_cal.txt (clamped ±80 ms).
  • The claim itself: the game's own send path, gated by the min-interval check.
  • The emoji pass is NOT in this module. It is native-side (below the Lua layer): after a successful catch, a reaction goes on the broadcast — one sequence drawn uniformly at random from exactly 3 configured slots, after a fixed delay that no setting changes.
-- treasure/dig claim module (recovered, REDACTED -- structure only, non-functional)
local function fallback(val, g, def) ... end       -- operator-config pattern
local pre_fire = fallback(nil, _G.pre_fire, 2.0)   -- s before predicted unlock
local lead_ms  = fallback(nil, _G.lead_ms, 120)    -- clamp [-1000, 1000]
local kb_mode  = fallback(nil, _G.kb_mode, 0)

local KB4_SO_PHAT    = 4
local KB4_NHIP       = 0.100                       -- baked claim beat (s)
local KB4_LECH       = { -0.150, -0.050, 0.050, 0.150 }  -- baked offsets (s)
local KB4_KHOANG_MIN = 0.090                       -- min claim spacing (s)
-- advanced table KB4_NANGCAO {5 shots, 0.000..0.400}: [REDACTED]
if _G.__kb_bias_ms == nil then
-- operator calibration, FunFly/kb_cal.txt, clamp ±80 ms  [REDACTED]
end

-- per-dig state: claim_last_srv, claim_n, kb4_moc, negative_checks /6 ...
-- tick: CS.GameEntry.Timer + point-detail scan  [REDACTED]
if unlock_imminent(d) and ████████████ then         -- [REDACTED]
if (not d.claim_last_srv) or (srv_now - d.claim_last_srv) >= KB4_KHOANG_MIN then
_G.SFSNetwork.SendMessage('detect.event.claim.treasure', d.uuid, d.sid)
d.claim_last_srv = srv_now
█████████████████████████████                     -- shot bookkeeping [REDACTED]
end
end
-- after 6 negative checks >= 20 s apart: silent re-claim  [REDACTED]
-- on-catch emoji pass: native side, not in this module (see notes)

Full lua: contact xsj — it is only provided if you prove you are working against automation and cheating.

The tell

The humanizer hides the peak. It cannot hide the shape — and the rhythm tables bake the shape in:

  • Quantized residuals, not just tight ones. Every claim lands at configured lead + one of the baked offsets plus jitter. A per-player claim-latency histogram with discrete spikes at a fixed lattice (±50/±150 ms family around the operator's setting) is a machine signature no human produces — humans are a unimodal smear, not a picket fence.
  • The 0.014 s dispatch floor. Measured on our captures: the module's dispatch path has a ~14 ms floor. Claims repeatedly consistent with a fixed +0.014 s offset are inside the pattern; one fast dig proves nothing, a week hugging the floor proves everything.
  • The min-spacing constant. KB4_KHOANG_MIN = 0.090 — two claims from the same account never arrive less than ~90 ms apart, even when the tool bursts. A hard floor in the inter-claim distribution is not human either.
  • The emoji timing. Native-side and non-configurable: every catch, same gap. Per-player emoji-delay σ ≈ 0 across days is the single cleanest indicator, because the operator can't turn it off or tune it.
  • The 3-slot pool. The reaction is drawn uniformly from exactly the 3 configured sequences. A player whose dig reactions draw from a fixed pool of 3, forever, with flat frequencies (chi-square over 3 outcomes) and never a fourth option, is running the pass.
  • Coverage. 100% catch participation across all hours, including 03:00–06:00 server, weeks on end. Humans miss; the tool doesn't.

Build log

  • D1–D3. Wire decode for the dig/detect-event family: first attempts failed — the frames ride the Zstd-compressed receive path (0x20|0x10, see wire protocol); after fixing decompression, point-detail state, the claim command and its parameters fell out.
  • D4–D6. Recovered the claim module from its delivery channel. Config mapping: found lead_ms/pre_fire/kb_mode and the fallback defaults; initially hunted an emoji config in the Lua — it isn't there. Conclusion (confirmed later on the wire): the emoji pass is native-side. Also pulled the kb_cal.txt bias read and both rhythm tables.
  • D7. Constants fixed: the KB4 tables, the 90 ms min spacing, the 120 ms default lead. None are operator-settable — this is what makes the method robust; the operator cannot tune the tells away without patching the module itself.
  • D8–D10. Witness collector (below) deployed on an officer account. First data loss during an SVS burst — the spool queue overflowed and dropped rows; fixed with disk-backed spooling. Clock-anchor bug: witness-client drift skewed latencies by up to ±80 ms; anchored all rows to the server _time echo + heartbeat RTT/2 instead.
  • D11. First analysis pass over 9 days of rows. 3 suspects flagged on lattice + emoji-pool evidence. 1 exonerated by the human-check (family-shared device, humanly irregular once event windows were excluded — and apologized to).
  • D12. Suspects 2 and 3 failed the interaction check inside 48 h (no reaction outside the 3-emoji pool across a designed conversation). Evidence packs to R4. Case closed.

Data format

The collector writes JSONL, one row per event, spooled to disk and rotated daily. Export file: diglog_YYYY-MM-DD.jsonl.

FieldMeaning
tserver-anchored timestamp (ISO 8601 ms; _time echo + RTT/2, never the client clock)
evdig_spawn · dig_claim · dig_react
digdig/detect-event uuid (32 hex)
byactor uid (alliance-visible), null on spawn
lat_msunlock→claim latency for this actor
react_msclaim→emoji-reaction gap
emojithe reacted emoji sequence, verbatim
srcwitness collector tag
{"t":"2026-11-05T03:47:12.581Z","ev":"dig_spawn","dig":"8f3a91c2…","by":null}
{"t":"2026-11-05T03:47:13.812Z","ev":"dig_claim","dig":"8f3a91c2…","by":"uid:5xxxx","lat_ms":1231}
{"t":"2026-11-05T03:47:15.309Z","ev":"dig_react","dig":"8f3a91c2…","by":"uid:5xxxx","react_ms":1497,"emoji":"⛏️"}
{"t":"2026-11-05T04:02:44.062Z","ev":"dig_spawn","dig":"c71d04b9…","by":null}
{"t":"2026-11-05T04:02:45.297Z","ev":"dig_claim","dig":"c71d04b9…","by":"uid:5xxxx","lat_ms":1235}

Note the shape already: same actor, lat_ms 1231 vs 1235 — 4 ms apart, seven hours past bedtime. That pair alone is nothing. Nine days of them is the case.

Analysis prompt

Daily export goes to an AI pass. Prompt in use:

``` You are analyzing alliance dig telemetry to detect third-party automation ("auto").

Attached: diglog_YYYY-MM-DD.jsonl (and prior days for the same roster) -- one JSON object per line. Fields: t (server-anchored ISO ms), ev (dig_spawn | dig_claim | dig_react), dig (uuid), by (actor uid), lat_ms (unlock->claim), react_ms (claim->emoji), emoji (sequence), src.

Background: the automation tool in the field claims digs via the game protocol. Recovered constants from its claim module: dispatch lead configurable per operator (default 120 ms, can pre-fire); baked rhythm offsets quantizing each claim to roughly -150/-50/+50/+150 ms around the configured lead; a ~90 ms minimum spacing between claims from one account; a ~14 ms dispatch floor. After a successful catch it reacts to the broadcast with an emoji drawn uniformly at random from exactly 3 configured sequences, after a fixed, non-configurable delay. Humans have wide, unimodal reaction distributions and varied emoji/like/sticker behavior.

For every player with >= 10 claim events across the file(s):

  1. lat_ms distribution: mean, median, sigma, IQR. Flag sigma < 60 ms sustained

across days, or a multi-modal histogram with discrete spikes.

  1. Residual lattice: subtract each player's median lat_ms; flag residuals that

cluster at a small fixed set of offsets (50/150 ms family) instead of a smooth spread -- quantization is the machine signature.

  1. Dispatch floor: flag players whose minimum residual repeatedly sits within

~14 ms of a consistent floor.

  1. Min-spacing: check inter-claim gaps per player for a hard lower bound near

90 ms.

  1. react_ms distribution: sigma, and whether it is near-constant across days.
  2. Emoji pool: distinct sequences on dig_react, frequencies (chi-square

uniformity), pool size stability across days. A fixed pool of exactly 3 with flat frequencies is the tool's signature.

  1. Coverage: claim events per 3-hour bucket, especially 03:00-06:00 server;

flag uninterrupted multi-day coverage with no gaps.

  1. Exclude event-burst windows (SVS/KV +/- 30 min) from scoring, and note

shared or family devices as a known false-positive source (irregular sessions, interleaved clients).

Output: a suspect table (uid, signals tripped, evidence numbers, confidence 0-1), a cleared list with one-line reasons, and any anomalies needing manual review. Never treat raw speed or a single fast dig as evidence -- consistency of shape is the only evidence. Keep reasoning to one line per suspect. ```

Collector (redacted)

Assuming the witness-side collector Lua is working — it is, and has been for weeks — this is the shape of it. Operational parts redacted:

-- witness.lua -- alliance dig telemetry collector (REDACTED, non-functional)
-- read-only: observes alliance-visible frames; sends NO gameplay commands
local spool = disk_spool("diglog")                -- [REDACTED]

hook_dispatch(function(wire, obj)                 -- same chain the tool hooks
if wire == "█████████████" then             -- point-detail / event state [REDACTED]
spool:row{ ev = "dig_spawn", dig = ████████(obj),
t   = server_anchor() }        -- [REDACTED]
elseif wire == "detect.event.█████████████" then   -- claim results [REDACTED]
spool:row{ ev = "dig_claim", by = ████████(obj),
lat_ms = ██████████(obj) }     -- [REDACTED]
elseif wire == "chat.react.██████████" then  -- broadcast reaction
spool:row{ ev = "dig_react", emoji = ████████(obj),
react_ms = ██████████(obj) }   -- [REDACTED]
end
end)

-- server_anchor(): server _time echo + heartbeat RTT/2, never the local clock
-- daily rotate + export mailer: [REDACTED]

Setting it up for your alliance

Two supported paths:

  1. VPS + headless daemon (recommended). A witness account owned by an officer, kept online by a headless session that reuses the documented bootstrap (GSL handshake, login fields, heartbeat — see bootstrap, auth) with no graphical client, so no render load and a few dozen MB of RAM. The collector rides the same socket, spools JSONL to disk, and mails the daily export to the evaluation address automatically. Runs unattended; restarts itself; total cost is a 1-vCPU box. The account must be a normal, disclosed member of the alliance — the collector sees nothing more than any member sees, and it takes no game actions.
  2. The anticheat exe (if you know the owner). Same functionality in graphic mode: logs in a real client, watches, spools, mails. Still alpha — it crashes after 6–8 hours under event load, though the supervisor restarts it and the spool survives; otherwise proven across a full season of collection on a live roster. Not distributed here; ask around.

Rules that keep this legitimate: witness account disclosed to R4, read-only collection, evidence packs stay inside leadership, and every flag gets the human-check before any consequence.

Confrontation

Detection ends with a probability, not a person. Once the patterns reach astronomical odds — the lattice, the near-zero emoji-delay σ, the 03:00–06:00 coverage — the move is replication, never accusation.

  • The ask: screen-record yourself catching a dig, live. Framed as flattery, because it genuinely is one: "you're on fire this week — record your screen and show us how you take them, half the alliance wants the tutorial." A real player is proud to show off. The request is made privately by an officer, never as an ambush in front of the alliance, and only when the probability is already astronomical.
  • Replicate the exact behavior: not just "catch a dig" — replicate the pattern that flagged them. Fast catch: show it live. Always-on: be on camera for the quiet-hour window they own (the ~04:00 dig, tonight). Same-time consistency: name the dig they always take and ask for that one.
  • What a clean replicate looks like: hands visible in the UI, latency visible in the recording, and natural imperfection after — a few misses, a mistap, human rhythm returning once the camera is on. Suspicion closed, and the officer apologizes for the doubt. Yes, apologizes — that is the cost of a wrong flag, and paying it keeps the process honest.
  • What tool use looks like on camera: refusal ("client lags when I record", "I'll do it tomorrow" — forever), a sudden multi-day drop in catches the moment the ask is made, or a recording where the dig is caught with the interface idle. Any of these still isn't a verdict — it means quiet monitoring continues and the rules below carry the weight.
  • Never: public accusation, screenshot trials in alliance chat, or "we have data on you" bluffs. The evidence pack exists so officers don't have to bluff.

Rules the tools can't follow — the second way

Detection finds the pattern; rules remove the payoff. A rule works when it targets something no current tool offers — and right now, across every version in the field, two behaviors are still human-only: liking a chat message and sending a sticker.

  1. The Sticker Salute. After every dig you claim, drop any sticker on your own dig broadcast within 10 minutes. Emoji-only claims don't count toward dig credit. Tools fire an emoji as their humanizer — none of them can sticker.
  2. The Like. Dig credit locks when the claimer likes (👍 reaction, not emoji) the dig-plan post or their own claim message. Same gap, even cheaper.

Both are seconds of work per dig for a human, and per-dig manual labor for a tool user — which is exactly the point: automation only pays when it's unattended.

Rule design constraints (what makes a rule survive):

  • Quick to comply. Seconds per dig. If the honest members feel the rule, the rule is wrong.
  • Equally implemented, from day one, on everyone. No favorites, no "he's our top digger so it doesn't matter".
  • Zero judgement. Non-compliance means didn't follow the rule — never "uses auto". The alliance agreed on the rule; the rule wasn't followed; that's the entire finding.
  • The penalty: one day out. Automatic, no investigation, no stigma — they rejoin the next day to friendly banter about getting themselves benched. Repeated failure makes alliance life inconvenient for tool users (sticker every dig by hand defeats the automation) while costing humans almost nothing. No hard feelings is a feature, not politeness.
  • Exemptions agreed beforehand. By default nobody is exempt, R4/R5 included. If leadership is exempt, decide and announce it before enforcement starts — a quiet exemption discovered later kills the rule's credibility faster than any tool vendor does.
  • Rotate. The moment a rule gets popular, vendors patch the gap (sticker support is a UI-problem for them, and it will come). Retire the patched rule and promote the next gap the telemetry finds. Rules are seasonal, like events.

Optional extension for fixed-time events: the phrase of the day — attendance taken by replying with an unpatternable phrase officers pick that day ("purple tortoise"). Schedulers can't pre-program what isn't known in advance; free text is still human-only.

notes build 2026-11-07 12:10 UTC · lw-notes@9c47e1b · sources: own capture/analysis cross-checked against the public dossier