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/,坐标索引用于区块恢复。 |
namespace 原则
每个模块都应该写入自己的命名空间,不要直接覆盖其他模块数据。CoreLib 使用 NamespacedKey 区分不同模块的数据:
emakiitem:item_id
emakiforge:forge_layer
emakistrengthen:star_level
emakigem:gem_slots
emakiattribute:attribute_payload这种设计确保:
- 强化模块不会意外覆盖宝石数据。
- 锻造模块不会破坏物品基础 ID。
- 各模块可以独立读写自己的数据。
- 清理某个模块状态时不影响其他模块。
PDC 与 Assembly 的关系
PDC 保存真实数据,Assembly 负责把这些数据渲染成最终展示。流程:
模块写入 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
NamespacedKey key = new NamespacedKey(yourPlugin, "your_data_key");不要使用其他模块的 namespace 写入数据。
控制数据大小
不要把大型对象直接无边界写入 PDC。物品 PDC 数据会随物品在网络中传输,过大的数据会影响性能。建议:
- 只保存必要的标识和数值。
- 复杂数据保存到外部文件,PDC 中只存引用 ID。
- 列表数据设置合理上限。
版本号和迁移
保存结构化数据时要考虑版本号:
container.set(versionKey, PersistentDataType.INTEGER, 2);当数据格式变化时,通过版本号判断是否需要迁移。CoreLib 的 SnapshotCodec 提供了编解码和版本兼容支持。
展示由 Assembly 统一生成
对外展示应由 Assembly / Renderer 统一生成,而不是直接拼 Lore。这样可以:
- 保证多模块展示顺序一致。
- 避免重复或遗漏。
- 支持模板化和国际化。
读取其他模块数据
优先使用数据归属方自己的 API 模块,不要依赖内部 key,也不要再从 CoreLib 找入口:
// 推荐:通过归属模块的 API 获取
// 属性 → EmakiAttributeApi(emaki.jiuwu.craft.attribute.api)
double attack = EmakiAttributeApi.get().readAttributeValue(player, "physical_attack");
// 不推荐:直接读取内部 PDC key
NamespacedKey internalKey = new NamespacedKey(attributePlugin, "internal_key");内部 key 可能在版本更新时变化,API 则保持稳定。
各类共享 PDC 数据的当前归属
CoreLib 曾经代持过一部分跨模块 PDC 契约,这些契约已下放到各自的 API / 协议模块。新代码请直接使用下表右侧的入口:
| 数据 | 当前权威入口 | CoreLib 中的遗留入口 |
|---|---|---|
| 装备属性 PDC 契约 | EmakiAttributeApi(EmakiAttributeApi 模块) | PdcAttributeGateway、EmakiAttributeBridge,均已 @Deprecated(forRemoval = true) |
| 装备技能 PDC 读写 | EquipmentSkillPdcCodec(EmakiSkillsProtocol 模块) | SkillPdcGateway,已 @Deprecated(since = "4.5.21", forRemoval = true),内部只是委托 |
CoreLib 侧的这些类仅作为一个同步发布窗口内的兼容面保留,之后会被移除;新代码不应再引用它们。
数据生命周期
| 事件 | PDC 行为 |
|---|---|
| 物品创建 | 模块写入初始 PDC 数据。 |
| 物品操作(强化/锻造/镶嵌) | 对应模块更新自己的 PDC 数据。 |
| 物品刷新 | CoreLib 读取 PDC,重建展示层。 |
| 物品丢弃/存箱 | PDC 数据随物品保存。 |
| 服务器重启 | PDC 数据随物品持久化,不会丢失。 |
| 物品被其他插件复制 | 取决于复制方式,ItemStack.clone() 会保留 PDC。 |
| 物品被 NBT 编辑器修改 | 可能破坏数据结构,不建议手动修改。 |