From 2e874939d5ec02246aa0f8fa8377897e25cc998b Mon Sep 17 00:00:00 2001 From: Shirona1337 Date: Fri, 22 May 2026 02:32:14 +0800 Subject: [PATCH] docs: rewrite paste/README.md with side-by-side code excerpts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per request: drop framework-level comparison, drop arguments based on field/class/method names (those are renamed during deobfuscation and do not prove plagiarism by themselves). Keep only the 14 modules listed by the user plus the utility classes, and back every claim with actual code blocks placed side-by-side so a reader can see the two snippets are the same thing. Modules included: AntiBots, CrystalAura, AntiFireball, Scaffold, FastWeb, Stuck, AutoMLG, SafeWalk, Disabler, AutoTools, ChestStealer, InventoryManager, ChestESP, Compass, Projectiles, plus a utility- class section (ChunkUtil, RotationUtil, BlockUtil, MovementUtil, RayTraceUtil). The evidence now hinges on three categories of artefacts that deobfuscation cannot fabricate: string literals ("Fake Staff Detected! (", "Stream limit didn't work.", "点击使用", "Normal"/"Telly Bridge" /"Keep Y", etc.), magic constants (+1337, (0.88,1.88,0.88), > 2 && < 0.0001, i*i+1, 72000, 0.6/0.2/1.2, +1.62, RGB (173,12,255)...), and signature import/dead-code fingerprints (antlr-runtime's OrderedHashSet used in a rotation util; MovementUtil.getDirectionAngle copied verbatim despite being a private unused method). Co-Authored-By: Claude Opus 4.7 --- paste/README.md | 1502 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 1184 insertions(+), 318 deletions(-) diff --git a/paste/README.md b/paste/README.md index 54d4916..5b8343b 100644 --- a/paste/README.md +++ b/paste/README.md @@ -1,404 +1,1270 @@ # Zen 抄袭 Naven 的源代码证据 -本文档基于公开可获取的两个仓库进行**逐行 / 逐字段 / 逐字符串**对比: +本文档基于公开可获取的两个仓库进行**代码段并排**对比: -- **参考实现(被抄袭方)**:[`Margele/Naven-Modern`](https://github.com/Margele/Naven-Modern)(即 Naven 客户端的简单移植版本) +- **参考实现(被抄袭方)**:[`Margele/Naven-Modern`](https://github.com/Margele/Naven-Modern) - **待审项目(疑似抄袭方)**:本仓 OpenZen(Zen 客户端反混淆产物) -> Naven 仓库的包名混淆 `com.heypixel.heypixel.VcX6svVqmeT8.*` 来自 Naven 原作者本人 push 上 GitHub 的状态,不是本文档的混淆操作。Zen 这边的对应文件全部位于 `src/main/java/shit/zen/`。 +> 字段 / 类 / 方法名是反编译时根据 Naven 重命名出来的,**不能算抄袭指纹**。所以下面所有证据全是真实的**字符串字面量、魔法常量、控制流、lambda 表达式**——这些东西在反混淆过程中不会被自动还原,只能是抄过来的。 --- -## TL;DR +## 目录 -- **Zen 的"功能模块"几乎全部能在 Naven 中找到一对一对应**:要么直接同名搬运,要么把 Naven 的 `@ModuleInfo(name=...)` 内部名直接拿来当类名(最典型的是 `InventoryManager` —— Naven 的类叫 `InventoryCleaner`,但 `@ModuleInfo(name="InventoryManager")`,Zen 直接以这个内部名命名)。 -- **框架层(事件 / 模块 / 设置 / 命令 / 工具)也全套搬运**:`EventBus` 是 Naven `EventManager` 的逐字段重命名;`EventPriority` 沿用 DarkMagician6 EventAPI 的特征数值;`@EventTarget value() default 2` 完全一致;`bind/toggle/config/language` 四条命令的提示串(`"Bound X to Y."`、`"Invalid module."`、`"Usage: .bind [key]"`)一字不差;`ChunkUtil.getLoadedChunks` 连异常文案 `"Stream limit didn't work."` 都照搬。 -- **多处遗留"反混淆指纹"**:`RotationUtil` 里那个工具类完全用不到的 `import org.antlr.v4.runtime.misc.OrderedHashSet`;`Stuck` 的魔法数 `+ 1337`;`Projectiles` 模块 RGB `(173, 12, 255)` / `(255, 238, 154)`;`InventoryManager` 的中文字面量 `"点击使用"`;都是 Naven 那边独有的特征。 -- **改动方向集中在三件事**:(a)拆模块(把 Naven 的 `HUD` 拆成 `Watermark` + 一堆 `shit.zen.hud.*`);(b)扩写(`Disabler` 在 Naven 的 yaw-only 一段外面包了一整套 Grim/ACA/Themis 子模块);(c)改名(`AttackCrystal → CrystalAura`、`Aura → KillAura`、`Glow → ESP`、`NoJumpDelay → NoDelay`、`InventoryCleaner → InventoryManager`),通常**只换类名不换 Setting 文案**,所以 Setting 字符串成为最稳定的抄袭指纹。 - -下表是模块级的总览: - -| 类别 | Naven 模块 | Zen 对应 | 抄袭程度 | -|------|-----------|---------|---------| -| Combat | `AntiBots` | `combat/AntiBots` | **逐行** | -| Combat | `Aura` (`@ModuleInfo name="KillAura"`) | `combat/KillAura` | **逐行**(最严重) | -| Combat | `AttackCrystal` (`@ModuleInfo name="CrystalAura"`) | `combat/CrystalAura` | **逐行** | -| Combat | `AntiFireball` | `combat/AntiFireball` | **逐行** | -| Combat | `AimAssist` | `misc/AimAssist` | 同名 + 部分重写 | -| Combat | `AutoClicker` | `misc/AutoClicker` | 同名 + 显著扩写 | -| Combat | `Velocity` | `combat/AntiKB` | 仅功能相近,不构成抄袭 | -| Move | `Scaffold` | `movement/Scaffold` | **逐行** + 扩展 | -| Move | `FastWeb` | `movement/FastWeb` | **逐行** | -| Move | `Sprint` | `movement/Sprint` | **逐行** | -| Move | `Stuck` | `player/Stuck` | **逐行** + 扩展 | -| Move | `AutoMLG` | `player/AutoMLG` | 重写 + 触发器抄袭 | -| Move | `SafeWalk` | `misc/SafeWalk` | **逐行**(仅改 Category) | -| Move | `Blink` | `movement/FireballBlink` | 骨架抄袭 + 场景重写 | -| Move | `NoJumpDelay` | `movement/NoDelay` | 改名(Setting label 露馅) | -| Misc | `Disabler` | `exploit/Disabler` | 核心段照搬 + 包装扩展 | -| Misc | `FastPlace` | `exploit/FastPlace` | hook 入口一致,算法换皮 | -| Misc | `AutoTools` | `world/AutoTools` | **逐行翻译** | -| Misc | `Teams` | `world/Teams` | **逐行**(加一个 Mode) | -| Misc | `ChestStealer` | `player/ChestStealer` | 核心判定段照搬 | -| Misc | `InventoryCleaner` (`@ModuleInfo name="InventoryManager"`) | `player/InventoryManager` | **逐行**(含中文字面量) | -| Render | `Glow` | `render/ESP` | 改名 + 扩展 | -| Render | `ChestESP` | `render/ChestESP` | **逐行** | -| Render | `Compass` | `render/Compass` | **逐行** | -| Render | `FullBright` | `render/FullBright` | **逐行**(仅放大范围) | -| Render | `ItemTags` | `render/ItemTags` | 同名空壳 | -| Render | `NameProtect` | `render/NameProtect` | 同算法 + 重写事件路径 | -| Render | `NameTags` | `render/NameTags` | 门面策略类(Scale=0.3/0.01 露馅) | -| Render | `NoHurtCam` | `render/NoHurtCam` | 同名空壳 | -| Render | `Projectile` | `render/Projectiles` | **逐行**(颜色/重力/算法常量全照搬) | -| Render | `HUD` | `render/Watermark` + `shit.zen.hud.*` | 拆模块 + 字段名 | -| 框架 | `EventManager` / `EventTarget` / `Priority` | `EventBus` / `EventTarget` / `EventPriority` | **逐字段** | -| 框架 | `Module` / `ModuleManager` / `Category` | 同名 | **逐行**(setEnabled、toggle 完全一致) | -| 框架 | `Value` / `ValueBuilder` / `*Value` | `Setting` / `*Setting` | **同结构**改名 | -| 框架 | `Command*` + `CommandManager` | `Command*` + `CommandManager` | **逐字符串**(命令名、提示串一致) | -| 框架 | `RotationUtils` / `ChunkUtils` / `BlockUtils` / ... | `RotationUtil` / `ChunkUtil` / `BlockUtil` / ... | **逐行** + 残留特征 import | +- [AntiBots](#antibots) +- [CrystalAura](#crystalaura) +- [AntiFireball](#antifireball) +- [Scaffold](#scaffold) +- [FastWeb](#fastweb) +- [Stuck](#stuck) +- [AutoMLG](#automlg) +- [SafeWalk](#safewalk) +- [Disabler](#disabler) +- [AutoTools](#autotools) +- [ChestStealer](#cheststealer) +- [InventoryManager](#inventorymanager) +- [ChestESP](#chestesp) +- [Compass](#compass) +- [Projectiles](#projectiles) +- [工具类](#工具类) --- -## 一、Combat 模块 +## AntiBots -### Zen `combat/AntiBots.java` vs Naven `combat/AntiBots.java` +### 三条 ChatUtil 字符串字面量与拼接模板 -**对应关系**:直接同名搬运。 +```java +// Naven AntiBots.java +ChatUtils.addChatMessage("Fake Staff Detected! (" + uuidDisplayNames.get(entry.getKey()) + ")"); +... +ChatUtils.addChatMessage("Bot Detected! (" + displayName + ")"); +... +ChatUtils.addChatMessage("Bot Removed! (" + displayName + ")"); +``` -**关键证据**: +```java +// Zen AntiBots.java +ChatUtil.print("Fake Staff Detected! (" + suspectNames.get(entry.getKey()) + ")"); +... +ChatUtil.print("Bot Detected! (" + botName + ")"); +... +ChatUtil.print("Bot Removed! (" + name + ")"); +``` -- 五个静态 map 一一对应:`uuidDisplayNames ↔ suspectNames`、`entityIdDisplayNames ↔ confirmedBotNames`、`uuids ↔ suspectJoinTimes`、`ids ↔ confirmedBotIds`、`respawnTime ↔ playerAddTimes`,全部用 `ConcurrentHashMap` / `HashSet`。 -- Setting `"Respawn Time"`:默认 2500、范围 0–10000、步长 100,完全一致。 -- 方法 `isBedWarsBot(Entity)` / `isBot(Entity)` 签名和返回路径完全相同。 -- **字符串字面量原样保留**:`"Fake Staff Detected! ("`、`"Bot Detected! ("`、`"Bot Removed! ("` 三条全部相同。 -- 500 ms 怀疑→确认管线、`ClientboundPlayerInfoUpdatePacket / ClientboundAddPlayerPacket / ClientboundRemoveEntitiesPacket` 三段式处理同构。 +三条字面量一字不差,拼接模板 `"X! (" + name + ")"` 形状完全一致。这是模块作者主观选的英文短句,撞车概率为零。 -### Zen `combat/KillAura.java` vs Naven `combat/Aura.java` +### `isBedWarsBot` 的核心三步 -**对应关系**:Naven 类名 `Aura`(`@ModuleInfo(name="KillAura")`)→ Zen 直接以 `KillAura` 命名。**这是抄袭最严重的模块。** +```java +// Naven AntiBots.java +public static boolean isBedWarsBot(Entity entity) { + AntiBots module = (AntiBots) Naven.getInstance().getModuleManager().getModule(AntiBots.class); + if (module.respawnTimeValue.getCurrentValue() < 1) { + return false; + } + if (!respawnTime.containsKey(entity.getUUID())) { + return false; + } + return System.currentTimeMillis() - respawnTime.get(entity.getUUID()) < module.respawnTimeValue.getCurrentValue(); +} -**关键证据**: +public static boolean isBot(Entity entity) { + return ids.contains(entity.getId()); +} +``` -- **12 个 BooleanSetting + 9 个 NumberSetting 与 Naven 一一对应**,名字/默认值/范围全套一致:`Attack Player / Attack Invisible / Attack Animals / Attack Mobs / Multi Attack / Prefer Baby / Target HUD / Target ESP / Attack Range(=Aim Range) / APS / Switch Size / FOV / Hurt Time`,默认 `range=5/1–6, aps=10/1–20, switchSize=1/1–5, fov=360/10–360, hurtTime=10/0–10`。 -- `switchSize` 的可见性 `() -> !infSwitch.getValue()` 等价 Naven `() -> !infSwitch.getCurrentValue()`。 -- `isValidTarget` 的实体类型检查链:`ArmorStand / Invisible / Player+bbWidth<0.5 || isSleeping / (Mob|Slime|Bat|AbstractGolem) / (Animal|Squid) / Villager / isSpectator`——import `Bat, AbstractGolem, Squid, Slime, Villager, ArmorStand` 完全一致。 -- 主循环:`isSwitch = switchSize > 1` → `updateTargets()` → `aimingTarget = shouldPreAim/getTarget()` → 记录 `prev/lastRotationData` → `attacks >= switchAttackTimes || distance > 3` 时 round-robin `++index` → `attacks += aps/20f`。 -- `getTargets()` 结尾:`possibleTargets.sort(Comparator.comparing(o -> o instanceof EndCrystal ? 0 : 1))`、`subList(0, Math.min(size, switchSize))` 限长、`preferBaby` 的 `removeIf(!isBaby)`,三处写法一对一。 -- TargetESP:`200/255F, 0, 0, 60/255F` red / `0, 200/255F, 0, 60/255F` green、`AABB box = entity.getBoundingBox().move(-motionX,-motionY,-motionZ).move(partialTick*motionX,...)`、`RenderSystem.setShader(GameRenderer::getPositionShader)`、`GL_LINE_SMOOTH` 开关顺序完全照搬。 +```java +// Zen AntiBots.java +public static boolean isBedWarsBot(Entity entity) { + AntiBots antiBots = INSTANCE; + if (entity.getId() >= 1000000000 || entity.getId() <= -1) { return true; } + if (entity.getName() == null) { return true; } + if (entity.getScoreboardName().isEmpty()) { return true; } + if (antiBots.newPlayerTimeout.getValue().floatValue() < 1.0f) { return false; } + if (!playerAddTimes.containsKey(entity.getUUID())) { return false; } + return (float)(System.currentTimeMillis() - playerAddTimes.get(entity.getUUID())) < antiBots.newPlayerTimeout.getValue().floatValue(); +} -### Zen `combat/CrystalAura.java` vs Naven `combat/AttackCrystal.java` +public static boolean isBot(Entity entity) { + return confirmedBotIds.contains(entity.getId()); +} +``` -**对应关系**:Naven 类名 `AttackCrystal`(`@ModuleInfo(name="CrystalAura")`)→ Zen 直接以 `CrystalAura` 命名。 +Zen 在前面加了几条短路判断遮掩,但核心三步 `< 1` 哨兵 → `containsKey` 检查 → `currentTimeMillis() - map.get(uuid) < timeout` 比对完全照抄,`isBot` 一行实现一模一样。 -**关键证据**: +### Setting 字面量 `"Respawn Time"` + 2500 + 0~10000 + 100 -- Setting `"Attack on Packet (Danger)"` 字符串、`boolean` 类型、`false` 默认完全一致。 -- `onPacket` 7 步序列:收 `ClientboundAddEntityPacket` → `EntityType.END_CRYSTAL` → `new EndCrystal(mc.level, packet.getX/Y/Z())` → `setId(packet.getId())` → 距离阈值 `≤ 4` → `ServerboundMovePlayerPacket.PosRot` + `ServerboundUseItemPacket(MAIN_HAND)` → 暂存→改 rotation→`createAttackPacket(target, false)`→swing→还原。一一对应。 -- `onTick` 中 `Optional any = StreamSupport.stream(... entitiesForRendering ...).filter(entity instanceof EndCrystal).findAny();` 配合 `≤ 3` 阈值。 -- 静态字段 `Entity entity / crystalTarget` 与 `rotations / aimRotation` 双字段同构。 +```java +// Naven AntiBots.java +private final FloatValue respawnTimeValue = ValueBuilder.create(this, "Respawn Time") + .setDefaultFloatValue(2500).setFloatStep(100) + .setMinFloatValue(0).setMaxFloatValue(10000).build().getFloatValue(); +``` -### Zen `combat/AntiFireball.java` vs Naven `misc/AntiFireball.java` +```java +// Zen AntiBots.java +private final NumberSetting newPlayerTimeout = + new NumberSetting("Respawn Time", 2500.0, 0.0, 10000.0, 100.0); +``` -**对应关系**:同名搬运(仅改 Category)。 +抛开字段名,`"Respawn Time"` 字符串、默认 2500、范围 0–10000、步长 100 四个字面量一一对应。 -**关键证据**: +### `onPacket` 三个 instanceof 分支的过滤条件 -- 流式查找一模一样:`StreamSupport.stream(mc.level.entitiesForRendering().spliterator(), …).filter(entity instanceof Fireball && mc.player.distanceTo(entity) < 6).map(...).findFirst()`,距离阈值 `6` 同。 -- 早退 `if (!optional.isPresent()) return;`。 -- 两行攻击代码顺序和变量名完全一致:`mc.gameMode.attack(mc.player, fireball); mc.player.swing(InteractionHand.MAIN_HAND);`。 +```java +// Naven AntiBots.java +if (packet.getAction() == ClientboundPlayerInfoPacket.Action.ADD_PLAYER) { + for (ClientboundPlayerInfoPacket.PlayerUpdate entry : packet.getEntries()) { + if (entry.getDisplayName() != null && (entry.getDisplayName().getSiblings().isEmpty() + && entry.getGameMode() == GameType.SURVIVAL)) { + UUID uuid = entry.getProfile().getId(); + uuids.put(uuid, System.currentTimeMillis()); + uuidDisplayNames.put(uuid, entry.getDisplayName().getString()); + } + } +} else if (e.getPacket() instanceof ClientboundAddPlayerPacket) { + if (uuids.containsKey(packet.getPlayerId())) { + String displayName = uuidDisplayNames.get(packet.getPlayerId()); + ChatUtils.addChatMessage("Bot Detected! (" + displayName + ")"); + entityIdDisplayNames.put(packet.getEntityId(), displayName); + uuids.remove(packet.getPlayerId()); + ids.add(packet.getEntityId()); + } +} +``` -### Zen `misc/AimAssist.java` vs Naven `combat/AimAssist.java` +```java +// Zen AntiBots.java +if (!infoUpdate.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) { return; } +for (ClientboundPlayerInfoUpdatePacket.Entry entry : infoUpdate.entries()) { + if (entry.displayName() == null + || !entry.displayName().getSiblings().isEmpty() + || entry.gameMode() != GameType.SURVIVAL) { continue; } + UUID uuid = entry.profile().getId(); + suspectJoinTimes.put(uuid, System.currentTimeMillis()); + suspectNames.put(uuid, entry.displayName().getString()); +} +... +if (packet instanceof ClientboundAddPlayerPacket addPlayer) { + if (!suspectJoinTimes.containsKey(addPlayer.getPlayerId())) { return; } + String botName = suspectNames.get(addPlayer.getPlayerId()); + ChatUtil.print("Bot Detected! (" + botName + ")"); + confirmedBotNames.put(addPlayer.getEntityId(), botName); + suspectJoinTimes.remove(addPlayer.getPlayerId()); + confirmedBotIds.add(addPlayer.getEntityId()); + return; +} +``` -**对应关系**:同名搬运 + 后续大改写。 +过滤条件三件套 `displayName != null && siblings.isEmpty() && gameMode == SURVIVAL` 一字不差(Zen 只用 De Morgan 取反 `continue`),随后五步 put / get / remove / add 顺序完全相同。 -**关键证据**:类名同;`getAngleDifference(double, double)` 静态助手同名同实现(`Mth.wrapDegrees(a-b)`);`fov` / `range` 字段语义相同(Setting label `"Fov"` / `"FoV"` 仅大小写差)。 +### 500ms 假员工阈值 -**改动**:算法已大幅重写为 smooth/adaptive 偏移模型,事件接入 `KillAura.isValidTarget(...)`,逐行抄袭已不显著。 +```java +// Naven AntiBots.java +for (Map.Entry entry : uuids.entrySet()) { + if (System.currentTimeMillis() - entry.getValue() > 500) { + ChatUtils.addChatMessage("Fake Staff Detected! (" + uuidDisplayNames.get(entry.getKey()) + ")"); + uuids.remove(entry.getKey()); + } +} +``` -### Zen `misc/AutoClicker.java` vs Naven `combat/AutoClicker.java` +```java +// Zen AntiBots.java +for (Map.Entry entry : suspectJoinTimes.entrySet()) { + if (System.currentTimeMillis() - entry.getValue() <= 500L) continue; + if (this.debug.getValue()) { + ChatUtil.print("Fake Staff Detected! (" + suspectNames.get(entry.getKey()) + ")"); + } + suspectJoinTimes.remove(entry.getKey()); +} +``` -**对应关系**:同名搬运 + 显著扩写。 - -**关键证据**:核心攻击路径相同 —— `mc.options.keyAttack.isDown()` + `mc.hitResult.getType() != BLOCK` + `setMissTime(0)`(Naven 通过 `MinecraftAccessor` mixin,Zen 通过 `ReflectionUtil`) + `KeyMapping.click(mc.options.keyAttack.getKey())`。 - -**改动**:Zen 加 Left/Right/Both、Method/Key/Mouse、Break Block、CPS Mode 等扩展。 - -### Zen `combat/AntiKB.java` vs Naven `combat/Velocity.java` - -**判定**:功能相近但实现路径完全不同(Zen 是 mode-dispatcher,Naven 是 packet-cancel),**不构成抄袭证据**。 +500 ms 这个魔法常量、`currentTimeMillis() - entry.getValue()` 减法表达式、循环里就地 remove 的反模式写法三处都对得上。 --- -## 二、Movement / Player 模块 +## CrystalAura -### Zen `movement/Scaffold.java` vs Naven `move/Scaffold.java` +### `onPacket` 末影水晶生成 9 步序列 -**对应关系**:同名搬运,框架翻新但核心算法逐句保留。 +```java +// Naven AttackCrystal.java +if (packet.getType() == EntityType.END_CRYSTAL) { + EndCrystal pTarget = new EndCrystal(mc.level, packet.getX(), packet.getY(), packet.getZ()); + pTarget.setId(packet.getId()); + if (mc.player.distanceTo(pTarget) <= 4) { + Vector2f rotations = RotationUtils.getRotations(pTarget); + mc.getConnection().send(new ServerboundMovePlayerPacket.PosRot(mc.player.getX(), mc.player.getY(), mc.player.getZ(), rotations.getX(), rotations.getY(), mc.player.isOnGround())); + mc.getConnection().send(new ServerboundUseItemPacket(InteractionHand.MAIN_HAND)); + float currentYaw = mc.player.getYRot(); + float currentPitch = mc.player.getXRot(); + mc.player.setYRot(RotationManager.rotations.x); + mc.player.setXRot(RotationManager.rotations.y); + mc.getConnection().send(ServerboundInteractPacket.createAttackPacket(pTarget, false)); + mc.player.swing(InteractionHand.MAIN_HAND); +``` -**关键证据**: +```java +// Zen CrystalAura.java +if (addEntityPacket.getType() == EntityType.END_CRYSTAL) { + EndCrystal endCrystal = new EndCrystal(mc.level, addEntityPacket.getX(), addEntityPacket.getY(), addEntityPacket.getZ()); + endCrystal.setId(addEntityPacket.getId()); + if (mc.player.distanceTo(endCrystal) <= 4.0f) { + Rotation rotation = RotationUtil.entityRotation(endCrystal); + mc.getConnection().send(new ServerboundMovePlayerPacket.PosRot(mc.player.getX(), mc.player.getY(), mc.player.getZ(), rotation.getYaw(), rotation.getPitch(), mc.player.onGround())); + PacketUtil.sendPredictive(seq -> new ServerboundUseItemPacket(InteractionHand.MAIN_HAND, seq)); + float prevYaw = mc.player.getYRot(); + float prevPitch = mc.player.getXRot(); + mc.player.setYRot(RotationHandler.targetRotation.getYaw()); + mc.player.setXRot(RotationHandler.targetRotation.getPitch()); + mc.getConnection().send(ServerboundInteractPacket.createAttackPacket(endCrystal, false)); + mc.player.swing(InteractionHand.MAIN_HAND); +``` -- `ModeSetting("Mode", "Normal", "Telly Bridge", ..., "Keep Y")` 与 Naven `mode.setModes("Normal","Telly Bridge","Keep Y")` 一一对应。 -- `renderItemSpoof` 字段名、默认 `true` 一致;`advancedBlockSearch` 字段名原样保留。 -- `eagle` / `snap` 的可见性 lambda `() -> mode.is("Normal")` 与 Naven `setVisibility(() -> mode.isCurrentMode("Normal"))` 是相同代码翻译。 -- `getPlayerYawRotation` 中随机抖动 `RandomUtils.nextFloat(0.0f, 0.5f) - 0.25f`、yaw `-180.0F`、pitch `82F / 75.5F` 这些 magic 数原样保留。 -- **`isOnBlockEdge(float)` 函数体逐字符相同**:`!mc.level.getCollisions(mc.player, mc.player.getBoundingBox().move(0.0,-0.5,0.0).inflate(-x,0.0,-x)).iterator().hasNext()`。 +相同的距离阈值 `<= 4`、相同的 9 步操作序列(new EndCrystal → setId → distance → getRotations → PosRot → UseItem → save yaw/pitch → setYRot/XRot → InteractPacket → swing)。 -### Zen `movement/FastWeb.java` vs Naven `move/FastWeb.java` +### `onTick` 的 StreamSupport 查找 -**关键证据**: +```java +// Naven AttackCrystal.java +Optional any = StreamSupport.stream(mc.level.entitiesForRendering().spliterator(), true).filter(entity -> entity instanceof EndCrystal).findAny(); +rotations = null; +if (any.isPresent()) { + Entity entity = any.get(); + Vector2f rots = RotationUtils.getRotations(entity); + double minDistance = RotationUtils.getMinDistance(entity, rots); + if (minDistance <= 3) { + rotations = rots; + this.entity = entity; +``` -- 字段 `playerInWebTick / ticksInWeb`(Naven)→ `lastWebTick / webCount`(Zen)只改名不改语义。 -- `onMotion` 中 `playerInWebTick < mc.player.tickCount → ticksInWeb = 0` 的 reset 逻辑完全照搬。 -- 阈值 `> 5` 与魔法向量 `new Vec3(0.88, 1.88, 0.88)` **逐字节相同**。 -- 无 Setting,行为路径一致。 +```java +// Zen CrystalAura.java +Optional crystalOpt = StreamSupport.stream(mc.level.entitiesForRendering().spliterator(), true).filter(entity -> entity instanceof EndCrystal).findAny(); +aimRotation = null; +if (crystalOpt.isPresent() && (hitDistance = RotationUtil.getMinHitDistance(crystalEntity = crystalOpt.get(), rotation = RotationUtil.entityRotation(crystalEntity))) <= 3.0) { + aimRotation = rotation; + this.crystalTarget = crystalEntity; +``` -### Zen `movement/Sprint.java` vs Naven `move/Sprint.java` - -**关键证据**:类签名一致,主体只有 `mc.options.toggleSprint().set(false)` + `keySprint` 强按下,PRE 阶段执行。 - -### Zen `player/Stuck.java` vs Naven `move/Stuck.java` - -**关键证据**: - -- 字段一一对应(仅重命名):`stage / packet / lastYaw / lastPitch / tryDisable / Queue packets` → `stuckState / capturedPacket / savedYaw / savedPitch / pendingDisable / pongQueue`。 -- `setEnabled` 的 `stage==3 → disable`、否则 `tryDisable=true` 三段分支完全保留。 -- **神奇位移常数 `mc.player.getX() + 1337` / `getZ() + 1337` 一字不差。** -- `onPacket` 的 `instanceof` 链顺序与分支体一致:`ServerboundMovePlayerPacket → cancel`、`ServerboundPongPacket → offer + cancel`、`ServerboundUseItemPacket || ServerboundPlayerActionPacket → capture + stage=1 + cancel`、`ClientboundPlayerPositionPacket → drain pong + stage=3 + disable`。 - -### Zen `player/AutoMLG.java` vs Naven `move/AutoMLG.java` - -**关键证据**: - -- Setting `triggerDistanceSetting = NumberSetting("Fall distance", 3.0f, 1.0f, 10.0f, 0.1f)` 与 Naven `distance = ValueBuilder("Fall Distance").setDefaultFloatValue(3).setFloatStep(0.1f)...` 默认/步进相同。 -- `Items.WATER_BUCKET` 在槽 0..9 的扫描写法、`originalSlot = inventory.selected` 切换+还原模式照搬。 -- pitch 强制朝下(90°)、useItem MAIN_HAND + swing 流程一致。 - -**Zen 自加**:Predict Ticks / Solid check / 一个拼写错误 `Recorvey`(typo,反而成为抄袭佐证)。 - -### Zen `misc/SafeWalk.java` vs Naven `move/SafeWalk.java` - -**关键证据**: - -- `isOnBlockEdge(float)` 函数体**逐字符相同**(含 `0.3F` 常量、参数 `inset` 是 `sensitivity` 的最小程度改名)。 -- `onMotion` 主体一句 `mc.options.keyShift.setDown(mc.player.onGround() && isOnBlockEdge(0.3F))`。 -- `onDisable` 复用 `InputConstants.isKeyDown(...) + mc.options.keyShift.setDown` 模式。 - -**改动**:仅改 Category(MOVEMENT → MISC)。 - -### Zen `movement/FireballBlink.java` vs Naven `move/Blink.java` - -**对应关系**:Naven 通用 `Blink` 被改名为 `FireballBlink` 并特化场景。 - -**关键证据**:`LinkedBlockingQueue> + ConcurrentLinkedQueue` 的"扣留发包再 flush"骨架、`flushPackets()` 在 disable 时清队列、监听 `ClientboundSetEntityMotionPacket` 玩家 ID 后入队,与 Naven 的 packet 暂存模型同构。 - -### Zen `movement/NoDelay.java` vs Naven `move/NoJumpDelay.java` - -**关键证据**: - -- Setting label `new BooleanSetting("No Jump Delay", true)` **直接抄 Naven 的 `@ModuleInfo(name="NoJumpDelay")`**。 -- 字段名 `fastDig` 与开关 label 不符 —— 提示是从其它项目搬来后未对齐。 -- 类本身没有事件回调,实际效果由 mixin 完成(同 Naven 的 `LivingEntityAccessor.setNoJumpDelay(0)` 思路)。 +`StreamSupport.stream(... .spliterator(), true)` 并行流、`EndCrystal` instanceof 过滤、`<= 3` 命中距离阈值、`aimRotation = null` 再赋值的控制流完全一致。 --- -## 三、Misc / Exploit / Player 模块 +## AntiFireball -### Zen `exploit/Disabler.java` vs Naven `misc/Disabler.java` +```java +// Naven AntiFireball.java +Stream stream = StreamSupport.stream(mc.level.entitiesForRendering().spliterator(), true); +Optional fireball = stream.filter(entity -> entity instanceof Fireball && mc.player.distanceTo(entity) < 6).map(entity -> (Fireball) entity).findFirst(); +if (!fireball.isPresent()) { + return; +} +Fireball entity = fireball.get(); +mc.gameMode.attack(mc.player, entity); +mc.player.swing(InteractionHand.MAIN_HAND); +``` -**对应关系**:Naven Disabler 的 `duplicateRotPlaceDisabler` 一整段被 Zen 当作 `grimDuplicateRotPlace` 子选项原样搬来,外面包了一整套扩展。 +```java +// Zen AntiFireball.java +Stream stream = StreamSupport.stream(mc.level.entitiesForRendering().spliterator(), false); +Optional optional = stream.filter(entity -> entity instanceof Fireball && (double)mc.player.distanceTo(entity) < 6.0).map(entity -> (Fireball)entity).findFirst(); +if (!optional.isPresent()) { + return; +} +Fireball fireball = optional.get(); +ChatUtil.print("§c[AntiFireball] Attacking fireball..."); +mc.gameMode.attack(mc.player, fireball); +mc.player.swing(InteractionHand.MAIN_HAND); +``` -**关键证据**: - -- 字段命名完全照搬:`rotated`、`lastPlacedDeltaYaw → lastPlacedYawDiff`、`lastPlacedDeltaPitch → lastPlacedPitchDiff`。 -- 算法控制流逐句对应:`hasRotation()` → 算 `deltaYaw = abs(yaw - last)` → `rotated = true` → `ServerboundUseItemOnPacket` 分支 `lastPlacedDeltaYaw = deltaYaw; rotated = false`。 -- **魔法常数照抄**:阈值 `deltaYaw > 2`、`xDiff < 0.0001` 完全一致。 -- `Logging` BooleanSetting + `log(...)` 函数同名同语义。 - -**改动**:把生成新 packet 改成 `ReflectionUtil.setYRot/setXRot` 改原 packet;外加 Grim/ACA/Themis 一堆扩展子模块。原始 yaw-only 核心是 Naven 的。 - -### Zen `exploit/FastPlace.java` vs Naven `misc/FastPlace.java` - -**判定**:算法换皮(Zen Delay 计时器,Naven CPS 累加器)但**底层 hook 点 `setRightClickDelay(0)` 完全一致**(Naven 通过 MinecraftAccessor mixin,Zen 通过 ReflectionUtil)。`BlockItem` 判定路径相同。最弱的证据。 - -### Zen `world/AutoTools.java` vs Naven `misc/AutoTools.java` - -**对应关系**:几乎逐行翻译。 - -**关键证据**: - -- 三个 Setting `"Check Sword"=true`、`"Switch Back"=true`、`"Silent"=true` 字符串和默认值全照搬;`Silent` 绑在 `switchBack` 可见性下也保留。 -- 状态字段 `originSlot = -1` → `previousSlot = -1` 仅改名。 -- `getBestTool` 算法逐行复制:`int slot=0; float dmg=1; for i in 0..9` → `isGodItem/isWeaponItem` 跳过 → `SwordItem instanceof && !(WebBlock)` 例外 → `destroySpeed > 1` 时加 `efficiencyLevel * efficiencyLevel + 1` 附魔加成 → `dmg > 1 ? slot : -1` 返回。 -- `onMotion` PRE/POST 切换逻辑同形,`onUpdateHeldItem` 在 MAIN_HAND && originSlot != -1 时用 `e.setItem(...)` 同一句话。 - -### Zen `world/Teams.java` vs Naven `misc/Teams.java` - -**关键证据**: - -- `isSameTeam(Entity)` 签名、返回路径、`instanceof Player` 后调 `getTeam(player) / getTeam(mc.player)` 用 `Objects.equals` 比较——完全一致。 -- `getTeam(Entity)` 函数 `mc.getConnection().getPlayerInfo(entity.getUUID()) → null 检查 → playerInfo.getTeam().getName()` 逐行对应。 - -**改动**:加一个 `ModeSetting("Mode", "Color", "Scoreboard")`,`Scoreboard` 分支就是 Naven 原版。 - -### Zen `player/ChestStealer.java` vs Naven `misc/ChestStealer.java` - -**关键证据**: - -- 容器标题判定一模一样:`Component.translatable("container.chest")`、`"container.chestDouble"`、`"container.enderchest"`,再 `|| title.equals("Chest")`。 -- `Ender Chest` BooleanSetting 默认 `false` 同名搬过来。 -- `isWorthStealing` 是 Naven `isItemUseful` 分支级翻译:`ArmorItem → protection vs BestArmorScore`、`SwordItem → damage`、`PickaxeItem / AxeItem / ShovelItem → ToolScore`、`CrossbowItem → score`、`BowItem + PunchBow / PowerBow` 两个变体、`COMPASS → !hasItem`、`WATER_BUCKET/LAVA_BUCKET >= count`、`BlockItem + isPlaceable + maxBlock`、`ARROW + maxArrow`、`FISHING_ROD >= 1`、`SNOWBALL/EGG + maxProjectile`、`ItemNameBlockItem → false` → `isCommonItemUseful`,**分支顺序与返回逻辑完全一致**。 -- 容器步进算法同形:`for (slot=0; slot` 列表 + `HashSet usedSlots` 检测槽位冲突,连 `Pair.of` 这种 commons-lang3 的导入选型都一样。 +lambda 链 `instanceof Fireball && distanceTo < 6` 一字不差,魔法数 `6` 完全一致,`mc.gameMode.attack(mc.player, ...)` + `swing(MAIN_HAND)` 收尾相同。Zen 仅多一行 debug 输出。 --- -## 四、Render 模块 +## Scaffold -### Zen `render/ESP.java` vs Naven `render/Glow.java` +### `onEnable` 中 `getYRot() - 180` 的初始化 -**对应关系**:Naven `Glow` 改名为 `ESP` 并扩充为 2D 边框 + 血条。Glow 模式下保留原过滤逻辑。 +```java +// Naven Scaffold.java +public void onEnable() { + if (mc.player != null) { + oldSlot = mc.player.getInventory().selected; + this.rots.set(mc.player.getYRot() - 180, mc.player.getXRot()); + this.lastRots.set(mc.player.yRotO - 180, mc.player.yRotO); + this.pos = null; + } +} +``` -**关键证据**: +```java +// Zen Scaffold.java +public void onEnable() { + if (mc.player != null) { + this.oldSlot = mc.player.getInventory().selected; + this.rots.setYawPitch(mc.player.getYRot() - 180.0f, mc.player.getXRot()); + this.lastRots.setYawPitch(mc.player.yRotO - 180.0f, mc.player.xRotO); + ... + } + super.onEnable(); +} +``` -- `ModeSetting("Mode", "Glow", "Outlined 2D")` —— "Glow" 模式即对应 Naven 整个 Glow 模块。 -- `isGlowing(Entity)` 分支顺序 `Player → Animal → Mob → ItemEntity → Arrow` 与 Naven `shouldGlow` 的 `Player → ItemEntity → Mob → Animal → Arrow` 高度一致,仅重排。 -- 字段 `playersSetting / mobsSetting / animalsSetting / itemsSetting / arrowsSetting` 对应 Naven `players / mobs / animals / items / arrows`,名字 + 类型 + 默认值(Players=true,其余 false)一一对应。 +把 yaw 减 180 度作为脚手架初始 yaw(让玩家朝身后放方块)是 Naven 自定义做法;4 个字段、顺序、`yRotO/xRotO` 完全对应。 -### Zen `render/ChestESP.java` vs Naven `render/ChestESP.java` +### 模式名 `"Normal" / "Telly Bridge" / "Keep Y"` + Eagle/Snap 的可见性 lambda -**关键证据**: +```java +// Naven Scaffold.java +public ModeValue mode = ValueBuilder.create(this, "Mode").setDefaultModeIndex(0).setModes("Normal", "Telly Bridge", "Keep Y")... +public BooleanValue eagle = ...("Eagle").setDefaultBooleanValue(true).setVisibility(() -> mode.isCurrentMode("Normal"))... +public BooleanValue snap = ...("Snap").setDefaultBooleanValue(true).setVisibility(() -> mode.isCurrentMode("Normal"))... +public BooleanValue renderItemSpoof = ...("Render Item Spoof").setDefaultBooleanValue(true)... +``` -- 静态字段 `chestColor / openedChestColor` 与初始值 `{0,1,0}` / `{1,0,0}` 完全一致。 -- `renderBoundingBoxes` 字段名 + `CopyOnWriteArrayList` 类型完全一致;`openedChestPositions` 仅是 Naven `openedChests` 加后缀。 -- `onPacket` 中 `ClientboundBlockEventPacket` 的 `block == CHEST || block == TRAPPED_CHEST && B0 == 1 && B1 == 1` 判定与 Naven 同条件同顺序。 -- `getChestAabb` / `getChestBox` 内部逻辑一一对应:`hasProperty(ChestBlock.TYPE)` 返回 null、`ChestType.LEFT` 跳过、`SINGLE` 之外用 `getConnectedDirection + min/max`。 -- 着色 `RenderSystem.setShaderColor(fArray[0], fArray[1], fArray[2], 0.25f)` 的 alpha = 0.25 与 Naven `0.25F` 相同。 +```java +// Zen Scaffold.java +public final ModeSetting mode = new ModeSetting("Mode", "Normal", "Telly Bridge", "Old Telly", "Keep Y").withDefault("Normal"); +public final BooleanSetting eagle = new BooleanSetting("Eagle", true, () -> this.mode.is("Normal")); +public final BooleanSetting snap = new BooleanSetting("Snap", true, () -> this.mode.is("Normal")); +public final BooleanSetting renderItemSpoof = new BooleanSetting("Render Item Spoof", true); +``` -### Zen `render/Compass.java` vs Naven `render/Compass.java` +模式列表 `Normal / Telly Bridge / Keep Y` 三个字符串字面量和顺序完全一致;Eagle/Snap 都用 `visibility = mode == "Normal"` 这种 lambda;`"Render Item Spoof"` 这种特定字符串字面量在两边都用作选项名。 -**关键证据**: +### `doSnap` 内 `+ nextFloat(0, 0.5F) - 0.25F` 抖动指纹 -- 两个 BooleanSetting `"Compass Only"`(默认 true)、`"No Player Only"`(默认 true)名字、默认值完全一致。 -- 字段 `spawnPosition / renderYaw / renderX / renderZ` 命名完全一致。 -- `getSpawnPosition` 使用 `dimensionType().natural() ? getSharedSpawnPos() : null` 逻辑相同。 -- **角度公式字面一致**:`Math.toDegrees(Math.atan2(spawn.z - renderZ, spawn.x - renderX)) - 90 - renderYaw`。 -- 提前 return 顺序 `compassOnly → hasPlayer/noPlayerOnly → spawnPosition == null` 完全一致。 +```java +// Naven Scaffold.java +if (!shouldPlaceBlock) { + rots.setX(mc.player.getYRot() + RandomUtils.nextFloat(0, 0.5F) - 0.25F); +} +``` -### Zen `render/FullBright.java` vs Naven `render/FullBright.java` +```java +// Zen Scaffold.java +if (!lookingAtBlock && mc.player.tickCount % 4 == 0) { + this.rots.setYaw(mc.player.getYRot() + RandomUtils.nextFloat(0.0f, 0.5f) - 0.25f); +} +``` -**关键证据**:唯一 Setting `"Brightness"` 名字、类型、最小值 0 与 Naven 一致;min/max/step 语义相同(仅把 0–1 缩放到 0–100)。 +用 `nextFloat(0, 0.5) - 0.25` 来得到 [-0.25, +0.25] 抖动量的写法是 Naven 风格(直接 `nextFloat(-0.25, 0.25)` 更自然)。这种"减半法"指纹两边完全一致。 -### Zen `render/NameTags.java` vs Naven `render/NameTags.java` +### `isOnBlockEdge` —— 再来一次 -**关键证据**:模块同名、`Category.RENDER`、事件分发 `onRender / onRender2D / onPacket` 多事件订阅模式一致;`NumberSetting("Scale", 0.3, 0.1, 1.0, 0.01)` 的 **默认 0.3、step 0.01 与 Naven 完全相同**,只把 max 从 0.5 放宽到 1.0。共 6 个 Setting 数量与 Naven 同。 +```java +// Naven Scaffold.java +public static boolean isOnBlockEdge(float sensitivity) { + return !mc.level.getCollisions(mc.player, mc.player.getBoundingBox().move(0.0, -0.5, 0.0).inflate(-sensitivity, 0.0, -sensitivity)).iterator().hasNext(); +} +... +mc.options.keyShift.setDown(mc.player.isOnGround() && isOnBlockEdge(0.3F)); +``` -**改动**:把真正实现外包给 `NameTagStyle` 策略类("Opal" / "Simple"),多数 Setting 名换成新含义掩饰。`Scale` 设置的默认值/step 露馅。 +```java +// Zen Scaffold.java +public static boolean isOnBlockEdge(float inflate) { + return !mc.level.getCollisions(mc.player, + mc.player.getBoundingBox().move(0.0, -0.5, 0.0).inflate(-inflate, 0.0, -inflate)) + .iterator().hasNext(); +} +... +mc.options.keyShift.setDown(mc.player.onGround() && isOnBlockEdge(0.3f)); +``` -### Zen `render/Projectiles.java` vs Naven `render/Projectile.java` - -**关键证据**: - -- 5 个 BooleanSetting **完全同名同默认**:`"Show Arrows"=true / "Show Pearls"=true / "Show Potions"=false / "Show Eggs"=false / "Show Snowballs"=false`。 -- 颜色字段一一对应且 **RGB 字面量完全相同**:`ThrownEnderpearl → new Color(173, 12, 255)`、`ThrownEgg → new Color(255, 238, 154)`、`Snowball → new Color(255, 255, 255)`。 -- `getProjectileGravity` 五个字面常量完全相同:`Bow/Crossbow → 0.05, Potion → 0.4, FishingRod → 0.15, Trident → 0.015, default → 0.03`。 -- `isThrowable` 类型集合 9 个 Item 类型相同顺序。 -- `simulateTrajectory` 与 Naven `getPath` 同算法:`1000` 步迭代、空气阻力 `0.99`、起点 `getEyeHeight - 0.1`、`bowPower = (p*p + p*2)/3 * 3`。 -- `getProjectileInfos` 中 `showArrows → arrowsColor / showPotions → potionsColor / showPearls → enderPearlColor / showEggs → eggColor / showSnowballs → snowballColor` 顺序完全一致。 -- 类名 `BasicProjectileData / EntityArrowData / EntityPotionData → ClassEspColor / ArrowEspColor / PotionEspColor` 是包装了一层的重命名。 - -### Zen `render/Watermark.java` vs Naven `render/HUD.java` - -**对应关系**:Zen 把 Naven 的 `HUD` 模块拆分——`Watermark` 只保留水印部分(`Style: Neverlose / DynamicIsland`),ArrayList / Notification 部分迁去 `shit.zen.hud.*`。模块名 `"Watermark"` 直接源自 Naven 的设置名 `"Water Mark"`。 - -### 其他同名 / 空壳 - -- `NameProtect`:同算法(`StringUtils.replace`)+ 重写事件路径,加 `Random` 模式。 -- `NoHurtCam`:两边都是空壳(实际功能在 mixin 中由模块名查询触发)。 -- `ItemTags`:Zen 端是空壳(13 行)。 +Zen Scaffold 把 SafeWalk 的同一份 `isOnBlockEdge` 在自己类里又粘贴了一遍,连 `0.3f` 阈值与 `keyShift.setDown(onGround && isOnBlockEdge(0.3f))` 这一整行表达式都一样。 --- -## 五、框架级抄袭(事件 / 模块 / 设置 / 命令 / 工具 / UI) +## FastWeb -### 5.1 事件系统 +### cobweb 阈值 + magic vector `(0.88, 1.88, 0.88)` -**对应关系**:Zen `EventBus` = Naven `EventManager`(功能 1:1,DarkMagician6 EventAPI 的同源派生)。 +```java +// Naven FastWeb.java +public void onStuck(EventStuckInBlock e) { + if (e.getState().getBlock() == Blocks.COBWEB) { + playerInWebTick = mc.player.tickCount; + ticksInWeb ++; + if (ticksInWeb > 5) { + Vec3 newSpeed = new Vec3(0.88, 1.88, 0.88); + e.setStuckSpeedMultiplier(newSpeed); + } + } +} +``` -**关键证据**: +```java +// Zen FastWeb.java +public void onStuckInBlock(StuckInBlockEvent stuckInBlockEvent) { + if (stuckInBlockEvent.getBlockState().getBlock() == Blocks.COBWEB && mc.player != null) { + this.lastWebTick = mc.player.tickCount; + ++this.webCount; + if (this.webCount > 5) { + Vec3 vec3 = new Vec3(0.88, 1.88, 0.88); + stuckInBlockEvent.setMotion(vec3); + } + } +} +``` -- 两个类都用 `Map, List<...>>` 作为唯一存储,注册时 `computeIfAbsent` + `CopyOnWriteArrayList`,注册完调用 `sortListValue / sortByPriority`;Zen 把内部 `MethodData` 改成 record `ListenerEntry`,字段顺序 `source/target/priority` 完全一致。 -- 排序逻辑逐行同形:外层遍历 `PRIORITIES`,内层把 `priority() == priority` 的项追加到新 `CopyOnWriteArrayList`,再 `put` 覆盖原 entry——这是 DarkMagician6 EventAPI 的特有写法。 -- **优先级数值/数组顺序完全一致**:`HIGHEST=0, HIGH=1, NORMAL/MEDIUM=2, LOW=3, LOWEST=4` + `PRIORITIES = {HIGHEST, HIGH, NORMAL, LOW, LOWEST}`。Zen 仅把 `MEDIUM` 改名 `NORMAL`,类名 `Priority` 改成 `EventPriority`。 -- `@EventTarget` 注解:同样 `@Documented @Target(METHOD) @Retention(RUNTIME)`,**方法签名 `byte value() default 2` 完全相同**。 -- `call(...)` 分发:先取 `dataList`,判空,再 `instanceof` 取消类(Naven `EventStoppable.isStopped()`、Zen `AbstractCancellable.isCancelled()`),循环里 `break` 提前退出——分支结构、变量命名节奏完全一致。 +`> 5` 阈值、`(0.88, 1.88, 0.88)` 三元魔法常量、记录 `tickCount` 然后递增计数器的顺序——三层细节全部撞车。0.88 / 1.88 不是 Minecraft 物理常量,是 Naven 独有的口味。 -### 5.2 Module 框架 +### `onMotion` 中的重置逻辑 -**关键证据**: +```java +// Naven FastWeb.java +public void onMotion(EventMotion e) { + if (e.getType() == EventType.POST) { + if (playerInWebTick < mc.player.tickCount) { + ticksInWeb = 0; + } + } +} +``` -- `Module.setEnabled(boolean)` 实现完全同构:`if (enabled)` 分支 → `this.enabled = true` → `eventManager.register(this)` → `onEnable()`;`else` 分支 → `this.enabled = false` → `eventManager.unregister(this)` → `onDisable()`。`toggle()` 都写成 `setEnabled(!enabled)`。 -- 字段 `name / category / key(keyCode) / enabled` 全部存在,都用 Lombok `@Getter`。 -- `ModuleManager.onKey(KeyEvent)`:`@EventTarget` 遍历 `modules`,`if (module.getKey() == e.getKey()/getKeyCode()) module.toggle()`,逻辑完全一致。 -- 异常 `ModuleNotFoundException` ↔ `NoSuchModuleException` 是直译。 -- `Category` enum 顺序保持 `COMBAT, MOVEMENT, ..., RENDER, ..., MISC`,`fromString` 静态方法的 `equalsIgnoreCase` + 默认返回 `COMBAT` 是 Naven 没有但典型的同源扩展。 +```java +// Zen FastWeb.java +public void onMotion(MotionEvent motionEvent) { + if (motionEvent.isPre() && mc.player != null && this.lastWebTick < mc.player.tickCount) { + this.webCount = 0; + } +} +``` -### 5.3 设置 / 值系统 - -**关键证据**: - -- 抽象基类 4 个字段一一对应:`name`、`value/currentValue`、`visibility (Supplier)`、子类持有的 `update/onChanged` 回调。 -- Naven `Value.isVisible()` 检查 `visibility == null || visibility.get()`;Zen 改名 `SettingVisibility`(仍是 `() -> true` 的 functional interface),构造时 `this.visibility = () -> true`——形状一致。 -- `ModeValue / ModeSetting`:都用 `String[]` 存模式列表,都提供"按字符串判断当前模式"的便捷方法(Naven `isCurrentMode`、Zen `is`),`getCurrentMode()` 写成 `values[currentValue]` / `getValue()`。 -- `NumberSetting` 字段 `min/max/step` 与 `FloatValue.minValue/maxValue/step` 直接对应;构造参数顺序一致。 - -### 5.4 命令系统 - -**关键证据**: - -- 命令名、别名一致:`bind / b`、`toggle / t`、`config`、`language`——四条命令的关键字与 Naven 完全相同。 -- `Command` 抽象类暴露同样两个抽象方法:`onCommand(String[])` 和 `onTab(String[])`。Naven 用 `@CommandInfo` 注解填字段,Zen 改成构造函数参数,但 `name/aliases` 字段命名一致。 -- **提示文案逐字符相同**:`"Bound X to Y."`、`"Unbound X."`、`"Invalid module."`、`"Invalid key."`、`"Usage: .bind [key]"`。 -- `onTab` 实现完全相同的一行 stream:`getModules().stream().map(Module::getName).filter(s -> s.toLowerCase().startsWith(args[0].toLowerCase())).toArray(String[]::new)`。 -- `CommandManager`:前缀 `.`、匹配后 `cancel/setCancelled`、按空格切分、第一段 toLowerCase 查 `aliasMap`、再用 `System.arraycopy` 切剩余参数——同样结构。 - -### 5.5 工具类 - -**关键证据**: - -- `ChunkUtil.getLoadedChunks`:与 Naven `ChunkUtils` 几乎逐行相同——`radius = max(2, render distance) + 3`、`min/max ChunkPos`、`Stream.iterate` 的 lambda(`x++ > max.x` 则 `x = min.x; z++`)、**`throw new IllegalStateException("Stream limit didn't work.")`(异常文本完全相同)**、`limit((long) diameter*diameter)`、`filter(hasChunk).map(getChunk).filter(Objects::nonNull)`。 -- `BlockUtil`:`canBeClicked(pos)` 返回 `getVoxelShape(pos) != Shapes.empty()`、`getBoundingBox(pos)` 返回 `getVoxelShape(pos).bounds().move(pos)`——同名同实现。 -- `RotationUtil` 保留了 Naven 独有的、对工具类而言**非常诡异的 `import org.antlr.v4.runtime.misc.OrderedHashSet`**,并实际使用 `OrderedHashSet`,与 Naven `RotationUtils` 一致。 -- "Sensitivity GCD" 公式:Naven `getFixedRotation` 里 `f = sensitivity * 0.6F + 0.2F; gcd = f*f*f*1.2F`。Zen 把它搬到 `Rotation.snapToSensitivity`:`float scaled = sensitivity * 0.6f + 0.2f; float step = scaled*scaled*scaled*1.2f`,数值常量、运算顺序完全相同。 -- `diffCalcVector / rotationTo`:都用 `Math.toDegrees(atan2(dz,dx)) - 90.0f` 求 yaw,`-Math.toDegrees(atan2(dy,diffXZ))` 求 pitch,再 `wrapDegrees`——同一公式同一写法。 - -### 5.6 UI(ClickGui) - -**对应关系**:Zen `gui/OldClickGui` + `gui/legacy/*` ≈ Naven `ui/ClickGUI`(视觉风格不同,但同样借助上面 5 个子系统)。 - -**关键证据**: - -- 重写 `Screen` 的同一组生命周期方法。 -- `onClose` 调用 `configManager.saveAll()`——与 Naven `ClickGUI` 关闭时通过 `FileManager.save()` 持久化设置的行为对应。 -- 按 `Category.values()` 枚举展开面板,构造 `CategoryPanel(x, 20, 140, 20, category)`——20/140 这种典型 GUI 尺寸常数和 Naven 默认布局一致量级。 -- `ModuleButton / CategoryPanel / BooleanComponent / ModeComponent / NumberComponent` 的拆分粒度与 Naven 在 `ClickGUI` 单文件内分块渲染时的命名一致。 +`lastTick < player.tickCount` 条件 + reset 计数到 0 的逻辑完全相同(仅 PRE/POST 差异)。 --- -## 总结 +## Stuck -OpenZen 的整体架构(事件总线、模块系统、设置系统、命令系统、工具类、ClickGui)几乎完全沿用 Naven 客户端的代码,外加大量同名功能模块。**改动主要集中在重命名、拆分、扩展三件事,核心逻辑、字符串字面量、魔法常数、特征 import 大量保留**——尤其是那个 `RotationUtil` 里完全用不到的 `org.antlr.v4.runtime.misc.OrderedHashSet`、`Stuck` 模块的 `+ 1337`、`Projectiles` 的颜色三元组、`InventoryManager` 的中文 `"点击使用"`、`ChunkUtil` 的 `"Stream limit didn't work."` 异常文案,这些都是无法通过功能重写解释的"抄袭指纹"。 +### `+ 1337` 魔法位移 -Zen 的反混淆产物在符号层面(类/方法/字段名)经过重新命名,但**字符串字面量层面没有清洗**,这一层是构成抄袭证据最强的源头。 +```java +// Naven Stuck.java +if (tryDisable) { + NetworkUtils.sendPacketNoEvent(new ServerboundMovePlayerPacket.Pos(mc.player.getX() + 1337, mc.player.getY(), mc.player.getZ() + 1337, mc.player.isOnGround())); + while (!packets.isEmpty()) { + NetworkUtils.sendPacketNoEvent(packets.poll()); + } + tryDisable = false; +} +``` + +```java +// Zen Stuck.java +if (this.pendingDisable) { + if (this.modeSetting.is("Delay")) { + PacketUtil.sendQueued(new ServerboundMovePlayerPacket.Pos(mc.player.getX() + 1337.0, mc.player.getY(), mc.player.getZ() + 1337.0, mc.player.onGround())); + } ... + while (!this.pongQueue.isEmpty()) { + PacketUtil.sendQueued((Packet) this.pongQueue.poll()); + } + ... + this.pendingDisable = false; +} +``` + +把玩家 X/Z 同时加 **1337** 这种"骇客致敬"魔法常量、外加紧跟其后的 `pongQueue.poll()` 循环——这就是经典 Naven 指纹。 + +### `onPacket` 四个 instanceof 分支链顺序完全相同 + +```java +// Naven Stuck.java +public void onPacket(EventPacket e) { + if (e.getPacket() instanceof ServerboundMovePlayerPacket) { + e.setCancelled(true); + } else if (e.getPacket() instanceof ServerboundPongPacket) { + packets.offer((ServerboundPongPacket) e.getPacket()); + e.setCancelled(true); + } else if (e.getPacket() instanceof ServerboundUseItemPacket || e.getPacket() instanceof ServerboundPlayerActionPacket) { + packet = e.getPacket(); + stage = 1; + e.setCancelled(true); + } else if (e.getPacket() instanceof ClientboundPlayerPositionPacket) { + while (!packets.isEmpty()) { NetworkUtils.sendPacketNoEvent(packets.poll()); } + stage = 3; + toggle(); + } +} +``` + +```java +// Zen Stuck.java +public void onPacket(PacketEvent packetEvent) { + ... + if (rawPacket instanceof ServerboundMovePlayerPacket movePacket) { ...; packetEvent.setCancelled(true); } + else if (packetEvent.getPacket() instanceof ServerboundPongPacket) { + this.pongQueue.offer((ServerboundPongPacket)packetEvent.getPacket()); + packetEvent.setCancelled(true); + } else if (packetEvent.getPacket() instanceof ServerboundUseItemPacket || packetEvent.getPacket() instanceof ServerboundPlayerActionPacket) { + this.capturedPacket = packetEvent.getPacket(); + this.stuckState = 1; + packetEvent.setCancelled(true); + } else if (packetEvent.getPacket() instanceof ClientboundPlayerPositionPacket && this.modeSetting.is("Delay")) { + while (!this.pongQueue.isEmpty()) { PacketUtil.sendQueued(...); } + this.stuckState = 3; + this.setEnabled(false); + } +} +``` + +四个 instanceof 分支顺序完全相同:`Move → Pong → (UseItem/PlayerAction) → ClientboundPos`。每个分支内部动作(offer/cancel、记 stage=1、最后 stage=3+toggle)也完全平行。 + +### `shouldRotate` 中 `BowlFoodItem || BowItem` 双过滤 + +```java +// Naven Stuck.java +if (packet instanceof ServerboundUseItemPacket) { + ServerboundUseItemPacket blockPlacement = (ServerboundUseItemPacket) packet; + ItemStack item = mc.player.getItemInHand(blockPlacement.getHand()); + if (item.getItem() instanceof BowlFoodItem || item.getItem() instanceof BowItem) { + return false; + } + return true; +} else if (packet instanceof ServerboundPlayerActionPacket) { + ... + if (playerDigging.getAction() == ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM) { + if (mc.player.getUseItem().getItem() instanceof BowItem) { return true; } + } + return false; +} +``` + +```java +// Zen Stuck.java +if (this.capturedPacket instanceof ServerboundUseItemPacket useItemPacket) { + ItemStack heldStack = mc.player.getItemInHand(useItemPacket.getHand()); + return !(heldStack.getItem() instanceof BowlFoodItem) && !(heldStack.getItem() instanceof BowItem); +} +if (this.capturedPacket instanceof ServerboundPlayerActionPacket actionPacket) { + return actionPacket.getAction() == ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM + && mc.player.getUseItem().getItem() instanceof BowItem; +} +``` + +`BowlFoodItem + BowItem` 的组合过滤是 Naven 自创口味(碗食物本就极少有人在这种场景去黑名单)。配上 `RELEASE_USE_ITEM + BowItem` 的分支,几乎是 1:1 翻译。 + +--- + +## AutoMLG + +### 触发结构(落地预测 + 热栏扫描 + 切槽放水) + +```java +// Naven AutoMLG.java +public static boolean isOnGround(double height) { + Iterable collisions = mc.level.getBlockCollisions(mc.player, mc.player.getBoundingBox().move(0.0D, height, 0.0D)); + return collisions.iterator().hasNext(); +} +... +for (int i = 0; i < 9; i++) { + ItemStack item = mc.player.getInventory().getItem(i); + if (!item.isEmpty() && item.getItem() == Items.WATER_BUCKET) { + originalSlot = mc.player.getInventory().selected; + mc.player.getInventory().selected = i; + ... + } +} +``` + +```java +// Zen AutoMLG.java +private boolean hasSolidBelow(BlockPos blockPos) { + return this.isSolidNonMenu(blockPos.below()) || this.isSolidNonMenu(blockPos.below(2)); +} +private boolean isSolidNonMenu(BlockPos blockPos) { + BlockState blockState = mc.level.getBlockState(blockPos); + boolean hasCollision = !blockState.getCollisionShape(mc.level, blockPos).isEmpty(); + ... +} +... +slot = ItemUtil.findItemInRange(0, 9, Items.WATER_BUCKET); +if (slot < 0) return; +... +this.selectSlot(slot); // 内部就是 selected=oldSlot; selected=slot +``` + +固定 0..9 热栏扫描 + `WATER_BUCKET` + 保存 `originalSlot` 再切换 —— 结构相同,Zen 仅把 for 循环抽成 `ItemUtil.findItemInRange` 工具函数。`Fall Distance` 默认 3.0 / 步长 0.1f 两个 NumberSetting 字面量参数也完全一致。 + +--- + +## SafeWalk + +### `isOnBlockEdge` 静态方法 —— 字符级雷同 + +```java +// Naven SafeWalk.java +public static boolean isOnBlockEdge(float sensitivity) { + return !mc.level.getCollisions(mc.player, mc.player.getBoundingBox().move(0.0, -0.5, 0.0).inflate(-sensitivity, 0.0, -sensitivity)).iterator().hasNext(); +} +``` + +```java +// Zen SafeWalk.java +public static boolean isOnBlockEdge(float inset) { + return !mc.level.getCollisions(mc.player, mc.player.getBoundingBox().move(0.0, -0.5, 0.0).inflate(-inset, 0.0, -inset)).iterator().hasNext(); +} +``` + +除参数名(`sensitivity` → `inset`)外完全一字不差。`move(0.0, -0.5, 0.0).inflate(-x, 0.0, -x).iterator().hasNext()` 这一连串调用是经典 Naven 指纹。 + +### `onDisable` 恢复 keyShift + +```java +// Naven SafeWalk.java +public void onDisable() { + boolean isHoldingShift = InputConstants.isKeyDown(mc.getWindow().getWindow(), mc.options.keyShift.getKey().getValue()); + mc.options.keyShift.setDown(isHoldingShift); +} +``` + +```java +// Zen SafeWalk.java +public void onDisable() { + boolean keyDown = InputConstants.isKeyDown(mc.getWindow().getWindow(), mc.options.keyShift.getKey().getValue()); + mc.options.keyShift.setDown(keyDown); +} +``` + +仅变量名不同,控制流、API 调用、参数顺序完全一致。 + +--- + +## Disabler + +### `duplicateRotPlace` 算法 —— `> 2` + `< 0.0001` 双阈值 + `rotated` 状态机 + +```java +// Naven Disabler.java +if (packet.hasRotation()) { + float lastPlayerYaw = playerYaw; + playerYaw = packet.getYRot(0); + deltaYaw = Math.abs(playerYaw - lastPlayerYaw); + rotated = true; + // Guess what will happen if you placed the block in current tick + if (deltaYaw > 2) { + float xDiff = Math.abs(deltaYaw - lastPlacedDeltaYaw); + if (xDiff < 0.0001) { + log("Disabling DuplicateRotPlace!"); + ... + } + } +} else if (e.getPacket() instanceof ServerboundUseItemOnPacket) { + if (rotated) { + lastPlacedDeltaYaw = deltaYaw; + rotated = false; + } +} +``` + +```java +// Zen Disabler.java +if (movePacket.hasRotation()) { + float prevYaw = this.currentYaw; + float prevPitch = this.currentPitch; + this.currentYaw = ReflectionUtil.getYRot(movePacket); + this.currentPitch = ReflectionUtil.getXRot(movePacket); + this.yawDiff = Math.abs(this.currentYaw - prevYaw); + this.pitchDiff = Math.abs(this.currentPitch - prevPitch); + this.rotated = true; + float yawDelta; + if (this.yawDiff > 2.0f && (double)(yawDelta = Math.abs(this.yawDiff - this.lastPlacedYawDiff)) < 1.0E-4) { + ... + } +} else if (packet instanceof ServerboundUseItemOnPacket && this.rotated) { + this.lastPlacedYawDiff = this.yawDiff; + this.lastPlacedPitchDiff = this.pitchDiff; + this.rotated = false; +} +``` + +完全一致的算法:取上一 yaw → 算 deltaYaw → `> 2` 阈值 → 算与上次放置 deltaYaw 的差 → `< 0.0001` (1.0E-4) 判定 → `USE_ITEM_ON` 时把当前 delta 写入 `lastPlacedDeltaYaw` 并清零 `rotated`。Zen 仅把单一 yaw 扩展为 yaw + pitch 两份。 + +--- + +## AutoTools + +### `getBestTool` 算法 —— `efficiencyLevel * efficiencyLevel + 1` 公式 + +```java +// Naven AutoTools.java +for (int index = 0; index < 9; index++) { + ItemStack itemStack = mc.player.getInventory().getItem(index); + if (InventoryUtils.isGodItem(itemStack)) continue; + if (!itemStack.isEmpty() && !blockState.isAir() && (!(itemStack.getItem() instanceof SwordItem) || block instanceof WebBlock)) { + float strVsBlock = itemStack.getItem().getDestroySpeed(itemStack, blockState); + if (strVsBlock > 1 && !(block instanceof OreBlock || block instanceof RedStoneOreBlock)) { + int i = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, itemStack); + if (i > 0) { + strVsBlock += (float) (i * i + 1); + } + } + if (strVsBlock > dmg) { + slot = index; + dmg = strVsBlock; + } + } +} +if (dmg > 1F) return slot; +return -1; +``` + +```java +// Zen AutoTools.java +for (int i = 0; i < 9; ++i) { + int efficiencyLevel; + ItemStack itemStack = mc.player.getInventory().getItem(i); + if (ItemUtil.isWeaponItem(itemStack) || itemStack.isEmpty() || blockState.isAir() + || itemStack.getItem() instanceof SwordItem && !(block instanceof WebBlock)) continue; + float destroySpeed = itemStack.getItem().getDestroySpeed(itemStack, blockState); + if (destroySpeed > 1.0f && !(block instanceof DropExperienceBlock) && !(block instanceof RedStoneOreBlock) + && (efficiencyLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, itemStack)) > 0) { + destroySpeed += (float)(efficiencyLevel * efficiencyLevel + 1); + } + if (!(destroySpeed > bestSpeed)) continue; + bestSlot = i; + bestSpeed = destroySpeed; +} +if (bestSpeed > 1.0f) return bestSlot; +return -1; +``` + +循环上限 `9`、`WebBlock` 例外、附魔加成公式 **`i * i + 1`**、对矿石方块跳过加成、`> 1.0f` 的最终阈值、不命中返 `-1`,逐行对应。`OreBlock → DropExperienceBlock` 只是 1.17 → 1.19 Mojang 类名重命名后的同义替换。 + +--- + +## ChestStealer + +### 三连 `Component.translatable("container.chest / chestDouble / enderchest")` + 兜底 `"Chest"` + +```java +// Naven ChestStealer.java (1.17 API) +String chestTitle = container.getTitle().getString(); +String chest = new TranslatableComponent("container.chest").getString(); +String largeChest = new TranslatableComponent("container.chestDouble").getString(); +String enderChest = new TranslatableComponent("container.enderchest").getString(); +if (chestTitle.equals(chest) || chestTitle.equals(largeChest) || chestTitle.equals("Chest") + || (pickEnderChest.getCurrentValue() && chestTitle.equals(enderChest))) { +``` + +```java +// Zen ChestStealer.java +String title = containerScreen.getTitle().getString(); +String chestTitle = Component.translatable("container.chest").getString(); +String doubleChestTitle = Component.translatable("container.chestDouble").getString(); +String enderChestTitle = Component.translatable("container.enderchest").getString(); +ChestMenu chestMenu = containerScreen.getMenu(); +if (this.chestSetting.getValue() && (title.equals(chestTitle) || title.equals(doubleChestTitle) || title.equals("Chest"))) { + ... +} else if (this.enderChestSetting.getValue() && title.equals(enderChestTitle) && ...) { +``` + +完全相同的三个翻译键 + 多出来的硬编码英文兜底 `"Chest"` + 单独把 ender chest 拎出来做开关。这种组合不是写 hack 的标准写法 —— Zen 把 1.17 `new TranslatableComponent(...)` 机械替换成 1.19+ 的 `Component.translatable(...)`。 + +--- + +## InventoryManager + +### 中文字面量 `"点击使用"` —— 终极铁证 + +```java +// Naven InventoryCleaner.java +public boolean isItemUseful(ItemStack stack) { + if (stack.isEmpty()) return false; + if (InventoryUtils.isGodItem(stack)) return true; + if (stack.getDisplayName().getString().contains("点击使用")) { + return true; + } + if (stack.getItem() instanceof ArmorItem) { ... } + else if (stack.getItem() instanceof SwordItem) { return InventoryUtils.getBestSword() == stack; } + else if (stack.getItem() instanceof PickaxeItem) { return InventoryUtils.getBestPickaxe() == stack; } + else if (stack.getItem() instanceof AxeItem && !InventoryUtils.isSharpnessAxe(stack)) { + return InventoryUtils.getBestAxe() == stack; + } + ... +} +``` + +```java +// Zen InventoryManager.java +public boolean isUsefulItem(ItemStack itemStack) { + if (itemStack.isEmpty()) return false; + if (ItemUtil.isWeaponItem(itemStack)) return true; + if (itemStack.getDisplayName().getString().contains("点击使用")) { + return true; + } + if (itemStack.getItem() == Items.COBWEB) return true; + Item item = itemStack.getItem(); + if (item instanceof ArmorItem armorItem) { ... } + if (itemStack.getItem() instanceof SwordItem) { return ItemUtil.getBestSword() == itemStack; } + if (itemStack.getItem() instanceof PickaxeItem) { return ItemUtil.getBestPickaxe() == itemStack; } + if (itemStack.getItem() instanceof AxeItem && !ItemUtil.isLegitAxe(itemStack)) { + return ItemUtil.getBestAxe() == itemStack; + } + ... +} +``` + +`contains("点击使用")` 是 Naven 作者针对国服服务端(hypixel.cn / 网易我的世界类)放进物品名的中文标记,与英文环境的 Minecraft **毫无关联**。Zen 一字不差搬了过来,连后续的 `getBestSword() == stack` 这种"引用比较"风格的奇怪判定也照抄。单凭这一行就足以认定整段 `isUsefulItem` 是直接复制粘贴的。 + +### 四种 offhand 模式与"水/岩浆桶上限 = 1" + +```java +// Zen InventoryManager.java +private final ModeSetting offhandItemSetting = new ModeSetting("Offhand Items", + "Golden Apple", "Fishing Rod", "None").withDefault("Projectile"); +... +if ("Golden Apple".equals(offhandPreference)) { if (this.handleGoldenAppleOffhand()) return true; } +else if ("Projectile".equals(offhandPreference)) { if (this.handleProjectileOffhand()) return true; } +else if ("Fishing Rod".equals(offhandPreference)) { if (this.handleFishingRodOffhand()) return true; } +else if ("Block".equals(offhandPreference)) { if (this.handleBlockOffhand()) return true; } + +public static int getMaxWaterBuckets() { return 1; } +public static int getMaxLavaBuckets() { return 1; } +``` + +Naven 的 `isItemUseful` 里也分别处理 GoldenApple / Projectile (Snowball + Egg) / FishingRod / Block 四类,Zen 直接把它们提升为四个 `handleXxxOffhand()` 函数,并把 Naven 的 `getWaterBucketCount() / getLavaBucketCount()` 设置项简化成硬编码 `return 1;`。 + +--- + +## ChestESP + +### 箱子打开判定 —— 四重条件 + +```java +// Naven ChestESP.java +if ((packet.getBlock() == Blocks.CHEST || packet.getBlock() == Blocks.TRAPPED_CHEST) && packet.getB0() == 1 && packet.getB1() == 1) { + openedChests.add(packet.getPos()); +} +``` + +```java +// Zen ChestESP.java +if ((clientboundBlockEventPacket.getBlock() == Blocks.CHEST || clientboundBlockEventPacket.getBlock() == Blocks.TRAPPED_CHEST) && clientboundBlockEventPacket.getB0() == 1 && clientboundBlockEventPacket.getB1() == 1) { + this.openedChestPositions.add(clientboundBlockEventPacket.getPos()); +``` + +四重条件(CHEST、TRAPPED_CHEST、b0 == 1、b1 == 1)完全一样,魔法常数 `1` 都来自 ChestBlockEntity.lidEvent。 + +### `getChestBox` 双箱合并算法 + +```java +// Naven ChestESP.java +ChestType chestType = state.getValue(ChestBlock.TYPE); +if (chestType == ChestType.LEFT) { + return null; +} +BlockPos pos = chestBE.getBlockPos(); +AABB box = BlockUtils.getBoundingBox(pos); +if (chestType != ChestType.SINGLE) { + BlockPos pos2 = pos.relative(ChestBlock.getConnectedDirection(state)); + if (BlockUtils.canBeClicked(pos2)) { + AABB box2 = BlockUtils.getBoundingBox(pos2); + box = box.minmax(box2); +``` + +```java +// Zen ChestESP.java +ChestType chestType = blockState.getValue(ChestBlock.TYPE); +if (chestType == ChestType.LEFT) { + return null; +} +BlockPos blockPos2 = chestBlockEntity.getBlockPos(); +AABB aABB = BlockUtil.getBoundingBox(blockPos2); +if (chestType != ChestType.SINGLE && BlockUtil.canBeClicked(blockPos = blockPos2.relative(ChestBlock.getConnectedDirection(blockState)))) { + AABB aABB2 = BlockUtil.getBoundingBox(blockPos); + aABB = aABB.minmax(aABB2); +``` + +`LEFT` 跳过、`SINGLE` 单箱、`getConnectedDirection` 双箱合并、`minmax` 合并 AABB —— 整段算法逐行复刻。 + +### 箱子颜色常量 + +```java +// Naven ChestESP.java +private static final float[] chestColor = {0, 1, 0}; +private static final float[] openedChestColor = {1, 0, 0}; +``` + +```java +// Zen ChestESP.java (static init) +chestColor = new float[]{0.0f, 1.0f, 0.0f}; +openedChestColor = new float[]{1.0f, 0.0f, 0.0f}; +``` + +两个 `float[]` 颜色常数(绿色未开、红色已开)原值搬过来,连透明度 `0.25f` 也一致。 + +--- + +## Compass + +### yaw 角度计算公式 + +```java +// Naven Compass.java +float yaw = (float) (Math.toDegrees(Math.atan2(spawnPosition.getZ() - renderZ, spawnPosition.getX() - renderX)) - 90 - renderYaw); +float x = mc.getWindow().getGuiScaledWidth() / 2f; +float y = mc.getWindow().getGuiScaledHeight() / 2f; +``` + +```java +// Zen Compass.java +float angle = (float)(Math.toDegrees(Math.atan2((double)this.spawnPosition.getZ() - this.renderZ, (double)this.spawnPosition.getX() - this.renderX)) - 90.0 - (double)this.renderYaw); +float centerX = (float)mc.getWindow().getGuiScaledWidth() / 2.0f; +float centerY = (float)mc.getWindow().getGuiScaledHeight() / 2.0f; +``` + +`atan2(Z, X) - 90 - yaw` 这个公式连项数顺序都没变,魔法常数 `90` 直接搬。 + +### 两个 BooleanSetting 字面量 + +```java +// Naven Compass.java +public BooleanValue compassOnly = ValueBuilder.create(this, "Compass Only").setDefaultBooleanValue(true).build()...; +public BooleanValue noPlayerOnly = ValueBuilder.create(this, "No Player Only").setDefaultBooleanValue(true).build()...; +``` + +```java +// Zen Compass.java +private final BooleanSetting compassOnly = new BooleanSetting("Compass Only", true); +private final BooleanSetting noPlayerOnly = new BooleanSetting("No Player Only", true); +``` + +字面量名(含空格大小写)`"Compass Only"` / `"No Player Only"` 与默认值 `true / true` 完全一致,注册顺序也相同。 + +### `renderX / renderZ / renderYaw` 的 `Mth.lerp` 三连 + +```java +// Naven Compass.java +renderX = Mth.lerp(e.getRenderPartialTicks(), mc.player.xOld, mc.player.getX()); +renderZ = Mth.lerp(e.getRenderPartialTicks(), mc.player.zOld, mc.player.getZ()); +renderYaw = Mth.lerp(e.getRenderPartialTicks(), mc.player.yRotO, mc.player.getYRot()); +``` + +```java +// Zen Compass.java +this.renderX = Mth.lerp(renderEvent.partialTick(), mc.player.xOld, mc.player.getX()); +this.renderZ = Mth.lerp(renderEvent.partialTick(), mc.player.zOld, mc.player.getZ()); +this.renderYaw = Mth.lerp(renderEvent.partialTick(), mc.player.yRotO, mc.player.getYRot()); +``` + +`xOld / zOld / yRotO` 的顺序都没换。 + +--- + +## Projectiles + +### 弓蓄力公式(含魔法数 72000 / 3 / 0.1) + +```java +// Naven Projectile.java +float bowPower = (72000 - player.getUseItemRemainingTicks()) / 20.0f; +bowPower = (bowPower * bowPower + bowPower * 2.0f) / 3.0f; +if (bowPower > 1 || bowPower <= 0.1F) bowPower = 1; +bowPower *= 3F; +``` + +```java +// Zen Projectiles.java +float pull = (72000 - localPlayer.getUseItemRemainingTicks()) / 20.0f; +pull = (pull * pull + pull * 2.0f) / 3.0f; +if (pull > 1.0f || pull <= 0.1f) pull = 1.0f; +pull *= 3.0f; +``` + +完整公式 `(72000 - x) / 20 → (p² + 2p) / 3 → clamp → × 3` 一模一样,仅变量名 `bowPower → pull`。 + +### 抛物物品白名单 —— 9 项 instanceof 链顺序相同 + +```java +// Naven Projectile.java +return item instanceof BowItem || item instanceof CrossbowItem || item instanceof SnowballItem || item instanceof EggItem || item instanceof EnderpearlItem || item instanceof SplashPotionItem || item instanceof LingeringPotionItem || item instanceof FishingRodItem || item instanceof TridentItem; +``` + +```java +// Zen Projectiles.java +return item instanceof BowItem || item instanceof CrossbowItem + || item instanceof SnowballItem || item instanceof EggItem + || item instanceof EnderpearlItem || item instanceof SplashPotionItem + || item instanceof LingeringPotionItem || item instanceof FishingRodItem + || item instanceof TridentItem; +``` + +9 个 Item 类型按完全相同的顺序排列。 + +### 重力 switch + 三个颜色 RGB 常数 + +```java +// Naven Projectile.java +if (item instanceof BowItem || item instanceof CrossbowItem) return 0.05; +if (item instanceof PotionItem) return 0.4; +if (item instanceof FishingRodItem) return 0.15; +if (item instanceof TridentItem) return 0.015; +return 0.03; +... +new BasicProjectileData(Collections.singleton(ThrownEnderpearl.class), new Color(173, 12, 255)); +new BasicProjectileData(Collections.singleton(ThrownEgg.class), new Color(255, 238, 154)); +new BasicProjectileData(Collections.singleton(Snowball.class), new Color(255, 255, 255)); +``` + +```java +// Zen Projectiles.java +if (item instanceof BowItem || item instanceof CrossbowItem) return 0.05; +if (item instanceof PotionItem) return 0.4; +if (item instanceof FishingRodItem) return 0.15; +if (item instanceof TridentItem) return 0.015; +return 0.03; +... +new ClassEspColor(Collections.singleton(ThrownEnderpearl.class), new Color(173, 12, 255)); +new ClassEspColor(Collections.singleton(ThrownEgg.class), new Color(255, 238, 154)); +new ClassEspColor(Collections.singleton(Snowball.class), new Color(255, 255, 255)); +``` + +5 条 gravity 分支顺序与数值(`0.05 / 0.4 / 0.15 / 0.015 / 0.03`)完全照搬。3 个 RGB 颜色魔法数 `(173, 12, 255)` `(255, 238, 154)` `(255, 255, 255)` 完全一致,且 `Collections.singleton(X.class)` 包装方式一致。 + +### 5 个 BooleanSetting 名 + 默认值 + +```java +// Naven Projectile.java +ValueBuilder.create(this, "Show Arrows").setDefaultBooleanValue(true)... +ValueBuilder.create(this, "Show Pearls").setDefaultBooleanValue(true)... +ValueBuilder.create(this, "Show Potions").setDefaultBooleanValue(false)... +ValueBuilder.create(this, "Show Eggs").setDefaultBooleanValue(false)... +ValueBuilder.create(this, "Show Snowballs").setDefaultBooleanValue(false)... +``` + +```java +// Zen Projectiles.java +new BooleanSetting("Show Arrows", true); +new BooleanSetting("Show Pearls", true); +new BooleanSetting("Show Potions", false); +new BooleanSetting("Show Eggs", false); +new BooleanSetting("Show Snowballs", false); +``` + +5 个字符串(大小写、空格、复数 s)、5 个默认值(`true / true / false / false / false`)、注册顺序全部一致。 + +--- + +## 工具类 + +### `ChunkUtil.getLoadedChunks` —— `"Stream limit didn't work."` 异常文本逐字符一致 + +```java +// Naven ChunkUtils.java +public static Stream getLoadedChunks() { + int radius = Math.max(2, mc.options.getEffectiveRenderDistance()) + 3; + int diameter = radius * 2 + 1; + ChunkPos center = mc.player.chunkPosition(); + ChunkPos min = new ChunkPos(center.x - radius, center.z - radius); + ChunkPos max = new ChunkPos(center.x + radius, center.z + radius); + Stream stream = Stream.iterate(min, pos -> { + int x = pos.x; + int z = pos.z; + x++; + if (x > max.x) { x = min.x; z++; } + if (z > max.z) throw new IllegalStateException("Stream limit didn't work."); + return new ChunkPos(x, z); + }).limit((long) diameter * diameter).filter(c -> mc.level.hasChunk(c.x, c.z)) + .map(c -> mc.level.getChunk(c.x, c.z)).filter(Objects::nonNull); + return stream; +} +``` + +```java +// Zen ChunkUtil.java +public static Stream getLoadedChunks() { + if (mc.player == null || mc.level == null) return Stream.empty(); + final int radius = Math.max(2, mc.options.getEffectiveRenderDistance()) + 3; + final int side = radius * 2 + 1; + final ChunkPos center = mc.player.chunkPosition(); + final ChunkPos min = new ChunkPos(center.x - radius, center.z - radius); + final ChunkPos max = new ChunkPos(center.x + radius, center.z + radius); + return Stream.iterate(min, current -> { + int x = current.x; int z = current.z; + if (++x > max.x) { x = min.x; ++z; } + if (z > max.z) throw new IllegalStateException("Stream limit didn't work."); + return new ChunkPos(x, z); + }).limit((long) side * side).filter(pos -> mc.level.hasChunk(pos.x, pos.z)) + .map(pos -> mc.level.getChunk(pos.x, pos.z)).filter(Objects::nonNull); +} +``` + +异常字符串 `"Stream limit didn't work."` 含句点、含撇号、连大小写都一致;`Math.max(2, ...) + 3`、`radius * 2 + 1`、`Stream.iterate` 的 lambda 行内 `if (x > max.x) { x = min.x; z++; }` 写法骨架完全一样。这种带异常 throw 的 `Stream.iterate` 谁都不会自己写两遍。 + +### `RotationUtil` 把 antlr-runtime 的 `OrderedHashSet` 当 Set 用 + +```java +// Naven RotationUtils.java +import org.antlr.v4.runtime.misc.OrderedHashSet; +... +Set points = new OrderedHashSet<>(); +points.add(new Vec3(minX + maxX / 2, minY + maxY / 2, minZ + maxZ / 2)); +points.add(getClosestPoint(eyePos, targetBox)); +``` + +```java +// Zen RotationUtil.java +import org.antlr.v4.runtime.misc.OrderedHashSet; +... +OrderedHashSet samplePoints = new OrderedHashSet<>(); +samplePoints.add(new Vec3(minX + maxX / 2.0, minY + maxY / 2.0, minZ + maxZ / 2.0)); +samplePoints.add(RotationUtil.closestPoint(eyePos, aABB)); +``` + +`org.antlr.v4.runtime.misc.OrderedHashSet` 是 ANTLR 解析器运行时里的类,**任何一个正常人写的旋转工具类都不会去 import 它**。两边都用它存 `Vec3` 采样点;连"先 add 中心点、再 add 最近点"的顺序和起手式都一模一样。 + +### Sensitivity GCD 公式 `f * f * f * 1.2F` + +```java +// Naven RotationUtils.getFixedRotation +final float f = (float) (mc.options.sensitivity * 0.6F + 0.2F); +final float gcd = f * f * f * 1.2F; +final float deltaYaw = yaw - lastYaw; +final float deltaPitch = pitch - lastPitch; +final float fixedDeltaYaw = deltaYaw - (deltaYaw % gcd); +final float fixedDeltaPitch = deltaPitch - (deltaPitch % gcd); +return new Vector2f(lastYaw + fixedDeltaYaw, lastPitch + fixedDeltaPitch); +``` + +```java +// Zen RotationUtil.getSensitivitySnappedRotation +float sensitivityFactor = (float)(mc.options.sensitivity().get() * 0.6f + 0.2f); +float gcd = sensitivityFactor * sensitivityFactor * sensitivityFactor * 1.2f; +float yawDelta = yaw - prevYaw; +float pitchDelta = pitch - prevPitch; +float snappedYawDelta = yawDelta - yawDelta % gcd; +float snappedPitchDelta = pitchDelta - pitchDelta % gcd; +return new Rotation(prevYaw + snappedYawDelta, prevPitch + snappedPitchDelta); +``` + +魔法常量 `0.6 / 0.2 / 1.2` 三件套 + `f * f * f` 立方 + `delta - delta % gcd` 求余截断,整套公式逐行同构。 + +### `rotationTo` 的 yaw / pitch 公式 + +```java +// Naven RotationUtils.getRotations +double x = target.x - eye.x; +double y = target.y - eye.y; +double z = target.z - eye.z; +double diffXZ = Math.sqrt(x * x + z * z); +float yaw = (float) Math.toDegrees(Math.atan2(z, x)) - 90.0f; +float pitch = (float) (-Math.toDegrees(Math.atan2(y, diffXZ))); +return new Vector2f(MathHelper.wrapDegrees(yaw), MathHelper.wrapDegrees(pitch)); +``` + +```java +// Zen RotationUtil.exactRotation +double dx = to.x - from.x; +double dy = to.y - from.y; +double dz = to.z - from.z; +double horizontalDist = Math.sqrt(dx * dx + dz * dz); +float yaw = (float)Math.toDegrees(Math.atan2(dz, dx)) - 90.0f; +float pitch = (float)(-Math.toDegrees(Math.atan2(dy, horizontalDist))); +return new Rotation(Mth.wrapDegrees(yaw), Mth.wrapDegrees(pitch)); +``` + +yaw 用 `atan2(z, x) - 90`、pitch 用 `-atan2(y, sqrt(x² + z²))`,外层 `Math.toDegrees` 强转 float 再 `wrapDegrees`,连 `- 90.0f` 的字面量写法都一致。这段代码在 Zen 里出现了 4 次(`rotationTo / exactRotation / rotationFromDeltas / createRotation`)。 + +### `BlockUtil.canBeClicked / getBoundingBox` + +```java +// Naven BlockUtils.java +public static AABB getBoundingBox(BlockPos pos) { + return getOutlineShape(pos).bounds().move(pos); +} +private static VoxelShape getOutlineShape(BlockPos pos) { + return getState(pos).getShape(mc.level, pos); +} +public static boolean canBeClicked(BlockPos pos) { + return getOutlineShape(pos) != Shapes.empty(); +} +``` + +```java +// Zen BlockUtil.java +public static boolean canBeClicked(BlockPos blockPos) { + return BlockUtil.getVoxelShape(blockPos) != Shapes.empty(); +} +public static AABB getBoundingBox(BlockPos blockPos) { + return BlockUtil.getVoxelShape(blockPos).bounds().move(blockPos); +} +private static VoxelShape getVoxelShape(BlockPos blockPos) { + return BlockUtil.getBlockState(blockPos).getShape(mc.level, blockPos); +} +``` + +`!= Shapes.empty()`(而不是更地道的 `.isEmpty()`)这种引用比较的写法两边一致;三段方法的实现一行一行对得上。 + +### `MovementUtil` direction 函数 —— 7 个 if 分支顺序与角度全一致 + +```java +// Naven MoveUtils.direction +if (forward != 0.0F || strafe != 0.0F) { + if (isMovingBack && !isMovingSideways) return direction + 180.0F; + else if (isMovingForward && isMovingLeft) return direction + 45.0F; + else if (isMovingForward && isMovingRight) return direction - 45.0F; + else if (!isMovingStraight && isMovingLeft) return direction + 90.0F; + else if (!isMovingStraight && isMovingRight) return direction - 90.0F; + else if (isMovingBack && isMovingLeft) return direction + 135.0F; + else if (isMovingBack) return direction - 135.0F; +} +return direction; +``` + +```java +// Zen MovementUtil.getDirectionAngle +if (forward != 0.0f || strafe != 0.0f) { + if (forwardNegative && !hasStrafe) return yaw + 180.0f; + if (forwardPositive && strafeNegative) return yaw + 45.0f; + if (forwardPositive && strafePositive) return yaw - 45.0f; + if (!hasForward && strafeNegative) return yaw + 90.0f; + if (!hasForward && strafePositive) return yaw - 90.0f; + if (forwardNegative && strafeNegative) return yaw + 135.0f; + if (forwardNegative) return yaw - 135.0f; +} +return yaw; +``` + +7 个分支的顺序、判断组合、`+180 / +45 / -45 / +90 / -90 / +135 / -135` 七个常量一字不差。Zen 仅改了变量名(`isMovingBack` → `forwardNegative`),把 `else if` 改成提前 `return`,这是典型的"反编译后稍微整形"痕迹。注意 Zen 这个 `getDirectionAngle` 是 `private` 且**根本没被调用**(死代码),更说明是整团抄过来的。 + +### `RayTraceUtil.pick` 的 `+ 1.62` 硬编码 eye height + +```java +// Naven RayTraceUtils.pick +Vec3 vec3 = new Vec3(mc.player.getX(), mc.player.getY() + 1.62, mc.player.getZ()); +Vec3 vec31 = calculateViewVector(pXRot, pYRot); +Vec3 vec32 = vec3.add(vec31.x * pHitDistance, vec31.y * pHitDistance, vec31.z * pHitDistance); +return mc.player.level.clip(new ClipContext(vec3, vec32, + ClipContext.Block.OUTLINE, + pHitFluids ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, mc.player)); +``` + +```java +// Zen RayTraceUtil.rayTrace +Vec3 eyePos = new Vec3(mc.player.getX(), mc.player.getY() + 1.62, mc.player.getZ()); +Vec3 viewVec = RayTraceUtil.getViewVector(pitch, yaw); +Vec3 endPos = eyePos.add(viewVec.x * range, viewVec.y * range, viewVec.z * range); +return mc.player.level().clip(new ClipContext(eyePos, endPos, + ClipContext.Block.OUTLINE, + clipFluids ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, mc.player)); +``` + +在 1.17+ 应该用 `mc.player.getEyeHeight()` 或 `getEyePosition(partialTicks)`,硬编码 `+ 1.62` 是历史遗留写法。两边都犯同一个 hardcode + 同样的 `Block.OUTLINE` + 同样的三元 `Fluid.ANY : Fluid.NONE`,ClipContext 的 5 个参数顺序也一致。 + +--- + +## 结语 + +上面这些代码段,凡是真正难辩解的硬证据全都集中在三类: + +1. **字符串字面量**(`"Fake Staff Detected! ("`、`"Bot Detected! ("`、`"Bot Removed! ("`、`"Stream limit didn't work."`、`"点击使用"`、`"Compass Only"`、`"No Player Only"`、`"Show Arrows / Pearls / Potions / Eggs / Snowballs"`、`"Normal" / "Telly Bridge" / "Keep Y"`、`"Render Item Spoof"`、`"Respawn Time"`)。 +2. **魔法常量**(`+1337`、`(0.88, 1.88, 0.88)`、`> 2 && < 0.0001`、`i * i + 1` 附魔加成、72000 / 0.1 / 3 弓蓄力、`0.6 / 0.2 / 1.2` 灵敏度 GCD、`+ 1.62` 硬编码 eye height、RGB `(173, 12, 255)` / `(255, 238, 154)` 等)。 +3. **特征 import 与"死代码"**(工具类里出现 `org.antlr.v4.runtime.misc.OrderedHashSet`;`MovementUtil.getDirectionAngle` 是 `private` 且没人调用却整团抄了过来)。 + +这些东西在反混淆环节是**不会被自动还原**的——反编译只能把 `var1 / var2` 还原成可读变量,但字符串、常数、字节码里的 instanceof 链顺序、import 表都跟字节码绑死。它们出现在两个项目里完全一致,唯一合理解释就是源代码直接复制。