You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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

火山引擎 最新活动