/wandOops Command
服务器命令,撤销魔杖最近一次批量放置的方块,并把材料掉回给玩家。
基本信息
| 属性 | 值 |
|---|---|
| 命令名称 | /wandOops |
| 源码 | getCommandName() 返回 "wandOops" |
| 用法字符串 | /wandOops |
| 类 | portablejim.bbw.core.OopsCommand(继承 CommandBase) |
| 注册时机 | FMLServerStartingEvent |
| 权限等级 | 0(所有玩家) |
| 是否仅限手持魔杖 | 是 |
| 参数 | 无 |
⚠️ 命令名是
/wandOops,不是/oo。源码中没有注册任何别名 (getCommandName()只返回"wandOops",也没有覆写getCommandAliases()), 所以/oo不会被识别,会报"未知命令"。
注册
@EventHandler
public void serverStarting(FMLServerStartingEvent event) {
event.registerServerCommand(new OopsCommand());
}
本 mod 只注册这一个命令(源码中 CommandBase 的唯一子类就是 OopsCommand)。
权限
| 方法 | 返回值 | 含义 |
|---|---|---|
getRequiredPermissionLevel() |
0 |
所有玩家都能用 |
canCommandSenderUseCommand() |
true |
不做额外限制 |
权限等级 0 意味着服务器上的任何玩家都能对自己手持的魔杖执行撤销, 无需 OP。
前置条件与报错
命令在服务端执行,按顺序检查,任一不满足就抛 WrongUsageException
(客户端收到本地化错误消息):
| 顺序 | 条件 | 不满足时的语言键 | 中文消息 |
|---|---|---|---|
| 0 | 执行者必须是 EntityPlayerMP |
bbw.chat.error.bot |
AI检测使用者不是玩家。请不要再这样做了。 |
| 1 | 手持物品非空且 instanceof IWandItem |
bbw.chat.error.nowand |
你需要手持更好的建筑法杖来运行它。 |
| 2 | NBT 里有 bbw.lastPlaced(int 数组) |
bbw.chat.error.noundo |
我记不得这个建筑之杖上次修建的历史了。 |
非玩家执行(控制台 / 命令方块)会得到
bot错误。 手持非魔杖物品得到nowand。魔杖从未放置过方块(或已经撤销过一次)得到noundo。
撤销流程
1. 重建要掉的物品
GameRegistry.UniqueIdentifier lastBlock = new GameRegistry.UniqueIdentifier(bbwCompound.getString("lastBlock"));
int damageValue = bbwCompound.getInteger("lastDamage");
ItemStack itemStack = GameRegistry.findItemStack(lastBlock.modId, lastBlock.name, 1);
itemStack.setItemDamage(damageValue);
Block block = Block.getBlockFromItem(itemStack.getItem());
从 NBT 的 lastBlock(注册名)+ lastDamage(metadata)还原出物品和方块。
这些字段由每次成功放置后写入,见 魔杖物品通用行为。
2. 逐点校验并清除
for (Point3d point : pointList) {
if (world.getBlock(point.x, point.y, point.z) == block
&& world.getBlockMetadata(point.x, point.y, point.z) == damageValue) {
world.setBlockToAir(point.x, point.y, point.z);
count++;
}
}
| 关键行为 | 说明 |
|---|---|
| 双重要求 | 位置上的方块和 metadata 都必须与上次一致才清除 |
| 被玩家改动过就跳过 | 玩家在撤销前手动拆掉/替换了某个方块,那个点会被跳过(不误删) |
| 只清空气 | 用 setBlockToAir,不返还掉落物(材料统一在下一步按计数掉) |
| 坐标校验 | y >= 255 之外的越界点不会被记录,所以不涉及越界 |
3. 按倍数掉回材料
int count = lastPerBlock * amount; // amount = 成功清除的点数
int stackSize = itemStack.getMaxStackSize();
int fullStacks = count / stackSize;
for (int i = 0; i < fullStacks; i++) { … 生成满堆 EntityItem … }
… 再掉一个 count % stackSize 的余数堆 …
| 环节 | 行为 |
|---|---|
| 倍数 | lastPerBlock × 实际清除点数(lastPerBlock 是每放 1 格消耗的材料数) |
| 拆分 | 按物品最大堆叠数拆成 N 个满堆 + 1 个余数堆 |
| 掉落位置 | 全部掉在玩家脚下(player.posX/posY/posZ) |
| 尾数 | 即使余数为 0 也会额外 spawnEntityInWorld 一个 stackSize = 0 的空堆 |
4. 清除 NBT(保证只能用一次)
bbwCompound.removeTag("lastPlaced");
bbwCompound.removeTag("lastBlock");
bbwCompound.removeTag("lastDamage");
bbwCompound.removeTag("lastPerBlock");
mask 和 fluidmask 保留不变 —— 撤销不会重置锁定模式。
因为 lastPlaced 被移除,同一根魔杖无法连续撤销两次,
必须先重新放置方块才能再次撤销。
坐标解包
lastPlaced 是扁平的 int 数组,每 3 个 int 表示一个点:
protected ArrayList<Point3d> unpackNbt(int[] placedBlocks) {
int countPoints = placedBlocks.length / 3;
for (int i = 0; i < countPoints * 3; i += 3) {
output.add(new Point3d(placedBlocks[i], placedBlocks[i+1], placedBlocks[i+2]));
}
}
数组长度由 ItemBasicWand.onItemUse 写入:placedBlocks.size() * 3。
限制
| 限制 | 说明 |
|---|---|
| 只能撤销最近一次 | 每次放置覆盖 lastPlaced |
| 只能撤销自己手持的魔杖 | NBT 在物品上,换人换杖无效 |
| 只能撤销同一物品同一 metadata | 玩家改动过的方块被跳过,不会误删 |
| 不能恢复 TileEntity 数据 | 只用 setBlockToAir,已放置方块的 NBT 一并丢失 |
| 不能撤销副手(Backhand)以外的取料差异 | 材料按 lastPerBlock 统一计算,不区分来源 |
⚠️ 不返还 TileEntity NBT:撤销后重新放置得到的是默认状态的方块, Forestry 原木的树种等数据不会恢复。
相关条目
- 魔杖物品通用行为 -
lastPlaced等 NBT 字段的写入方 - BFS 方块扩展算法 - 产生
placedBlocks的搜索 - 魔杖等级系统 - 一次放置可能涉及多少个坐标