Update all codec to suspend; added a default ProtocolCryptoContext implementation

This commit is contained in:
2026-09-07 07:40:08 +08:00
parent 687039f69a
commit 641df674e4
296 files changed
+2199 -1818

No files matched your search

+1
View File
@@ -48,3 +48,4 @@ bin/
.kotlin/ .kotlin/
**/build/* **/build/*
/libmc-protocol/src/jvmTest/resources/accessToken.txt /libmc-protocol/src/jvmTest/resources/accessToken.txt
/libmc-protocol-encrypt/src/commonTest/resources/accessToken.txt
+11 -8
View File
@@ -3,24 +3,27 @@ kotlin = "2.4.10"
kotlinx-io = "0.9.1" kotlinx-io = "0.9.1"
ktor-network = "3.5.2" ktor-network = "3.5.2"
coroutines-test = "1.11.0" coroutines-test = "1.11.0"
#kotlinx-serialization = "1.11.0"
kotlinx-coroutines = "1.11.0" kotlinx-coroutines = "1.11.0"
ktor-core = "3.5.2" ktor-core = "3.5.2"
cryptography-core = "0.6.0"
#kotlinx-serialization = "1.11.0"
[libraries] [libraries]
kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" } kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" }
ktor-network = { module = "io.ktor:ktor-network", version.ref = "ktor-network" } ktor-network = { module = "io.ktor:ktor-network", version.ref = "ktor-network" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines-test" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines-test" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
cryptography-provider-optimal = { module = "dev.whyoleg.cryptography:cryptography-provider-optimal", version.ref = "cryptography-core" }
cryptography-core = { module = "dev.whyoleg.cryptography:cryptography-core", version.ref = "cryptography-core" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor-core" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor-core" }
ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor-core" }
ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor-core" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor-core" }
#kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } #kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
#kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } #kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor-core" }
#ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor-core" }
#ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor-core" }
#ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor-core" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor-core" }
[plugins] [plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
maven-publish = { id = "maven-publish" } maven-publish = { id = "maven-publish" }
#kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+3 -3
View File
@@ -11,10 +11,10 @@ kotlin {
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
sourceSets { sourceSets {
jvmMain.dependencies { commonMain.dependencies {
// no dependencies needed api(libs.kotlinx.coroutines)
} }
jvmMain.dependencies {}
nativeMain.dependencies { nativeMain.dependencies {
implementation(libs.ktor.network) implementation(libs.ktor.network)
implementation(libs.kotlinx.io) implementation(libs.kotlinx.io)
@@ -1,47 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.common
public expect class BytesBuffer {
public constructor()
public constructor(bytes: ByteArray)
public fun writeByte(value: Byte)
public fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public fun writeDouble(value: Double, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public fun writeFloat(value: Float, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public fun writeBytes(bytes: ByteArray)
public fun writeBoolean(value: Boolean)
public fun readByte(): Byte
public fun readUByte(): UByte
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
public fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
public fun readDouble(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Double
public fun readFloat(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Float
public fun readBytes(length: Int): ByteArray
public fun readBoolean(): Boolean
public fun readRemainingBytes(): ByteArray
public fun toByteArray(): ByteArray
public fun hasRemaining(): Boolean
public fun close()
public val size: Int
public val remaining: Long
}
@Suppress("NOTHING_TO_INLINE")
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
public fun ReadChannel.readPacketFrame(): BytesBuffer {
val length = this.readVarInt()
return this.readBytes(length).wrap()
}
@@ -1,36 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.common
public expect open class ReadChannel() {
public open fun readByte(): Byte
public open fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
public open fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
public open fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
public open fun readBytes(length: Int): ByteArray
public open fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
}
public expect open class WriteChannel() {
public open fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
public open fun flush()
}
public fun ReadChannel.readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = this.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
@@ -1,30 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
*/
package cn.rtast.libmc.common
public interface Encoder<in T> {
public fun encode(buffer: BytesBuffer, value: T)
}
public interface Decoder<out T> {
public fun decode(buffer: BytesBuffer): T
}
public interface PacketCodec<T> : Encoder<T>, Decoder<T>
@Suppress("NOTHING_TO_INLINE")
public inline fun <T> BytesBuffer.write(value: T, encoder: Encoder<T>): Unit = encoder.encode(this, value)
@Suppress("NOTHING_TO_INLINE")
public inline fun <T> BytesBuffer.read(decoder: Decoder<T>): T = decoder.decode(this)
public fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) {
if (length <= 0) return
val bytes = source.readBytes(length.toInt())
this.writeBytes(bytes)
}
@@ -5,7 +5,7 @@
*/ */
package cn.rtast.libmc.protocol.session package cn.rtast.libmc.common.crypto
public fun interface AuthenticationProvider { public fun interface AuthenticationProvider {
public suspend fun joinServer(url: String, accessToken: String, uuid: String, serverIdHash: String) public suspend fun joinServer(url: String, accessToken: String, uuid: String, serverIdHash: String)
@@ -0,0 +1,13 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.crypto
public interface NetworkCipher {
public suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int)
public suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int)
}
@@ -5,9 +5,7 @@
*/ */
package cn.rtast.libmc.protocol.crypto package cn.rtast.libmc.common.crypto
import cn.rtast.libmc.protocol.session.AuthenticationProvider
public data class ProtocolContext( public data class ProtocolContext(
val rsaEncryptor: RSA1024Encryptor, val rsaEncryptor: RSA1024Encryptor,
@@ -16,25 +14,25 @@ public data class ProtocolContext(
val authProvider: AuthenticationProvider, val authProvider: AuthenticationProvider,
) )
public class ProtocolContextBuilder internal constructor(private val onlineMode: Boolean) { public class ProtocolContextBuilder(private val onlineMode: Boolean) {
public lateinit var rsaEncryptor: RSA1024Encryptor public lateinit var rsaEncryptor: RSA1024Encryptor
public lateinit var sha1Hasher: Sha1Hasher public lateinit var sha1Hasher: Sha1Hasher
public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher
public lateinit var authProvider: AuthenticationProvider public lateinit var authProvider: AuthenticationProvider
internal fun build(): ProtocolContext = ProtocolContext( public fun build(): ProtocolContext = ProtocolContext(
rsaEncryptor = if (::rsaEncryptor.isInitialized) rsaEncryptor else error("rsaEncryptor is required"), rsaEncryptor = if (::rsaEncryptor.isInitialized) rsaEncryptor else error("rsaEncryptor is required"),
sha1Hasher = if (::sha1Hasher.isInitialized) sha1Hasher else error("sha1Hasher is required"), sha1Hasher = if (::sha1Hasher.isInitialized) sha1Hasher else error("sha1Hasher is required"),
cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"), cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"),
authProvider = if (::authProvider.isInitialized && !onlineMode) authProvider else error("authProvider is required") authProvider = if (::authProvider.isInitialized) if (onlineMode) authProvider
else error("authProvider is required") else error("authProvider is required")
) )
} }
public fun interface RSA1024Encryptor { public fun interface RSA1024Encryptor {
public fun encrypt(key: ByteArray, data: ByteArray): ByteArray public suspend fun encrypt(key: ByteArray, data: ByteArray): ByteArray
} }
public fun interface Sha1Hasher { public fun interface Sha1Hasher {
public fun hash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String public suspend fun hash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String
} }
@@ -1,232 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.common
import kotlin.uuid.Uuid
public object VarIntCodec : PacketCodec<Int> {
override fun encode(buffer: BytesBuffer, value: Int) {
var v = value
while (true) {
if ((v and 0x7F.inv()) == 0) {
buffer.writeByte(v.toByte())
return
}
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
v = v ushr 7
}
}
override fun decode(buffer: BytesBuffer): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = buffer.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
}
public object VarLongCodec : PacketCodec<Long> {
override fun encode(buffer: BytesBuffer, value: Long) {
var v = value
while (true) {
if ((v and 0x7FL.inv()) == 0L) {
buffer.writeByte(v.toByte())
return
}
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
v = v ushr 7
}
}
override fun decode(buffer: BytesBuffer): Long {
var numRead = 0
var result = 0L
var read: Byte
do {
read = buffer.readByte()
val value = (read.toLong() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 10) throw IllegalArgumentException("VarLong is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
}
public object McStringCodec : PacketCodec<String> {
override fun encode(buffer: BytesBuffer, value: String) {
val bytes = value.encodeToByteArray()
VarIntCodec.encode(buffer, bytes.size)
buffer.writeBytes(bytes)
}
override fun decode(buffer: BytesBuffer): String {
val length = VarIntCodec.decode(buffer)
val bytes = buffer.readBytes(length)
return bytes.decodeToString()
}
}
public sealed interface IdOrX<out T> {
public data class Inline<T>(val value: T) : IdOrX<T>
public data class Reference(val registryId: Int) : IdOrX<Nothing>
}
public inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
when (value) {
is IdOrX.Inline -> {
this.writeVarInt(0)
this.writeX(value.value)
}
is IdOrX.Reference -> this.writeVarInt(value.registryId + 1)
}
}
public inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> {
val id = this.readVarInt()
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
}
public fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits ->
this.writeLong(mostSignificantBits)
this.writeLong(leastSignificantBits)
}
public fun BytesBuffer.readUuid(): Uuid {
val most = this.readLong()
val least = this.readLong()
return Uuid.fromLongs(most, least)
}
public fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
public fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
public fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
public fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
public fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
public fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
public fun BytesBuffer.readPrefixedByteArray(): ByteArray {
val length = this.readVarInt()
val data = this.readBytes(length)
return data
}
public fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
this.writeVarInt(data.size)
this.writeBytes(data)
}
public fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
if (data != null) {
this.writeBoolean(true)
this.writeVarInt(data.size)
this.writeBytes(data)
} else this.writeBoolean(false)
}
public fun BytesBuffer.readPrefixedStringArray(): List<String> {
val length = readVarInt()
val list = ArrayList<String>(length)
repeat(length) { list.add(readMcString()) }
return list
}
public fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
writeVarInt(value.size)
for (item in value) writeMcString(item)
}
public inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? {
val hasData = this.readBoolean()
return if (hasData) block.invoke(this) else null
}
/**
* buffer.writeOptional(value.someValue) { writeBlockPos(it) }
*/
public inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
if (value != null) {
this.writeBoolean(true)
block.invoke(this, value)
} else this.writeBoolean(false)
}
public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
val count = this.readVarInt()
val list = ArrayList<T>(count)
repeat(count) { _ -> list.add(this.reader()) }
return list
}
public inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) {
this.writeVarInt(list.size)
for (item in list) this.writer(item)
}
public fun BytesBuffer.readBitSet(): LongArray {
val count = this.readVarInt()
return LongArray(count) { this.readLong() }
}
public fun BytesBuffer.writeBitSet(data: LongArray) {
this.writeVarInt(data.size)
for (i in data.indices) this.writeLong(data[i])
}
public fun LongArray.countSetBits(): Int {
var count = 0
for (i in indices) count += this[i].countOneBits()
return count
}
public fun LongArray.getBit(bitIndex: Int): Boolean {
val longIndex = bitIndex shr 6
if (longIndex !in this.indices) return false
val bitOffset = bitIndex and 63
return (this[longIndex] and (1L shl bitOffset)) != 0L
}
public sealed interface IdSet {
public data class Tag(val tagName: String) : IdSet
public data class Entries(val ids: List<Int>) : IdSet
}
public fun BytesBuffer.readIdSet(): IdSet {
val type = this.readVarInt()
return if (type == 0) {
IdSet.Tag(tagName = this.readMcString())
} else {
val count = type - 1
val ids = ArrayList<Int>(count)
(0 until count).forEach { _ -> ids.add(this.readVarInt()) }
IdSet.Entries(ids)
}
}
public fun BytesBuffer.writeIdSet(idSet: IdSet) {
when (idSet) {
is IdSet.Tag -> {
this.writeVarInt(0)
this.writeMcString(idSet.tagName)
}
is IdSet.Entries -> {
this.writeVarInt(idSet.ids.size + 1)
for (id in idSet.ids) this.writeVarInt(id)
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
*/
package cn.rtast.libmc.common.packet
import cn.rtast.libmc.common.stream.BytesBuffer
public interface Encoder<in T> {
public suspend fun encode(buffer: BytesBuffer, value: T)
}
public interface Decoder<out T> {
public suspend fun decode(buffer: BytesBuffer): T
}
public interface PacketCodec<T> : Encoder<T>, Decoder<T>
public suspend fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) {
if (length <= 0) return
val bytes = source.readBytes(length.toInt())
this.writeBytes(bytes)
}
@@ -7,9 +7,8 @@
package cn.rtast.libmc.common.packet package cn.rtast.libmc.common.packet
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.common.writeVarInt
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class PacketRegistry { public class PacketRegistry {
@@ -29,10 +28,10 @@ public class PacketRegistry {
register(id, T::class, codec) register(id, T::class, codec)
} }
public fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket = public suspend fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
idToCodec[packetId]?.decode(buffer) ?: UnknownPacket(packetId, buffer.readBytes(buffer.remaining.toInt())) idToCodec[packetId]?.decode(buffer) ?: UnknownPacket(packetId, buffer.readBytes(buffer.remaining.toInt()))
public fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) { public suspend fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val info = requireNotNull(classToInfo[packet::class]) { val info = requireNotNull(classToInfo[packet::class]) {
"Unregistered Packet ${packet::class.simpleName}" "Unregistered Packet ${packet::class.simpleName}"
@@ -0,0 +1,33 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
public suspend fun BytesBuffer.readBitSet(): LongArray {
val count = this.readVarInt()
return LongArray(count) { this.readLong() }
}
public suspend fun BytesBuffer.writeBitSet(data: LongArray) {
this.writeVarInt(data.size)
for (i in data.indices) this.writeLong(data[i])
}
public fun LongArray.countSetBits(): Int {
var count = 0
for (i in indices) count += this[i].countOneBits()
return count
}
public fun LongArray.getBit(bitIndex: Int): Boolean {
val longIndex = bitIndex shr 6
if (longIndex !in this.indices) return false
val bitOffset = bitIndex and 63
return (this[longIndex] and (1L shl bitOffset)) != 0L
}
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
public sealed interface IdOrX<out T> {
public data class Inline<T>(val value: T) : IdOrX<T>
public data class Reference(val registryId: Int) : IdOrX<Nothing>
}
public suspend inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
when (value) {
is IdOrX.Inline -> {
this.writeVarInt(0)
this.writeX(value.value)
}
is IdOrX.Reference -> this.writeVarInt(value.registryId + 1)
}
}
public suspend inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> {
val id = this.readVarInt()
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
}
@@ -0,0 +1,41 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
public sealed interface IdSet {
public data class Tag(val tagName: String) : IdSet
public data class Entries(val ids: List<Int>) : IdSet
}
public suspend fun BytesBuffer.readIdSet(): IdSet {
val type = this.readVarInt()
return if (type == 0) {
IdSet.Tag(tagName = this.readMcString())
} else {
val count = type - 1
val ids = ArrayList<Int>(count)
(0 until count).forEach { _ -> ids.add(this.readVarInt()) }
IdSet.Entries(ids)
}
}
public suspend fun BytesBuffer.writeIdSet(idSet: IdSet) {
when (idSet) {
is IdSet.Tag -> {
this.writeVarInt(0)
this.writeMcString(idSet.tagName)
}
is IdSet.Entries -> {
this.writeVarInt(idSet.ids.size + 1)
for (id in idSet.ids) this.writeVarInt(id)
}
}
}
@@ -0,0 +1,28 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
public object McStringCodec : PacketCodec<String> {
override suspend fun encode(buffer: BytesBuffer, value: String) {
val bytes = value.encodeToByteArray()
VarIntCodec.encode(buffer, bytes.size)
buffer.writeBytes(bytes)
}
override suspend fun decode(buffer: BytesBuffer): String {
val length = VarIntCodec.decode(buffer)
val bytes = buffer.readBytes(length)
return bytes.decodeToString()
}
}
public suspend fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
public suspend fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
@@ -0,0 +1,25 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
public suspend inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? {
val hasData = this.readBoolean()
return if (hasData) block.invoke(this) else null
}
/**
* buffer.writeOptional(value.someValue) { writeBlockPos(it) }
*/
public suspend inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
if (value != null) {
this.writeBoolean(true)
block.invoke(this, value)
} else this.writeBoolean(false)
}
@@ -0,0 +1,56 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
public suspend fun BytesBuffer.readPrefixedByteArray(): ByteArray {
val length = this.readVarInt()
val data = this.readBytes(length)
return data
}
public suspend fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
this.writeVarInt(data.size)
this.writeBytes(data)
}
public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
if (data != null) {
this.writeBoolean(true)
this.writeVarInt(data.size)
this.writeBytes(data)
} else this.writeBoolean(false)
}
public suspend fun BytesBuffer.readPrefixedStringArray(): List<String> {
val length = readVarInt()
val list = ArrayList<String>(length)
repeat(length) { list.add(readMcString()) }
return list
}
public suspend fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
writeVarInt(value.size)
for (item in value) writeMcString(item)
}
public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
val count = this.readVarInt()
val list = ArrayList<T>(count)
repeat(count) { _ -> list.add(this.reader()) }
return list
}
public suspend inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) {
this.writeVarInt(list.size)
for (item in list) this.writer(item)
}
@@ -0,0 +1,22 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
import kotlin.uuid.Uuid
public suspend fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits ->
this.writeLong(mostSignificantBits)
this.writeLong(leastSignificantBits)
}
public suspend fun BytesBuffer.readUuid(): Uuid {
val most = this.readLong()
val least = this.readLong()
return Uuid.fromLongs(most, least)
}
@@ -0,0 +1,45 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
public object VarIntCodec : PacketCodec<Int> {
override suspend fun encode(buffer: BytesBuffer, value: Int) {
var v = value
while (true) {
if ((v and 0x7F.inv()) == 0) {
buffer.writeByte(v.toByte())
return
}
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
v = v ushr 7
}
}
override suspend fun decode(buffer: BytesBuffer): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = buffer.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
}
public suspend fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
public suspend fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
public suspend fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
public suspend fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
@@ -0,0 +1,39 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.common.primitives
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
public object VarLongCodec : PacketCodec<Long> {
override suspend fun encode(buffer: BytesBuffer, value: Long) {
var v = value
while (true) {
if ((v and 0x7FL.inv()) == 0L) {
buffer.writeByte(v.toByte())
return
}
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
v = v ushr 7
}
}
override suspend fun decode(buffer: BytesBuffer): Long {
var numRead = 0
var result = 0L
var read: Byte
do {
read = buffer.readByte()
val value = (read.toLong() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 10) throw IllegalArgumentException("VarLong is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
}
@@ -0,0 +1,47 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.common.stream
public expect class BytesBuffer {
public constructor()
public constructor(bytes: ByteArray)
public suspend fun writeByte(value: Byte)
public suspend fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public suspend fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public suspend fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public suspend fun writeDouble(value: Double, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public suspend fun writeFloat(value: Float, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
public suspend fun writeBytes(bytes: ByteArray)
public suspend fun writeBoolean(value: Boolean)
public suspend fun readByte(): Byte
public suspend fun readUByte(): UByte
public suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
public suspend fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
public suspend fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
public suspend fun readDouble(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Double
public suspend fun readFloat(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Float
public suspend fun readBytes(length: Int): ByteArray
public suspend fun readBoolean(): Boolean
public suspend fun readRemainingBytes(): ByteArray
public suspend fun toByteArray(): ByteArray
public suspend fun hasRemaining(): Boolean
public suspend fun close()
public val size: Int
public val remaining: Long
}
@Suppress("NOTHING_TO_INLINE")
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
public suspend fun ReadChannel.readPacketFrame(): BytesBuffer {
val length = this.readVarInt()
return this.readBytes(length).wrap()
}
@@ -5,7 +5,7 @@
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
public enum class ByteOrder { public enum class ByteOrder {
BIG_ENDIAN, LITTLE_ENDIAN BIG_ENDIAN, LITTLE_ENDIAN
@@ -0,0 +1,36 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.common.stream
public expect open class ReadChannel() {
public open suspend fun readByte(): Byte
public open suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
public open suspend fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
public open suspend fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
public open suspend fun readBytes(length: Int): ByteArray
public open suspend fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
}
public expect open class WriteChannel() {
public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
public open suspend fun flush()
}
public suspend fun ReadChannel.readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = this.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
@@ -5,7 +5,9 @@
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import cn.rtast.libmc.common.LibMCContext
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) { public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
public fun openReadChannel(): ReadChannel public fun openReadChannel(): ReadChannel
@@ -14,6 +16,6 @@ public expect class Socket public constructor(host: String, port: Int, context:
} }
public expect class UdpSocket public constructor(host: String, port: Int, context: LibMCContext) { public expect class UdpSocket public constructor(host: String, port: Int, context: LibMCContext) {
public fun sendAndReceive(data: ByteArray): ByteArray public suspend fun sendAndReceive(data: ByteArray): ByteArray
public fun close() public fun close()
} }
@@ -4,8 +4,10 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
public actual class BytesBuffer { public actual class BytesBuffer {
@@ -19,12 +21,12 @@ public actual class BytesBuffer {
outStream.write(bytes) outStream.write(bytes)
} }
public actual fun writeByte(value: Byte) { public actual suspend fun writeByte(value: Byte) {
readBuffer = null readBuffer = null
outStream.write(value.toInt()) outStream.write(value.toInt())
} }
public actual fun writeShort(value: Short, endian: ByteOrder) { public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
readBuffer = null readBuffer = null
val v = value.toInt() val v = value.toInt()
if (endian == ByteOrder.BIG_ENDIAN) { if (endian == ByteOrder.BIG_ENDIAN) {
@@ -36,7 +38,7 @@ public actual class BytesBuffer {
} }
} }
public actual fun writeInt(value: Int, endian: ByteOrder) { public actual suspend fun writeInt(value: Int, endian: ByteOrder) {
readBuffer = null readBuffer = null
if (endian == ByteOrder.BIG_ENDIAN) { if (endian == ByteOrder.BIG_ENDIAN) {
outStream.write(value shr 24) outStream.write(value shr 24)
@@ -51,7 +53,7 @@ public actual class BytesBuffer {
} }
} }
public actual fun writeLong(value: Long, endian: ByteOrder) { public actual suspend fun writeLong(value: Long, endian: ByteOrder) {
readBuffer = null readBuffer = null
if (endian == ByteOrder.BIG_ENDIAN) { if (endian == ByteOrder.BIG_ENDIAN) {
for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt()) for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt())
@@ -60,22 +62,22 @@ public actual class BytesBuffer {
} }
} }
public actual fun writeDouble(value: Double, endian: ByteOrder) { public actual suspend fun writeDouble(value: Double, endian: ByteOrder) {
writeLong(value.toRawBits(), endian) writeLong(value.toRawBits(), endian)
} }
public actual fun writeFloat(value: Float, endian: ByteOrder) { public actual suspend fun writeFloat(value: Float, endian: ByteOrder) {
writeInt(value.toRawBits(), endian) writeInt(value.toRawBits(), endian)
} }
public actual fun writeBytes(bytes: ByteArray) { public actual suspend fun writeBytes(bytes: ByteArray) {
readBuffer = null readBuffer = null
outStream.write(bytes) withContext(Dispatchers.IO) { outStream.write(bytes) }
} }
public actual fun writeBoolean(value: Boolean): Unit = writeByte(if (value) 0x01 else 0x00) public actual suspend fun writeBoolean(value: Boolean): Unit = writeByte(if (value) 0x01 else 0x00)
private fun ensureReadArray(): ByteArray { private suspend fun ensureReadArray(): ByteArray {
var buf = readBuffer var buf = readBuffer
if (buf == null) { if (buf == null) {
buf = outStream.toByteArray() buf = outStream.toByteArray()
@@ -84,26 +86,26 @@ public actual class BytesBuffer {
return buf return buf
} }
public actual fun readByte(): Byte { public actual suspend fun readByte(): Byte {
val array = ensureReadArray() val array = ensureReadArray()
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow") if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
return array[readOffset++] return array[readOffset++]
} }
public actual fun readUByte(): UByte = this.readByte().toUByte() public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
public actual fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian) public actual suspend fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian) public actual suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian) public actual suspend fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
public actual fun readDouble(endian: ByteOrder): Double { public actual suspend fun readDouble(endian: ByteOrder): Double {
return Double.fromBits(readLong(endian)) return Double.fromBits(readLong(endian))
} }
public actual fun readFloat(endian: ByteOrder): Float { public actual suspend fun readFloat(endian: ByteOrder): Float {
return Float.fromBits(readInt(endian)) return Float.fromBits(readInt(endian))
} }
public actual fun readBytes(length: Int): ByteArray { public actual suspend fun readBytes(length: Int): ByteArray {
val array = ensureReadArray() val array = ensureReadArray()
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow") if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
val result = array.copyOfRange(readOffset, readOffset + length) val result = array.copyOfRange(readOffset, readOffset + length)
@@ -111,9 +113,9 @@ public actual class BytesBuffer {
return result return result
} }
public actual fun readBoolean(): Boolean = this.readByte() != 0x00.toByte() public actual suspend fun readBoolean(): Boolean = this.readByte() != 0x00.toByte()
public actual fun readRemainingBytes(): ByteArray { public actual suspend fun readRemainingBytes(): ByteArray {
val array = ensureReadArray() val array = ensureReadArray()
if (readOffset >= array.size) return byteArrayOf() if (readOffset >= array.size) return byteArrayOf()
val result = array.copyOfRange(readOffset, array.size) val result = array.copyOfRange(readOffset, array.size)
@@ -121,10 +123,10 @@ public actual class BytesBuffer {
return result return result
} }
public actual fun toByteArray(): ByteArray = outStream.toByteArray() public actual suspend fun toByteArray(): ByteArray = outStream.toByteArray()
public actual fun hasRemaining(): Boolean = readOffset < ensureReadArray().size public actual suspend fun hasRemaining(): Boolean = readOffset < ensureReadArray().size
public actual fun close(): Unit = outStream.close() public actual suspend fun close(): Unit = withContext(Dispatchers.IO) { outStream.close() }
public actual val size: Int get() = outStream.size() public actual val size: Int get() = outStream.size()
public actual val remaining: Long get() = (outStream.size() - readOffset).coerceAtLeast(0).toLong() public actual val remaining: Long get() = (outStream.size() - readOffset).coerceAtLeast(0).toLong()
} }
@@ -4,7 +4,7 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import java.io.EOFException import java.io.EOFException
import java.io.InputStream import java.io.InputStream
@@ -17,7 +17,7 @@ public actual open class ReadChannel public actual constructor() {
this._inputStream = inputStream this._inputStream = inputStream
} }
public actual open fun readFully(out: ByteArray, start: Int, end: Int) { public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int) {
var bytesRead = 0 var bytesRead = 0
val length = end - start val length = end - start
while (bytesRead < length) { while (bytesRead < length) {
@@ -27,21 +27,21 @@ public actual open class ReadChannel public actual constructor() {
} }
} }
public actual open fun readByte(): Byte { public actual open suspend fun readByte(): Byte {
val buf = ByteArray(1) val buf = ByteArray(1)
readFully(buf, 0, 1) readFully(buf, 0, 1)
return buf[0] return buf[0]
} }
public actual open fun readBytes(length: Int): ByteArray { public actual open suspend fun readBytes(length: Int): ByteArray {
val bytes = ByteArray(length) val bytes = ByteArray(length)
readFully(bytes, 0, length) readFully(bytes, 0, length)
return bytes return bytes
} }
public actual open fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian) public actual open suspend fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual open fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian) public actual open suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
public actual open fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian) public actual open suspend fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
} }
public actual open class WriteChannel public actual constructor() { public actual open class WriteChannel public actual constructor() {
@@ -51,8 +51,8 @@ public actual open class WriteChannel public actual constructor() {
_outputStream = outputStream _outputStream = outputStream
} }
public actual open fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit = public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
_outputStream.write(value, startIndex, endIndex - startIndex) _outputStream.write(value, startIndex, endIndex - startIndex)
public actual open fun flush(): Unit = _outputStream.flush() public actual open suspend fun flush(): Unit = _outputStream.flush()
} }
@@ -5,7 +5,7 @@
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
internal fun ByteArray.toShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short { internal fun ByteArray.toShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short {
val b1 = this[0].toInt() and 0xFF val b1 = this[0].toInt() and 0xFF
@@ -4,8 +4,11 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import cn.rtast.libmc.common.LibMCContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.DatagramPacket import java.net.DatagramPacket
import java.net.DatagramSocket import java.net.DatagramSocket
import java.net.InetSocketAddress import java.net.InetSocketAddress
@@ -25,13 +28,12 @@ public actual class UdpSocket public actual constructor(host: String, port: Int,
connect(InetSocketAddress(host, port)) connect(InetSocketAddress(host, port))
} }
public actual fun sendAndReceive(data: ByteArray): ByteArray { public actual suspend fun sendAndReceive(data: ByteArray): ByteArray = withContext(Dispatchers.IO) {
socket.send(DatagramPacket(data, data.size)) socket.send(DatagramPacket(data, data.size))
val buf = ByteArray(2048) val buf = ByteArray(2048)
val receivePacket = DatagramPacket(buf, buf.size) val receivePacket = DatagramPacket(buf, buf.size)
socket.receive(receivePacket) socket.receive(receivePacket)
return buf.copyOf(receivePacket.length) return@withContext buf.copyOf(receivePacket.length)
} }
public actual fun close(): Unit = socket.close() public actual fun close(): Unit = socket.close()
@@ -4,7 +4,7 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
import kotlinx.io.* import kotlinx.io.*
@@ -21,53 +21,53 @@ public actual class BytesBuffer {
_delegateBuf = Buffer().apply { write(bytes) } _delegateBuf = Buffer().apply { write(bytes) }
} }
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value) public actual suspend fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
public actual fun writeShort(value: Short, endian: ByteOrder) { public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value) if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value)
else _delegateBuf.writeShortLe(value) else _delegateBuf.writeShortLe(value)
} }
public actual fun writeInt(value: Int, endian: ByteOrder) { public actual suspend fun writeInt(value: Int, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value) if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
else _delegateBuf.writeIntLe(value) else _delegateBuf.writeIntLe(value)
} }
public actual fun writeLong(value: Long, endian: ByteOrder) { public actual suspend fun writeLong(value: Long, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value) if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
else _delegateBuf.writeLongLe(value) else _delegateBuf.writeLongLe(value)
} }
public actual fun writeDouble(value: Double, endian: ByteOrder): Unit = public actual suspend fun writeDouble(value: Double, endian: ByteOrder): Unit =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeDouble(value) else _delegateBuf.writeDoubleLe(value) if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeDouble(value) else _delegateBuf.writeDoubleLe(value)
public actual fun writeFloat(value: Float, endian: ByteOrder): Unit = public actual suspend fun writeFloat(value: Float, endian: ByteOrder): Unit =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeFloat(value) else _delegateBuf.writeFloatLe(value) if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeFloat(value) else _delegateBuf.writeFloatLe(value)
public actual fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes) public actual suspend fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
public actual fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00) public actual suspend fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
public actual fun readByte(): Byte = _delegateBuf.readByte() public actual suspend fun readByte(): Byte = _delegateBuf.readByte()
public actual fun readUByte(): UByte = this.readByte().toUByte() public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
public actual fun readShort(endian: ByteOrder): Short = public actual suspend fun readShort(endian: ByteOrder): Short =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readShort() else _delegateBuf.readShortLe() if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readShort() else _delegateBuf.readShortLe()
public actual fun readInt(endian: ByteOrder): Int = public actual suspend fun readInt(endian: ByteOrder): Int =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe() if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe()
public actual fun readLong(endian: ByteOrder): Long = public actual suspend fun readLong(endian: ByteOrder): Long =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe() if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe()
public actual fun readDouble(endian: ByteOrder): Double = public actual suspend fun readDouble(endian: ByteOrder): Double =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readDouble() else _delegateBuf.readDoubleLe() if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readDouble() else _delegateBuf.readDoubleLe()
public actual fun readFloat(endian: ByteOrder): Float = public actual suspend fun readFloat(endian: ByteOrder): Float =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readFloat() else _delegateBuf.readFloatLe() if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readFloat() else _delegateBuf.readFloatLe()
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length) public actual suspend fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
public actual fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte() public actual suspend fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
public actual fun readRemainingBytes(): ByteArray = this.toByteArray() public actual suspend fun readRemainingBytes(): ByteArray = this.toByteArray()
public actual fun toByteArray(): ByteArray { public actual suspend fun toByteArray(): ByteArray {
val copy = _delegateBuf.peek() val copy = _delegateBuf.peek()
return try { return try {
copy.readByteArray() copy.readByteArray()
@@ -76,8 +76,8 @@ public actual class BytesBuffer {
} }
} }
public actual fun hasRemaining(): Boolean = !_delegateBuf.exhausted() public actual suspend fun hasRemaining(): Boolean = !_delegateBuf.exhausted()
public actual fun close(): Unit = _delegateBuf.close() public actual suspend fun close(): Unit = _delegateBuf.close()
public actual val size: Int get() = _delegateBuf.size.toInt() public actual val size: Int get() = _delegateBuf.size.toInt()
public actual val remaining: Long get() = _delegateBuf.remaining public actual val remaining: Long get() = _delegateBuf.remaining
@@ -4,11 +4,10 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import io.ktor.utils.io.* import io.ktor.utils.io.*
import io.ktor.utils.io.bits.* import io.ktor.utils.io.bits.*
import kotlinx.coroutines.runBlocking
public actual open class ReadChannel public actual constructor() { public actual open class ReadChannel public actual constructor() {
@@ -18,18 +17,18 @@ public actual open class ReadChannel public actual constructor() {
this._readChannel = readChannel this._readChannel = readChannel
} }
public actual open fun readByte(): Byte = runBlocking { _readChannel.readByte() } public actual open suspend fun readByte(): Byte = _readChannel.readByte()
public actual open fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) } public actual open suspend fun readBytes(length: Int): ByteArray = _readChannel.readByteArray(length)
public actual open fun readFully(out: ByteArray, start: Int, end: Int): Unit = public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int): Unit =
runBlocking { _readChannel.readFully(out, start, end) } _readChannel.readFully(out, start, end)
public actual open fun readShort(endian: ByteOrder): Short { public actual open suspend fun readShort(endian: ByteOrder): Short {
val bytes = readBytes(2) val bytes = readBytes(2)
val v = ((bytes[0].toInt() and 0xFF shl 8) or (bytes[1].toInt() and 0xFF)).toShort() val v = ((bytes[0].toInt() and 0xFF shl 8) or (bytes[1].toInt() and 0xFF)).toShort()
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder() return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
} }
public actual open fun readInt(endian: ByteOrder): Int { public actual open suspend fun readInt(endian: ByteOrder): Int {
val bytes = readBytes(4) val bytes = readBytes(4)
val v = (bytes[0].toInt() and 0xFF shl 24) or val v = (bytes[0].toInt() and 0xFF shl 24) or
(bytes[1].toInt() and 0xFF shl 16) or (bytes[1].toInt() and 0xFF shl 16) or
@@ -38,7 +37,7 @@ public actual open class ReadChannel public actual constructor() {
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder() return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
} }
public actual open fun readLong(endian: ByteOrder): Long { public actual open suspend fun readLong(endian: ByteOrder): Long {
val bytes = readBytes(8) val bytes = readBytes(8)
var v = 0L var v = 0L
for (i in 0 until 8) { for (i in 0 until 8) {
@@ -55,8 +54,8 @@ public actual open class WriteChannel public actual constructor() {
_writeChannel = writeChannel _writeChannel = writeChannel
} }
public actual open fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit = public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
runBlocking { _writeChannel.writeFully(value, startIndex, endIndex) } _writeChannel.writeFully(value, startIndex, endIndex)
public actual open fun flush(): Unit = runBlocking { _writeChannel.flush() } public actual open suspend fun flush(): Unit = _writeChannel.flush()
} }
@@ -4,8 +4,9 @@
* Date: 2026/9/3 * Date: 2026/9/3
*/ */
package cn.rtast.libmc.common package cn.rtast.libmc.common.stream
import cn.rtast.libmc.common.LibMCContext
import io.ktor.network.sockets.* import io.ktor.network.sockets.*
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@@ -32,10 +33,10 @@ public actual class UdpSocket public actual constructor(host: String, port: Int,
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() } private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
private val remoteAddress = InetSocketAddress(host, port) private val remoteAddress = InetSocketAddress(host, port)
public actual fun sendAndReceive(data: ByteArray): ByteArray = runBlocking { public actual suspend fun sendAndReceive(data: ByteArray): ByteArray {
val packet = buildPacket { writeFully(data) } val packet = buildPacket { writeFully(data) }
socket.send(Datagram(packet, remoteAddress)) socket.send(Datagram(packet, remoteAddress))
socket.receive().packet.readByteArray() return socket.receive().packet.readByteArray()
} }
public actual fun close() { public actual fun close() {
@@ -7,8 +7,8 @@
package cn.rtast.libmc.mcping.bedrock package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import kotlin.random.Random import kotlin.random.Random
private val RAKNET_MAGIC = byteArrayOf( private val RAKNET_MAGIC = byteArrayOf(
@@ -33,14 +33,14 @@ internal data class BedrockRequestPacket(
override val packetId: Byte = 0x01 override val packetId: Byte = 0x01
companion object Codec : PacketCodec<BedrockRequestPacket> { companion object Codec : PacketCodec<BedrockRequestPacket> {
override fun encode(buffer: BytesBuffer, value: BedrockRequestPacket) { override suspend fun encode(buffer: BytesBuffer, value: BedrockRequestPacket) {
buffer.writeByte(value.packetId) buffer.writeByte(value.packetId)
buffer.writeLong(value.time) buffer.writeLong(value.time)
buffer.writeBytes(value.magic) buffer.writeBytes(value.magic)
buffer.writeLong(value.guid) buffer.writeLong(value.guid)
} }
override fun decode(buffer: BytesBuffer): BedrockRequestPacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): BedrockRequestPacket = throw UnsupportedOperationException()
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -72,8 +72,8 @@ internal data class BedrockResponsePacket(
) : MinecraftBedrockPacket { ) : MinecraftBedrockPacket {
companion object Codec : PacketCodec<BedrockResponsePacket> { companion object Codec : PacketCodec<BedrockResponsePacket> {
override fun encode(buffer: BytesBuffer, value: BedrockResponsePacket) = throw UnsupportedOperationException() override suspend fun encode(buffer: BytesBuffer, value: BedrockResponsePacket) = throw UnsupportedOperationException()
override fun decode(buffer: BytesBuffer): BedrockResponsePacket { override suspend fun decode(buffer: BytesBuffer): BedrockResponsePacket {
val packetId = buffer.readByte() val packetId = buffer.readByte()
if (packetId != 0x1C.toByte()) throw IllegalStateException("Expected pong id 0x1C, got $packetId") if (packetId != 0x1C.toByte()) throw IllegalStateException("Expected pong id 0x1C, got $packetId")
val time = buffer.readLong() val time = buffer.readLong()
@@ -7,12 +7,12 @@
package cn.rtast.libmc.mcping.bedrock package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.UdpSocket import cn.rtast.libmc.common.stream.UdpSocket
internal fun <T : MinecraftBedrockPacket> UdpSocket.sendPacket(packet: T, codec: PacketCodec<T>): ByteArray { internal suspend fun <T : MinecraftBedrockPacket> UdpSocket.sendPacket(packet: T, codec: PacketCodec<T>): ByteArray {
val buf = BytesBuffer() val buf = BytesBuffer()
codec.encode(buf, packet) codec.encode(buf, packet)
return sendAndReceive(buf.toByteArray()) return sendAndReceive(buf.toByteArray())
@@ -8,12 +8,12 @@
package cn.rtast.libmc.mcping.bedrock package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.common.UdpSocket import cn.rtast.libmc.common.stream.UdpSocket
import cn.rtast.libmc.common.wrap import cn.rtast.libmc.common.stream.wrap
import cn.rtast.libmc.mcping.PingResponse import cn.rtast.libmc.mcping.PingResponse
import kotlin.time.Clock import kotlin.time.Clock
internal fun pingBedrockServer(host: String, port: Int, context: LibMCContext): PingResponse { internal suspend fun pingBedrockServer(host: String, port: Int, context: LibMCContext): PingResponse {
val socket = UdpSocket(host, port, context) val socket = UdpSocket(host, port, context)
return try { return try {
val sendTime = Clock.System.now().toEpochMilliseconds() val sendTime = Clock.System.now().toEpochMilliseconds()
@@ -7,10 +7,10 @@
package cn.rtast.libmc.mcping.java package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.McStringCodec import cn.rtast.libmc.common.primitives.McStringCodec
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.VarIntCodec import cn.rtast.libmc.common.primitives.VarIntCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake // ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
@@ -22,30 +22,30 @@ internal data class HandshakePacket(
val nextState: Int, val nextState: Int,
) : MinecraftPacket { ) : MinecraftPacket {
companion object Codec : PacketCodec<HandshakePacket> { companion object Codec : PacketCodec<HandshakePacket> {
override fun encode(buffer: BytesBuffer, value: HandshakePacket) { override suspend fun encode(buffer: BytesBuffer, value: HandshakePacket) {
VarIntCodec.encode(buffer, value.protocolVersion) VarIntCodec.encode(buffer, value.protocolVersion)
McStringCodec.encode(buffer, value.serverAddress) McStringCodec.encode(buffer, value.serverAddress)
buffer.writeShort(value.serverPort.toShort()) buffer.writeShort(value.serverPort.toShort())
VarIntCodec.encode(buffer, value.nextState) VarIntCodec.encode(buffer, value.nextState)
} }
override fun decode(buffer: BytesBuffer): HandshakePacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): HandshakePacket = throw UnsupportedOperationException()
} }
} }
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status // ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status
internal data object StatusRequestPacket : MinecraftPacket, PacketCodec<StatusRequestPacket> { internal data object StatusRequestPacket : MinecraftPacket, PacketCodec<StatusRequestPacket> {
override fun encode(buffer: BytesBuffer, value: StatusRequestPacket) {} override suspend fun encode(buffer: BytesBuffer, value: StatusRequestPacket) {}
override fun decode(buffer: BytesBuffer): StatusRequestPacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): StatusRequestPacket = throw UnsupportedOperationException()
} }
internal data class PingPacket(val currentTime: Long) : MinecraftPacket { internal data class PingPacket(val currentTime: Long) : MinecraftPacket {
companion object : PacketCodec<PingPacket> { companion object : PacketCodec<PingPacket> {
override fun encode(buffer: BytesBuffer, value: PingPacket) { override suspend fun encode(buffer: BytesBuffer, value: PingPacket) {
buffer.writeLong(value.currentTime) buffer.writeLong(value.currentTime)
} }
override fun decode(buffer: BytesBuffer): PingPacket { override suspend fun decode(buffer: BytesBuffer): PingPacket {
return PingPacket(buffer.readLong()) return PingPacket(buffer.readLong())
} }
} }
@@ -8,11 +8,15 @@
package cn.rtast.libmc.mcping.java package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.common.* import cn.rtast.libmc.common.*
import cn.rtast.libmc.common.primitives.McStringCodec
import cn.rtast.libmc.common.primitives.VarIntCodec
import cn.rtast.libmc.common.stream.Socket
import cn.rtast.libmc.common.stream.readPacketFrame
import cn.rtast.libmc.mcping.PingResponse import cn.rtast.libmc.mcping.PingResponse
import cn.rtast.libmc.mcping.sendPacket import cn.rtast.libmc.mcping.sendPacket
import kotlin.time.Clock import kotlin.time.Clock
internal fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse { internal suspend fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse {
val socket = Socket(host, port, context) val socket = Socket(host, port, context)
val receiveChannel = socket.openReadChannel() val receiveChannel = socket.openReadChannel()
val sendChannel = socket.openWriteChannel() val sendChannel = socket.openWriteChannel()
@@ -16,7 +16,7 @@ import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
@JvmOverloads @JvmOverloads
public fun mcping( public suspend fun mcping(
host: String, host: String,
port: Int, port: Int,
type: ServerType = ServerType.Java, type: ServerType = ServerType.Java,
@@ -7,15 +7,19 @@
package cn.rtast.libmc.mcping package cn.rtast.libmc.mcping
import cn.rtast.libmc.common.* import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.primitives.VarIntCodec
import cn.rtast.libmc.common.stream.WriteChannel
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.writeBuffer
public fun <T : MinecraftPacket> WriteChannel.sendPacket(packet: T, packetId: Int, codec: PacketCodec<T>) { public suspend fun <T : MinecraftPacket> WriteChannel.sendPacket(packet: T, packetId: Int, codec: PacketCodec<T>) {
val bodyBuffer = BytesBuffer() val bodyBuffer = BytesBuffer()
bodyBuffer.write(packetId, VarIntCodec) VarIntCodec.encode(bodyBuffer, packetId)
codec.encode(bodyBuffer, packet) codec.encode(bodyBuffer, packet)
val frameBuffer = BytesBuffer() val frameBuffer = BytesBuffer()
frameBuffer.write(bodyBuffer.size, VarIntCodec) VarIntCodec.encode(bodyBuffer, bodyBuffer.size)
frameBuffer.writeBuffer(bodyBuffer) frameBuffer.writeBuffer(bodyBuffer)
val bytes = frameBuffer.toByteArray() val bytes = frameBuffer.toByteArray()
this.writeFully(bytes, 0, bytes.size) this.writeFully(bytes, 0, bytes.size)
@@ -7,12 +7,12 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder import cn.rtast.libmc.common.stream.ByteOrder
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput { public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput {
override fun readByte(): Byte = buffer.readByte() override suspend fun readByte(): Byte = buffer.readByte()
override fun readBytes(count: Int): ByteArray = buffer.readBytes(count) override suspend fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
} }
public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput = public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput =
@@ -7,17 +7,17 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder import cn.rtast.libmc.common.stream.ByteOrder
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
public class BytesBufferNBTOutput( public class BytesBufferNBTOutput(
override val order: ByteOrder, override val order: ByteOrder,
override val root: NBTTag.CompoundTag, override val root: NBTTag.CompoundTag,
private val buffer: BytesBuffer, private val buffer: BytesBuffer,
) : NBTOutput { ) : NBTOutput {
override fun writeByte(value: Byte): Unit = buffer.writeByte(value) override suspend fun writeByte(value: Byte): Unit = buffer.writeByte(value)
override fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value) override suspend fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
override fun toByteArray(): ByteArray = buffer.toByteArray() override suspend fun toByteArray(): ByteArray = buffer.toByteArray()
} }
public fun BytesBuffer.toNBTOutput( public fun BytesBuffer.toNBTOutput(
@@ -7,33 +7,33 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder import cn.rtast.libmc.common.stream.ByteOrder
public interface NBTInput { public interface NBTInput {
public val order: ByteOrder public val order: ByteOrder
public fun readByte(): Byte public suspend fun readByte(): Byte
public fun readBytes(count: Int): ByteArray public suspend fun readBytes(count: Int): ByteArray
public fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE() public suspend fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE()
public fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE() public suspend fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE()
public fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE() public suspend fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE()
public fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE() public suspend fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE()
public fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE() public suspend fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE()
// big endian // big endian
public fun readLongBE(): Long = public suspend fun readLongBE(): Long =
(readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL) (readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL)
public fun readFloatBE(): Float = Float.fromBits(readIntBE()) public suspend fun readFloatBE(): Float = Float.fromBits(readIntBE())
public fun readDoubleBE(): Double = Double.fromBits(readLongBE()) public suspend fun readDoubleBE(): Double = Double.fromBits(readLongBE())
public fun readShortBE(): Short { public suspend fun readShortBE(): Short {
val b1 = readByte().toInt() and 0xFF val b1 = readByte().toInt() and 0xFF
val b2 = readByte().toInt() and 0xFF val b2 = readByte().toInt() and 0xFF
return ((b1 shl 8) or b2).toShort() return ((b1 shl 8) or b2).toShort()
} }
public fun readIntBE(): Int { public suspend fun readIntBE(): Int {
return ((readByte().toInt() and 0xFF) shl 24) or return ((readByte().toInt() and 0xFF) shl 24) or
((readByte().toInt() and 0xFF) shl 16) or ((readByte().toInt() and 0xFF) shl 16) or
((readByte().toInt() and 0xFF) shl 8) or ((readByte().toInt() and 0xFF) shl 8) or
@@ -41,7 +41,7 @@ public interface NBTInput {
} }
// little endian // little endian
public fun readLongLE(): Long { public suspend fun readLongLE(): Long {
return ((readByte().toInt() and 0xFF).toLong() shl 0) or return ((readByte().toInt() and 0xFF).toLong() shl 0) or
((readByte().toInt() and 0xFF).toLong() shl 8) or ((readByte().toInt() and 0xFF).toLong() shl 8) or
((readByte().toInt() and 0xFF).toLong() shl 16) or ((readByte().toInt() and 0xFF).toLong() shl 16) or
@@ -52,14 +52,14 @@ public interface NBTInput {
(readByte().toInt() and 0xFF).toLong() (readByte().toInt() and 0xFF).toLong()
} }
public fun readFloatLE(): Float = Float.fromBits(readIntLE()) public suspend fun readFloatLE(): Float = Float.fromBits(readIntLE())
public fun readDoubleLE(): Double = Double.fromBits(readLongLE()) public suspend fun readDoubleLE(): Double = Double.fromBits(readLongLE())
public fun readShortLE(): Short = public suspend fun readShortLE(): Short =
((readByte().toInt() and 0xFF) or (readByte().toInt() and 0xFF shl 8)).toShort() ((readByte().toInt() and 0xFF) or (readByte().toInt() and 0xFF shl 8)).toShort()
public fun readIntLE(): Int = public suspend fun readIntLE(): Int =
((readByte().toInt() and 0xFF)) or ((readByte().toInt() and 0xFF)) or
((readByte().toInt() and 0xFF) shl 8) or ((readByte().toInt() and 0xFF) shl 8) or
((readByte().toInt() and 0xFF) shl 16) or ((readByte().toInt() and 0xFF) shl 16) or
@@ -7,44 +7,44 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder import cn.rtast.libmc.common.stream.ByteOrder
public interface NBTOutput { public interface NBTOutput {
public val order: ByteOrder public val order: ByteOrder
public val root: NBTTag.CompoundTag public val root: NBTTag.CompoundTag
public fun writeByte(value: Byte) public suspend fun writeByte(value: Byte)
public fun writeBytes(value: ByteArray) public suspend fun writeBytes(value: ByteArray)
public fun writeShort(value: Short): Unit = public suspend fun writeShort(value: Short): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeShortBE(value) else writeShortLE(value) if (order == ByteOrder.BIG_ENDIAN) writeShortBE(value) else writeShortLE(value)
public fun writeInt(value: Int): Unit = public suspend fun writeInt(value: Int): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeIntBE(value) else writeIntLE(value) if (order == ByteOrder.BIG_ENDIAN) writeIntBE(value) else writeIntLE(value)
public fun writeLong(value: Long): Unit = public suspend fun writeLong(value: Long): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeLongBE(value) else writeLongLE(value) if (order == ByteOrder.BIG_ENDIAN) writeLongBE(value) else writeLongLE(value)
public fun writeFloat(value: Float): Unit = public suspend fun writeFloat(value: Float): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeFloatBE(value) else writeFloatLE(value) if (order == ByteOrder.BIG_ENDIAN) writeFloatBE(value) else writeFloatLE(value)
public fun writeDouble(value: Double): Unit = public suspend fun writeDouble(value: Double): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeDoubleBE(value) else writeDoubleLE(value) if (order == ByteOrder.BIG_ENDIAN) writeDoubleBE(value) else writeDoubleLE(value)
// big endian // big endian
public fun writeShortBE(value: Short) { public suspend fun writeShortBE(value: Short) {
writeByte(((value.toInt() shr 8) and 0xFF).toByte()) writeByte(((value.toInt() shr 8) and 0xFF).toByte())
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
} }
public fun writeIntBE(value: Int) { public suspend fun writeIntBE(value: Int) {
writeByte(((value shr 24) and 0xFF).toByte()) writeByte(((value shr 24) and 0xFF).toByte())
writeByte(((value shr 16) and 0xFF).toByte()) writeByte(((value shr 16) and 0xFF).toByte())
writeByte(((value shr 8) and 0xFF).toByte()) writeByte(((value shr 8) and 0xFF).toByte())
writeByte((value and 0xFF).toByte()) writeByte((value and 0xFF).toByte())
} }
public fun writeLongBE(value: Long) { public suspend fun writeLongBE(value: Long) {
writeByte(((value shr 56).toInt() and 0xFF).toByte()) writeByte(((value shr 56).toInt() and 0xFF).toByte())
writeByte(((value shr 48).toInt() and 0xFF).toByte()) writeByte(((value shr 48).toInt() and 0xFF).toByte())
writeByte(((value shr 40).toInt() and 0xFF).toByte()) writeByte(((value shr 40).toInt() and 0xFF).toByte())
@@ -55,28 +55,28 @@ public interface NBTOutput {
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
} }
public fun writeFloatBE(value: Float) { public suspend fun writeFloatBE(value: Float) {
writeIntBE(value.toBits()) writeIntBE(value.toBits())
} }
public fun writeDoubleBE(value: Double) { public suspend fun writeDoubleBE(value: Double) {
writeLongBE(value.toBits()) writeLongBE(value.toBits())
} }
// Little Endian // Little Endian
public fun writeShortLE(value: Short) { public suspend fun writeShortLE(value: Short) {
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
writeByte(((value.toInt() shr 8) and 0xFF).toByte()) writeByte(((value.toInt() shr 8) and 0xFF).toByte())
} }
public fun writeIntLE(value: Int) { public suspend fun writeIntLE(value: Int) {
writeByte((value and 0xFF).toByte()) writeByte((value and 0xFF).toByte())
writeByte(((value shr 8) and 0xFF).toByte()) writeByte(((value shr 8) and 0xFF).toByte())
writeByte(((value shr 16) and 0xFF).toByte()) writeByte(((value shr 16) and 0xFF).toByte())
writeByte(((value shr 24) and 0xFF).toByte()) writeByte(((value shr 24) and 0xFF).toByte())
} }
public fun writeLongLE(value: Long) { public suspend fun writeLongLE(value: Long) {
writeByte(((value and 0xFF).toInt()).toByte()) writeByte(((value and 0xFF).toInt()).toByte())
writeByte((((value shr 8) and 0xFF).toInt()).toByte()) writeByte((((value shr 8) and 0xFF).toInt()).toByte())
writeByte((((value shr 16) and 0xFF).toInt()).toByte()) writeByte((((value shr 16) and 0xFF).toInt()).toByte())
@@ -87,10 +87,7 @@ public interface NBTOutput {
writeByte((((value shr 56) and 0xFF).toInt()).toByte()) writeByte((((value shr 56) and 0xFF).toInt()).toByte())
} }
public fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits()) public suspend fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
public suspend fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
public fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits()) public suspend fun toByteArray(): ByteArray
public fun toByteArray(): ByteArray
} }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
public fun NBTInput.readStringTag(): String { public suspend fun NBTInput.readStringTag(): String {
val length = readShort().toInt() and 0xFFFF val length = readShort().toInt() and 0xFFFF
val bytes = readBytes(length) val bytes = readBytes(length)
return bytes.decodeToString() return bytes.decodeToString()
} }
public fun NBTInput.readListTag(): NBTTag.ListTag { public suspend fun NBTInput.readListTag(): NBTTag.ListTag {
val elementTypeId = readByte().toInt() and 0xFF val elementTypeId = readByte().toInt() and 0xFF
val elementType = NBTType.fromID(elementTypeId) val elementType = NBTType.fromID(elementTypeId)
val length = readInt() val length = readInt()
@@ -22,7 +22,7 @@ public fun NBTInput.readListTag(): NBTTag.ListTag {
return NBTTag.ListTag(elementType, list) return NBTTag.ListTag(elementType, list)
} }
public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag { public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
val map = LinkedHashMap<String, NBTTag>() val map = LinkedHashMap<String, NBTTag>()
while (true) { while (true) {
val typeId = readByte().toInt() and 0xFF val typeId = readByte().toInt() and 0xFF
@@ -35,7 +35,7 @@ public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
return NBTTag.CompoundTag(map) return NBTTag.CompoundTag(map)
} }
public fun NBTInput.readTagPayload(type: NBTType): NBTTag = public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag =
when (type) { when (type) {
NBTType.Byte -> NBTTag.ByteTag(readByte()) NBTType.Byte -> NBTTag.ByteTag(readByte())
NBTType.Short -> NBTTag.ShortTag(readShort()) NBTType.Short -> NBTTag.ShortTag(readShort())
@@ -52,7 +52,7 @@ public fun NBTInput.readTagPayload(type: NBTType): NBTTag =
NBTType.End -> error("TAG_End no payload") NBTType.End -> error("TAG_End no payload")
} }
public fun NBTInput.readCompound(): NBTTag { public suspend fun NBTInput.readCompound(): NBTTag {
val map = LinkedHashMap<String, NBTTag>() val map = LinkedHashMap<String, NBTTag>()
while (true) { while (true) {
val typeId = readByte().toInt() val typeId = readByte().toInt()
@@ -67,7 +67,7 @@ public fun NBTInput.readCompound(): NBTTag {
return NBTTag.ListTag(NBTType.Compound, map.values.toMutableList()) return NBTTag.ListTag(NBTType.Compound, map.values.toMutableList())
} }
public fun NBTInput.readRootCompound(): NBTCompound { public suspend fun NBTInput.readRootCompound(): NBTCompound {
val rootType = NBTType.fromID(readByte().toInt()) val rootType = NBTType.fromID(readByte().toInt())
require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" } require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" }
val nameLen = readShort().toInt() and 0xFFFF val nameLen = readShort().toInt() and 0xFFFF
@@ -76,7 +76,7 @@ public fun NBTInput.readRootCompound(): NBTCompound {
return NBTCompound(name, root) return NBTCompound(name, root)
} }
public fun NBTInput.readNetworkCompound(): NBTCompound { public suspend fun NBTInput.readNetworkCompound(): NBTCompound {
return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) { return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) {
NBTType.Compound -> NBTCompound("", readCompoundTag()) NBTType.Compound -> NBTCompound("", readCompoundTag())
NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag())))) NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag()))))
@@ -8,13 +8,13 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
public fun NBTOutput.writeStringTag(value: String) { public suspend fun NBTOutput.writeStringTag(value: String) {
val bytes = value.encodeToByteArray() val bytes = value.encodeToByteArray()
writeShort(bytes.size.toShort()) writeShort(bytes.size.toShort())
writeBytes(bytes) writeBytes(bytes)
} }
public fun NBTOutput.writeTagPayload(tag: NBTTag) { public suspend fun NBTOutput.writeTagPayload(tag: NBTTag) {
when (tag) { when (tag) {
is NBTTag.ByteTag -> writeByte(tag.value) is NBTTag.ByteTag -> writeByte(tag.value)
is NBTTag.ShortTag -> writeShort(tag.value) is NBTTag.ShortTag -> writeShort(tag.value)
@@ -56,14 +56,14 @@ public fun NBTOutput.writeTagPayload(tag: NBTTag) {
} }
} }
public fun NBTOutput.writeRootNBTCompound(name: String = ""): ByteArray { public suspend fun NBTOutput.writeRootNBTCompound(name: String = ""): ByteArray {
writeByte(NBTType.Compound.id) writeByte(NBTType.Compound.id)
writeStringTag(name) writeStringTag(name)
writeTagPayload(root) writeTagPayload(root)
return toByteArray() return toByteArray()
} }
public fun NBTOutput.writeNetworkCompound(compound: NBTCompound) { public suspend fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
val compoundTag = compound.element as? NBTTag.CompoundTag val compoundTag = compound.element as? NBTTag.CompoundTag
?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag") ?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag")
writeByte(NBTType.Compound.id) writeByte(NBTType.Compound.id)
@@ -7,7 +7,7 @@
package test package test
import cn.rtast.libmc.common.wrap import cn.rtast.libmc.common.stream.wrap
import cn.rtast.libmc.nbt.NbtReader import cn.rtast.libmc.nbt.NbtReader
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
+21
View File
@@ -0,0 +1,21 @@
# libmc-protocol-encrypt
This module implemented `ProtocolCryptoContext` and provided a `DefaultProtocolCryptoContext`.
## Get started
> `Sha1`, `RSA1024`, `AES-128-CFB8` from `cryptography-kotlin`(and its based provider).
> `HTTP Client` from `ktor-client`
> Before start, you need to add a `ktor client engine` for your platform. For JVM, `ktor-client-okhttp`(JVM 1.8+)
> or `ktor-client-java`(JVM 11+), for Linux, use `ktor-client-curl`, for Windows, use `ktor-client-winhttp`,
> for Apple, use `ktor-client-darwin`
```kotlin
fun main() {
val cli = createMinecraftClient(
// ... other paramater
crypto = DefaultProtocolCryptoContext
)
}
```
+43
View File
@@ -0,0 +1,43 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
kotlin {
explicitApi()
withSourcesJar()
linuxX64()
linuxArm64()
macosArm64()
mingwX64()
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
sourceSets {
commonMain.dependencies {
api(project(":common"))
implementation(libs.cryptography.core)
implementation(libs.cryptography.provider.optimal)
implementation(libs.ktor.client.core)
}
jvmTest.dependencies {
implementation(libs.ktor.client.okhttp)
}
linuxTest.dependencies {
implementation(libs.ktor.client.curl)
}
mingwTest.dependencies {
implementation(libs.ktor.client.winhttp)
}
appleTest.dependencies {
implementation(libs.ktor.client.darwin)
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(project(":protocol"))
implementation(libs.kotlinx.coroutines.test)
}
}
}
@@ -0,0 +1,48 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
@file:OptIn(DelicateCryptographyApi::class)
package cn.rtast.libmc.protocol.crypto
import cn.rtast.libmc.common.crypto.NetworkCipher
import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.AES
public class AesCFB8Cipher(sharedKey: ByteArray) : NetworkCipher {
private val encryptIv = sharedKey.copyOf()
private val decryptIv = sharedKey.copyOf()
private val cipher = provider.get(AES.CFB8)
.keyDecoder()
.decodeFromByteArrayBlocking(AES.Key.Format.RAW, sharedKey)
.cipher()
override suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int) {
val plaintext = buffer.copyOfRange(offset, offset + length)
val ciphertext = cipher.encryptWithIvBlocking(encryptIv, plaintext)
ciphertext.copyInto(buffer, destinationOffset = offset)
updateIv(encryptIv, ciphertext)
}
override suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int) {
val ciphertext = buffer.copyOfRange(offset, offset + length)
val plaintext = cipher.decryptWithIvBlocking(decryptIv, ciphertext)
plaintext.copyInto(buffer, destinationOffset = offset)
updateIv(decryptIv, ciphertext)
}
private fun updateIv(iv: ByteArray, ciphertext: ByteArray) {
val len = ciphertext.size
if (len >= iv.size) {
ciphertext.copyInto(iv, destinationOffset = 0, startIndex = len - iv.size, endIndex = len)
} else {
iv.copyInto(iv, destinationOffset = 0, startIndex = len, endIndex = iv.size)
ciphertext.copyInto(iv, destinationOffset = iv.size - len)
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.protocol.crypto
import cn.rtast.libmc.common.crypto.AuthenticationProvider
import cn.rtast.libmc.common.crypto.ProtocolContextBuilder
import cn.rtast.libmc.common.crypto.RSA1024Encryptor
import cn.rtast.libmc.common.crypto.Sha1Hasher
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.http.*
private val httpClient = HttpClient()
public val DefaultProtocolCryptoContext: ProtocolContextBuilder.() -> Unit = {
rsaEncryptor = RSA1024Encryptor { key, data -> rsaEncrypt(key, data) }
sha1Hasher = Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) }
cipherFactory = { key -> AesCFB8Cipher(key) }
authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
val status = httpClient.post(url) {
headers { header("Content-Type", "application/json") }
setBody("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}")
}.status
require(status == HttpStatusCode.NoContent)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
@file:OptIn(DelicateCryptographyApi::class)
package cn.rtast.libmc.protocol.crypto
import dev.whyoleg.cryptography.CryptographyProvider
import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.RSA
import dev.whyoleg.cryptography.algorithms.SHA1
internal val provider = CryptographyProvider.Default
public suspend fun rsaEncrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray {
val provider = CryptographyProvider.Default
val rsa = provider.get(RSA.PKCS1)
val publicKey = rsa.publicKeyDecoder(SHA1).decodeFromByteArray(RSA.PublicKey.Format.DER, publicKeyBytes)
return publicKey.encryptor().encrypt(data)
}
@@ -0,0 +1,41 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
@file:OptIn(DelicateCryptographyApi::class)
package cn.rtast.libmc.protocol.crypto
import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.SHA1
public suspend fun minecraftServerIdHash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String {
val serverIdBytes = serverId.encodeToByteArray()
for (b in serverIdBytes) require((b.toInt() and 0xFF) <= 0x7F) { "serverId contains non-US-ASCII character" }
val data = serverIdBytes + secretKey + publicKey
val hash = provider.get(SHA1).hasher().hash(data)
return mcDigestToString(hash)
}
private fun mcDigestToString(digest: ByteArray): String {
val isNegative = (digest[0].toInt() and 0x80) != 0
val bytes = if (isNegative) twosComplement(digest) else digest
var hex = bytes.joinToString("") { (it.toInt() and 0xFF).toString(16).padStart(2, '0') }
hex = hex.trimStart('0')
if (hex.isEmpty()) hex = "0"
return if (isNegative) "-$hex" else hex
}
private fun twosComplement(bytes: ByteArray): ByteArray {
val result = ByteArray(bytes.size)
var carry = 1
for (i in bytes.size - 1 downTo 0) {
val inverted = (bytes[i].toInt().inv() and 0xFF) + carry
result[i] = (inverted and 0xFF).toByte()
carry = inverted ushr 8
}
return result
}
@@ -0,0 +1,41 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package test
import cn.rtast.libmc.protocol.client.createMinecraftClient
import cn.rtast.libmc.protocol.crypto.DefaultProtocolCryptoContext
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundLoginSuccessPacket
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSystemChatMessagePacket
import kotlinx.coroutines.launch
import org.junit.Test
import java.io.File
import kotlin.uuid.Uuid
class TestJvmClient {
val accessToken = File("src/commonTest/resources/accessToken.txt").readText()
@Test
fun `test default protocol context`() {
val cli = createMinecraftClient(
"127.0.0.1",
25565,
"RTAkland",
// generateOfflineUuid("RTAkland"),
Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
// null,
accessToken,
crypto = DefaultProtocolCryptoContext
)
cli.on<ClientboundSystemChatMessagePacket> { println(it) }
cli.on<ClientboundLoginSuccessPacket> { println(it) }
cli.launch { cli.connect() }
while (true) {
}
}
}
+1 -3
View File
@@ -14,7 +14,6 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
api(project(":common")) api(project(":common"))
api(project(":nbt")) api(project(":nbt"))
api(libs.kotlinx.coroutines)
} }
jvmMain.dependencies { jvmMain.dependencies {
@@ -27,8 +26,7 @@ kotlin {
} }
jvmTest.dependencies { jvmTest.dependencies {
implementation(libs.ktor.client.core) implementation(project(":protocol-encrypt"))
implementation(libs.ktor.client.okhttp)
} }
} }
} }
@@ -7,8 +7,8 @@
package cn.rtast.libmc.protocol.client package cn.rtast.libmc.protocol.client
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.protocol.crypto.ProtocolContext import cn.rtast.libmc.common.crypto.ProtocolContext
import cn.rtast.libmc.protocol.crypto.ProtocolContextBuilder import cn.rtast.libmc.common.crypto.ProtocolContextBuilder
import cn.rtast.libmc.protocol.event.InternalPacketDispatcher import cn.rtast.libmc.protocol.event.InternalPacketDispatcher
import cn.rtast.libmc.protocol.event.PacketEventDispatcher import cn.rtast.libmc.protocol.event.PacketEventDispatcher
import cn.rtast.libmc.protocol.network.NetworkChannel import cn.rtast.libmc.protocol.network.NetworkChannel
@@ -56,7 +56,7 @@ public class MinecraftClient internal constructor(
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = clientJob + ioDispatcher + CoroutineName("LibMC-MinecraftClient-$username") get() = clientJob + ioDispatcher + CoroutineName("LibMC-MinecraftClient-$username")
public fun connect(protocolVersion: Int = 776) { public suspend fun connect(protocolVersion: Int = 776) {
networkChannel.connect() networkChannel.connect()
startListening() startListening()
networkChannel.sendPacket( networkChannel.sendPacket(
@@ -1,13 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.protocol.crypto
public interface NetworkCipher {
public fun encrypt(buffer: ByteArray, offset: Int, length: Int)
public fun decrypt(buffer: ByteArray, offset: Int, length: Int)
}
@@ -19,7 +19,7 @@ import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundConfigurationA
import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundKeepAlivePlayPacket import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundKeepAlivePlayPacket
import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundPongPlayPacket import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundPongPlayPacket
import cn.rtast.libmc.protocol.protocol.state.ProtocolState import cn.rtast.libmc.protocol.protocol.state.ProtocolState
import cn.rtast.libmc.protocol.session.AuthenticationProvider import cn.rtast.libmc.common.crypto.AuthenticationProvider
import cn.rtast.libmc.protocol.util.generateRandom16Bytes import cn.rtast.libmc.protocol.util.generateRandom16Bytes
public class InternalPacketDispatcher( public class InternalPacketDispatcher(
@@ -31,19 +31,8 @@ public class InternalPacketDispatcher(
public suspend fun handleIncomingPackets(packet: MinecraftPacket) { public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
this.dispatchEvent(packet) this.dispatchEvent(packet)
when (packet) { when (packet) {
is ClientboundLoginPacket -> this.handleLoginPackets(packet) // login
is ClientboundConfigurationPacket -> this.handleConfigurationPackets(packet) is ClientboundDisconnectLoginPacket -> client.close()
is ClientboundPlayPacket -> this.handlePlayPackets(packet)
}
}
private suspend fun handleLoginPackets(packet: ClientboundLoginPacket) {
when (packet) {
is ClientboundDisconnectLoginPacket -> {
println("Login denied: ${packet.reason}")
client.close()
}
is ClientboundSetCompressionPacket -> client.networkChannel.setCompression(packet.threshold) is ClientboundSetCompressionPacket -> client.networkChannel.setCompression(packet.threshold)
is ClientboundLoginSuccessPacket -> { is ClientboundLoginSuccessPacket -> {
client.networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket) client.networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket)
@@ -67,13 +56,7 @@ public class InternalPacketDispatcher(
client.session.enableEncryption(sharedSecret) client.session.enableEncryption(sharedSecret)
} }
else -> {} // configuration
}
}
private fun handleConfigurationPackets(packet: ClientboundConfigurationPacket) {
when (packet) {
is ClientboundDisconnectConfigurationPacket -> println("Configuration disconnected: ${packet.reason}")
ClientboundFinishConfigurationPacket -> { ClientboundFinishConfigurationPacket -> {
client.networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket) client.networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket)
client.stateMachine.transitionTo(ProtocolState.PLAY) client.stateMachine.transitionTo(ProtocolState.PLAY)
@@ -91,24 +74,16 @@ public class InternalPacketDispatcher(
ServerboundSelectKnownPacksPacket(emptyList()) ServerboundSelectKnownPacksPacket(emptyList())
) // TODO empty resource packs list ) // TODO empty resource packs list
is ClientboundCodeOfConductPacket -> client.networkChannel.sendPacket(ServerboundAcceptCodeOfConductPacket) is ClientboundCodeOfConductPacket -> client.networkChannel.sendPacket(ServerboundAcceptCodeOfConductPacket)
else -> {}
}
}
private fun handlePlayPackets(packet: ClientboundPlayPacket) { // play
when (packet) {
is ClientboundDisconnectPlayPacket -> client.close() is ClientboundDisconnectPlayPacket -> client.close()
is ClientboundKeepAlivePlayPacket -> client.networkChannel.sendPacket(ServerboundKeepAlivePlayPacket(id = packet.id)) is ClientboundKeepAlivePlayPacket -> client.networkChannel.sendPacket(ServerboundKeepAlivePlayPacket(id = packet.id))
is ClientboundLoginPlayPacket -> println("Successfully joined world Entity ID: ${packet.entityId}")
is ClientboundPingPacket -> client.networkChannel.sendPacket(ServerboundPongPlayPacket(packet.id)) is ClientboundPingPacket -> client.networkChannel.sendPacket(ServerboundPongPlayPacket(packet.id))
is ClientboundPlayerChatMessagePacket -> println("[Player Chat Message] ${packet.message}")
ClientboundStartConfigurationPacket -> { ClientboundStartConfigurationPacket -> {
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket) client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION) client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
} }
else -> {}
is ClientboundSystemChatMessagePacket -> println("[System message] ${packet.content}")
else -> println(packet)
} }
} }
} }
@@ -7,27 +7,27 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.common.ReadChannel import cn.rtast.libmc.common.stream.ReadChannel
import cn.rtast.libmc.common.WriteChannel import cn.rtast.libmc.common.stream.WriteChannel
import cn.rtast.libmc.protocol.crypto.NetworkCipher import cn.rtast.libmc.common.crypto.NetworkCipher
internal class CipherReadChannel( internal class CipherReadChannel(
private val delegate: ReadChannel, private val delegate: ReadChannel,
private val crypto: NetworkCipher private val crypto: NetworkCipher
) : ReadChannel() { ) : ReadChannel() {
override fun readFully(out: ByteArray, start: Int, end: Int) { override suspend fun readFully(out: ByteArray, start: Int, end: Int) {
delegate.readFully(out, start, end) delegate.readFully(out, start, end)
val length = end - start val length = end - start
if (length > 0) crypto.decrypt(out, start, length) if (length > 0) crypto.decrypt(out, start, length)
} }
override fun readByte(): Byte { override suspend fun readByte(): Byte {
val buf = ByteArray(1) val buf = ByteArray(1)
readFully(buf, 0, 1) readFully(buf, 0, 1)
return buf[0] return buf[0]
} }
override fun readBytes(length: Int): ByteArray { override suspend fun readBytes(length: Int): ByteArray {
val bytes = ByteArray(length) val bytes = ByteArray(length)
readFully(bytes, 0, length) readFully(bytes, 0, length)
return bytes return bytes
@@ -38,14 +38,14 @@ internal class CipherWriteChannel(
private val delegate: WriteChannel, private val delegate: WriteChannel,
private val crypto: NetworkCipher private val crypto: NetworkCipher
) : WriteChannel() { ) : WriteChannel() {
override fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) { override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
val length = endIndex - startIndex val length = endIndex - startIndex
if (length <= 0) return if (length <= 0) return
val encrypted = value.copyOfRange(startIndex, endIndex) val encrypted = value.copyOfRange(startIndex, endIndex)
crypto.encrypt(encrypted, 0, length) crypto.encrypt(encrypted, 0, length)
delegate.writeFully(encrypted, 0, length) delegate.writeFully(encrypted, 0, length)
} }
override fun flush() { override suspend fun flush() {
delegate.flush() delegate.flush()
} }
} }
@@ -7,9 +7,14 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.common.* import cn.rtast.libmc.common.*
import cn.rtast.libmc.common.crypto.NetworkCipher
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.writeBuffer
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.stream.wrap
import cn.rtast.libmc.protocol.client.ClientStateMachine import cn.rtast.libmc.protocol.client.ClientStateMachine
import cn.rtast.libmc.protocol.crypto.NetworkCipher
import cn.rtast.libmc.protocol.protocol.GameProtocols import cn.rtast.libmc.protocol.protocol.GameProtocols
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
@@ -33,7 +38,7 @@ internal class NetworkChannel(
this.threshold = threshold this.threshold = threshold
} }
fun readNextPacket(): MinecraftPacket { suspend fun readNextPacket(): MinecraftPacket {
val packetLength = session.readVarInt() val packetLength = session.readVarInt()
val rawFrameBytes = session.readBytes(packetLength) val rawFrameBytes = session.readBytes(packetLength)
val frameBuf = rawFrameBytes.wrap() val frameBuf = rawFrameBytes.wrap()
@@ -49,7 +54,7 @@ internal class NetworkChannel(
.decodePacket(packetId, payloadBuf) .decodePacket(packetId, payloadBuf)
} }
fun sendPacket(packet: MinecraftPacket) { suspend fun sendPacket(packet: MinecraftPacket) {
val uncompressedBodyBuf = BytesBuffer() val uncompressedBodyBuf = BytesBuffer()
GameProtocols.serverboundGameProtocols GameProtocols.serverboundGameProtocols
.getRegistry(stateMachine.currentState) .getRegistry(stateMachine.currentState)
@@ -7,10 +7,10 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.common.ReadChannel import cn.rtast.libmc.common.stream.ReadChannel
import cn.rtast.libmc.common.Socket import cn.rtast.libmc.common.stream.Socket
import cn.rtast.libmc.common.WriteChannel import cn.rtast.libmc.common.stream.WriteChannel
import cn.rtast.libmc.protocol.crypto.NetworkCipher import cn.rtast.libmc.common.crypto.NetworkCipher
internal class NetworkSession( internal class NetworkSession(
private val host: String, private val host: String,
@@ -41,17 +41,17 @@ internal class NetworkSession(
this.writeChannel = CipherWriteChannel(currentWrite, cipher) this.writeChannel = CipherWriteChannel(currentWrite, cipher)
} }
fun readByte(): Byte { suspend fun readByte(): Byte {
val channel = requireNotNull(readChannel) { "ReadChannel not connected" } val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readByte() return channel.readByte()
} }
fun readBytes(length: Int): ByteArray { suspend fun readBytes(length: Int): ByteArray {
val channel = requireNotNull(readChannel) { "ReadChannel not connected" } val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readBytes(length) return channel.readBytes(length)
} }
fun readVarInt(): Int { suspend fun readVarInt(): Int {
var numRead = 0 var numRead = 0
var result = 0 var result = 0
var read: Byte var read: Byte
@@ -65,7 +65,7 @@ internal class NetworkSession(
return result return result
} }
fun writeFully(data: ByteArray) { suspend fun writeFully(data: ByteArray) {
val channel = requireNotNull(writeChannel) { "WriteChannel not connected" } val channel = requireNotNull(writeChannel) { "WriteChannel not connected" }
channel.writeFully(data, 0, data.size) channel.writeFully(data, 0, data.size)
channel.flush() channel.flush()
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.configuration package cn.rtast.libmc.protocol.packet.configuration
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.writeMcString import cn.rtast.libmc.common.primitives.writeMcString
public data class KnownPacks(val namespace: String, val id: String, val version: String) { public data class KnownPacks(val namespace: String, val id: String, val version: String) {
internal companion object Codec : PacketCodec<KnownPacks> { internal companion object Codec : PacketCodec<KnownPacks> {
override fun encode(buffer: BytesBuffer, value: KnownPacks) { override suspend fun encode(buffer: BytesBuffer, value: KnownPacks) {
buffer.writeMcString(value.namespace) buffer.writeMcString(value.namespace)
buffer.writeMcString(value.id) buffer.writeMcString(value.id)
buffer.writeMcString(value.version) buffer.writeMcString(value.version)
} }
override fun decode(buffer: BytesBuffer): KnownPacks { override suspend fun decode(buffer: BytesBuffer): KnownPacks {
val namespace = buffer.readMcString() val namespace = buffer.readMcString()
val id = buffer.readMcString() val id = buffer.readMcString()
val version = buffer.readMcString() val version = buffer.readMcString()
@@ -7,7 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.* import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.primitives.readUuid
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.nbt.NBTCompound import cn.rtast.libmc.nbt.NBTCompound
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
@@ -18,10 +23,10 @@ public data class ClientboundAddResourcePackPacket(
val hash: String, val hash: String,
val forced: Boolean, val forced: Boolean,
val prompt: NBTCompound, val prompt: NBTCompound,
) : ClientboundConfigurationPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> { internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
val uuid = buffer.readUuid() val uuid = buffer.readUuid()
val url = buffer.readMcString() val url = buffer.readMcString()
val hash = buffer.readMcString() val hash = buffer.readMcString()
@@ -7,11 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data object ClientboundClearDialogPacket : ClientboundConfigurationPacket, public data object ClientboundClearDialogPacket : MinecraftPacket,
PacketCodec<ClientboundClearDialogPacket> { PacketCodec<ClientboundClearDialogPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket override suspend fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
} }
@@ -7,14 +7,15 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readMcString
public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : ClientboundConfigurationPacket { public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> { internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
return ClientboundCodeOfConductPacket(buffer.readMcString()) return ClientboundCodeOfConductPacket(buffer.readMcString())
} }
} }
@@ -1,12 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/5
*/
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.packet.MinecraftPacket
public sealed interface ClientboundConfigurationPacket : MinecraftPacket
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.nbt.NBTCompound import cn.rtast.libmc.nbt.NBTCompound
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
public data class ClientboundConfigurationShowDialogPacket(val dialog: NBTCompound) : ClientboundConfigurationPacket { public data class ClientboundConfigurationShowDialogPacket(val dialog: NBTCompound) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundConfigurationShowDialogPacket> { internal companion object Codec : PacketCodec<ClientboundConfigurationShowDialogPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound()) return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound())
} }
} }
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
public data class ClientboundCookieRequestPacket(val key: Identifier) : ClientboundConfigurationPacket { public data class ClientboundCookieRequestPacket(val key: Identifier) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCookieRequestPacket> { internal companion object Codec : PacketCodec<ClientboundCookieRequestPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
return ClientboundCookieRequestPacket(key = buffer.readIdentifier()) return ClientboundCookieRequestPacket(key = buffer.readIdentifier())
} }
} }
@@ -7,9 +7,9 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -18,8 +18,8 @@ public data class ClientboundCustomPayloadPacket(
val data: ByteArray, val data: ByteArray,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> { internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
val channel = buffer.readIdentifier() val channel = buffer.readIdentifier()
val data = buffer.readRemainingBytes() val data = buffer.readRemainingBytes()
return ClientboundCustomPayloadPacket(channel, data) return ClientboundCustomPayloadPacket(channel, data)
@@ -7,18 +7,22 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.* import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.common.primitives.writeMcString
public data class ClientboundCustomReportDetailsPacket(val details: List<ReportDetail>) : public data class ClientboundCustomReportDetailsPacket(val details: List<ReportDetail>) : MinecraftPacket {
ClientboundConfigurationPacket {
public data class ReportDetail(val title: String, val description: String) { public data class ReportDetail(val title: String, val description: String) {
internal companion object Codec : PacketCodec<ReportDetail> { internal companion object Codec : PacketCodec<ReportDetail> {
override fun encode(buffer: BytesBuffer, value: ReportDetail) { override suspend fun encode(buffer: BytesBuffer, value: ReportDetail) {
buffer.writeMcString(value.title) buffer.writeMcString(value.title)
buffer.writeMcString(value.description) buffer.writeMcString(value.description)
} }
override fun decode(buffer: BytesBuffer): ReportDetail { override suspend fun decode(buffer: BytesBuffer): ReportDetail {
val title = buffer.readMcString() val title = buffer.readMcString()
val description = buffer.readMcString() val description = buffer.readMcString()
return ReportDetail(title, description) return ReportDetail(title, description)
@@ -27,8 +31,8 @@ public data class ClientboundCustomReportDetailsPacket(val details: List<ReportD
} }
internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> { internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
val detailCount = buffer.readVarInt() val detailCount = buffer.readVarInt()
val details = ArrayList<ReportDetail>(detailCount) val details = ArrayList<ReportDetail>(detailCount)
repeat(detailCount) { details.add(ReportDetail.decode(buffer)) } repeat(detailCount) { details.add(ReportDetail.decode(buffer)) }
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.nbt.NBTCompound import cn.rtast.libmc.nbt.NBTCompound
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
public data class ClientboundDisconnectConfigurationPacket(val reason: NBTCompound) : ClientboundConfigurationPacket { public data class ClientboundDisconnectConfigurationPacket(val reason: NBTCompound) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
return ClientboundDisconnectConfigurationPacket(reason = buffer.readNetworkNBTCompound()) return ClientboundDisconnectConfigurationPacket(reason = buffer.readNetworkNBTCompound())
} }
} }
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data object ClientboundFinishConfigurationPacket : ClientboundConfigurationPacket, public data object ClientboundFinishConfigurationPacket : MinecraftPacket,
PacketCodec<ClientboundFinishConfigurationPacket> { PacketCodec<ClientboundFinishConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
return ClientboundFinishConfigurationPacket return ClientboundFinishConfigurationPacket
} }
} }
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : ClientboundConfigurationPacket { public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundKeepAliveConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundKeepAliveConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
return ClientboundKeepAliveConfigurationPacket(buffer.readLong()) return ClientboundKeepAliveConfigurationPacket(buffer.readLong())
} }
} }
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data class ClientboundPingConfigurationPacket(val id: Int) : ClientboundConfigurationPacket { public data class ClientboundPingConfigurationPacket(val id: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundPingConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundPingConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
return ClientboundPingConfigurationPacket(id = buffer.readInt()) return ClientboundPingConfigurationPacket(id = buffer.readInt())
} }
} }
@@ -7,9 +7,10 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.registry.RegistryEntry import cn.rtast.libmc.protocol.protocol.game.registry.RegistryEntry
@@ -20,10 +21,10 @@ public data class ClientboundRegistryDataPacket(
*/ */
val registryId: Identifier, val registryId: Identifier,
val entries: List<RegistryEntry>, val entries: List<RegistryEntry>,
) : ClientboundConfigurationPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> { internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
val id = buffer.readIdentifier() val id = buffer.readIdentifier()
val entryCount = buffer.readVarInt() val entryCount = buffer.readVarInt()
val entries = ArrayList<RegistryEntry>(entryCount) val entries = ArrayList<RegistryEntry>(entryCount)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readUuid import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readUuid
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
public data class ClientboundRemoveResourcePackPacket(val uuid: Uuid) : ClientboundConfigurationPacket { public data class ClientboundRemoveResourcePackPacket(val uuid: Uuid) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundRemoveResourcePackPacket> { internal companion object Codec : PacketCodec<ClientboundRemoveResourcePackPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
val uuid = buffer.readUuid() val uuid = buffer.readUuid()
return ClientboundRemoveResourcePackPacket(uuid) return ClientboundRemoveResourcePackPacket(uuid)
} }
@@ -7,11 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data object ClientboundResetChatPacket : ClientboundConfigurationPacket, public data object ClientboundResetChatPacket : MinecraftPacket,
PacketCodec<ClientboundResetChatPacket> { PacketCodec<ClientboundResetChatPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket override suspend fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
} }
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.protocol.packet.configuration.KnownPacks import cn.rtast.libmc.protocol.packet.configuration.KnownPacks
public data class ClientboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : ClientboundConfigurationPacket { public data class ClientboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundSelectKnownPacksPacket> { internal companion object Codec : PacketCodec<ClientboundSelectKnownPacksPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
val packsCount = buffer.readVarInt() val packsCount = buffer.readVarInt()
val packs = List(packsCount) { KnownPacks.decode(buffer) } val packs = List(packsCount) { KnownPacks.decode(buffer) }
return ClientboundSelectKnownPacksPacket(packs) return ClientboundSelectKnownPacksPacket(packs)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.protocol.registry.report.ServerLink import cn.rtast.libmc.protocol.registry.report.ServerLink
public data class ClientboundServerLinksPacket(val links: List<ServerLink>) : ClientboundConfigurationPacket { public data class ClientboundServerLinksPacket(val links: List<ServerLink>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> { internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
val count = buffer.readVarInt() val count = buffer.readVarInt()
val links = ArrayList<ServerLink>(count) val links = ArrayList<ServerLink>(count)
repeat(count) { links.add(ServerLink.decode(buffer)) } repeat(count) { links.add(ServerLink.decode(buffer)) }
@@ -7,9 +7,10 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readPrefixedByteArray import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readPrefixedByteArray
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -19,10 +20,10 @@ public data class ClientboundStoreCookiePacket(
* cookie * cookie
*/ */
val payload: ByteArray, val payload: ByteArray,
) : ClientboundConfigurationPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> { internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
override fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket { override suspend fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
val key = buffer.readIdentifier() val key = buffer.readIdentifier()
val payload = buffer.readPrefixedByteArray() val payload = buffer.readPrefixedByteArray()
return ClientboundStoreCookiePacket(key, payload) return ClientboundStoreCookiePacket(key, payload)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.primitives.readVarInt
public data class ClientboundTransferPacket(val host: String, val port: Int) : ClientboundConfigurationPacket { public data class ClientboundTransferPacket(val host: String, val port: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundTransferPacket> { internal companion object Codec : PacketCodec<ClientboundTransferPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundTransferPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
val host = buffer.readMcString() val host = buffer.readMcString()
val port = buffer.readVarInt() val port = buffer.readVarInt()
return ClientboundTransferPacket(host, port) return ClientboundTransferPacket(host, port)
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) : public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) : MinecraftPacket {
ClientboundConfigurationPacket {
internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> { internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
val featureCount = buffer.readVarInt() val featureCount = buffer.readVarInt()
val features = ArrayList<Identifier>(featureCount) val features = ArrayList<Identifier>(featureCount)
repeat(featureCount) { features.add(buffer.readIdentifier()) } repeat(featureCount) { features.add(buffer.readIdentifier()) }
@@ -7,24 +7,25 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistry>) : ClientboundConfigurationPacket { public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistry>) : MinecraftPacket {
public data class TaggedRegistry(val registryId: Identifier, val tags: List<Tag>) { public data class TaggedRegistry(val registryId: Identifier, val tags: List<Tag>) {
internal companion object Codec : PacketCodec<TaggedRegistry> { internal companion object Codec : PacketCodec<TaggedRegistry> {
override fun encode(buffer: BytesBuffer, value: TaggedRegistry) { override suspend fun encode(buffer: BytesBuffer, value: TaggedRegistry) {
buffer.writeIdentifier(value.registryId) buffer.writeIdentifier(value.registryId)
buffer.writeVarInt(value.tags.size) buffer.writeVarInt(value.tags.size)
value.tags.forEach { Tag.encode(buffer, it) } value.tags.forEach { Tag.encode(buffer, it) }
} }
override fun decode(buffer: BytesBuffer): TaggedRegistry { override suspend fun decode(buffer: BytesBuffer): TaggedRegistry {
val id = buffer.readIdentifier() val id = buffer.readIdentifier()
val tagCount = buffer.readVarInt() val tagCount = buffer.readVarInt()
val tags = ArrayList<Tag>(tagCount) val tags = ArrayList<Tag>(tagCount)
@@ -36,13 +37,13 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
public data class Tag(val name: Identifier, val entries: IntArray) { public data class Tag(val name: Identifier, val entries: IntArray) {
internal companion object Codec : PacketCodec<Tag> { internal companion object Codec : PacketCodec<Tag> {
override fun encode(buffer: BytesBuffer, value: Tag) { override suspend fun encode(buffer: BytesBuffer, value: Tag) {
buffer.writeIdentifier(value.name) buffer.writeIdentifier(value.name)
buffer.writeVarInt(value.entries.size) buffer.writeVarInt(value.entries.size)
value.entries.forEach { buffer.writeVarInt(it) } value.entries.forEach { buffer.writeVarInt(it) }
} }
override fun decode(buffer: BytesBuffer): Tag { override suspend fun decode(buffer: BytesBuffer): Tag {
val name = buffer.readIdentifier() val name = buffer.readIdentifier()
val entrySize = buffer.readVarInt() val entrySize = buffer.readVarInt()
val entries = ArrayList<Int>(entrySize) val entries = ArrayList<Int>(entrySize)
@@ -68,8 +69,8 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
} }
internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> { internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
val registryCount = buffer.readVarInt() val registryCount = buffer.readVarInt()
val registries = ArrayList<TaggedRegistry>(registryCount) val registries = ArrayList<TaggedRegistry>(registryCount)
repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) } repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket, public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket,
PacketCodec<ServerboundAcceptCodeOfConductPacket> { PacketCodec<ServerboundAcceptCodeOfConductPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
ServerboundAcceptCodeOfConductPacket ServerboundAcceptCodeOfConductPacket
} }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket, public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket,
PacketCodec<ServerboundAckFinishConfigurationPacket> { PacketCodec<ServerboundAckFinishConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,16 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket { public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundCookieResponsePacket> { internal companion object Codec : PacketCodec<ServerboundCookieResponsePacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundCookieResponsePacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundCookieResponsePacket) {
buffer.writeIdentifier(value.key) buffer.writeIdentifier(value.key)
if (value.payload != null) { if (value.payload != null) {
buffer.writeBoolean(true) buffer.writeBoolean(true)
@@ -25,7 +25,7 @@ public data class ServerboundCookieResponsePacket(val key: Identifier, val paylo
} else buffer.writeBoolean(false) } else buffer.writeBoolean(false)
} }
override fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket = override suspend fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket { public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundKeepAliveConfigurationPacket> { internal companion object Codec : PacketCodec<ServerboundKeepAliveConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundKeepAliveConfigurationPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundKeepAliveConfigurationPacket) {
buffer.writeLong(value.id) buffer.writeLong(value.id)
} }
override fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
} }
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
public data class ServerboundPongConfigurationPacket(val id: Int) : MinecraftPacket { public data class ServerboundPongConfigurationPacket(val id: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundPongConfigurationPacket> { internal companion object Codec : PacketCodec<ServerboundPongConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundPongConfigurationPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundPongConfigurationPacket) {
buffer.writeInt(value.id) buffer.writeInt(value.id)
} }
override fun decode(buffer: BytesBuffer): ServerboundPongConfigurationPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundPongConfigurationPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
} }
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.packet.configuration.KnownPacks import cn.rtast.libmc.protocol.packet.configuration.KnownPacks
public data class ServerboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket { public data class ServerboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundSelectKnownPacksPacket> { internal companion object Codec : PacketCodec<ServerboundSelectKnownPacksPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundSelectKnownPacksPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundSelectKnownPacksPacket) {
buffer.writeVarInt(value.knownPacks.size) buffer.writeVarInt(value.knownPacks.size)
value.knownPacks.forEach { KnownPacks.encode(buffer, it) } value.knownPacks.forEach { KnownPacks.encode(buffer, it) }
} }
override fun decode(buffer: BytesBuffer): ServerboundSelectKnownPacksPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundSelectKnownPacksPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
} }
@@ -7,11 +7,11 @@
package cn.rtast.libmc.protocol.packet.handshake package cn.rtast.libmc.protocol.packet.handshake
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeMcString import cn.rtast.libmc.common.primitives.writeMcString
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent
public data class ServerboundHandshakePacket( public data class ServerboundHandshakePacket(
@@ -21,13 +21,13 @@ public data class ServerboundHandshakePacket(
val intent: HandshakeIntent, val intent: HandshakeIntent,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundHandshakePacket> { internal companion object Codec : PacketCodec<ServerboundHandshakePacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundHandshakePacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundHandshakePacket) {
buffer.writeVarInt(value.protocolVersion) buffer.writeVarInt(value.protocolVersion)
buffer.writeMcString(value.serverAddress) buffer.writeMcString(value.serverAddress)
buffer.writeShort(value.serverPort.toShort()) buffer.writeShort(value.serverPort.toShort())
buffer.writeVarInt(value.intent.intentID) buffer.writeVarInt(value.intent.intentID)
} }
override fun decode(buffer: BytesBuffer): ServerboundHandshakePacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): ServerboundHandshakePacket = throw UnsupportedOperationException()
} }
} }
@@ -7,9 +7,10 @@
package cn.rtast.libmc.protocol.packet.login.clientbound package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -17,10 +18,10 @@ public data class ClientboundCustomQueryPacket(
val messageId: Int, val messageId: Int,
val channel: Identifier, val channel: Identifier,
val data: ByteArray, val data: ByteArray,
) : ClientboundLoginPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCustomQueryPacket> { internal companion object Codec : PacketCodec<ClientboundCustomQueryPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomQueryPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomQueryPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomQueryPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundCustomQueryPacket {
val messageId = buffer.readVarInt() val messageId = buffer.readVarInt()
val channel = buffer.readIdentifier() val channel = buffer.readIdentifier()
val data = buffer.readBytes(buffer.remaining.toInt()) val data = buffer.readBytes(buffer.remaining.toInt())
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.login.clientbound package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.nbt.NBTCompound import cn.rtast.libmc.nbt.NBTCompound
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
public data class ClientboundDisconnectLoginPacket(val reason: NBTCompound) : ClientboundLoginPacket { public data class ClientboundDisconnectLoginPacket(val reason: NBTCompound) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundDisconnectLoginPacket> { internal companion object Codec : PacketCodec<ClientboundDisconnectLoginPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket {
return ClientboundDisconnectLoginPacket(reason = buffer.readNetworkNBTCompound()) return ClientboundDisconnectLoginPacket(reason = buffer.readNetworkNBTCompound())
} }
} }
@@ -7,10 +7,11 @@
package cn.rtast.libmc.protocol.packet.login.clientbound package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.readPrefixedByteArray import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.primitives.readPrefixedByteArray
/** /**
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Encryption_Request * ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Encryption_Request
@@ -24,10 +25,10 @@ public data class ClientboundHelloPacket(
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Encryption#Authentication * ref: https://minecraft.wiki/w/Java_Edition_protocol/Encryption#Authentication
*/ */
val shouldAuthenticate: Boolean, val shouldAuthenticate: Boolean,
) : ClientboundLoginPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundHelloPacket> { internal companion object Codec : PacketCodec<ClientboundHelloPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundHelloPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundHelloPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundHelloPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundHelloPacket {
val serverId = buffer.readMcString() val serverId = buffer.readMcString()
val publicKey = buffer.readPrefixedByteArray() val publicKey = buffer.readPrefixedByteArray()
val verifyToken = buffer.readPrefixedByteArray() val verifyToken = buffer.readPrefixedByteArray()
@@ -1,12 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/5
*/
package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.packet.MinecraftPacket
public sealed interface ClientboundLoginPacket : MinecraftPacket
@@ -7,17 +7,18 @@
package cn.rtast.libmc.protocol.packet.login.clientbound package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readUuid import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readUuid
import cn.rtast.libmc.protocol.session.GameProfile import cn.rtast.libmc.protocol.session.GameProfile
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
public data class ClientboundLoginSuccessPacket(val gameProfile: GameProfile, val sessionId: Uuid) : public data class ClientboundLoginSuccessPacket(val gameProfile: GameProfile, val sessionId: Uuid) :
ClientboundLoginPacket { MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundLoginSuccessPacket> { internal companion object Codec : PacketCodec<ClientboundLoginSuccessPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundLoginSuccessPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundLoginSuccessPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundLoginSuccessPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundLoginSuccessPacket {
val gameProfile = GameProfile.decode(buffer) val gameProfile = GameProfile.decode(buffer)
val sessionId = buffer.readUuid() val sessionId = buffer.readUuid()
return ClientboundLoginSuccessPacket(gameProfile, sessionId) return ClientboundLoginSuccessPacket(gameProfile, sessionId)
@@ -7,18 +7,19 @@
package cn.rtast.libmc.protocol.packet.login.clientbound package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.readVarInt
import cn.rtast.libmc.common.primitives.writeVarInt
public data class ClientboundSetCompressionPacket(val threshold: Int) : ClientboundLoginPacket { public data class ClientboundSetCompressionPacket(val threshold: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundSetCompressionPacket> { internal companion object Codec : PacketCodec<ClientboundSetCompressionPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundSetCompressionPacket) { override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetCompressionPacket) {
buffer.writeVarInt(value.threshold) buffer.writeVarInt(value = value.threshold)
} }
override fun decode(buffer: BytesBuffer): ClientboundSetCompressionPacket { override suspend fun decode(buffer: BytesBuffer): ClientboundSetCompressionPacket {
return ClientboundSetCompressionPacket(buffer.readVarInt()) return ClientboundSetCompressionPacket(buffer.readVarInt())
} }
} }
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.login.serverbound package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeOptionalPrefixedByteArray import cn.rtast.libmc.common.primitives.writeOptionalPrefixedByteArray
import cn.rtast.libmc.common.writeVarInt import cn.rtast.libmc.common.primitives.writeVarInt
public data class ServerboundCustomQueryAnswerPacket(val messageId: Int, val data: ByteArray?) : MinecraftPacket { public data class ServerboundCustomQueryAnswerPacket(val messageId: Int, val data: ByteArray?) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundCustomQueryAnswerPacket> { internal companion object Codec : PacketCodec<ServerboundCustomQueryAnswerPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundCustomQueryAnswerPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundCustomQueryAnswerPacket) {
buffer.writeVarInt(value.messageId) buffer.writeVarInt(value.messageId)
buffer.writeOptionalPrefixedByteArray(value.data) buffer.writeOptionalPrefixedByteArray(value.data)
} }
override fun decode(buffer: BytesBuffer): ServerboundCustomQueryAnswerPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundCustomQueryAnswerPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,22 +7,22 @@
package cn.rtast.libmc.protocol.packet.login.serverbound package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writePrefixedByteArray import cn.rtast.libmc.common.primitives.writePrefixedByteArray
/** /**
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Encryption * ref: https://minecraft.wiki/w/Java_Edition_protocol/Encryption
*/ */
public data class ServerboundKeyPacket(val sharedSecret: ByteArray, val verifyToken: ByteArray) : MinecraftPacket { public data class ServerboundKeyPacket(val sharedSecret: ByteArray, val verifyToken: ByteArray) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundKeyPacket> { internal companion object Codec : PacketCodec<ServerboundKeyPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundKeyPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundKeyPacket) {
buffer.writePrefixedByteArray(value.sharedSecret) buffer.writePrefixedByteArray(value.sharedSecret)
buffer.writePrefixedByteArray(value.verifyToken) buffer.writePrefixedByteArray(value.verifyToken)
} }
override fun decode(buffer: BytesBuffer): ServerboundKeyPacket = override suspend fun decode(buffer: BytesBuffer): ServerboundKeyPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,12 +7,13 @@
package cn.rtast.libmc.protocol.packet.login.serverbound package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data object ServerboundLoginAcknowledgedPacket : MinecraftPacket, public data object ServerboundLoginAcknowledgedPacket : MinecraftPacket,
PacketCodec<ServerboundLoginAcknowledgedPacket> { PacketCodec<ServerboundLoginAcknowledgedPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundLoginAcknowledgedPacket) {} override suspend fun encode(buffer: BytesBuffer, value: ServerboundLoginAcknowledgedPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundLoginAcknowledgedPacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): ServerboundLoginAcknowledgedPacket =
throw UnsupportedOperationException()
} }
@@ -7,20 +7,21 @@
package cn.rtast.libmc.protocol.packet.login.serverbound package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeMcString import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.writeUuid import cn.rtast.libmc.common.primitives.writeMcString
import cn.rtast.libmc.common.primitives.writeUuid
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
public data class ServerboundLoginStartPacket(val username: String, val playerUuid: Uuid) : MinecraftPacket { public data class ServerboundLoginStartPacket(val username: String, val playerUuid: Uuid) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundLoginStartPacket> { internal companion object Codec : PacketCodec<ServerboundLoginStartPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundLoginStartPacket) { override suspend fun encode(buffer: BytesBuffer, value: ServerboundLoginStartPacket) {
buffer.writeMcString(value.username) buffer.writeMcString(value.username)
buffer.writeUuid(value.playerUuid) buffer.writeUuid(value.playerUuid)
} }
override fun decode(buffer: BytesBuffer): ServerboundLoginStartPacket = throw UnsupportedOperationException() override suspend fun decode(buffer: BytesBuffer): ServerboundLoginStartPacket =
throw UnsupportedOperationException()
} }
} }
@@ -7,17 +7,18 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.PacketCodec import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.readVarInt import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readVarInt
/** /**
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Acknowledge_Block_Change * ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Acknowledge_Block_Change
*/ */
public data class ClientboundAcknowledgeBlockChangePacket(val sequenceId: Int) : ClientboundPlayPacket { public data class ClientboundAcknowledgeBlockChangePacket(val sequenceId: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundAcknowledgeBlockChangePacket> { internal companion object Codec : PacketCodec<ClientboundAcknowledgeBlockChangePacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundAcknowledgeBlockChangePacket) {} override suspend fun encode(buffer: BytesBuffer, value: ClientboundAcknowledgeBlockChangePacket) {}
override fun decode(buffer: BytesBuffer): ClientboundAcknowledgeBlockChangePacket { override suspend fun decode(buffer: BytesBuffer): ClientboundAcknowledgeBlockChangePacket {
return ClientboundAcknowledgeBlockChangePacket(buffer.readVarInt()) return ClientboundAcknowledgeBlockChangePacket(buffer.readVarInt())
} }
} }
Loaded 100 of 296 files, more files were not shown because too many files have changed in this diff. Show more