Skip to content

PDC ​

CoreLib 把模块状态存进 PDC 的方式: 键名的写法规范, 旧键怎么迁移, 各模块各写各的命名空间, 以及数据和物品展示之间的关系

PDC 是 Bukkit PersistentDataContainer 的简称 (挂在物品/实体/方块/玩家身上的附加数据存储). 它用来把自定义数据加到物品, 实体, 方块状态或玩家这些对象上. CoreLib 用 PDC 保存模块状态, 每个模块往自己的 namespace 里写数据, 不会互相覆盖

PDC 解决什么问题 ​

只把状态写进 Lore 的话, 会碰到一堆问题:

  • 玩家或其他插件可能修改 Lore
  • 多模块同时修改 Lore 时容易互相覆盖
  • 解析文本很慢, 而且容易受颜色和语言影响
  • 无法可靠区分"展示文本"和"真实数据"
  • 物品更新或刷新时, 文本可能丢失或格式变化

真实状态放 PDC 就没这些烦恼: 数据以二进制或结构化的形式存在物品的 NBT 里, 不受显示层影响

各模块写入的数据 ​

模块写入内容说明
EmakiItem物品 ID, 套装 ID, 基础属性, 技能绑定, 版本号物品的身份标识和基础配置数据
EmakiForge锻造层, 品质, 材料贡献, 配方 ID锻造结果的结构化记录
EmakiStrengthen强化星级, 锻印状态, 保护状态, 强化属性, 里程碑强化进度和惩罚状态
EmakiGem插槽列表, 宝石 ID, 宝石等级, 升级状态宝石系统的完整状态
EmakiAttribute属性 payload, 属性来源标记装备提供的属性数值
EmakiSkills玩家技能槽, 技能等级, 触发器绑定保存在玩家数据文件里, 不在物品 PDC 上
EmakiCooking世界工位状态, 方块实体 PDC 键 emakicooking:station_state 等优先写到工位所在方块的方块实体 PDC 上; 普通方块回退到 data/stations/, 坐标索引用来做区块恢复

键名规范: 不使用点号 ​

Emaki 全系的 PDC 键名一律用下划线 _ 连接层级, 不使用点号 .:

text
emaki_attribute:item_attributes_source_index
emakiforge:forge_quality_id
emaki:item_operations
emaki:item_state_meta_revision

原因是 Bukkit 的 YamlConfiguration 会把 . 当成路径分隔符. 键名里一旦带点, 第三方插件就没法在 YAML 里把它写出来 -- 加引号或反斜杠转义都没用, 因为拆分发生在 YAML 加载阶段. 改用下划线以后, 这些键就能直接写进任何 Bukkit 的 YAML 配置

这是破坏性变更

早期版本是用点号连接的 (emakiforge:forge.quality_id). 已经存在的物品, 玩家和实体上还是旧键, 插件会自动迁移, 见下方历史键迁移

你要是写扩展模块, 分区路径和字段名都别带点. PdcPartition 会用 _ 连接分区与字段, 但不会替换你传进去的点号

历史键迁移 ​

旧键迁移有两条路, 两条都不需要你动手

懒转换 (自动): 模块读数据时先读新键, 读不到就回落到历史带点键, 一旦命中, 就地写入新键并删掉旧键. 玩家加入时, 还会主动把他自己 PDC 里的键全量转一遍, 把那些只写不读的镜像键也一起覆盖掉

手动命令 (加速与排查):

text
/emakicorelib pdc-convert <players|containers|entities|all> [--dry-run]
scope覆盖范围
players在线玩家的背包, 装备栏, 副手, 末影箱 (含嵌套的潜影盒), 以及玩家自身的 PDC
containers已加载区块里容器方块中的物品
entities已加载区块内实体自身的 PDC, 以及掉落物, 展示框, 生物装备栏上的物品
all上面全部

权限是 emakicorelib.admin. 加 --dry-run 就只统计不改写. 命令可以放心重复执行, 第二次跑转换数应该是 0. 扫描按已加载区块分批进行, 不会强制加载区块

命令无法触达的范围

离线玩家的背包和它自己的 PDC (数据在 playerdata 的 NBT 文件里), 以及未加载区块中的容器和实体, 都扫不到. 这些靠懒转换处理: 玩家上线或区块加载后一读取就自动转, 不用手动干预

配置文件需要手工检查

转换器只处理 PDC 数据, 不会改配置文件. 下面两类写法得你自己动手改:

  • 自建配置里 pdc_match 直接书写的完整键名, 如 "emakiforge:forge.quality_id"
  • 表达式或匹配器里使用的, 由 PDC 键名派生的变量名: pdc_<命名空间>_<键> 与 item_pdc_<命名空间>_<键>

EmakiStrengthen 的 Forge 变量是例外, 它的点号写法作为别名保留下来, 不用改

namespace 原则 ​

