Split Socket implementation, improve performance
This commit is contained in:
318 files changed
+1109
-2722
No files matched your search
@@ -49,3 +49,4 @@ bin/
|
||||
**/build/*
|
||||
/libmc-protocol/src/jvmTest/resources/accessToken.txt
|
||||
/libmc-protocol-encrypt/src/commonTest/resources/accessToken.txt
|
||||
/libmc-protocol-context/src/commonTest/resources/accessToken.txt
|
||||
@@ -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
|
||||
|
||||
Send Command via rcon protocol
|
||||
|
||||
@@ -20,6 +20,7 @@ subprojects {
|
||||
pluginManager.apply("org.jetbrains.kotlin.multiplatform")
|
||||
pluginManager.apply("maven-publish")
|
||||
|
||||
if (project.path.startsWith(":example")) return@subprojects
|
||||
publishing {
|
||||
repositories {
|
||||
maven("https://repo.rtast.cn/packages") {
|
||||
|
||||
@@ -12,18 +12,24 @@ kotlin {
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(libs.kotlinx.io)
|
||||
api(libs.kotlinx.coroutines)
|
||||
}
|
||||
|
||||
jvmMain.dependencies {}
|
||||
nativeMain.dependencies {
|
||||
implementation(libs.ktor.network)
|
||||
implementation(libs.kotlinx.io)
|
||||
}
|
||||
|
||||
// nativeMain.dependencies {
|
||||
// implementation(libs.ktor.network)
|
||||
// }
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
||||
jvmTest.dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
public interface NetworkCipher {
|
||||
public suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
public suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
public fun encrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
public fun decrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
}
|
||||
@@ -7,18 +7,23 @@
|
||||
|
||||
package cn.rtast.libmc.crypto
|
||||
|
||||
import cn.rtast.libmc.network.SocketContext
|
||||
import cn.rtast.libmc.network.SocketEngine
|
||||
|
||||
public data class ProtocolContext(
|
||||
val rsaEncryptor: RSA1024Encryptor,
|
||||
val sha1Hasher: Sha1Hasher,
|
||||
val cipherFactory: (sharedKey: ByteArray) -> NetworkCipher,
|
||||
val authProvider: AuthenticationProvider?,
|
||||
)
|
||||
override val engine: SocketEngine,
|
||||
) : SocketContext()
|
||||
|
||||
public class ProtocolContextBuilder(private val onlineMode: Boolean) {
|
||||
public lateinit var rsaEncryptor: RSA1024Encryptor
|
||||
public lateinit var sha1Hasher: Sha1Hasher
|
||||
public lateinit var cipherFactory: (sharedKey: ByteArray) -> NetworkCipher
|
||||
public lateinit var authProvider: AuthenticationProvider
|
||||
public lateinit var socketEngine: SocketEngine
|
||||
|
||||
public fun build(): ProtocolContext =
|
||||
ProtocolContext(
|
||||
@@ -27,14 +32,15 @@ public class ProtocolContextBuilder(private val onlineMode: Boolean) {
|
||||
cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"),
|
||||
authProvider = if (onlineMode) {
|
||||
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 suspend fun encrypt(key: ByteArray, data: ByteArray): ByteArray
|
||||
public fun encrypt(key: ByteArray, data: ByteArray): ByteArray
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.stream
|
||||
package cn.rtast.libmc.network
|
||||
|
||||
public enum class ByteOrder {
|
||||
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
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
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 suspend fun decode(buffer: BytesBuffer): T
|
||||
public fun decode(buffer: BytesBuffer): 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
|
||||
val bytes = source.readBytes(length.toInt())
|
||||
val bytes = source.readBytes(length)
|
||||
this.writeBytes(bytes)
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
package cn.rtast.libmc.packet
|
||||
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class PacketRegistry {
|
||||
@@ -26,10 +26,10 @@ public class PacketRegistry {
|
||||
register(id, T::class, codec)
|
||||
}
|
||||
|
||||
public suspend fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
|
||||
idToCodec[packetId]?.decode(buffer) ?: ClientboundUnknownPacket(packetId, buffer.readRemainingBytes())
|
||||
public fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
|
||||
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")
|
||||
val info = requireNotNull(classToInfo[packet::class]) {
|
||||
"Unregistered Packet ${packet::class.simpleName}"
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public typealias BitSet = LongArray
|
||||
|
||||
public suspend fun BytesBuffer.readBitSet(): BitSet {
|
||||
public fun BytesBuffer.readBitSet(): BitSet {
|
||||
val count = this.readVarInt()
|
||||
return LongArray(count) { this.readLong() }
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeBitSet(data: BitSet) {
|
||||
public fun BytesBuffer.writeBitSet(data: BitSet) {
|
||||
this.writeVarInt(data.size)
|
||||
for (i in data.indices) this.writeLong(data[i])
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
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 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 suspend inline fun <L, R> BytesBuffer.readEither(
|
||||
public inline fun <L, R> BytesBuffer.readEither(
|
||||
readLeft: BytesBuffer.() -> L,
|
||||
readRight: BytesBuffer.() -> 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))
|
||||
}
|
||||
|
||||
public suspend inline fun <L, R> BytesBuffer.writeEither(
|
||||
public inline fun <L, R> BytesBuffer.writeEither(
|
||||
either: Either<L, R>,
|
||||
writeLeft: BytesBuffer.(L) -> Unit,
|
||||
writeRight: BytesBuffer.(R) -> Unit,
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public sealed interface IdOrX<out T> {
|
||||
public data class Inline<T>(val value: T) : IdOrX<T>
|
||||
public data class Reference(val registryId: Int) : IdOrX<Nothing>
|
||||
}
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
|
||||
public inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
|
||||
when (value) {
|
||||
is IdOrX.Inline -> {
|
||||
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()
|
||||
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
|
||||
}
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public sealed interface IdSet {
|
||||
public data class Tag(val tagName: String) : IdSet
|
||||
public data class Entries(val ids: List<Int>) : IdSet
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readIdSet(): IdSet {
|
||||
public fun BytesBuffer.readIdSet(): IdSet {
|
||||
val type = this.readVarInt()
|
||||
return if (type == 0) {
|
||||
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) {
|
||||
is IdSet.Tag -> {
|
||||
this.writeVarInt(0)
|
||||
|
||||
@@ -8,21 +8,21 @@
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public object McStringCodec : PacketCodec<String> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: String) {
|
||||
override fun encode(buffer: BytesBuffer, value: String) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
VarIntCodec.encode(buffer, bytes.size)
|
||||
buffer.writeBytes(bytes)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): String {
|
||||
override fun decode(buffer: BytesBuffer): String {
|
||||
val length = VarIntCodec.decode(buffer)
|
||||
val bytes = buffer.readBytes(length)
|
||||
return bytes.decodeToString()
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
|
||||
public suspend fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
|
||||
public fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
|
||||
public fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
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()
|
||||
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) }
|
||||
*/
|
||||
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) {
|
||||
this.writeBoolean(true)
|
||||
block.invoke(this, value)
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
|
||||
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 data = this.readBytes(length)
|
||||
return data
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
|
||||
public fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
|
||||
this.writeVarInt(data.size)
|
||||
this.writeBytes(data)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
|
||||
public fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
|
||||
if (data != null) {
|
||||
this.writeBoolean(true)
|
||||
this.writeVarInt(data.size)
|
||||
@@ -29,21 +29,21 @@ public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?)
|
||||
} else this.writeBoolean(false)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readPrefixedStringArray(): List<String> {
|
||||
public fun BytesBuffer.readPrefixedStringArray(): List<String> {
|
||||
val length = readVarInt()
|
||||
val list = ArrayList<String>(length)
|
||||
repeat(length) { list.add(readMcString()) }
|
||||
return list
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
|
||||
public fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
|
||||
writeVarInt(value.size)
|
||||
for (item in value) writeMcString(item)
|
||||
}
|
||||
|
||||
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
|
||||
public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
|
||||
val count = this.readVarInt()
|
||||
require(count in 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
|
||||
}
|
||||
|
||||
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)
|
||||
for (item in list) this.writer(item)
|
||||
}
|
||||
@@ -7,15 +7,15 @@
|
||||
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
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(leastSignificantBits)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readUuid(): Uuid {
|
||||
public fun BytesBuffer.readUuid(): Uuid {
|
||||
val most = this.readLong()
|
||||
val least = this.readLong()
|
||||
return Uuid.fromLongs(most, least)
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
package cn.rtast.libmc.primitives
|
||||
|
||||
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> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: Int) {
|
||||
override fun encode(buffer: BytesBuffer, value: Int) {
|
||||
var v = value
|
||||
while (true) {
|
||||
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 result = 0
|
||||
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 suspend fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
|
||||
public fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
|
||||
public fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
|
||||
|
||||
public suspend fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
|
||||
public suspend fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
|
||||
public fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
|
||||
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
|
||||
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public object VarLongCodec : PacketCodec<Long> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: Long) {
|
||||
override fun encode(buffer: BytesBuffer, value: Long) {
|
||||
var v = value
|
||||
while (true) {
|
||||
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 result = 0L
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
-116
@@ -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
|
||||
|
||||
import cn.rtast.libmc.stream.ByteOrder
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.ByteOrder
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
public class BytesBufferNBTInput(override val order: ByteOrder, private val buffer: BytesBuffer) : NBTInput {
|
||||
override suspend fun readByte(): Byte = buffer.readByte()
|
||||
override suspend fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
|
||||
override fun readByte(): Byte = buffer.readByte()
|
||||
override fun readBytes(count: Int): ByteArray = buffer.readBytes(count)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.toNBTInput(order: ByteOrder = ByteOrder.BIG_ENDIAN): BytesBufferNBTInput =
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
package cn.rtast.libmc.nbt
|
||||
|
||||
import cn.rtast.libmc.stream.ByteOrder
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.network.ByteOrder
|
||||
import cn.rtast.libmc.network.BytesBuffer
|
||||
|
||||
|
||||
public class BytesBufferNBTOutput(
|
||||
@@ -16,9 +16,9 @@ public class BytesBufferNBTOutput(
|
||||
override val root: NBTTag.CompoundTag,
|
||||
private val buffer: BytesBuffer,
|
||||
) : NBTOutput {
|
||||
override suspend fun writeByte(value: Byte): Unit = buffer.writeByte(value)
|
||||
override suspend fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
|
||||
override suspend fun toByteArray(): ByteArray = buffer.toByteArray()
|
||||
override fun writeByte(value: Byte): Unit = buffer.writeByte(value)
|
||||
override fun writeBytes(value: ByteArray): Unit = buffer.writeBytes(value)
|
||||
override fun toByteArray(): ByteArray = buffer.toByteArray()
|
||||
}
|
||||
|
||||
public fun BytesBuffer.toNBTOutput(
|
||||
|
||||
@@ -7,33 +7,33 @@
|
||||
|
||||
package cn.rtast.libmc.nbt
|
||||
|
||||
import cn.rtast.libmc.stream.ByteOrder
|
||||
import cn.rtast.libmc.network.ByteOrder
|
||||
|
||||
public interface NBTInput {
|
||||
public val order: ByteOrder
|
||||
|
||||
public suspend fun readByte(): Byte
|
||||
public suspend fun readBytes(count: Int): ByteArray
|
||||
public fun readByte(): Byte
|
||||
public fun readBytes(count: Int): ByteArray
|
||||
|
||||
public suspend fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE()
|
||||
public suspend fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE()
|
||||
public suspend fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE()
|
||||
public suspend fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE()
|
||||
public suspend fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE()
|
||||
public fun readLong(): Long = if (order == ByteOrder.BIG_ENDIAN) readLongBE() else readLongLE()
|
||||
public fun readFloat(): Float = if (order == ByteOrder.BIG_ENDIAN) readFloatBE() else readFloatLE()
|
||||
public fun readDouble(): Double = if (order == ByteOrder.BIG_ENDIAN) readDoubleBE() else readDoubleLE()
|
||||
public fun readShort(): Short = if (order == ByteOrder.BIG_ENDIAN) readShortBE() else readShortLE()
|
||||
public fun readInt(): Int = if (order == ByteOrder.BIG_ENDIAN) readIntBE() else readIntLE()
|
||||
|
||||
// big endian
|
||||
public suspend fun readLongBE(): Long =
|
||||
public fun readLongBE(): Long =
|
||||
(readIntBE().toLong() shl 32) or (readIntBE().toLong() and 0xFFFFFFFFL)
|
||||
|
||||
public suspend fun readFloatBE(): Float = Float.fromBits(readIntBE())
|
||||
public suspend fun readDoubleBE(): Double = Double.fromBits(readLongBE())
|
||||
public suspend fun readShortBE(): Short {
|
||||
public fun readFloatBE(): Float = Float.fromBits(readIntBE())
|
||||
public fun readDoubleBE(): Double = Double.fromBits(readLongBE())
|
||||
public fun readShortBE(): Short {
|
||||
val b1 = readByte().toInt() and 0xFF
|
||||
val b2 = readByte().toInt() and 0xFF
|
||||
return ((b1 shl 8) or b2).toShort()
|
||||
}
|
||||
|
||||
public suspend fun readIntBE(): Int {
|
||||
public fun readIntBE(): Int {
|
||||
return ((readByte().toInt() and 0xFF) shl 24) or
|
||||
((readByte().toInt() and 0xFF) shl 16) or
|
||||
((readByte().toInt() and 0xFF) shl 8) or
|
||||
@@ -41,7 +41,7 @@ public interface NBTInput {
|
||||
}
|
||||
|
||||
// little endian
|
||||
public suspend fun readLongLE(): Long {
|
||||
public fun readLongLE(): Long {
|
||||
return ((readByte().toInt() and 0xFF).toLong() shl 0) or
|
||||
((readByte().toInt() and 0xFF).toLong() shl 8) or
|
||||
((readByte().toInt() and 0xFF).toLong() shl 16) or
|
||||
@@ -52,14 +52,14 @@ public interface NBTInput {
|
||||
(readByte().toInt() and 0xFF).toLong()
|
||||
}
|
||||
|
||||
public 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()
|
||||
|
||||
public suspend fun readIntLE(): Int =
|
||||
public fun readIntLE(): Int =
|
||||
((readByte().toInt() and 0xFF)) or
|
||||
((readByte().toInt() and 0xFF) shl 8) or
|
||||
((readByte().toInt() and 0xFF) shl 16) or
|
||||
|
||||
@@ -7,44 +7,44 @@
|
||||
|
||||
package cn.rtast.libmc.nbt
|
||||
|
||||
import cn.rtast.libmc.stream.ByteOrder
|
||||
import cn.rtast.libmc.network.ByteOrder
|
||||
|
||||
public interface NBTOutput {
|
||||
public val order: ByteOrder
|
||||
public val root: NBTTag.CompoundTag
|
||||
|
||||
public suspend fun writeByte(value: Byte)
|
||||
public suspend fun writeBytes(value: ByteArray)
|
||||
public fun writeByte(value: Byte)
|
||||
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)
|
||||
|
||||
public suspend fun writeInt(value: Int): Unit =
|
||||
public fun writeInt(value: Int): Unit =
|
||||
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)
|
||||
|
||||
public suspend fun writeFloat(value: Float): Unit =
|
||||
public fun writeFloat(value: Float): Unit =
|
||||
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)
|
||||
|
||||
// big endian
|
||||
public suspend fun writeShortBE(value: Short) {
|
||||
public fun writeShortBE(value: Short) {
|
||||
writeByte(((value.toInt() shr 8) 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 16) and 0xFF).toByte())
|
||||
writeByte(((value shr 8) 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 48).toInt() and 0xFF).toByte())
|
||||
writeByte(((value shr 40).toInt() and 0xFF).toByte())
|
||||
@@ -55,28 +55,28 @@ public interface NBTOutput {
|
||||
writeByte((value.toInt() and 0xFF).toByte())
|
||||
}
|
||||
|
||||
public suspend fun writeFloatBE(value: Float) {
|
||||
public fun writeFloatBE(value: Float) {
|
||||
writeIntBE(value.toBits())
|
||||
}
|
||||
|
||||
public suspend fun writeDoubleBE(value: Double) {
|
||||
public fun writeDoubleBE(value: Double) {
|
||||
writeLongBE(value.toBits())
|
||||
}
|
||||
|
||||
// Little Endian
|
||||
public suspend fun writeShortLE(value: Short) {
|
||||
public fun writeShortLE(value: Short) {
|
||||
writeByte((value.toInt() 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 shr 8) and 0xFF).toByte())
|
||||
writeByte(((value shr 16) 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 shr 8) 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())
|
||||
}
|
||||
|
||||
public suspend fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
|
||||
public suspend fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
|
||||
public suspend fun toByteArray(): ByteArray
|
||||
public fun writeFloatLE(value: Float): Unit = writeIntLE(value.toBits())
|
||||
public fun writeDoubleLE(value: Double): Unit = writeLongLE(value.toBits())
|
||||
public fun toByteArray(): ByteArray
|
||||
}
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
package cn.rtast.libmc.nbt
|
||||
|
||||
public suspend fun NBTInput.readStringTag(): String {
|
||||
public fun NBTInput.readStringTag(): String {
|
||||
val length = readShort().toInt() and 0xFFFF
|
||||
val bytes = readBytes(length)
|
||||
return bytes.decodeToString()
|
||||
}
|
||||
|
||||
public suspend fun NBTInput.readListTag(): NBTTag.ListTag {
|
||||
public fun NBTInput.readListTag(): NBTTag.ListTag {
|
||||
val elementTypeId = readByte().toInt() and 0xFF
|
||||
val elementType = NBTType.fromID(elementTypeId)
|
||||
val length = readInt()
|
||||
@@ -22,7 +22,7 @@ public suspend fun NBTInput.readListTag(): NBTTag.ListTag {
|
||||
return NBTTag.ListTag(elementType, list)
|
||||
}
|
||||
|
||||
public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
|
||||
public fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
|
||||
val map = LinkedHashMap<String, NBTTag>()
|
||||
while (true) {
|
||||
val typeId = readByte().toInt() and 0xFF
|
||||
@@ -35,7 +35,7 @@ public suspend fun NBTInput.readCompoundTag(): NBTTag.CompoundTag {
|
||||
return NBTTag.CompoundTag(map)
|
||||
}
|
||||
|
||||
public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag =
|
||||
public fun NBTInput.readTagPayload(type: NBTType): NBTTag =
|
||||
when (type) {
|
||||
NBTType.Byte -> NBTTag.ByteTag(readByte())
|
||||
NBTType.Short -> NBTTag.ShortTag(readShort())
|
||||
@@ -52,7 +52,7 @@ public suspend fun NBTInput.readTagPayload(type: NBTType): NBTTag =
|
||||
NBTType.End -> error("TAG_End no payload")
|
||||
}
|
||||
|
||||
public suspend fun NBTInput.readCompound(): NBTTag {
|
||||
public fun NBTInput.readCompound(): NBTTag {
|
||||
val map = LinkedHashMap<String, NBTTag>()
|
||||
while (true) {
|
||||
val typeId = readByte().toInt()
|
||||
@@ -67,7 +67,7 @@ public suspend fun NBTInput.readCompound(): NBTTag {
|
||||
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())
|
||||
require(rootType == NBTType.Compound) { "Root tag must be TAG_Compound" }
|
||||
val nameLen = readShort().toInt() and 0xFFFF
|
||||
@@ -76,7 +76,7 @@ public suspend fun NBTInput.readRootCompound(): NBTCompound {
|
||||
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)) {
|
||||
NBTType.Compound -> NBTCompound("", readCompoundTag())
|
||||
NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag()))))
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
package cn.rtast.libmc.nbt
|
||||
|
||||
|
||||
public suspend fun NBTOutput.writeStringTag(value: String) {
|
||||
public fun NBTOutput.writeStringTag(value: String) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
writeShort(bytes.size.toShort())
|
||||
writeBytes(bytes)
|
||||
}
|
||||
|
||||
public suspend fun NBTOutput.writeTagPayload(tag: NBTTag) {
|
||||
public fun NBTOutput.writeTagPayload(tag: NBTTag) {
|
||||
when (tag) {
|
||||
is NBTTag.ByteTag -> writeByte(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)
|
||||
writeStringTag(name)
|
||||
writeTagPayload(root)
|
||||
return toByteArray()
|
||||
}
|
||||
|
||||
public suspend fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
|
||||
public fun NBTOutput.writeNetworkCompound(compound: NBTCompound) {
|
||||
val compoundTag = compound.element as? NBTTag.CompoundTag
|
||||
?: throw IllegalArgumentException("NBTCompound element must be an NBTTag.CompoundTag")
|
||||
writeByte(NBTType.Compound.id)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
package test
|
||||
|
||||
import cn.rtast.libmc.stream.wrap
|
||||
import cn.rtast.libmc.network.wrap
|
||||
import cn.rtast.libmc.nbt.NbtReader
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
File renamed without changes.
@@ -16,6 +16,7 @@ kotlin {
|
||||
implementation(libs.cryptography.core)
|
||||
implementation(libs.cryptography.provider.optimal)
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.network)
|
||||
}
|
||||
|
||||
jvmTest.dependencies {
|
||||
+2
-2
@@ -22,14 +22,14 @@ public class AesCFB8Cipher(sharedKey: ByteArray) : NetworkCipher {
|
||||
.decodeFromByteArrayBlocking(AES.Key.Format.RAW, sharedKey)
|
||||
.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 ciphertext = cipher.encryptWithIvBlocking(encryptIv, plaintext)
|
||||
ciphertext.copyInto(buffer, destinationOffset = offset)
|
||||
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 plaintext = cipher.decryptWithIvBlocking(decryptIv, ciphertext)
|
||||
plaintext.copyInto(buffer, destinationOffset = offset)
|
||||
+1
@@ -28,4 +28,5 @@ public val DefaultProtocolContext: ProtocolContextBuilder.() -> Unit = {
|
||||
}.status
|
||||
require(status == HttpStatusCode.NoContent)
|
||||
}
|
||||
socketEngine = KtorNetworkEngine()
|
||||
}
|
||||
+50
@@ -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()
|
||||
}
|
||||
+4
-3
@@ -16,9 +16,10 @@ import dev.whyoleg.cryptography.algorithms.SHA1
|
||||
|
||||
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 rsa = provider.get(RSA.PKCS1)
|
||||
val publicKey = rsa.publicKeyDecoder(SHA1).decodeFromByteArray(RSA.PublicKey.Format.DER, publicKeyBytes)
|
||||
return publicKey.encryptor().encrypt(data)
|
||||
val publicKey = rsa.publicKeyDecoder(SHA1)
|
||||
.decodeFromByteArrayBlocking(RSA.PublicKey.Format.DER, publicKeyBytes)
|
||||
return publicKey.encryptor().encryptBlocking(data)
|
||||
}
|
||||
+2
-2
@@ -12,11 +12,11 @@ package cn.rtast.libmc.protocol.crypto
|
||||
import dev.whyoleg.cryptography.DelicateCryptographyApi
|
||||
import dev.whyoleg.cryptography.algorithms.SHA1
|
||||
|
||||
public suspend fun minecraftServerIdHash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String {
|
||||
public fun minecraftServerIdHash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String {
|
||||
val serverIdBytes = serverId.encodeToByteArray()
|
||||
for (b in serverIdBytes) require((b.toInt() and 0xFF) <= 0x7F) { "serverId contains non-US-ASCII character" }
|
||||
val data = serverIdBytes + secretKey + publicKey
|
||||
val hash = provider.get(SHA1).hasher().hash(data)
|
||||
val hash = provider.get(SHA1).hasher().hashBlocking(data)
|
||||
return mcDigestToString(hash)
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class TestJvmClient {
|
||||
Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
|
||||
// null,
|
||||
accessToken,
|
||||
crypto = DefaultProtocolContext
|
||||
contextBuilder = DefaultProtocolContext
|
||||
)
|
||||
// cli.on<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
// cli.on<ClientboundLoginSuccessPacket> { println(it) }
|
||||
@@ -22,7 +22,7 @@ kotlin {
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(project(":protocol-encrypt"))
|
||||
implementation(project(":protocol-context"))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
||||
|
||||
+11
-13
@@ -6,7 +6,6 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.client
|
||||
|
||||
import cn.rtast.libmc.LibMCContext
|
||||
import cn.rtast.libmc.crypto.ProtocolContext
|
||||
import cn.rtast.libmc.crypto.ProtocolContextBuilder
|
||||
import cn.rtast.libmc.protocol.event.InternalPacketDispatcher
|
||||
@@ -24,22 +23,23 @@ import kotlin.uuid.Uuid
|
||||
|
||||
public class MinecraftClient internal constructor(
|
||||
private val host: String,
|
||||
private val port: Int = 25565,
|
||||
private val port: Int,
|
||||
private val username: String,
|
||||
internal val uuid: Uuid,
|
||||
internal val accessToken: String?,
|
||||
context: LibMCContext,
|
||||
parentJob: Job?,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
cryptoContext: ProtocolContext,
|
||||
protocolContext: ProtocolContext,
|
||||
) : PacketEventDispatcher(), CoroutineScope {
|
||||
internal val rsa1024Encryptor = cryptoContext.rsaEncryptor
|
||||
internal val serverIdHasher = cryptoContext.sha1Hasher
|
||||
internal val authProvider = cryptoContext.authProvider
|
||||
internal val rsa1024Encryptor = protocolContext.rsaEncryptor
|
||||
internal val serverIdHasher = protocolContext.sha1Hasher
|
||||
internal val authProvider = protocolContext.authProvider
|
||||
internal val stateMachine = ClientStateMachine()
|
||||
|
||||
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)
|
||||
@@ -95,21 +95,19 @@ public fun createMinecraftClient(
|
||||
username: String,
|
||||
uuid: Uuid = generateOfflineUuid(username),
|
||||
accessToken: String?,
|
||||
context: LibMCContext = LibMCContext(),
|
||||
parentJob: Job? = null,
|
||||
ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
crypto: ProtocolContextBuilder.() -> Unit,
|
||||
contextBuilder: ProtocolContextBuilder.() -> Unit,
|
||||
): MinecraftClient {
|
||||
val cryptoContext = ProtocolContextBuilder(accessToken != null).apply(crypto).build()
|
||||
val context = ProtocolContextBuilder(accessToken != null).apply(contextBuilder).build()
|
||||
return MinecraftClient(
|
||||
host = host,
|
||||
port = port,
|
||||
username = username,
|
||||
uuid = uuid,
|
||||
accessToken = accessToken,
|
||||
context = context,
|
||||
parentJob = parentJob,
|
||||
ioDispatcher = ioDispatcher,
|
||||
cryptoContext = cryptoContext
|
||||
protocolContext = context
|
||||
)
|
||||
}
|
||||
+5
-7
@@ -8,8 +8,8 @@
|
||||
package cn.rtast.libmc.protocol.network
|
||||
|
||||
import cn.rtast.libmc.crypto.NetworkCipher
|
||||
import cn.rtast.libmc.stream.ReadChannel
|
||||
import cn.rtast.libmc.stream.WriteChannel
|
||||
import cn.rtast.libmc.network.ReadChannel
|
||||
import cn.rtast.libmc.network.WriteChannel
|
||||
|
||||
/**
|
||||
* AES-128-CFB8 ***ciphered*** read channel
|
||||
@@ -17,7 +17,7 @@ import cn.rtast.libmc.stream.WriteChannel
|
||||
internal class CipherReadChannel(
|
||||
private val delegate: ReadChannel,
|
||||
private val crypto: NetworkCipher,
|
||||
) : ReadChannel() {
|
||||
) : ReadChannel {
|
||||
override suspend fun readFully(out: ByteArray, start: Int, end: Int) {
|
||||
delegate.readFully(out, start, end)
|
||||
val length = end - start
|
||||
@@ -43,7 +43,7 @@ internal class CipherReadChannel(
|
||||
internal class CipherWriteChannel(
|
||||
private val delegate: WriteChannel,
|
||||
private val crypto: NetworkCipher,
|
||||
) : WriteChannel() {
|
||||
) : WriteChannel {
|
||||
override suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) {
|
||||
val length = endIndex - startIndex
|
||||
if (length <= 0) return
|
||||
@@ -52,7 +52,5 @@ internal class CipherWriteChannel(
|
||||
delegate.writeFully(encrypted, 0, length)
|
||||
}
|
||||
|
||||
override suspend fun flush() {
|
||||
delegate.flush()
|
||||
}
|
||||
override suspend fun flush() = delegate.flush()
|
||||
}
|
||||
+15
-17
@@ -6,17 +6,18 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.network
|
||||
|
||||
import cn.rtast.libmc.LibMCContext
|
||||
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.writeBuffer
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
||||
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
|
||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.stream.wrap
|
||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.clientboundGameProtocols
|
||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.serverboundGameProtocols
|
||||
import cn.rtast.libmc.zlibCompress
|
||||
import cn.rtast.libmc.zlibDecompress
|
||||
import kotlin.concurrent.Volatile
|
||||
@@ -24,17 +25,17 @@ import kotlin.concurrent.Volatile
|
||||
public class NetworkChannel internal constructor(
|
||||
host: String,
|
||||
port: Int,
|
||||
context: LibMCContext,
|
||||
private val stateMachine: ClientStateMachine,
|
||||
cipherProvider: (ByteArray) -> NetworkCipher,
|
||||
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
|
||||
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 }
|
||||
|
||||
/**
|
||||
@@ -44,18 +45,17 @@ public class NetworkChannel internal constructor(
|
||||
*/
|
||||
public suspend fun readNextPacket(): MinecraftPacket {
|
||||
val packetLength = session.readVarInt()
|
||||
val rawFrameBytes = session.readBytes(packetLength)
|
||||
val frameBuf = rawFrameBytes.wrap()
|
||||
val frameBuf = session.readBytes(packetLength).wrap()
|
||||
val payloadBuf = if (threshold < 0) frameBuf else {
|
||||
val dataLength = frameBuf.readVarInt()
|
||||
val remainingBytes = frameBuf.readBytes(frameBuf.remaining.toInt())
|
||||
if (dataLength == 0) remainingBytes.wrap() else remainingBytes.zlibDecompress(dataLength).wrap()
|
||||
if (dataLength == 0) frameBuf else {
|
||||
val compressedBytes = frameBuf.toByteArray()
|
||||
compressedBytes.zlibDecompress(dataLength).wrap()
|
||||
}
|
||||
}
|
||||
val currentState = stateMachine.currentState
|
||||
val packetId = payloadBuf.readVarInt()
|
||||
val packet = GamePacketsProtocolCodec.clientboundGameProtocols
|
||||
.getRegistry(currentState)
|
||||
.decodePacket(packetId, payloadBuf)
|
||||
val packet = clientboundGameProtocols.getRegistry(currentState).decodePacket(packetId, payloadBuf)
|
||||
dispatcher.dispatchReceive(packet)
|
||||
return packet
|
||||
}
|
||||
@@ -67,9 +67,7 @@ public class NetworkChannel internal constructor(
|
||||
*/
|
||||
public suspend fun sendPacket(packet: MinecraftPacket) {
|
||||
val uncompressedBodyBuf = BytesBuffer()
|
||||
GamePacketsProtocolCodec.serverboundGameProtocols
|
||||
.getRegistry(stateMachine.currentState)
|
||||
.encodePacket(uncompressedBodyBuf, packet)
|
||||
serverboundGameProtocols.getRegistry(stateMachine.currentState).encodePacket(uncompressedBodyBuf, packet)
|
||||
val uncompressedData = uncompressedBodyBuf.toByteArray()
|
||||
val frameBuffer = BytesBuffer()
|
||||
if (threshold < 0) {
|
||||
|
||||
+15
-33
@@ -6,19 +6,20 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.network
|
||||
|
||||
import cn.rtast.libmc.LibMCContext
|
||||
import cn.rtast.libmc.crypto.NetworkCipher
|
||||
import cn.rtast.libmc.stream.ReadChannel
|
||||
import cn.rtast.libmc.stream.Socket
|
||||
import cn.rtast.libmc.stream.WriteChannel
|
||||
import cn.rtast.libmc.crypto.ProtocolContext
|
||||
import cn.rtast.libmc.network.RawSocket
|
||||
import cn.rtast.libmc.network.ReadChannel
|
||||
import cn.rtast.libmc.network.WriteChannel
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
|
||||
public class NetworkSession internal constructor(
|
||||
private val host: String,
|
||||
private val port: Int,
|
||||
private val context: LibMCContext,
|
||||
private var cipherProvider: (ByteArray) -> NetworkCipher,
|
||||
private val context: ProtocolContext,
|
||||
) {
|
||||
private var socket: Socket? = null
|
||||
private var socket: RawSocket? = null
|
||||
|
||||
public var readChannel: ReadChannel? = null
|
||||
private set
|
||||
@@ -26,47 +27,28 @@ public class NetworkSession internal constructor(
|
||||
public var writeChannel: WriteChannel? = null
|
||||
private set
|
||||
|
||||
public fun connect() {
|
||||
val sk = Socket(host, port, context)
|
||||
public suspend fun connect() {
|
||||
val sk = context.createSocket(host, port)
|
||||
sk.connect()
|
||||
this.socket = sk
|
||||
this.readChannel = sk.openReadChannel()
|
||||
this.writeChannel = sk.openWriteChannel()
|
||||
}
|
||||
|
||||
public fun enableEncryption(sharedKey: ByteArray) {
|
||||
val currentRead = requireNotNull(readChannel) { "ReadChannel not connected" }
|
||||
val currentWrite = requireNotNull(writeChannel) { "WriteChannel not connected" }
|
||||
val currentRead = requireNotNull(readChannel)
|
||||
val currentWrite = requireNotNull(writeChannel)
|
||||
val cipher = cipherProvider(sharedKey)
|
||||
this.readChannel = CipherReadChannel(currentRead, cipher)
|
||||
this.writeChannel = CipherWriteChannel(currentWrite, cipher)
|
||||
}
|
||||
|
||||
internal suspend fun readByte(): Byte {
|
||||
val channel = requireNotNull(readChannel) { "ReadChannel not connected" }
|
||||
return channel.readByte()
|
||||
}
|
||||
internal suspend fun readBytes(length: Int): ByteArray = requireNotNull(readChannel).readBytes(length)
|
||||
|
||||
internal suspend fun readBytes(length: Int): ByteArray {
|
||||
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 readVarInt(): Int = requireNotNull(readChannel).readVarInt()
|
||||
|
||||
internal suspend fun writeFully(data: ByteArray) {
|
||||
val channel = requireNotNull(writeChannel) { "WriteChannel not connected" }
|
||||
val channel = requireNotNull(writeChannel)
|
||||
channel.writeFully(data, 0, data.size)
|
||||
channel.flush()
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,17 +10,17 @@ package cn.rtast.libmc.protocol.packet.configuration
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
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) {
|
||||
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.id)
|
||||
buffer.writeMcString(value.version)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): KnownPacks {
|
||||
override fun decode(buffer: BytesBuffer): KnownPacks {
|
||||
val namespace = buffer.readMcString()
|
||||
val id = buffer.readMcString()
|
||||
val version = buffer.readMcString()
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ import cn.rtast.libmc.primitives.readUuid
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
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
|
||||
|
||||
public data class ClientboundAddResourcePackPacket(
|
||||
@@ -25,8 +25,8 @@ public data class ClientboundAddResourcePackPacket(
|
||||
val prompt: TextComponent,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundAddResourcePackPacket {
|
||||
val uuid = buffer.readUuid()
|
||||
val url = buffer.readMcString()
|
||||
val hash = buffer.readMcString()
|
||||
|
||||
+3
-3
@@ -7,12 +7,12 @@
|
||||
|
||||
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.PacketCodec
|
||||
|
||||
public data object ClientboundClearDialogPacket : MinecraftPacket,
|
||||
PacketCodec<ClientboundClearDialogPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundClearDialogPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundClearDialogPacket = ClientboundClearDialogPacket
|
||||
}
|
||||
+3
-3
@@ -7,15 +7,15 @@
|
||||
|
||||
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.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
|
||||
public data class ClientboundCodeOfConductPacket(val codeOfConduct: String) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundCodeOfConductPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundCodeOfConductPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundCodeOfConductPacket {
|
||||
return ClientboundCodeOfConductPacket(buffer.readMcString())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundConfigurationShowDialogPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundConfigurationShowDialogPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundConfigurationShowDialogPacket {
|
||||
return ClientboundConfigurationShowDialogPacket(buffer.readNetworkNBTCompound())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundCookieRequestPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket {
|
||||
return ClientboundCookieRequestPacket(key = buffer.readIdentifier())
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.Identifier
|
||||
@@ -18,10 +18,10 @@ public data class ClientboundCustomPayloadPacket(
|
||||
val data: ByteArray,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundCustomPayloadPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket {
|
||||
val channel = buffer.readIdentifier()
|
||||
val data = buffer.readRemainingBytes()
|
||||
val data = buffer.toByteArray()
|
||||
return ClientboundCustomPayloadPacket(channel, data)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 ReportDetail(val title: String, val description: String) {
|
||||
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.description)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): ReportDetail {
|
||||
override fun decode(buffer: BytesBuffer): ReportDetail {
|
||||
val title = buffer.readMcString()
|
||||
val description = buffer.readMcString()
|
||||
return ReportDetail(title, description)
|
||||
@@ -31,8 +31,8 @@ public data class ClientboundCustomReportDetailsPacket(val details: List<ReportD
|
||||
}
|
||||
|
||||
internal companion object Codec : PacketCodec<ClientboundCustomReportDetailsPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundCustomReportDetailsPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundCustomReportDetailsPacket {
|
||||
val detailCount = buffer.readVarInt()
|
||||
val details = ArrayList<ReportDetail>(detailCount)
|
||||
repeat(detailCount) { details.add(ReportDetail.decode(buffer)) }
|
||||
|
||||
+3
-3
@@ -11,12 +11,12 @@ import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
|
||||
return ClientboundDisconnectConfigurationPacket(reason = buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,14 +7,14 @@
|
||||
|
||||
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.PacketCodec
|
||||
|
||||
public data object ClientboundFinishConfigurationPacket : MinecraftPacket,
|
||||
PacketCodec<ClientboundFinishConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket {
|
||||
return ClientboundFinishConfigurationPacket
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -7,14 +7,14 @@
|
||||
|
||||
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.PacketCodec
|
||||
|
||||
public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundKeepAliveConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket {
|
||||
return ClientboundKeepAliveConfigurationPacket(buffer.readLong())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,14 +7,14 @@
|
||||
|
||||
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.PacketCodec
|
||||
|
||||
public data class ClientboundPingConfigurationPacket(val id: Int) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundPingConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundPingConfigurationPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundPingConfigurationPacket {
|
||||
return ClientboundPingConfigurationPacket(id = buffer.readInt())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
@@ -23,8 +23,8 @@ public data class ClientboundRegistryDataPacket(
|
||||
val entries: List<RegistryEntry>,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundRegistryDataPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundRegistryDataPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundRegistryDataPacket {
|
||||
val id = buffer.readIdentifier()
|
||||
val entryCount = buffer.readVarInt()
|
||||
val entries = ArrayList<RegistryEntry>(entryCount)
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readUuid
|
||||
@@ -15,8 +15,8 @@ import kotlin.uuid.Uuid
|
||||
|
||||
public data class ClientboundRemoveResourcePackPacket(val uuid: Uuid) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundRemoveResourcePackPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundRemoveResourcePackPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundRemoveResourcePackPacket {
|
||||
val uuid = buffer.readUuid()
|
||||
return ClientboundRemoveResourcePackPacket(uuid)
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,12 +7,12 @@
|
||||
|
||||
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.PacketCodec
|
||||
|
||||
public data object ClientboundResetChatPacket : MinecraftPacket,
|
||||
PacketCodec<ClientboundResetChatPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundResetChatPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundResetChatPacket = ClientboundResetChatPacket
|
||||
}
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundSelectKnownPacksPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket {
|
||||
val packsCount = buffer.readVarInt()
|
||||
val packs = List(packsCount) { KnownPacks.decode(buffer) }
|
||||
return ClientboundSelectKnownPacksPacket(packs)
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundServerLinksPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundServerLinksPacket {
|
||||
val count = buffer.readVarInt()
|
||||
val links = ArrayList<ServerLink>(count)
|
||||
repeat(count) { links.add(ServerLink.decode(buffer)) }
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readPrefixedByteArray
|
||||
@@ -22,8 +22,8 @@ public data class ClientboundStoreCookiePacket(
|
||||
val payload: ByteArray,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundStoreCookiePacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundStoreCookiePacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundStoreCookiePacket {
|
||||
val key = buffer.readIdentifier()
|
||||
val payload = buffer.readPrefixedByteArray()
|
||||
return ClientboundStoreCookiePacket(key, payload)
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundTransferPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundTransferPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundTransferPacket {
|
||||
val host = buffer.readMcString()
|
||||
val port = buffer.readVarInt()
|
||||
return ClientboundTransferPacket(host, port)
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 {
|
||||
internal companion object Codec : PacketCodec<ClientboundUpdateEnabledFeaturesPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateEnabledFeaturesPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundUpdateEnabledFeaturesPacket {
|
||||
val featureCount = buffer.readVarInt()
|
||||
val features = ArrayList<Identifier>(featureCount)
|
||||
repeat(featureCount) { features.add(buffer.readIdentifier()) }
|
||||
|
||||
+7
-7
@@ -7,7 +7,7 @@
|
||||
|
||||
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.PacketCodec
|
||||
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 TaggedRegistry(val registryId: Identifier, val tags: List<Tag>) {
|
||||
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.writeVarInt(value.tags.size)
|
||||
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 tagCount = buffer.readVarInt()
|
||||
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) {
|
||||
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.writeVarInt(value.entries.size)
|
||||
value.entries.forEach { buffer.writeVarInt(it) }
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): Tag {
|
||||
override fun decode(buffer: BytesBuffer): Tag {
|
||||
val name = buffer.readIdentifier()
|
||||
val entrySize = buffer.readVarInt()
|
||||
val entries = ArrayList<Int>(entrySize)
|
||||
@@ -69,8 +69,8 @@ public data class ClientboundUpdateTagsPacket(val registries: List<TaggedRegistr
|
||||
}
|
||||
|
||||
internal companion object Codec : PacketCodec<ClientboundUpdateTagsPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
|
||||
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateTagsPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ClientboundUpdateTagsPacket {
|
||||
val registryCount = buffer.readVarInt()
|
||||
val registries = ArrayList<TaggedRegistry>(registryCount)
|
||||
repeat(registryCount) { registries.add(TaggedRegistry.decode(buffer)) }
|
||||
|
||||
+3
-3
@@ -7,13 +7,13 @@
|
||||
|
||||
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.MinecraftPacket
|
||||
|
||||
public data object ServerboundAcceptCodeOfConductPacket : MinecraftPacket,
|
||||
PacketCodec<ServerboundAcceptCodeOfConductPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
|
||||
override fun encode(buffer: BytesBuffer, value: ServerboundAcceptCodeOfConductPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ServerboundAcceptCodeOfConductPacket =
|
||||
ServerboundAcceptCodeOfConductPacket
|
||||
}
|
||||
+3
-3
@@ -7,13 +7,13 @@
|
||||
|
||||
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.MinecraftPacket
|
||||
|
||||
public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket,
|
||||
PacketCodec<ServerboundAckFinishConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
|
||||
override fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {}
|
||||
override fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket =
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
|
||||
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.MinecraftPacket
|
||||
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 {
|
||||
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)
|
||||
if (value.payload != null) {
|
||||
buffer.writeBoolean(true)
|
||||
@@ -25,7 +25,7 @@ public data class ServerboundCookieResponsePacket(val key: Identifier, val paylo
|
||||
} else buffer.writeBoolean(false)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
|
||||
override fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket =
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -7,17 +7,17 @@
|
||||
|
||||
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.MinecraftPacket
|
||||
|
||||
public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket {
|
||||
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)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
|
||||
override fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket =
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
Loaded 100 of 318 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in New Issue
Block a user