Skip to content

数据同步

Behemiron 提供 CrossPlatformDataContainer (CPDC) 系统,用于在服务端和客户端之间同步数据或在各端存储 KV 型持久化数据

概念导图

CPDC 接口

基本 API

CPDC 使用 ResourceIdWrapper 作为键,提供类型化的读写方法:

kotlin
interface CrossPersistentPlatformDataContainer {
    val scope: CrossPlatformDataScope

    // 读取
    fun getBoolean(key: ResourceIdWrapper): Boolean?
    fun getInt(key: ResourceIdWrapper): Int?
    fun getLong(key: ResourceIdWrapper): Long?
    fun getDouble(key: ResourceIdWrapper): Double?
    fun getString(key: ResourceIdWrapper): String?
    fun getBytes(key: ResourceIdWrapper): ByteArray?
    fun getObject(key: ResourceIdWrapper): Any?
    fun getKeys(): Set<ResourceIdWrapper>

    // 写入
    fun setBoolean(key: ResourceIdWrapper, value: Boolean)
    fun setInt(key: ResourceIdWrapper, value: Int)
    fun setLong(key: ResourceIdWrapper, value: Long)
    fun setDouble(key: ResourceIdWrapper, value: Double)
    fun setString(key: ResourceIdWrapper, value: String)
    fun setBytes(key: ResourceIdWrapper, value: ByteArray)
    fun setObject(key: ResourceIdWrapper, value: Any)
    fun remove(key: ResourceIdWrapper)
}

使用示例

kotlin
// 创建键
val levelKey = ResourceIdWrapper.of("myplugin", "player_level")
val nameKey = ResourceIdWrapper.of("myplugin", "display_name")

// 写入数据
cpdc.setInt(levelKey, 10)
cpdc.setString(nameKey, "Hero")

// 读取数据
val level = cpdc.getInt(levelKey) ?: 1
val name = cpdc.getString(nameKey) ?: "Unknown"

// 删除数据
cpdc.remove(levelKey)

// 获取所有键
val allKeys = cpdc.getKeys()

获取 CPDC 容器

通过扩展函数从平台对象获取 CPDC 容器。

Forge 客户端

kotlin
import com.behemiron.engine.forge.util.crossplatform.*

// 物品
val itemCpdc = itemStack.getCrossPlatformDataContainer(CrossPlatformDataScope.SYNC)

// 实体
val entityCpdc = entity.getCrossPlatformDataContainer(CrossPlatformDataScope.SYNC)

// 区块
val chunkCpdc = levelChunk.getCrossPlatformDataContainer(CrossPlatformDataScope.LOCAL)

// 方块(需要 Level)
val blockCpdc = blockPos.getCrossPlatformDataContainer(level, CrossPlatformDataScope.SYNC)

// 区块坐标(需要 Level)
val chunkPosCpdc = chunkPos.getCrossPlatformDataContainer(level, CrossPlatformDataScope.LOCAL)

// 容器槽位物品(支持 SYNC 写入)
val slotCpdc = menu.getCrossPlatformDataContainer(slotIndex, CrossPlatformDataScope.SYNC)

// BUI LocalStorage
val storageCpdc = getBUILocalStorageContainer("my_template_id")

物品 SYNC 写入

直接对 ItemStack 获取的 SYNC 容器是只读的。要写入物品的 SYNC 数据,必须通过 AbstractContainerMenu.getCrossPlatformDataContainer(slotIndex, SYNC) 使用槽位寻址。

Spigot 服务端

kotlin
import com.behemiron.engine.spigot.util.crossplatform.*

// 物品
val itemCpdc = itemStack.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.SYNC)

// 实体
val entityCpdc = entity.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.SYNC)

// 区块
val chunkCpdc = chunk.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.LOCAL)

// 方块
val blockCpdc = block.getCrossPlatformPersistentDataContainer(CrossPlatformDataScope.SYNC)

扩展函数一览

目标类型Forge 方法Spigot 方法
物品ItemStack.getCrossPlatformDataContainer(scope)ItemStack.getCrossPlatformPersistentDataContainer(scope)
实体Entity.getCrossPlatformDataContainer(scope)Entity.getCrossPlatformPersistentDataContainer(scope)
区块LevelChunk.getCrossPlatformDataContainer(scope)Chunk.getCrossPlatformPersistentDataContainer(scope)
方块BlockPos.getCrossPlatformDataContainer(level, scope)Block.getCrossPlatformPersistentDataContainer(scope)
槽位AbstractContainerMenu.getCrossPlatformDataContainer(slotIndex, scope)-
存储getBUILocalStorageContainer(storeId)-

作用域

CrossPlatformDataScope

kotlin
enum class CrossPlatformDataScope {
    /** 本端持久化(每个端独立存储) */
    LOCAL,

    /** 服务端权威同步(服务端是权威,同步到客户端) */
    SYNC
}

LOCAL - 本地数据

仅在当前端存储,不同步:

kotlin
// 服务端的 LOCAL 数据只在服务端可见
// 客户端的 LOCAL 数据只在客户端可见

使用场景

  • 临时缓存
  • 客户端 UI 状态
  • 服务端内部状态

SYNC - 同步数据

服务端是权威,自动同步到客户端:

kotlin
// 服务端修改 SYNC 数据
cpdc.setInt(key, 100)
// 数据自动同步到客户端

使用场景

  • 需要双端访问的数据
  • UI 显示所需的数据
  • 游戏状态

数据值类型

kotlin
sealed interface CrossPlatformDataValue {
    data class Bool(val value: Boolean) : CrossPlatformDataValue
    data class Int32(val value: Int) : CrossPlatformDataValue
    data class Int64(val value: Long) : CrossPlatformDataValue
    data class Float64(val value: Double) : CrossPlatformDataValue
    data class Text(val value: String) : CrossPlatformDataValue
    data class Bytes(val value: ByteArray) : CrossPlatformDataValue
    data class Object(val bytes: ByteArray, val typeHint: String?) : CrossPlatformDataValue
}

最佳实践

1. 使用命名空间

kotlin
// ✅ 推荐:使用命名空间
val key = ResourceIdWrapper.of("myplugin", "player_level")

// ❌ 不推荐:不使用命名空间可能冲突
val key = ResourceIdWrapper.of("minecraft", "level")

2. 选择正确的作用域

kotlin
// UI 显示需要 -> SYNC
cpdc.setString(displayNameKey, "Hero")  // scope = SYNC

// 仅服务端内部使用 -> LOCAL
cpdc.setLong(lastCalculationKey, timestamp)  // scope = LOCAL

3. 使用合适的数据类型

kotlin
// ✅ 使用具体类型方法
cpdc.setInt(countKey, 100)
cpdc.setString(nameKey, "Player")

// ❌ 避免滥用 Object 类型
cpdc.setObject(dataKey, complexObject)  // 序列化开销大

4. 批量操作

对于多个数据变更,尽量批量提交以减少同步开销。

同步流程