Split Socket implementation, improve performance

This commit is contained in:
2026-09-08 01:16:01 +08:00
parent ebb4ff848d
commit ff14d8bf00
318 files changed
+1109 -2722

No files matched your search

+1
View File
@@ -49,3 +49,4 @@ bin/
**/build/* **/build/*
/libmc-protocol/src/jvmTest/resources/accessToken.txt /libmc-protocol/src/jvmTest/resources/accessToken.txt
/libmc-protocol-encrypt/src/commonTest/resources/accessToken.txt /libmc-protocol-encrypt/src/commonTest/resources/accessToken.txt
/libmc-protocol-context/src/commonTest/resources/accessToken.txt
-6
View File
@@ -346,12 +346,6 @@ A lightweight minecraft client-side protocol library and related library, includ
--- ---
# libmc-mcping
A lightweight module to query Minecraft Java and Bedrock server status, MOTD, and latency
[Use mcping](https://repo.rtast.cn/packages/-/cn.rtast.libmc:mcping)
# libmc-rconlib # libmc-rconlib
Send Command via rcon protocol Send Command via rcon protocol
+1
View File
@@ -20,6 +20,7 @@ subprojects {
pluginManager.apply("org.jetbrains.kotlin.multiplatform") pluginManager.apply("org.jetbrains.kotlin.multiplatform")
pluginManager.apply("maven-publish") pluginManager.apply("maven-publish")
if (project.path.startsWith(":example")) return@subprojects
publishing { publishing {
repositories { repositories {
maven("https://repo.rtast.cn/packages") { maven("https://repo.rtast.cn/packages") {
+10 -4
View File
@@ -12,18 +12,24 @@ kotlin {
sourceSets { sourceSets {
commonMain.dependencies { commonMain.dependencies {
implementation(libs.kotlinx.io)
api(libs.kotlinx.coroutines) api(libs.kotlinx.coroutines)
} }
jvmMain.dependencies {} jvmMain.dependencies {}
nativeMain.dependencies {
implementation(libs.ktor.network) // nativeMain.dependencies {
implementation(libs.kotlinx.io) // implementation(libs.ktor.network)
} // }
commonTest.dependencies { commonTest.dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.coroutines.test)
} }
jvmTest.dependencies {
}
} }
compilerOptions.freeCompilerArgs.addAll("-Xexpect-actual-classes") compilerOptions.freeCompilerArgs.addAll("-Xexpect-actual-classes")
@@ -1,14 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc
/**
* An object class used to pass some parameters
* that exists only on native targets
*/
public expect class LibMCContext()
@@ -8,6 +8,6 @@
package cn.rtast.libmc.crypto package cn.rtast.libmc.crypto
public interface NetworkCipher { public interface NetworkCipher {
public suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int) public fun encrypt(buffer: ByteArray, offset: Int, length: Int)
public suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int) public fun decrypt(buffer: ByteArray, offset: Int, length: Int)
} }
@@ -7,18 +7,23 @@
package cn.rtast.libmc.crypto package cn.rtast.libmc.crypto
import cn.rtast.libmc.network.SocketContext
import cn.rtast.libmc.network.SocketEngine
public data class ProtocolContext( public data class ProtocolContext(
val rsaEncryptor: RSA1024Encryptor, val rsaEncryptor: RSA1024Encryptor,
val sha1Hasher: Sha1Hasher, val sha1Hasher: Sha1Hasher,
val cipherFactory: (sharedKey: ByteArray) -> NetworkCipher, val cipherFactory: (sharedKey: ByteArray) -> NetworkCipher,
val authProvider: AuthenticationProvider?, val authProvider: AuthenticationProvider?,
) override val engine: SocketEngine,
) : SocketContext()
public class ProtocolContextBuilder(private val onlineMode: Boolean) { public class ProtocolContextBuilder(private val onlineMode: Boolean) {
public lateinit var rsaEncryptor: RSA1024Encryptor public lateinit var rsaEncryptor: RSA1024Encryptor
public lateinit var sha1Hasher: Sha1Hasher public lateinit var sha1Hasher: Sha1Hasher
public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher
public lateinit var authProvider: AuthenticationProvider public lateinit var authProvider: AuthenticationProvider
public lateinit var socketEngine: SocketEngine
public fun build(): ProtocolContext = public fun build(): ProtocolContext =
ProtocolContext( ProtocolContext(
@@ -27,14 +32,15 @@ public class ProtocolContextBuilder(private val onlineMode: Boolean) {
cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"), cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"),
authProvider = if (onlineMode) { authProvider = if (onlineMode) {
if (::authProvider.isInitialized) authProvider else error("authProvider is required in online mode") if (::authProvider.isInitialized) authProvider else error("authProvider is required in online mode")
} else if (::authProvider.isInitialized) authProvider else null } else if (::authProvider.isInitialized) authProvider else null,
engine = if (::socketEngine.isInitialized) socketEngine else error("SocketEngine is not configured")
) )
} }
public fun interface RSA1024Encryptor { public fun interface RSA1024Encryptor {
public suspend fun encrypt(key: ByteArray, data: ByteArray): ByteArray public fun encrypt(key: ByteArray, data: ByteArray): ByteArray
} }
public fun interface Sha1Hasher { public fun interface Sha1Hasher {
public suspend fun hash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String public fun hash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String
} }
@@ -5,7 +5,7 @@
*/ */
package cn.rtast.libmc.stream package cn.rtast.libmc.network
public enum class ByteOrder { public enum class ByteOrder {
BIG_ENDIAN, LITTLE_ENDIAN BIG_ENDIAN, LITTLE_ENDIAN
@@ -0,0 +1,47 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.network
import kotlinx.io.*
public class BytesBuffer {
private val _buffer = Buffer()
public constructor()
public constructor(bytes: ByteArray) {
this._buffer.write(bytes)
}
public fun writeByte(value: Byte): Unit = _buffer.writeByte(value)
public fun writeShort(value: Short): Unit = _buffer.writeShort(value)
public fun writeInt(value: Int): Unit = _buffer.writeInt(value)
public fun writeLong(value: Long): Unit = _buffer.writeLong(value)
public fun writeDouble(value: Double): Unit = _buffer.writeDouble(value)
public fun writeFloat(value: Float): Unit = _buffer.writeFloat(value)
public fun writeBytes(bytes: ByteArray): Unit = _buffer.write(bytes)
public fun writeBoolean(value: Boolean): Unit = _buffer.writeByte(if (value) 0x01 else 0x00)
public fun readByte(): Byte = _buffer.readByte()
public fun readUByte(): UByte = _buffer.readUByte()
public fun readShort(): Short = _buffer.readShort()
public fun readInt(): Int = _buffer.readInt()
public fun readLong(): Long = _buffer.readLong()
public fun readDouble(): Double = _buffer.readDouble()
public fun readFloat(): Float = _buffer.readFloat()
public fun readBytes(length: Int): ByteArray = _buffer.readByteArray(length)
public fun readBoolean(): Boolean = _buffer.readByte() != 0x00.toByte()
public fun toByteArray(): ByteArray = _buffer.readByteArray()
public fun peek(): ByteArray = _buffer.peek().readByteArray()
public fun close(): Unit = _buffer.close()
public val size: Int get() = _buffer.size.toInt()
}
@Suppress("NOTHING_TO_INLINE")
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
@@ -0,0 +1,30 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.network
public interface RawSocket : AutoCloseable {
/**
* Open socket connection
*/
public suspend fun connect()
/**
* An abstract function, used to open tcp socket read channel
*/
public fun openReadChannel(): ReadChannel
/**
* An abstract function, used to open tcp socket write/send channel
*/
public fun openWriteChannel(): WriteChannel
/**
* Close socket
*/
override fun close()
}
@@ -0,0 +1,14 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.network
public interface ReadChannel {
public suspend fun readByte(): Byte
public suspend fun readBytes(length: Int): ByteArray
public suspend fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
}
@@ -0,0 +1,14 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.network
public abstract class SocketContext {
public abstract val engine: SocketEngine
public fun createSocket(host: String, port: Int): RawSocket = engine.create(host, port)
}
@@ -0,0 +1,12 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.network
public fun interface SocketEngine {
public fun create(host: String, port: Int): RawSocket
}
@@ -0,0 +1,13 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.network
public interface WriteChannel {
public suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
public suspend fun flush()
}
@@ -7,20 +7,20 @@
package cn.rtast.libmc.packet package cn.rtast.libmc.packet
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public interface Encoder<in T> { public interface Encoder<in T> {
public suspend fun encode(buffer: BytesBuffer, value: T) public fun encode(buffer: BytesBuffer, value: T)
} }
public interface Decoder<out T> { public interface Decoder<out T> {
public suspend fun decode(buffer: BytesBuffer): T public fun decode(buffer: BytesBuffer): T
} }
public interface PacketCodec<T> : Encoder<T>, Decoder<T> public interface PacketCodec<T> : Encoder<T>, Decoder<T>
public suspend fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) { public fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Int = source.size) {
if (length <= 0) return if (length <= 0) return
val bytes = source.readBytes(length.toInt()) val bytes = source.readBytes(length)
this.writeBytes(bytes) this.writeBytes(bytes)
} }
@@ -8,7 +8,7 @@
package cn.rtast.libmc.packet package cn.rtast.libmc.packet
import cn.rtast.libmc.primitives.writeVarInt import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class PacketRegistry { public class PacketRegistry {
@@ -26,10 +26,10 @@ public class PacketRegistry {
register(id, T::class, codec) register(id, T::class, codec)
} }
public suspend fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket = public fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
idToCodec[packetId]?.decode(buffer) ?: ClientboundUnknownPacket(packetId, buffer.readRemainingBytes()) idToCodec[packetId]?.decode(buffer) ?: ClientboundUnknownPacket(packetId, buffer.toByteArray())
public suspend fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) { public fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val info = requireNotNull(classToInfo[packet::class]) { val info = requireNotNull(classToInfo[packet::class]) {
"Unregistered Packet ${packet::class.simpleName}" "Unregistered Packet ${packet::class.simpleName}"
@@ -7,16 +7,16 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public typealias BitSet = LongArray public typealias BitSet = LongArray
public suspend fun BytesBuffer.readBitSet(): BitSet { public fun BytesBuffer.readBitSet(): BitSet {
val count = this.readVarInt() val count = this.readVarInt()
return LongArray(count) { this.readLong() } return LongArray(count) { this.readLong() }
} }
public suspend fun BytesBuffer.writeBitSet(data: BitSet) { public fun BytesBuffer.writeBitSet(data: BitSet) {
this.writeVarInt(data.size) this.writeVarInt(data.size)
for (i in data.indices) this.writeLong(data[i]) for (i in data.indices) this.writeLong(data[i])
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public sealed class Either<out L, out R> { public sealed class Either<out L, out R> {
public data class Left<out L>(val value: L) : Either<L, Nothing>() public data class Left<out L>(val value: L) : Either<L, Nothing>()
@@ -17,7 +17,7 @@ public sealed class Either<out L, out R> {
public val isRight: Boolean get() = this is Right public val isRight: Boolean get() = this is Right
} }
public suspend inline fun <L, R> BytesBuffer.readEither( public inline fun <L, R> BytesBuffer.readEither(
readLeft: BytesBuffer.() -> L, readLeft: BytesBuffer.() -> L,
readRight: BytesBuffer.() -> R, readRight: BytesBuffer.() -> R,
): Either<L, R> { ): Either<L, R> {
@@ -25,7 +25,7 @@ public suspend inline fun <L, R> BytesBuffer.readEither(
return if (isLeft) Either.Left(readLeft(this)) else Either.Right(readRight(this)) return if (isLeft) Either.Left(readLeft(this)) else Either.Right(readRight(this))
} }
public suspend inline fun <L, R> BytesBuffer.writeEither( public inline fun <L, R> BytesBuffer.writeEither(
either: Either<L, R>, either: Either<L, R>,
writeLeft: BytesBuffer.(L) -> Unit, writeLeft: BytesBuffer.(L) -> Unit,
writeRight: BytesBuffer.(R) -> Unit, writeRight: BytesBuffer.(R) -> Unit,
@@ -7,14 +7,14 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public sealed interface IdOrX<out T> { public sealed interface IdOrX<out T> {
public data class Inline<T>(val value: T) : IdOrX<T> public data class Inline<T>(val value: T) : IdOrX<T>
public data class Reference(val registryId: Int) : IdOrX<Nothing> public data class Reference(val registryId: Int) : IdOrX<Nothing>
} }
public suspend inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) { public inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
when (value) { when (value) {
is IdOrX.Inline -> { is IdOrX.Inline -> {
this.writeVarInt(0) this.writeVarInt(0)
@@ -25,7 +25,7 @@ public suspend inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: By
} }
} }
public suspend inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> { public inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> {
val id = this.readVarInt() val id = this.readVarInt()
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1) return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
} }
@@ -7,14 +7,14 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public sealed interface IdSet { public sealed interface IdSet {
public data class Tag(val tagName: String) : IdSet public data class Tag(val tagName: String) : IdSet
public data class Entries(val ids: List<Int>) : IdSet public data class Entries(val ids: List<Int>) : IdSet
} }
public suspend fun BytesBuffer.readIdSet(): IdSet { public fun BytesBuffer.readIdSet(): IdSet {
val type = this.readVarInt() val type = this.readVarInt()
return if (type == 0) { return if (type == 0) {
IdSet.Tag(tagName = this.readMcString()) IdSet.Tag(tagName = this.readMcString())
@@ -26,7 +26,7 @@ public suspend fun BytesBuffer.readIdSet(): IdSet {
} }
} }
public suspend fun BytesBuffer.writeIdSet(idSet: IdSet) { public fun BytesBuffer.writeIdSet(idSet: IdSet) {
when (idSet) { when (idSet) {
is IdSet.Tag -> { is IdSet.Tag -> {
this.writeVarInt(0) this.writeVarInt(0)
@@ -8,21 +8,21 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public object McStringCodec : PacketCodec<String> { public object McStringCodec : PacketCodec<String> {
override suspend fun encode(buffer: BytesBuffer, value: String) { override fun encode(buffer: BytesBuffer, value: String) {
val bytes = value.encodeToByteArray() val bytes = value.encodeToByteArray()
VarIntCodec.encode(buffer, bytes.size) VarIntCodec.encode(buffer, bytes.size)
buffer.writeBytes(bytes) buffer.writeBytes(bytes)
} }
override suspend fun decode(buffer: BytesBuffer): String { override fun decode(buffer: BytesBuffer): String {
val length = VarIntCodec.decode(buffer) val length = VarIntCodec.decode(buffer)
val bytes = buffer.readBytes(length) val bytes = buffer.readBytes(length)
return bytes.decodeToString() return bytes.decodeToString()
} }
} }
public suspend fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value) public fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
public suspend fun BytesBuffer.readMcString(): String = McStringCodec.decode(this) public fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
@@ -7,9 +7,9 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public suspend inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? { public inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? {
val hasData = this.readBoolean() val hasData = this.readBoolean()
return if (hasData) block.invoke(this) else null return if (hasData) block.invoke(this) else null
} }
@@ -17,7 +17,7 @@ public suspend inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() ->
/** /**
* buffer.writeOptional(value.someValue) { writeBlockPos(it) } * buffer.writeOptional(value.someValue) { writeBlockPos(it) }
*/ */
public suspend inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) { public inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
if (value != null) { if (value != null) {
this.writeBoolean(true) this.writeBoolean(true)
block.invoke(this, value) block.invoke(this, value)
@@ -7,21 +7,21 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public suspend fun BytesBuffer.readPrefixedByteArray(): ByteArray { public fun BytesBuffer.readPrefixedByteArray(): ByteArray {
val length = this.readVarInt() val length = this.readVarInt()
val data = this.readBytes(length) val data = this.readBytes(length)
return data return data
} }
public suspend fun BytesBuffer.writePrefixedByteArray(data: ByteArray) { public fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
this.writeVarInt(data.size) this.writeVarInt(data.size)
this.writeBytes(data) this.writeBytes(data)
} }
public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) { public fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
if (data != null) { if (data != null) {
this.writeBoolean(true) this.writeBoolean(true)
this.writeVarInt(data.size) this.writeVarInt(data.size)
@@ -29,21 +29,21 @@ public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?)
} else this.writeBoolean(false) } else this.writeBoolean(false)
} }
public suspend fun BytesBuffer.readPrefixedStringArray(): List<String> { public fun BytesBuffer.readPrefixedStringArray(): List<String> {
val length = readVarInt() val length = readVarInt()
val list = ArrayList<String>(length) val list = ArrayList<String>(length)
repeat(length) { list.add(readMcString()) } repeat(length) { list.add(readMcString()) }
return list return list
} }
public suspend fun BytesBuffer.writePrefixedStringArray(value: List<String>) { public fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
writeVarInt(value.size) writeVarInt(value.size)
for (item in value) writeMcString(item) for (item in value) writeMcString(item)
} }
public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> { public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
val count = this.readVarInt() val count = this.readVarInt()
require(count in 0..4096) { require(count in 0..4096) {
"Prefixed array count $count is invalid (expected 0..4096). " + "Prefixed array count $count is invalid (expected 0..4096). " +
@@ -54,7 +54,7 @@ public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() ->
return list return list
} }
public suspend inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) { public inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) {
this.writeVarInt(list.size) this.writeVarInt(list.size)
for (item in list) this.writer(item) for (item in list) this.writer(item)
} }
@@ -7,15 +7,15 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
public suspend fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits -> public fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits ->
this.writeLong(mostSignificantBits) this.writeLong(mostSignificantBits)
this.writeLong(leastSignificantBits) this.writeLong(leastSignificantBits)
} }
public suspend fun BytesBuffer.readUuid(): Uuid { public fun BytesBuffer.readUuid(): Uuid {
val most = this.readLong() val most = this.readLong()
val least = this.readLong() val least = this.readLong()
return Uuid.fromLongs(most, least) return Uuid.fromLongs(most, least)
@@ -8,10 +8,11 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.network.ReadChannel
public object VarIntCodec : PacketCodec<Int> { public object VarIntCodec : PacketCodec<Int> {
override suspend fun encode(buffer: BytesBuffer, value: Int) { override fun encode(buffer: BytesBuffer, value: Int) {
var v = value var v = value
while (true) { while (true) {
if ((v and 0x7F.inv()) == 0) { if ((v and 0x7F.inv()) == 0) {
@@ -23,7 +24,7 @@ public object VarIntCodec : PacketCodec<Int> {
} }
} }
override suspend fun decode(buffer: BytesBuffer): Int { override fun decode(buffer: BytesBuffer): Int {
var numRead = 0 var numRead = 0
var result = 0 var result = 0
var read: Byte var read: Byte
@@ -38,8 +39,22 @@ public object VarIntCodec : PacketCodec<Int> {
} }
} }
public suspend fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value) public fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
public suspend fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this) public fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
public suspend fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value) public fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
public suspend fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this) public fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
public suspend fun ReadChannel.readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = 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
}
@@ -8,10 +8,10 @@
package cn.rtast.libmc.primitives package cn.rtast.libmc.primitives
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public object VarLongCodec : PacketCodec<Long> { public object VarLongCodec : PacketCodec<Long> {
override suspend fun encode(buffer: BytesBuffer, value: Long) { override fun encode(buffer: BytesBuffer, value: Long) {
var v = value var v = value
while (true) { while (true) {
if ((v and 0x7FL.inv()) == 0L) { if ((v and 0x7FL.inv()) == 0L) {
@@ -23,7 +23,7 @@ public object VarLongCodec : PacketCodec<Long> {
} }
} }
override suspend fun decode(buffer: BytesBuffer): Long { override fun decode(buffer: BytesBuffer): Long {
var numRead = 0 var numRead = 0
var result = 0L var result = 0L
var read: Byte var read: Byte
@@ -1,42 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.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)
@@ -1,28 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
/**
* Platform specified raw byte read channel
*/
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)
}
/**
* Platform specified raw byte write channel
*/
public expect open class WriteChannel() {
public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
public open suspend fun flush()
}
@@ -1,22 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import cn.rtast.libmc.LibMCContext
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
public fun openReadChannel(): ReadChannel
public fun openWriteChannel(): WriteChannel
public fun close()
}
public expect class UdpSocket public constructor(host: String, port: Int, context: LibMCContext) {
public suspend fun sendAndReceive(data: ByteArray): ByteArray
public fun close()
}
@@ -1,12 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc
/**
* No Context for jvm targets
*/
public actual class LibMCContext
@@ -1,132 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
public actual class BytesBuffer {
private val outStream = ByteArrayOutputStream()
private var readBuffer: ByteArray? = null
private var readOffset = 0
public actual constructor()
public actual constructor(bytes: ByteArray) {
this.readBuffer = bytes
outStream.write(bytes)
}
public actual suspend fun writeByte(value: Byte) {
readBuffer = null
outStream.write(value.toInt())
}
public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
readBuffer = null
val v = value.toInt()
if (endian == ByteOrder.BIG_ENDIAN) {
outStream.write(v shr 8)
outStream.write(v)
} else {
outStream.write(v)
outStream.write(v shr 8)
}
}
public actual suspend fun writeInt(value: Int, endian: ByteOrder) {
readBuffer = null
if (endian == ByteOrder.BIG_ENDIAN) {
outStream.write(value shr 24)
outStream.write(value shr 16)
outStream.write(value shr 8)
outStream.write(value)
} else {
outStream.write(value)
outStream.write(value shr 8)
outStream.write(value shr 16)
outStream.write(value shr 24)
}
}
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())
} else {
for (i in 0..56 step 8) outStream.write((value shr i).toInt())
}
}
public actual suspend fun writeDouble(value: Double, endian: ByteOrder) {
writeLong(value.toRawBits(), endian)
}
public actual suspend fun writeFloat(value: Float, endian: ByteOrder) {
writeInt(value.toRawBits(), endian)
}
public actual suspend fun writeBytes(bytes: ByteArray) {
readBuffer = null
withContext(Dispatchers.IO) { outStream.write(bytes) }
}
public actual suspend fun writeBoolean(value: Boolean): Unit = writeByte(if (value) 0x01 else 0x00)
private suspend fun ensureReadArray(): ByteArray {
var buf = readBuffer
if (buf == null) {
buf = outStream.toByteArray()
readBuffer = buf
}
return buf
}
public actual suspend fun readByte(): Byte {
val array = ensureReadArray()
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
return array[readOffset++]
}
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 suspend fun readDouble(endian: ByteOrder): Double {
return Double.fromBits(readLong(endian))
}
public actual suspend fun readFloat(endian: ByteOrder): Float {
return Float.fromBits(readInt(endian))
}
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)
readOffset += length
return result
}
public actual suspend fun readBoolean(): Boolean = this.readByte() != 0x00.toByte()
public actual suspend fun readRemainingBytes(): ByteArray {
val array = ensureReadArray()
if (readOffset >= array.size) return byteArrayOf()
val result = array.copyOfRange(readOffset, array.size)
readOffset = array.size
return result
}
public actual suspend fun toByteArray(): ByteArray = outStream.toByteArray()
public actual suspend fun hasRemaining(): Boolean = readOffset < ensureReadArray().size
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()
}
@@ -1,58 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import java.io.EOFException
import java.io.InputStream
import java.io.OutputStream
public actual open class ReadChannel public actual constructor() {
private lateinit var _inputStream: InputStream
public constructor(inputStream: InputStream) : this() {
this._inputStream = inputStream
}
public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int) {
var bytesRead = 0
val length = end - start
while (bytesRead < length) {
val read = _inputStream.read(out, start + bytesRead, length - bytesRead)
if (read == -1) throw EOFException("End of stream reached")
bytesRead += read
}
}
public actual open suspend fun readByte(): Byte {
val buf = ByteArray(1)
readFully(buf, 0, 1)
return buf[0]
}
public actual open suspend fun readBytes(length: Int): ByteArray {
val bytes = ByteArray(length)
readFully(bytes, 0, length)
return bytes
}
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() {
private lateinit var _outputStream: OutputStream
public constructor(outputStream: OutputStream) : this() {
_outputStream = outputStream
}
public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
_outputStream.write(value, startIndex, endIndex - startIndex)
public actual open suspend fun flush(): Unit = _outputStream.flush()
}
@@ -1,40 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
*/
package cn.rtast.libmc.stream
internal fun ByteArray.toShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short {
val b1 = this[0].toInt() and 0xFF
val b2 = this[1].toInt() and 0xFF
return if (endian == ByteOrder.BIG_ENDIAN) {
((b1 shl 8) or b2).toShort()
} else {
((b2 shl 8) or b1).toShort()
}
}
internal fun ByteArray.toInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int {
val b1 = this[0].toInt() and 0xFF
val b2 = this[1].toInt() and 0xFF
val b3 = this[2].toInt() and 0xFF
val b4 = this[3].toInt() and 0xFF
return if (endian == ByteOrder.BIG_ENDIAN) {
(b1 shl 24) or (b2 shl 16) or (b3 shl 8) or b4
} else {
(b4 shl 24) or (b3 shl 16) or (b2 shl 8) or b1
}
}
internal fun ByteArray.toLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long {
var result = 0L
if (endian == ByteOrder.BIG_ENDIAN) {
for (b in this) result = (result shl 8) or (b.toLong() and 0xFF)
} else {
for (i in 7 downTo 0) result = (result shl 8) or (this[i].toLong() and 0xFF)
}
return result
}
@@ -1,40 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import cn.rtast.libmc.LibMCContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import java.net.Socket as JvmSocket
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
private val socket = JvmSocket(host, port)
public actual fun openReadChannel(): ReadChannel = ReadChannel(socket.getInputStream())
public actual fun openWriteChannel(): WriteChannel = WriteChannel(socket.getOutputStream())
public actual fun close(): Unit = socket.close()
}
public actual class UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
private val socket = DatagramSocket().apply {
soTimeout = 3000
connect(InetSocketAddress(host, port))
}
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@withContext buf.copyOf(receivePacket.length)
}
public actual fun close(): Unit = socket.close()
}
@@ -1,23 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
@file:Suppress("PropertyName")
package cn.rtast.libmc
import io.ktor.network.selector.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
public actual class LibMCContext actual constructor() {
internal var _selectorManager: SelectorManager = SelectorManager(Dispatchers.IO)
internal var _autoCloseSelectorManager: Boolean = false
public constructor(selectorManager: SelectorManager, autoClose: Boolean = false) : this() {
_selectorManager = selectorManager
_autoCloseSelectorManager = autoClose
}
}
@@ -1,84 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import io.ktor.utils.io.core.*
import kotlinx.io.*
import kotlinx.io.Buffer
public actual class BytesBuffer {
private val _delegateBuf: Buffer
public actual constructor() {
_delegateBuf = Buffer()
}
public actual constructor(bytes: ByteArray) {
_delegateBuf = Buffer().apply { write(bytes) }
}
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 suspend fun writeInt(value: Int, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
else _delegateBuf.writeIntLe(value)
}
public actual suspend fun writeLong(value: Long, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
else _delegateBuf.writeLongLe(value)
}
public actual suspend fun writeDouble(value: Double, endian: ByteOrder): Unit =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeDouble(value) else _delegateBuf.writeDoubleLe(value)
public actual suspend fun writeFloat(value: Float, endian: ByteOrder): Unit =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeFloat(value) else _delegateBuf.writeFloatLe(value)
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 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 suspend fun readInt(endian: ByteOrder): Int =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe()
public actual suspend fun readLong(endian: ByteOrder): Long =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe()
public actual suspend fun readDouble(endian: ByteOrder): Double =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readDouble() else _delegateBuf.readDoubleLe()
public actual suspend fun readFloat(endian: ByteOrder): Float =
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readFloat() else _delegateBuf.readFloatLe()
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 suspend fun toByteArray(): ByteArray {
val copy = _delegateBuf.peek()
return try {
copy.readByteArray()
} finally {
copy.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
}
@@ -1,61 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import io.ktor.utils.io.*
import io.ktor.utils.io.bits.*
public actual open class ReadChannel public actual constructor() {
private lateinit var _readChannel: ByteReadChannel
public constructor(readChannel: ByteReadChannel) : this() {
this._readChannel = readChannel
}
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 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 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
(bytes[2].toInt() and 0xFF shl 8) or
(bytes[3].toInt() and 0xFF)
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual open suspend fun readLong(endian: ByteOrder): Long {
val bytes = readBytes(8)
var v = 0L
for (i in 0 until 8) {
v = (v shl 8) or (bytes[i].toLong() and 0xFF)
}
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
}
public actual open class WriteChannel public actual constructor() {
private lateinit var _writeChannel: ByteWriteChannel
public constructor(writeChannel: ByteWriteChannel) : this() {
_writeChannel = writeChannel
}
public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
_writeChannel.writeFully(value, startIndex, endIndex)
public actual open suspend fun flush(): Unit = _writeChannel.flush()
}
@@ -1,46 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.stream
import cn.rtast.libmc.LibMCContext
import io.ktor.network.sockets.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.runBlocking
import kotlinx.io.readByteArray
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
private val ctx = context
private val socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) }
public actual fun openReadChannel(): ReadChannel = ReadChannel(socket.openReadChannel())
public actual fun openWriteChannel(): WriteChannel =
WriteChannel(socket.openWriteChannel(autoFlush = true))
public actual fun close() {
socket.close()
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
}
}
public actual class UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
private val ctx = context
// use bind to create an unconnected socket
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
private val remoteAddress = InetSocketAddress(host, port)
public actual suspend fun sendAndReceive(data: ByteArray): ByteArray {
val packet = buildPacket { writeFully(data) }
socket.send(Datagram(packet, remoteAddress))
return socket.receive().packet.readByteArray()
}
public actual fun close() {
socket.close()
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
}
}
-23
View File
@@ -1,23 +0,0 @@
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"))
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
}
}
@@ -1,110 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec
import kotlin.random.Random
private val RAKNET_MAGIC = byteArrayOf(
0x00, 0xFF.toByte(),
0xFF.toByte(), 0x00,
0xFE.toByte(), 0xFE.toByte(),
0xFE.toByte(), 0xFE.toByte(),
0xFD.toByte(), 0xFD.toByte(),
0xFD.toByte(), 0xFD.toByte(),
0x12, 0x34, 0x56, 0x78
)
internal interface MinecraftBedrockPacket {
val packetId: Byte
}
internal data class BedrockRequestPacket(
val time: Long,
val magic: ByteArray = RAKNET_MAGIC,
val guid: Long = Random.nextLong(),
) : MinecraftBedrockPacket {
override val packetId: Byte = 0x01
companion object Codec : PacketCodec<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 suspend fun decode(buffer: BytesBuffer): BedrockRequestPacket = throw UnsupportedOperationException()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as BedrockRequestPacket
if (time != other.time) return false
if (guid != other.guid) return false
if (packetId != other.packetId) return false
if (!magic.contentEquals(other.magic)) return false
return true
}
override fun hashCode(): Int {
var result = time.hashCode()
result = 31 * result + guid.hashCode()
result = 31 * result + packetId
result = 31 * result + magic.contentHashCode()
return result
}
}
internal data class BedrockResponsePacket(
override val packetId: Byte,
val time: Long,
val serverGuid: Long,
val magic: ByteArray,
val payload: String,
) : MinecraftBedrockPacket {
companion object Codec : PacketCodec<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()
val serverGuid = buffer.readLong()
val magic = buffer.readBytes(16)
if (!magic.contentEquals(RAKNET_MAGIC)) throw IllegalStateException("Invalid magic in response")
val payloadLength = buffer.readShort().toInt() and 0xFFFF
val payloadBytes = buffer.readBytes(payloadLength)
val payload = payloadBytes.decodeToString()
return BedrockResponsePacket(packetId, time, serverGuid, magic, payload)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as BedrockResponsePacket
if (packetId != other.packetId) return false
if (time != other.time) return false
if (serverGuid != other.serverGuid) return false
if (!magic.contentEquals(other.magic)) return false
if (payload != other.payload) return false
return true
}
override fun hashCode(): Int {
var result = packetId.toInt()
result = 31 * result + time.hashCode()
result = 31 * result + serverGuid.hashCode()
result = 31 * result + magic.contentHashCode()
result = 31 * result + payload.hashCode()
return result
}
}
@@ -1,19 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.stream.UdpSocket
internal suspend fun <T : MinecraftBedrockPacket> UdpSocket.sendPacket(packet: T, codec: PacketCodec<T>): ByteArray {
val buf = BytesBuffer()
codec.encode(buf, packet)
return sendAndReceive(buf.toByteArray())
}
@@ -1,29 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.stream.UdpSocket
import cn.rtast.libmc.stream.wrap
import cn.rtast.libmc.mcping.PingResponse
import kotlin.time.Clock
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()
val requestPacket = BedrockRequestPacket(sendTime)
val responseBytes = socket.sendPacket(requestPacket, BedrockRequestPacket)
val receiveTime = Clock.System.now().toEpochMilliseconds()
val responsePacket = BedrockResponsePacket.decode(responseBytes.wrap())
val latency = (receiveTime - sendTime).toInt()
PingResponse(responsePacket.payload, latency)
} finally {
socket.close()
}
}
@@ -1,116 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.mcping.PingResponse
public enum class BedrockGameMode(public val gameMode: String) {
Survival("Survival"),
Creative("Creative"),
Adventure("Adventure"),
Spectator("Spectator"), // reserved?
Hardcore("Hardcore"),
Unknown(""); // reserved
public companion object {
public fun parse(value: String?): BedrockGameMode {
return entries.firstOrNull {
it.name.equals(value, ignoreCase = true)
} ?: Unknown
}
}
}
public data class BedrockPingResponse(
/**
* always be `MCPE`
* index 0
*/
val protocolHeader: String,
/**
* MOTD line 1
* index 1
*/
val motdLine1: String,
/**
* protocol version
* index 2
*/
val protocolVersion: Int,
/**
* game version
* index 3
*/
val gameVersion: String,
/**
* online players
* index 4
*/
val onlinePlayers: Int,
/**
* maximum players
* index 5
*/
val maximumPlayers: Int,
/**
* server GUID
* parsed as String -> origin bytes count is 8
* index 6
*/
val serverGUID: String,
/**
* MOTD line 2
* index 7
*/
val motdLine2: String,
/**
* game mode
* index 8
*/
val gameMode: BedrockGameMode,
/**
* Nintendo Limited
* Nintendo Switch online restriction flag (1 indicates restriction enabled/processed)
* index 9
*/
val nintendoLimited: Boolean,
/**
* ipv4 port
* if disabled or unconfigured, it will be 0
* index 10
*/
val ipv4Port: Int,
/**
* ipv6 port
* if disabled or unconfigured, it will be 0
* index 11
*/
val ipv6Port: Int,
/**
* latency ms
* reserved not exists in response packet
*/
val latency: Int,
)
internal fun PingResponse.parseBedrockPingResponse(): BedrockPingResponse {
try {
val fields = content.split(";")
return BedrockPingResponse(
fields[0], fields[1], fields[2].toInt(),
fields[3], fields[4].toInt(),
fields[5].toInt(), fields[6],
fields[7], BedrockGameMode.parse(fields[8]),
fields.getOrNull(9) == "1", fields[10].toInt(),
fields[11].toInt(), this.latency // pass through PingResponse.latency
)
} catch (e: Exception) {
e.printStackTrace()
throw IllegalStateException("The server respond incorrect response: Missing fields or type mismatch")
}
}
@@ -1,31 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.stream.ReadChannel
import cn.rtast.libmc.stream.wrap
private 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
}
public suspend fun ReadChannel.readPacketFrame(): BytesBuffer {
val length = this.readVarInt()
return this.readBytes(length).wrap()
}
@@ -1,52 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.primitives.McStringCodec
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.VarIntCodec
import cn.rtast.libmc.packet.MinecraftPacket
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
internal data class HandshakePacket(
val protocolVersion: Int,
val serverAddress: String,
val serverPort: UShort,
// 1 -> Status, 2 -> Login
val nextState: Int,
) : MinecraftPacket {
companion object Codec : PacketCodec<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 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 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 suspend fun encode(buffer: BytesBuffer, value: PingPacket) {
buffer.writeLong(value.currentTime)
}
override suspend fun decode(buffer: BytesBuffer): PingPacket {
return PingPacket(buffer.readLong())
}
}
}
@@ -1,54 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.mcping.PingResponse
import cn.rtast.libmc.mcping.sendPacket
import cn.rtast.libmc.primitives.McStringCodec
import cn.rtast.libmc.primitives.VarIntCodec
import cn.rtast.libmc.stream.Socket
import kotlin.time.Clock
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()
return try {
val handshakePacket = HandshakePacket(
protocolVersion = -1,
serverAddress = host,
serverPort = port.toUShort(),
nextState = 1
)
sendChannel.sendPacket(handshakePacket, 0x00, HandshakePacket)
sendChannel.sendPacket(StatusRequestPacket, 0x00, StatusRequestPacket)
val statusFrameBuffer = receiveChannel.readPacketFrame()
val statusPacketId = VarIntCodec.decode(statusFrameBuffer)
if (statusPacketId != 0x00) {
throw IllegalStateException("Expected StatusResponse packet ID 0x00, got $statusPacketId")
}
val jsonResponse = McStringCodec.decode(statusFrameBuffer)
val sendTime = Clock.System.now().toEpochMilliseconds()
val pingPacket = PingPacket(sendTime)
sendChannel.sendPacket(pingPacket, 0x01, PingPacket)
val pongFrameBuffer = receiveChannel.readPacketFrame()
val pongPacketId = VarIntCodec.decode(pongFrameBuffer)
if (pongPacketId != 0x01) {
throw IllegalStateException("Expected Pong packet ID 0x01, got $pongPacketId")
}
val latency = (Clock.System.now().toEpochMilliseconds() - sendTime).toInt()
PingResponse(jsonResponse, latency)
} finally {
socket.close()
}
}
@@ -1,27 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
@file:JvmName("McPing")
package cn.rtast.libmc.mcping
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.mcping.bedrock.pingBedrockServer
import cn.rtast.libmc.mcping.java.pingJavaServer
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
@JvmOverloads
public suspend fun mcping(
host: String,
port: Int,
type: ServerType = ServerType.Java,
context: LibMCContext = LibMCContext(),
): PingResponse = when (type) {
ServerType.Java -> pingJavaServer(host, port, context)
ServerType.Bedrock -> pingBedrockServer(host, port, context)
}
@@ -1,27 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/5
*/
package cn.rtast.libmc.mcping
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.primitives.VarIntCodec
import cn.rtast.libmc.stream.WriteChannel
import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.writeBuffer
public suspend fun <T : MinecraftPacket> WriteChannel.sendPacket(packet: T, packetId: Int, codec: PacketCodec<T>) {
val bodyBuffer = BytesBuffer()
VarIntCodec.encode(bodyBuffer, packetId)
codec.encode(bodyBuffer, packet)
val frameBuffer = BytesBuffer()
VarIntCodec.encode(bodyBuffer, bodyBuffer.size)
frameBuffer.writeBuffer(bodyBuffer)
val bytes = frameBuffer.toByteArray()
this.writeFully(bytes, 0, bytes.size)
this.flush()
}
@@ -1,24 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping
import cn.rtast.libmc.mcping.bedrock.BedrockPingResponse
import cn.rtast.libmc.mcping.bedrock.parseBedrockPingResponse
public data class PingResponse(
/**
* raw response content
*/
public val content: String,
/**
* latency ms
*/
public val latency: Int,
) {
public fun toBedrockResponse(): BedrockPingResponse = parseBedrockPingResponse()
}
@@ -1,12 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package cn.rtast.libmc.mcping
public enum class ServerType {
Java, Bedrock
}
@@ -1,29 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package test
import cn.rtast.libmc.mcping.ServerType
import cn.rtast.libmc.mcping.mcping
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
class TestPing {
@Test
fun `test ping java server`() = runTest {
val response = mcping("org.mc-complex.com", 25565)
println(response)
}
@Test
fun `test ping bedrock server`() = runTest {
val response = mcping("play.wildnetwork.net", 19132, ServerType.Bedrock)
println(response)
println(response.toBedrockResponse())
}
}
@@ -1 +0,0 @@
MCPE;" &e&lWILD&f&lNETWORK &6&lS24 &8-&e discord.gg/WildPrison\n&a Release: &f531d 18h 19m 18s ago";2169;26.45;393;1500;11173189777763089377;Another Geyser server.;Survival;1;19132;0;
File diff suppressed because one or more lines are too long.
@@ -1,33 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package test;
import cn.rtast.libmc.mcping.McPing;
import cn.rtast.libmc.mcping.PingResponse;
import cn.rtast.libmc.mcping.ServerType;
import org.junit.Test;
public class TestPingInJava {
@Test
public void testPingJava() {
String testJavaHost = "org.mc-complex.com";
PingResponse resp = McPing.mcping(testJavaHost, 25565);
System.out.println(resp);
}
@Test
public void testPingBedrock() {
String testBedrockHost = "play.wildnetwork.net";
PingResponse resp = McPing.mcping(testBedrockHost, 19132, ServerType.Bedrock);
System.out.println(resp.getContent());
System.out.println(resp.getLatency());
System.out.println(resp.toBedrockResponse());
}
}
@@ -1,37 +0,0 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/3
*/
package test
import cn.rtast.libmc.common.LibMCContext
import cn.rtast.libmc.mcping.ServerType
import cn.rtast.libmc.mcping.mcping
import io.ktor.network.selector.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
class TestMingwPing {
@Test
fun `test ping java on mingw with selectorManager`() = runTest {
val sm = SelectorManager(Dispatchers.IO)
val response =
mcping(host = "org.mc-complex.com", port = 25565, type = ServerType.Java, context = LibMCContext(sm))
println(response)
}
@Test
fun `test ping bedrock on mingw with selectorManager`() = runTest {
val sm = SelectorManager(Dispatchers.IO)
val response =
mcping(host = "play.wildnetwork.net", port = 19132, type = ServerType.Bedrock, context = LibMCContext(sm))
println(response)
println(response.toBedrockResponse())
}
}
@@ -7,12 +7,12 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.stream.ByteOrder import cn.rtast.libmc.network.ByteOrder
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput { public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput {
override suspend fun readByte(): Byte = buffer.readByte() override fun readByte(): Byte = buffer.readByte()
override suspend fun readBytes(count: Int): ByteArray = buffer.readBytes(count) override fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
} }
public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput = public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput =
@@ -7,8 +7,8 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.stream.ByteOrder import cn.rtast.libmc.network.ByteOrder
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public class BytesBufferNBTOutput( public class BytesBufferNBTOutput(
@@ -16,9 +16,9 @@ public class BytesBufferNBTOutput(
override val root: NBTTag.CompoundTag, override val root: NBTTag.CompoundTag,
private val buffer: BytesBuffer, private val buffer: BytesBuffer,
) : NBTOutput { ) : NBTOutput {
override suspend fun writeByte(value: Byte): Unit = buffer.writeByte(value) override fun writeByte(value: Byte): Unit = buffer.writeByte(value)
override suspend fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value) override fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
override suspend fun toByteArray(): ByteArray = buffer.toByteArray() override fun toByteArray(): ByteArray = buffer.toByteArray()
} }
public fun BytesBuffer.toNBTOutput( public fun BytesBuffer.toNBTOutput(
@@ -7,33 +7,33 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.stream.ByteOrder import cn.rtast.libmc.network.ByteOrder
public interface NBTInput { public interface NBTInput {
public val order: ByteOrder public val order: ByteOrder
public suspend fun readByte(): Byte public fun readByte(): Byte
public suspend fun readBytes(count: Int): ByteArray public fun readBytes(count: Int): ByteArray
public suspend fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE() public 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 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 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 fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE()
public suspend fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE() public fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE()
// big endian // big endian
public suspend fun readLongBE(): Long = public fun readLongBE(): Long =
(readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL) (readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL)
public suspend fun readFloatBE(): Float = Float.fromBits(readIntBE()) public fun readFloatBE(): Float = Float.fromBits(readIntBE())
public suspend fun readDoubleBE(): Double = Double.fromBits(readLongBE()) public fun readDoubleBE(): Double = Double.fromBits(readLongBE())
public suspend fun readShortBE(): Short { public fun readShortBE(): Short {
val b1 = readByte().toInt() and 0xFF val b1 = readByte().toInt() and 0xFF
val b2 = readByte().toInt() and 0xFF val b2 = readByte().toInt() and 0xFF
return ((b1 shl 8) or b2).toShort() return ((b1 shl 8) or b2).toShort()
} }
public suspend fun readIntBE(): Int { public fun readIntBE(): Int {
return ((readByte().toInt() and 0xFF) shl 24) or return ((readByte().toInt() and 0xFF) shl 24) or
((readByte().toInt() and 0xFF) shl 16) or ((readByte().toInt() and 0xFF) shl 16) or
((readByte().toInt() and 0xFF) shl 8) or ((readByte().toInt() and 0xFF) shl 8) or
@@ -41,7 +41,7 @@ public interface NBTInput {
} }
// little endian // little endian
public suspend fun readLongLE(): Long { public fun readLongLE(): Long {
return ((readByte().toInt() and 0xFF).toLong() shl 0) or return ((readByte().toInt() and 0xFF).toLong() shl 0) or
((readByte().toInt() and 0xFF).toLong() shl 8) or ((readByte().toInt() and 0xFF).toLong() shl 8) or
((readByte().toInt() and 0xFF).toLong() shl 16) or ((readByte().toInt() and 0xFF).toLong() shl 16) or
@@ -52,14 +52,14 @@ public interface NBTInput {
(readByte().toInt() and 0xFF).toLong() (readByte().toInt() and 0xFF).toLong()
} }
public suspend fun readFloatLE(): Float = Float.fromBits(readIntLE()) public fun readFloatLE(): Float = Float.fromBits(readIntLE())
public suspend fun readDoubleLE(): Double = Double.fromBits(readLongLE()) public fun readDoubleLE(): Double = Double.fromBits(readLongLE())
public suspend fun readShortLE(): Short = public fun readShortLE(): Short =
((readByte().toInt() and 0xFF) or (readByte().toInt() and 0xFF shl 8)).toShort() ((readByte().toInt() and 0xFF) or (readByte().toInt() and 0xFF shl 8)).toShort()
public suspend fun readIntLE(): Int = public fun readIntLE(): Int =
((readByte().toInt() and 0xFF)) or ((readByte().toInt() and 0xFF)) or
((readByte().toInt() and 0xFF) shl 8) or ((readByte().toInt() and 0xFF) shl 8) or
((readByte().toInt() and 0xFF) shl 16) or ((readByte().toInt() and 0xFF) shl 16) or
@@ -7,44 +7,44 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
import cn.rtast.libmc.stream.ByteOrder import cn.rtast.libmc.network.ByteOrder
public interface NBTOutput { public interface NBTOutput {
public val order: ByteOrder public val order: ByteOrder
public val root: NBTTag.CompoundTag public val root: NBTTag.CompoundTag
public suspend fun writeByte(value: Byte) public fun writeByte(value: Byte)
public suspend fun writeBytes(value: ByteArray) public fun writeBytes(value: ByteArray)
public suspend fun writeShort(value: Short): Unit = public fun writeShort(value: Short): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeShortBE(value) else writeShortLE(value) if (order == ByteOrder.BIG_ENDIAN) writeShortBE(value) else writeShortLE(value)
public suspend fun writeInt(value: Int): Unit = public fun writeInt(value: Int): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeIntBE(value) else writeIntLE(value) if (order == ByteOrder.BIG_ENDIAN) writeIntBE(value) else writeIntLE(value)
public suspend fun writeLong(value: Long): Unit = public fun writeLong(value: Long): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeLongBE(value) else writeLongLE(value) if (order == ByteOrder.BIG_ENDIAN) writeLongBE(value) else writeLongLE(value)
public suspend fun writeFloat(value: Float): Unit = public fun writeFloat(value: Float): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeFloatBE(value) else writeFloatLE(value) if (order == ByteOrder.BIG_ENDIAN) writeFloatBE(value) else writeFloatLE(value)
public suspend fun writeDouble(value: Double): Unit = public fun writeDouble(value: Double): Unit =
if (order == ByteOrder.BIG_ENDIAN) writeDoubleBE(value) else writeDoubleLE(value) if (order == ByteOrder.BIG_ENDIAN) writeDoubleBE(value) else writeDoubleLE(value)
// big endian // big endian
public suspend fun writeShortBE(value: Short) { public fun writeShortBE(value: Short) {
writeByte(((value.toInt() shr 8) and 0xFF).toByte()) writeByte(((value.toInt() shr 8) and 0xFF).toByte())
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
} }
public suspend fun writeIntBE(value: Int) { public fun writeIntBE(value: Int) {
writeByte(((value shr 24) and 0xFF).toByte()) writeByte(((value shr 24) and 0xFF).toByte())
writeByte(((value shr 16) and 0xFF).toByte()) writeByte(((value shr 16) and 0xFF).toByte())
writeByte(((value shr 8) and 0xFF).toByte()) writeByte(((value shr 8) and 0xFF).toByte())
writeByte((value and 0xFF).toByte()) writeByte((value and 0xFF).toByte())
} }
public suspend fun writeLongBE(value: Long) { public fun writeLongBE(value: Long) {
writeByte(((value shr 56).toInt() and 0xFF).toByte()) writeByte(((value shr 56).toInt() and 0xFF).toByte())
writeByte(((value shr 48).toInt() and 0xFF).toByte()) writeByte(((value shr 48).toInt() and 0xFF).toByte())
writeByte(((value shr 40).toInt() and 0xFF).toByte()) writeByte(((value shr 40).toInt() and 0xFF).toByte())
@@ -55,28 +55,28 @@ public interface NBTOutput {
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
} }
public suspend fun writeFloatBE(value: Float) { public fun writeFloatBE(value: Float) {
writeIntBE(value.toBits()) writeIntBE(value.toBits())
} }
public suspend fun writeDoubleBE(value: Double) { public fun writeDoubleBE(value: Double) {
writeLongBE(value.toBits()) writeLongBE(value.toBits())
} }
// Little Endian // Little Endian
public suspend fun writeShortLE(value: Short) { public fun writeShortLE(value: Short) {
writeByte((value.toInt() and 0xFF).toByte()) writeByte((value.toInt() and 0xFF).toByte())
writeByte(((value.toInt() shr 8) and 0xFF).toByte()) writeByte(((value.toInt() shr 8) and 0xFF).toByte())
} }
public suspend fun writeIntLE(value: Int) { public fun writeIntLE(value: Int) {
writeByte((value and 0xFF).toByte()) writeByte((value and 0xFF).toByte())
writeByte(((value shr 8) and 0xFF).toByte()) writeByte(((value shr 8) and 0xFF).toByte())
writeByte(((value shr 16) and 0xFF).toByte()) writeByte(((value shr 16) and 0xFF).toByte())
writeByte(((value shr 24) and 0xFF).toByte()) writeByte(((value shr 24) and 0xFF).toByte())
} }
public suspend fun writeLongLE(value: Long) { public fun writeLongLE(value: Long) {
writeByte(((value and 0xFF).toInt()).toByte()) writeByte(((value and 0xFF).toInt()).toByte())
writeByte((((value shr 8) and 0xFF).toInt()).toByte()) writeByte((((value shr 8) and 0xFF).toInt()).toByte())
writeByte((((value shr 16) and 0xFF).toInt()).toByte()) writeByte((((value shr 16) and 0xFF).toInt()).toByte())
@@ -87,7 +87,7 @@ public interface NBTOutput {
writeByte((((value shr 56) and 0xFF).toInt()).toByte()) writeByte((((value shr 56) and 0xFF).toInt()).toByte())
} }
public suspend fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits()) public fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
public suspend fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits()) public fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
public suspend fun toByteArray(): ByteArray public fun toByteArray(): ByteArray
} }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
public suspend fun NBTInput.readStringTag(): String { public fun NBTInput.readStringTag(): String {
val length = readShort().toInt() and 0xFFFF val length = readShort().toInt() and 0xFFFF
val bytes = readBytes(length) val bytes = readBytes(length)
return bytes.decodeToString() return bytes.decodeToString()
} }
public suspend fun NBTInput.readListTag(): NBTTag.ListTag { public fun NBTInput.readListTag(): NBTTag.ListTag {
val elementTypeId = readByte().toInt() and 0xFF val elementTypeId = readByte().toInt() and 0xFF
val elementType = NBTType.fromID(elementTypeId) val elementType = NBTType.fromID(elementTypeId)
val length = readInt() val length = readInt()
@@ -22,7 +22,7 @@ public suspend fun NBTInput.readListTag(): NBTTag.ListTag {
return NBTTag.ListTag(elementType, list) return NBTTag.ListTag(elementType, list)
} }
public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag { public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
val map = LinkedHashMap<String, NBTTag>() val map = LinkedHashMap<String, NBTTag>()
while (true) { while (true) {
val typeId = readByte().toInt() and 0xFF val typeId = readByte().toInt() and 0xFF
@@ -35,7 +35,7 @@ public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
return NBTTag.CompoundTag(map) return NBTTag.CompoundTag(map)
} }
public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag = public fun NBTInput.readTagPayload(type: NBTType): NBTTag =
when (type) { when (type) {
NBTType.Byte -> NBTTag.ByteTag(readByte()) NBTType.Byte -> NBTTag.ByteTag(readByte())
NBTType.Short -> NBTTag.ShortTag(readShort()) NBTType.Short -> NBTTag.ShortTag(readShort())
@@ -52,7 +52,7 @@ public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag =
NBTType.End -> error("TAG_End no payload") NBTType.End -> error("TAG_End no payload")
} }
public suspend fun NBTInput.readCompound(): NBTTag { public fun NBTInput.readCompound(): NBTTag {
val map = LinkedHashMap<String, NBTTag>() val map = LinkedHashMap<String, NBTTag>()
while (true) { while (true) {
val typeId = readByte().toInt() val typeId = readByte().toInt()
@@ -67,7 +67,7 @@ public suspend fun NBTInput.readCompound(): NBTTag {
return NBTTag.ListTag(NBTType.Compound, map.values.toMutableList()) return NBTTag.ListTag(NBTType.Compound, map.values.toMutableList())
} }
public suspend fun NBTInput.readRootCompound(): NBTCompound { public fun NBTInput.readRootCompound(): NBTCompound {
val rootType = NBTType.fromID(readByte().toInt()) val rootType = NBTType.fromID(readByte().toInt())
require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" } require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" }
val nameLen = readShort().toInt() and 0xFFFF val nameLen = readShort().toInt() and 0xFFFF
@@ -76,7 +76,7 @@ public suspend fun NBTInput.readRootCompound(): NBTCompound {
return NBTCompound(name, root) return NBTCompound(name, root)
} }
public suspend fun NBTInput.readNetworkCompound(): NBTCompound { public fun NBTInput.readNetworkCompound(): NBTCompound {
return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) { return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) {
NBTType.Compound -> NBTCompound("", readCompoundTag()) NBTType.Compound -> NBTCompound("", readCompoundTag())
NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag())))) NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag()))))
@@ -8,13 +8,13 @@
package cn.rtast.libmc.nbt package cn.rtast.libmc.nbt
public suspend fun NBTOutput.writeStringTag(value: String) { public fun NBTOutput.writeStringTag(value: String) {
val bytes = value.encodeToByteArray() val bytes = value.encodeToByteArray()
writeShort(bytes.size.toShort()) writeShort(bytes.size.toShort())
writeBytes(bytes) writeBytes(bytes)
} }
public suspend fun NBTOutput.writeTagPayload(tag: NBTTag) { public fun NBTOutput.writeTagPayload(tag: NBTTag) {
when (tag) { when (tag) {
is NBTTag.ByteTag -> writeByte(tag.value) is NBTTag.ByteTag -> writeByte(tag.value)
is NBTTag.ShortTag -> writeShort(tag.value) is NBTTag.ShortTag -> writeShort(tag.value)
@@ -56,14 +56,14 @@ public suspend fun NBTOutput.writeTagPayload(tag: NBTTag) {
} }
} }
public suspend fun NBTOutput.writeRootNBTCompound(name: String = ""): ByteArray { public fun NBTOutput.writeRootNBTCompound(name: String = ""): ByteArray {
writeByte(NBTType.Compound.id) writeByte(NBTType.Compound.id)
writeStringTag(name) writeStringTag(name)
writeTagPayload(root) writeTagPayload(root)
return toByteArray() return toByteArray()
} }
public suspend fun NBTOutput.writeNetworkCompound(compound: NBTCompound) { public fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
val compoundTag = compound.element as? NBTTag.CompoundTag val compoundTag = compound.element as? NBTTag.CompoundTag
?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag") ?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag")
writeByte(NBTType.Compound.id) writeByte(NBTType.Compound.id)
@@ -7,7 +7,7 @@
package test package test
import cn.rtast.libmc.stream.wrap import cn.rtast.libmc.network.wrap
import cn.rtast.libmc.nbt.NbtReader import cn.rtast.libmc.nbt.NbtReader
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
File renamed without changes.
@@ -16,6 +16,7 @@ kotlin {
implementation(libs.cryptography.core) implementation(libs.cryptography.core)
implementation(libs.cryptography.provider.optimal) implementation(libs.cryptography.provider.optimal)
implementation(libs.ktor.client.core) implementation(libs.ktor.client.core)
implementation(libs.ktor.network)
} }
jvmTest.dependencies { jvmTest.dependencies {
@@ -22,14 +22,14 @@ public class AesCFB8Cipher(sharedKey: ByteArray) : NetworkCipher {
.decodeFromByteArrayBlocking(AES.Key.Format.RAW, sharedKey) .decodeFromByteArrayBlocking(AES.Key.Format.RAW, sharedKey)
.cipher() .cipher()
override suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int) { override fun encrypt(buffer: ByteArray, offset: Int, length: Int) {
val plaintext = buffer.copyOfRange(offset, offset + length) val plaintext = buffer.copyOfRange(offset, offset + length)
val ciphertext = cipher.encryptWithIvBlocking(encryptIv, plaintext) val ciphertext = cipher.encryptWithIvBlocking(encryptIv, plaintext)
ciphertext.copyInto(buffer, destinationOffset = offset) ciphertext.copyInto(buffer, destinationOffset = offset)
updateIv(encryptIv, ciphertext) updateIv(encryptIv, ciphertext)
} }
override suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int) { override fun decrypt(buffer: ByteArray, offset: Int, length: Int) {
val ciphertext = buffer.copyOfRange(offset, offset + length) val ciphertext = buffer.copyOfRange(offset, offset + length)
val plaintext = cipher.decryptWithIvBlocking(decryptIv, ciphertext) val plaintext = cipher.decryptWithIvBlocking(decryptIv, ciphertext)
plaintext.copyInto(buffer, destinationOffset = offset) plaintext.copyInto(buffer, destinationOffset = offset)
@@ -28,4 +28,5 @@ public val DefaultProtocolContext: ProtocolContextBuilder.() -> Unit = {
}.status }.status
require(status == HttpStatusCode.NoContent) require(status == HttpStatusCode.NoContent)
} }
socketEngine = KtorNetworkEngine()
} }
@@ -0,0 +1,50 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/8
*/
package cn.rtast.libmc.protocol.crypto
import cn.rtast.libmc.network.RawSocket
import cn.rtast.libmc.network.ReadChannel
import cn.rtast.libmc.network.SocketEngine
import cn.rtast.libmc.network.WriteChannel
import io.ktor.network.selector.*
import io.ktor.network.sockets.*
import io.ktor.utils.io.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
public class KtorNetworkEngine : SocketEngine {
override fun create(host: String, port: Int): RawSocket = KtorNetworkSocket(host, port)
}
public class KtorNetworkSocket(private val host: String, private val port: Int) : RawSocket {
private val sm = SelectorManager(Dispatchers.IO)
private lateinit var socket: Socket
override suspend fun connect(): Unit = run { socket = aSocket(sm).tcp().connect(host, port) }
override fun openReadChannel(): ReadChannel = KtorReadChannel(socket.openReadChannel())
override fun openWriteChannel(): WriteChannel = KtorWriteChannel(socket.openWriteChannel())
override fun close() {
socket.close()
sm.close()
}
}
public class KtorReadChannel(private val readChannel: ByteReadChannel) : ReadChannel {
override suspend fun readByte(): Byte = readChannel.readByte()
override suspend fun readBytes(length: Int): ByteArray = readChannel.readByteArray(length)
override suspend fun readFully(out: ByteArray, start: Int, end: Int): Unit =
readChannel.readFully(out, start, end)
}
public class KtorWriteChannel(private val writeChannel: ByteWriteChannel) : WriteChannel {
override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
writeChannel.writeFully(value, startIndex, endIndex)
}
override suspend fun flush(): Unit = writeChannel.flush()
}
@@ -16,9 +16,10 @@ import dev.whyoleg.cryptography.algorithms.SHA1
internal val provider = CryptographyProvider.Default internal val provider = CryptographyProvider.Default
public suspend fun rsaEncrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray { public fun rsaEncrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray {
val provider = CryptographyProvider.Default val provider = CryptographyProvider.Default
val rsa = provider.get(RSA.PKCS1) val rsa = provider.get(RSA.PKCS1)
val publicKey = rsa.publicKeyDecoder(SHA1).decodeFromByteArray(RSA.PublicKey.Format.DER, publicKeyBytes) val publicKey = rsa.publicKeyDecoder(SHA1)
return publicKey.encryptor().encrypt(data) .decodeFromByteArrayBlocking(RSA.PublicKey.Format.DER, publicKeyBytes)
return publicKey.encryptor().encryptBlocking(data)
} }
@@ -12,11 +12,11 @@ package cn.rtast.libmc.protocol.crypto
import dev.whyoleg.cryptography.DelicateCryptographyApi import dev.whyoleg.cryptography.DelicateCryptographyApi
import dev.whyoleg.cryptography.algorithms.SHA1 import dev.whyoleg.cryptography.algorithms.SHA1
public suspend fun minecraftServerIdHash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String { public fun minecraftServerIdHash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String {
val serverIdBytes = serverId.encodeToByteArray() val serverIdBytes = serverId.encodeToByteArray()
for (b in serverIdBytes) require((b.toInt() and 0xFF) <= 0x7F) { "serverId contains non-US-ASCII character" } for (b in serverIdBytes) require((b.toInt() and 0xFF) <= 0x7F) { "serverId contains non-US-ASCII character" }
val data = serverIdBytes + secretKey + publicKey val data = serverIdBytes + secretKey + publicKey
val hash = provider.get(SHA1).hasher().hash(data) val hash = provider.get(SHA1).hasher().hashBlocking(data)
return mcDigestToString(hash) return mcDigestToString(hash)
} }
@@ -29,7 +29,7 @@ class TestJvmClient {
Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"), Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
// null, // null,
accessToken, accessToken,
crypto = DefaultProtocolContext contextBuilder = DefaultProtocolContext
) )
// cli.on<ClientboundSystemChatMessagePacket> { println(it) } // cli.on<ClientboundSystemChatMessagePacket> { println(it) }
// cli.on<ClientboundLoginSuccessPacket> { println(it) } // cli.on<ClientboundLoginSuccessPacket> { println(it) }
+1 -1
View File
@@ -22,7 +22,7 @@ kotlin {
commonTest.dependencies { commonTest.dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation(project(":protocol-encrypt")) implementation(project(":protocol-context"))
implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.coroutines.test)
} }
@@ -6,7 +6,6 @@
package cn.rtast.libmc.protocol.client package cn.rtast.libmc.protocol.client
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.crypto.ProtocolContext import cn.rtast.libmc.crypto.ProtocolContext
import cn.rtast.libmc.crypto.ProtocolContextBuilder import cn.rtast.libmc.crypto.ProtocolContextBuilder
import cn.rtast.libmc.protocol.event.InternalPacketDispatcher import cn.rtast.libmc.protocol.event.InternalPacketDispatcher
@@ -24,22 +23,23 @@ import kotlin.uuid.Uuid
public class MinecraftClient internal constructor( public class MinecraftClient internal constructor(
private val host: String, private val host: String,
private val port: Int = 25565, private val port: Int,
private val username: String, private val username: String,
internal val uuid: Uuid, internal val uuid: Uuid,
internal val accessToken: String?, internal val accessToken: String?,
context: LibMCContext,
parentJob: Job?, parentJob: Job?,
private val ioDispatcher: CoroutineDispatcher, private val ioDispatcher: CoroutineDispatcher,
cryptoContext: ProtocolContext, protocolContext: ProtocolContext,
) : PacketEventDispatcher(), CoroutineScope { ) : PacketEventDispatcher(), CoroutineScope {
internal val rsa1024Encryptor = cryptoContext.rsaEncryptor internal val rsa1024Encryptor = protocolContext.rsaEncryptor
internal val serverIdHasher = cryptoContext.sha1Hasher internal val serverIdHasher = protocolContext.sha1Hasher
internal val authProvider = cryptoContext.authProvider internal val authProvider = protocolContext.authProvider
internal val stateMachine = ClientStateMachine() internal val stateMachine = ClientStateMachine()
public val networkChannel: NetworkChannel = NetworkChannel( public val networkChannel: NetworkChannel = NetworkChannel(
host, port, context, stateMachine, cryptoContext.cipherFactory, this host, port, stateMachine,
protocolContext.cipherFactory,
this, protocolContext
) )
private val internalPacketDispatcher = InternalPacketDispatcher(this, authProvider) private val internalPacketDispatcher = InternalPacketDispatcher(this, authProvider)
@@ -95,21 +95,19 @@ public fun createMinecraftClient(
username: String, username: String,
uuid: Uuid = generateOfflineUuid(username), uuid: Uuid = generateOfflineUuid(username),
accessToken: String?, accessToken: String?,
context: LibMCContext = LibMCContext(),
parentJob: Job? = null, parentJob: Job? = null,
ioDispatcher: CoroutineDispatcher = Dispatchers.IO, ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
crypto: ProtocolContextBuilder.() -> Unit, contextBuilder: ProtocolContextBuilder.() -> Unit,
): MinecraftClient { ): MinecraftClient {
val cryptoContext = ProtocolContextBuilder(accessToken != null).apply(crypto).build() val context = ProtocolContextBuilder(accessToken != null).apply(contextBuilder).build()
return MinecraftClient( return MinecraftClient(
host = host, host = host,
port = port, port = port,
username = username, username = username,
uuid = uuid, uuid = uuid,
accessToken = accessToken, accessToken = accessToken,
context = context,
parentJob = parentJob, parentJob = parentJob,
ioDispatcher = ioDispatcher, ioDispatcher = ioDispatcher,
cryptoContext = cryptoContext protocolContext = context
) )
} }
@@ -8,8 +8,8 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.crypto.NetworkCipher import cn.rtast.libmc.crypto.NetworkCipher
import cn.rtast.libmc.stream.ReadChannel import cn.rtast.libmc.network.ReadChannel
import cn.rtast.libmc.stream.WriteChannel import cn.rtast.libmc.network.WriteChannel
/** /**
* AES-128-CFB8 ***ciphered*** read channel * AES-128-CFB8 ***ciphered*** read channel
@@ -17,7 +17,7 @@ import cn.rtast.libmc.stream.WriteChannel
internal class CipherReadChannel( internal class CipherReadChannel(
private val delegate: ReadChannel, private val delegate: ReadChannel,
private val crypto: NetworkCipher, private val crypto: NetworkCipher,
) : ReadChannel() { ) : ReadChannel {
override suspend fun readFully(out: ByteArray, start: Int, end: Int) { override suspend fun readFully(out: ByteArray, start: Int, end: Int) {
delegate.readFully(out, start, end) delegate.readFully(out, start, end)
val length = end - start val length = end - start
@@ -43,7 +43,7 @@ internal class CipherReadChannel(
internal class CipherWriteChannel( internal class CipherWriteChannel(
private val delegate: WriteChannel, private val delegate: WriteChannel,
private val crypto: NetworkCipher, private val crypto: NetworkCipher,
) : WriteChannel() { ) : WriteChannel {
override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) { override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
val length = endIndex - startIndex val length = endIndex - startIndex
if (length <= 0) return if (length <= 0) return
@@ -52,7 +52,5 @@ internal class CipherWriteChannel(
delegate.writeFully(encrypted, 0, length) delegate.writeFully(encrypted, 0, length)
} }
override suspend fun flush() { override suspend fun flush() = delegate.flush()
delegate.flush()
}
} }
@@ -6,17 +6,18 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.crypto.NetworkCipher import cn.rtast.libmc.crypto.NetworkCipher
import cn.rtast.libmc.crypto.ProtocolContext
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.network.wrap
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.writeBuffer import cn.rtast.libmc.packet.writeBuffer
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.client.ClientStateMachine import cn.rtast.libmc.protocol.client.ClientStateMachine
import cn.rtast.libmc.protocol.event.PacketEventDispatcher import cn.rtast.libmc.protocol.event.PacketEventDispatcher
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.clientboundGameProtocols
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.serverboundGameProtocols
import cn.rtast.libmc.stream.wrap
import cn.rtast.libmc.zlibCompress import cn.rtast.libmc.zlibCompress
import cn.rtast.libmc.zlibDecompress import cn.rtast.libmc.zlibDecompress
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
@@ -24,17 +25,17 @@ import kotlin.concurrent.Volatile
public class NetworkChannel internal constructor( public class NetworkChannel internal constructor(
host: String, host: String,
port: Int, port: Int,
context: LibMCContext,
private val stateMachine: ClientStateMachine, private val stateMachine: ClientStateMachine,
cipherProvider: (ByteArray) -> NetworkCipher, cipherProvider: (ByteArray) -> NetworkCipher,
private val dispatcher: PacketEventDispatcher, private val dispatcher: PacketEventDispatcher,
protocolContext: ProtocolContext,
) { ) {
internal val session: NetworkSession = NetworkSession(host, port, context, cipherProvider) internal val session: NetworkSession = NetworkSession(host, port, cipherProvider, protocolContext)
@Volatile @Volatile
private var threshold = -1 private var threshold = -1
public fun connect(): Unit = session.connect() public suspend fun connect(): Unit = session.connect()
public fun setCompression(threshold: Int): Unit = run { this.threshold = threshold } public fun setCompression(threshold: Int): Unit = run { this.threshold = threshold }
/** /**
@@ -44,18 +45,17 @@ public class NetworkChannel internal constructor(
*/ */
public suspend fun readNextPacket(): MinecraftPacket { public suspend fun readNextPacket(): MinecraftPacket {
val packetLength = session.readVarInt() val packetLength = session.readVarInt()
val rawFrameBytes = session.readBytes(packetLength) val frameBuf = session.readBytes(packetLength).wrap()
val frameBuf = rawFrameBytes.wrap()
val payloadBuf = if (threshold < 0) frameBuf else { val payloadBuf = if (threshold < 0) frameBuf else {
val dataLength = frameBuf.readVarInt() val dataLength = frameBuf.readVarInt()
val remainingBytes = frameBuf.readBytes(frameBuf.remaining.toInt()) if (dataLength == 0) frameBuf else {
if (dataLength == 0) remainingBytes.wrap() else remainingBytes.zlibDecompress(dataLength).wrap() val compressedBytes = frameBuf.toByteArray()
compressedBytes.zlibDecompress(dataLength).wrap()
}
} }
val currentState = stateMachine.currentState val currentState = stateMachine.currentState
val packetId = payloadBuf.readVarInt() val packetId = payloadBuf.readVarInt()
val packet = GamePacketsProtocolCodec.clientboundGameProtocols val packet = clientboundGameProtocols.getRegistry(currentState).decodePacket(packetId, payloadBuf)
.getRegistry(currentState)
.decodePacket(packetId, payloadBuf)
dispatcher.dispatchReceive(packet) dispatcher.dispatchReceive(packet)
return packet return packet
} }
@@ -67,9 +67,7 @@ public class NetworkChannel internal constructor(
*/ */
public suspend fun sendPacket(packet: MinecraftPacket) { public suspend fun sendPacket(packet: MinecraftPacket) {
val uncompressedBodyBuf = BytesBuffer() val uncompressedBodyBuf = BytesBuffer()
GamePacketsProtocolCodec.serverboundGameProtocols serverboundGameProtocols.getRegistry(stateMachine.currentState).encodePacket(uncompressedBodyBuf, packet)
.getRegistry(stateMachine.currentState)
.encodePacket(uncompressedBodyBuf, packet)
val uncompressedData = uncompressedBodyBuf.toByteArray() val uncompressedData = uncompressedBodyBuf.toByteArray()
val frameBuffer = BytesBuffer() val frameBuffer = BytesBuffer()
if (threshold < 0) { if (threshold < 0) {
@@ -6,19 +6,20 @@
package cn.rtast.libmc.protocol.network package cn.rtast.libmc.protocol.network
import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.crypto.NetworkCipher import cn.rtast.libmc.crypto.NetworkCipher
import cn.rtast.libmc.stream.ReadChannel import cn.rtast.libmc.crypto.ProtocolContext
import cn.rtast.libmc.stream.Socket import cn.rtast.libmc.network.RawSocket
import cn.rtast.libmc.stream.WriteChannel import cn.rtast.libmc.network.ReadChannel
import cn.rtast.libmc.network.WriteChannel
import cn.rtast.libmc.primitives.readVarInt
public class NetworkSession internal constructor( public class NetworkSession internal constructor(
private val host: String, private val host: String,
private val port: Int, private val port: Int,
private val context: LibMCContext,
private var cipherProvider: (ByteArray) -> NetworkCipher, private var cipherProvider: (ByteArray) -> NetworkCipher,
private val context: ProtocolContext,
) { ) {
private var socket: Socket? = null private var socket: RawSocket? = null
public var readChannel: ReadChannel? = null public var readChannel: ReadChannel? = null
private set private set
@@ -26,47 +27,28 @@ public class NetworkSession internal constructor(
public var writeChannel: WriteChannel? = null public var writeChannel: WriteChannel? = null
private set private set
public fun connect() { public suspend fun connect() {
val sk = Socket(host, port, context) val sk = context.createSocket(host, port)
sk.connect()
this.socket = sk this.socket = sk
this.readChannel = sk.openReadChannel() this.readChannel = sk.openReadChannel()
this.writeChannel = sk.openWriteChannel() this.writeChannel = sk.openWriteChannel()
} }
public fun enableEncryption(sharedKey: ByteArray) { public fun enableEncryption(sharedKey: ByteArray) {
val currentRead = requireNotNull(readChannel) { "ReadChannel not connected" } val currentRead = requireNotNull(readChannel)
val currentWrite = requireNotNull(writeChannel) { "WriteChannel not connected" } val currentWrite = requireNotNull(writeChannel)
val cipher = cipherProvider(sharedKey) val cipher = cipherProvider(sharedKey)
this.readChannel = CipherReadChannel(currentRead, cipher) this.readChannel = CipherReadChannel(currentRead, cipher)
this.writeChannel = CipherWriteChannel(currentWrite, cipher) this.writeChannel = CipherWriteChannel(currentWrite, cipher)
} }
internal suspend fun readByte(): Byte { internal suspend fun readBytes(length: Int): ByteArray = requireNotNull(readChannel).readBytes(length)
val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readByte()
}
internal suspend fun readBytes(length: Int): ByteArray { internal suspend fun readVarInt(): Int = requireNotNull(readChannel).readVarInt()
val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
return channel.readBytes(length)
}
internal suspend fun readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = 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
}
internal suspend fun writeFully(data: ByteArray) { internal suspend fun writeFully(data: ByteArray) {
val channel = requireNotNull(writeChannel) { "WriteChannel not connected" } val channel = requireNotNull(writeChannel)
channel.writeFully(data, 0, data.size) channel.writeFully(data, 0, data.size)
channel.flush() channel.flush()
} }
@@ -10,17 +10,17 @@ package cn.rtast.libmc.protocol.packet.configuration
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
import cn.rtast.libmc.primitives.writeMcString import cn.rtast.libmc.primitives.writeMcString
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public data class KnownPacks(val namespace: String, val id: String, val version: String) { public data class KnownPacks(val namespace: String, val id: String, val version: String) {
internal companion object Codec : PacketCodec<KnownPacks> { internal companion object Codec : PacketCodec<KnownPacks> {
override suspend fun encode(buffer: BytesBuffer, value: KnownPacks) { override fun encode(buffer: BytesBuffer, value: KnownPacks) {
buffer.writeMcString(value.namespace) buffer.writeMcString(value.namespace)
buffer.writeMcString(value.id) buffer.writeMcString(value.id)
buffer.writeMcString(value.version) buffer.writeMcString(value.version)
} }
override suspend fun decode(buffer: BytesBuffer): KnownPacks { override fun decode(buffer: BytesBuffer): KnownPacks {
val namespace = buffer.readMcString() val namespace = buffer.readMcString()
val id = buffer.readMcString() val id = buffer.readMcString()
val version = buffer.readMcString() val version = buffer.readMcString()
@@ -14,7 +14,7 @@ import cn.rtast.libmc.primitives.readUuid
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import kotlin.uuid.Uuid import kotlin.uuid.Uuid
public data class ClientboundAddResourcePackPacket( public data class ClientboundAddResourcePackPacket(
@@ -25,8 +25,8 @@ public data class ClientboundAddResourcePackPacket(
val prompt: TextComponent, val prompt: TextComponent,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> { internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket { override fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
val uuid = buffer.readUuid() val uuid = buffer.readUuid()
val url = buffer.readMcString() val url = buffer.readMcString()
val hash = buffer.readMcString() val hash = buffer.readMcString()
@@ -7,12 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
public data object ClientboundClearDialogPacket : MinecraftPacket, public data object ClientboundClearDialogPacket : MinecraftPacket,
PacketCodec<ClientboundClearDialogPacket> { PacketCodec<ClientboundClearDialogPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket override fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
} }
@@ -7,15 +7,15 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : MinecraftPacket { public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> { internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket { override fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
return ClientboundCodeOfConductPacket(buffer.readMcString()) return ClientboundCodeOfConductPacket(buffer.readMcString())
} }
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.nbt.NBTCompound import cn.rtast.libmc.nbt.NBTCompound
@@ -15,8 +15,8 @@ import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
public data class ClientboundConfigurationShowDialogPacket(val dialog: NBTCompound) : MinecraftPacket { public data class ClientboundConfigurationShowDialogPacket(val dialog: NBTCompound) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundConfigurationShowDialogPacket> { internal companion object Codec : PacketCodec<ClientboundConfigurationShowDialogPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket { override fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound()) return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound())
} }
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
@@ -15,8 +15,8 @@ import cn.rtast.libmc.protocol.protocol.game.readIdentifier
public data class ClientboundCookieRequestPacket(val key: Identifier) : MinecraftPacket { public data class ClientboundCookieRequestPacket(val key: Identifier) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCookieRequestPacket> { internal companion object Codec : PacketCodec<ClientboundCookieRequestPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket { override fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
return ClientboundCookieRequestPacket(key = buffer.readIdentifier()) return ClientboundCookieRequestPacket(key = buffer.readIdentifier())
} }
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
@@ -18,10 +18,10 @@ public data class ClientboundCustomPayloadPacket(
val data: ByteArray, val data: ByteArray,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> { internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket { override fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
val channel = buffer.readIdentifier() val channel = buffer.readIdentifier()
val data = buffer.readRemainingBytes() val data = buffer.toByteArray()
return ClientboundCustomPayloadPacket(channel, data) return ClientboundCustomPayloadPacket(channel, data)
} }
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
@@ -17,12 +17,12 @@ import cn.rtast.libmc.primitives.writeMcString
public data class ClientboundCustomReportDetailsPacket(val details: List<ReportDetail>) : MinecraftPacket { public data class ClientboundCustomReportDetailsPacket(val details: List<ReportDetail>) : MinecraftPacket {
public data class ReportDetail(val title: String, val description: String) { public data class ReportDetail(val title: String, val description: String) {
internal companion object Codec : PacketCodec<ReportDetail> { internal companion object Codec : PacketCodec<ReportDetail> {
override suspend fun encode(buffer: BytesBuffer, value: ReportDetail) { override fun encode(buffer: BytesBuffer, value: ReportDetail) {
buffer.writeMcString(value.title) buffer.writeMcString(value.title)
buffer.writeMcString(value.description) buffer.writeMcString(value.description)
} }
override suspend fun decode(buffer: BytesBuffer): ReportDetail { override fun decode(buffer: BytesBuffer): ReportDetail {
val title = buffer.readMcString() val title = buffer.readMcString()
val description = buffer.readMcString() val description = buffer.readMcString()
return ReportDetail(title, description) return ReportDetail(title, description)
@@ -31,8 +31,8 @@ public data class ClientboundCustomReportDetailsPacket(val details: List<ReportD
} }
internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> { internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket { override fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
val detailCount = buffer.readVarInt() val detailCount = buffer.readVarInt()
val details = ArrayList<ReportDetail>(detailCount) val details = ArrayList<ReportDetail>(detailCount)
repeat(detailCount) { details.add(ReportDetail.decode(buffer)) } repeat(detailCount) { details.add(ReportDetail.decode(buffer)) }
@@ -11,12 +11,12 @@ import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundDisconnectConfigurationPacket(val reason: TextComponent) : MinecraftPacket { public data class ClientboundDisconnectConfigurationPacket(val reason: TextComponent) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket { override fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
return ClientboundDisconnectConfigurationPacket(reason = buffer.readTextComponent()) return ClientboundDisconnectConfigurationPacket(reason = buffer.readTextComponent())
} }
} }
@@ -7,14 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
public data object ClientboundFinishConfigurationPacket : MinecraftPacket, public data object ClientboundFinishConfigurationPacket : MinecraftPacket,
PacketCodec<ClientboundFinishConfigurationPacket> { PacketCodec<ClientboundFinishConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket { override fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
return ClientboundFinishConfigurationPacket return ClientboundFinishConfigurationPacket
} }
} }
@@ -7,14 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket { public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundKeepAliveConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundKeepAliveConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket { override fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
return ClientboundKeepAliveConfigurationPacket(buffer.readLong()) return ClientboundKeepAliveConfigurationPacket(buffer.readLong())
} }
} }
@@ -7,14 +7,14 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
public data class ClientboundPingConfigurationPacket(val id: Int) : MinecraftPacket { public data class ClientboundPingConfigurationPacket(val id: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundPingConfigurationPacket> { internal companion object Codec : PacketCodec<ClientboundPingConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket { override fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
return ClientboundPingConfigurationPacket(id = buffer.readInt()) return ClientboundPingConfigurationPacket(id = buffer.readInt())
} }
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -23,8 +23,8 @@ public data class ClientboundRegistryDataPacket(
val entries: List<RegistryEntry>, val entries: List<RegistryEntry>,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> { internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket { override fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
val id = buffer.readIdentifier() val id = buffer.readIdentifier()
val entryCount = buffer.readVarInt() val entryCount = buffer.readVarInt()
val entries = ArrayList<RegistryEntry>(entryCount) val entries = ArrayList<RegistryEntry>(entryCount)
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readUuid import cn.rtast.libmc.primitives.readUuid
@@ -15,8 +15,8 @@ import kotlin.uuid.Uuid
public data class ClientboundRemoveResourcePackPacket(val uuid: Uuid) : MinecraftPacket { public data class ClientboundRemoveResourcePackPacket(val uuid: Uuid) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundRemoveResourcePackPacket> { internal companion object Codec : PacketCodec<ClientboundRemoveResourcePackPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket { override fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
val uuid = buffer.readUuid() val uuid = buffer.readUuid()
return ClientboundRemoveResourcePackPacket(uuid) return ClientboundRemoveResourcePackPacket(uuid)
} }
@@ -7,12 +7,12 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
public data object ClientboundResetChatPacket : MinecraftPacket, public data object ClientboundResetChatPacket : MinecraftPacket,
PacketCodec<ClientboundResetChatPacket> { PacketCodec<ClientboundResetChatPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket override fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -15,8 +15,8 @@ import cn.rtast.libmc.protocol.packet.configuration.KnownPacks
public data class ClientboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket { public data class ClientboundSelectKnownPacksPacket(val knownPacks: List<KnownPacks>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundSelectKnownPacksPacket> { internal companion object Codec : PacketCodec<ClientboundSelectKnownPacksPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket { override fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
val packsCount = buffer.readVarInt() val packsCount = buffer.readVarInt()
val packs = List(packsCount) { KnownPacks.decode(buffer) } val packs = List(packsCount) { KnownPacks.decode(buffer) }
return ClientboundSelectKnownPacksPacket(packs) return ClientboundSelectKnownPacksPacket(packs)
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -15,8 +15,8 @@ import cn.rtast.libmc.protocol.protocol.game.registry.report.ServerLink
public data class ClientboundServerLinksPacket(val links: List<ServerLink>) : MinecraftPacket { public data class ClientboundServerLinksPacket(val links: List<ServerLink>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> { internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket { override fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
val count = buffer.readVarInt() val count = buffer.readVarInt()
val links = ArrayList<ServerLink>(count) val links = ArrayList<ServerLink>(count)
repeat(count) { links.add(ServerLink.decode(buffer)) } repeat(count) { links.add(ServerLink.decode(buffer)) }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readPrefixedByteArray import cn.rtast.libmc.primitives.readPrefixedByteArray
@@ -22,8 +22,8 @@ public data class ClientboundStoreCookiePacket(
val payload: ByteArray, val payload: ByteArray,
) : MinecraftPacket { ) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> { internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket { override fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
val key = buffer.readIdentifier() val key = buffer.readIdentifier()
val payload = buffer.readPrefixedByteArray() val payload = buffer.readPrefixedByteArray()
return ClientboundStoreCookiePacket(key, payload) return ClientboundStoreCookiePacket(key, payload)
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
@@ -15,8 +15,8 @@ import cn.rtast.libmc.primitives.readVarInt
public data class ClientboundTransferPacket(val host: String, val port: Int) : MinecraftPacket { public data class ClientboundTransferPacket(val host: String, val port: Int) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundTransferPacket> { internal companion object Codec : PacketCodec<ClientboundTransferPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundTransferPacket { override fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
val host = buffer.readMcString() val host = buffer.readMcString()
val port = buffer.readVarInt() val port = buffer.readVarInt()
return ClientboundTransferPacket(host, port) return ClientboundTransferPacket(host, port)
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -16,8 +16,8 @@ import cn.rtast.libmc.protocol.protocol.game.readIdentifier
public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) : MinecraftPacket { public data class ClientboundUpdateEnabledFeaturesPacket(val features: List<Identifier>) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> { internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket { override fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
val featureCount = buffer.readVarInt() val featureCount = buffer.readVarInt()
val features = ArrayList<Identifier>(featureCount) val features = ArrayList<Identifier>(featureCount)
repeat(featureCount) { features.add(buffer.readIdentifier()) } repeat(featureCount) { features.add(buffer.readIdentifier()) }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.clientbound package cn.rtast.libmc.protocol.packet.configuration.clientbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -19,13 +19,13 @@ import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistry>) : MinecraftPacket { public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistry>) : MinecraftPacket {
public data class TaggedRegistry(val registryId: Identifier, val tags: List<Tag>) { public data class TaggedRegistry(val registryId: Identifier, val tags: List<Tag>) {
internal companion object Codec : PacketCodec<TaggedRegistry> { internal companion object Codec : PacketCodec<TaggedRegistry> {
override suspend fun encode(buffer: BytesBuffer, value: TaggedRegistry) { override fun encode(buffer: BytesBuffer, value: TaggedRegistry) {
buffer.writeIdentifier(value.registryId) buffer.writeIdentifier(value.registryId)
buffer.writeVarInt(value.tags.size) buffer.writeVarInt(value.tags.size)
value.tags.forEach { Tag.encode(buffer, it) } value.tags.forEach { Tag.encode(buffer, it) }
} }
override suspend fun decode(buffer: BytesBuffer): TaggedRegistry { override fun decode(buffer: BytesBuffer): TaggedRegistry {
val id = buffer.readIdentifier() val id = buffer.readIdentifier()
val tagCount = buffer.readVarInt() val tagCount = buffer.readVarInt()
val tags = ArrayList<Tag>(tagCount) val tags = ArrayList<Tag>(tagCount)
@@ -37,13 +37,13 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
public data class Tag(val name: Identifier, val entries: IntArray) { public data class Tag(val name: Identifier, val entries: IntArray) {
internal companion object Codec : PacketCodec<Tag> { internal companion object Codec : PacketCodec<Tag> {
override suspend fun encode(buffer: BytesBuffer, value: Tag) { override fun encode(buffer: BytesBuffer, value: Tag) {
buffer.writeIdentifier(value.name) buffer.writeIdentifier(value.name)
buffer.writeVarInt(value.entries.size) buffer.writeVarInt(value.entries.size)
value.entries.forEach { buffer.writeVarInt(it) } value.entries.forEach { buffer.writeVarInt(it) }
} }
override suspend fun decode(buffer: BytesBuffer): Tag { override fun decode(buffer: BytesBuffer): Tag {
val name = buffer.readIdentifier() val name = buffer.readIdentifier()
val entrySize = buffer.readVarInt() val entrySize = buffer.readVarInt()
val entries = ArrayList<Int>(entrySize) val entries = ArrayList<Int>(entrySize)
@@ -69,8 +69,8 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
} }
internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> { internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket { override fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
val registryCount = buffer.readVarInt() val registryCount = buffer.readVarInt()
val registries = ArrayList<TaggedRegistry>(registryCount) val registries = ArrayList<TaggedRegistry>(registryCount)
repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) } repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket, public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket,
PacketCodec<ServerboundAcceptCodeOfConductPacket> { PacketCodec<ServerboundAcceptCodeOfConductPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {} override fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
override suspend fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket = override fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
ServerboundAcceptCodeOfConductPacket ServerboundAcceptCodeOfConductPacket
} }
@@ -7,13 +7,13 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket, public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket,
PacketCodec<ServerboundAckFinishConfigurationPacket> { PacketCodec<ServerboundAckFinishConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {} override fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
override suspend fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket = override fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,7 +7,7 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.primitives.writeVarInt import cn.rtast.libmc.primitives.writeVarInt
@@ -16,7 +16,7 @@ import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket { public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundCookieResponsePacket> { internal companion object Codec : PacketCodec<ServerboundCookieResponsePacket> {
override suspend fun encode(buffer: BytesBuffer, value: ServerboundCookieResponsePacket) { override fun encode(buffer: BytesBuffer, value: ServerboundCookieResponsePacket) {
buffer.writeIdentifier(value.key) buffer.writeIdentifier(value.key)
if (value.payload != null) { if (value.payload != null) {
buffer.writeBoolean(true) buffer.writeBoolean(true)
@@ -25,7 +25,7 @@ public data class ServerboundCookieResponsePacket(val key: Identifier, val paylo
} else buffer.writeBoolean(false) } else buffer.writeBoolean(false)
} }
override suspend fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket = override fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
@@ -7,17 +7,17 @@
package cn.rtast.libmc.protocol.packet.configuration.serverbound package cn.rtast.libmc.protocol.packet.configuration.serverbound
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket { public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
internal companion object Codec : PacketCodec<ServerboundKeepAliveConfigurationPacket> { internal companion object Codec : PacketCodec<ServerboundKeepAliveConfigurationPacket> {
override suspend fun encode(buffer: BytesBuffer, value: ServerboundKeepAliveConfigurationPacket) { override fun encode(buffer: BytesBuffer, value: ServerboundKeepAliveConfigurationPacket) {
buffer.writeLong(value.id) buffer.writeLong(value.id)
} }
override suspend fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket = override fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
} }
Loaded 100 of 318 files, more files were not shown because too many files have changed in this diff. Show more