矿石与刷怪笼高亮 ESP

基本信息

属性 值
所属维度 纯客户端渲染功能,专用服务器不加载
实现类 com.myname.mymodid.client.ESPRenderer
事件接线 com.myname.mymodid.client.ClientEvents(ClientEvents.java:12-22)
注册位置 MinecraftForge.EVENT_BUS.register(new ClientEvents())(ClientProxy.java:32)
触发事件 RenderWorldLastEvent(ClientEvents.java:17)
渲染形式 红色线框立方体(GL_LINES)
关闭方式 无(见「已知问题」)

功能

在每个世界渲染帧的最后(区块渲染完成之后),扫描玩家周围的方块,把命中目标的方块以红色线框描边,从而透视被地形遮挡的矿石与刷怪笼。

代码注释明确说明了为何选 RenderWorldLastEvent 而非 FML 总线:后者只承载 FML 生命周期事件,而该事件在区块渲染结束后触发,因而不会破坏 Embeddium / Celeritas 区块渲染器共享的 OpenGL 状态(ClientEvents.java:1-4)。

该功能仅在客户端注册:ClientProxy 经 @SidedProxy 的 clientSide 挂载(MyMod.java:19),ClientEvents 只在 ClientProxy.init 中挂到 Forge 事件总线(ClientProxy.java:32),服务端走 CommonProxy,其 init 是空方法(CommonProxy.java:20)。

目标方块判定

isTargetBlock(ESPRenderer.java:39-45)先排除 null 与 Blocks.air,再分两路:

  1. 自定义集合优先:若 ClientProxy.espTargetBlocks 非空,则只高亮集合内的方块(ESPRenderer.java:42-43)。
  2. 默认判定 isDefaultTarget(ESPRenderer.java:47-63),满足任一条件即命中:
    • 该方块的任意物品字典名以 ore 或 crushedOre 开头(前缀数组 ORE_PREFIXES,ESPRenderer.java:31;遍历逻辑 ESPRenderer.java:49-56)
    • 方块非本地化名转小写后包含 spawner 或 mob_spawner(ESPRenderer.java:57-61)

扫描与缓存

rescanIfNeeded(ESPRenderer.java:65-91)以玩家坐标向下取整得到基准点(ESPRenderer.java:66-68),按 Config.espRange 做球形扫描(ESPRenderer.java:76-80),把命中方块按打包坐标存入静态缓存 SCAN_CACHE(ESPRenderer.java:36, 84)。扫描完成后向 stdout 打印一行 [ESP] scan complete: cached N blocks around (x,y,z) range=R(ESPRenderer.java:89-90)。

缓存仅在玩家跨入新整数格或配置范围变化时失效(ESPRenderer.java:71-73),命中结果与缓存坐标同时更新(ESPRenderer.java:88)。

绘制

render(float frame)(ESPRenderer.java:93-174):

  • 开头检查 ClientProxy.espEnabled(ESPRenderer.java:94),再经 FMLClientHandler 取客户端玩家与世界,任一为 null 即返回(ESPRenderer.java:98-101)
  • 缓存为空则直接返回(ESPRenderer.java:104)
  • 先备份 Tessellator.instance.drawMode,绘制结束后在 finally 中还原,避免影响区块渲染器(ESPRenderer.java:108, 172)
  • 用 lastTickPos + (pos - lastTickPos) * frame 做帧间插值(ESPRenderer.java:110-115),frame 即 partialTicks,通过 ObfuscationReflectionHelper 反射取得(ClientEvents.java:19-20)
  • glPushAttrib 保存 GL 状态(ESPRenderer.java:121-123),finally 中 glPopMatrix + glPopAttrib 还原(ESPRenderer.java:169-171)
  • 逐个方块画 12 条边(底面 4 条、顶面 4 条、竖直 4 条,ESPRenderer.java:146-160),颜色为 RGBA (1.0, 0.2, 0.2, 0.8)(ESPRenderer.java:143),线宽 2.0(ESPRenderer.java:134)
  • 绘制循环受 FRAME_BUDGET 限制(ESPRenderer.java:32, 140),超出即 break
  • 整段绘制包在 try/catch(Throwable) 中,异常打印到 stderr 且不影响游戏继续(ESPRenderer.java:165-167)

数值

数值名 值 来源
线框颜色 RGBA 1.0, 0.2, 0.2, 0.8 ESPRenderer.java:143
线宽 2.0F ESPRenderer.java:134
每帧绘制上限 500 个方块 ESPRenderer.java:32
物品字典前缀 ore、crushedOre ESPRenderer.java:31
刷怪笼匹配子串 spawner、mob_spawner ESPRenderer.java:60
默认检测半径 16(1–256 可配) Config.java:15, 33-34
混合模式 GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA ESPRenderer.java:132
深度测试 GL_LEQUAL ESPRenderer.java:129

交互

触发 行为
每帧世界渲染结束 执行一次 render(partialTicks)
玩家跨入新整数格 触发球形重扫并刷新缓存
玩家静止不动 复用缓存,不再重扫
按 L 键 无反应 —— 仓库内无任何快捷键注册代码

本功能无 GUI、无指令、无方块/物品形态,也不改变世界数据;它只读取方块状态并画线。

实现依赖

ESPRenderer 的字段头注释(ESPRenderer.java:1-8)说明:该文件直接使用 MCP 命名的字段与方法(如 Tessellator.instance、Entity.posX、Blocks.air、World.getBlock),既不经 ObfuscationReflectionHelper 也不缓存 Method.invoke,而是依赖 MCP 反混淆转换器 在类加载时把 MCP 引用改写为运行时 Searge 名——与 NEI 通过 CodeChickenCore 的同名转换器所用的做法一致。

与之配套,仓库提供了 Access Transformer 声明 META-INF/mymodid_at.cfg(由 gradle.properties:98 的 accessTransformersFile 指向),把 Entity 的 6 个坐标字段、Tessellator.field_78409_u(drawMode)、Blocks.field_150350_a(air)开放为 public 字段,并把 World.func_147439_a(III) 开放为 public 方法。

已知问题

  1. 无法关闭。 ClientProxy.espEnabled 硬编码为 true(ClientProxy.java:15)且全仓无任何赋值语句;配置项 espEnabledByDefault 只写不读;源码中不存在按键注册或按键轮询代码。详见 ESP 默认开关。
  2. 自定义目标集合是死代码。 ClientProxy.espTargetBlocks 声明为空的 HashSet(ClientProxy.java:16),全仓检索该字段只有 2 处命中——声明处与读取处(ESPRenderer.java:42)——没有任何 add 操作。因此 espTargetBlocks 恒为空集,ESPRenderer.java:43 的 targets.contains(block) 分支在运行时永远不可达,所有目标都走默认判定。
  3. 每帧绘制上限与扫描范围不联动。 半径 256 时可缓存数亿方块,但每帧只画前 500 个,且遍历的是 HashMap.entrySet(),输出顺序不确定。扫描结果越多,被实际画出的反而越是随机子集。

相关条目