Split rconlib and mcping
This commit is contained in:
38 files changed
+350
-268
No files matched your search
+22
-47
@@ -1,56 +1,31 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.4.10"
|
kotlin("multiplatform") version "2.4.10" apply false
|
||||||
id("maven-publish")
|
id("maven-publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "cn.rtast.mcping"
|
allprojects {
|
||||||
version = providers.gradleProperty("libVersion").get()
|
group = "cn.rtast.mcping"
|
||||||
|
version = providers.gradleProperty("libVersion").get()
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://repo.rtast.cn/packages") {
|
mavenCentral()
|
||||||
credentials(HttpHeaderCredentials::class) {
|
}
|
||||||
name = "Authorization"
|
}
|
||||||
value = "Bearer ${System.getenv("PUBLISH_TOKEN")}"
|
|
||||||
}
|
subprojects {
|
||||||
authentication {
|
pluginManager.apply("org.jetbrains.kotlin.multiplatform")
|
||||||
create<HttpHeaderAuthentication>("header")
|
pluginManager.apply("maven-publish")
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven("https://repo.rtast.cn/packages") {
|
||||||
|
credentials(HttpHeaderCredentials::class) {
|
||||||
|
name = "Authorization"
|
||||||
|
value = "Bearer ${System.getenv("PUBLISH_TOKEN")}"
|
||||||
|
}
|
||||||
|
authentication {
|
||||||
|
create<HttpHeaderAuthentication>("header")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -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()
|
||||||
|
}
|
||||||
+14
-14
@@ -8,29 +8,29 @@ package cn.rtast.mcping.platform
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
internal actual class PlatformBuffer {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _Buffer {
|
||||||
private val outStream = ByteArrayOutputStream()
|
private val outStream = ByteArrayOutputStream()
|
||||||
private var readBuffer: ByteArray? = null
|
private var readBuffer: ByteArray? = null
|
||||||
private var readOffset = 0
|
private var readOffset = 0
|
||||||
|
|
||||||
actual constructor()
|
public actual constructor()
|
||||||
|
public actual constructor(bytes: ByteArray) {
|
||||||
actual constructor(bytes: ByteArray) {
|
|
||||||
this.readBuffer = bytes
|
this.readBuffer = bytes
|
||||||
outStream.write(bytes)
|
outStream.write(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun writeByte(value: Byte) {
|
public actual fun writeByte(value: Byte) {
|
||||||
outStream.write(value.toInt())
|
outStream.write(value.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun writeShort(value: Short) {
|
public actual fun writeShort(value: Short) {
|
||||||
val v = value.toInt()
|
val v = value.toInt()
|
||||||
outStream.write(v shr 8)
|
outStream.write(v shr 8)
|
||||||
outStream.write(v)
|
outStream.write(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun writeLong(value: Long) {
|
public actual fun writeLong(value: Long) {
|
||||||
outStream.write((value shr 56).toInt())
|
outStream.write((value shr 56).toInt())
|
||||||
outStream.write((value shr 48).toInt())
|
outStream.write((value shr 48).toInt())
|
||||||
outStream.write((value shr 40).toInt())
|
outStream.write((value shr 40).toInt())
|
||||||
@@ -41,7 +41,7 @@ internal actual class PlatformBuffer {
|
|||||||
outStream.write(value.toInt())
|
outStream.write(value.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun writeBytes(bytes: ByteArray) {
|
public actual fun writeBytes(bytes: ByteArray) {
|
||||||
outStream.write(bytes)
|
outStream.write(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,19 +54,19 @@ internal actual class PlatformBuffer {
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readByte(): Byte {
|
public actual fun readByte(): Byte {
|
||||||
val array = ensureReadArray()
|
val array = ensureReadArray()
|
||||||
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||||
return array[readOffset++]
|
return array[readOffset++]
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readShort(): Short {
|
public actual fun readShort(): Short {
|
||||||
val b1 = readByte().toInt() and 0xFF
|
val b1 = readByte().toInt() and 0xFF
|
||||||
val b2 = readByte().toInt() and 0xFF
|
val b2 = readByte().toInt() and 0xFF
|
||||||
return ((b1 shl 8) or b2).toShort()
|
return ((b1 shl 8) or b2).toShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readLong(): Long {
|
public actual fun readLong(): Long {
|
||||||
return (readByte().toLong() and 0xFF shl 56) or
|
return (readByte().toLong() and 0xFF shl 56) or
|
||||||
(readByte().toLong() and 0xFF shl 48) or
|
(readByte().toLong() and 0xFF shl 48) or
|
||||||
(readByte().toLong() and 0xFF shl 40) or
|
(readByte().toLong() and 0xFF shl 40) or
|
||||||
@@ -77,7 +77,7 @@ internal actual class PlatformBuffer {
|
|||||||
(readByte().toLong() and 0xFF)
|
(readByte().toLong() and 0xFF)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readBytes(length: Int): ByteArray {
|
public actual fun readBytes(length: Int): ByteArray {
|
||||||
val array = ensureReadArray()
|
val array = ensureReadArray()
|
||||||
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||||
val result = array.copyOfRange(readOffset, readOffset + length)
|
val result = array.copyOfRange(readOffset, readOffset + length)
|
||||||
@@ -85,8 +85,8 @@ internal actual class PlatformBuffer {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun toByteArray(): ByteArray = outStream.toByteArray()
|
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
|
||||||
|
|
||||||
actual val size: Int
|
public actual val size: Int
|
||||||
get() = outStream.size()
|
get() = outStream.size()
|
||||||
}
|
}
|
||||||
+12
-10
@@ -11,16 +11,17 @@ import java.io.EOFException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
internal actual class ReadChannel {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _ReadChannel {
|
||||||
private val _inputStream: InputStream
|
private val _inputStream: InputStream
|
||||||
|
|
||||||
constructor(inputStream: InputStream) {
|
public constructor(inputStream: InputStream) {
|
||||||
_inputStream = inputStream
|
_inputStream = inputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readByte(): Byte = _inputStream.read().toByte()
|
public actual fun readByte(): Byte = _inputStream.read().toByte()
|
||||||
actual fun readBytes(length: Int): ByteArray = _inputStream.readNBytes(length)
|
public actual fun readBytes(length: Int): ByteArray = _inputStream.readNBytes(length)
|
||||||
actual fun readFully(out: ByteArray, start: Int, end: Int) {
|
public actual fun readFully(out: ByteArray, start: Int, end: Int) {
|
||||||
var bytesRead = 0
|
var bytesRead = 0
|
||||||
val length = end - start
|
val length = end - start
|
||||||
while (bytesRead < length) {
|
while (bytesRead < length) {
|
||||||
@@ -30,7 +31,7 @@ internal actual class ReadChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun readLong(): Long {
|
public actual fun readLong(): Long {
|
||||||
val bytes = ByteArray(8)
|
val bytes = ByteArray(8)
|
||||||
var read = 0
|
var read = 0
|
||||||
while (read < 8) {
|
while (read < 8) {
|
||||||
@@ -49,17 +50,18 @@ internal actual class ReadChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal actual class WriteChannel {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _WriteChannel {
|
||||||
private val _outputStream: OutputStream
|
private val _outputStream: OutputStream
|
||||||
|
|
||||||
constructor(outputStream: OutputStream) {
|
public constructor(outputStream: OutputStream) {
|
||||||
_outputStream = outputStream
|
_outputStream = outputStream
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) =
|
public actual fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int): Unit =
|
||||||
_outputStream.write(value, startIndex, endIndex - startIndex)
|
_outputStream.write(value, startIndex, endIndex - startIndex)
|
||||||
|
|
||||||
actual fun flush() = _outputStream.flush()
|
public actual fun flush(): Unit = _outputStream.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun InputStream.readNBytes(length: Int): ByteArray {
|
private fun InputStream.readNBytes(length: Int): ByteArray {
|
||||||
File renamed without changes.
+9
-7
@@ -11,21 +11,23 @@ import java.net.DatagramSocket
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Socket as JvmSocket
|
import java.net.Socket as JvmSocket
|
||||||
|
|
||||||
internal actual class Socket internal actual constructor(host: String, port: Int, context: PingContext) {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _Socket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||||
private val socket = JvmSocket(host, port)
|
private val socket = JvmSocket(host, port)
|
||||||
|
|
||||||
actual fun openReadChannel(): ReadChannel = ReadChannel(socket.getInputStream())
|
public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.getInputStream())
|
||||||
actual fun openWriteChannel(): WriteChannel = WriteChannel(socket.getOutputStream())
|
public actual fun openWriteChannel(): _WriteChannel = _WriteChannel(socket.getOutputStream())
|
||||||
actual fun close() = socket.close()
|
public actual fun close(): Unit = socket.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal actual class UdpSocket internal actual constructor(host: String, port: Int, context: PingContext) {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||||
private val socket = DatagramSocket().apply {
|
private val socket = DatagramSocket().apply {
|
||||||
soTimeout = 3000
|
soTimeout = 3000
|
||||||
connect(InetSocketAddress(host, port))
|
connect(InetSocketAddress(host, port))
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun sendAndReceive(data: ByteArray): ByteArray {
|
public actual fun sendAndReceive(data: ByteArray): ByteArray {
|
||||||
socket.send(DatagramPacket(data, data.size))
|
socket.send(DatagramPacket(data, data.size))
|
||||||
|
|
||||||
val buf = ByteArray(2048)
|
val buf = ByteArray(2048)
|
||||||
@@ -34,5 +36,5 @@ internal actual class UdpSocket internal actual constructor(host: String, port:
|
|||||||
return buf.copyOf(receivePacket.length)
|
return buf.copyOf(receivePacket.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun close() = socket.close()
|
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() }
|
||||||
|
}
|
||||||
File renamed without changes.
+10
-8
@@ -11,34 +11,36 @@ import io.ktor.utils.io.core.*
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.io.readByteArray
|
import kotlinx.io.readByteArray
|
||||||
|
|
||||||
internal actual class Socket internal actual constructor(host: String, port: Int, context: PingContext) {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _Socket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||||
private val ctx = context
|
private val ctx = context
|
||||||
private val socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) }
|
private val socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) }
|
||||||
|
|
||||||
actual fun openReadChannel(): ReadChannel = ReadChannel(socket.openReadChannel())
|
public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.openReadChannel())
|
||||||
actual fun openWriteChannel(): WriteChannel =
|
public actual fun openWriteChannel(): _WriteChannel =
|
||||||
WriteChannel(socket.openWriteChannel(autoFlush = true))
|
_WriteChannel(socket.openWriteChannel(autoFlush = true))
|
||||||
|
|
||||||
actual fun close() {
|
public actual fun close() {
|
||||||
socket.close()
|
socket.close()
|
||||||
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
|
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal actual class UdpSocket internal actual constructor(host: String, port: Int, context: PingContext) {
|
@Suppress("CLASSNAME")
|
||||||
|
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: PingContext) {
|
||||||
private val ctx = context
|
private val ctx = context
|
||||||
|
|
||||||
// use bind to create an unconnected socket
|
// use bind to create an unconnected socket
|
||||||
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
|
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
|
||||||
private val remoteAddress = InetSocketAddress(host, port)
|
private val remoteAddress = InetSocketAddress(host, port)
|
||||||
|
|
||||||
actual fun sendAndReceive(data: ByteArray): ByteArray = runBlocking {
|
public actual fun sendAndReceive(data: ByteArray): ByteArray = runBlocking {
|
||||||
val packet = buildPacket { writeFully(data) }
|
val packet = buildPacket { writeFully(data) }
|
||||||
socket.send(Datagram(packet, remoteAddress))
|
socket.send(Datagram(packet, remoteAddress))
|
||||||
socket.receive().packet.readByteArray()
|
socket.receive().packet.readByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun close() {
|
public actual fun close() {
|
||||||
socket.close()
|
socket.close()
|
||||||
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
|
if (ctx._autoCloseSelectorManager) ctx._selectorManager.close()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
explicitApi()
|
||||||
|
|
||||||
|
linuxX64()
|
||||||
|
linuxArm64()
|
||||||
|
macosArm64()
|
||||||
|
mingwX64()
|
||||||
|
iosArm64()
|
||||||
|
iosSimulatorArm64()
|
||||||
|
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain.dependencies {
|
||||||
|
implementation(project(":common"))
|
||||||
|
}
|
||||||
|
|
||||||
|
commonTest.dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-5
@@ -7,9 +7,8 @@
|
|||||||
|
|
||||||
package cn.rtast.mcping.bedrock
|
package cn.rtast.mcping.bedrock
|
||||||
|
|
||||||
import cn.rtast.mcping.platform.PlatformBuffer
|
import cn.rtast.mcping.platform._Buffer
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.time.Clock
|
|
||||||
|
|
||||||
private val rakNetMagic = byteArrayOf(
|
private val rakNetMagic = byteArrayOf(
|
||||||
0x00, 0xFF.toByte(),
|
0x00, 0xFF.toByte(),
|
||||||
@@ -26,7 +25,7 @@ internal interface MinecraftBedrockPacket {
|
|||||||
val time: Long
|
val time: Long
|
||||||
val magic: ByteArray
|
val magic: ByteArray
|
||||||
|
|
||||||
fun writePayload(buffer: PlatformBuffer)
|
fun writePayload(buffer: _Buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class BedrockRequestPacket(
|
internal data class BedrockRequestPacket(
|
||||||
@@ -36,7 +35,7 @@ internal data class BedrockRequestPacket(
|
|||||||
) : MinecraftBedrockPacket {
|
) : MinecraftBedrockPacket {
|
||||||
override val packetId: Byte = 0x01
|
override val packetId: Byte = 0x01
|
||||||
|
|
||||||
override fun writePayload(buffer: PlatformBuffer) {
|
override fun writePayload(buffer: _Buffer) {
|
||||||
buffer.writeByte(packetId)
|
buffer.writeByte(packetId)
|
||||||
buffer.writeLong(time)
|
buffer.writeLong(time)
|
||||||
buffer.writeBytes(magic)
|
buffer.writeBytes(magic)
|
||||||
@@ -71,7 +70,7 @@ internal data class BedrockResponsePacket(
|
|||||||
val stringLength: Short,
|
val stringLength: Short,
|
||||||
val payload: String,
|
val payload: String,
|
||||||
) : MinecraftBedrockPacket {
|
) : MinecraftBedrockPacket {
|
||||||
override fun writePayload(buffer: PlatformBuffer) {}
|
override fun writePayload(buffer: _Buffer) {}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/3
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.mcping.bedrock
|
||||||
|
|
||||||
|
import cn.rtast.mcping.platform._Buffer
|
||||||
|
import cn.rtast.mcping.platform._UdpSocket
|
||||||
|
|
||||||
|
internal fun _UdpSocket.sendPacket(packet: MinecraftBedrockPacket): ByteArray {
|
||||||
|
val buf = _Buffer()
|
||||||
|
packet.writePayload(buf)
|
||||||
|
return sendAndReceive(buf.toByteArray())
|
||||||
|
}
|
||||||
+2
-2
@@ -9,12 +9,12 @@ package cn.rtast.mcping.bedrock
|
|||||||
|
|
||||||
import cn.rtast.mcping.PingResponse
|
import cn.rtast.mcping.PingResponse
|
||||||
import cn.rtast.mcping.platform.PingContext
|
import cn.rtast.mcping.platform.PingContext
|
||||||
import cn.rtast.mcping.platform.UdpSocket
|
import cn.rtast.mcping.platform._UdpSocket
|
||||||
import cn.rtast.mcping.platform.wrap
|
import cn.rtast.mcping.platform.wrap
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
|
||||||
internal fun pingBedrockServer(host: String, port: Int, context: PingContext): PingResponse {
|
internal fun pingBedrockServer(host: String, port: Int, context: PingContext): PingResponse {
|
||||||
val socket = UdpSocket(host, port, context)
|
val socket = _UdpSocket(host, port, context)
|
||||||
return try {
|
return try {
|
||||||
val sendTime = Clock.System.now().toEpochMilliseconds()
|
val sendTime = Clock.System.now().toEpochMilliseconds()
|
||||||
val packet = BedrockRequestPacket(sendTime)
|
val packet = BedrockRequestPacket(sendTime)
|
||||||
File renamed without changes.
+6
-6
@@ -6,11 +6,11 @@
|
|||||||
|
|
||||||
package cn.rtast.mcping.java
|
package cn.rtast.mcping.java
|
||||||
|
|
||||||
import cn.rtast.mcping.platform.PlatformBuffer
|
import cn.rtast.mcping.platform._Buffer
|
||||||
import cn.rtast.mcping.platform.ReadChannel
|
import cn.rtast.mcping.platform._ReadChannel
|
||||||
|
|
||||||
|
|
||||||
internal fun PlatformBuffer.writeVarInt(value: Int) {
|
internal fun _Buffer.writeVarInt(value: Int) {
|
||||||
var v = value
|
var v = value
|
||||||
while (true) {
|
while (true) {
|
||||||
if ((v and 0x7F.inv()) == 0) {
|
if ((v and 0x7F.inv()) == 0) {
|
||||||
@@ -22,7 +22,7 @@ internal fun PlatformBuffer.writeVarInt(value: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ReadChannel.readVarInt(): Int {
|
internal fun _ReadChannel.readVarInt(): Int {
|
||||||
var value = 0
|
var value = 0
|
||||||
var position = 0
|
var position = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -35,13 +35,13 @@ internal fun ReadChannel.readVarInt(): Int {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun PlatformBuffer.writeMcString(value: String) {
|
internal fun _Buffer.writeMcString(value: String) {
|
||||||
val bytes = value.encodeToByteArray()
|
val bytes = value.encodeToByteArray()
|
||||||
this.writeVarInt(bytes.size)
|
this.writeVarInt(bytes.size)
|
||||||
this.writeBytes(bytes)
|
this.writeBytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ReadChannel.readMcString(): String {
|
internal fun _ReadChannel.readMcString(): String {
|
||||||
val length = this.readVarInt()
|
val length = this.readVarInt()
|
||||||
val bytes = this.readBytes(length)
|
val bytes = this.readBytes(length)
|
||||||
return bytes.decodeToString()
|
return bytes.decodeToString()
|
||||||
+5
-5
@@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
package cn.rtast.mcping.java
|
package cn.rtast.mcping.java
|
||||||
|
|
||||||
import cn.rtast.mcping.platform.PlatformBuffer
|
import cn.rtast.mcping.platform._Buffer
|
||||||
|
|
||||||
internal interface MinecraftPacket {
|
internal interface MinecraftPacket {
|
||||||
val packetId: Int
|
val packetId: Int
|
||||||
|
|
||||||
fun writePayload(buffer: PlatformBuffer)
|
fun writePayload(buffer: _Buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
|
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
|
||||||
@@ -25,7 +25,7 @@ internal data class HandshakePacket(
|
|||||||
) : MinecraftPacket {
|
) : MinecraftPacket {
|
||||||
override val packetId: Int = 0x00
|
override val packetId: Int = 0x00
|
||||||
|
|
||||||
override fun writePayload(buffer: PlatformBuffer) {
|
override fun writePayload(buffer: _Buffer) {
|
||||||
buffer.writeVarInt(protocolVersion)
|
buffer.writeVarInt(protocolVersion)
|
||||||
buffer.writeMcString(serverAddress)
|
buffer.writeMcString(serverAddress)
|
||||||
// write UShort
|
// write UShort
|
||||||
@@ -39,10 +39,10 @@ internal data class HandshakePacket(
|
|||||||
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status
|
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status
|
||||||
internal data object StatusRequestPacket : MinecraftPacket {
|
internal data object StatusRequestPacket : MinecraftPacket {
|
||||||
override val packetId: Int = 0x00
|
override val packetId: Int = 0x00
|
||||||
override fun writePayload(buffer: PlatformBuffer) {}
|
override fun writePayload(buffer: _Buffer) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class PingPacket(val currentTime: Long) : MinecraftPacket {
|
internal data class PingPacket(val currentTime: Long) : MinecraftPacket {
|
||||||
override val packetId: Int = 0x01
|
override val packetId: Int = 0x01
|
||||||
override fun writePayload(buffer: PlatformBuffer) = buffer.writeLong(currentTime)
|
override fun writePayload(buffer: _Buffer) = buffer.writeLong(currentTime)
|
||||||
}
|
}
|
||||||
+5
-5
@@ -7,14 +7,14 @@
|
|||||||
|
|
||||||
package cn.rtast.mcping.java
|
package cn.rtast.mcping.java
|
||||||
|
|
||||||
import cn.rtast.mcping.platform.PlatformBuffer
|
import cn.rtast.mcping.platform._Buffer
|
||||||
import cn.rtast.mcping.platform.WriteChannel
|
import cn.rtast.mcping.platform._WriteChannel
|
||||||
|
|
||||||
internal fun WriteChannel.sendPacket(packet: MinecraftPacket) {
|
internal fun _WriteChannel.sendPacket(packet: MinecraftPacket) {
|
||||||
val bodyBuffer = PlatformBuffer()
|
val bodyBuffer = _Buffer()
|
||||||
bodyBuffer.writeVarInt(packet.packetId)
|
bodyBuffer.writeVarInt(packet.packetId)
|
||||||
packet.writePayload(bodyBuffer)
|
packet.writePayload(bodyBuffer)
|
||||||
val frameBuffer = PlatformBuffer()
|
val frameBuffer = _Buffer()
|
||||||
frameBuffer.writeVarInt(bodyBuffer.size)
|
frameBuffer.writeVarInt(bodyBuffer.size)
|
||||||
frameBuffer.writeBytes(bodyBuffer.toByteArray())
|
frameBuffer.writeBytes(bodyBuffer.toByteArray())
|
||||||
val bytes = frameBuffer.toByteArray()
|
val bytes = frameBuffer.toByteArray()
|
||||||
+2
-2
@@ -9,11 +9,11 @@ package cn.rtast.mcping.java
|
|||||||
|
|
||||||
import cn.rtast.mcping.PingResponse
|
import cn.rtast.mcping.PingResponse
|
||||||
import cn.rtast.mcping.platform.PingContext
|
import cn.rtast.mcping.platform.PingContext
|
||||||
import cn.rtast.mcping.platform.Socket
|
import cn.rtast.mcping.platform._Socket
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
|
||||||
internal fun pingJavaServer(host: String, port: Int, context: PingContext): PingResponse {
|
internal fun pingJavaServer(host: String, port: Int, context: PingContext): PingResponse {
|
||||||
val socket = Socket(host, port, context)
|
val socket = _Socket(host, port, context)
|
||||||
val receiveChannel = socket.openReadChannel()
|
val receiveChannel = socket.openReadChannel()
|
||||||
val sendChannel = socket.openWriteChannel()
|
val sendChannel = socket.openWriteChannel()
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,24 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
explicitApi()
|
||||||
|
|
||||||
|
linuxX64()
|
||||||
|
linuxArm64()
|
||||||
|
macosArm64()
|
||||||
|
mingwX64()
|
||||||
|
iosArm64()
|
||||||
|
iosSimulatorArm64()
|
||||||
|
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 }
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain.dependencies {
|
||||||
|
implementation(project(":common"))
|
||||||
|
}
|
||||||
|
|
||||||
|
commonTest.dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/4
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.mcping.rconlib
|
||||||
|
|
||||||
+5
-1
@@ -1,4 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||||
}
|
}
|
||||||
rootProject.name = "mcping"
|
rootProject.name = "mcping"
|
||||||
|
|
||||||
|
include(":mcping")
|
||||||
|
include(":rconlib")
|
||||||
|
include(":common")
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package cn.rtast.mcping.bedrock
|
|
||||||
|
|
||||||
import cn.rtast.mcping.platform.PlatformBuffer
|
|
||||||
import cn.rtast.mcping.platform.UdpSocket
|
|
||||||
|
|
||||||
internal fun UdpSocket.sendPacket(packet: MinecraftBedrockPacket): ByteArray {
|
|
||||||
val buf = PlatformBuffer()
|
|
||||||
packet.writePayload(buf)
|
|
||||||
return sendAndReceive(buf.toByteArray())
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package cn.rtast.mcping.platform
|
|
||||||
|
|
||||||
internal expect class PlatformBuffer {
|
|
||||||
constructor()
|
|
||||||
constructor(bytes: ByteArray)
|
|
||||||
|
|
||||||
fun writeByte(value: Byte)
|
|
||||||
fun writeShort(value: Short)
|
|
||||||
fun writeLong(value: Long)
|
|
||||||
fun writeBytes(bytes: ByteArray)
|
|
||||||
|
|
||||||
fun readByte(): Byte
|
|
||||||
fun readShort(): Short
|
|
||||||
fun readLong(): Long
|
|
||||||
fun readBytes(length: Int): ByteArray
|
|
||||||
fun toByteArray(): ByteArray
|
|
||||||
val size: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun ByteArray.wrap(): PlatformBuffer = PlatformBuffer(this)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package cn.rtast.mcping.platform
|
|
||||||
|
|
||||||
internal expect class ReadChannel {
|
|
||||||
fun readByte(): Byte
|
|
||||||
fun readBytes(length: Int): ByteArray
|
|
||||||
fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
|
||||||
fun readLong(): Long
|
|
||||||
}
|
|
||||||
|
|
||||||
internal expect class WriteChannel {
|
|
||||||
fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
|
||||||
fun flush()
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package cn.rtast.mcping.platform
|
|
||||||
|
|
||||||
internal expect class Socket internal constructor(host: String, port: Int, context: PingContext) {
|
|
||||||
fun openReadChannel(): ReadChannel
|
|
||||||
fun openWriteChannel(): WriteChannel
|
|
||||||
fun close()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal expect class UdpSocket internal constructor(host: String, port: Int, context: PingContext) {
|
|
||||||
fun sendAndReceive(data: ByteArray): ByteArray
|
|
||||||
fun close()
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cn.rtast.mcping.platform
|
|
||||||
|
|
||||||
import kotlinx.io.Buffer
|
|
||||||
import kotlinx.io.readByteArray
|
|
||||||
|
|
||||||
internal actual class PlatformBuffer {
|
|
||||||
private val _delegateBuf: Buffer
|
|
||||||
|
|
||||||
actual constructor() {
|
|
||||||
_delegateBuf = Buffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual constructor(bytes: ByteArray) {
|
|
||||||
_delegateBuf = Buffer().apply { write(bytes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun writeByte(value: Byte) = _delegateBuf.writeByte(value)
|
|
||||||
actual fun writeShort(value: Short) = _delegateBuf.writeShort(value)
|
|
||||||
actual fun writeLong(value: Long) = _delegateBuf.writeLong(value)
|
|
||||||
actual fun writeBytes(bytes: ByteArray) = _delegateBuf.write(bytes)
|
|
||||||
|
|
||||||
actual fun readByte(): Byte = _delegateBuf.readByte()
|
|
||||||
actual fun readShort(): Short = _delegateBuf.readShort()
|
|
||||||
actual fun readLong(): Long = _delegateBuf.readLong()
|
|
||||||
actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
|
|
||||||
|
|
||||||
actual fun toByteArray(): ByteArray = _delegateBuf.peek().readByteArray()
|
|
||||||
|
|
||||||
actual val size: Int
|
|
||||||
get() = _delegateBuf.size.toInt()
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/3
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cn.rtast.mcping.platform
|
|
||||||
|
|
||||||
import io.ktor.utils.io.*
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
internal actual class ReadChannel {
|
|
||||||
private val _readChannel: ByteReadChannel
|
|
||||||
|
|
||||||
constructor(readChannel: ByteReadChannel) {
|
|
||||||
_readChannel = readChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun readByte(): Byte = runBlocking { _readChannel.readByte() }
|
|
||||||
actual fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) }
|
|
||||||
actual fun readFully(out: ByteArray, start: Int, end: Int) = runBlocking { _readChannel.readFully(out, start, end) }
|
|
||||||
actual fun readLong(): Long = runBlocking { _readChannel.readLong() }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal actual class WriteChannel {
|
|
||||||
private val _writeChannel: ByteWriteChannel
|
|
||||||
|
|
||||||
constructor(writeChannel: ByteWriteChannel) {
|
|
||||||
_writeChannel = writeChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun writeFully(value: ByteArray, startIndex: Int, endIndex: Int) =
|
|
||||||
runBlocking { _writeChannel.writeFully(value, startIndex, endIndex) }
|
|
||||||
|
|
||||||
actual fun flush() = runBlocking { _writeChannel.flush() }
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user