调试菜单

调试菜单

一个独立的Minecraft Fabric调试工具包。它将调试开关、HUD叠加层和玩家行为日志收集到一个可滚动菜单中,并公开一个API,以便其他模组可以将自己的调试开关接入同一屏幕。

Debug Menu

一个适用于 Minecraft Fabric 的独立调试工具包。它将调试开关、HUD 叠加层和玩家行为日志收集到一个可滚动的菜单中,并暴露一个 API,让其他模组可以将自己的调试开关接入同一个界面。

  • Minecraft:1.20.4(默认)/ 1.20.1(单一源码树,构建时选择目标版本)
  • Fabric Loader:>= 0.15.0(需要 Fabric API)
  • Java:17
  • 环境:客户端 + 服务端
  • 作者:liuzeen1234 (liuzeen1234@qq.com)
  • 许可证:MIT

功能

统一调试菜单

使用可配置的按键绑定打开菜单(默认未绑定,可在 选项 → 控制 → Debug Menu 中设置)。该界面会读取所有已注册的调试开关,并按模组 ID 分组,当列表溢出时支持滚动。如果没有注册任何内容,则只显示内置的 HUD 设置项。

HUD 叠加层

  • 实体生命值(右上角):以 [name][current/max] 的形式显示准星所指实体的名称和生命值。非生物实体显示为 [name][-/-]。可启用详细的 NBT 显示;客户端会向服务端请求实体 NBT 并缓存响应。追踪距离可配置(1–256,默认 128)。
  • 手持物品信息(左上角):显示主手物品名称和堆叠数量。高级模式会添加耐久度和全套 NBT 标签,以缩小比例渲染并自动换行。

实时玩家行为日志

启用后,玩家行为会通过 DebugMenu 日志记录器写入:

  • 战斗与状态:攻击、受到伤害、死亡、饥饿值变化
  • 移动与姿态:跳跃、移动、疾跑 / 潜行 / 游泳 / 飞行状态切换
  • 物品与交互:丢弃物品、快捷栏切换、使用物品、右键点击方块、破坏方块
  • 客户端输入:按键、鼠标点击和滚轮、界面打开/关闭

持久化配置

开关状态和 HUD 设置存储在 config/debug-menu.json 中,并在更改时立即保存,因此重启后依然保留。

面向其他模组开发者的 API

在你的模组初始化期间注册一个开关,调试菜单会自动为其构建 UI:

DebugMenuApi.register(new DebugToggleEntry(
"my-mod", // 所属模组 ID(用于分组)
"my-mod:feature_debug", // 唯一键
"Feature Debug", // 菜单中的显示名称
() -> myDebugEnabled, // getter
v -> { myDebugEnabled = v; saveConfig(); } // setter
));

其他可用方法:

  • DebugMenuApi.registerAll(Collection<DebugToggleEntry>) — 批量注册
  • DebugMenuApi.isEnabled(String key) — 从你自己的代码中查询某个开关
  • DebugMenuApi.getEntries() / getEntriesByMod() / getEntry(key) — 读取已注册的条目

注册表由 CopyOnWriteArrayList 支持,因此跨线程读取是安全的。

自定义分组显示名称

菜单按 modId 对条目分组,并使用 modId 作为分组标题。要显示更友好的标题,请在初始化时注册一次显示名称:

DebugMenuApi.setModDisplayName("my-mod", "My Mod");
  • 应用于该 modId 下的所有条目(布尔开关 / 数值滑块 / 多状态切换);只需调用一次。
  • 未设置时,标题回退为 modId,因此完全向后兼容。
  • 分组、折叠和查找仍然以 modId 为键;更改显示名称不会影响它们。
  • 传入 null 或空白字符串会清除已注册的名称(回退为 modId);getModDisplayName(modId) 读取当前名称(未设置时返回 modId)。

数值条目(滑块,支持服务端同步)

当你需要一个受 min/max/step 约束的整数值时,注册一个 DebugValueEntry,菜单会将其渲染为滑块:

DebugMenuApi.registerValue(new DebugValueEntry(
"my-mod", // 所属模组 ID
"my-mod:spawn_rate", // 唯一键
"Spawn Rate", // 显示名称
0, 100, // 最小值 / 最大值(含)
() -> spawnRate, // getter
v -> { spawnRate = v; saveConfig(); } // setter(值会在内部被限制到 [min, max])
));

