rendered paste body问题一:getY() 判断了两次——复制粘贴 bug,出现在 6 个文件先看 [RotationEvent.java](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/api/events/RotationEvent.java) 里 getAbsDelta() 的定义,后面都用得上:public static double absDelta(float v1, float v2) { return Math.abs(Math.abs(v1) - Math.abs(v2)); // 两个绝对值的差再取绝对值}public Vec2f getDelta() { return new Vec2f(to.getX() - from.getX(), to.getY() - from.getY()); // 有符号增量}public Vec2f getAbsDelta() { return new Vec2f(absDelta(to.getX(), from.getX()), absDelta(to.getY(), from.getY()));}然后这行代码原样出现在 6 个组件的入口:[AimBasicCheck.java (line 30)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimBasicCheck.java:30)public void process(final RotationEvent event) { if (check.getProfile().ignoreCinematic()) return; if (event.getAbsDelta().getY() == 0 && event.getAbsDelta().getY() == 0) return; // ← 两个 getY() this.rawRotations.add(new Vec2(event.getTo().getX(), event.getTo().getY()));同样写法出现在 [AimConstantCheck.java (line 45)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimConstantCheck.java:45)、[AimInvalidCheck.java (line 37)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimInvalidCheck.java:37)、[AimInconsistentCheck.java (line 42)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimInconsistentCheck.java:42)、[AimPatternCheck.java (line 44)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimPatternCheck.java:44)、[AimSmoothCheck.java (line 37)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimSmoothCheck.java:37)。问题本质:意图显然是"该 tick 完全没有旋转就跳过",即 getX() == 0 && getY() == 0。但实际只检查了 Y 两次,语义变成"pitch 没变就跳过"。于是:Δyaw ≠ 0、Δpitch = 0(纯水平转动鼠标):被错误丢弃;Δyaw = 0、Δpitch ≠ 0(纯上下看):照常进入检测;pitch 从 -1° 转到 +1° 这类跨过 0 点的移动,因为 |abs(-1) - abs(1)| = 0 也会被误判为"没旋转"。影响:这 6 个组件采集到的旋转序列整体缺失了"水平-only"样本。对 [AimConstantCheck](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimConstantCheck.java) 这类依赖相邻 tick 增量做 GCD 的检查,跳过的 tick 会让"相邻"变成不相邻,GCD 计算结果直接失真;对 Smooth(平滑角)、Basic(机器化计数)则是检测证据缺失。而且同一行 bug 复制了 6 份,说明是初版手误后一直没被发现——这类守卫代码的错误最难排查,因为系统"看起来还能跑"。问题二:AimPatternCheck 里 X 轴和 Y 轴混用[AimPatternCheck.java (line 55)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/heuristic/AimPatternCheck.java:55)final Vec2f delta = event.getDelta();final float yawFactor = delta.getX() - oldDelta.getX(); // 本 tick 与上 tick 的 yaw 增量之差final float pitchFactor = delta.getY() - oldDelta.getY(); // 同理 pitchfinal Vec2f vec = new Vec2f(yawFactor, pitchFactor);this.sample.add(vec);...for (int i = 0; i < SAMPLE_SIZE; i++) if (i > 0 && Math.abs(this.sample.get(i).getX()) > 1.0) { rawPatterns.add(Math.abs(this.sample.get(i).getX() - this.sample.get(i - 1).getY())); // ^^^^^^^^^^^^ ^^^^^^^^^^^^ // 当前 tick 的 X 减去 上一 tick 的 Y }for (final float x : rawPatterns) if (x < 1e-4) filteredPatterns.add(x);if (filteredPatterns.size() > 3) { flagged = true; // → "Suspicious patterns"rawPatterns 收集的应该是"同一轴二阶差分",即 X(i) - X(i-1)(或 Y-Y),也就是增量变化量是否恒定——恒定说明 bot 的平滑是固定步长。实际写成了 X(i) - Y(i-1):当前 tick 的 yaw 增量减上一 tick 的 pitch 增量,两个物理意义不同的量相减。影响:正常"水平匀速微调"的 bot(X 恒定、Y≈0)被漏掉:X(i) - Y(i-1) = a - 0 = a,进不了 < 1e-4 过滤;反而"对角线匀速移动"的 bot(X 和 Y 每 tick 增量恰好相同)会意外命中:X(i) - Y(i-1) = a - a = 0;过滤门槛 Math.abs(sample[i].X) > 1.0 只看 X,pitch 主导的移动基本进不来。所以这个检测器名义上叫 pattern 检测,实际只剩一个"yaw 与上一 tick pitch 相关性"的残缺版本,横竖都不对——它是"歪打正着"型 bug。问题三:灵敏度估计里 lastDeltaPitch 永远是 0(空操作赋值)先看调用处 [RawMovementListener.java (line 88)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/listeners/RawMovementListener.java:88):if (hasRotation) { SensitivityProcessor controller = profile.getSensitivityProcessor(); controller.setLastDeltaPitch(controller.getLastDeltaPitch()); // ← 读出来再原样写回去,no-op Vec2f from = new Vec2f(profile.getFrom().getYaw(), profile.getFrom().getPitch()); Vec2f to = new Vec2f(profile.getTo().getYaw(), profile.getTo().getPitch()); RotationEvent rotationEvent = new RotationEvent(profile, to, from); controller.setDeltaPitch(rotationEvent.getDelta().getY()); // 下一行才写入"本次" pitch delta controller.processSensitivity();setLastDeltaPitch(getLastDeltaPitch()) 是"自己赋值给自己"。正确的意图应该是把上一 tick 的 deltaPitch 存进 lastDeltaPitch(在覆盖前先转移),即:controller.setLastDeltaPitch(controller.getDeltaPitch());controller.setDeltaPitch(rotationEvent.getDelta().getY());由于这个赋值被写成空操作,lastDeltaPitch 从初始值 0 开始就再也没变过。再看消费它的 [SensitivityProcessor.java (line 130)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/api/player/SensitivityProcessor.java:130):public static double getGcd(final double a, final double b) { if (Math.abs(b) < 0.001 || a == b) { return a; // b≈0 时直接返回 a } ...}public void processSensitivity() { ... final float gcd = (float) getGcd(this.deltaPitch, this.lastDeltaPitch); // lastDeltaPitch 恒为 0 final double sensitivityModifier = Math.cbrt(0.8333 * gcd); final double sensitivityStepTwo = 1.666 * sensitivityModifier - 0.3333; final double finalSensitivity = sensitivityStepTwo * 200.0;推演后果:getGcd(deltaPitch, 0) 直接返回 deltaPitch。也就是说:设计上应该取"相邻两次 pitch 增量的 GCD"(即最小鼠标步长),实际退化成单样本反推——每 tick 都假设这次移动恰好等于一个鼠标步长;真实玩家一次移动 k 个像素时,cbrt(k·step) 会把估计值放大 ∛k 倍,灵敏度估计系统性偏高;deltaPitch = 0 时 gcd = 0,cbrt(0) = 0,finalSensitivity = -66.7,负值样本混进 40 样本的众数统计,进一步污染结果。影响:灵敏度是整套检测的地基——熵检查要求 sens 60–150、Inconsistent 要求 75–175、AimConstantCheck 要排除低灵敏度。估计值失真会同时造成两类后果:该检测的窗口被跳过(漏检),不该检测的玩家被纳入(误报)。唯一遮羞布是上面那段独立的"严格路径"(|deltaPitch| < 0.31 时直接对照 MCP 灵敏度表,不依赖 lastDeltaPitch),所以系统没有完全失效,bug 也因此很难暴露。问题四:AimStatisticsCheck 的 Shannon 熵分支被注释掉了(死代码)[AimStatisticsCheck.java (line 120)](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/checks/aim/AimStatisticsCheck.java:120)final double kTest = Statistics.kolmogorovSmirnovTest(Statistics.getJiffDelta(x, 6), Function.identity());{ // Kolmogorov Smirnov Test if (kTest > 10 && Math.abs(Statistics.getAverage(x)) < 13) { total++; }}shannonAnalysis.add(Statistics.getShannonEntropy(jiffYaw));if (shannonAnalysis.size() > 9) { final Set<Double> uniq = new HashSet<>(shannonAnalysis); final double diff = getDifference(Statistics.getMin(uniq), Statistics.getMax(uniq)); if (uniq.size() <= 5 && uniq.size() > 3 && diff < 0.38) { // this.profile.punish("Aim", "[Statistics] AimBot Entropy Heuristic " + uniq.size() + " " + diff, 3.0f); } shannonAnalysis.clear();}问题:这段代码设计了一个"熵启发式"——对 jiff-5 后的 yaw 增量算 Shannon 熵,累积 10 个窗口后检查:去重后只剩 4–5 个不同熵值、且最大最小值差距 < 0.38,说明熵异常稳定,疑似 bot。但唯一的动作 profile.punish(...) 被整行注释,整个 if 分支什么都不做。后果:每个 25-tick 窗口都要对 5 层 jiff 差分结果算熵、建 HashSet,纯浪费 CPU,产出为 0;注释掉的调用签名是 3 参(punish(check, info, vl)),而当前 [PlayerProfile.punish](C:/Users/Administrator/Documents/Codex/2026-08-02/fen-x/work/mx-src/MX-Project-master/src/main/java/kireiko/dev/anticheat/api/player/PlayerProfile.java) 签名是 punish(check, component, info, vl)——即使取消注释也无法编译,说明这是早期版本遗留;上面的 KS 检验是启用的,所以这不是"整块没做完",而是单独把熵检测关了——很可能是当时误报太高被作者手动注释掉,但留下了执行一半的框架。小结这四个问题里,问题一和问题三是"系统级"的:一个让 6 个组件持续丢样本,一个让灵敏度估计从设计意图退化成了近似值,都会静默影响检测精度和误报率,且不报错。问题二是单点逻辑错误,检测语义被偷换。问题四则是功能被关闭后的残留死代码。如果我要给修复建议,优先级是:问题一改成 getX()==0 && getY()==0(或直接删掉守卫让各组件自己处理零样本)、问题三把空操作改成先存旧值再写新值,然后问题二统一成同轴差分,问题四要么恢复(补上 component 参数)要么删掉整段。需要的话我可以直接给出修复后的 diff。8:44 AM