/angelica 命令(AngelicaCommand)

基本信息

属性 值
类 com.gtnewhorizons.angelica.commands.AngelicaCommand
路径 src/main/java/com/gtnewhorizons/angelica/commands/AngelicaCommand.java
行数 345
基类 net.minecraft.command.CommandBase
命令名 angelica
上游来源 ⚠️ 调试命令改编自 Beddium(Ven, FalsePattern)

⚠️ :3 有归属注释:

// Debug commands adapted from Beddium by Ven and FalsePattern

⚠️ 这是 // 行注释,不是完整 LGPL 版权头 —— 与 ChunkDebugMinimap 的 21 行完整头部不同。debug/ChunkDebugMinimap.java 有完整的 LGPL 声明,本文件只有一行归属。两处改编自同一上游,但许可声明形式不一致(一个是完整头、一个是行注释)。这是否满足 LGPL 的「随附声明」要求,值得单独确认。

commands/ 包只有这 1 个文件。

子命令的构建是静态的

HELP(:32)与 SUBCOMMANDS(:33)都是 private static final,由 buildHelp()(:36-52)与 buildSubcommands()(:54-58)在类加载时求值。

三级门控

级 条件 行 影响
1 SystemProperties.debugTooling() :38 6 个调试子命令的全部
2 SystemProperties.isDeobf() :44 额外加 crashtest
3 Tracy.ENABLED :48 额外加 tracy

6 个调试子命令(:39-43):

子命令 参数 描述 行
wireframe — Toggle wireframe rendering :39
fog — Toggle fog debug on F3 :40
minimap — Toggle chunk debug overlay :41
flyby <route> Run a deterministic benchmark route :42
profile <start|stop|status> Control async-profiler (JFR) recording :43

1 个 deobf-only:crashtest(:45,“Arm a crash on the next tile entity render”)。

1 个 Tracy-only:tracy(:49)。

⚠️ HELP 用 LinkedHashMap(:37)—— 保序,所以 getAutocompleteSuggestions(:54-58)返回的顺序是插入顺序(wireframe → fog → minimap → flyby → profile → [crashtest] → [tracy] → help)。

⚠️ buildSubcommands 的数组大小是 HELP.size() + 1(:55),然后 subcommands[HELP.size()] = "help"(:56)—— help 永远在最后,且不在 HELP 里。

⚠️ HELP 在静态初始化时快照配置。SystemProperties.debugTooling() / isDeobf() / Tracy.ENABLED 若在运行期变化,HELP 不会更新。而 :126 的分发是 if (!HELP.containsKey(subcommand)) return ... —— 不在 HELP 里的子命令即使内部有 handler 也无法执行。这是双重门控(HELP 快照 + switch)。

USAGE(:34)

private static final String USAGE = "/angelica <" + String.join("|", SUBCOMMANDS) + ">";

静态拼接,SUBCOMMANDS 变化才影响。

requiresCheats:两个子命令需要作弊

static boolean requiresCheats(String subcommand) {          // :84
    return "wireframe".equals(subcommand) || "flyby".equals(subcommand);   // :85
}

⚠️ 与 HELP 的门控是独立的两套机制:requiresCheats 检查的是字符串字面量,不是 HELP 的 key 集合。改 HELP 里的 key 不会同步改这里,反之亦然。 若将来重命名子命令,必须同时改两处,否则要么命令能过 cheats 检查却没有 handler,要么反之。

拒绝消息(:130-132):

