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 键名一律用下划线 _ 连接层级,不使用点号 .:
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 做一次主动全量转换,覆盖那些只写不读的镜像键。
手动命令(加速与排查):
/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 区分不同模块的数据:
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 负责把这些数据渲染成最终展示。流程:
模块写入 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)
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 编辑器修改 | 可能破坏数据结构,不建议手动修改。 |