Skip to content

网络系统

Behemiron 提供统一的跨平台网络通信层,实现 Spigot 服务端与 Forge 客户端之间的双向通信。大数据包支持分片发送,通信端到端加密

架构概览

通信安全

Behemiron 网络层内置端到端加密:

  • 密钥交换:ECDH(椭圆曲线 Diffie-Hellman)握手
  • 数据加密:AES-GCM 对称加密
  • 信任存储:TofuStore(Trust on First Use)记录首次连接的服务端公钥
  • 防重放:滑动窗口机制防止数据包重放攻击

数据包定义

基本用法

数据包必须实现 IPacket 接口,并使用 @Packet 注解:

kotlin
@Packet
data class PlayerInfoPacket(
    val playerId: String,
    val level: Int,
    val health: Float
) : IPacket

打了@Packet的数据包默认走内置自动序列化

自定义序列化

对于需要自定义序列化的数据包,使用 @PacketEncoder@PacketDecoder

kotlin
@Packet
data class CustomPacket(
    val value: String,
    val items: List<String>
) : IPacket {

    // 自定义编码
    @PacketEncoder
    fun encode(writer: ForyWriter) {
        writer.writeString(value)
        writer.writeInt(items.size)
        items.forEach { writer.writeString(it) }
    }

    companion object {
        // 自定义解码
        @PacketDecoder
        fun decode(reader: ForyReader): CustomPacket {
            val value = reader.readString()
            val size = reader.readInt()
            val items = (0 until size).map { reader.readString() }
            return CustomPacket(value, items)
        }
    }
}

数据包处理

使用 @PacketHandler 注解

处理器方法需要指定数据包类型:

kotlin
@Component
class MyPacketHandler {

    // 客户端处理器(无 player 参数)
    @PacketHandler(PlayerInfoPacket::class)
    fun handlePlayerInfo(packet: PlayerInfoPacket) {
        println("收到玩家信息: ${packet.playerId}")
    }
}

服务端处理器

服务端处理器可以接收 player 参数:

kotlin
@Component
class ServerPacketHandler {

    // 服务端处理器(带 player 参数)
    @PacketHandler(ActionPacket::class)
    fun handleAction(packet: ActionPacket, player: Any?) {
        // player 是发送数据包的玩家
        println("玩家 $player 执行操作: ${packet.action}")
    }
}

序列化系统 (Fory)

Behemiron 使用近来蚂蚁集团捐赠给Apache开源基金会的 Fory 高性能序列化框架,并使用 Netty 优化。

ForyWriter API

kotlin
// 基本类型
writer.writeBoolean(true)
writer.writeByte(1)
writer.writeShort(100)
writer.writeInt(1000)
writer.writeLong(10000L)
writer.writeFloat(1.5f)
writer.writeDouble(3.14)
writer.writeString("hello")

// 数组
writer.writeBytes(byteArray)
writer.writeIntArray(intArray)

// 对象(自动序列化)
writer.writeObject(myObject)

ForyReader API

kotlin
// 基本类型
val bool = reader.readBoolean()
val byte = reader.readByte()
val short = reader.readShort()
val int = reader.readInt()
val long = reader.readLong()
val float = reader.readFloat()
val double = reader.readDouble()
val string = reader.readString()

// 数组
val bytes = reader.readBytes()
val ints = reader.readIntArray()

// 对象
val obj = reader.readObject<MyClass>()

跨平台数据包装

BlockPosWrapper

方块坐标包装:

kotlin
val pos = BlockPosWrapper(100, 64, -200)

// 偏移操作
val above = pos.above()      // y + 1
val below = pos.below()      // y - 1
val north = pos.north()      // z - 1
val south = pos.south()      // z + 1

// 距离计算
val dist = pos.manhattanDistance(otherPos)

// 区块坐标
val chunkX = pos.chunkX
val chunkZ = pos.chunkZ

LocationWrapper

精确位置包装(含旋转):

kotlin
val loc = LocationWrapper(
    dimension = "minecraft:overworld",
    x = 100.5,
    y = 64.0,
    z = -200.5,
    yaw = 0f,
    pitch = 0f
)

// 方块坐标
val blockX = loc.blockX
val blockY = loc.blockY
val blockZ = loc.blockZ

// 操作
val newLoc = loc.add(1.0, 0.0, 0.0)
val distance = loc.distance(otherLoc)

ItemStackWrapper

物品包装(NBT 数据):

kotlin
// 空物品
val empty = ItemStackWrapper.EMPTY

// 从 NBT 字节数组创建
val item = ItemStackWrapper(nbtBytes)

