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