Split rconlib and mcping
This commit is contained in:
38 files changed
+350
-268
No files matched your search
+11
-36
@@ -1,48 +1,22 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "2.4.10"
|
||||
kotlin("multiplatform") version "2.4.10" apply false
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
group = "cn.rtast.mcping"
|
||||
version = providers.gradleProperty("libVersion").get()
|
||||
allprojects {
|
||||
group = "cn.rtast.mcping"
|
||||
version = providers.gradleProperty("libVersion").get()
|
||||
|
||||
repositories {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
subprojects {
|
||||
pluginManager.apply("org.jetbrains.kotlin.multiplatform")
|
||||
pluginManager.apply("maven-publish")
|
||||
|
||||
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 {
|
||||
publishing {
|
||||
repositories {
|
||||
maven("https://repo.rtast.cn/packages") {
|
||||
credentials(HttpHeaderCredentials::class) {
|
||||
@@ -54,4 +28,5 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
internal actual class PlatformBuffer {
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _Buffer {
|
||||
private val outStream = ByteArrayOutputStream()
|
||||
private var readBuffer: ByteArray? = null
|
||||
private var readOffset = 0
|
||||
|
||||
actual constructor()
|
||||
|
||||
actual constructor(bytes: ByteArray) {
|
||||
public actual constructor()
|
||||
public actual constructor(bytes: ByteArray) {
|
||||
this.readBuffer = bytes
|
||||
outStream.write(bytes)
|
||||
}
|
||||
|
||||
actual fun writeByte(value: Byte) {
|
||||
public actual fun writeByte(value: Byte) {
|
||||
outStream.write(value.toInt())
|
||||
}
|
||||
|
||||
actual fun writeShort(value: Short) {
|
||||
public actual fun writeShort(value: Short) {
|
||||
val v = value.toInt()
|
||||
outStream.write(v shr 8)
|
||||
outStream.write(v)
|
||||
}
|
||||
|
||||
actual fun writeLong(value: Long) {
|
||||
public actual fun writeLong(value: Long) {
|
||||
outStream.write((value shr 56).toInt())
|
||||
outStream.write((value shr 48).toInt())
|
||||
outStream.write((value shr 40).toInt())
|
||||
@@ -41,7 +41,7 @@ internal actual class PlatformBuffer {
|
||||
outStream.write(value.toInt())
|
||||
}
|
||||
|
||||
actual fun writeBytes(bytes: ByteArray) {
|
||||
public actual fun writeBytes(bytes: ByteArray) {
|
||||
outStream.write(bytes)
|
||||
}
|
||||
|
||||
@@ -54,19 +54,19 @@ internal actual class PlatformBuffer {
|
||||
return buf
|
||||
}
|
||||
|
||||
actual fun readByte(): Byte {
|
||||
public actual fun readByte(): Byte {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset >= array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||
return array[readOffset++]
|
||||
}
|
||||
|
||||
actual fun readShort(): Short {
|
||||
public actual fun readShort(): Short {
|
||||
val b1 = readByte().toInt() and 0xFF
|
||||
val b2 = readByte().toInt() and 0xFF
|
||||
return ((b1 shl 8) or b2).toShort()
|
||||
}
|
||||
|
||||
actual fun readLong(): Long {
|
||||
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
|
||||
@@ -77,7 +77,7 @@ internal actual class PlatformBuffer {
|
||||
(readByte().toLong() and 0xFF)
|
||||
}
|
||||
|
||||
actual fun readBytes(length: Int): ByteArray {
|
||||
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)
|
||||
@@ -85,8 +85,8 @@ internal actual class PlatformBuffer {
|
||||
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()
|
||||
}
|
||||
+12
-10
@@ -11,16 +11,17 @@ import java.io.EOFException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
internal actual class ReadChannel {
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class _ReadChannel {
|
||||
private val _inputStream: InputStream
|
||||
|
||||
constructor(inputStream: InputStream) {
|
||||
public constructor(inputStream: InputStream) {
|
||||
_inputStream = inputStream
|
||||
}
|
||||
|
||||
actual fun readByte(): Byte = _inputStream.read().toByte()
|
||||
actual fun readBytes(length: Int): ByteArray = _inputStream.readNBytes(length)
|
||||
actual fun readFully(out: ByteArray, start: Int, end: Int) {
|
||||
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) {
|
||||
@@ -30,7 +31,7 @@ internal actual class ReadChannel {
|
||||
}
|
||||
}
|
||||
|
||||
actual fun readLong(): Long {
|
||||
public actual fun readLong(): Long {
|
||||
val bytes = ByteArray(8)
|
||||
var read = 0
|
||||
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
|
||||
|
||||
constructor(outputStream: OutputStream) {
|
||||
public constructor(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)
|
||||
|
||||
actual fun flush() = _outputStream.flush()
|
||||
public actual fun flush(): Unit = _outputStream.flush()
|
||||
}
|
||||
|
||||
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.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)
|
||||
|
||||
actual fun openReadChannel(): ReadChannel = ReadChannel(socket.getInputStream())
|
||||
actual fun openWriteChannel(): WriteChannel = WriteChannel(socket.getOutputStream())
|
||||
actual fun close() = socket.close()
|
||||
public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.getInputStream())
|
||||
public actual fun openWriteChannel(): _WriteChannel = _WriteChannel(socket.getOutputStream())
|
||||
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 {
|
||||
soTimeout = 3000
|
||||
connect(InetSocketAddress(host, port))
|
||||
}
|
||||
|
||||
actual fun sendAndReceive(data: ByteArray): ByteArray {
|
||||
public actual fun sendAndReceive(data: ByteArray): ByteArray {
|
||||
socket.send(DatagramPacket(data, data.size))
|
||||
|
||||
val buf = ByteArray(2048)
|
||||
@@ -34,5 +36,5 @@ internal actual class UdpSocket internal actual constructor(host: String, port:
|
||||
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.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 socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) }
|
||||
|
||||
actual fun openReadChannel(): ReadChannel = ReadChannel(socket.openReadChannel())
|
||||
actual fun openWriteChannel(): WriteChannel =
|
||||
WriteChannel(socket.openWriteChannel(autoFlush = true))
|
||||
public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.openReadChannel())
|
||||
public actual fun openWriteChannel(): _WriteChannel =
|
||||
_WriteChannel(socket.openWriteChannel(autoFlush = true))
|
||||
|
||||
actual fun close() {
|
||||
public actual fun close() {
|
||||
socket.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
|
||||
|
||||
// use bind to create an unconnected socket
|
||||
private val socket = runBlocking { aSocket(ctx._selectorManager).udp().bind() }
|
||||
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) }
|
||||
socket.send(Datagram(packet, remoteAddress))
|
||||
socket.receive().packet.readByteArray()
|
||||
}
|
||||
|
||||
actual fun close() {
|
||||
public actual fun close() {
|
||||
socket.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
|
||||
|
||||
import cn.rtast.mcping.platform.PlatformBuffer
|
||||
import cn.rtast.mcping.platform._Buffer
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Clock
|
||||
|
||||
private val rakNetMagic = byteArrayOf(
|
||||
0x00, 0xFF.toByte(),
|
||||
@@ -26,7 +25,7 @@ internal interface MinecraftBedrockPacket {
|
||||
val time: Long
|
||||
val magic: ByteArray
|
||||
|
||||
fun writePayload(buffer: PlatformBuffer)
|
||||
fun writePayload(buffer: _Buffer)
|
||||
}
|
||||
|
||||
internal data class BedrockRequestPacket(
|
||||
@@ -36,7 +35,7 @@ internal data class BedrockRequestPacket(
|
||||
) : MinecraftBedrockPacket {
|
||||
override val packetId: Byte = 0x01
|
||||
|
||||
override fun writePayload(buffer: PlatformBuffer) {
|
||||
override fun writePayload(buffer: _Buffer) {
|
||||
buffer.writeByte(packetId)
|
||||
buffer.writeLong(time)
|
||||
buffer.writeBytes(magic)
|
||||
@@ -71,7 +70,7 @@ internal data class BedrockResponsePacket(
|
||||
val stringLength: Short,
|
||||
val payload: String,
|
||||
) : MinecraftBedrockPacket {
|
||||
override fun writePayload(buffer: PlatformBuffer) {}
|
||||
override fun writePayload(buffer: _Buffer) {}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
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.platform.PingContext
|
||||
import cn.rtast.mcping.platform.UdpSocket
|
||||
import cn.rtast.mcping.platform._UdpSocket
|
||||
import cn.rtast.mcping.platform.wrap
|
||||
import kotlin.time.Clock
|
||||
|
||||
internal fun pingBedrockServer(host: String, port: Int, context: PingContext): PingResponse {
|
||||
val socket = UdpSocket(host, port, context)
|
||||
val socket = _UdpSocket(host, port, context)
|
||||
return try {
|
||||
val sendTime = Clock.System.now().toEpochMilliseconds()
|
||||
val packet = BedrockRequestPacket(sendTime)
|
||||
File renamed without changes.
+6
-6
@@ -6,11 +6,11 @@
|
||||
|
||||
package cn.rtast.mcping.java
|
||||
|
||||
import cn.rtast.mcping.platform.PlatformBuffer
|
||||
import cn.rtast.mcping.platform.ReadChannel
|
||||
import cn.rtast.mcping.platform._Buffer
|
||||
import cn.rtast.mcping.platform._ReadChannel
|
||||
|
||||
|
||||
internal fun PlatformBuffer.writeVarInt(value: Int) {
|
||||
internal fun _Buffer.writeVarInt(value: Int) {
|
||||
var v = value
|
||||
while (true) {
|
||||
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 position = 0
|
||||
while (true) {
|
||||
@@ -35,13 +35,13 @@ internal fun ReadChannel.readVarInt(): Int {
|
||||
return value
|
||||
}
|
||||
|
||||
internal fun PlatformBuffer.writeMcString(value: String) {
|
||||
internal fun _Buffer.writeMcString(value: String) {
|
||||
val bytes = value.encodeToByteArray()
|
||||
this.writeVarInt(bytes.size)
|
||||
this.writeBytes(bytes)
|
||||
}
|
||||
|
||||
internal fun ReadChannel.readMcString(): String {
|
||||
internal fun _ReadChannel.readMcString(): String {
|
||||
val length = this.readVarInt()
|
||||
val bytes = this.readBytes(length)
|
||||
return bytes.decodeToString()
|
||||
+5
-5
@@ -7,12 +7,12 @@
|
||||
|
||||
package cn.rtast.mcping.java
|
||||
|
||||
import cn.rtast.mcping.platform.PlatformBuffer
|
||||
import cn.rtast.mcping.platform._Buffer
|
||||
|
||||
internal interface MinecraftPacket {
|
||||
val packetId: Int
|
||||
|
||||
fun writePayload(buffer: PlatformBuffer)
|
||||
fun writePayload(buffer: _Buffer)
|
||||
}
|
||||
|
||||
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake
|
||||
@@ -25,7 +25,7 @@ internal data class HandshakePacket(
|
||||
) : MinecraftPacket {
|
||||
override val packetId: Int = 0x00
|
||||
|
||||
override fun writePayload(buffer: PlatformBuffer) {
|
||||
override fun writePayload(buffer: _Buffer) {
|
||||
buffer.writeVarInt(protocolVersion)
|
||||
buffer.writeMcString(serverAddress)
|
||||
// write UShort
|
||||
@@ -39,10 +39,10 @@ internal data class HandshakePacket(
|
||||
// ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status
|
||||
internal data object StatusRequestPacket : MinecraftPacket {
|
||||
override val packetId: Int = 0x00
|
||||
override fun writePayload(buffer: PlatformBuffer) {}
|
||||
override fun writePayload(buffer: _Buffer) {}
|
||||
}
|
||||
|
||||
internal data class PingPacket(val currentTime: Long) : MinecraftPacket {
|
||||
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
|
||||
|
||||
import cn.rtast.mcping.platform.PlatformBuffer
|
||||
import cn.rtast.mcping.platform.WriteChannel
|
||||
import cn.rtast.mcping.platform._Buffer
|
||||
import cn.rtast.mcping.platform._WriteChannel
|
||||
|
||||
internal fun WriteChannel.sendPacket(packet: MinecraftPacket) {
|
||||
val bodyBuffer = PlatformBuffer()
|
||||
internal fun _WriteChannel.sendPacket(packet: MinecraftPacket) {
|
||||
val bodyBuffer = _Buffer()
|
||||
bodyBuffer.writeVarInt(packet.packetId)
|
||||
packet.writePayload(bodyBuffer)
|
||||
val frameBuffer = PlatformBuffer()
|
||||
val frameBuffer = _Buffer()
|
||||
frameBuffer.writeVarInt(bodyBuffer.size)
|
||||
frameBuffer.writeBytes(bodyBuffer.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.platform.PingContext
|
||||
import cn.rtast.mcping.platform.Socket
|
||||
import cn.rtast.mcping.platform._Socket
|
||||
import kotlin.time.Clock
|
||||
|
||||
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 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
|
||||
|
||||
@@ -2,3 +2,7 @@ plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
}
|
||||
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