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

如何在Kotlin Maven开发的Minecraft插件中修改潜影盒GUI标题

修改潜影盒GUI标题并支持&颜色代码

实现思路

直接打开潜影盒自带的Inventory时,标题是固定的。要修改标题并支持&颜色代码,我们可以通过ProtocolLib拦截窗口打开数据包替换标题内容;同时利用Bukkit的ChatColor.translateAlternateColorCodes方法,将&格式的颜色代码转换为游戏可识别的§符号。

完整修改代码

package me.hoangxuanlam2007.baloshulker

import com.comphenix.protocol.PacketType
import com.comphenix.protocol.ProtocolLibrary
import com.comphenix.protocol.events.PacketAdapter
import com.comphenix.protocol.events.PacketEvent
import org.bukkit.ChatColor
import org.bukkit.Material
import org.bukkit.block.ShulkerBox
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.EquipmentSlot
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.BlockStateMeta
import org.bukkit.plugin.java.JavaPlugin

class BaloShulker : JavaPlugin(), Listener {

    private val openedShulkers = HashMap<Player, ItemStack>()

    override fun onEnable() {
        server.pluginManager.registerEvents(this, this)
        // 注册ProtocolLib数据包监听器
        registerPacketListener()
        logger.info("BaloShulker plugin enabled!")
    }

    override fun onDisable() {
        logger.info("BaloShulker plugin disabled!")
    }

    private fun registerPacketListener() {
        ProtocolLibrary.getProtocolManager().addPacketListener(object : PacketAdapter(this, PacketType.Play.Server.OPEN_WINDOW) {
            override fun onPacketSending(event: PacketEvent) {
                val player = event.player
                // 仅处理我们插件打开的潜影盒
                if (!openedShulkers.containsKey(player)) return

                val packet = event.packet
                val windowType = packet.windowType.read(0)
                // 判断是否是潜影盒窗口
                if (windowType != "minecraft:shulker_box") return

                // 获取潜影盒物品,生成自定义标题
                val shulkerItem = openedShulkers[player] ?: return
                val baseTitle = if (shulkerItem.hasItemMeta() && shulkerItem.itemMeta.hasDisplayName()) {
                    shulkerItem.itemMeta.displayName
                } else {
                    // 默认标题,可自定义
                    "&b我的潜影盒"
                }
                // 转换&颜色代码
                val coloredTitle = ChatColor.translateAlternateColorCodes('&', baseTitle)
                // 修改数据包中的标题
                packet.windowTitle.write(0, coloredTitle)
            }
        })
    }

    @EventHandler
    fun onPlayerInteract(event: PlayerInteractEvent) {
        val player = event.player
        val action = event.action
        val hand = event.hand ?: return
        val item = if (hand == EquipmentSlot.HAND) player.inventory.itemInMainHand else player.inventory.itemInOffHand

        if (isShulkerBox(item) && action == Action.RIGHT_CLICK_AIR) {
            event.isCancelled = true
            openShulkerBox(player, item)
        }
    }

    private fun isShulkerBox(item: ItemStack): Boolean {
        return item.type == Material.SHULKER_BOX || item.type.name.endsWith("_SHULKER_BOX")
    }

    private fun openShulkerBox(player: Player, shulkerBoxItem: ItemStack) {
        val shulkerBoxMeta = shulkerBoxItem.itemMeta as? BlockStateMeta ?: return
        val shulkerBox = shulkerBoxMeta.blockState as? ShulkerBox ?: return

        player.openInventory(shulkerBox.inventory)
        openedShulkers[player] = shulkerBoxItem
    }

    @EventHandler
    fun onInventoryClose(event: InventoryCloseEvent) {
        val player = event.player as? Player ?: return
        val shulkerBoxItem = openedShulkers.remove(player) ?: return

        saveShulkerContents(shulkerBoxItem, event.inventory.contents.filterNotNull().toTypedArray())
    }

    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        if (command.name.equals("bs", ignoreCase = true) && args.isNotEmpty() && args[0].equals("open", ignoreCase = true)) {
            if (sender !is Player) {
                sender.sendMessage("Only players can use this command!")
                return true
            }
            if (!sender.hasPermission("baloshulker.use")) {
                sender.sendMessage("You don't have permission to use this command!")
                return true
            }
            val mainHandItem = sender.inventory.itemInMainHand
            val offHandItem = sender.inventory.itemInOffHand
            when {
                isShulkerBox(mainHandItem) -> openShulkerBox(sender, mainHandItem)
                isShulkerBox(offHandItem) -> openShulkerBox(sender, offHandItem)
                else -> sender.sendMessage("You are not holding a shulker box!")
            }
            return true
        }
        return false
    }

    private fun saveShulkerContents(shulkerItem: ItemStack, contents: Array<ItemStack>) {
        val shulkerBoxMeta = shulkerItem.itemMeta as? BlockStateMeta ?: return
        val shulkerBox = shulkerBoxMeta.blockState as? ShulkerBox ?: return

        shulkerBox.inventory.contents = contents
        shulkerBox.update()
        shulkerBoxMeta.blockState = shulkerBox
        shulkerItem.itemMeta = shulkerBoxMeta
    }
}

关键修改点

  1. 注册ProtocolLib数据包监听器:在onEnable中添加registerPacketListener方法,拦截服务器发送的OPEN_WINDOW数据包。
  2. 标题逻辑处理:
    • 优先使用潜影盒物品的自定义名称作为标题
    • 无自定义名称时使用默认标题(可自行修改&b我的潜影盒)
    • 通过ChatColor.translateAlternateColorCodes将&转换为§,支持颜色代码
  3. 精准拦截判断:利用openedShulkers集合确保只修改我们插件打开的潜影盒窗口,不影响正常放置的潜影盒。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:32:41