键的约定:

  • 在两侧都注册:同一个 key 必须在客户端注册一次、在服务端注册一次。客户端条目驱动 UI(本地滑块显示、发送数据包);服务端条目在收到同步数据包时在服务端主线程上运行。两者通过共享的 key 匹配。
  • 侧标记:每个条目都会标记 DebugValueEntry.Side(CLIENT / SERVER / BOTH)。在单人游戏中,客户端和集成服务端共享同一个 JVM,因此侧过滤可避免重复渲染 UI 以及在写回时更新错误的对象。仅当 getter/setter 都指向同一状态时才使用 BOTH。
  • 权限:在应用客户端值之前,服务端会执行权限检查,默认要求权限等级 >= 2。可通过向完整构造函数传入自定义 BiPredicate<ServerPlayerEntity, Integer> 来覆盖。
  • 选项:完整构造函数支持自定义步长(step > 0)和单位后缀(例如 "blocks"、"%")。
  • 其他方法:registerAllValues(...) 用于批量注册,getValueEntry(key) / getValueEntry(key, side) 用于查询,getValueEntriesByMod(side) 用于按模组分组(按侧过滤并去重)。

条件 / 嵌套开关

开关可以仅在满足某个条件时显示,从而让你构建"父开关 → 子选项"的层级结构。向 DebugToggleEntry 传入一个可见性谓词:

// 仅在父布尔开关 my-mod:feature 开启时显示
DebugMenuApi.register(new DebugToggleEntry(
"my-mod", "my-mod:detail", "Detail Sub-option",
() -> detailOn, v -> { detailOn = v; save(); },
DebugMenuApi.visibleWhenEnabled("my-mod:feature")));

// 仅在父多状态切换 my-mod:mode 为 "Advanced" 或 "Expert" 时显示
DebugMenuApi.register(new DebugToggleEntry(
"my-mod", "my-mod:expert_opt", "Expert Option",
() -> expertOn, v -> { expertOn = v; save(); },
DebugMenuApi.visibleWhenOption("my-mod:mode", "Advanced", "Expert")));
  • visibleWhenEnabled(parentKey):当父布尔开关开启时可见;缺少父键视为关闭。
  • visibleWhenOption(parentKey, states...):当父多状态切换的当前状态匹配 states 之一时可见;缺少键或状态不匹配则隐藏。

多状态切换(自定义状态名称)

除了开/关布尔开关之外,你还可以注册一个具有多个状态且名称完全自定义的切换(例如语言选择器)。菜单会将其渲染为一个按钮,点击时循环切换状态:

DebugMenuApi.registerOption(new DebugOptionEntry(
"my-mod", // 所属模组 ID(用于分组)
"my-mod:language", // 唯一键
"Language", // 显示名称
java.util.List.of("English", "简体中文", "日本語"), // 状态名称(顺序 = 循环顺序)
() -> currentLanguage, // getter:返回当前状态名称
v -> { currentLanguage = v; saveConfig(); } // setter:存储新的状态名称
));

注意事项:

  • 状态由**名称(String)**标识。getter 应返回所列状态之一;如果返回无效名称(或 null),菜单会回退到第一个状态,而不是崩溃。
  • 多状态切换是客户端侧概念(与布尔开关类似):状态仅在客户端更改,不与服务端同步。
  • 其他方法:registerAllOptions(...) 用于批量注册,getSelectedOption(String key) 用于查询当前状态名称,getOptionEntries() / getOptionEntriesByMod() / getOptionEntry(key) 用于读取已注册的条目。

构建

默认目标来自 gradle.properties 中的 default_mc(当前为 1.20.4),因此直接使用命令即可:

./gradlew build
./gradlew runClient

使用 -Pmc 切换目标(在 PowerShell 中需加引号):

./gradlew build "-Pmc=1.20.1"
./gradlew runClient "-Pmc=1.20.1"

产物位于 build/libs/ 中,文件名包含游戏版本,例如 debug-menu-mc1.20.4-1.0.0.jar。

编译固定使用 JDK 17(参见 gradle.properties 中的 org.gradle.java.home 以及 build.gradle 中的工具链)。如果其他机器上的 JDK 17 路径不同,请编辑 gradle.properties 中的那一行。

每个目标的 Yarn 映射和 Fabric API 版本位于 build.gradle 的 supportedVersions 映射中;添加新目标只需添加一个条目。