主题
网络系统
Spigot↔Forge 双向 Packet 通道,支持自动/自定义序列化、端到端加密和大数据包分片。
前置知识
需要了解 IoC 容器(处理器需要 @Component 注册)。跨服同步/RPC(横向链路)请查看 ServerSync。
Quick Start
kotlin
// 1. 定义数据包
@Packet
data class GreetPacket(val name: String, val level: Int) : IPacket
// 2. 注册处理器
@Component
class GreetHandler {
@PacketHandler(GreetPacket::class)
fun handle(packet: GreetPacket) {
println("Hello ${packet.name}, Lv.${packet.level}")
}
}API Reference
数据包注解
| 注解 | 说明 |
|---|---|
@Packet | 标记一个类为数据包,默认使用内置自动序列化 |
@PacketEncoder | 标记实例方法为自定义编码器 |
@PacketDecoder | 标记 companion object 中的静态方法为自定义解码器 |
@PacketHandler | 标记方法为数据包处理器 |
@PacketHandler
| 参数 | 类型 | 说明 |
|---|---|---|
value | KClass<out IPacket> | 要处理的数据包类型 |
方法签名:
| 场景 | 签名 | 说明 |
|---|---|---|
| 客户端处理器 | fun handle(packet: T) | 无 player 参数 |
| 服务端处理器 | fun handle(packet: T, player: Any?) | player 为发送者 |
ForyWriter API
| 方法 | 参数类型 | 说明 |
|---|---|---|
writeBoolean(v) | Boolean | 写入布尔值 |
writeByte(v) | Byte | 写入字节 |
writeShort(v) | Short | 写入短整数 |
writeInt(v) | Int | 写入整数 |
writeLong(v) | Long | 写入长整数 |
writeFloat(v) | Float | 写入浮点数 |
writeDouble(v) | Double | 写入双精度浮点 |
writeString(v) | String | 写入字符串 |
writeBytes(v) | ByteArray | 写入字节数组 |
writeIntArray(v) | IntArray | 写入整数数组 |
writeObject(v) | Any | 自动序列化对象 |
ForyReader API
| 方法 | 返回值 | 说明 |
|---|---|---|
readBoolean() | Boolean | 读取布尔值 |
readByte() | Byte | 读取字节 |
readShort() | Short | 读取短整数 |
readInt() | Int | 读取整数 |
readLong() | Long | 读取长整数 |
readFloat() | Float | 读取浮点数 |
readDouble() | Double | 读取双精度浮点 |
readString() | String | 读取字符串 |
readBytes() | ByteArray | 读取字节数组 |
readIntArray() | IntArray | 读取整数数组 |
readObject<T>() | T | 反序列化对象 |
跨平台 Wrapper 类型
| 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 | 世界引用 |
通信安全
| 机制 | 说明 |
|---|---|
| 密钥交换 | ECDH(椭圆曲线 Diffie-Hellman)握手 |
| 数据加密 | AES-GCM 对称加密 |
| 信任存储 | TofuStore(Trust on First Use)记录首次连接的服务端公钥 |
| 防重放 | 滑动窗口机制防止数据包重放攻击 |
自定义序列化
对于需要自定义序列化的数据包,使用 @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)
}
}
}跨平台 Wrapper 用法
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 newLoc = loc.add(1.0, 0.0, 0.0)
val distance = loc.distance(otherLoc)ResourceIdWrapper
kotlin
val id = ResourceIdWrapper.of("minecraft:diamond")
val id2 = ResourceIdWrapper.of("mymod", "custom_item")
id.namespace // "minecraft"
id.path // "diamond"
id.isVanilla // true平台转换扩展
kotlin
// Forge 客户端
import com.behemiron.engine.forge.util.crossplatform.*
val wrapper = minecraftItemStack.toWrapper()
val forgeStack = wrapper.toForge()
// Spigot 服务端
import com.behemiron.engine.spigot.util.crossplatform.*
val wrapper = bukkitItemStack.toWrapper()
val bukkitStack = wrapper.toBukkit()完整示例
kotlin
// 定义业务数据包
@Packet
data class TeleportPacket(val target: LocationWrapper) : IPacket
@Packet
data class SpawnEntityPacket(
val entity: EntityWrapper,
val position: BlockPosWrapper
) : IPacket
// 客户端处理器
@Component
class ClientHandler {
@PacketHandler(TeleportPacket::class)
fun onTeleport(packet: TeleportPacket) {
println("传送到: ${packet.target}")
}
}
// 服务端处理器(带 player 参数)
@Component
class ServerHandler {
@PacketHandler(SpawnEntityPacket::class)
fun onSpawn(packet: SpawnEntityPacket, player: Any?) {
if (packet.entity.typeId.isBlank()) return
println("玩家 $player 请求生成实体: ${packet.entity.typeId}")
}
}注意事项
- 数据包保持精简,只传输必要数据,避免传输
Map<String, Any>等大而模糊的结构。 - 优先使用 Wrapper 类型(
LocationWrapper等)而非分散的基本类型字段。 - 服务端处理器中必须验证客户端数据的合法性,不要信任客户端输入。
- 处理器类必须用
@Component注册到 IoC 容器中。