ShulkerBoxDuplicate

ShulkerBoxDuplicate.java v1.0
潜影盒被破坏10次后其自身物品(包括NBT内容)自我复制
作者: ScriptIrc Engine
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;
    }
}

上一篇: CatAttrib下一篇: ObserverControl

举报内容

意见反馈