Update all codec to suspend; added a default ProtocolCryptoContext implementation
This commit is contained in:
296 files changed
+2199
-1818
No files matched your search
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
public expect class BytesBuffer {
|
||||
public constructor()
|
||||
public constructor(bytes: ByteArray)
|
||||
|
||||
public fun writeByte(value: Byte)
|
||||
public fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public fun writeDouble(value: Double, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public fun writeFloat(value: Float, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public fun writeBytes(bytes: ByteArray)
|
||||
public fun writeBoolean(value: Boolean)
|
||||
|
||||
public fun readByte(): Byte
|
||||
public fun readUByte(): UByte
|
||||
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||
public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||
public fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
||||
public fun readDouble(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Double
|
||||
public fun readFloat(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Float
|
||||
public fun readBytes(length: Int): ByteArray
|
||||
public fun readBoolean(): Boolean
|
||||
public fun readRemainingBytes(): ByteArray
|
||||
|
||||
public fun toByteArray(): ByteArray
|
||||
public fun hasRemaining(): Boolean
|
||||
public fun close()
|
||||
public val size: Int
|
||||
public val remaining: Long
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
|
||||
|
||||
public fun ReadChannel.readPacketFrame(): BytesBuffer {
|
||||
val length = this.readVarInt()
|
||||
return this.readBytes(length).wrap()
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
public expect open class ReadChannel() {
|
||||
public open fun readByte(): Byte
|
||||
public open fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||
public open fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||
public open fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
||||
public open fun readBytes(length: Int): ByteArray
|
||||
public open fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
||||
}
|
||||
|
||||
public expect open class WriteChannel() {
|
||||
public open fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||
public open fun flush()
|
||||
}
|
||||
|
||||
public fun ReadChannel.readVarInt(): Int {
|
||||
var numRead = 0
|
||||
var result = 0
|
||||
var read: Byte
|
||||
do {
|
||||
read = this.readByte()
|
||||
val value = (read.toInt() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/4
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
public interface Encoder<in T> {
|
||||
public fun encode(buffer: BytesBuffer, value: T)
|
||||
}
|
||||
|
||||
public interface Decoder<out T> {
|
||||
public fun decode(buffer: BytesBuffer): T
|
||||
}
|
||||
|
||||
public interface PacketCodec<T> : Encoder<T>, Decoder<T>
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun <T> BytesBuffer.write(value: T, encoder: Encoder<T>): Unit = encoder.encode(this, value)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun <T> BytesBuffer.read(decoder: Decoder<T>): T = decoder.decode(this)
|
||||
|
||||
public fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) {
|
||||
if (length <= 0) return
|
||||
val bytes = source.readBytes(length.toInt())
|
||||
this.writeBytes(bytes)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.crypto
|
||||
|
||||
public fun interface AuthenticationProvider {
|
||||
public suspend fun joinServer(url: String, accessToken: String, uuid: String, serverIdHash: String)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.crypto
|
||||
|
||||
public interface NetworkCipher {
|
||||
public suspend fun encrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
public suspend fun decrypt(buffer: ByteArray, offset: Int, length: Int)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.crypto
|
||||
|
||||
public data class ProtocolContext(
|
||||
val rsaEncryptor: RSA1024Encryptor,
|
||||
val sha1Hasher: Sha1Hasher,
|
||||
val cipherFactory: (sharedKey: ByteArray) -> NetworkCipher,
|
||||
val authProvider: AuthenticationProvider,
|
||||
)
|
||||
|
||||
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 fun build(): ProtocolContext = ProtocolContext(
|
||||
rsaEncryptor = if (::rsaEncryptor.isInitialized) rsaEncryptor else error("rsaEncryptor is required"),
|
||||
sha1Hasher = if (::sha1Hasher.isInitialized) sha1Hasher else error("sha1Hasher is required"),
|
||||
cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"),
|
||||
authProvider = if (::authProvider.isInitialized) if (onlineMode) authProvider
|
||||
else error("authProvider is required") else error("authProvider is required")
|
||||
)
|
||||
}
|
||||
|
||||
public fun interface RSA1024Encryptor {
|
||||
public suspend fun encrypt(key: ByteArray, data: ByteArray): ByteArray
|
||||
}
|
||||
|
||||
public fun interface Sha1Hasher {
|
||||
public suspend fun hash(serverId: String, secretKey: ByteArray, publicKey: ByteArray): String
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public object VarIntCodec : PacketCodec<Int> {
|
||||
override fun encode(buffer: BytesBuffer, value: Int) {
|
||||
var v = value
|
||||
while (true) {
|
||||
if ((v and 0x7F.inv()) == 0) {
|
||||
buffer.writeByte(v.toByte())
|
||||
return
|
||||
}
|
||||
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
|
||||
v = v ushr 7
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: BytesBuffer): Int {
|
||||
var numRead = 0
|
||||
var result = 0
|
||||
var read: Byte
|
||||
do {
|
||||
read = buffer.readByte()
|
||||
val value = (read.toInt() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public object VarLongCodec : PacketCodec<Long> {
|
||||
override fun encode(buffer: BytesBuffer, value: Long) {
|
||||
var v = value
|
||||
while (true) {
|
||||
if ((v and 0x7FL.inv()) == 0L) {
|
||||
buffer.writeByte(v.toByte())
|
||||
return
|
||||
}
|
||||
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
|
||||
v = v ushr 7
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: BytesBuffer): Long {
|
||||
var numRead = 0
|
||||
var result = 0L
|
||||
var read: Byte
|
||||
do {
|
||||
read = buffer.readByte()
|
||||
val value = (read.toLong() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 10) throw IllegalArgumentException("VarLong is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public object McStringCodec : PacketCodec<String> {
|
||||
override fun encode(buffer: BytesBuffer, value: String) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
VarIntCodec.encode(buffer, bytes.size)
|
||||
buffer.writeBytes(bytes)
|
||||
}
|
||||
|
||||
override fun decode(buffer: BytesBuffer): String {
|
||||
val length = VarIntCodec.decode(buffer)
|
||||
val bytes = buffer.readBytes(length)
|
||||
return bytes.decodeToString()
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface IdOrX<out T> {
|
||||
public data class Inline<T>(val value: T) : IdOrX<T>
|
||||
public data class Reference(val registryId: Int) : IdOrX<Nothing>
|
||||
}
|
||||
|
||||
public inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
|
||||
when (value) {
|
||||
is IdOrX.Inline -> {
|
||||
this.writeVarInt(0)
|
||||
this.writeX(value.value)
|
||||
}
|
||||
|
||||
is IdOrX.Reference -> this.writeVarInt(value.registryId + 1)
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> {
|
||||
val id = this.readVarInt()
|
||||
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits ->
|
||||
this.writeLong(mostSignificantBits)
|
||||
this.writeLong(leastSignificantBits)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.readUuid(): Uuid {
|
||||
val most = this.readLong()
|
||||
val least = this.readLong()
|
||||
return Uuid.fromLongs(most, least)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
|
||||
public fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
|
||||
|
||||
public fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
|
||||
public fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
|
||||
|
||||
public fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
|
||||
public fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
|
||||
|
||||
public fun BytesBuffer.readPrefixedByteArray(): ByteArray {
|
||||
val length = this.readVarInt()
|
||||
val data = this.readBytes(length)
|
||||
return data
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
|
||||
this.writeVarInt(data.size)
|
||||
this.writeBytes(data)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
|
||||
if (data != null) {
|
||||
this.writeBoolean(true)
|
||||
this.writeVarInt(data.size)
|
||||
this.writeBytes(data)
|
||||
} else this.writeBoolean(false)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.readPrefixedStringArray(): List<String> {
|
||||
val length = readVarInt()
|
||||
val list = ArrayList<String>(length)
|
||||
repeat(length) { list.add(readMcString()) }
|
||||
return list
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
|
||||
writeVarInt(value.size)
|
||||
for (item in value) writeMcString(item)
|
||||
}
|
||||
|
||||
public inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? {
|
||||
val hasData = this.readBoolean()
|
||||
return if (hasData) block.invoke(this) else null
|
||||
}
|
||||
|
||||
/**
|
||||
* buffer.writeOptional(value.someValue) { writeBlockPos(it) }
|
||||
*/
|
||||
public inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
|
||||
if (value != null) {
|
||||
this.writeBoolean(true)
|
||||
block.invoke(this, value)
|
||||
} else this.writeBoolean(false)
|
||||
}
|
||||
|
||||
public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
|
||||
val count = this.readVarInt()
|
||||
val list = ArrayList<T>(count)
|
||||
repeat(count) { _ -> list.add(this.reader()) }
|
||||
return list
|
||||
}
|
||||
|
||||
public inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) {
|
||||
this.writeVarInt(list.size)
|
||||
for (item in list) this.writer(item)
|
||||
}
|
||||
|
||||
public fun BytesBuffer.readBitSet(): LongArray {
|
||||
val count = this.readVarInt()
|
||||
return LongArray(count) { this.readLong() }
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writeBitSet(data: LongArray) {
|
||||
this.writeVarInt(data.size)
|
||||
for (i in data.indices) this.writeLong(data[i])
|
||||
}
|
||||
|
||||
public fun LongArray.countSetBits(): Int {
|
||||
var count = 0
|
||||
for (i in indices) count += this[i].countOneBits()
|
||||
return count
|
||||
}
|
||||
|
||||
public fun LongArray.getBit(bitIndex: Int): Boolean {
|
||||
val longIndex = bitIndex shr 6
|
||||
if (longIndex !in this.indices) return false
|
||||
val bitOffset = bitIndex and 63
|
||||
return (this[longIndex] and (1L shl bitOffset)) != 0L
|
||||
}
|
||||
|
||||
public sealed interface IdSet {
|
||||
public data class Tag(val tagName: String) : IdSet
|
||||
public data class Entries(val ids: List<Int>) : IdSet
|
||||
}
|
||||
|
||||
public fun BytesBuffer.readIdSet(): IdSet {
|
||||
val type = this.readVarInt()
|
||||
return if (type == 0) {
|
||||
IdSet.Tag(tagName = this.readMcString())
|
||||
} else {
|
||||
val count = type - 1
|
||||
val ids = ArrayList<Int>(count)
|
||||
(0 until count).forEach { _ -> ids.add(this.readVarInt()) }
|
||||
IdSet.Entries(ids)
|
||||
}
|
||||
}
|
||||
|
||||
public fun BytesBuffer.writeIdSet(idSet: IdSet) {
|
||||
when (idSet) {
|
||||
is IdSet.Tag -> {
|
||||
this.writeVarInt(0)
|
||||
this.writeMcString(idSet.tagName)
|
||||
}
|
||||
|
||||
is IdSet.Entries -> {
|
||||
this.writeVarInt(idSet.ids.size + 1)
|
||||
for (id in idSet.ids) this.writeVarInt(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/4
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.packet
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
public interface Encoder<in T> {
|
||||
public suspend fun encode(buffer: BytesBuffer, value: T)
|
||||
}
|
||||
|
||||
public interface Decoder<out T> {
|
||||
public suspend fun decode(buffer: BytesBuffer): T
|
||||
}
|
||||
|
||||
public interface PacketCodec<T> : Encoder<T>, Decoder<T>
|
||||
|
||||
public suspend fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) {
|
||||
if (length <= 0) return
|
||||
val bytes = source.readBytes(length.toInt())
|
||||
this.writeBytes(bytes)
|
||||
}
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
package cn.rtast.libmc.common.packet
|
||||
|
||||
import cn.rtast.libmc.common.BytesBuffer
|
||||
import cn.rtast.libmc.common.PacketCodec
|
||||
import cn.rtast.libmc.common.writeVarInt
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
import cn.rtast.libmc.common.primitives.writeVarInt
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class PacketRegistry {
|
||||
@@ -29,10 +28,10 @@ public class PacketRegistry {
|
||||
register(id, T::class, codec)
|
||||
}
|
||||
|
||||
public fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
|
||||
public suspend fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket =
|
||||
idToCodec[packetId]?.decode(buffer) ?: UnknownPacket(packetId, buffer.readBytes(buffer.remaining.toInt()))
|
||||
|
||||
public fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
|
||||
public suspend fun <T : MinecraftPacket> encodePacket(buffer: BytesBuffer, packet: T) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val info = requireNotNull(classToInfo[packet::class]) {
|
||||
"Unregistered Packet ${packet::class.simpleName}"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
public suspend fun BytesBuffer.readBitSet(): LongArray {
|
||||
val count = this.readVarInt()
|
||||
return LongArray(count) { this.readLong() }
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeBitSet(data: LongArray) {
|
||||
this.writeVarInt(data.size)
|
||||
for (i in data.indices) this.writeLong(data[i])
|
||||
}
|
||||
|
||||
public fun LongArray.countSetBits(): Int {
|
||||
var count = 0
|
||||
for (i in indices) count += this[i].countOneBits()
|
||||
return count
|
||||
}
|
||||
|
||||
public fun LongArray.getBit(bitIndex: Int): Boolean {
|
||||
val longIndex = bitIndex shr 6
|
||||
if (longIndex !in this.indices) return false
|
||||
val bitOffset = bitIndex and 63
|
||||
return (this[longIndex] and (1L shl bitOffset)) != 0L
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
public sealed interface IdOrX<out T> {
|
||||
public data class Inline<T>(val value: T) : IdOrX<T>
|
||||
public data class Reference(val registryId: Int) : IdOrX<Nothing>
|
||||
}
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.writeIdOrX(value: IdOrX<T>, writeX: BytesBuffer.(T) -> Unit) {
|
||||
when (value) {
|
||||
is IdOrX.Inline -> {
|
||||
this.writeVarInt(0)
|
||||
this.writeX(value.value)
|
||||
}
|
||||
|
||||
is IdOrX.Reference -> this.writeVarInt(value.registryId + 1)
|
||||
}
|
||||
}
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.readIdOrX(readX: BytesBuffer.() -> T): IdOrX<T> {
|
||||
val id = this.readVarInt()
|
||||
return if (id == 0) IdOrX.Inline(this.readX()) else IdOrX.Reference(id - 1)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
public sealed interface IdSet {
|
||||
public data class Tag(val tagName: String) : IdSet
|
||||
public data class Entries(val ids: List<Int>) : IdSet
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readIdSet(): IdSet {
|
||||
val type = this.readVarInt()
|
||||
return if (type == 0) {
|
||||
IdSet.Tag(tagName = this.readMcString())
|
||||
} else {
|
||||
val count = type - 1
|
||||
val ids = ArrayList<Int>(count)
|
||||
(0 until count).forEach { _ -> ids.add(this.readVarInt()) }
|
||||
IdSet.Entries(ids)
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeIdSet(idSet: IdSet) {
|
||||
when (idSet) {
|
||||
is IdSet.Tag -> {
|
||||
this.writeVarInt(0)
|
||||
this.writeMcString(idSet.tagName)
|
||||
}
|
||||
|
||||
is IdSet.Entries -> {
|
||||
this.writeVarInt(idSet.ids.size + 1)
|
||||
for (id in idSet.ids) this.writeVarInt(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
import cn.rtast.libmc.common.packet.PacketCodec
|
||||
|
||||
public object McStringCodec : PacketCodec<String> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: String) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
VarIntCodec.encode(buffer, bytes.size)
|
||||
buffer.writeBytes(bytes)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): String {
|
||||
val length = VarIntCodec.decode(buffer)
|
||||
val bytes = buffer.readBytes(length)
|
||||
return bytes.decodeToString()
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value)
|
||||
public suspend fun BytesBuffer.readMcString(): String = McStringCodec.decode(this)
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? {
|
||||
val hasData = this.readBoolean()
|
||||
return if (hasData) block.invoke(this) else null
|
||||
}
|
||||
|
||||
/**
|
||||
* buffer.writeOptional(value.someValue) { writeBlockPos(it) }
|
||||
*/
|
||||
public suspend inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
|
||||
if (value != null) {
|
||||
this.writeBoolean(true)
|
||||
block.invoke(this, value)
|
||||
} else this.writeBoolean(false)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
|
||||
|
||||
public suspend fun BytesBuffer.readPrefixedByteArray(): ByteArray {
|
||||
val length = this.readVarInt()
|
||||
val data = this.readBytes(length)
|
||||
return data
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writePrefixedByteArray(data: ByteArray) {
|
||||
this.writeVarInt(data.size)
|
||||
this.writeBytes(data)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeOptionalPrefixedByteArray(data: ByteArray?) {
|
||||
if (data != null) {
|
||||
this.writeBoolean(true)
|
||||
this.writeVarInt(data.size)
|
||||
this.writeBytes(data)
|
||||
} else this.writeBoolean(false)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readPrefixedStringArray(): List<String> {
|
||||
val length = readVarInt()
|
||||
val list = ArrayList<String>(length)
|
||||
repeat(length) { list.add(readMcString()) }
|
||||
return list
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
|
||||
writeVarInt(value.size)
|
||||
for (item in value) writeMcString(item)
|
||||
}
|
||||
|
||||
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
|
||||
val count = this.readVarInt()
|
||||
val list = ArrayList<T>(count)
|
||||
repeat(count) { _ -> list.add(this.reader()) }
|
||||
return list
|
||||
}
|
||||
|
||||
public suspend inline fun <T> BytesBuffer.writePrefixed(list: List<T>, writer: BytesBuffer.(T) -> Unit) {
|
||||
this.writeVarInt(list.size)
|
||||
for (item in list) this.writer(item)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public suspend fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits ->
|
||||
this.writeLong(mostSignificantBits)
|
||||
this.writeLong(leastSignificantBits)
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.readUuid(): Uuid {
|
||||
val most = this.readLong()
|
||||
val least = this.readLong()
|
||||
return Uuid.fromLongs(most, least)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
import cn.rtast.libmc.common.packet.PacketCodec
|
||||
|
||||
public object VarIntCodec : PacketCodec<Int> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: Int) {
|
||||
var v = value
|
||||
while (true) {
|
||||
if ((v and 0x7F.inv()) == 0) {
|
||||
buffer.writeByte(v.toByte())
|
||||
return
|
||||
}
|
||||
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
|
||||
v = v ushr 7
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): Int {
|
||||
var numRead = 0
|
||||
var result = 0
|
||||
var read: Byte
|
||||
do {
|
||||
read = buffer.readByte()
|
||||
val value = (read.toInt() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value)
|
||||
public suspend fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this)
|
||||
|
||||
public suspend fun BytesBuffer.writeVarLong(value: Long): Unit = VarLongCodec.encode(this, value)
|
||||
public suspend fun BytesBuffer.readVarLong(): Long = VarLongCodec.decode(this)
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.primitives
|
||||
|
||||
import cn.rtast.libmc.common.stream.BytesBuffer
|
||||
import cn.rtast.libmc.common.packet.PacketCodec
|
||||
|
||||
public object VarLongCodec : PacketCodec<Long> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: Long) {
|
||||
var v = value
|
||||
while (true) {
|
||||
if ((v and 0x7FL.inv()) == 0L) {
|
||||
buffer.writeByte(v.toByte())
|
||||
return
|
||||
}
|
||||
buffer.writeByte(((v and 0x7F) or 0x80).toByte())
|
||||
v = v ushr 7
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): Long {
|
||||
var numRead = 0
|
||||
var result = 0L
|
||||
var read: Byte
|
||||
do {
|
||||
read = buffer.readByte()
|
||||
val value = (read.toLong() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 10) throw IllegalArgumentException("VarLong is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
public expect class BytesBuffer {
|
||||
public constructor()
|
||||
public constructor(bytes: ByteArray)
|
||||
|
||||
public suspend fun writeByte(value: Byte)
|
||||
public suspend fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public suspend fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public suspend fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public suspend fun writeDouble(value: Double, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public suspend fun writeFloat(value: Float, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||
public suspend fun writeBytes(bytes: ByteArray)
|
||||
public suspend fun writeBoolean(value: Boolean)
|
||||
|
||||
public suspend fun readByte(): Byte
|
||||
public suspend fun readUByte(): UByte
|
||||
public suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||
public suspend fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||
public suspend fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
||||
public suspend fun readDouble(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Double
|
||||
public suspend fun readFloat(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Float
|
||||
public suspend fun readBytes(length: Int): ByteArray
|
||||
public suspend fun readBoolean(): Boolean
|
||||
public suspend fun readRemainingBytes(): ByteArray
|
||||
|
||||
public suspend fun toByteArray(): ByteArray
|
||||
public suspend fun hasRemaining(): Boolean
|
||||
public suspend fun close()
|
||||
public val size: Int
|
||||
public val remaining: Long
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
|
||||
|
||||
public suspend fun ReadChannel.readPacketFrame(): BytesBuffer {
|
||||
val length = this.readVarInt()
|
||||
return this.readBytes(length).wrap()
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
public enum class ByteOrder {
|
||||
BIG_ENDIAN, LITTLE_ENDIAN
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
public expect open class ReadChannel() {
|
||||
public open suspend fun readByte(): Byte
|
||||
public open suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||
public open suspend fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||
public open suspend fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
||||
public open suspend fun readBytes(length: Int): ByteArray
|
||||
public open suspend fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
||||
}
|
||||
|
||||
public expect open class WriteChannel() {
|
||||
public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||
public open suspend fun flush()
|
||||
}
|
||||
|
||||
public suspend fun ReadChannel.readVarInt(): Int {
|
||||
var numRead = 0
|
||||
var result = 0
|
||||
var read: Byte
|
||||
do {
|
||||
read = this.readByte()
|
||||
val value = (read.toInt() and 0x7F)
|
||||
result = result or (value shl (7 * numRead))
|
||||
numRead++
|
||||
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
|
||||
} while ((read.toInt() and 0x80) != 0)
|
||||
return result
|
||||
}
|
||||
+4
-2
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import cn.rtast.libmc.common.LibMCContext
|
||||
|
||||
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
|
||||
public fun openReadChannel(): ReadChannel
|
||||
@@ -14,6 +16,6 @@ public expect class Socket public constructor(host: String, port: Int, context:
|
||||
}
|
||||
|
||||
public expect class UdpSocket public constructor(host: String, port: Int, context: LibMCContext) {
|
||||
public fun sendAndReceive(data: ByteArray): ByteArray
|
||||
public suspend fun sendAndReceive(data: ByteArray): ByteArray
|
||||
public fun close()
|
||||
}
|
||||
+26
-24
@@ -4,8 +4,10 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
public actual class BytesBuffer {
|
||||
@@ -19,12 +21,12 @@ public actual class BytesBuffer {
|
||||
outStream.write(bytes)
|
||||
}
|
||||
|
||||
public actual fun writeByte(value: Byte) {
|
||||
public actual suspend fun writeByte(value: Byte) {
|
||||
readBuffer = null
|
||||
outStream.write(value.toInt())
|
||||
}
|
||||
|
||||
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
||||
public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
val v = value.toInt()
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
@@ -36,7 +38,7 @@ public actual class BytesBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
||||
public actual suspend fun writeInt(value: Int, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
outStream.write(value shr 24)
|
||||
@@ -51,7 +53,7 @@ public actual class BytesBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
||||
public actual suspend fun writeLong(value: Long, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt())
|
||||
@@ -60,22 +62,22 @@ public actual class BytesBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
public actual fun writeDouble(value: Double, endian: ByteOrder) {
|
||||
public actual suspend fun writeDouble(value: Double, endian: ByteOrder) {
|
||||
writeLong(value.toRawBits(), endian)
|
||||
}
|
||||
|
||||
public actual fun writeFloat(value: Float, endian: ByteOrder) {
|
||||
public actual suspend fun writeFloat(value: Float, endian: ByteOrder) {
|
||||
writeInt(value.toRawBits(), endian)
|
||||
}
|
||||
|
||||
public actual fun writeBytes(bytes: ByteArray) {
|
||||
public actual suspend fun writeBytes(bytes: ByteArray) {
|
||||
readBuffer = null
|
||||
outStream.write(bytes)
|
||||
withContext(Dispatchers.IO) { outStream.write(bytes) }
|
||||
}
|
||||
|
||||
public actual fun writeBoolean(value: Boolean): Unit = writeByte(if (value) 0x01 else 0x00)
|
||||
public actual suspend fun writeBoolean(value: Boolean): Unit = writeByte(if (value) 0x01 else 0x00)
|
||||
|
||||
private fun ensureReadArray(): ByteArray {
|
||||
private suspend fun ensureReadArray(): ByteArray {
|
||||
var buf = readBuffer
|
||||
if (buf == null) {
|
||||
buf = outStream.toByteArray()
|
||||
@@ -84,26 +86,26 @@ public actual class BytesBuffer {
|
||||
return buf
|
||||
}
|
||||
|
||||
public actual fun readByte(): Byte {
|
||||
public actual suspend fun readByte(): Byte {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||
return array[readOffset++]
|
||||
}
|
||||
|
||||
public actual fun readUByte(): UByte = this.readByte().toUByte()
|
||||
public actual fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
||||
public actual fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
||||
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||
public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
|
||||
public actual suspend fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
||||
public actual suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
||||
public actual suspend fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||
|
||||
public actual fun readDouble(endian: ByteOrder): Double {
|
||||
public actual suspend fun readDouble(endian: ByteOrder): Double {
|
||||
return Double.fromBits(readLong(endian))
|
||||
}
|
||||
|
||||
public actual fun readFloat(endian: ByteOrder): Float {
|
||||
public actual suspend fun readFloat(endian: ByteOrder): Float {
|
||||
return Float.fromBits(readInt(endian))
|
||||
}
|
||||
|
||||
public actual fun readBytes(length: Int): ByteArray {
|
||||
public actual suspend fun readBytes(length: Int): ByteArray {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||
val result = array.copyOfRange(readOffset, readOffset + length)
|
||||
@@ -111,9 +113,9 @@ public actual class BytesBuffer {
|
||||
return result
|
||||
}
|
||||
|
||||
public actual fun readBoolean(): Boolean = this.readByte() != 0x00.toByte()
|
||||
public actual suspend fun readBoolean(): Boolean = this.readByte() != 0x00.toByte()
|
||||
|
||||
public actual fun readRemainingBytes(): ByteArray {
|
||||
public actual suspend fun readRemainingBytes(): ByteArray {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset >= array.size) return byteArrayOf()
|
||||
val result = array.copyOfRange(readOffset, array.size)
|
||||
@@ -121,10 +123,10 @@ public actual class BytesBuffer {
|
||||
return result
|
||||
}
|
||||
|
||||
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
|
||||
public actual fun hasRemaining(): Boolean = readOffset < ensureReadArray().size
|
||||
public actual suspend fun toByteArray(): ByteArray = outStream.toByteArray()
|
||||
public actual suspend fun hasRemaining(): Boolean = readOffset < ensureReadArray().size
|
||||
|
||||
public actual fun close(): Unit = outStream.close()
|
||||
public actual suspend fun close(): Unit = withContext(Dispatchers.IO) { outStream.close() }
|
||||
public actual val size: Int get() = outStream.size()
|
||||
public actual val remaining: Long get() = (outStream.size() - readOffset).coerceAtLeast(0).toLong()
|
||||
}
|
||||
+9
-9
@@ -4,7 +4,7 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import java.io.EOFException
|
||||
import java.io.InputStream
|
||||
@@ -17,7 +17,7 @@ public actual open class ReadChannel public actual constructor() {
|
||||
this._inputStream = inputStream
|
||||
}
|
||||
|
||||
public actual open fun readFully(out: ByteArray, start: Int, end: Int) {
|
||||
public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int) {
|
||||
var bytesRead = 0
|
||||
val length = end - start
|
||||
while (bytesRead < length) {
|
||||
@@ -27,21 +27,21 @@ public actual open class ReadChannel public actual constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
public actual open fun readByte(): Byte {
|
||||
public actual open suspend fun readByte(): Byte {
|
||||
val buf = ByteArray(1)
|
||||
readFully(buf, 0, 1)
|
||||
return buf[0]
|
||||
}
|
||||
|
||||
public actual open fun readBytes(length: Int): ByteArray {
|
||||
public actual open suspend fun readBytes(length: Int): ByteArray {
|
||||
val bytes = ByteArray(length)
|
||||
readFully(bytes, 0, length)
|
||||
return bytes
|
||||
}
|
||||
|
||||
public actual open fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
||||
public actual open fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
||||
public actual open fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||
public actual open suspend fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
||||
public actual open suspend fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
||||
public actual open suspend fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||
}
|
||||
|
||||
public actual open class WriteChannel public actual constructor() {
|
||||
@@ -51,8 +51,8 @@ public actual open class WriteChannel public actual constructor() {
|
||||
_outputStream = outputStream
|
||||
}
|
||||
|
||||
public actual open fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
_outputStream.write(value, startIndex, endIndex - startIndex)
|
||||
|
||||
public actual open fun flush(): Unit = _outputStream.flush()
|
||||
public actual open suspend fun flush(): Unit = _outputStream.flush()
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
internal fun ByteArray.toShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short {
|
||||
val b1 = this[0].toInt() and 0xFF
|
||||
+6
-4
@@ -4,8 +4,11 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import cn.rtast.libmc.common.LibMCContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetSocketAddress
|
||||
@@ -25,13 +28,12 @@ public actual class UdpSocket public actual constructor(host: String, port: Int,
|
||||
connect(InetSocketAddress(host, port))
|
||||
}
|
||||
|
||||
public actual fun sendAndReceive(data: ByteArray): ByteArray {
|
||||
public actual suspend fun sendAndReceive(data: ByteArray): ByteArray = withContext(Dispatchers.IO) {
|
||||
socket.send(DatagramPacket(data, data.size))
|
||||
|
||||
val buf = ByteArray(2048)
|
||||
val receivePacket = DatagramPacket(buf, buf.size)
|
||||
socket.receive(receivePacket)
|
||||
return buf.copyOf(receivePacket.length)
|
||||
return@withContext buf.copyOf(receivePacket.length)
|
||||
}
|
||||
|
||||
public actual fun close(): Unit = socket.close()
|
||||
+22
-22
@@ -4,7 +4,7 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.*
|
||||
@@ -21,53 +21,53 @@ public actual class BytesBuffer {
|
||||
_delegateBuf = Buffer().apply { write(bytes) }
|
||||
}
|
||||
|
||||
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
|
||||
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
||||
public actual suspend fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
|
||||
public actual suspend fun writeShort(value: Short, endian: ByteOrder) {
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value)
|
||||
else _delegateBuf.writeShortLe(value)
|
||||
}
|
||||
|
||||
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
||||
public actual suspend fun writeInt(value: Int, endian: ByteOrder) {
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
|
||||
else _delegateBuf.writeIntLe(value)
|
||||
}
|
||||
|
||||
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
||||
public actual suspend fun writeLong(value: Long, endian: ByteOrder) {
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
|
||||
else _delegateBuf.writeLongLe(value)
|
||||
}
|
||||
|
||||
public actual fun writeDouble(value: Double, endian: ByteOrder): Unit =
|
||||
public actual suspend fun writeDouble(value: Double, endian: ByteOrder): Unit =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeDouble(value) else _delegateBuf.writeDoubleLe(value)
|
||||
|
||||
public actual fun writeFloat(value: Float, endian: ByteOrder): Unit =
|
||||
public actual suspend fun writeFloat(value: Float, endian: ByteOrder): Unit =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeFloat(value) else _delegateBuf.writeFloatLe(value)
|
||||
|
||||
public actual fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
|
||||
public actual fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
|
||||
public actual suspend fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
|
||||
public actual suspend fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
|
||||
|
||||
public actual fun readByte(): Byte = _delegateBuf.readByte()
|
||||
public actual fun readUByte(): UByte = this.readByte().toUByte()
|
||||
public actual fun readShort(endian: ByteOrder): Short =
|
||||
public actual suspend fun readByte(): Byte = _delegateBuf.readByte()
|
||||
public actual suspend fun readUByte(): UByte = this.readByte().toUByte()
|
||||
public actual suspend fun readShort(endian: ByteOrder): Short =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readShort() else _delegateBuf.readShortLe()
|
||||
|
||||
public actual fun readInt(endian: ByteOrder): Int =
|
||||
public actual suspend fun readInt(endian: ByteOrder): Int =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe()
|
||||
|
||||
public actual fun readLong(endian: ByteOrder): Long =
|
||||
public actual suspend fun readLong(endian: ByteOrder): Long =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe()
|
||||
|
||||
public actual fun readDouble(endian: ByteOrder): Double =
|
||||
public actual suspend fun readDouble(endian: ByteOrder): Double =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readDouble() else _delegateBuf.readDoubleLe()
|
||||
|
||||
public actual fun readFloat(endian: ByteOrder): Float =
|
||||
public actual suspend fun readFloat(endian: ByteOrder): Float =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readFloat() else _delegateBuf.readFloatLe()
|
||||
|
||||
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
|
||||
public actual fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
|
||||
public actual fun readRemainingBytes(): ByteArray = this.toByteArray()
|
||||
public actual suspend fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
|
||||
public actual suspend fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
|
||||
public actual suspend fun readRemainingBytes(): ByteArray = this.toByteArray()
|
||||
|
||||
public actual fun toByteArray(): ByteArray {
|
||||
public actual suspend fun toByteArray(): ByteArray {
|
||||
val copy = _delegateBuf.peek()
|
||||
return try {
|
||||
copy.readByteArray()
|
||||
@@ -76,8 +76,8 @@ public actual class BytesBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
public actual fun hasRemaining(): Boolean = !_delegateBuf.exhausted()
|
||||
public actual fun close(): Unit = _delegateBuf.close()
|
||||
public actual suspend fun hasRemaining(): Boolean = !_delegateBuf.exhausted()
|
||||
public actual suspend fun close(): Unit = _delegateBuf.close()
|
||||
|
||||
public actual val size: Int get() = _delegateBuf.size.toInt()
|
||||
public actual val remaining: Long get() = _delegateBuf.remaining
|
||||
+11
-12
@@ -4,11 +4,10 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import io.ktor.utils.io.*
|
||||
import io.ktor.utils.io.bits.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
public actual open class ReadChannel public actual constructor() {
|
||||
|
||||
@@ -18,18 +17,18 @@ public actual open class ReadChannel public actual constructor() {
|
||||
this._readChannel = readChannel
|
||||
}
|
||||
|
||||
public actual open fun readByte(): Byte = runBlocking { _readChannel.readByte() }
|
||||
public actual open fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) }
|
||||
public actual open fun readFully(out: ByteArray, start: Int, end: Int): Unit =
|
||||
runBlocking { _readChannel.readFully(out, start, end) }
|
||||
public actual open suspend fun readByte(): Byte = _readChannel.readByte()
|
||||
public actual open suspend fun readBytes(length: Int): ByteArray = _readChannel.readByteArray(length)
|
||||
public actual open suspend fun readFully(out: ByteArray, start: Int, end: Int): Unit =
|
||||
_readChannel.readFully(out, start, end)
|
||||
|
||||
public actual open fun readShort(endian: ByteOrder): Short {
|
||||
public actual open suspend fun readShort(endian: ByteOrder): Short {
|
||||
val bytes = readBytes(2)
|
||||
val v = ((bytes[0].toInt() and 0xFF shl 8) or (bytes[1].toInt() and 0xFF)).toShort()
|
||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
||||
}
|
||||
|
||||
public actual open fun readInt(endian: ByteOrder): Int {
|
||||
public actual open suspend fun readInt(endian: ByteOrder): Int {
|
||||
val bytes = readBytes(4)
|
||||
val v = (bytes[0].toInt() and 0xFF shl 24) or
|
||||
(bytes[1].toInt() and 0xFF shl 16) or
|
||||
@@ -38,7 +37,7 @@ public actual open class ReadChannel public actual constructor() {
|
||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
||||
}
|
||||
|
||||
public actual open fun readLong(endian: ByteOrder): Long {
|
||||
public actual open suspend fun readLong(endian: ByteOrder): Long {
|
||||
val bytes = readBytes(8)
|
||||
var v = 0L
|
||||
for (i in 0 until 8) {
|
||||
@@ -55,8 +54,8 @@ public actual open class WriteChannel public actual constructor() {
|
||||
_writeChannel = writeChannel
|
||||
}
|
||||
|
||||
public actual open fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
runBlocking { _writeChannel.writeFully(value, startIndex, endIndex) }
|
||||
public actual open suspend fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
_writeChannel.writeFully(value, startIndex, endIndex)
|
||||
|
||||
public actual open fun flush(): Unit = runBlocking { _writeChannel.flush() }
|
||||
public actual open suspend fun flush(): Unit = _writeChannel.flush()
|
||||
}
|
||||
+4
-3
@@ -4,8 +4,9 @@
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
package cn.rtast.libmc.common.stream
|
||||
|
||||
import cn.rtast.libmc.common.LibMCContext
|
||||
import io.ktor.network.sockets.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -32,10 +33,10 @@ public actual class UdpSocket public actual constructor(host: String, port: Int,
|
||||
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
|
||||
private val remoteAddress = InetSocketAddress(host, port)
|
||||
|
||||
public actual fun sendAndReceive(data: ByteArray): ByteArray = runBlocking {
|
||||
public actual suspend fun sendAndReceive(data: ByteArray): ByteArray {
|
||||
val packet = buildPacket { writeFully(data) }
|
||||
socket.send(Datagram(packet, remoteAddress))
|
||||
socket.receive().packet.readByteArray()
|
||||
return socket.receive().packet.readByteArray()
|
||||
}
|
||||
|
||||
public actual fun close() {
|
||||
Reference in New Issue
Block a user