如何防止Spigot插件中玩家重复破坏放置方块无限扩展世界边界?
Spigot插件世界边界扩展防刷解决方案
问题分析
当前代码的核心问题是未区分自然生成的矿石和玩家重新放置的矿石,导致玩家可以无限重复破坏-放置操作刷世界边界扩展。
解决方案一:区分玩家放置的方块
通过监听方块放置事件,给玩家放置的目标矿石添加元数据标记,破坏时检查该标记,仅处理无标记的自然生成方块。
1. 注册方块放置监听器
给玩家放置的特定矿石添加元数据:
@EventHandler public void onBlockPlace(BlockPlaceEvent e) { Material blockType = e.getBlock().getType(); // 仅针对目标矿石类型添加标记 if (blockType == Material.DIAMOND_ORE || blockType == Material.IRON_ORE || blockType == Material.GOLD_ORE || blockType == Material.ANCIENT_DEBRIS) { // 替换yourPluginInstance为你的插件实例对象 e.getBlock().setMetadata("playerPlaced", new FixedMetadataValue(yourPluginInstance, true)); } }
2. 修改方块破坏监听器
检查方块是否有玩家放置标记,无标记时才执行边界扩展:
@EventHandler public void onBlockBreak(BlockBreakEvent e) { Block block = e.getBlock(); Material blockType = block.getType(); // 跳过玩家放置的方块 if (block.hasMetadata("playerPlaced")) { return; } switch (blockType) { case DIAMOND_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 6 1"); break; case IRON_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 0.5 1"); break; case GOLD_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 1 1"); break; case ANCIENT_DEBRIS: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 0.5 1"); break; default: // 其他方块不处理 break; } }
解决方案二:记录已处理的方块坐标
通过持久化存储(配置文件/数据库)记录已触发过边界扩展的方块坐标,确保每个方块仅生效一次。
1. 插件生命周期中管理存储
private Set<String> processedBlocks = new HashSet<>(); @Override public void onEnable() { saveDefaultConfig(); // 从配置文件读取已处理的方块 processedBlocks.addAll(getConfig().getStringList("processed-blocks")); } @Override public void onDisable() { // 将已处理方块保存到配置文件 getConfig().set("processed-blocks", new ArrayList<>(processedBlocks)); saveConfig(); }
2. 修改方块破坏监听器
检查方块坐标是否已记录,未记录时执行扩展并添加到存储:
@EventHandler public void onBlockBreak(BlockBreakEvent e) { Block block = e.getBlock(); Material blockType = block.getType(); // 生成唯一坐标标识:世界名,x,y,z String blockKey = String.format("%s,%d,%d,%d", block.getWorld().getName(), block.getX(), block.getY(), block.getZ()); // 跳过已处理的方块 if (processedBlocks.contains(blockKey)) { return; } switch (blockType) { case DIAMOND_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 6 1"); processedBlocks.add(blockKey); break; case IRON_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 0.5 1"); processedBlocks.add(blockKey); break; case GOLD_ORE: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 1 1"); processedBlocks.add(blockKey); break; case ANCIENT_DEBRIS: Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "worldborder add 0.5 1"); processedBlocks.add(blockKey); break; default: break; } }
方案对比
- 方案一:实现简单,性能开销低,但无法区分其他插件生成的方块,适合需求简单的场景。
- 方案二:精准控制每个方块仅触发一次,适合需要严格限制的场景,但需要维护持久化存储,大量方块时可考虑改用SQLite数据库优化性能。
内容的提问来源于stack exchange,提问作者Jimmy Graham
相关产品推荐
相关产品推荐

