pluginiPlugin

自定义元数据开发

为 Zaphkiel 开发自定义元数据类型

概述

Zaphkiel 的元数据系统允许开发者创建自定义的元数据类型,为物品添加特殊功能。通过实现 Meta 接口并使用 @MetaKey 注解,你可以创建自己的元数据类型。

创建自定义元数据

1. 定义元数据类

创建一个继承 Meta 的类并使用 @MetaKey 注解:

@MetaKey("custom_effect")
class CustomEffectMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "custom_effect"
    
    // 从配置中读取参数
    private val effectType = config.getString("meta.custom_effect.type", "speed")
    private val effectLevel = config.getInt("meta.custom_effect.level", 1)
    private val effectDuration = config.getInt("meta.custom_effect.duration", 200)
    
    override fun build(player: Player?, compound: ItemTag) {
        // 在物品构建时执行的逻辑
        // 这里可以修改物品的 NBT 数据
        compound.putDeep("custom_effect.type", effectType)
        compound.putDeep("custom_effect.level", effectLevel)
        compound.putDeep("custom_effect.duration", effectDuration)
    }
    
    override fun build(event: ItemReleaseEvent) {
        // 在物品释放时执行的逻辑
        // 这里可以修改最终的 ItemStack
    }
    
    override fun build(itemMeta: ItemMeta) {
        // 修改物品的 ItemMeta
        // 例如添加特殊的 lore 或属性
    }
    
    override fun drop(player: Player?, compound: ItemTag) {
        // 当元数据被移除时执行的清理逻辑
        compound.removeDeep("custom_effect")
    }
    
    override fun drop(event: ItemReleaseEvent) {
        // 物品释放时的清理逻辑
    }
    
    override fun drop(itemMeta: ItemMeta) {
        // ItemMeta 的清理逻辑
    }
}

2. 配置文件使用

在物品配置中使用自定义元数据:

magic_boots:
  icon: diamond_boots
  name:
    item_name: '&b魔法靴子'
  meta:
    custom_effect:
      type: "speed"
      level: 2
      duration: 300
    custom_effect!!:  # 锁定版本,强制更新
      type: "jump_boost"
      level: 1
      duration: 200

元数据应用机制

构建时应用

元数据在物品构建过程中被应用:

@SubscribeEvent
fun onBuildPost(e: ItemBuildEvent.Post) {
    e.itemStream.dropMeta.forEach {
        dropMeta[it]?.drop(e.player, e.itemStream.sourceCompound)
    }
    e.item.meta.forEach {
        if (it.locked || ItemSignal.UPDATE_CHECKED !in e.itemStream.signal) {
            it.build(e.player, e.itemStream.sourceCompound)
        }
    }
    e.itemStream.setZaphkielMetaHistory(e.item.meta.map { it.id })
}

释放时应用

元数据在物品转换为 ItemStack 时被应用:

@SubscribeEvent
fun onRelease(e: ItemReleaseEvent) {
    val itemStream = e.itemStream
    itemStream.dropMeta.forEach {
        val meta = dropMeta[it]
        if (meta != null) {
            meta.drop(e)
            meta.drop(e.itemMeta)
        }
    }
    e.item.meta.forEach {
        if (it.locked || ItemSignal.UPDATE_CHECKED !in itemStream.signal) {
            it.build(e)
            it.build(e.itemMeta)
        }
    }
}

高级元数据示例

自定义属性元数据

@MetaKey("custom_attributes")
class CustomAttributesMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "custom_attributes"
    
    private val attributes = mutableMapOf<String, Double>()
    
    init {
        config.getConfigurationSection("meta.custom_attributes")?.let { section ->
            section.getKeys(false).forEach { key ->
                attributes[key] = section.getDouble(key)
            }
        }
    }
    
    override fun build(event: ItemReleaseEvent) {
        val itemMeta = event.itemMeta
        if (itemMeta is ItemMeta) {
            // 添加自定义属性到 lore
            val lore = itemMeta.lore?.toMutableList() ?: mutableListOf()
            lore.add("")
            lore.add("§6自定义属性:")
            attributes.forEach { (name, value) ->
                lore.add("§7• $name: §f$value")
            }
            itemMeta.lore = lore
        }
    }
    
    override fun build(player: Player?, compound: ItemTag) {
        // 将属性数据存储到 NBT
        attributes.forEach { (name, value) ->
            compound.putDeep("custom_attributes.$name", value)
        }
    }
}

技能冷却元数据

