Archived
1
0
Fork 0

Move packet encoders and decoders to their own project

This commit is contained in:
Moritz Ruth 2020-09-09 22:56:25 +02:00
parent 8403140a70
commit b021a4b753
94 changed files with 733 additions and 727 deletions

View 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"
}
}

View file

@ -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
}
}

View file

@ -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
)
}

View file

@ -0,0 +1,5 @@
package space.blokk.net.packets.handshaking
import space.blokk.net.packets.Protocol
object HandshakingProtocol : Protocol("HANDSHAKING", HandshakePacketCodec)

View file

@ -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())
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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())
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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())
}

View file

@ -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)
}
}

View file

@ -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()
}

View file

@ -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" }
)))
}
}

View file

@ -0,0 +1,11 @@
package space.blokk.net.packets.status
import space.blokk.net.packets.Protocol
object StatusProtocol : Protocol(
"STATUS",
PingPacketCodec,
RequestPacketCodec,
PongPacketCodec,
ResponsePacketCodec
)