每个模块都该往自己的命名空间里写, 别直接覆盖别的模块的数据. CoreLib 用 NamespacedKey 来区分不同模块的数据:

text
emaki:item_schema_version
emakiforge:forge_quality_id
emaki_strengthen:strengthen_affix_layer
emaki:gem_item_id
emaki_attribute:item_attributes_source_index

这样做, 你就不用担心这些情况:

  • 强化模块不会意外覆盖宝石数据
  • 锻造模块不会破坏物品基础 ID
  • 各模块可以独立读写自己的数据
  • 清理某个模块状态时不影响其他模块

PDC 与 Assembly 的关系 ​

PDC 存真实数据, Assembly 负责把它们渲染成最终展示. 流程:

text
模块写入 PDC → CoreLib 读取所有 layer → Assembly 渲染 → 最终 Lore/名称/属性

所以你只要记住:

  • Lore 是"输出", 不是"输入". 别从 Lore 去推断数据
  • 修改 Lore 不会改变真实状态
  • 刷新物品时, CoreLib 会重新从 PDC 读取并重建展示

配置者需要知道什么 ​

大多数时候你不用手动编辑 PDC, 知道下面几点就行:

  • 不要用其他插件随意清理 Emaki 物品的 PDC. 例如某些"物品清理"插件可能会移除自定义 NBT 数据
  • 不要把有 PDC 的装备通过不兼容插件重写成全新物品. 例如某些物品编辑插件会创建一个新的 ItemStack, 把原有的 PDC 丢掉
  • 调试装备时, 要用 inspect / dump 这类命令查看真实状态. 不要只看 Lore 就判断装备是否正常
  • 要清除某个模块状态, 优先用模块提供的 clear / reset 命令. 手动删 PDC key 可能导致数据对不上
  • 备份重要装备. 做破坏性测试之前, 先确保有备份

给开发者的说明 ​

如果你要写扩展模块, 或者要把项目接到 CoreLib 上:

使用独立 namespace ​

java
NamespacedKey key = new NamespacedKey(yourPlugin, "your_data_key");

不要往别的模块的 namespace 里写数据

控制数据大小 ​

不要把大的对象不加限制地写进 PDC. 物品的 PDC 数据会跟着物品在网络里传, 数据太大会影响性能. 建议:

  • 只保存必要的标识和数值
  • 复杂数据保存到外部文件, PDC 里只存引用 ID
  • 列表数据设置合理的上限

版本号和迁移 ​

存结构化数据时要带上版本号:

java
container.set(versionKey, PersistentDataType.INTEGER, 2);

数据格式变了以后, 靠版本号判断要不要迁移. CoreLib 的 SnapshotCodec 提供了编解码和版本兼容的支持

展示由 Assembly 统一生成 ​

对外的展示应该由 Assembly / Renderer 统一生成, 不要自己直接拼 Lore. 这么做的好处:

  • 保证多模块展示顺序一致
  • 避免重复或遗漏
  • 支持模板化和国际化

读取其他模块数据 ​

优先用数据归属方自己的 API 模块, 不要依赖内部 key, 也别再从 CoreLib 里找入口:

java
// 推荐: 通过归属模块的 API 获取
// 属性 → EmakiAttributeApi (emaki.jiuwu.craft.attribute.api)
EmakiResult<Double> attack = EmakiAttributeApi.catalog().attributeValue(player, "physical_attack");

// 不推荐: 直接读取内部 PDC key
NamespacedKey internalKey = new NamespacedKey(attributePlugin, "internal_key");

内部 key 可能在版本更新时变化, API 则保持稳定

各类共享 PDC 数据的当前归属 ​

CoreLib 以前代管过一部分跨模块的 PDC 约定, 这些约定现在都下放到各自的 API / 协议模块了. 新代码直接用下表右侧的入口:

数据当前权威入口CoreLib 中的遗留入口
装备属性 PDC 约定EmakiAttributeApi (EmakiAttributeApi 模块)PdcAttributeGateway, EmakiAttributeBridge 已在 CoreLib 4.6.7 删除
装备技能 PDC 读写EquipmentSkillPdcCodec (EmakiSkillsApi 模块的 emaki.jiuwu.craft.skills.api.pdc 包)SkillPdcGateway 已在 CoreLib 4.6.7 删除

这些类在 CoreLib 这边已经没有了, 新代码只能用上表右侧的入口

数据生命周期 ​

事件PDC 行为
物品创建模块写入初始 PDC 数据
物品操作 (强化/锻造/镶嵌)对应模块更新自己的 PDC 数据
物品刷新CoreLib 读取 PDC, 重建展示层
物品丢弃/存箱PDC 数据随物品保存
服务器重启PDC 数据跟着物品一起保存下来, 不会丢
物品被其他插件复制看对方怎么复制; 用 ItemStack.clone() 复制的话 PDC 会保留
物品被 NBT 编辑器修改可能把数据结构弄坏, 不建议手动改