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
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class BytesBuffer {
|
||||
public constructor()
|
||||
public constructor(bytes: ByteArray)
|
||||
@@ -18,6 +17,8 @@ public expect class BytesBuffer {
|
||||
public fun writeShort(value: Short, 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 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 writeBoolean(value: Boolean)
|
||||
|
||||
@@ -25,6 +26,8 @@ public expect class BytesBuffer {
|
||||
public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
|
||||
public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int
|
||||
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 readBoolean(): Boolean
|
||||
public fun readRemainingBytes(): ByteArray
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class ReadChannel {
|
||||
public fun readByte(): Byte
|
||||
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)
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class WriteChannel {
|
||||
public fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||
public fun flush()
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) {
|
||||
public fun openReadChannel(): ReadChannel
|
||||
public fun openWriteChannel(): WriteChannel
|
||||
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 close()
|
||||
}
|
||||
@@ -8,7 +8,6 @@ package cn.rtast.libmc.common
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class BytesBuffer {
|
||||
private val outStream = ByteArrayOutputStream()
|
||||
private var readBuffer: ByteArray? = null
|
||||
@@ -26,6 +25,7 @@ public actual class BytesBuffer {
|
||||
}
|
||||
|
||||
public actual fun writeShort(value: Short, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
val v = value.toInt()
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
outStream.write(v shr 8)
|
||||
@@ -37,6 +37,7 @@ public actual class BytesBuffer {
|
||||
}
|
||||
|
||||
public actual fun writeInt(value: Int, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
outStream.write(value shr 24)
|
||||
outStream.write(value shr 16)
|
||||
@@ -51,6 +52,7 @@ public actual class BytesBuffer {
|
||||
}
|
||||
|
||||
public actual fun writeLong(value: Long, endian: ByteOrder) {
|
||||
readBuffer = null
|
||||
if (endian == ByteOrder.BIG_ENDIAN) {
|
||||
for (i in 56 downTo 0 step 8) outStream.write((value shr i).toInt())
|
||||
} 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) {
|
||||
readBuffer = null
|
||||
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 readInt(endian: ByteOrder): Int = readBytes(4).toInt(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 {
|
||||
val array = ensureReadArray()
|
||||
if (readOffset + length > array.size) throw IndexOutOfBoundsException("Buffer underflow")
|
||||
@@ -96,7 +115,9 @@ public actual class BytesBuffer {
|
||||
public actual fun readRemainingBytes(): ByteArray {
|
||||
val array = ensureReadArray()
|
||||
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()
|
||||
|
||||
@@ -11,7 +11,6 @@ import java.io.EOFException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class ReadChannel(private val _inputStream: InputStream) {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class WriteChannel {
|
||||
private val _outputStream: OutputStream
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import java.net.DatagramSocket
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Socket as JvmSocket
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||
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()
|
||||
}
|
||||
|
||||
@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 {
|
||||
soTimeout = 3000
|
||||
connect(InetSocketAddress(host, port))
|
||||
|
||||
@@ -6,12 +6,10 @@
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
import io.ktor.utils.io.bits.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.io.*
|
||||
import kotlinx.io.Buffer
|
||||
import kotlinx.io.readByteArray
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class BytesBuffer {
|
||||
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 writeShort(value: Short, endian: ByteOrder) {
|
||||
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) {
|
||||
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) {
|
||||
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 writeBoolean(value: Boolean): Unit = _delegateBuf.writeByte(if (value) 0x01 else 0x00)
|
||||
|
||||
public actual fun readByte(): Byte = _delegateBuf.readByte()
|
||||
public actual fun readShort(endian: ByteOrder): Short {
|
||||
val v = _delegateBuf.readShort()
|
||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
||||
}
|
||||
public actual fun readShort(endian: ByteOrder): Short =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readShort() else _delegateBuf.readShortLe()
|
||||
|
||||
public actual fun readInt(endian: ByteOrder): Int {
|
||||
val v = _delegateBuf.readInt()
|
||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
||||
}
|
||||
public actual fun readInt(endian: ByteOrder): Int =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readInt() else _delegateBuf.readIntLe()
|
||||
|
||||
public actual fun readLong(endian: ByteOrder): Long {
|
||||
val v = _delegateBuf.readLong()
|
||||
return if (endian == ByteOrder.BIG_ENDIAN) v else v.reverseByteOrder()
|
||||
}
|
||||
public actual fun readLong(endian: ByteOrder): Long =
|
||||
if (endian == ByteOrder.BIG_ENDIAN) _delegateBuf.readLong() else _delegateBuf.readLongLe()
|
||||
|
||||
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 readBoolean(): Boolean = _delegateBuf.readByte() != 0x00.toByte()
|
||||
|
||||
@@ -10,7 +10,6 @@ import io.ktor.utils.io.*
|
||||
import io.ktor.utils.io.bits.reverseByteOrder
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class ReadChannel(private val _readChannel: ByteReadChannel) {
|
||||
|
||||
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 {
|
||||
private val _writeChannel: ByteWriteChannel
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.io.readByteArray
|
||||
|
||||
@Suppress("CLASSNAME")
|
||||
public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) {
|
||||
private val ctx = context
|
||||
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
|
||||
|
||||
// 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.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()
|
||||
codec.encode(buf, packet)
|
||||
return sendAndReceive(buf.toByteArray())
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
package cn.rtast.libmc.mcping.bedrock
|
||||
|
||||
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.mcping.PingResponse
|
||||
import kotlin.time.Clock
|
||||
|
||||
internal fun pingBedrockServer(host: String, port: Int, context: LibMCContext): PingResponse {
|
||||
val socket = _UdpSocket(host, port, context)
|
||||
val socket = UdpSocket(host, port, context)
|
||||
return try {
|
||||
val sendTime = Clock.System.now().toEpochMilliseconds()
|
||||
val requestPacket = BedrockRequestPacket(sendTime)
|
||||
|
||||
+2
@@ -88,6 +88,8 @@ internal class InternalPacketDispatcher(private val client: MinecraftClient) {
|
||||
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
|
||||
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.handshake.ServerboundHandshakePacket
|
||||
import cn.rtast.libmc.protocol.packet.login.ClientboundDisconnectLoginPacket
|
||||
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.login.*
|
||||
import cn.rtast.libmc.protocol.packet.play.*
|
||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry
|
||||
@@ -40,6 +36,7 @@ internal object GameProtocols {
|
||||
register(0x3d, ClientboundPingPlayPacket)
|
||||
register(0x41, ClientboundPlayerChatMessagePacket)
|
||||
register(0x76, ClientboundStartConfigurationPacket)
|
||||
register(0x79, ClientboundSystemChatMessagePacket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +55,9 @@ internal object GameProtocols {
|
||||
register(0x03, ServerboundLoginAcknowledgedPacket)
|
||||
}
|
||||
register(ProtocolState.PLAY) {
|
||||
register(0x00, ClientboundDelimiterPacket)
|
||||
register(0x01, ClientboundSpawnEntityPacket)
|
||||
register(0x02, ClientboundEntityAnimationPacket)
|
||||
register(0x09, ServerboundChatMessagePacket)
|
||||
register(0x2d, ServerboundPongPlayPacket)
|
||||
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 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) {
|
||||
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.packet.play.ClientboundDisconnectPlayPacket
|
||||
import cn.rtast.libmc.protocol.packet.play.ClientboundLoginPlayPacket
|
||||
import cn.rtast.libmc.protocol.packet.play.ClientboundSystemChatMessagePacket
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
@@ -27,6 +28,9 @@ class TestClient {
|
||||
cli.on<ClientboundDisconnectPlayPacket> {
|
||||
println(it)
|
||||
}
|
||||
cli.on<ClientboundSystemChatMessagePacket> {
|
||||
println(it)
|
||||
}
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user