/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 等覆写里用。
已知问题 / 风险
- 改编自 Beddium 但只有一行
//注释(:3),无完整 LGPL 版权头 —— 与debug/ChunkDebugMinimap.java的 21 行完整头形式不一致。许可声明是否充分,值得单独核实。 help在补全列表里但无法执行(:56放入SUBCOMMANDS,但不在HELP,:126拦下)—— 补全提示与执行能力不一致。requiresCheats用硬编码字符串而非HELPkey(:85),改名需同步两处。crashtest不受requiresCheats门控(:85只有 wireframe / flyby),只受isDeobf()门控 —— 在多人服务器上可能可主动触发客户端崩溃(取决于isDeobf()的判定,本条目未核实)。HELP/SUBCOMMANDS是静态快照(:32-33),运行期配置变化不反映。- 回复消息全部硬编码英文,无语言键,与仓库的本地化体系不一致。
switch使用 Java 14+ arrow 表达式(:136-142),本项目的编译 JDK 版本本条目未核实。- 主参数
toLowerCase()(:125)与子参数equalsIgnoreCase()(:172)风格不一致。 CeleritasDebugScreenHandlerimport 了但未找到对应 case(:15)—— 未使用 import 或在未读 handler 内使用,本条目不下结论。
相关条目
- 调试叠加层 -
handleMinimap的目标;ChunkDebugMinimap - Flyby 巡航录制 -
handleFlyby的 4 条 route - FPS / 性能剖析 -
handleProfile/handleTracy的两个录制器 - 渲染崩溃恢复 -
handleCrashTest的目标 - Celeritas(内嵌地形渲染引擎) -
CeleritasWorldRenderer/CeleritasDebugScreenHandler - SystemProperties -
debugTooling()/isDeobf()的判定 - KeyBinding 注册总表 - 命令是聊天输入,不占按键槽
- Subprojects(内嵌子项目) -
Tracy/TracyBackend的定义