Move packet encoders and decoders to their own project
This commit is contained in:
parent
8403140a70
commit
b021a4b753
94 changed files with 733 additions and 727 deletions
25
blokk-packet-codecs/build.gradle.kts
Normal file
25
blokk-packet-codecs/build.gradle.kts
Normal file
|
@ -0,0 +1,25 @@
|
|||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val nettyVersion = properties["version.netty"].toString()
|
||||
|
||||
dependencies {
|
||||
api(project(":blokk-api"))
|
||||
|
||||
// Netty
|
||||
api("io.netty:netty-buffer:${nettyVersion}")
|
||||
}
|
||||
|
||||
tasks {
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package space.blokk.net
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import java.io.IOException
|
||||
import kotlin.experimental.and
|
||||
|
||||
/**
|
||||
* Extension functions on [ByteBuf] for reading and writing data types used by the Minecraft protocol
|
||||
*
|
||||
* You can access them using the [with] function (`with(MinecraftDataTypes) { ... }`).
|
||||
*/
|
||||
object MinecraftDataTypes {
|
||||
/**
|
||||
* Gets a variable length integer at the current readerIndex and increases the readerIndex by up to 5 in this buffer.
|
||||
*
|
||||
* @throws IOException if the VarInt is longer than the maximum of 5 bytes
|
||||
* @see <a href="https://wiki.vg/Protocol#VarInt_and_VarLong">https://wiki.vg/Protocol#VarInt_and_VarLong</a>
|
||||
*/
|
||||
fun ByteBuf.readVarInt(): Int {
|
||||
var bytesRead = 0
|
||||
var result = 0
|
||||
|
||||
do {
|
||||
if (bytesRead == 5) throw IOException("VarInt is longer than the maximum of 5 bytes")
|
||||
|
||||
val byte = readByte()
|
||||
val value: Int = (byte and 0b01111111).toInt()
|
||||
result = result or (value shl (7 * bytesRead))
|
||||
bytesRead += 1
|
||||
} while ((byte and 0b10000000.toByte()).toInt() != 0)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a VarInt can be read starting from the current readerIndex.
|
||||
*/
|
||||
fun ByteBuf.varIntReadable(): Boolean {
|
||||
if (readableBytes() > 5) {
|
||||
// maximum VarInt size
|
||||
return true
|
||||
}
|
||||
|
||||
val initialIndex = readerIndex()
|
||||
do {
|
||||
if (readableBytes() < 1) {
|
||||
readerIndex(initialIndex)
|
||||
return false
|
||||
}
|
||||
|
||||
val value = readByte().toInt()
|
||||
} while ((value and 0b10000000) != 0)
|
||||
|
||||
readerIndex(initialIndex)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable length integer at the current writerIndex and increases the writerIndex by up to 5 in this buffer.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Protocol#VarInt_and_VarLong">https://wiki.vg/Protocol#VarInt_and_VarLong</a>
|
||||
*/
|
||||
fun ByteBuf.writeVarInt(value: Int): ByteBuf {
|
||||
var v = value
|
||||
var part: Byte
|
||||
while (true) {
|
||||
part = (v and 0x7F).toByte()
|
||||
v = v ushr 7
|
||||
if (v != 0) {
|
||||
part = (part.toInt() or 0x80).toByte()
|
||||
}
|
||||
writeByte(part.toInt())
|
||||
if (v == 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a variable length UTF-8 encoded string at the current readerIndex and increases the readerIndex in this
|
||||
* buffer by the number of bytes read.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Protocol#Data_types">https://wiki.vg/Protocol#Data_types</a>
|
||||
*/
|
||||
fun ByteBuf.readString(): String {
|
||||
val length = readVarInt()
|
||||
val bytes = ByteArray(length)
|
||||
readBytes(bytes)
|
||||
return bytes.toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable length UTF-8 encoded string at the current writerIndex and increases the writerIndex in this
|
||||
* buffer by the number of bytes written.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Protocol#Data_types">https://wiki.vg/Protocol#Data_types</a>
|
||||
*/
|
||||
fun ByteBuf.writeString(value: String): ByteBuf {
|
||||
val bytes = value.toByteArray(Charsets.UTF_8)
|
||||
writeVarInt(bytes.size)
|
||||
writeBytes(bytes)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package space.blokk.net.packets.handshaking
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readString
|
||||
import space.blokk.net.MinecraftDataTypes.readVarInt
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object HandshakePacketCodec : IncomingPacketCodec<HandshakePacket>(0x00, HandshakePacket::class) {
|
||||
override fun decode(msg: ByteBuf): HandshakePacket = HandshakePacket(
|
||||
protocolVersion = msg.readVarInt(),
|
||||
serverAddress = msg.readString(),
|
||||
serverPort = msg.readUnsignedShort(),
|
||||
loginAttempt = msg.readVarInt() == 2
|
||||
)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package space.blokk.net.packets.handshaking
|
||||
|
||||
import space.blokk.net.packets.Protocol
|
||||
|
||||
object HandshakingProtocol : Protocol("HANDSHAKING", HandshakePacketCodec)
|
|
@ -0,0 +1,11 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object DisconnectPacketCodec : OutgoingPacketCodec<DisconnectPacket>(0x00, DisconnectPacket::class) {
|
||||
override fun DisconnectPacket.encode(dst: ByteBuf) {
|
||||
dst.writeString(reason.toJson())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object EncryptionRequestPacketCodec :
|
||||
OutgoingPacketCodec<EncryptionRequestPacket>(0x01, EncryptionRequestPacket::class) {
|
||||
override fun EncryptionRequestPacket.encode(dst: ByteBuf) {
|
||||
dst.writeString("") // Required for legacy reasons
|
||||
dst.writeVarInt(publicKey.size)
|
||||
dst.writeBytes(publicKey)
|
||||
dst.writeVarInt(verifyToken.size)
|
||||
dst.writeBytes(verifyToken)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readVarInt
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object EncryptionResponsePacketCodec :
|
||||
IncomingPacketCodec<EncryptionResponsePacket>(0x01, EncryptionResponsePacket::class) {
|
||||
override fun decode(msg: ByteBuf): EncryptionResponsePacket {
|
||||
val sharedSecretLength = msg.readVarInt()
|
||||
val sharedSecret = ByteArray(sharedSecretLength)
|
||||
msg.readBytes(sharedSecret)
|
||||
|
||||
val verifyTokenLength = msg.readVarInt()
|
||||
val verifyToken = ByteArray(verifyTokenLength)
|
||||
msg.readBytes(verifyToken)
|
||||
|
||||
return EncryptionResponsePacket(sharedSecret, verifyToken)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object LoginPluginRequestPacketCodec :
|
||||
OutgoingPacketCodec<LoginPluginRequestPacket>(0x04, LoginPluginRequestPacket::class) {
|
||||
override fun LoginPluginRequestPacket.encode(dst: ByteBuf) {
|
||||
dst.writeVarInt(messageID)
|
||||
dst.writeString(channel)
|
||||
dst.writeBytes(data)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readVarInt
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object LoginPluginResponsePacketCodec :
|
||||
IncomingPacketCodec<LoginPluginResponsePacket>(0x02, LoginPluginResponsePacket::class) {
|
||||
override fun decode(msg: ByteBuf): LoginPluginResponsePacket {
|
||||
val messageID = msg.readVarInt()
|
||||
val successful = msg.readBoolean()
|
||||
|
||||
// TODO: Test this
|
||||
val data =
|
||||
if (successful) ByteArray(msg.readableBytes()).also { msg.readBytes(it) }
|
||||
else null
|
||||
|
||||
return LoginPluginResponsePacket(messageID, data)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import space.blokk.net.packets.Protocol
|
||||
|
||||
object LoginProtocol : Protocol(
|
||||
"LOGIN",
|
||||
DisconnectPacketCodec,
|
||||
EncryptionRequestPacketCodec,
|
||||
EncryptionResponsePacketCodec,
|
||||
LoginPluginRequestPacketCodec,
|
||||
LoginPluginResponsePacketCodec,
|
||||
LoginStartPacketCodec,
|
||||
LoginSuccessPacketCodec,
|
||||
SetCompressionPacketCodec
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readString
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object LoginStartPacketCodec : IncomingPacketCodec<LoginStartPacket>(0x00, LoginStartPacket::class) {
|
||||
override fun decode(msg: ByteBuf): LoginStartPacket = LoginStartPacket(msg.readString())
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object LoginSuccessPacketCodec : OutgoingPacketCodec<LoginSuccessPacket>(0x02, LoginSuccessPacket::class) {
|
||||
override fun LoginSuccessPacket.encode(dst: ByteBuf) {
|
||||
dst.writeString(uuid.toString())
|
||||
dst.writeString(username)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package space.blokk.net.packets.login
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object SetCompressionPacketCodec : OutgoingPacketCodec<SetCompressionPacket>(0x03, SetCompressionPacket::class) {
|
||||
override fun SetCompressionPacket.encode(dst: ByteBuf) {
|
||||
dst.writeVarInt(threshold)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readString
|
||||
import space.blokk.net.MinecraftDataTypes.readVarInt
|
||||
import space.blokk.net.PacketDecodingException
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
import space.blokk.players.ChatMode
|
||||
import space.blokk.players.DisplayedSkinParts
|
||||
import space.blokk.players.MainHand
|
||||
import space.blokk.utils.checkBit
|
||||
|
||||
object ClientSettingsPacketCodec : IncomingPacketCodec<ClientSettingsPacket>(0x05, ClientSettingsPacket::class) {
|
||||
override fun decode(msg: ByteBuf): ClientSettingsPacket {
|
||||
val locale = msg.readString()
|
||||
val viewDistance = msg.readByte()
|
||||
|
||||
val chatMode = when (msg.readVarInt()) {
|
||||
0 -> ChatMode.ENABLED
|
||||
1 -> ChatMode.COMMANDS_ONLY
|
||||
2 -> ChatMode.HIDDEN
|
||||
else -> throw PacketDecodingException()
|
||||
}
|
||||
|
||||
val chatColorsEnabled = msg.readBoolean()
|
||||
|
||||
val skinFlags = msg.readByte()
|
||||
val displayedSkinParts = DisplayedSkinParts(
|
||||
cape = skinFlags.checkBit(0),
|
||||
jacket = skinFlags.checkBit(1),
|
||||
leftSleeve = skinFlags.checkBit(2),
|
||||
rightSleeve = skinFlags.checkBit(3),
|
||||
leftPantsLeg = skinFlags.checkBit(4),
|
||||
rightPantsLeg = skinFlags.checkBit(5),
|
||||
hat = skinFlags.checkBit(6)
|
||||
)
|
||||
|
||||
val mainHand = when (msg.readVarInt()) {
|
||||
0 -> MainHand.LEFT
|
||||
1 -> MainHand.RIGHT
|
||||
else -> throw PacketDecodingException()
|
||||
}
|
||||
|
||||
return ClientSettingsPacket(
|
||||
locale,
|
||||
viewDistance,
|
||||
chatMode,
|
||||
chatColorsEnabled,
|
||||
displayedSkinParts,
|
||||
mainHand
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.readString
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object IncomingPluginMessagePacketCodec :
|
||||
IncomingPacketCodec<IncomingPluginMessagePacket>(0x0B, IncomingPluginMessagePacket::class) {
|
||||
override fun decode(msg: ByteBuf): IncomingPluginMessagePacket {
|
||||
val channel = msg.readString()
|
||||
|
||||
// TODO: Test this
|
||||
val data = ByteArray(msg.readableBytes()).also { msg.readBytes(it) }
|
||||
|
||||
return IncomingPluginMessagePacket(channel, data)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
import space.blokk.players.GameMode
|
||||
import space.blokk.worlds.WorldDimension
|
||||
import space.blokk.worlds.WorldType
|
||||
|
||||
object JoinGamePacketCodec : OutgoingPacketCodec<JoinGamePacket>(0x26, JoinGamePacket::class) {
|
||||
override fun JoinGamePacket.encode(dst: ByteBuf) {
|
||||
dst.writeInt(entityID)
|
||||
var gameMode = when (gameMode) {
|
||||
GameMode.SURVIVAL -> 0
|
||||
GameMode.CREATIVE -> 1
|
||||
GameMode.ADVENTURE -> 2
|
||||
GameMode.SPECTATOR -> 3
|
||||
}
|
||||
|
||||
if (hardcore) gameMode = gameMode and 0x8
|
||||
|
||||
dst.writeByte(gameMode)
|
||||
|
||||
dst.writeInt(
|
||||
when (worldDimension) {
|
||||
WorldDimension.NETHER -> -1
|
||||
WorldDimension.OVERWORLD -> 0
|
||||
WorldDimension.END -> 1
|
||||
}
|
||||
)
|
||||
|
||||
dst.writeLong(worldSeedHash)
|
||||
dst.writeByte(0x00) // max players; not used anymore
|
||||
|
||||
dst.writeString(
|
||||
when (worldType) {
|
||||
WorldType.DEFAULT -> "default"
|
||||
WorldType.FLAT -> "flat"
|
||||
WorldType.LARGE_BIOMES -> "largeBiomes"
|
||||
WorldType.AMPLIFIED -> "amplified"
|
||||
WorldType.CUSTOMIZED -> "customized"
|
||||
WorldType.BUFFET -> "buffet"
|
||||
}
|
||||
)
|
||||
|
||||
dst.writeVarInt(viewDistance.toInt())
|
||||
dst.writeBoolean(reducedDebugInfo)
|
||||
dst.writeBoolean(respawnScreenEnabled)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object OutgoingPluginMessagePacketCodec :
|
||||
OutgoingPacketCodec<OutgoingPluginMessagePacket>(0x19, OutgoingPluginMessagePacket::class) {
|
||||
override fun OutgoingPluginMessagePacket.encode(dst: ByteBuf) {
|
||||
dst.writeString(channel)
|
||||
dst.writeBytes(data)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import space.blokk.net.packets.Protocol
|
||||
|
||||
object PlayProtocol : Protocol(
|
||||
"PLAY",
|
||||
ClientSettingsPacketCodec,
|
||||
IncomingPluginMessagePacketCodec,
|
||||
JoinGamePacketCodec,
|
||||
OutgoingPluginMessagePacketCodec,
|
||||
PlayerAbilitiesPacketCodec,
|
||||
ServerDifficultyPacketCodec
|
||||
)
|
|
@ -0,0 +1,18 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object PlayerAbilitiesPacketCodec : OutgoingPacketCodec<PlayerAbilitiesPacket>(0x32, PlayerAbilitiesPacket::class) {
|
||||
override fun PlayerAbilitiesPacket.encode(dst: ByteBuf) {
|
||||
var flags = 0
|
||||
if (invulnerable) flags = flags and 0x01
|
||||
if (flying) flags = flags and 0x02
|
||||
if (flyingAllowed) flags = flags and 0x04
|
||||
if (instantlyBreakBlocks) flags = flags and 0x08
|
||||
|
||||
dst.writeByte(flags)
|
||||
dst.writeFloat(flyingSpeed)
|
||||
dst.writeFloat(fieldOfView)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package space.blokk.net.packets.play
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
import space.blokk.worlds.WorldDifficulty
|
||||
|
||||
object ServerDifficultyPacketCodec : OutgoingPacketCodec<ServerDifficultyPacket>(0x0E, ServerDifficultyPacket::class) {
|
||||
override fun ServerDifficultyPacket.encode(dst: ByteBuf) {
|
||||
dst.writeByte(
|
||||
when (difficulty) {
|
||||
WorldDifficulty.PEACEFUL -> 0
|
||||
WorldDifficulty.EASY -> 1
|
||||
WorldDifficulty.NORMAL -> 2
|
||||
WorldDifficulty.HARD -> 3
|
||||
}
|
||||
)
|
||||
|
||||
dst.writeBoolean(locked)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package space.blokk.net.packets.status
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object PingPacketCodec : IncomingPacketCodec<PingPacket>(0x01, PingPacket::class) {
|
||||
override fun decode(msg: ByteBuf): PingPacket = PingPacket(msg.readLong())
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package space.blokk.net.packets.status
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
|
||||
object PongPacketCodec : OutgoingPacketCodec<PongPacket>(0x01, PongPacket::class) {
|
||||
override fun PongPacket.encode(dst: ByteBuf) {
|
||||
dst.writeLong(payload)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package space.blokk.net.packets.status
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.net.packets.IncomingPacketCodec
|
||||
|
||||
object RequestPacketCodec : IncomingPacketCodec<RequestPacket>(0x00, RequestPacket::class) {
|
||||
override fun decode(msg: ByteBuf): RequestPacket = RequestPacket()
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package space.blokk.net.packets.status
|
||||
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.Blokk
|
||||
import space.blokk.net.MinecraftDataTypes.writeString
|
||||
import space.blokk.net.packets.OutgoingPacketCodec
|
||||
import space.blokk.utils.toJson
|
||||
|
||||
object ResponsePacketCodec : OutgoingPacketCodec<ResponsePacket>(0x00, ResponsePacket::class) {
|
||||
override fun ResponsePacket.encode(dst: ByteBuf) {
|
||||
dst.writeString(
|
||||
Blokk.json.toJson(mapOf(
|
||||
"version" to mapOf(
|
||||
"name" to versionName,
|
||||
"protocol" to protocolVersion
|
||||
),
|
||||
"players" to players,
|
||||
"description" to description,
|
||||
"favicon" to favicon?.let { "data:image/png;base64,$it" }
|
||||
)))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package space.blokk.net.packets.status
|
||||
|
||||
import space.blokk.net.packets.Protocol
|
||||
|
||||
object StatusProtocol : Protocol(
|
||||
"STATUS",
|
||||
PingPacketCodec,
|
||||
RequestPacketCodec,
|
||||
PongPacketCodec,
|
||||
ResponsePacketCodec
|
||||
)
|
Reference in a new issue