服务端指令
基本信息
| 属性 | 值 |
|---|---|
| 注册位置 | FMLServerStartingEvent,hooks/HEHooksShared.java:151-153 |
| 指令总数 | 3 |
| 权限 | 全部未覆写 getRequiredPermissionLevel() → 继承 CommandBase 默认值 4(OP) |
| 别名 | 全部未覆写 getCommandAliases() → 无别名 |
HEServer 实例在同一次事件里初始化
(hooks/HEHooksShared.java:154-156):
if (event.getSide() == Side.SERVER || event.getServer().isSingleplayer()) {
HEServer.instance = HEServer.load(event.getServer().worldServers[0]);
}
3 个指令
| 指令 | 用法 | 实现类 | 行 |
|---|---|---|---|
/hesetwater |
/hesetwater <controllerId> <waterLevel> |
server/commands/HECommandSetWater.java |
HECommandSetWater.java:20 |
/hedebug |
/hedebug <id> <on|off> |
server/commands/HECommandDebug.java |
HECommandDebug.java:19 |
/helistcontrollers |
/helistcontrollers |
server/commands/HECommandListControllers.java |
HECommandListControllers.java:20 |
/hedebug的用法串没有前导斜杠(getCommandName() + " <id> <on|off>"), 另外两个有("/" + getCommandName() + ...)。仅影响报错提示文案。
/hesetwater <controllerId> <waterLevel>
HECommandSetWater.java:24-52:
float waterLevel = Float.parseFloat(params[1]);
int controllerId = Integer.parseInt(params[0]);
if (controllerId >= 0 || controllerId < HEConfig.maxDams) {
HEServer.instance.setWaterLevel(controllerId, waterLevel);
sender.addChatMessage(new ChatComponentText(
"Set water level of controller " + controllerId + " to " + waterLevel));
HE.info(sender.getCommandSenderName() + " set water level of controller "
+ controllerId + " to " + waterLevel);
} else {
flag = false;
}
| 行为 | 说明 |
|---|---|
waterLevel |
Float.parseFloat → 接受小数(如 64.5) |
controllerId |
Integer.parseInt |
| 副作用 | 调 HEServer.setWaterLevel(int, float) → HEDam.setWaterLevel(float)(受 minimalWaterUpdateInterval 节流) |
| 日志 | 同时发聊天消息与 HE.info 日志(含执行者名 getCommandSenderName()) |
| 失败提示 | "Could not parse command!\n" + getCommandUsage(null) |
⚠️ 源码缺陷:边界检查用了 ||,恒为真
HECommandSetWater.java:31:
if (controllerId >= 0 || controllerId < HEConfig.maxDams) {
应为 &&,实际写成了 ||。
|| 的语义是「非负 或 小于 maxDams」——
对任何 int 都至少满足其中一个,因此这个边界检查恒为真,等于完全失效。
正确写法应是:
if (controllerId >= 0 && controllerId < HEConfig.maxDams) {
实际后果:
| 输入 | 走向 |
|---|---|
controllerId 超出 [0, maxDams) |
仍然进入分支 → HEServer.setWaterLevel(controllerId, ...) → HEServer.java:119 dams[waterId].setWaterLevel(...) → ArrayIndexOutOfBoundsException |
| 异常去向 | 被同方法的 catch (Exception ex) 捕获(HECommandSetWater.java:44)→ flag = false |
| 用户看到 | "Could not parse command!" —— 误导性提示(实际是 ID 越界,不是「无法解析」) |
| 负数 ID | 同样越界(dams[-1]),表现一致 |
dams 数组长度 = HEConfig.maxDams(HEServer.java:27),
HEServer.setWaterLevel / setMode 等访问器均无边界检查(如 HEServer.java:118-126)。
/hedebug <id> <on|off>
HECommandDebug.java:23-56:
int controllerId = Integer.parseInt(params[0]);
boolean debugState = false;
if (params[1].equalsIgnoreCase("on")) {
debugState = true;
} else if (params[1].equalsIgnoreCase("off")) {
debugState = false;
} else {
flag = false;
}
if (flag) {
HEServer.instance.setMode(controllerId, debugState ? HE.DamMode.DEBUG : HE.DamMode.SPREAD);
...
}
on / off |
映射到的 DamMode |
|---|---|
on |
HE.DamMode.DEBUG(getValue() == 2) |
off |
HE.DamMode.SPREAD(getValue() == 3) |
⚠️ 不能切回
DRAIN——off映射到SPREAD而非DRAIN。 排水模式只能通过坝 GUI 设置。
⚠️ 完全没有 ID 边界检查(
grep 'controllerId >=\|maxDams'在本文件无命中), 越界 ID 直接进dams[waterId]→ArrayIndexOutOfBoundsException→ 被catch (Exception ex)吞掉 → 同样显示"Could not parse command!"。
⚠️
DEBUG模式无法持久化:HEDam.writeToNBTFull只存drainState = (mode == DRAIN)布尔(HEDam.java:72), 重载后DEBUG会变成SPREAD(HEDam.java:63-67)。见 水体方块与扩散系统。
/helistcontrollers
HECommandListControllers.java:24 起。遍历 dams,
对 dam.isPlaced() 为真的项输出一条描述串:
public String getShortDescription() {
return "HEController @(" + blockX + ", " + blockY + ", " + blockZ + ")";
}
(HEDam.java:316-318)
配套 HEServer.getControllerCoordinates()(HEServer.java:241+)返回 List<String>。
helistcontrollers是查到合法controllerId的正规途径—— 因为hesetwater/hedebug的边界检查都不可靠。
三个指令的共同缺陷
| # | 位置 | 问题 |
|---|---|---|
| 1 | HECommandSetWater.java:31 |
|| 应为 &&,边界检查恒真 |
| 2 | HECommandDebug.java |
无 ID 边界检查 |
| 3 | 三个指令的 catch (Exception ex) |
把越界、数值格式错误等所有异常统一报成 "Could not parse command!",无法区分 |
| 4 | 三个指令 | 无中文本地化(addChatMessage(new ChatComponentText(...)) 全是硬编码英文,且 en_US.lang 中无对应键) |