diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/primitives/Either.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/primitives/Either.kt new file mode 100644 index 0000000..da51236 --- /dev/null +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/primitives/Either.kt @@ -0,0 +1,44 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/7 + */ + + +package cn.rtast.libmc.primitives + +import cn.rtast.libmc.stream.BytesBuffer + +public sealed class Either { + public data class Left(val value: L) : Either() + public data class Right(val value: R) : Either() + + public val isLeft: Boolean get() = this is Left + public val isRight: Boolean get() = this is Right +} + +public suspend inline fun BytesBuffer.readEither( + readLeft: BytesBuffer.() -> L, + readRight: BytesBuffer.() -> R, +): Either { + val isLeft = readBoolean() + return if (isLeft) Either.Left(readLeft(this)) else Either.Right(readRight(this)) +} + +public suspend inline fun BytesBuffer.writeEither( + either: Either, + writeLeft: BytesBuffer.(L) -> Unit, + writeRight: BytesBuffer.(R) -> Unit, +) { + when (either) { + is Either.Left -> { + writeBoolean(true) + writeLeft(this, either.value) + } + + is Either.Right -> { + writeBoolean(false) + writeRight(this, either.value) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundWaypointPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundWaypointPacket.kt new file mode 100644 index 0000000..fadd1dc --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundWaypointPacket.kt @@ -0,0 +1,63 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/7 + */ + + +package cn.rtast.libmc.protocol.packet.play.clientbound + +import cn.rtast.libmc.packet.MinecraftPacket +import cn.rtast.libmc.packet.PacketCodec +import cn.rtast.libmc.primitives.* +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readIdentifier +import cn.rtast.libmc.protocol.protocol.game.world.waypoint.WaypointColor +import cn.rtast.libmc.protocol.protocol.game.world.waypoint.WaypointData +import cn.rtast.libmc.protocol.protocol.game.world.waypoint.WaypointOperation +import cn.rtast.libmc.stream.BytesBuffer +import kotlin.uuid.Uuid + +public data class ClientboundWaypointPacket( + val operation: WaypointOperation, + val identifier: Either, + val iconStyle: Identifier, + val waypointColor: WaypointColor?, + val waypointData: WaypointData, +) : MinecraftPacket { + internal companion object Codec : PacketCodec { + override suspend fun encode(buffer: BytesBuffer, value: ClientboundWaypointPacket) {} + override suspend fun decode(buffer: BytesBuffer): ClientboundWaypointPacket { + val operation = WaypointOperation.fromID(buffer.readVarInt()) + val identifier = buffer.readEither( + readLeft = { readUuid() }, + readRight = { readIdentifier() } + ) + val iconStyle = buffer.readIdentifier() + val color = buffer.readOptional { + val red = readUByte() + val green = readUByte() + val blue = readUByte() + WaypointColor(red, green, blue) + } + val data = when (WaypointData.Type.fromID(buffer.readVarInt())) { + WaypointData.Type.EMPTY -> WaypointData.Empty + WaypointData.Type.VEC3I -> { + val x = buffer.readVarInt() + val y = buffer.readVarInt() + val z = buffer.readVarInt() + WaypointData.Vec3i(x, y, z) + } + + WaypointData.Type.CHUNK -> { + val x = buffer.readVarInt() + val z = buffer.readVarInt() + WaypointData.Chunk(x, z) + } + + WaypointData.Type.AZIMUTH -> WaypointData.Azimuth(buffer.readFloat()) + } + return ClientboundWaypointPacket(operation, identifier, iconStyle, color, data) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GamePacketsProtocolCodec.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GamePacketsProtocolCodec.kt index 9706c3c..20266d8 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GamePacketsProtocolCodec.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GamePacketsProtocolCodec.kt @@ -198,7 +198,7 @@ internal object GamePacketsProtocolCodec { register(0x87, ClientboundProjectilePowerPacket) register(0x88, ClientboundCustomReportDetailsPacket) register(0x89, ClientboundServerLinksPacket) -// register(0x8a, ClientboundWaypointPacket) + register(0x8a, ClientboundWaypointPacket) register(0x8b, ClientboundClearDialogPacket) register(0x8c, ClientboundShowDialogPacket) } diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointColor.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointColor.kt new file mode 100644 index 0000000..79cd64f --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointColor.kt @@ -0,0 +1,10 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/7 + */ + + +package cn.rtast.libmc.protocol.protocol.game.world.waypoint + +public data class WaypointColor(val red: UByte, val green: UByte, val blue: UByte) \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointData.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointData.kt new file mode 100644 index 0000000..12da61b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointData.kt @@ -0,0 +1,39 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/7 + */ + + +package cn.rtast.libmc.protocol.protocol.game.world.waypoint + +public sealed class WaypointData { + public enum class Type(public val id: Int) { + EMPTY(0), + VEC3I(1), + CHUNK(2), + AZIMUTH(3); + + public companion object { + public fun fromID(id: Int): Type = entries.find { it.id == id } ?: EMPTY + } + } + + public abstract val type: Type + + public data object Empty : WaypointData() { + override val type: Type = Type.EMPTY + } + + public data class Vec3i(val x: Int, val y: Int, val z: Int) : WaypointData() { + override val type: Type = Type.VEC3I + } + + public data class Chunk(val x: Int, val z: Int) : WaypointData() { + override val type: Type = Type.CHUNK + } + + public data class Azimuth(val angle: Float) : WaypointData() { + override val type: Type = Type.AZIMUTH + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointOperation.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointOperation.kt new file mode 100644 index 0000000..b8b4ebf --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/world/waypoint/WaypointOperation.kt @@ -0,0 +1,18 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/7 + */ + + +package cn.rtast.libmc.protocol.protocol.game.world.waypoint + +public enum class WaypointOperation(public val id: Int) { + TRACK(0), + UNTRACK(1), + UPDATE(2); + + public companion object { + public fun fromID(id: Int): WaypointOperation = entries.find { it.id == id } ?: TRACK + } +} \ No newline at end of file