Split rconlib and mcping
This commit is contained in:
38 files changed
+350
-268
No files matched your search
@@ -0,0 +1,31 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
macosArm64()
|
||||
mingwX64()
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
|
||||
|
||||
sourceSets {
|
||||
jvmMain.dependencies {
|
||||
// no dependencies needed
|
||||
}
|
||||
|
||||
nativeMain.dependencies {
|
||||
implementation("io.ktor:ktor-network:3.5.2")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.9.1")
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
|
||||
}
|
||||
}
|
||||
|
||||
compilerOptions.freeCompilerArgs.addAll("-Xexpect-actual-classes")
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class _Buffer {
|
||||
public constructor()
|
||||
public constructor(bytes: ByteArray)
|
||||
|
||||
public fun writeByte(value: Byte)
|
||||
public fun writeShort(value: Short)
|
||||
public fun writeLong(value: Long)
|
||||
public fun writeBytes(bytes: ByteArray)
|
||||
|
||||
public fun readByte(): Byte
|
||||
public fun readShort(): Short
|
||||
public fun readLong(): Long
|
||||
public fun readBytes(length: Int): ByteArray
|
||||
public fun toByteArray(): ByteArray
|
||||
public val size: Int
|
||||
}
|
||||
|
||||
public fun ByteArray.wrap(): _Buffer = _Buffer(this)
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class _ReadChannel {
|
||||
public fun readByte(): Byte
|
||||
public fun readBytes(length: Int): ByteArray
|
||||
public fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
||||
public fun readLong(): Long
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class _WriteChannel {
|
||||
public fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||
public fun flush()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
/**
|
||||
* An object class used to pass some parameters
|
||||
* that exists only on native targets
|
||||
*/
|
||||
public expect class PingContext()
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class _Socket public constructor(host: String, port: Int, context: PingContext) {
|
||||
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 fun sendAndReceive(data: ByteArray): ByteArray
|
||||
public fun close()
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _Buffer {
|
||||
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 fun writeByte(value: Byte) {
|
||||
outStream.write(value.toInt())
|
||||
}
|
||||
|
||||
public actual fun writeShort(value: Short) {
|
||||
val v = value.toInt()
|
||||
outStream.write(v shr 8)
|
||||
outStream.write(v)
|
||||
}
|
||||
|
||||
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 writeBytes(bytes: ByteArray) {
|
||||
outStream.write(bytes)
|
||||
}
|
||||
|
||||
private fun ensureReadArray(): ByteArray {
|
||||
var buf = readBuffer
|
||||
if (buf == null) {
|
||||
buf = outStream.toByteArray()
|
||||
readBuffer = buf
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
public actual fun readByte(): Byte {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||
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 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 fun toByteArray(): ByteArray = outStream.toByteArray()
|
||||
|
||||
public actual val size: Int
|
||||
get() = outStream.size()
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import java.io.EOFException
|
||||
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 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
|
||||
while (bytesRead < length) {
|
||||
val read = _inputStream.read(out, start + bytesRead, length - bytesRead)
|
||||
if (read == -1) throw IllegalStateException("End of stream")
|
||||
bytesRead += read
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _WriteChannel {
|
||||
private val _outputStream: OutputStream
|
||||
|
||||
public constructor(outputStream: OutputStream) {
|
||||
_outputStream = outputStream
|
||||
}
|
||||
|
||||
public actual fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
_outputStream.write(value, startIndex, endIndex - startIndex)
|
||||
|
||||
public actual fun flush(): Unit = _outputStream.flush()
|
||||
}
|
||||
|
||||
private fun InputStream.readNBytes(length: Int): ByteArray {
|
||||
val buffer = ByteArray(length)
|
||||
var totalRead = 0
|
||||
while (totalRead < length) {
|
||||
val read = this.read(buffer, totalRead, length - totalRead)
|
||||
if (read == -1) throw EOFException()
|
||||
totalRead += read
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
/**
|
||||
* No Context for jvm targets
|
||||
*/
|
||||
public actual class PingContext
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||
private val socket = DatagramSocket().apply {
|
||||
soTimeout = 3000
|
||||
connect(InetSocketAddress(host, port))
|
||||
}
|
||||
|
||||
public actual fun sendAndReceive(data: ByteArray): ByteArray {
|
||||
socket.send(DatagramPacket(data, data.size))
|
||||
|
||||
val buf = ByteArray(2048)
|
||||
val receivePacket = DatagramPacket(buf, buf.size)
|
||||
socket.receive(receivePacket)
|
||||
return buf.copyOf(receivePacket.length)
|
||||
}
|
||||
|
||||
public actual fun close(): Unit = socket.close()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import kotlinx.io.Buffer
|
||||
import kotlinx.io.readByteArray
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _Buffer {
|
||||
private val _delegateBuf: Buffer
|
||||
|
||||
public actual constructor() {
|
||||
_delegateBuf = Buffer()
|
||||
}
|
||||
|
||||
public actual constructor(bytes: ByteArray) {
|
||||
_delegateBuf = Buffer().apply { write(bytes) }
|
||||
}
|
||||
|
||||
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 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 toByteArray(): ByteArray = _delegateBuf.peek().readByteArray()
|
||||
|
||||
public actual val size: Int
|
||||
get() = _delegateBuf.size.toInt()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import io.ktor.utils.io.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _ReadChannel {
|
||||
private val _readChannel: ByteReadChannel
|
||||
|
||||
public constructor(readChannel: ByteReadChannel) {
|
||||
_readChannel = readChannel
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _WriteChannel {
|
||||
private val _writeChannel: ByteWriteChannel
|
||||
|
||||
public constructor(writeChannel: ByteWriteChannel) {
|
||||
_writeChannel = writeChannel
|
||||
}
|
||||
|
||||
public actual fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||
runBlocking { _writeChannel.writeFully(value, startIndex, endIndex) }
|
||||
|
||||
public actual fun flush(): Unit = runBlocking { _writeChannel.flush() }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
@file:Suppress("PropertyName")
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import io.ktor.network.selector.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
|
||||
public actual class PingContext 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/3
|
||||
*/
|
||||
|
||||
package cn.rtast.mcping.platform
|
||||
|
||||
import io.ktor.network.sockets.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.io.readByteArray
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _Socket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||
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 fun sendAndReceive(data: ByteArray): ByteArray = runBlocking {
|
||||
val packet = buildPacket { writeFully(data) }
|
||||
socket.send(Datagram(packet, remoteAddress))
|
||||
socket.receive().packet.readByteArray()
|
||||
}
|
||||
|
||||
public actual fun close() {
|
||||
socket.close()
|
||||
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user