You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从自定义铁砧菜单获取玩家输入?Spigot插件开发问题

Spigot插件自定义铁砧UI获取玩家输入问题

我在开发Spigot插件时,需要从自定义铁砧UI获取玩家输入:玩家输入指令后打开自定义铁砧UI,通过重命名物品输入字符串,点击输出槽物品时获取该名称来处理输入内容。

尝试用Spigot-API实现时遇到问题:点击输出槽物品时,事件触发后整个铁砧Inventory会在获取物品名称前重置,导致无法拿到输入内容。


原实现代码

public final class Test extends JavaPlugin implements Listener
{
   @Override
    public void onEnable()
    {
        this.getServer().getPluginManager().registerEvents(this, this);
        getCommand("openGui").setExecutor(new Command(this));
    }
    
    @EventHandler 
    public void OnInventoryClick(InventoryClickEvent e) 
    {     
        if(e.getInventory().getHolder() instanceof MenuHolder)     
        {        
            // 从点击物品的DisplayName获取用户输入
            String input = e.getCurrentItem().getItemMeta().getDisplayName().toString(); 
            e.getView().getPlayer().sendMessage("用户输入: " + input);
    
            // 取消事件并关闭自定义铁砧UI
            e.setCancelled(true); 
            e.getView().getPlayer().closeInventory();
        } 
    }  
}

public class Command implements CommandExecutor
{
    @Override
    public boolean onCommand(CommandSender sender, Command command, String s, String[] args)
    {
        if(sender instanceof Player)
        {
            Player player = (Player)sender;

            // 创建新的铁砧菜单
            Inventory inv = Bukkit.createInventory(new MenuHolder(), InventoryType.ANVIL, "自定义菜单");
        
            // 给铁砧输入槽添加一个无DisplayName的自定义物品
            ItemStack paper = new ItemStack(Material.PAPER);
            ItemMeta paperMeta = paper.getItemMeta();
            paperMeta.setDisplayName("");     
            paper.setItemMeta(paperMeta);          
            inv.setItem(0, paper);       
           
            player.openInventory(inv); 
        }
        return true;
    }
}


// 用于识别自定义Inventory的InventoryHolder类
public class MenuHolder implements InventoryHolder
{
    @Override
    public Inventory getInventory()
    {
        return  null;
    }
}

测试1:尝试通过InventoryClickEvent获取输入

@EventHandler
    public void onInventoryClick(InventoryClickEvent e)
    {
        if(e.getInventory().getHolder() instanceof MenuHolder)
        {
            // 测试1
            if(e.getCurrentItem() != null)
            {
                ItemMeta input = e.getCurrentItem().getItemMeta();
                if(input != null)
                {
                    e.getView().getPlayer().sendMessage("用户输入1: " + input.getDisplayName());
                }
                else
                {
                    e.getView().getPlayer().sendMessage("当前物品没有ItemMeta");
                }
            }
            else
            {
                e.getView().getPlayer().sendMessage("当前没有物品");
            }

            //-----------------------------------------------

            // 测试2
            if(e.getView().getCursor() != null)
            {
                ItemMeta input2 = e.getView().getCursor().getItemMeta();
                if(input2 != null)
                {
                    e.getView().getPlayer().sendMessage("用户输入: " + input2.getDisplayName());
                }
                else
                {
                    e.getView().getPlayer().sendMessage("光标物品没有ItemMeta");
                }
            }
            else
            {
                e.getView().getPlayer().sendMessage("光标没有物品");
            }
        }
    }

测试结果

  • 点击输入槽(槽位0)时:
    测试1输出:用户输入1: (默认设置的空字符串)
    测试2输出:光标物品没有ItemMeta
  • 点击输出槽(槽位2)时:
    测试1输出:当前没有物品
    测试2输出:光标没有物品

测试2:尝试通过PrepareAnvilEvent获取输入

public class MenuHolder implements InventoryHolder
{
    public MenuTypes menuType;
    private final Player player;