@MetaKey("skill_cooldown")
class SkillCooldownMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "skill_cooldown"
    
    private val cooldownTime = config.getLong("meta.skill_cooldown.time", 5000L)
    private val skillName = config.getString("meta.skill_cooldown.skill", "未知技能")
    
    override fun build(player: Player?, compound: ItemTag) {
        compound.putDeep("skill_cooldown.time", cooldownTime)
        compound.putDeep("skill_cooldown.skill", skillName)
        compound.putDeep("skill_cooldown.last_use", 0L)
    }
    
    override fun build(event: ItemReleaseEvent) {
        val itemMeta = event.itemMeta
        val compound = event.itemStream.sourceCompound
        
        val lastUse = compound.getDeep("skill_cooldown.last_use")?.asLong() ?: 0L
        val currentTime = System.currentTimeMillis()
        val remainingCooldown = maxOf(0L, cooldownTime - (currentTime - lastUse))
        
        if (remainingCooldown > 0) {
            val lore = itemMeta.lore?.toMutableList() ?: mutableListOf()
            lore.add("")
            lore.add("§c技能冷却: §f${remainingCooldown / 1000}秒")
            itemMeta.lore = lore
        }
    }
}

元数据历史管理

历史记录机制

系统维护元数据应用历史:

override fun getZaphkielMetaHistory(): List<String> {
    if (isVanilla()) {
        error("This item is not an extension item.")
    }
    return getZaphkielCompound()!![ItemKey.META_HISTORY.key]?.asList()?.map { it.asString() }?.toList() ?: emptyList()
}
 
override fun setZaphkielMetaHistory(meta: List<String>) {
    if (isVanilla()) {
        error("This item is not extension item.")
    }
    if (isLocked) {
        error("This item is locked.")
    }
    getZaphkielCompound()!![ItemKey.META_HISTORY.key] = ItemTagList.of(*meta.map { ItemTagData(it) }.toTypedArray())
}

清理过期元数据

@MetaKey("cleanup_example")
class CleanupExampleMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "cleanup_example"
    
    override fun drop(player: Player?, compound: ItemTag) {
        // 清理所有相关的 NBT 数据
        compound.removeDeep("cleanup_example")
        compound.removeDeep("temp_data")
    }
    
    override fun drop(itemMeta: ItemMeta) {
        // 清理 ItemMeta 中的相关数据
        val lore = itemMeta.lore?.toMutableList() ?: return
        lore.removeIf { it.contains("临时效果") }
        itemMeta.lore = lore
    }
}

最佳实践

1. 配置验证

@MetaKey("validated_meta")
class ValidatedMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "validated_meta"
    
    private val value: Int
    
    init {
        val configValue = config.getInt("meta.validated_meta.value", 0)
        if (configValue < 0 || configValue > 100) {
            throw IllegalArgumentException("Value must be between 0 and 100, got: $configValue")
        }
        value = configValue
    }
}

2. 性能优化

@MetaKey("optimized_meta")
class OptimizedMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "optimized_meta"
    
    // 缓存计算结果
    private val cachedResult by lazy {
        expensiveCalculation()
    }
    
    override fun build(player: Player?, compound: ItemTag) {
        // 使用缓存的结果
        compound.putDeep("optimized.result", cachedResult)
    }
    
    private fun expensiveCalculation(): String {
        // 复杂的计算逻辑
        return "calculated_value"
    }
}

3. 错误处理

@MetaKey("safe_meta")
class SafeMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "safe_meta"
    
    override fun build(player: Player?, compound: ItemTag) {
        try {
            // 可能失败的操作
            riskyOperation(compound)
        } catch (e: Exception) {
            // 记录错误但不中断物品构建
            plugin.logger.warning("Failed to apply safe_meta: ${e.message}")
        }
    }
    
    private fun riskyOperation(compound: ItemTag) {
        // 风险操作
    }
}

注册和管理

手动注册元数据

// 在插件启动时手动注册
val itemManager = Zaphkiel.api().getItemManager()
itemManager.registerMeta(CustomEffectMeta::class.java)

注销元数据

// 在插件关闭时注销
val itemManager = Zaphkiel.api().getItemManager()
itemManager.unregisterMeta(CustomEffectMeta::class.java)

调试和测试

调试元数据应用

@MetaKey("debug_meta")
class DebugMeta(config: ConfigurationSection) : Meta(config) {
    
    override val id: String = "debug_meta"
    
    override fun build(player: Player?, compound: ItemTag) {
        info("Applying debug_meta for player: ${player?.name}")
        info("Current compound: $compound")
        
        // 应用
    }