Added some packets
This commit is contained in:
22 files changed
+326
-45
No files matched your search
@@ -9,7 +9,6 @@ package cn.rtast.libmc.common
|
|||||||
|
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public expect class BytesBuffer {
|
public expect class BytesBuffer {
|
||||||
public constructor()
|
public constructor()
|
||||||
public constructor(bytes: ByteArray)
|
public constructor(bytes: ByteArray)
|
||||||
@@ -18,6 +17,8 @@ public expect class BytesBuffer {
|
|||||||
public fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
public fun writeShort(value: Short, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||||
public fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
public fun writeInt(value: Int, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||||
public fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
public fun writeLong(value: Long, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||||
|
public fun writeDouble(value: Double, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||||
|
public fun writeFloat(value: Float, endian: ByteOrder = ByteOrder.BIG_ENDIAN)
|
||||||
public fun writeBytes(bytes: ByteArray)
|
public fun writeBytes(bytes: ByteArray)
|
||||||
public fun writeBoolean(value: Boolean)
|
public fun writeBoolean(value: Boolean)
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@ public expect class BytesBuffer {
|
|||||||
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||||
public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||||
public fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
public fun readLong(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Long
|
||||||
|
public fun readDouble(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Double
|
||||||
|
public fun readFloat(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Float
|
||||||
public fun readBytes(length: Int): ByteArray
|
public fun readBytes(length: Int): ByteArray
|
||||||
public fun readBoolean(): Boolean
|
public fun readBoolean(): Boolean
|
||||||
public fun readRemainingBytes(): ByteArray
|
public fun readRemainingBytes(): ByteArray
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.common
|
package cn.rtast.libmc.common
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public expect class ReadChannel {
|
public expect class ReadChannel {
|
||||||
public fun readByte(): Byte
|
public fun readByte(): Byte
|
||||||
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||||
@@ -17,7 +16,6 @@ public expect class ReadChannel {
|
|||||||
public fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
public fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public expect class WriteChannel {
|
public expect class WriteChannel {
|
||||||
public fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
public fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||||
public fun flush()
|
public fun flush()
|
||||||
|
|||||||
@@ -7,15 +7,13 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.common
|
package cn.rtast.libmc.common
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
|
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
public fun openReadChannel(): ReadChannel
|
public fun openReadChannel(): ReadChannel
|
||||||
public fun openWriteChannel(): WriteChannel
|
public fun openWriteChannel(): WriteChannel
|
||||||
public fun close()
|
public fun close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
public expect class UdpSocket public constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
public expect class _UdpSocket public constructor(host: String, port: Int, context: LibMCContext) {
|
|
||||||
public fun sendAndReceive(data: ByteArray): ByteArray
|
public fun sendAndReceive(data: ByteArray): ByteArray
|
||||||
public fun close()
|
public fun close()
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ package cn.rtast.libmc.common
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class BytesBuffer {
|
public actual class BytesBuffer {
|
||||||
private val outStream = ByteArrayOutputStream()
|
private val outStream = ByteArrayOutputStream()
|
||||||
private var readBuffer: ByteArray? = null
|
private var readBuffer: ByteArray? = null
|
||||||
@@ -26,6 +25,7 @@ public actual class BytesBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
||||||
|
readBuffer = null
|
||||||
val v = value.toInt()
|
val v = value.toInt()
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||||
outStream.write(v shr 8)
|
outStream.write(v shr 8)
|
||||||
@@ -37,6 +37,7 @@ public actual class BytesBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
||||||
|
readBuffer = null
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||||
outStream.write(value shr 24)
|
outStream.write(value shr 24)
|
||||||
outStream.write(value shr 16)
|
outStream.write(value shr 16)
|
||||||
@@ -51,6 +52,7 @@ public actual class BytesBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
||||||
|
readBuffer = null
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||||
for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt())
|
for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt())
|
||||||
} else {
|
} else {
|
||||||
@@ -58,6 +60,14 @@ public actual class BytesBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public actual fun writeDouble(value: Double, endian: ByteOrder) {
|
||||||
|
writeLong(value.toRawBits(), endian)
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual fun writeFloat(value: Float, endian: ByteOrder) {
|
||||||
|
writeInt(value.toRawBits(), endian)
|
||||||
|
}
|
||||||
|
|
||||||
public actual fun writeBytes(bytes: ByteArray) {
|
public actual fun writeBytes(bytes: ByteArray) {
|
||||||
readBuffer = null
|
readBuffer = null
|
||||||
outStream.write(bytes)
|
outStream.write(bytes)
|
||||||
@@ -83,6 +93,15 @@ public actual class BytesBuffer {
|
|||||||
public actual fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
public actual fun readShort(endian: ByteOrder): Short = readBytes(2).toShort(endian)
|
||||||
public actual fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
public actual fun readInt(endian: ByteOrder): Int = readBytes(4).toInt(endian)
|
||||||
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||||
|
|
||||||
|
public actual fun readDouble(endian: ByteOrder): Double {
|
||||||
|
return Double.fromBits(readLong(endian))
|
||||||
|
}
|
||||||
|
|
||||||
|
public actual fun readFloat(endian: ByteOrder): Float {
|
||||||
|
return Float.fromBits(readInt(endian))
|
||||||
|
}
|
||||||
|
|
||||||
public 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")
|
||||||
@@ -96,7 +115,9 @@ public actual class BytesBuffer {
|
|||||||
public actual fun readRemainingBytes(): ByteArray {
|
public actual fun readRemainingBytes(): ByteArray {
|
||||||
val array = ensureReadArray()
|
val array = ensureReadArray()
|
||||||
if (readOffset >= array.size) return byteArrayOf()
|
if (readOffset >= array.size) return byteArrayOf()
|
||||||
return array.copyOfRange(readOffset, array.size)
|
val result = array.copyOfRange(readOffset, array.size)
|
||||||
|
readOffset = array.size
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
|
public actual fun toByteArray(): ByteArray = outStream.toByteArray()
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import java.io.EOFException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class ReadChannel(private val _inputStream: InputStream) {
|
public actual class ReadChannel(private val _inputStream: InputStream) {
|
||||||
|
|
||||||
public actual fun readByte(): Byte = _inputStream.read().toByte()
|
public actual fun readByte(): Byte = _inputStream.read().toByte()
|
||||||
@@ -32,7 +31,6 @@ public actual class ReadChannel(private val _inputStream: InputStream) {
|
|||||||
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
public actual fun readLong(endian: ByteOrder): Long = readBytes(8).toLong(endian)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class WriteChannel {
|
public actual class WriteChannel {
|
||||||
private val _outputStream: OutputStream
|
private val _outputStream: OutputStream
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import java.net.DatagramSocket
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Socket as JvmSocket
|
import java.net.Socket as JvmSocket
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
private val socket = JvmSocket(host, port)
|
private val socket = JvmSocket(host, port)
|
||||||
|
|
||||||
@@ -20,8 +19,7 @@ public actual class Socket public actual constructor(host: String, port: Int, co
|
|||||||
public actual fun close(): Unit = socket.close()
|
public actual fun close(): Unit = socket.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
public actual class UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
|
||||||
private val socket = DatagramSocket().apply {
|
private val socket = DatagramSocket().apply {
|
||||||
soTimeout = 3000
|
soTimeout = 3000
|
||||||
connect(InetSocketAddress(host, port))
|
connect(InetSocketAddress(host, port))
|
||||||
|
|||||||
@@ -6,12 +6,10 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.common
|
package cn.rtast.libmc.common
|
||||||
|
|
||||||
import io.ktor.utils.io.bits.*
|
|
||||||
import io.ktor.utils.io.core.*
|
import io.ktor.utils.io.core.*
|
||||||
|
import kotlinx.io.*
|
||||||
import kotlinx.io.Buffer
|
import kotlinx.io.Buffer
|
||||||
import kotlinx.io.readByteArray
|
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class BytesBuffer {
|
public actual class BytesBuffer {
|
||||||
private val _delegateBuf: Buffer
|
private val _delegateBuf: Buffer
|
||||||
|
|
||||||
@@ -26,37 +24,43 @@ public actual class BytesBuffer {
|
|||||||
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
|
public actual fun writeByte(value: Byte): Unit = _delegateBuf.writeByte(value)
|
||||||
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value)
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeShort(value)
|
||||||
else _delegateBuf.writeShort(value.reverseByteOrder())
|
else _delegateBuf.writeShortLe(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeInt(value)
|
||||||
else _delegateBuf.writeInt(value.reverseByteOrder())
|
else _delegateBuf.writeIntLe(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
||||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeLong(value)
|
||||||
else _delegateBuf.writeLong(value.reverseByteOrder())
|
else _delegateBuf.writeLongLe(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public actual fun writeDouble(value: Double, endian: ByteOrder): Unit =
|
||||||
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeDouble(value) else _delegateBuf.writeDoubleLe(value)
|
||||||
|
|
||||||
|
public actual fun writeFloat(value: Float, endian: ByteOrder): Unit =
|
||||||
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.writeFloat(value) else _delegateBuf.writeFloatLe(value)
|
||||||
|
|
||||||
public actual fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
|
public actual fun writeBytes(bytes: ByteArray): Unit = _delegateBuf.write(bytes)
|
||||||
public actual fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
|
public actual fun writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
|
||||||
|
|
||||||
public actual fun readByte(): Byte = _delegateBuf.readByte()
|
public actual fun readByte(): Byte = _delegateBuf.readByte()
|
||||||
public actual fun readShort(endian: ByteOrder): Short {
|
public actual fun readShort(endian: ByteOrder): Short =
|
||||||
val v = _delegateBuf.readShort()
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readShort() else _delegateBuf.readShortLe()
|
||||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
|
||||||
}
|
|
||||||
|
|
||||||
public actual fun readInt(endian: ByteOrder): Int {
|
public actual fun readInt(endian: ByteOrder): Int =
|
||||||
val v = _delegateBuf.readInt()
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe()
|
||||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
|
||||||
}
|
|
||||||
|
|
||||||
public actual fun readLong(endian: ByteOrder): Long {
|
public actual fun readLong(endian: ByteOrder): Long =
|
||||||
val v = _delegateBuf.readLong()
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe()
|
||||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
|
||||||
}
|
public actual fun readDouble(endian: ByteOrder): Double =
|
||||||
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readDouble() else _delegateBuf.readDoubleLe()
|
||||||
|
|
||||||
|
public actual fun readFloat(endian: ByteOrder): Float =
|
||||||
|
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readFloat() else _delegateBuf.readFloatLe()
|
||||||
|
|
||||||
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
|
public actual fun readBytes(length: Int): ByteArray = _delegateBuf.readByteArray(length)
|
||||||
public actual fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
|
public actual fun readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import io.ktor.utils.io.*
|
|||||||
import io.ktor.utils.io.bits.reverseByteOrder
|
import io.ktor.utils.io.bits.reverseByteOrder
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class ReadChannel(private val _readChannel: ByteReadChannel) {
|
public actual class ReadChannel(private val _readChannel: ByteReadChannel) {
|
||||||
|
|
||||||
public actual fun readByte(): Byte = runBlocking { _readChannel.readByte() }
|
public actual fun readByte(): Byte = runBlocking { _readChannel.readByte() }
|
||||||
@@ -34,7 +33,6 @@ public actual class ReadChannel(private val _readChannel: ByteReadChannel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class WriteChannel {
|
public actual class WriteChannel {
|
||||||
private val _writeChannel: ByteWriteChannel
|
private val _writeChannel: ByteWriteChannel
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.ktor.utils.io.core.*
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.io.readByteArray
|
import kotlinx.io.readByteArray
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
|
||||||
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
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) }
|
||||||
@@ -26,8 +25,7 @@ public actual class Socket public actual constructor(host: String, port: Int, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CLASSNAME")
|
public actual class UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||||
public actual class _UdpSocket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
|
||||||
private val ctx = context
|
private val ctx = context
|
||||||
|
|
||||||
// use bind to create an unconnected socket
|
// use bind to create an unconnected socket
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ package cn.rtast.libmc.mcping.bedrock
|
|||||||
|
|
||||||
import cn.rtast.libmc.common.PacketCodec
|
import cn.rtast.libmc.common.PacketCodec
|
||||||
import cn.rtast.libmc.common.BytesBuffer
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
import cn.rtast.libmc.common._UdpSocket
|
import cn.rtast.libmc.common.UdpSocket
|
||||||
|
|
||||||
|
|
||||||
internal fun <T : MinecraftBedrockPacket> _UdpSocket.sendPacket(packet: T, codec: PacketCodec<T>): ByteArray {
|
internal fun <T : MinecraftBedrockPacket> UdpSocket.sendPacket(packet: T, codec: PacketCodec<T>): ByteArray {
|
||||||
val buf = BytesBuffer()
|
val buf = BytesBuffer()
|
||||||
codec.encode(buf, packet)
|
codec.encode(buf, packet)
|
||||||
return sendAndReceive(buf.toByteArray())
|
return sendAndReceive(buf.toByteArray())
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
package cn.rtast.libmc.mcping.bedrock
|
package cn.rtast.libmc.mcping.bedrock
|
||||||
|
|
||||||
import cn.rtast.libmc.common.LibMCContext
|
import cn.rtast.libmc.common.LibMCContext
|
||||||
import cn.rtast.libmc.common._UdpSocket
|
import cn.rtast.libmc.common.UdpSocket
|
||||||
import cn.rtast.libmc.common.wrap
|
import cn.rtast.libmc.common.wrap
|
||||||
import cn.rtast.libmc.mcping.PingResponse
|
import cn.rtast.libmc.mcping.PingResponse
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
|
||||||
internal fun pingBedrockServer(host: String, port: Int, context: LibMCContext): PingResponse {
|
internal fun pingBedrockServer(host: String, port: Int, context: LibMCContext): 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 requestPacket = BedrockRequestPacket(sendTime)
|
val requestPacket = BedrockRequestPacket(sendTime)
|
||||||
|
|||||||
+2
@@ -88,6 +88,8 @@ internal class InternalPacketDispatcher(private val client: MinecraftClient) {
|
|||||||
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
|
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
|
||||||
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ClientboundSystemChatMessagePacket -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.packet.play
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import cn.rtast.libmc.common.PacketCodec
|
||||||
|
|
||||||
|
public data object ClientboundDelimiterPacket : ClientboundPlayPacket, PacketCodec<ClientboundDelimiterPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundDelimiterPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundDelimiterPacket {
|
||||||
|
return ClientboundDelimiterPacket
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.packet.play
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import cn.rtast.libmc.common.PacketCodec
|
||||||
|
import cn.rtast.libmc.common.readVarInt
|
||||||
|
import cn.rtast.libmc.protocol.types.Animations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Entity_Animation
|
||||||
|
*/
|
||||||
|
public data class ClientboundEntityAnimationPacket(val entityId: Int, val animation: Animations) :
|
||||||
|
ClientboundPlayPacket {
|
||||||
|
public companion object Codec : PacketCodec<ClientboundEntityAnimationPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundEntityAnimationPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundEntityAnimationPacket {
|
||||||
|
val entityId = buffer.readVarInt()
|
||||||
|
val animation = Animations.fromID(buffer.readByte())
|
||||||
|
return ClientboundEntityAnimationPacket(entityId, animation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.packet.play
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import cn.rtast.libmc.common.PacketCodec
|
||||||
|
import cn.rtast.libmc.common.readUuid
|
||||||
|
import cn.rtast.libmc.common.readVarInt
|
||||||
|
import cn.rtast.libmc.protocol.types.Angle
|
||||||
|
import cn.rtast.libmc.protocol.types.LpVec3
|
||||||
|
import cn.rtast.libmc.protocol.types.readAngle
|
||||||
|
import cn.rtast.libmc.protocol.types.readLpVec3
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Spawn_Entity
|
||||||
|
*/
|
||||||
|
public data class ClientboundSpawnEntityPacket(
|
||||||
|
val entityId: Int,
|
||||||
|
val entityUuid: Uuid,
|
||||||
|
val type: Int,
|
||||||
|
val x: Double,
|
||||||
|
val y: Double,
|
||||||
|
val z: Double,
|
||||||
|
val velocity: LpVec3,
|
||||||
|
val pitch: Angle,
|
||||||
|
val yaw: Angle,
|
||||||
|
val headYaw: Angle,
|
||||||
|
val data: Int,
|
||||||
|
) : ClientboundPlayPacket {
|
||||||
|
public companion object Codec : PacketCodec<ClientboundSpawnEntityPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundSpawnEntityPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundSpawnEntityPacket {
|
||||||
|
val entityId = buffer.readVarInt()
|
||||||
|
val entityUuid = buffer.readUuid()
|
||||||
|
val type = buffer.readVarInt()
|
||||||
|
val x = buffer.readDouble()
|
||||||
|
val y = buffer.readDouble()
|
||||||
|
val z = buffer.readDouble()
|
||||||
|
val velocity = buffer.readLpVec3()
|
||||||
|
val pitch = buffer.readAngle()
|
||||||
|
val yaw = buffer.readAngle()
|
||||||
|
val headYaw = buffer.readAngle()
|
||||||
|
val data = buffer.readVarInt()
|
||||||
|
return ClientboundSpawnEntityPacket(
|
||||||
|
entityId, entityUuid,
|
||||||
|
type, x, y, z, velocity,
|
||||||
|
pitch, yaw, headYaw, data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.packet.play
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import cn.rtast.libmc.common.PacketCodec
|
||||||
|
import cn.rtast.libmc.nbt.NBTCompound
|
||||||
|
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||||
|
|
||||||
|
public data class ClientboundSystemChatMessagePacket(val content: NBTCompound, val overlay: Boolean) :
|
||||||
|
ClientboundPlayPacket {
|
||||||
|
public companion object Codec : PacketCodec<ClientboundSystemChatMessagePacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundSystemChatMessagePacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundSystemChatMessagePacket {
|
||||||
|
val content = buffer.readNetworkNBTCompound()
|
||||||
|
val overlay = buffer.readBoolean()
|
||||||
|
return ClientboundSystemChatMessagePacket(content, overlay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -8,11 +8,7 @@ package cn.rtast.libmc.protocol.protocol
|
|||||||
|
|
||||||
import cn.rtast.libmc.protocol.packet.configuration.*
|
import cn.rtast.libmc.protocol.packet.configuration.*
|
||||||
import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket
|
import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket
|
||||||
import cn.rtast.libmc.protocol.packet.login.ClientboundDisconnectLoginPacket
|
import cn.rtast.libmc.protocol.packet.login.*
|
||||||
import cn.rtast.libmc.protocol.packet.login.ClientboundLoginSuccessPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.ClientboundSetCompressionPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.ServerboundLoginAcknowledgedPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.ServerboundLoginStartPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.*
|
import cn.rtast.libmc.protocol.packet.play.*
|
||||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
||||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry
|
import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry
|
||||||
@@ -40,6 +36,7 @@ internal object GameProtocols {
|
|||||||
register(0x3d, ClientboundPingPlayPacket)
|
register(0x3d, ClientboundPingPlayPacket)
|
||||||
register(0x41, ClientboundPlayerChatMessagePacket)
|
register(0x41, ClientboundPlayerChatMessagePacket)
|
||||||
register(0x76, ClientboundStartConfigurationPacket)
|
register(0x76, ClientboundStartConfigurationPacket)
|
||||||
|
register(0x79, ClientboundSystemChatMessagePacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +55,9 @@ internal object GameProtocols {
|
|||||||
register(0x03, ServerboundLoginAcknowledgedPacket)
|
register(0x03, ServerboundLoginAcknowledgedPacket)
|
||||||
}
|
}
|
||||||
register(ProtocolState.PLAY) {
|
register(ProtocolState.PLAY) {
|
||||||
|
register(0x00, ClientboundDelimiterPacket)
|
||||||
|
register(0x01, ClientboundSpawnEntityPacket)
|
||||||
|
register(0x02, ClientboundEntityAnimationPacket)
|
||||||
register(0x09, ServerboundChatMessagePacket)
|
register(0x09, ServerboundChatMessagePacket)
|
||||||
register(0x2d, ServerboundPongPlayPacket)
|
register(0x2d, ServerboundPongPlayPacket)
|
||||||
register(0x10, ServerboundConfigurationAcknowledgedPacket)
|
register(0x10, ServerboundConfigurationAcknowledgedPacket)
|
||||||
|
|||||||
+1
@@ -27,6 +27,7 @@ public value class Identifier internal constructor(public val raw: String) {
|
|||||||
|
|
||||||
public companion object Codec : PacketCodec<Identifier> {
|
public companion object Codec : PacketCodec<Identifier> {
|
||||||
public fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path")
|
public fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path")
|
||||||
|
public fun of(full: String): Identifier = Identifier(full)
|
||||||
|
|
||||||
override fun encode(buffer: BytesBuffer, value: Identifier) {
|
override fun encode(buffer: BytesBuffer, value: Identifier) {
|
||||||
buffer.writeMcString(value.toString())
|
buffer.writeMcString(value.toString())
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.types
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Type:Angle
|
||||||
|
*/
|
||||||
|
@JvmInline
|
||||||
|
public value class Angle(public val raw: Byte) {
|
||||||
|
/**
|
||||||
|
* convert the Angle(byte) to 360-degree float angles
|
||||||
|
*/
|
||||||
|
public fun toDegrees(): Float = (raw.toInt() and 0xFF) * 360.0f / 256.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encode 360-degress float angles into Angle(byte)
|
||||||
|
*/
|
||||||
|
public fun toAngleByte(): Byte = ((raw % 360.0f) * 256.0f / 360.0f).toInt().toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun BytesBuffer.readAngle(): Angle = Angle(this.readByte())
|
||||||
|
internal fun BytesBuffer.writeAngle(value: Angle) = this.writeByte(value.raw)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ref: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Entity_Animation
|
||||||
|
*/
|
||||||
|
public enum class Animations(public val animationId: Byte) {
|
||||||
|
SwingMainArm(0),
|
||||||
|
LeaveBed(2),
|
||||||
|
SwingOffhand(3),
|
||||||
|
CriticalEffect(4),
|
||||||
|
MagicCriticalEffect(5);
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public fun fromID(id: Byte): Animations =
|
||||||
|
entries.firstOrNull { it.animationId == id } ?: throw IllegalArgumentException("Unknown animation ID")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/5
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.types
|
||||||
|
|
||||||
|
import cn.rtast.libmc.common.ByteOrder
|
||||||
|
import cn.rtast.libmc.common.BytesBuffer
|
||||||
|
import cn.rtast.libmc.common.readVarInt
|
||||||
|
import cn.rtast.libmc.common.writeVarInt
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
|
public data class LpVec3(
|
||||||
|
val x: Double,
|
||||||
|
val y: Double,
|
||||||
|
val z: Double,
|
||||||
|
) {
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
public companion object {
|
||||||
|
public val ZERO: LpVec3 = LpVec3(0.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
private const val MAX_QUANTIZED_VALUE = 32766.0
|
||||||
|
private const val CONTINUATION_FLAG = 0x04L
|
||||||
|
private const val SCALE_BITS = 0x03L
|
||||||
|
|
||||||
|
public fun pack(value: Long): Long =
|
||||||
|
((value * 0.5 + 0.5) * MAX_QUANTIZED_VALUE).toLong()
|
||||||
|
|
||||||
|
public fun unpack(value: Long): Double =
|
||||||
|
min((value and 32767L).toDouble(), MAX_QUANTIZED_VALUE) * 2.0 / MAX_QUANTIZED_VALUE - 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
|
internal fun BytesBuffer.readLpVec3(): LpVec3 {
|
||||||
|
val byte1 = readByte().toInt() and 0xFF
|
||||||
|
if (byte1 == 0) return LpVec3.ZERO
|
||||||
|
val byte2 = readByte().toInt() and 0xFF
|
||||||
|
val bytes3To6 = readInt(ByteOrder.BIG_ENDIAN).toLong() and 0xFFFFFFFFL
|
||||||
|
val packed = (bytes3To6 shl 16) or (byte2.toLong() shl 8) or byte1.toLong()
|
||||||
|
var scaleFactor = byte1.toLong() and 0x03L
|
||||||
|
if ((byte1.toLong() and 0x04L) != 0L) scaleFactor = scaleFactor or (readVarInt().toLong() shl 2)
|
||||||
|
val scale = scaleFactor.toDouble()
|
||||||
|
val x = LpVec3.unpack(packed shr 3) * scale
|
||||||
|
val y = LpVec3.unpack(packed shr 18) * scale
|
||||||
|
val z = LpVec3.unpack(packed shr 33) * scale
|
||||||
|
return LpVec3(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
|
internal fun BytesBuffer.writeLpVec3(vec3: LpVec3) {
|
||||||
|
val maxCoordinate = max(abs(vec3.x), max(abs(vec3.y), abs(vec3.z)))
|
||||||
|
if (maxCoordinate.isNaN() || maxCoordinate < 1.0 / 32766.0) {
|
||||||
|
writeByte(0x00)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val scaleFactor = ceil(maxCoordinate).toLong()
|
||||||
|
val needContinuation = (scaleFactor and 0x03L) != scaleFactor
|
||||||
|
val packedScale = if (needContinuation) ((scaleFactor and 0x03L) or 0x04L) else scaleFactor
|
||||||
|
val packedX = LpVec3.pack((vec3.x / scaleFactor.toDouble()).toLong()) shl 3
|
||||||
|
val packedY = LpVec3.pack((vec3.y / scaleFactor.toDouble()).toLong()) shl 18
|
||||||
|
val packedZ = LpVec3.pack((vec3.z / scaleFactor.toDouble()).toLong()) shl 33
|
||||||
|
val packed = packedZ or packedY or packedX or packedScale
|
||||||
|
writeByte(packed.toByte())
|
||||||
|
writeByte((packed shr 8).toByte())
|
||||||
|
writeInt((packed shr 16).toInt(), ByteOrder.BIG_ENDIAN)
|
||||||
|
if (needContinuation) writeVarInt((scaleFactor shr 2).toInt())
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ package test
|
|||||||
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
||||||
import cn.rtast.libmc.protocol.packet.play.ClientboundDisconnectPlayPacket
|
import cn.rtast.libmc.protocol.packet.play.ClientboundDisconnectPlayPacket
|
||||||
import cn.rtast.libmc.protocol.packet.play.ClientboundLoginPlayPacket
|
import cn.rtast.libmc.protocol.packet.play.ClientboundLoginPlayPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.play.ClientboundSystemChatMessagePacket
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@@ -27,6 +28,9 @@ class TestClient {
|
|||||||
cli.on<ClientboundDisconnectPlayPacket> {
|
cli.on<ClientboundDisconnectPlayPacket> {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
cli.on<ClientboundSystemChatMessagePacket> {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user