    public MenuHolder (MenuTypes menuType, Player player)
    {
        this.menuType = menuType;
        this.player = player;
    }

    public Player getPlayer() {
        return player;
    }

    @Override
    public Inventory getInventory()
    {
        return  null;
    }
}

    @EventHandler
    public void onPrepareAnvilEvent(PrepareAnvilEvent e) {
        AnvilInventory inv = e.getInventory();

        if(e.getResult() == null)
        {
            e.getView().getPlayer().sendMessage("结果为空");
            return;
        }

        if(!e.getResult().hasItemMeta())
        {
            e.getView().getPlayer().sendMessage("结果物品没有ItemMeta");
            return;
        }

        e.getView().getPlayer().sendMessage("输入内容: " + e.getResult().getItemMeta().getDisplayName());
    }

解决方案

问题核心是:点击铁砧输出槽时,Minecraft会先执行默认铁砧逻辑(重置输出槽、移动物品),导致InventoryClickEvent触发时无法获取到输出物品。正确做法是通过PrepareAnvilEvent实时缓存玩家输入,再在InventoryClickEvent中读取缓存值。

完整实现代码

  1. 修改MenuHolder,添加输入缓存字段
public class MenuHolder implements InventoryHolder {
    private final Player player;
    private String cachedInput; // 缓存玩家输入的名称

    public MenuHolder(Player player) {
        this.player = player;
    }

    public Player getPlayer() {
        return player;
    }

    public String getCachedInput() {
        return cachedInput;
    }

    public void setCachedInput(String cachedInput) {
        this.cachedInput = cachedInput;
    }

    @Override
    public Inventory getInventory() {
        return null;
    }
}
  1. 在PrepareAnvilEvent中缓存输入内容
@EventHandler
public void onPrepareAnvil(PrepareAnvilEvent e) {
    if (e.getInventory().getHolder() instanceof MenuHolder holder) {
        ItemStack result = e.getResult();
        if (result != null && result.hasItemMeta()) {
            // 实时缓存玩家输入的DisplayName
            holder.setCachedInput(result.getItemMeta().getDisplayName());
        }
    }
}
  1. 在InventoryClickEvent中读取缓存并处理
@EventHandler
public void onInventoryClick(InventoryClickEvent e) {
    if (e.getInventory().getHolder() instanceof MenuHolder holder) {
        e.setCancelled(true); // 先取消默认逻辑,阻止铁砧重置输出槽

        // 判断是否点击的是输出槽(铁砧输出槽固定为槽位2)
        if (e.getRawSlot() == 2) {
            String input = holder.getCachedInput();
            if (input != null && !input.isEmpty()) {
                e.getView().getPlayer().sendMessage("获取到的用户输入: " + input);
                // 在这里编写你的业务处理逻辑
            } else {
                e.getView().getPlayer().sendMessage("请输入有效内容");
            }
            e.getView().getPlayer().closeInventory();
        }
    }
}
  1. 调整打开铁砧的指令逻辑
public class OpenGuiCommand implements CommandExecutor {
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage("仅玩家可执行此指令");
            return true;
        }

        // 创建绑定玩家的MenuHolder
        MenuHolder holder = new MenuHolder(player);
        AnvilInventory inv = (AnvilInventory) Bukkit.createInventory(holder, InventoryType.ANVIL, "输入内容");

        // 设置输入槽物品
        ItemStack paper = new ItemStack(Material.PAPER);
        ItemMeta meta = paper.getItemMeta();
        meta.setDisplayName("");
        paper.setItemMeta(meta);
        inv.setItem(0, paper);

        player.openInventory(inv);
        return true;
    }
}

关键说明

  • 必须先调用e.setCancelled(true),阻止Minecraft的默认铁砧逻辑,避免输出槽被提前清空。
  • PrepareAnvilEvent会在玩家修改名称时实时触发,能准确获取最新的输入内容并缓存。
  • 使用getRawSlot()判断点击的是铁砧的输出槽(槽位固定为2),避免误处理其他槽位的操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 06:24:53