You need to enable JavaScript to run this app. Spigot插件开发:resetblocks命令无法正确重置放置/破坏方块的问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Spigot插件开发:resetblocks命令无法正确重置放置/破坏方块的问题求助

插件功能异常的核心问题与修复方案

你之前猜测的作用域问题其实不存在——既然控制台能正常打印方块放置、破坏的日志,说明命令执行器和事件监听器使用的是同一个类实例,两个列表的引用是共享的,数据不会出现隔离。真正的问题出在几个基础逻辑错误上:

  • 对局部变量的赋值完全不会影响实际方块状态
    你在循环里写的Material blockMaterial = block.getType(),只是把方块当前的材质值读取出来存到局部变量里,后续把这个局部变量改成AIR,本质是在改一个临时副本,根本不会同步到世界里的实际方块。要修改方块材质必须直接调用block.setType(目标材质)方法才会生效。你写的blockMaterial = blockMaterial属于无意义的自赋值,不会产生任何实际效果。
  • 被破坏方块的存储时机完全错误
    BlockBreakEvent触发时,方块已经进入破坏流程、即将被替换成空气,你在这个节点把Block对象存进列表,等后续执行reset命令时,拿到的方块本身已经是空气状态,根本拿不到被破坏前的原始材质。必须在事件触发的瞬间,先把方块的原始材质值记下来,不能等方块消失了再取数据。
  • 直接存储Block对象存在长期失效风险
    Spigot的Block对象是区块加载时生成的临时句柄,只要对应区块被卸载、服务器重启,之前存的Block引用就会变成无效句柄,哪怕你写对了setType操作,也会改到错误位置甚至抛出空指针。正确的存储方式应该是记录方块所在的世界名、三维坐标、原始材质这类可持久化的信息,需要修改方块时再根据坐标临时获取Block对象。
  • 在主线程直接调用Thread.sleep会直接卡死服务器
    Spigot的所有方块修改操作都必须在服务器主线程执行,你在命令触发的主线程循环里每处理一个方块就睡500ms,会直接阻塞整个服务器的主循环,导致所有玩家卡顿、服务器tick超时甚至崩溃。如果需要批量处理时做间隔,必须用Bukkit自带的任务调度系统分tick执行,绝对不能在主线程做任意时长的阻塞休眠。

修正后的核心代码逻辑

首先定义轻量的数据结构替代直接存储Block对象:

// 记录方块的可序列化状态,避免存失效的Block引用
record BlockRecord(String worldName, int x, int y, int z, Material originalMaterial) {}

把类里的存储列表替换为上述数据结构:

// 替换原来的Block列表
List<BlockRecord> placedBlocks = new ArrayList<>();
List<BlockRecord> brokenBlocks = new ArrayList<>();

修正事件监听的存储逻辑,在事件触发的正确节点记录数据:

@EventHandler
public void blockPlaceEvent(BlockPlaceEvent event) {
    Block block = event.getBlock();
    placedBlocks.add(new BlockRecord(
        block.getWorld().getName(),
        block.getX(),
        block.getY(),
        block.getZ(),
        block.getType()
    ));
}

@EventHandler
public void blockBreakEvent(BlockBreakEvent event) {
    Block block = event.getBlock();
    // 事件触发瞬间存储原始材质,此时方块还未被替换成空气
    brokenBlocks.add(new BlockRecord(
        block.getWorld().getName(),
        block.getX(),
        block.getY(),
        block.getZ(),
        block.getType()
    ));
}

修正命令执行的修改逻辑,去掉无意义的sleep,直接操作方块:

@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
    if (!cmd.getName().equalsIgnoreCase("resetblocks")) return true;

    // 把玩家放置的方块全部改为空气
    for (BlockRecord record : placedBlocks) {
        World world = Bukkit.getWorld(record.worldName());
        if (world == null) continue;
        Block block = world.getBlockAt(record.x(), record.y(), record.z());
        block.setType(Material.AIR);
    }

    // 把玩家破坏的方块恢复为原始材质
    for (BlockRecord record : brokenBlocks) {
        World world = Bukkit.getWorld(record.worldName());
        if (world == null) continue;
        Block block = world.getBlockAt(record.x(), record.y(), record.z());
        block.setType(record.originalMaterial());
    }

    // 重置完成后清空列表,避免重复处理
    placedBlocks.clear();
    brokenBlocks.clear();
    sender.sendMessage("方块状态已全部重置");
    return true;
}

如果需要在批量处理时加间隔避免瞬间修改大量方块造成卡顿,可以用BukkitRunnable创建同步分tick任务替代原来的sleep方法,不要直接在线程里做休眠操作。

内容的提问来源于stack exchange,提问作者santres

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 13:45:03