Minecraft插件开发:实现/invsee实时显示目标玩家护甲与背包
实现实时同步的背包与护甲查看功能
1. 创建自定义Inventory模拟玩家完整界面
原版玩家界面为9×5(45格)布局:顶部4格对应护甲栏(头盔、胸甲、护腿、靴子),下方41格是快捷栏+背包。我们需要创建对应尺寸的自定义Inventory,初始化时填充目标玩家的所有物品:
fun createPlayerViewInventory(target: Player): Inventory { // 创建9×5的自定义界面,标题显示目标玩家名称 val inventory = Bukkit.createInventory(null, 45, "${target.name}的背包与护甲") // 填充快捷栏+背包(原版玩家inventory的0-35格对应自定义界面的9-44格) for (i in 0..35) { inventory.setItem(9 + i, target.inventory.getItem(i) ?: ItemStack(Material.AIR)) } // 填充护甲栏(自定义界面的5-8格对应头盔、胸甲、护腿、靴子) inventory.setItem(5, target.inventory.helmet ?: ItemStack(Material.AIR)) inventory.setItem(6, target.inventory.chestplate ?: ItemStack(Material.AIR)) inventory.setItem(7, target.inventory.leggings ?: ItemStack(Material.AIR)) inventory.setItem(8, target.inventory.boots ?: ItemStack(Material.AIR)) return inventory }
2. 监听目标玩家物品变化事件实现同步
通过监听物品操作事件,实时更新查看者的自定义界面,同时维护查看关系的映射:
class InventorySyncListener : Listener { // 存储查看关系:查看者 -> (目标玩家, 打开的自定义Inventory) private val viewingMap = mutableMapOf<Player, Pair<Player, Inventory>>() // 注册查看关系 fun registerView(viewer: Player, target: Player, inventory: Inventory) { viewingMap[viewer] = Pair(target, inventory) } // 移除查看关系 fun unregisterView(viewer: Player) { viewingMap.remove(viewer) } @EventHandler fun onTargetInventoryChange(event: InventoryClickEvent) { val target = event.whoClicked as? Player ?: return // 遍历所有查看者,同步目标玩家的物品变化 viewingMap.entries.removeIf { (viewer, pair) -> val (viewTarget, viewInv) = pair if (viewTarget == target) { // 根据操作的槽位更新自定义界面 when (event.slot) { 39 -> viewInv.setItem(5, target.inventory.helmet ?: ItemStack(Material.AIR)) 38 -> viewInv.setItem(6, target.inventory.chestplate ?: ItemStack(Material.AIR)) 37 -> viewInv.setItem(7, target.inventory.leggings ?: ItemStack(Material.AIR)) 36 -> viewInv.setItem(8, target.inventory.boots ?: ItemStack(Material.AIR)) in 0..35 -> viewInv.setItem(9 + event.slot, event.currentItem ?: ItemStack(Material.AIR)) } // 刷新查看者的界面 viewer.updateInventory() false } else { // 清理已离线或关闭界面的查看者映射 !viewer.isOnline || viewer.openInventory.topInventory != viewInv } } } @EventHandler fun onViewInventoryClose(event: InventoryCloseEvent) { val viewer = event.player as? Player ?: return unregisterView(viewer) } }
3. 调整命令执行逻辑
在/invsee命令中创建自定义界面,注册查看关系后打开:
fun onInvSeeCommand(sender: CommandSender, args: Array<String>) { if (sender !is Player) { sender.sendMessage("仅玩家可使用此命令") return } if (args.size != 1) { sender.sendMessage("用法: /invsee <目标玩家>") return } val target = Bukkit.getPlayer(args[0]) ?: run { sender.sendMessage("目标玩家不在线") return } val viewInventory = createPlayerViewInventory(target) // 获取监听器实例(需提前在插件主类注册) val listener = Bukkit.getPluginManager().getListeners().find { it is InventorySyncListener } as? InventorySyncListener listener?.registerView(sender, target, viewInventory) sender.openInventory(viewInventory) }
4. 额外配置与注意事项
- 在插件主类注册监听器:
override fun onEnable() { Bukkit.getPluginManager().registerEvents(InventorySyncListener(), this) } - 若需禁止查看者修改目标物品,可添加事件拦截:
@EventHandler fun onViewInventoryClick(event: InventoryClickEvent) { if (event.inventory.title.endsWith("的背包与护甲")) { event.isCancelled = true } }
内容的提问来源于stack exchange,提问作者Ganzatron