// 检查是否为空
if (!item.isEmpty) {
    // 处理物品
}

ResourceIdWrapper

资源标识符:

kotlin
// 创建
val id = ResourceIdWrapper.of("minecraft:diamond")
val id2 = ResourceIdWrapper.of("mymod", "custom_item")

// 属性
val namespace = id.namespace  // "minecraft"
val path = id.path            // "diamond"
val fullId = id.id            // "minecraft:diamond"
val isVanilla = id.isVanilla  // true

EntityWrapper

实体包装:

kotlin
val entity = EntityWrapper(
    uuid = entityUUID,
    typeId = "minecraft:zombie",
    customName = "Boss",
    location = locationWrapper
)

// 属性
val isPlayer = entity.isPlayer
val isVanilla = entity.isVanilla

// 附加数据
val withExtra = entity.withExtra("health", "100")
val health = entity.getExtra("health")

数据包中使用 Wrapper

kotlin
@Packet
data class TeleportPacket(
    val target: LocationWrapper
) : IPacket

@Packet
data class SpawnEntityPacket(
    val entity: EntityWrapper,
    val position: BlockPosWrapper
) : IPacket

@Packet
data class GiveItemPacket(
    val itemId: ResourceIdWrapper,
    val count: Int
) : IPacket

平台转换扩展

Behemiron 提供扩展函数,用于在平台特定类型和 Wrapper 类型之间转换。

Forge 客户端

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

// ItemStack 转换
val wrapper = minecraftItemStack.toWrapper()
val forgeStack = wrapper.toForge()

// Location 转换
val locWrapper = forgeLocation.toWrapper()
val forgeLoc = locWrapper.toForge()

// BlockPos 转换
val posWrapper = blockPos.toWrapper()
val forgePos = posWrapper.toForge()

// Entity 转换
val entityWrapper = entity.toWrapper()

// Vec3 转换
val vec3Wrapper = vec3.toWrapper()
val forgeVec3 = vec3Wrapper.toForge()

// ResourceLocation 转换
val resId = resourceLocation.toWrapper()
val forgeRes = resId.toForge()

// MobEffectInstance 转换
val effectWrapper = mobEffectInstance.toWrapper()
val forgeEffect = effectWrapper.toForge()

Spigot 服务端

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

// ItemStack 转换
val wrapper = bukkitItemStack.toWrapper()
val bukkitStack = wrapper.toBukkit()

// Location 转换
val locWrapper = bukkitLocation.toWrapper()
val bukkitLoc = locWrapper.toBukkit()

// BlockPos 转换(从 Block)
val posWrapper = block.toWrapper()

// Entity 转换
val entityWrapper = entity.toWrapper()

// Player 转换
val playerWrapper = player.toWrapper()

// World 转换
val worldRef = world.toWrapper()

// NamespacedKey 转换
val resId = namespacedKey.toWrapper()
val bukkitKey = resId.toBukkit()

// PotionEffect 转换
val effectWrapper = potionEffect.toWrapper()
val bukkitEffect = effectWrapper.toBukkit()

转换类型一览

Wrapper 类型Forge 类型Spigot 类型
ItemStackWrapperItemStackItemStack
LocationWrapperForgeLocationLocation
BlockPosWrapperBlockPosBlock
EntityWrapperEntityEntity
ResourceIdWrapperResourceLocationNamespacedKey
Vec3WrapperVec3Vector
PotionEffectWrapperMobEffectInstancePotionEffect
WorldRefWrapperLevelWorld

最佳实践

1. 数据包保持精简

kotlin
// ✅ 推荐:只传输必要数据
@Packet
data class PlayerUpdatePacket(
    val playerId: String,
    val health: Float
) : IPacket

// ❌ 不推荐:传输过多数据
@Packet
data class PlayerFullPacket(
    val allData: Map<String, Any>  // 避免传输大量数据
) : IPacket

2. 使用 Wrapper 类型

kotlin
// ✅ 推荐:使用包装类
@Packet
data class TeleportPacket(
    val target: LocationWrapper
) : IPacket

// ❌ 不推荐:分散的字段
@Packet
data class TeleportBadPacket(
    val x: Double,
    val y: Double,
    val z: Double,
    val dimension: String
) : IPacket

3. 处理器验证数据

kotlin
@PacketHandler(ActionPacket::class)
fun handleAction(packet: ActionPacket, player: Any?) {
    // 验证数据合法性
    if (packet.actionId.isBlank()) {
        return
    }

    // 处理操作
    performAction(packet.actionId)
}