Split rconlib and mcping

This commit is contained in:
2026-09-04 00:19:17 +08:00
parent 23e7a827db
commit 350b900aaa
38 files changed
+350 -268

No files matched your search

+22 -47
View File
@@ -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")
}
} }
} }
} }
+31
View File
@@ -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,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()
}
@@ -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()
} }
@@ -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 {
@@ -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() }
}
@@ -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()
} }
+24
View File
@@ -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")
}
}
}
@@ -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())
}
@@ -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)
@@ -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()
@@ -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)
} }
@@ -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()
@@ -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()
+24
View File
@@ -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
View File
@@ -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() }
}