Added some packets
This commit is contained in:
22 files changed
+326
-45
No files matched your search
+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