if (requiresCheats(subcommand) && !cheatsAllowed()) {
    sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[Angelica] " + subcommand + " needs cheats enabled on a world you host"));

⚠️ 消息里没有前缀说明如何开启作弊,只说「needs cheats enabled on a world you host」。

分发:switch 表达式(:135-142)

switch (subcommand) {
    case "wireframe" -> handleWireframe(sender);
    case "fog"       -> handleFog(sender);
    case "minimap"   -> handleMinimap(sender);
    case "flyby"     -> handleFlyby(sender, args);
    case "profile"   -> handleProfile(sender, args);
    case "tracy"     -> handleTracy(sender, args);
    case "crashtest" -> handleCrashTest(sender);

7 个 arrow 分支(无 fallthrough)。subcommand 在 :125 已 args[0].toLowerCase(),所以大小写不敏感。

⚠️ switch 是 Java 14+ 的 arrow 表达式语法。1.7.10 目标环境是 Java 8 —— 本项目编译目标必须是新 JDK + 字节码降级(version_gate.py 已确认 minecraftVersion = 1.7.10,但未确认编译 JDK 版本)。这是本条目未独立核实的点。

⚠️ switch 里没有 help 分支。:126-128 的 if (!HELP.containsKey(subcommand)) 会先拦下 help(因为 help 不在 HELP 里)—— 所以 help 走不到 switch,会被当作未知子命令处理。⚠️ buildSubcommands 把 help 放进补全列表(:56),但 help 本身无法执行。这是一个真实的不一致:补全提示用户输入 help,输入后却得到「未知子命令」。

handleMinimap(:160-164)

private void handleMinimap(ICommandSender sender) {
    ChunkDebugMinimap.toggle();
    final String state = ChunkDebugMinimap.isEnabled() ? "ON" : "OFF";
    sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Chunk debug minimap: " + state));
}

toggle 后立即回读状态(先 toggle 再 isEnabled())—— 正确,避免了逻辑反转。

⚠️ ChunkDebugMinimap.toggle() 与 isEnabled() 的组合说明 enabled 确实有 @Getter 生成的 isEnabled()(见 debug 条目)。直接写 enabled 的代码绕过这个 API。

⚠️ 回复消息硬编码英文,无语言键(与 资源与语言键 里的本地化体系不一致)。

handleCrashTest(:166-169)

private void handleCrashTest(ICommandSender sender) {
    RenderRecovery.armCrashTest();
    sender.addChatMessage(new ChatComponentText(... "Crash test armed: fires on the next tile entity render"));
}

对应 渲染崩溃恢复 的 armCrashTest / throwIfCrashTestArmed 一对。 抛出点在 mixins/early/shaders/MixinTileEntityRendererDispatcher.java:21 —— 与消息文案「fires on the next tile entity render」完全一致,说明消息文本是照着 mixin 注入点写的。

⚠️ crashtest 只受 isDeobf() 门控(:44),不受 requiresCheats 门控(:85 只有 wireframe 与 flyby)。即:在多人服务器上,/angelica crashtest 可能可用(若 isDeobf() 为 true),主动让客户端崩溃。这依赖 isDeobf() 在服务器上必然为 false —— 本条目未核实 SystemProperties.isDeobf() 的判定条件(见 SystemProperties)。

handleFlyby(`:171-~200+)

if (args.length >= 2 && "cancel".equalsIgnoreCase(args[1])) {     // :172
    FlybyRunner.INSTANCE.cancel();                                 // :173
    ... "Flyby cancelled"
    return;
}
if (args.length < 2) {                                             // :178
    ... "Usage: /angelica flyby <" + FlybyRoute.ids() + "|cancel> [length] [blocksPerTick]"   // :179
    for (FlybyRoute r : FlybyRoute.values()) { ... }               // :180

⚠️ "cancel".equalsIgnoreCase(args[1])(:172)—— 但 subcommand 主参数在 :125 已 toLowerCase(),子参数却是 equalsIgnoreCase 而非 toLowerCase()。两处大小写处理风格不一致。

⚠️ FlybyRoute.ids()(:42、:179)是枚举的 id 列表方法(FlybyRoute 的 id() 在 FlybyRoute.java:46-48),4 条 route 的 id 是 straight / pan / circuit / static。见 Flyby 巡航录制。

三级子命令(:228、:247、:256):

子命令 case 行 位置
start :228 handleProfile 内
stop :247
status :256

第二组三级(:273、:295、:306)—— handleTracy 的 start / stop / status。

Tracy 状态到字符串的映射(:321-326):

case TracyBackend.CAPTURE_IDLE -> "idle";
case TracyBackend.CAPTURE_CONNECTING -> "connecting";
case TracyBackend.CAPTURE_RECORDING -> "recording";
case TracyBackend.CAPTURE_SAVING -> "saving";
case TracyBackend.CAPTURE_DONE -> "done";
case TracyBackend.CAPTURE_FAILED -> "failed";

6 个状态,全部 TracyBackend 常量(在 GLSM 子项目,见 Subprojects)。⚠️ 这是一个 switch,若 GLSM 新增状态,编译期会因缺 default 而报错(取决于是否穷尽)—— 这是好事,说明枚举扩展会被发现。

⚠️ handleProfile 控制的是 async-profiler(JFR),见 debug/profiling/AsprofRecorder(见 FPS / 性能剖析)。profile 与 tracy 是两套完全独立的录制器。

其余 handler 的依赖

Handler 依赖 行
handleWireframe AngelicaConfig(:5 import) —
handleFog AngelicaConfig / CeleritasWorldRenderer(:16 import) —
handleMinimap ChunkDebugMinimap(:7 import) :160
handleFlyby FlybyRoute / FlybyRunner(:8-9 import) :171
handleProfile AsprofRecorder(:10 import) —
handleTracy TracyCaptureNotifier(:11)/ Tracy / TracyBackend(:12-13 import) —
handleCrashTest RenderRecovery(:14 import) :166
(未见 handler) CeleritasDebugScreenHandler(:15 import) ⚠️ import 了但本条目未找到对应 case

⚠️ CeleritasDebugScreenHandler 被 import(:15)但 7 个 switch 分支里没有它 —— 可能是未使用的 import,或在 wireframe / fog handler 内部使用。本条目未读这两个 handler 的实现,不下结论。

⚠️ Minecraft(:17)、IntegratedServer(:21)、ArrayList(:25)、List(:26) 等 import 的实际使用情况未逐一核实。

未 import 但使用的类型:ICommandSender(:19)、CommandBase(:18)、ICommand(:19)、ChatComponentText(:22)、EnumChatFormatting(:23)—— 这些在 getCommandName / getCommandUsage / processCommand 等覆写里用。

已知问题 / 风险

  1. 改编自 Beddium 但只有一行 // 注释(:3),无完整 LGPL 版权头 —— 与 debug/ChunkDebugMinimap.java 的 21 行完整头形式不一致。许可声明是否充分,值得单独核实。
  2. help 在补全列表里但无法执行(:56 放入 SUBCOMMANDS,但不在 HELP,:126 拦下)—— 补全提示与执行能力不一致。
  3. requiresCheats 用硬编码字符串而非 HELP key(:85),改名需同步两处。
  4. crashtest 不受 requiresCheats 门控(:85 只有 wireframe / flyby),只受 isDeobf() 门控 —— 在多人服务器上可能可主动触发客户端崩溃(取决于 isDeobf() 的判定,本条目未核实)。
  5. HELP / SUBCOMMANDS 是静态快照(:32-33),运行期配置变化不反映。
  6. 回复消息全部硬编码英文,无语言键,与仓库的本地化体系不一致。
  7. switch 使用 Java 14+ arrow 表达式(:136-142),本项目的编译 JDK 版本本条目未核实。
  8. 主参数 toLowerCase()(:125)与子参数 equalsIgnoreCase()(:172)风格不一致。
  9. CeleritasDebugScreenHandler import 了但未找到对应 case(:15)—— 未使用 import 或在未读 handler 内使用,本条目不下结论。

相关条目