rcon client supports

This commit is contained in:
2026-09-04 01:27:18 +08:00
parent 350b900aaa
commit 817ec27813
27 files changed
+412 -93

No files matched your search

@@ -13,15 +13,18 @@ public expect class _Buffer {
public constructor(bytes: ByteArray)
public fun writeByte(value: Byte)
public fun writeShort(value: Short)
public fun writeLong(value: Long)
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 writeBytes(bytes: ByteArray)
public fun readByte(): Byte
public fun readShort(): Short
public fun readLong(): Long
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 readBytes(length: Int): ByteArray
public fun toByteArray(): ByteArray
public fun close()
public val size: Int
}
@@ -0,0 +1,12 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
*/
package cn.rtast.mcping.platform
public enum class ByteOrder {
BIG_ENDIAN, LITTLE_ENDIAN
}
@@ -10,9 +10,11 @@ package cn.rtast.mcping.platform
@Suppress("CLASSNAME")
public expect class _ReadChannel {
public fun readByte(): Byte
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 readBytes(length: Int): ByteArray
public fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
public fun readLong(): Long
}
@Suppress("CLASSNAME")
@@ -11,4 +11,4 @@ package cn.rtast.mcping.platform
* An object class used to pass some parameters
* that exists only on native targets
*/
public expect class PingContext()
public expect class MCPingContext()
@@ -8,14 +8,14 @@
package cn.rtast.mcping.platform
@Suppress("CLASSNAME")
public expect class _Socket public constructor(host: String, port: Int, context: PingContext) {
public expect class _Socket public constructor(host: String, port: Int, context: MCPingContext) {
public fun openReadChannel(): _ReadChannel
public fun openWriteChannel(): _WriteChannel
public fun close()
}
@Suppress("CLASSNAME")
public expect class _UdpSocket public constructor(host: String, port: Int, context: PingContext) {
public expect class _UdpSocket public constructor(host: String, port: Int, context: MCPingContext) {
public fun sendAndReceive(data: ByteArray): ByteArray
public fun close()
}
@@ -24,21 +24,37 @@ public actual class _Buffer {
outStream.write(value.toInt())
}
public actual fun writeShort(value: Short) {
public actual fun writeShort(value: Short, endian: ByteOrder) {
val v = value.toInt()
outStream.write(v shr 8)
outStream.write(v)
if (endian == ByteOrder.BIG_ENDIAN) {
outStream.write(v shr 8)
outStream.write(v)
} else {
outStream.write(v)
outStream.write(v shr 8)
}
}
public actual fun writeLong(value: Long) {
outStream.write((value shr 56).toInt())
outStream.write((value shr 48).toInt())
outStream.write((value shr 40).toInt())
outStream.write((value shr 32).toInt())
outStream.write((value shr 24).toInt())
outStream.write((value shr 16).toInt())
outStream.write((value shr 8).toInt())
outStream.write(value.toInt())
public actual fun writeInt(value: Int, endian: ByteOrder) {
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 fun writeLong(value: Long, endian: ByteOrder) {
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 fun writeBytes(bytes: ByteArray) {
@@ -60,23 +76,9 @@ public actual class _Buffer {
return array[readOffset++]
}
public actual fun readShort(): Short {
val b1 = readByte().toInt() and 0xFF
val b2 = readByte().toInt() and 0xFF
return ((b1 shl 8) or b2).toShort()
}
public actual fun readLong(): Long {
return (readByte().toLong() and 0xFF shl 56) or
(readByte().toLong() and 0xFF shl 48) or
(readByte().toLong() and 0xFF shl 40) or
(readByte().toLong() and 0xFF shl 32) or
(readByte().toLong() and 0xFF shl 24) or
(readByte().toLong() and 0xFF shl 16) or
(readByte().toLong() and 0xFF shl 8) or
(readByte().toLong() and 0xFF)
}
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 fun readBytes(length: Int): ByteArray {
val array = ensureReadArray()
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
@@ -87,6 +89,6 @@ public actual class _Buffer {
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
public actual val size: Int
get() = outStream.size()
public actual fun close(): Unit = outStream.close()
public actual val size: Int get() = outStream.size()
}
@@ -12,15 +12,11 @@ import java.io.InputStream
import java.io.OutputStream
@Suppress("CLASSNAME")
public actual class _ReadChannel {
private val _inputStream: InputStream
public constructor(inputStream: InputStream) {
_inputStream = inputStream
}
public actual class _ReadChannel(private val _inputStream: InputStream) {
public actual fun readByte(): Byte = _inputStream.read().toByte()
public actual fun readBytes(length: Int): ByteArray = _inputStream.readNBytes(length)
public actual fun readFully(out: ByteArray, start: Int, end: Int) {
var bytesRead = 0
val length = end - start
@@ -31,23 +27,9 @@ public actual class _ReadChannel {
}
}
public actual fun readLong(): Long {
val bytes = ByteArray(8)
var read = 0
while (read < 8) {
val count = _inputStream.read(bytes, read, 8 - read)
if (count == -1) throw EOFException()
read += count
}
return ((bytes[0].toLong() and 0xFF shl 56) or
(bytes[1].toLong() and 0xFF shl 48) or
(bytes[2].toLong() and 0xFF shl 40) or
(bytes[3].toLong() and 0xFF shl 32) or
(bytes[4].toLong() and 0xFF shl 24) or
(bytes[5].toLong() and 0xFF shl 16) or
(bytes[6].toLong() and 0xFF shl 8) or
(bytes[7].toLong() and 0xFF))
}
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)
}
@Suppress("CLASSNAME")
@@ -9,4 +9,4 @@ package cn.rtast.mcping.platform
/**
* No Context for jvm targets
*/
public actual class PingContext
public actual class MCPingContext
@@ -0,0 +1,40 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/4
*/
package cn.rtast.mcping.platform
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
}
@@ -12,7 +12,7 @@ import java.net.InetSocketAddress
import java.net.Socket as JvmSocket
@Suppress("CLASSNAME")
public actual class _Socket public actual constructor(host: String, port: Int, context: PingContext) {
public actual class _Socket public actual constructor(host: String, port: Int, context: MCPingContext) {
private val socket = JvmSocket(host, port)
public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.getInputStream())
@@ -21,7 +21,7 @@ public actual class _Socket public actual constructor(host: String, port: Int, c
}
@Suppress("CLASSNAME")
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: MCPingContext) {
private val socket = DatagramSocket().apply {
soTimeout = 3000
connect(InetSocketAddress(host, port))
@@ -6,6 +6,7 @@
package cn.rtast.mcping.platform
import io.ktor.utils.io.bits.*
import kotlinx.io.Buffer
import kotlinx.io.readByteArray
@@ -22,16 +23,41 @@ public actual class _Buffer {
}
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
public actual fun writeShort(value: Short): Unit = _delegateBuf.writeShort(value)
public actual fun writeLong(value: Long): Unit = _delegateBuf.writeLong(value)
public actual fun writeShort(value: Short, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value)
else _delegateBuf.writeShort(value.reverseByteOrder())
}
public actual fun writeInt(value: Int, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
else _delegateBuf.writeInt(value.reverseByteOrder())
}
public actual fun writeLong(value: Long, endian: ByteOrder) {
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
else _delegateBuf.writeLong(value.reverseByteOrder())
}
public actual fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
public actual fun readByte(): Byte = _delegateBuf.readByte()
public actual fun readShort(): Short = _delegateBuf.readShort()
public actual fun readLong(): Long = _delegateBuf.readLong()
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
public actual fun readShort(endian: ByteOrder): Short {
val v = _delegateBuf.readShort()
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual fun readInt(endian: ByteOrder): Int {
val v = _delegateBuf.readInt()
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual fun readLong(endian: ByteOrder): Long {
val v = _delegateBuf.readLong()
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
public actual fun toByteArray(): ByteArray = _delegateBuf.peek().readByteArray()
public actual fun close(): Unit = _delegateBuf.close()
public actual val size: Int
get() = _delegateBuf.size.toInt()
@@ -7,22 +7,31 @@
package cn.rtast.mcping.platform
import io.ktor.utils.io.*
import io.ktor.utils.io.bits.reverseByteOrder
import kotlinx.coroutines.runBlocking
@Suppress("CLASSNAME")
public actual class _ReadChannel {
private val _readChannel: ByteReadChannel
public constructor(readChannel: ByteReadChannel) {
_readChannel = readChannel
}
public actual class _ReadChannel(private val _readChannel: ByteReadChannel) {
public actual fun readByte(): Byte = runBlocking { _readChannel.readByte() }
public actual fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) }
public actual fun readFully(out: ByteArray, start: Int, end: Int): Unit =
runBlocking { _readChannel.readFully(out, start, end) }
public actual fun readLong(): Long = runBlocking { _readChannel.readLong() }
public actual fun readShort(endian: ByteOrder): Short = runBlocking {
val v = _readChannel.readShort()
if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual fun readInt(endian: ByteOrder): Int = runBlocking {
val v = _readChannel.readInt()
if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
public actual fun readLong(endian: ByteOrder): Long = runBlocking {
val v = _readChannel.readLong()
if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
}
}
@Suppress("CLASSNAME")
@@ -12,7 +12,7 @@ import io.ktor.network.selector.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
public actual class PingContext actual constructor() {
public actual class MCPingContext actual constructor() {
internal var _selectorManager: SelectorManager = SelectorManager(Dispatchers.IO)
internal var _autoCloseSelectorManager: Boolean = false
@@ -12,7 +12,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.io.readByteArray
@Suppress("CLASSNAME")
public actual class _Socket public actual constructor(host: String, port: Int, context: PingContext) {
public actual class _Socket public actual constructor(host: String, port: Int, context: MCPingContext) {
private val ctx = context
private val socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) }
@@ -27,7 +27,7 @@ public actual class _Socket public actual constructor(host: String, port: Int, c
}
@Suppress("CLASSNAME")
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: MCPingContext) {
private val ctx = context
// use bind to create an unconnected socket