ShulkerBoxDuplicate.java v1.0
潜影盒被破坏10次后其自身物品(包括NBT内容)自我复制
package com.myscriptirc.shulkerduplicate;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* @pluginName ShulkerBoxDuplicate
* @author ScriptIrc Engine
* @version 1.0
* @description 潜影盒被破坏10次后其自身物品(包括NBT内容)自我复制
*/
public class ShulkerBoxDuplicate extends JavaPlugin implements Listener {
// 保存统计的文件(物理存储)
private File statsFile;
private FileConfiguration statsConfig;
// 支持的潜影盒种类
private final List<Material> shulkerBoxTypes = new ArrayList<>();
// 每次服务端启动或数据有变都会自动保存文件
@Override
public void onEnable() {
// 初始化潜影盒种类
for (Material m : Material.values()) {
if (m.name().endsWith("SHULKER_BOX")) {
shulkerBoxTypes.add(m);
}
}
// 配置文件初始化
statsFile = new File(getDataFolder(), "shulker_stats.yml");
if (!statsFile.exists()) {
try {
getDataFolder().mkdirs();
statsFile.createNewFile();
} catch (IOException e) {
getLogger().warning("无法创建统计数据文件");
}
}
statsConfig = YamlConfiguration.loadConfiguration(statsFile);
Bukkit.getPluginManager().registerEvents(this, this);
getLogger().info("ShulkerBoxDuplicate 插件已启用");
}
@Override
public void onDisable() {
this.saveStats();
}
/**
* BlockBreak事件处理:核心逻辑
*/
@EventHandler(ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
Block block = event.getBlock();
if (!shulkerBoxTypes.contains(block.getType())) {
return; // 不是潜影盒
}
// 获取其对应掉落物,唯一识别其NBT
BlockState state = block.getState();
ItemStack shulkerItem = new ItemStack(block.getType());
if (state instanceof org.bukkit.block.ShulkerBox) {
org.bukkit.block.ShulkerBox shulkerBox = (org.bukkit.block.ShulkerBox) state;
BlockStateMeta meta = (BlockStateMeta) shulkerItem.getItemMeta();
meta.setBlockState(shulkerBox);
shulkerItem.setItemMeta(meta);
}
// 生成一个可唯一标识的hash
String boxHash = getShulkerBoxHash(shulkerItem);
int count = statsConfig.getInt(boxHash, 0) + 1;
if (count < 10) {
// 统计+1并保存
statsConfig.set(boxHash, count);
saveLater();
return;
}
// 10次,掉落多一个并重置
statsConfig.set(boxHash, 0);
saveLater();
// 延迟1tick掉落复制体,确保原版掉落已完成
final ItemStack cloned = shulkerItem.clone();
Bukkit.getScheduler().runTaskLater(this, () -> {
block.getWorld().dropItemNaturally(block.getLocation().add(0.5,0.2,0.5), cloned);
}, 1);
}
// 哈希方法:保证唯一性并支持存储不泄露具体内容
private String getShulkerBoxHash(ItemStack itemStack) {
try {
ItemMeta meta = itemStack.getItemMeta();
StringBuilder sb = new StringBuilder();
sb.append(itemStack.getType().name());
if (meta instanceof BlockStateMeta) {
BlockStateMeta bsm = (BlockStateMeta) meta;
if (bsm.hasBlockState() && bsm.getBlockState() instanceof org.bukkit.block.ShulkerBox) {
org.bukkit.block.ShulkerBox box = (org.bukkit.block.ShulkerBox) bsm.getBlockState();
// 将其中物品名字/种类/数量/自定义名打包 hash
for (ItemStack is : box.getInventory().getContents()) {
sb.append(":");
if(is != null) {
sb.append(is.getType()).append("_").append(is.getAmount());
if (is.hasItemMeta()) {
sb.append(Objects.toString(is.getItemMeta().getDisplayName(), ""));
sb.append(Objects.toString(is.getItemMeta().getLocalizedName(), ""));
}
}
}
// 也可加入染色情况等信息 或自定义名
}
}
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(sb.toString().getBytes());
StringBuilder hexStr = new StringBuilder();
for (byte b : hashBytes) {
String s = Integer.toHexString(b & 0xff);
if (s.length() < 2) hexStr.append('0');
hexStr.append(s);
}
return hexStr.toString();
} catch (NoSuchAlgorithmException e) {
return String.valueOf(itemStack.hashCode()); // fallback
}
}
/**
* 异步延迟保存,减少IO压力
*/
private void saveLater() {
new BukkitRunnable() {
public void run() { saveStats(); }
}.runTaskAsynchronously(this);
}
private void saveStats() {
try {
statsConfig.save(statsFile);
} catch (IOException e) {
getLogger().warning("无法保存潜影盒统计: " + e.getMessage());
}
}
// 管理员命令重载配置与查看
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
statsConfig = YamlConfiguration.loadConfiguration(statsFile);
sender.sendMessage("§aShulkerBoxDuplicate 配置已重载");
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("stats")) {
sender.sendMessage("§e共计追踪潜影盒类型: " + statsConfig.getKeys(false).size());
return true;
}
return false;
}
}