简体中文
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 编辑器修改 | 可能把数据结构弄坏, 不建议手动改 |