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/
**/build/*
/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"
ktor-network = "3.5.2"
coroutines-test = "1.11.0"
#kotlinx-serialization = "1.11.0"
kotlinx-coroutines = "1.11.0"
ktor-core = "3.5.2"
cryptography-core = "0.6.0"
#kotlinx-serialization = "1.11.0"
[libraries]
kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" }
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 = { 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-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]
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" }
#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 }
sourceSets {
jvmMain.dependencies {
// no dependencies needed
commonMain.dependencies {
api(libs.kotlinx.coroutines)
}
jvmMain.dependencies {}
nativeMain.dependencies {
implementation(libs.ktor.network)
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 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
import cn.rtast.libmc.protocol.session.AuthenticationProvider
package cn.rtast.libmc.common.crypto
public data class ProtocolContext(
val rsaEncryptor: RSA1024Encryptor,
@@ -16,25 +14,25 @@ public data class ProtocolContext(
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 sha1Hasher: Sha1Hasher
public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher
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"),
sha1Hasher = if (::sha1Hasher.isInitialized) sha1Hasher else error("sha1Hasher 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 encrypt(key: ByteArray, data: ByteArray): ByteArray
public suspend fun encrypt(key: ByteArray, data: ByteArray): ByteArray
}
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
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.writeVarInt
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.primitives.writeVarInt
import kotlin.reflect.KClass
public class PacketRegistry {
@@ -29,10 +28,10 @@ public class PacketRegistry {
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()))
public fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
public suspend fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
@Suppress("UNCHECKED_CAST")
val info = requireNotNull(classToInfo[packet::class]) {
"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 {
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 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 fun sendAndReceive(data: ByteArray): ByteArray
public suspend fun sendAndReceive(data: ByteArray): ByteArray
public fun close()
}
@@ -4,8 +4,10 @@
* 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
public actual class BytesBuffer {
@@ -19,12 +21,12 @@ public actual class BytesBuffer {
outStream.write(bytes)
}
public actual fun writeByte(value: Byte) {
public actual suspend fun writeByte(value: Byte) {
readBuffer = null
outStream.write(value.toInt())
}
public actual fun writeShort(value: Short, endian: ByteOrder) {
public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
readBuffer = null
val v = value.toInt()
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
if (endian == ByteOrder.BIG_ENDIAN) {
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
if (endian == ByteOrder.BIG_ENDIAN) {
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)
}
public actual fun writeFloat(value: Float, endian: ByteOrder) {
public actual suspend fun writeFloat(value: Float, endian: ByteOrder) {
writeInt(value.toRawBits(), endian)
}
public actual fun writeBytes(bytes: ByteArray) {
public actual suspend fun writeBytes(bytes: ByteArray) {
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
if (buf == null) {
buf = outStream.toByteArray()
@@ -84,26 +86,26 @@ public actual class BytesBuffer {
return buf
}
public actual fun readByte(): Byte {
public actual suspend fun readByte(): Byte {
val array = ensureReadArray()
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
return array[readOffset++]
}
public actual fun readUByte(): UByte = this.readByte().toUByte()
public actual fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
public actual suspend fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(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))
}
public actual fun readFloat(endian: ByteOrder): Float {
public actual suspend fun readFloat(endian: ByteOrder): Float {
return Float.fromBits(readInt(endian))
}
public actual fun readBytes(length: Int): ByteArray {
public actual suspend fun readBytes(length: Int): ByteArray {
val array = ensureReadArray()
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
val result = array.copyOfRange(readOffset, readOffset + length)
@@ -111,9 +113,9 @@ public actual class BytesBuffer {
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()
if (readOffset >= array.size) return byteArrayOf()
val result = array.copyOfRange(readOffset, array.size)
@@ -121,10 +123,10 @@ public actual class BytesBuffer {
return result
}
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
public actual fun hasRemaining(): Boolean = readOffset < ensureReadArray().size
public actual suspend fun toByteArray(): ByteArray = outStream.toByteArray()
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 remaining: Long get() = (outStream.size() - readOffset).coerceAtLeast(0).toLong()
}
@@ -4,7 +4,7 @@
* Date: 2026/9/3
*/
package cn.rtast.libmc.common
package cn.rtast.libmc.common.stream
import java.io.EOFException
import java.io.InputStream
@@ -17,7 +17,7 @@ public actual open class ReadChannel public actual constructor() {
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
val length = end - start
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)
readFully(buf, 0, 1)
return buf[0]
}
public actual open fun readBytes(length: Int): ByteArray {
public actual open suspend fun readBytes(length: Int): ByteArray {
val bytes = ByteArray(length)
readFully(bytes, 0, length)
return bytes
}
public actual open fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual open 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 readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
public actual open suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
public actual open suspend fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
}
public actual open class WriteChannel public actual constructor() {
@@ -51,8 +51,8 @@ public actual open class WriteChannel public actual constructor() {
_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)
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 {
val b1 = this[0].toInt() and 0xFF
@@ -4,8 +4,11 @@
* 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.DatagramSocket
import java.net.InetSocketAddress
@@ -25,13 +28,12 @@ public actual class UdpSocket public actual constructor(host: String, port: Int,
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))
val buf = ByteArray(2048)
val receivePacket = DatagramPacket(buf, buf.size)
socket.receive(receivePacket)
return buf.copyOf(receivePacket.length)
return@withContext buf.copyOf(receivePacket.length)
}
public actual fun close(): Unit = socket.close()
@@ -4,7 +4,7 @@
* Date: 2026/9/3
*/
package cn.rtast.libmc.common
package cn.rtast.libmc.common.stream
import io.ktor.utils.io.core.*
import kotlinx.io.*
@@ -21,53 +21,53 @@ public actual class BytesBuffer {
_delegateBuf = Buffer().apply { write(bytes) }
}
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
public actual fun writeShort(value: Short, endian: ByteOrder) {
public actual suspend fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(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)
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)
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)
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)
public actual 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 writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
public actual suspend fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
public actual fun readByte(): Byte = _delegateBuf.readByte()
public actual fun readUByte(): UByte = this.readByte().toUByte()
public actual fun readShort(endian: ByteOrder): Short =
public actual suspend fun readByte(): Byte = _delegateBuf.readByte()
public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
public actual suspend fun readShort(endian: ByteOrder): Short =
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()
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()
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()
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()
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
public actual fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
public actual fun readRemainingBytes(): ByteArray = this.toByteArray()
public actual suspend fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
public actual suspend fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
public actual suspend fun readRemainingBytes(): ByteArray = this.toByteArray()
public actual fun toByteArray(): ByteArray {
public actual suspend fun toByteArray(): ByteArray {
val copy = _delegateBuf.peek()
return try {
copy.readByteArray()
@@ -76,8 +76,8 @@ public actual class BytesBuffer {
}
}
public actual fun hasRemaining(): Boolean = !_delegateBuf.exhausted()
public actual fun close(): Unit = _delegateBuf.close()
public actual suspend fun hasRemaining(): Boolean = !_delegateBuf.exhausted()
public actual suspend fun close(): Unit = _delegateBuf.close()
public actual val size: Int get() = _delegateBuf.size.toInt()
public actual val remaining: Long get() = _delegateBuf.remaining
@@ -4,11 +4,10 @@
* 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.bits.*
import kotlinx.coroutines.runBlocking
public actual open class ReadChannel public actual constructor() {
@@ -18,18 +17,18 @@ public actual open class ReadChannel public actual constructor() {
this._readChannel = readChannel
}
public actual open fun readByte(): Byte = runBlocking { _readChannel.readByte() }
public actual open fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) }
public actual open fun readFully(out: ByteArray, start: Int, end: Int): Unit =
runBlocking { _readChannel.readFully(out, start, end) }
public actual open suspend fun readByte(): Byte = _readChannel.readByte()
public actual open suspend fun readBytes(length: Int): ByteArray = _readChannel.readByteArray(length)
public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int): Unit =
_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 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()
}
public actual open fun readInt(endian: ByteOrder): Int {
public actual open suspend fun readInt(endian: ByteOrder): Int {
val bytes = readBytes(4)
val v = (bytes[0].toInt() and 0xFF shl 24) 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()
}
public actual open fun readLong(endian: ByteOrder): Long {
public actual open suspend fun readLong(endian: ByteOrder): Long {
val bytes = readBytes(8)
var v = 0L
for (i in 0 until 8) {
@@ -55,8 +54,8 @@ public actual open class WriteChannel public actual constructor() {
_writeChannel = writeChannel
}
public actual open fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
runBlocking { _writeChannel.writeFully(value, startIndex, endIndex) }
public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
_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
*/
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.utils.io.core.*
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 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) }
socket.send(Datagram(packet, remoteAddress))
socket.receive().packet.readByteArray()
return socket.receive().packet.readByteArray()
}
public actual fun close() {
@@ -7,8 +7,8 @@
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import kotlin.random.Random
private val RAKNET_MAGIC = byteArrayOf(
@@ -33,14 +33,14 @@ internal data class BedrockRequestPacket(
override val packetId: Byte = 0x01
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.writeLong(value.time)
buffer.writeBytes(value.magic)
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 {
@@ -72,8 +72,8 @@ internal data class BedrockResponsePacket(
) : MinecraftBedrockPacket {
companion object Codec : PacketCodec<BedrockResponsePacket> {
override fun encode(buffer: BytesBuffer, value: BedrockResponsePacket) = throw UnsupportedOperationException()
override fun decode(buffer: BytesBuffer): BedrockResponsePacket {
override suspend fun encode(buffer: BytesBuffer, value: BedrockResponsePacket) = throw UnsupportedOperationException()
override suspend fun decode(buffer: BytesBuffer): BedrockResponsePacket {
val packetId = buffer.readByte()
if (packetId != 0x1C.toByte()) throw IllegalStateException("Expected pong id 0x1C, got $packetId")
val time = buffer.readLong()
@@ -7,12 +7,12 @@
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.UdpSocket
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
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()
codec.encode(buf, packet)
return sendAndReceive(buf.toByteArray())
@@ -8,12 +8,12 @@
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.common.UdpSocket
import cn.rtast.libmc.common.wrap
import cn.rtast.libmc.common.stream.UdpSocket
import cn.rtast.libmc.common.stream.wrap
import cn.rtast.libmc.mcping.PingResponse
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)
return try {
val sendTime = Clock.System.now().toEpochMilliseconds()
@@ -7,10 +7,10 @@
package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.McStringCodec
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.VarIntCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.primitives.McStringCodec
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.VarIntCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
@@ -22,30 +22,30 @@ internal data class HandshakePacket(
val nextState: Int,
) : MinecraftPacket {
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)
McStringCodec.encode(buffer, value.serverAddress)
buffer.writeShort(value.serverPort.toShort())
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
internal data object StatusRequestPacket : MinecraftPacket, PacketCodec<StatusRequestPacket> {
override fun encode(buffer: BytesBuffer, value: StatusRequestPacket) {}
override fun decode(buffer: BytesBuffer): StatusRequestPacket = throw UnsupportedOperationException()
override suspend fun encode(buffer: BytesBuffer, value: StatusRequestPacket) {}
override suspend fun decode(buffer: BytesBuffer): StatusRequestPacket = throw UnsupportedOperationException()
}
internal data class PingPacket(val currentTime: Long) : MinecraftPacket {
companion object : PacketCodec<PingPacket> {
override fun encode(buffer: BytesBuffer, value: PingPacket) {
override suspend fun encode(buffer: BytesBuffer, value: PingPacket) {
buffer.writeLong(value.currentTime)
}
override fun decode(buffer: BytesBuffer): PingPacket {
override suspend fun decode(buffer: BytesBuffer): PingPacket {
return PingPacket(buffer.readLong())
}
}
@@ -8,11 +8,15 @@
package cn.rtast.libmc.mcping.java
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.sendPacket
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 receiveChannel = socket.openReadChannel()
val sendChannel = socket.openWriteChannel()
@@ -16,7 +16,7 @@ import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
@JvmOverloads
public fun mcping(
public suspend fun mcping(
host: String,
port: Int,
type: ServerType = ServerType.Java,
@@ -7,15 +7,19 @@
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.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()
bodyBuffer.write(packetId, VarIntCodec)
VarIntCodec.encode(bodyBuffer, packetId)
codec.encode(bodyBuffer, packet)
val frameBuffer = BytesBuffer()
frameBuffer.write(bodyBuffer.size, VarIntCodec)
VarIntCodec.encode(bodyBuffer, bodyBuffer.size)
frameBuffer.writeBuffer(bodyBuffer)
val bytes = frameBuffer.toByteArray()
this.writeFully(bytes, 0, bytes.size)
@@ -7,12 +7,12 @@
package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.stream.ByteOrder
import cn.rtast.libmc.common.stream.BytesBuffer
public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput {
override fun readByte(): Byte = buffer.readByte()
override fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
override suspend fun readByte(): Byte = buffer.readByte()
override suspend fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
}
public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput =
@@ -7,17 +7,17 @@
package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.stream.ByteOrder
import cn.rtast.libmc.common.stream.BytesBuffer
public class BytesBufferNBTOutput(
override val order: ByteOrder,
override val root: NBTTag.CompoundTag,
private val buffer: BytesBuffer,
) : NBTOutput {
override fun writeByte(value: Byte): Unit = buffer.writeByte(value)
override fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
override fun toByteArray(): ByteArray = buffer.toByteArray()
override suspend fun writeByte(value: Byte): Unit = buffer.writeByte(value)
override suspend fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
override suspend fun toByteArray(): ByteArray = buffer.toByteArray()
}
public fun BytesBuffer.toNBTOutput(
@@ -7,33 +7,33 @@
package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder
import cn.rtast.libmc.common.stream.ByteOrder
public interface NBTInput {
public val order: ByteOrder
public fun readByte(): Byte
public fun readBytes(count: Int): ByteArray
public suspend fun readByte(): Byte
public suspend fun readBytes(count: Int): ByteArray
public fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE()
public fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE()
public fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE()
public 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 readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE()
public suspend fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE()
public suspend fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE()
public suspend fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE()
public suspend fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE()
// big endian
public fun readLongBE(): Long =
public suspend fun readLongBE(): Long =
(readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL)
public fun readFloatBE(): Float = Float.fromBits(readIntBE())
public fun readDoubleBE(): Double = Double.fromBits(readLongBE())
public fun readShortBE(): Short {
public suspend fun readFloatBE(): Float = Float.fromBits(readIntBE())
public suspend fun readDoubleBE(): Double = Double.fromBits(readLongBE())
public suspend fun readShortBE(): Short {
val b1 = readByte().toInt() and 0xFF
val b2 = readByte().toInt() and 0xFF
return ((b1 shl 8) or b2).toShort()
}
public fun readIntBE(): Int {
public suspend fun readIntBE(): Int {
return ((readByte().toInt() and 0xFF) shl 24) or
((readByte().toInt() and 0xFF) shl 16) or
((readByte().toInt() and 0xFF) shl 8) or
@@ -41,7 +41,7 @@ public interface NBTInput {
}
// little endian
public fun readLongLE(): Long {
public suspend fun readLongLE(): Long {
return ((readByte().toInt() and 0xFF).toLong() shl 0) or
((readByte().toInt() and 0xFF).toLong() shl 8) or
((readByte().toInt() and 0xFF).toLong() shl 16) or
@@ -52,14 +52,14 @@ public interface NBTInput {
(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()
public fun readIntLE(): Int =
public suspend fun readIntLE(): Int =
((readByte().toInt() and 0xFF)) or
((readByte().toInt() and 0xFF) shl 8) or
((readByte().toInt() and 0xFF) shl 16) or
@@ -7,44 +7,44 @@
package cn.rtast.libmc.nbt
import cn.rtast.libmc.common.ByteOrder
import cn.rtast.libmc.common.stream.ByteOrder
public interface NBTOutput {
public val order: ByteOrder
public val root: NBTTag.CompoundTag
public fun writeByte(value: Byte)
public fun writeBytes(value: ByteArray)
public suspend fun writeByte(value: Byte)
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)
public fun writeInt(value: Int): Unit =
public suspend fun writeInt(value: Int): Unit =
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)
public fun writeFloat(value: Float): Unit =
public suspend fun writeFloat(value: Float): Unit =
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)
// big endian
public fun writeShortBE(value: Short) {
public suspend fun writeShortBE(value: Short) {
writeByte(((value.toInt() shr 8) 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 16) and 0xFF).toByte())
writeByte(((value shr 8) 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 48).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())
}
public fun writeFloatBE(value: Float) {
public suspend fun writeFloatBE(value: Float) {
writeIntBE(value.toBits())
}
public fun writeDoubleBE(value: Double) {
public suspend fun writeDoubleBE(value: Double) {
writeLongBE(value.toBits())
}
// Little Endian
public fun writeShortLE(value: Short) {
public suspend fun writeShortLE(value: Short) {
writeByte((value.toInt() 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 shr 8) and 0xFF).toByte())
writeByte(((value shr 16) 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 shr 8) 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())
}
public fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
public fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
public fun toByteArray(): ByteArray
public suspend fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
public suspend fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
public suspend fun toByteArray(): ByteArray
}
@@ -7,13 +7,13 @@
package cn.rtast.libmc.nbt
public fun NBTInput.readStringTag(): String {
public suspend fun NBTInput.readStringTag(): String {
val length = readShort().toInt() and 0xFFFF
val bytes = readBytes(length)
return bytes.decodeToString()
}
public fun NBTInput.readListTag(): NBTTag.ListTag {
public suspend fun NBTInput.readListTag(): NBTTag.ListTag {
val elementTypeId = readByte().toInt() and 0xFF
val elementType = NBTType.fromID(elementTypeId)
val length = readInt()
@@ -22,7 +22,7 @@ public fun NBTInput.readListTag(): NBTTag.ListTag {
return NBTTag.ListTag(elementType, list)
}
public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
val map = LinkedHashMap<String, NBTTag>()
while (true) {
val typeId = readByte().toInt() and 0xFF
@@ -35,7 +35,7 @@ public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
return NBTTag.CompoundTag(map)
}
public fun NBTInput.readTagPayload(type: NBTType): NBTTag =
public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag =
when (type) {
NBTType.Byte -> NBTTag.ByteTag(readByte())
NBTType.Short -> NBTTag.ShortTag(readShort())
@@ -52,7 +52,7 @@ public fun NBTInput.readTagPayload(type: NBTType): NBTTag =
NBTType.End -> error("TAG_End no payload")
}
public fun NBTInput.readCompound(): NBTTag {
public suspend fun NBTInput.readCompound(): NBTTag {
val map = LinkedHashMap<String, NBTTag>()
while (true) {
val typeId = readByte().toInt()
@@ -67,7 +67,7 @@ public fun NBTInput.readCompound(): NBTTag {
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())
require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" }
val nameLen = readShort().toInt() and 0xFFFF
@@ -76,7 +76,7 @@ public fun NBTInput.readRootCompound(): NBTCompound {
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)) {
NBTType.Compound -> NBTCompound("", readCompoundTag())
NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag()))))
@@ -8,13 +8,13 @@
package cn.rtast.libmc.nbt
public fun NBTOutput.writeStringTag(value: String) {
public suspend fun NBTOutput.writeStringTag(value: String) {
val bytes = value.encodeToByteArray()
writeShort(bytes.size.toShort())
writeBytes(bytes)
}
public fun NBTOutput.writeTagPayload(tag: NBTTag) {
public suspend fun NBTOutput.writeTagPayload(tag: NBTTag) {
when (tag) {
is NBTTag.ByteTag -> writeByte(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)
writeStringTag(name)
writeTagPayload(root)
return toByteArray()
}
public fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
public suspend fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
val compoundTag = compound.element as? NBTTag.CompoundTag
?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag")
writeByte(NBTType.Compound.id)
@@ -7,7 +7,7 @@
package test
import cn.rtast.libmc.common.wrap
import cn.rtast.libmc.common.stream.wrap
import cn.rtast.libmc.nbt.NbtReader
import org.junit.Test
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 {
api(project(":common"))
api(project(":nbt"))
api(libs.kotlinx.coroutines)
}
jvmMain.dependencies {
@@ -27,8 +26,7 @@ kotlin {
}
jvmTest.dependencies {
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.okhttp)
implementation(project(":protocol-encrypt"))
}
}
}
@@ -7,8 +7,8 @@
package cn.rtast.libmc.protocol.client
import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.protocol.crypto.ProtocolContext
import cn.rtast.libmc.protocol.crypto.ProtocolContextBuilder
import cn.rtast.libmc.common.crypto.ProtocolContext
import cn.rtast.libmc.common.crypto.ProtocolContextBuilder
import cn.rtast.libmc.protocol.event.InternalPacketDispatcher
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
import cn.rtast.libmc.protocol.network.NetworkChannel
@@ -56,7 +56,7 @@ public class MinecraftClient internal constructor(
override val coroutineContext: CoroutineContext
get() = clientJob + ioDispatcher + CoroutineName("LibMC-MinecraftClient-$username")
public fun connect(protocolVersion: Int = 776) {
public suspend fun connect(protocolVersion: Int = 776) {
networkChannel.connect()
startListening()
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.ServerboundPongPlayPacket
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
public class InternalPacketDispatcher(
@@ -31,19 +31,8 @@ public class InternalPacketDispatcher(
public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
this.dispatchEvent(packet)
when (packet) {
is ClientboundLoginPacket -> this.handleLoginPackets(packet)
is ClientboundConfigurationPacket -> this.handleConfigurationPackets(packet)
is ClientboundPlayPacket -> this.handlePlayPackets(packet)
}
}
private suspend fun handleLoginPackets(packet: ClientboundLoginPacket) {
when (packet) {
is ClientboundDisconnectLoginPacket -> {
println("Login denied: ${packet.reason}")
client.close()
}
// login
is ClientboundDisconnectLoginPacket -> client.close()
is ClientboundSetCompressionPacket -> client.networkChannel.setCompression(packet.threshold)
is ClientboundLoginSuccessPacket -> {
client.networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket)
@@ -67,13 +56,7 @@ public class InternalPacketDispatcher(
client.session.enableEncryption(sharedSecret)
}
else -> {}
}
}
private fun handleConfigurationPackets(packet: ClientboundConfigurationPacket) {
when (packet) {
is ClientboundDisconnectConfigurationPacket -> println("Configuration disconnected: ${packet.reason}")
// configuration
ClientboundFinishConfigurationPacket -> {
client.networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket)
client.stateMachine.transitionTo(ProtocolState.PLAY)
@@ -91,24 +74,16 @@ public class InternalPacketDispatcher(
ServerboundSelectKnownPacksPacket(emptyList())
) // TODO empty resource packs list
is ClientboundCodeOfConductPacket -> client.networkChannel.sendPacket(ServerboundAcceptCodeOfConductPacket)
else -> {}
}
}
private fun handlePlayPackets(packet: ClientboundPlayPacket) {
when (packet) {
// play
is ClientboundDisconnectPlayPacket -> client.close()
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 ClientboundPlayerChatMessagePacket -> println("[Player Chat Message] ${packet.message}")
ClientboundStartConfigurationPacket -> {
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
}
is ClientboundSystemChatMessagePacket -> println("[System message] ${packet.content}")
else -> println(packet)
else -> {}
}
}
}
@@ -7,27 +7,27 @@
package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.common.ReadChannel
import cn.rtast.libmc.common.WriteChannel
import cn.rtast.libmc.protocol.crypto.NetworkCipher
import cn.rtast.libmc.common.stream.ReadChannel
import cn.rtast.libmc.common.stream.WriteChannel
import cn.rtast.libmc.common.crypto.NetworkCipher
internal class CipherReadChannel(
private val delegate: ReadChannel,
private val crypto: NetworkCipher
) : 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)
val length = end - start
if (length > 0) crypto.decrypt(out, start, length)
}
override fun readByte(): Byte {
override suspend fun readByte(): Byte {
val buf = ByteArray(1)
readFully(buf, 0, 1)
return buf[0]
}
override fun readBytes(length: Int): ByteArray {
override suspend fun readBytes(length: Int): ByteArray {
val bytes = ByteArray(length)
readFully(bytes, 0, length)
return bytes
@@ -38,14 +38,14 @@ internal class CipherWriteChannel(
private val delegate: WriteChannel,
private val crypto: NetworkCipher
) : WriteChannel() {
override fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
val length = endIndex - startIndex
if (length <= 0) return
val encrypted = value.copyOfRange(startIndex, endIndex)
crypto.encrypt(encrypted, 0, length)
delegate.writeFully(encrypted, 0, length)
}
override fun flush() {
override suspend fun flush() {
delegate.flush()
}
}
@@ -7,9 +7,14 @@
package cn.rtast.libmc.protocol.network
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.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.crypto.NetworkCipher
import cn.rtast.libmc.protocol.protocol.GameProtocols
import kotlin.concurrent.Volatile
@@ -33,7 +38,7 @@ internal class NetworkChannel(
this.threshold = threshold
}
fun readNextPacket(): MinecraftPacket {
suspend fun readNextPacket(): MinecraftPacket {
val packetLength = session.readVarInt()
val rawFrameBytes = session.readBytes(packetLength)
val frameBuf = rawFrameBytes.wrap()
@@ -49,7 +54,7 @@ internal class NetworkChannel(
.decodePacket(packetId, payloadBuf)
}
fun sendPacket(packet: MinecraftPacket) {
suspend fun sendPacket(packet: MinecraftPacket) {
val uncompressedBodyBuf = BytesBuffer()
GameProtocols.serverboundGameProtocols
.getRegistry(stateMachine.currentState)
@@ -7,10 +7,10 @@
package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.common.ReadChannel
import cn.rtast.libmc.common.Socket
import cn.rtast.libmc.common.WriteChannel
import cn.rtast.libmc.protocol.crypto.NetworkCipher
import cn.rtast.libmc.common.stream.ReadChannel
import cn.rtast.libmc.common.stream.Socket
import cn.rtast.libmc.common.stream.WriteChannel
import cn.rtast.libmc.common.crypto.NetworkCipher
internal class NetworkSession(
private val host: String,
@@ -41,17 +41,17 @@ internal class NetworkSession(
this.writeChannel = CipherWriteChannel(currentWrite, cipher)
}
fun readByte(): Byte {
suspend fun readByte(): Byte {
val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readByte()
}
fun readBytes(length: Int): ByteArray {
suspend fun readBytes(length: Int): ByteArray {
val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readBytes(length)
}
fun readVarInt(): Int {
suspend fun readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
@@ -65,7 +65,7 @@ internal class NetworkSession(
return result
}
fun writeFully(data: ByteArray) {
suspend fun writeFully(data: ByteArray) {
val channel = requireNotNull(writeChannel) { "WriteChannel not connected" }
channel.writeFully(data, 0, data.size)
channel.flush()
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.configuration
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readMcString
import cn.rtast.libmc.common.writeMcString
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.readMcString
import cn.rtast.libmc.common.primitives.writeMcString
public data class KnownPacks(val namespace: String, val id: String, val version: String) {
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.id)
buffer.writeMcString(value.version)
}
override fun decode(buffer: BytesBuffer): KnownPacks {
override suspend fun decode(buffer: BytesBuffer): KnownPacks {
val namespace = buffer.readMcString()
val id = buffer.readMcString()
val version = buffer.readMcString()
@@ -7,7 +7,12 @@
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.protocol.protocol.util.readNetworkNBTCompound
import kotlin.uuid.Uuid
@@ -18,10 +23,10 @@ public data class ClientboundAddResourcePackPacket(
val hash: String,
val forced: Boolean,
val prompt: NBTCompound,
) : ClientboundConfigurationPacket {
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
val uuid = buffer.readUuid()
val url = buffer.readMcString()
val hash = buffer.readMcString()
@@ -7,11 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
override suspend fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
}
@@ -7,14 +7,15 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readMcString
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
public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : ClientboundConfigurationPacket {
public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
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
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
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.nbt.NBTCompound
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound())
}
}
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
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.protocol.protocol.game.Identifier
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
return ClientboundCookieRequestPacket(key = buffer.readIdentifier())
}
}
@@ -7,9 +7,9 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
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.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -18,8 +18,8 @@ public data class ClientboundCustomPayloadPacket(
val data: ByteArray,
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
val channel = buffer.readIdentifier()
val data = buffer.readRemainingBytes()
return ClientboundCustomPayloadPacket(channel, data)
@@ -7,18 +7,22 @@
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>) :
ClientboundConfigurationPacket {
public data class ClientboundCustomReportDetailsPacket(val details: List<ReportDetail>) : MinecraftPacket {
public data class ReportDetail(val title: String, val description: String) {
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.description)
}
override fun decode(buffer: BytesBuffer): ReportDetail {
override suspend fun decode(buffer: BytesBuffer): ReportDetail {
val title = buffer.readMcString()
val description = buffer.readMcString()
return ReportDetail(title, description)
@@ -27,8 +31,8 @@ public data class ClientboundCustomReportDetailsPacket(val details: List<ReportD
}
internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
val detailCount = buffer.readVarInt()
val details = ArrayList<ReportDetail>(detailCount)
repeat(detailCount) { details.add(ReportDetail.decode(buffer)) }
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
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.nbt.NBTCompound
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
return ClientboundDisconnectConfigurationPacket(reason = buffer.readNetworkNBTCompound())
}
}
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
return ClientboundFinishConfigurationPacket
}
}
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
return ClientboundKeepAliveConfigurationPacket(buffer.readLong())
}
}
@@ -7,13 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
return ClientboundPingConfigurationPacket(id = buffer.readInt())
}
}
@@ -7,9 +7,10 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.registry.RegistryEntry
@@ -20,10 +21,10 @@ public data class ClientboundRegistryDataPacket(
*/
val registryId: Identifier,
val entries: List<RegistryEntry>,
) : ClientboundConfigurationPacket {
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
val id = buffer.readIdentifier()
val entryCount = buffer.readVarInt()
val entries = ArrayList<RegistryEntry>(entryCount)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readUuid
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.readUuid
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
val uuid = buffer.readUuid()
return ClientboundRemoveResourcePackPacket(uuid)
}
@@ -7,11 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
override suspend fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
}
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
val packsCount = buffer.readVarInt()
val packs = List(packsCount) { KnownPacks.decode(buffer) }
return ClientboundSelectKnownPacksPacket(packs)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
val count = buffer.readVarInt()
val links = ArrayList<ServerLink>(count)
repeat(count) { links.add(ServerLink.decode(buffer)) }
@@ -7,9 +7,10 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readPrefixedByteArray
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.readPrefixedByteArray
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -19,10 +20,10 @@ public data class ClientboundStoreCookiePacket(
* cookie
*/
val payload: ByteArray,
) : ClientboundConfigurationPacket {
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
override fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
val key = buffer.readIdentifier()
val payload = buffer.readPrefixedByteArray()
return ClientboundStoreCookiePacket(key, payload)
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readMcString
import cn.rtast.libmc.common.readVarInt
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
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
val host = buffer.readMcString()
val port = buffer.readVarInt()
return ClientboundTransferPacket(host, port)
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) :
ClientboundConfigurationPacket {
public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
val featureCount = buffer.readVarInt()
val features = ArrayList<Identifier>(featureCount)
repeat(featureCount) { features.add(buffer.readIdentifier()) }
@@ -7,24 +7,25 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
import cn.rtast.libmc.common.writeVarInt
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.readVarInt
import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
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>) {
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.writeVarInt(value.tags.size)
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 tagCount = buffer.readVarInt()
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) {
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.writeVarInt(value.entries.size)
value.entries.forEach { buffer.writeVarInt(it) }
}
override fun decode(buffer: BytesBuffer): Tag {
override suspend fun decode(buffer: BytesBuffer): Tag {
val name = buffer.readIdentifier()
val entrySize = buffer.readVarInt()
val entries = ArrayList<Int>(entrySize)
@@ -68,8 +69,8 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
}
internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
val registryCount = buffer.readVarInt()
val registries = ArrayList<TaggedRegistry>(registryCount)
repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket,
PacketCodec<ServerboundAcceptCodeOfConductPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
override suspend fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
ServerboundAcceptCodeOfConductPacket
}
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket,
PacketCodec<ServerboundAckFinishConfigurationPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
throw UnsupportedOperationException()
}
@@ -7,16 +7,16 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
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.writeIdentifier
public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket {
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)
if (value.payload != null) {
buffer.writeBoolean(true)
@@ -25,7 +25,7 @@ public data class ServerboundCookieResponsePacket(val key: Identifier, val paylo
} else buffer.writeBoolean(false)
}
override fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
throw UnsupportedOperationException()
}
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
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)
}
override fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
throw UnsupportedOperationException()
}
}
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
public data class ServerboundPongConfigurationPacket(val id: Int) : MinecraftPacket {
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)
}
override fun decode(buffer: BytesBuffer): ServerboundPongConfigurationPacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundPongConfigurationPacket =
throw UnsupportedOperationException()
}
}
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
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
public data class ServerboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket {
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)
value.knownPacks.forEach { KnownPacks.encode(buffer, it) }
}
override fun decode(buffer: BytesBuffer): ServerboundSelectKnownPacksPacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundSelectKnownPacksPacket =
throw UnsupportedOperationException()
}
}
@@ -7,11 +7,11 @@
package cn.rtast.libmc.protocol.packet.handshake
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeMcString
import cn.rtast.libmc.common.writeVarInt
import cn.rtast.libmc.common.primitives.writeMcString
import cn.rtast.libmc.common.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent
public data class ServerboundHandshakePacket(
@@ -21,13 +21,13 @@ public data class ServerboundHandshakePacket(
val intent: HandshakeIntent,
) : MinecraftPacket {
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.writeMcString(value.serverAddress)
buffer.writeShort(value.serverPort.toShort())
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
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -17,10 +18,10 @@ public data class ClientboundCustomQueryPacket(
val messageId: Int,
val channel: Identifier,
val data: ByteArray,
) : ClientboundLoginPacket {
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCustomQueryPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundCustomQueryPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundCustomQueryPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomQueryPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomQueryPacket {
val messageId = buffer.readVarInt()
val channel = buffer.readIdentifier()
val data = buffer.readBytes(buffer.remaining.toInt())
@@ -7,15 +7,16 @@
package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
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.nbt.NBTCompound
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> {
override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket {
return ClientboundDisconnectLoginPacket(reason = buffer.readNetworkNBTCompound())
}
}
@@ -7,10 +7,11 @@
package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readMcString
import cn.rtast.libmc.common.readPrefixedByteArray
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.readPrefixedByteArray
/**
* 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
*/
val shouldAuthenticate: Boolean,
) : ClientboundLoginPacket {
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundHelloPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundHelloPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundHelloPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundHelloPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundHelloPacket {
val serverId = buffer.readMcString()
val publicKey = 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
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readUuid
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.readUuid
import cn.rtast.libmc.protocol.session.GameProfile
import kotlin.uuid.Uuid
public data class ClientboundLoginSuccessPacket(val gameProfile: GameProfile, val sessionId: Uuid) :
ClientboundLoginPacket {
MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundLoginSuccessPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundLoginSuccessPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundLoginSuccessPacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundLoginSuccessPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundLoginSuccessPacket {
val gameProfile = GameProfile.decode(buffer)
val sessionId = buffer.readUuid()
return ClientboundLoginSuccessPacket(gameProfile, sessionId)
@@ -7,18 +7,19 @@
package cn.rtast.libmc.protocol.packet.login.clientbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
import cn.rtast.libmc.common.writeVarInt
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.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> {
override fun encode(buffer: BytesBuffer, value: ClientboundSetCompressionPacket) {
buffer.writeVarInt(value.threshold)
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetCompressionPacket) {
buffer.writeVarInt(value = value.threshold)
}
override fun decode(buffer: BytesBuffer): ClientboundSetCompressionPacket {
override suspend fun decode(buffer: BytesBuffer): ClientboundSetCompressionPacket {
return ClientboundSetCompressionPacket(buffer.readVarInt())
}
}
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeOptionalPrefixedByteArray
import cn.rtast.libmc.common.writeVarInt
import cn.rtast.libmc.common.primitives.writeOptionalPrefixedByteArray
import cn.rtast.libmc.common.primitives.writeVarInt
public data class ServerboundCustomQueryAnswerPacket(val messageId: Int, val data: ByteArray?) : MinecraftPacket {
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.writeOptionalPrefixedByteArray(value.data)
}
override fun decode(buffer: BytesBuffer): ServerboundCustomQueryAnswerPacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundCustomQueryAnswerPacket =
throw UnsupportedOperationException()
}
@@ -7,22 +7,22 @@
package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.PacketCodec
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
*/
public data class ServerboundKeyPacket(val sharedSecret: ByteArray, val verifyToken: ByteArray) : MinecraftPacket {
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.verifyToken)
}
override fun decode(buffer: BytesBuffer): ServerboundKeyPacket =
override suspend fun decode(buffer: BytesBuffer): ServerboundKeyPacket =
throw UnsupportedOperationException()
}
@@ -7,12 +7,13 @@
package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.packet.PacketCodec
public data object ServerboundLoginAcknowledgedPacket : MinecraftPacket,
PacketCodec<ServerboundLoginAcknowledgedPacket> {
override fun encode(buffer: BytesBuffer, value: ServerboundLoginAcknowledgedPacket) {}
override fun decode(buffer: BytesBuffer): ServerboundLoginAcknowledgedPacket = throw UnsupportedOperationException()
override suspend fun encode(buffer: BytesBuffer, value: ServerboundLoginAcknowledgedPacket) {}
override suspend fun decode(buffer: BytesBuffer): ServerboundLoginAcknowledgedPacket =
throw UnsupportedOperationException()
}
@@ -7,20 +7,21 @@
package cn.rtast.libmc.protocol.packet.login.serverbound
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.stream.BytesBuffer
import cn.rtast.libmc.common.packet.MinecraftPacket
import cn.rtast.libmc.common.writeMcString
import cn.rtast.libmc.common.writeUuid
import cn.rtast.libmc.common.packet.PacketCodec
import cn.rtast.libmc.common.primitives.writeMcString
import cn.rtast.libmc.common.primitives.writeUuid
import kotlin.uuid.Uuid
public data class ServerboundLoginStartPacket(val username: String, val playerUuid: Uuid) : MinecraftPacket {
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.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
import cn.rtast.libmc.common.BytesBuffer
import cn.rtast.libmc.common.PacketCodec
import cn.rtast.libmc.common.readVarInt
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.readVarInt
/**
* 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> {
override fun encode(buffer: BytesBuffer, value: ClientboundAcknowledgeBlockChangePacket) {}
override fun decode(buffer: BytesBuffer): ClientboundAcknowledgeBlockChangePacket {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundAcknowledgeBlockChangePacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundAcknowledgeBlockChangePacket {
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