如何从自定义铁砧菜单获取玩家输入?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中读取缓存值。
完整实现代码
- 修改
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; } }
- 在
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()); } } }
- 在
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(); } } }
- 调整打开铁砧的指令逻辑
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
相关产品推荐
相关产品推荐

