主题
网络系统
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.chunkZLocationWrapper
精确位置包装(含旋转):
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 // trueEntityWrapper
实体包装:
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 类型 |
|---|---|---|
ItemStackWrapper | ItemStack | ItemStack |
LocationWrapper | ForgeLocation | Location |
BlockPosWrapper | BlockPos | Block |
EntityWrapper | Entity | Entity |
ResourceIdWrapper | ResourceLocation | NamespacedKey |
Vec3Wrapper | Vec3 | Vector |
PotionEffectWrapper | MobEffectInstance | PotionEffect |
WorldRefWrapper | Level | World |
最佳实践
1. 数据包保持精简
kotlin
// ✅ 推荐:只传输必要数据
@Packet
data class PlayerUpdatePacket(
val playerId: String,
val health: Float
) : IPacket
// ❌ 不推荐:传输过多数据
@Packet
data class PlayerFullPacket(
val allData: Map<String, Any> // 避免传输大量数据
) : IPacket2. 使用 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
) : IPacket3. 处理器验证数据
kotlin
@PacketHandler(ActionPacket::class)
fun handleAction(packet: ActionPacket, player: Any?) {
// 验证数据合法性
if (packet.actionId.isBlank()) {
return
}
// 处理操作
performAction(packet.actionId)
}