Skip to content

层(Layer)与装配管线

所属:开发者 · 相关:核心概念 · API 参考

层是物品创建后追加的补丁(打孔 / 镶嵌 / 强化 / 附加词缀),装配管线是把模板 + 实例 + 层合成 ItemStack 的过程。本章是这套核心机制的开发者参考。


1. 层是什么

层(QinhLayerPatchPack)是叠在编译后定义之上的补丁,无需重编译模板即可改物品:

kotlin
data class QinhLayerPatchPack(
    val id: String,
    val priority: Int = 0,                       // 排序(小者先应用)
    val owner: String,                           // 写域归属(gem/enchant/forge…)
    val variables: Map<String,String> = emptyMap(),    // 变量补丁
    val lockRequests: Map<String,String> = emptyMap(), // 变量锁
    val meta: Map<String,String> = emptyMap(),
) {
    fun sanitized(): QinhLayerPatchPack          // 过滤受保护键
}

层类型与顺序

kotlin
enum class QinhItemLayerType(val namespace: String, val order: Int) {
    BASE("base", 10),
    VARIABLES("variables", 20),
    SECTION("section", 35),
    AFFIX("affix", 36),
    EXTERNAL("external", 100),
}

层按 order 升序叠加。


2. 语义守卫(LayerSemanticGuard)

层补丁不能改动战斗关键键(防破坏平衡)。拒绝列表包括 attack_damage / crit / defense / health / mana / skill / strength / attack_speed / lifesteal 等及含 *attack* 等可疑模式的键。sanitized() 会过滤掉这些键并警告。

要改属性,走 Provider 而非层补丁。


3. 写域策略(WriteDomainPolicy)

层 / 实例 / 运行时三个域,限制谁能改:

默认 owner默认前缀用途
INSTANCEqi_core / give / init / template核心物品数据
LAYERstrengthen / forge / gem / enchant / socket / refine / embed / upgradesys_ / layer_持久化改造
RUNTIMEqi_admin / qi_ui / adminbuff_ / temp_ / ui_临时覆盖 / UI
kotlin
WriteDomainPolicy.validateLayerWrite(owner, layerId): Verdict
WriteDomainPolicy.validateRuntimeWrite(owner): Verdict

配置见 config.yml → write-domains。越权写入在 strict: true 时报错。

LayerWriteResultOK / NOT_QINH_ITEM / DOMAIN_VIOLATION / PROVIDER_PATCH_FORBIDDEN


4. 层 API

kotlin
// 写补丁(自动校验写域)
val (item, result) = QinhItemsAPI.assembly().applyLayerPatch(stack, QinhLayerPatchPack(
    id = "gem_1",
    owner = "gem",
    priority = 50,
    variables = mapOf("gem_slot_1" to "amethyst"),
    lockRequests = mapOf("gem_slot_1" to "gem"),
))

// 读
QinhItemsAPI.assembly().readLayerPack(stack, "gem_1"): QinhLayerPatchPack?
QinhItemsAPI.assembly().layerStack(stack): List<QinhLayerPatchPack>

// 删
QinhItemsAPI.assembly().removeLayer(stack, "gem_1")

// 类型安全读层值
QinhItemsAPI.layers().read(stack, "gem_1"): QinhLayerState?
QinhItemsAPI.layers().int(stack, "gem_1", "level"): Int?

NBT 序列化由 LayerPatchCodec 完成(存为 YAML 串),栈管理由 LayerStack


5. 装配管线(QinhItemAssemblyService)

入口:

kotlin
build(definition, amount = 1, player = null, templateInstance = null): ItemStack?  // 全新
rebuild(item): ItemStack?                                                          // 重建
applyLayerPatch(item, pack): Pair<ItemStack?, LayerWriteResult>
removeLayer(item, layerId): ItemStack

装配序列

assemble(definition, amount, player, freshGenerate, instance?, layerStack?)
├─ 建 QinhItemAssemblyContext(freshGenerate 时按 InstanceSeedPolicy 分配种子)
├─ 事件 QinhItemGenerateEvent(freshGenerate 时,pre)
├─ 事件 QinhItemAssembleEvent(pre)
├─ 逐个层贡献者执行:
│    BaseLayerContributor(10)     建基础 ItemStack
│    VariablesLayerContributor(20) 解析变量、渲染、加锁、抛 QinhItemCompiledEvent
│    SectionLayerContributor(35)  注入段
│    AffixLayerContributor(36)    注入词缀
│    External(100)                第三方
│    (各贡献者返回 QinhItemLayerSnapshot,并入 compiledState)
├─ 加 layer.summary.* 汇总变量
├─ QinhItemTags.stamp(stack, id, version, hash)
├─ 写实例数据到 NBT
├─ 事件 QinhItemAssembleEvent(post)
└─ 返回 ItemStack
  • 全新生成才分配随机种子;重建保留种子 / 实例,只重渲染。
  • 变量由 QinhVariableEngine.resolveWithTrace() 解析,结果存 CompiledState

自定义层贡献者

kotlin
interface QinhItemLayerContributor {
    val namespace: String
    val order: Int
    fun contribute(context: QinhItemAssemblyContext): QinhItemLayerSnapshot?
}
QinhIntegrationRegistry.registerLayerContributor(myContributor)

6. 模板系统

TemplateCompiler 把 YAML 编译成 QinhItemDefinition:合并碎片 → override → 根字段,注入 tier 变量,迁移旧 effects,编译基础值进 ap,读 actionLore / 宝石孔 / 段 / 词缀 / 附魔 / 资源包。合并规则见 碎片与模板


7. 编译追踪

kotlin
data class CompiledState(
    val variableTrace: Map<String, VariableTrace.Entry>,
    val conflicts: List<VariableConflict>,
    val resolvedVariables: Map<String, String>,
    val layerSnapshots: List<QinhItemLayerSnapshot>,
    val compileEpoch: Long,
)
  • CompileEpoch.next():每次装配自增的单调计数。
  • CompileSessionStore:编译结果 LRU 缓存(最多 4096,按物品指纹)。

8. 库清单

kotlin
data class LibraryManifest(
    val id: String, val displayName: String, val version: String,
    val description: String, val category: String,
    val types: List<String>, val capabilities: Set<TypeCapability>,
    val dependencies: List<String>, val author: String,
)
LibraryManifestRegistry.get(id) / all() / reload()

服主向说明见 碎片与模板 → 库清单


下一步