主题
数据同步 (CPDC)
CrossPlatformDataContainer -- 纵向(Server↔Client)对象级 KV 数据容器,支持同步与本地持久化。
前置知识
需要了解 网络系统 的 Wrapper 类型。横向跨服同步请查看 ServerSync。
Quick Start
kotlin
val key = ResourceIdWrapper.of("myplugin", "player_level")
// 服务端写入 SYNC 数据 -> 自动同步到客户端
cpdc.setInt(key, 10)
// 任一端读取
val level = cpdc.getInt(key) ?: 1API Reference
CrossPersistentPlatformDataContainer
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getBoolean(key) | ResourceIdWrapper | Boolean? | 读取布尔值 |
getInt(key) | ResourceIdWrapper | Int? | 读取整数 |
getLong(key) | ResourceIdWrapper | Long? | 读取长整数 |
getDouble(key) | ResourceIdWrapper | Double? | 读取双精度浮点 |
getString(key) | ResourceIdWrapper | String? | 读取字符串 |
getBytes(key) | ResourceIdWrapper | ByteArray? | 读取字节数组 |
getObject(key) | ResourceIdWrapper | Any? | 读取对象 |
getKeys() | — | Set<ResourceIdWrapper> | 获取所有键 |
setBoolean(key, value) | ResourceIdWrapper, Boolean | — | 写入布尔值 |
setInt(key, value) | ResourceIdWrapper, Int | — | 写入整数 |
setLong(key, value) | ResourceIdWrapper, Long | — | 写入长整数 |
setDouble(key, value) | ResourceIdWrapper, Double | — | 写入双精度浮点 |
setString(key, value) | ResourceIdWrapper, String | — | 写入字符串 |
setBytes(key, value) | ResourceIdWrapper, ByteArray | — | 写入字节数组 |
setObject(key, value) | ResourceIdWrapper, Any | — | 写入对象 |
remove(key) | ResourceIdWrapper | — | 删除键 |
CrossPlatformDataScope
| 值 | 说明 |
|---|---|
LOCAL | 本端持久化,每个端独立存储,不同步 |
SYNC | 服务端权威同步,自动推送到客户端 |
获取 CPDC 容器
Forge 客户端:
| 目标类型 | 方法签名 |
|---|---|
| 物品 | ItemStack.getCrossPlatformDataContainer(scope) |
| 实体 | Entity.getCrossPlatformDataContainer(scope) |
| 区块 | LevelChunk.getCrossPlatformDataContainer(scope) |
| 方块 | BlockPos.getCrossPlatformDataContainer(level, scope) |
| 槽位物品 | AbstractContainerMenu.getCrossPlatformDataContainer(slotIndex, scope) |
| BUIServerAPI 存储 | getBUILocalStorageContainer(storeId) |
Spigot 服务端:
| 目标类型 | 方法签名 |
|---|---|
| 物品 | ItemStack.getCrossPlatformPersistentDataContainer(scope) |
| 实体 | Entity.getCrossPlatformPersistentDataContainer(scope) |
| 区块 | Chunk.getCrossPlatformPersistentDataContainer(scope) |
| 方块 | Block.getCrossPlatformPersistentDataContainer(scope) |
数据值类型
| 类型 | Kotlin 类 | 说明 |
|---|---|---|
| Bool | CrossPlatformDataValue.Bool | 布尔值 |
| Int32 | CrossPlatformDataValue.Int32 | 32 位整数 |
| Int64 | CrossPlatformDataValue.Int64 | 64 位整数 |
| Float64 | CrossPlatformDataValue.Float64 | 双精度浮点 |
| Text | CrossPlatformDataValue.Text | 字符串 |
| Bytes | CrossPlatformDataValue.Bytes | 字节数组 |
| Object | CrossPlatformDataValue.Object | 序列化对象(含 typeHint) |
作用域详解
LOCAL - 本地数据
仅在当前端存储,不同步。
使用场景: 临时缓存、客户端 UI 状态、服务端内部状态。
kotlin
// 客户端:存储 UI 偏好
val cpdc = entity.getCrossPlatformDataContainer(CrossPlatformDataScope.LOCAL)
cpdc.setBoolean(showHudKey, true)SYNC - 同步数据
服务端是权威,写入后自动同步到客户端。
使用场景: 需要双端访问的数据、UI 显示数据、游戏状态。
kotlin
// 服务端写入
val cpdc = entity.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.SYNC)
cpdc.setInt(levelKey, 10)
// -> 自动同步到客户端,客户端读取即可获得最新值同步流程
完整示例
kotlin
// === 服务端(Spigot) ===
import com.behemiron.engine.spigot.util.crossplatform.*
@Service
class ItemDataService {
private val rarityKey = ResourceIdWrapper.of("mygame", "rarity")
private val levelKey = ResourceIdWrapper.of("mygame", "level")
private val cacheKey = ResourceIdWrapper.of("mygame", "last_check")
// 设置物品品质(SYNC -> 客户端可读取用于 UI 显示)
fun setItemRarity(item: ItemStack, rarity: String) {
val cpdc = item.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.SYNC)
cpdc.setString(rarityKey, rarity)
cpdc.setInt(levelKey, calculateLevel(rarity))
}
// 服务端内部缓存(LOCAL -> 仅服务端可见)
fun markChecked(entity: Entity) {
val cpdc = entity.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.LOCAL)
cpdc.setLong(cacheKey, System.currentTimeMillis())
}
}
// === 客户端(Forge) ===
import com.behemiron.engine.forge.util.crossplatform.*
class ItemTooltipRenderer {
private val rarityKey = ResourceIdWrapper.of("mygame", "rarity")
// 读取服务端同步过来的品质数据
fun getRarity(itemStack: ItemStack): String {
val cpdc = itemStack.getCrossPlatformDataContainer(CrossPlatformDataScope.SYNC)
return cpdc.getString(rarityKey) ?: "common"
}
}注意事项
- 物品 SYNC 写入限制:直接对
ItemStack获取的 SYNC 容器是只读的。要写入物品的 SYNC 数据,必须通过AbstractContainerMenu.getCrossPlatformDataContainer(slotIndex, SYNC)使用槽位寻址。 - 使用命名空间作为 key 前缀(
ResourceIdWrapper.of("myplugin", "xxx")),避免与其他插件冲突。 - 避免滥用
setObject,序列化开销较大,优先使用具体类型方法。 - 对于多个数据变更,尽量批量提交以减少同步开销。