Implemented 0x12-ClientboundContainerSetContentPacket, 0x14-ClientboundContainerSetSlotPacket, implemented slot data codec

This commit is contained in:
2026-09-11 01:19:59 +08:00
parent 7c22cdd158
commit 59189d2fad
69 files changed
+3146 -67

No files matched your search

@@ -9,17 +9,8 @@ package cn.rtast.libmc.primitives
import cn.rtast.libmc.network.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
public inline fun <T> BytesBuffer.readOptional(block: BytesBuffer.() -> T): T? { public inline fun <T> BytesBuffer.readOptional(condition: Boolean, block: BytesBuffer.() -> T): T? =
val hasData = this.readBoolean() if (condition) block() else null
return if (hasData) block.invoke(this) else null
}
/** public inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit): Unit =
* buffer.writeOptional(value.someValue) { writeBlockPos(it) } if (value != null) block.invoke(this, value) else Unit
*/
public inline fun <T> BytesBuffer.writeOptional(value: T?, block: BytesBuffer.(T) -> Unit) {
if (value != null) {
this.writeBoolean(true)
block.invoke(this, value)
} else this.writeBoolean(false)
}
@@ -44,10 +44,7 @@ public fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> { public inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
val count = this.readVarInt() val count = this.readVarInt()
require(count in 0..4096) { require(count in 0..4096)
"Prefixed array count $count is invalid (expected 0..4096). " +
"Stream offset is corrupted. Check Heightmaps/NBT encoding."
}
val list = ArrayList<T>(count) val list = ArrayList<T>(count)
repeat(count) { _ -> list.add(this.reader()) } repeat(count) { _ -> list.add(this.reader()) }
return list return list
@@ -80,6 +80,7 @@ public fun NBTInput.readNetworkCompound(): NBTCompound {
return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) { return when (val type = NBTType.fromID(readByte().toInt() and 0xFF)) {
NBTType.Compound -> NBTCompound("", readCompoundTag()) NBTType.Compound -> NBTCompound("", readCompoundTag())
NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag())))) NBTType.String -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf("text" to NBTTag.StringTag(readStringTag()))))
NBTType.End -> NBTCompound("", NBTTag.CompoundTag(linkedMapOf()))
else -> throw UnsupportedOperationException("Unsupported network nbt tag 0x${type.id.toString(16).uppercase()}") else -> throw UnsupportedOperationException("Unsupported network nbt tag 0x${type.id.toString(16).uppercase()}")
} }
} }
@@ -7,16 +7,28 @@
package cn.rtast.libmc.protocol.client package cn.rtast.libmc.protocol.client
import cn.rtast.libmc.protocol.protocol.session.Session
import cn.rtast.libmc.protocol.protocol.session.SessionEvent
import cn.rtast.libmc.protocol.protocol.state.ProtocolState import cn.rtast.libmc.protocol.protocol.state.ProtocolState
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
internal class ClientStateMachine { internal class ClientStateMachine(private val session: Session) {
@Volatile @Volatile
var currentState: ProtocolState = ProtocolState.HANDSHAKE var currentState: ProtocolState = ProtocolState.HANDSHAKE
private set private set
fun transitionTo(newState: ProtocolState) { suspend fun transitionTo(newState: ProtocolState) {
println("Changing State $currentState to $newState") println("Changing State $currentState to $newState")
currentState = newState currentState = newState
session.emitEvent(
when (newState) {
ProtocolState.HANDSHAKE -> SessionEvent.ChangedState.HANDSHAKE
ProtocolState.LOGIN -> SessionEvent.ChangedState.LOGIN
ProtocolState.CONFIGURATION -> SessionEvent.ChangedState.CONFIGURATION
ProtocolState.PLAY -> SessionEvent.ChangedState.PLAY
ProtocolState.STATUS -> SessionEvent.ChangedState.STATUS
ProtocolState.DISCONNECTED -> SessionEvent.ChangedState.DISCONNECTED
}
)
} }
} }
@@ -30,7 +30,7 @@ public class MinecraftClient internal constructor(
internal val protocolContext: ProtocolContext, internal val protocolContext: ProtocolContext,
public val session: SessionImpl = SessionImpl(), public val session: SessionImpl = SessionImpl(),
) : PacketEventDispatcher(), CoroutineScope, Session by session { ) : PacketEventDispatcher(), CoroutineScope, Session by session {
internal val stateMachine = ClientStateMachine() internal val stateMachine = ClientStateMachine(session)
public val networkChannel: NetworkChannel = NetworkChannel(this) public val networkChannel: NetworkChannel = NetworkChannel(this)
private val clientJob = SupervisorJob(parentJob) private val clientJob = SupervisorJob(parentJob)
private var listenJob: Job? = null private var listenJob: Job? = null
@@ -52,8 +52,9 @@ public class MinecraftClient internal constructor(
try { try {
while (isActive) networkChannel.readNextPacket() while (isActive) networkChannel.readNextPacket()
} catch (e: Throwable) { } catch (e: Throwable) {
if (e is CancellationException) return@launch e.printStackTrace()
println("Network read loop exception: ${e.message}") println("Network read loop exception: ${e.message}")
if (e is CancellationException) return@launch
} finally { } finally {
networkChannel.close() networkChannel.close()
} }
@@ -14,8 +14,8 @@ import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.client.MinecraftClient import cn.rtast.libmc.protocol.client.MinecraftClient
import cn.rtast.libmc.protocol.protocol.event.PacketEventDispatcher import cn.rtast.libmc.protocol.protocol.event.PacketEventDispatcher
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.clientboundGameProtocols import cn.rtast.libmc.protocol.registry.GamePacketsProtocolRegistry.clientboundGameProtocols
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.serverboundGameProtocols import cn.rtast.libmc.protocol.registry.GamePacketsProtocolRegistry.serverboundGameProtocols
import cn.rtast.libmc.zlibCompress import cn.rtast.libmc.zlibCompress
import cn.rtast.libmc.zlibDecompress import cn.rtast.libmc.zlibDecompress
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
@@ -0,0 +1,34 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readPrefixed
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot
import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot
public data class ClientboundContainerSetContentPacket(
val windowId: Int,
val stateId: Int,
val slotData: List<Slot>,
val carriedItem: Slot?,
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundContainerSetContentPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundContainerSetContentPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundContainerSetContentPacket {
val windowId = buffer.readVarInt()
val stateId = buffer.readVarInt()
val slotData = buffer.readPrefixed { readSlot() }
// val carriedItem = buffer.readSlot() // TODO FIX ME
return ClientboundContainerSetContentPacket(windowId, stateId, slotData, null)
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/11
*/
package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot
import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot
public data class ClientboundContainerSetSlotPacket(
val windowId: Int,
val stateId: Int,
val slot: Short,
val slotData: Slot,
) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundContainerSetSlotPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundContainerSetSlotPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundContainerSetSlotPacket {
val windowId = buffer.readVarInt()
val stateId = buffer.readVarInt()
val slot = buffer.readShort()
val slotData = buffer.readSlot()
return ClientboundContainerSetSlotPacket(windowId, stateId, slot, slotData)
}
}
}
@@ -18,7 +18,7 @@ public data class ClientboundDeleteChatPacket(val messageId: Int, val signature:
override fun encode(buffer: BytesBuffer, value: ClientboundDeleteChatPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundDeleteChatPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundDeleteChatPacket { override fun decode(buffer: BytesBuffer): ClientboundDeleteChatPacket {
val messageId = buffer.readVarInt() val messageId = buffer.readVarInt()
val signature = buffer.readOptional { buffer.readBytes(256) } val signature = buffer.readOptional(messageId == 0) { buffer.readBytes(256) }
return ClientboundDeleteChatPacket(messageId, signature) return ClientboundDeleteChatPacket(messageId, signature)
} }
} }
@@ -26,12 +26,9 @@ public data class ClientboundDeleteChatPacket(val messageId: Int, val signature:
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || this::class != other::class) return false if (other == null || this::class != other::class) return false
other as ClientboundDeleteChatPacket other as ClientboundDeleteChatPacket
if (messageId != other.messageId) return false if (messageId != other.messageId) return false
if (!signature.contentEquals(other.signature)) return false if (!signature.contentEquals(other.signature)) return false
return true return true
} }
@@ -7,16 +7,16 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.IdOrX import cn.rtast.libmc.primitives.IdOrX
import cn.rtast.libmc.primitives.readIdOrX import cn.rtast.libmc.primitives.readIdOrX
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.protocol.protocol.game.chat.InlineChatType import cn.rtast.libmc.protocol.protocol.game.chat.InlineChatType
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readInlineChatType import cn.rtast.libmc.protocol.protocol.game.chat.readInlineChatType
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundDisguisedChatMessagePacket( public data class ClientboundDisguisedChatMessagePacket(
val message: TextComponent, val message: TextComponent,
@@ -30,7 +30,7 @@ public data class ClientboundDisguisedChatMessagePacket(
val message = buffer.readTextComponent() val message = buffer.readTextComponent()
val chatType = buffer.readIdOrX { readInlineChatType() } val chatType = buffer.readIdOrX { readInlineChatType() }
val senderName = buffer.readTextComponent() val senderName = buffer.readTextComponent()
val targetName = buffer.readOptional { readTextComponent() } val targetName = buffer.readPrefixOptional { readTextComponent() }
return ClientboundDisguisedChatMessagePacket(message, chatType, senderName, targetName) return ClientboundDisguisedChatMessagePacket(message, chatType, senderName, targetName)
} }
} }
@@ -7,9 +7,11 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readOptional
import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.readPrefixed import cn.rtast.libmc.primitives.readPrefixed
import cn.rtast.libmc.primitives.readPrefixedByteArray import cn.rtast.libmc.primitives.readPrefixedByteArray
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
@@ -17,7 +19,6 @@ import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.protocol.protocol.game.item.MapItemColorPatch import cn.rtast.libmc.protocol.protocol.game.item.MapItemColorPatch
import cn.rtast.libmc.protocol.protocol.game.item.MapItemIcon import cn.rtast.libmc.protocol.protocol.game.item.MapItemIcon
import cn.rtast.libmc.protocol.protocol.game.item.MapItemIconType import cn.rtast.libmc.protocol.protocol.game.item.MapItemIconType
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundMapItemDataPacket( public data class ClientboundMapItemDataPacket(
val mapId: Int, val mapId: Int,
@@ -32,24 +33,24 @@ public data class ClientboundMapItemDataPacket(
val mapId = buffer.readVarInt() val mapId = buffer.readVarInt()
val scale = buffer.readByte() val scale = buffer.readByte()
val locked = buffer.readBoolean() val locked = buffer.readBoolean()
val icons = buffer.readOptional { val icons = buffer.readPrefixOptional {
readPrefixed { readPrefixed {
val type = MapItemIconType.fromID(readVarInt()) val type = MapItemIconType.fromID(readVarInt())
val x = readByte() val x = readByte()
val z = readByte() val z = readByte()
val direction = readByte() val direction = readByte()
val displayName = readOptional { readTextComponent() } val displayName = readPrefixOptional { readTextComponent() }
MapItemIcon(type, x, z, direction, displayName) MapItemIcon(type, x, z, direction, displayName)
} }
} }
val columns = buffer.readUByte() val columns = buffer.readUByte()
val colorPatch = if (columns > 0u) { val colorPatch = buffer.readOptional(columns > 0u) {
val rows = buffer.readUByte() val rows = buffer.readUByte()
val xOffset = buffer.readUByte() val xOffset = buffer.readUByte()
val zOffset = buffer.readUByte() val zOffset = buffer.readUByte()
val data = buffer.readPrefixedByteArray() val data = buffer.readPrefixedByteArray()
MapItemColorPatch(columns, rows, xOffset, zOffset, data) MapItemColorPatch(columns, rows, xOffset, zOffset, data)
} else null }
return ClientboundMapItemDataPacket(mapId, scale, locked, icons, colorPatch) return ClientboundMapItemDataPacket(mapId, scale, locked, icons, colorPatch)
} }
} }
@@ -43,7 +43,7 @@ public data class ClientboundPlayerInfoUpdatePacket(
} }
PlayerUpdateInfoAction.INITIALIZE_CHAT -> { PlayerUpdateInfoAction.INITIALIZE_CHAT -> {
buffer.readOptional { buffer.readPrefixOptional {
SinglePlayerAction.InitializeChat( SinglePlayerAction.InitializeChat(
readUuid(), readLong(), readUuid(), readLong(),
readBytes(512), readBytes(4096) readBytes(512), readBytes(4096)
@@ -7,19 +7,18 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.readPrefixed
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundResetScorePacket(val entityName: String, val objectiveName: String?) : MinecraftPacket { public data class ClientboundResetScorePacket(val entityName: String, val objectiveName: String?) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundResetScorePacket> { internal companion object Codec : PacketCodec<ClientboundResetScorePacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundResetScorePacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundResetScorePacket) {}
override fun decode(buffer: BytesBuffer): ClientboundResetScorePacket { override fun decode(buffer: BytesBuffer): ClientboundResetScorePacket {
val entityName = buffer.readMcString() val entityName = buffer.readMcString()
val objectiveName = buffer.readPrefixed { readOptional { readMcString() } }.first() // ? val objectiveName = buffer.readPrefixOptional { readMcString() }
return ClientboundResetScorePacket(entityName, objectiveName) return ClientboundResetScorePacket(entityName, objectiveName)
} }
} }
@@ -8,8 +8,8 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readOptional
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.GameMode import cn.rtast.libmc.protocol.protocol.game.GameMode
@@ -45,8 +45,8 @@ public data class ClientboundRespawnPacket(
val isDebug = buffer.readBoolean() val isDebug = buffer.readBoolean()
val isFlat = buffer.readBoolean() val isFlat = buffer.readBoolean()
val hasDeathLocation = buffer.readBoolean() val hasDeathLocation = buffer.readBoolean()
val deathDimensionName = buffer.readOptional { readIdentifier() } val deathDimensionName = buffer.readOptional(hasDeathLocation) { readIdentifier() }
val deathLocation = buffer.readOptional { readBlockPos() } val deathLocation = buffer.readOptional(hasDeathLocation) { readBlockPos() }
val portalCooldown = buffer.readVarInt() val portalCooldown = buffer.readVarInt()
val seaLevel = buffer.readVarInt() val seaLevel = buffer.readVarInt()
val dataKept = RespawnDataToKeep.fromByte(buffer.readByte()) val dataKept = RespawnDataToKeep.fromByte(buffer.readByte())
@@ -8,9 +8,9 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.protocol.protocol.game.Identifier import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.readIdentifier import cn.rtast.libmc.protocol.protocol.game.readIdentifier
@@ -18,7 +18,7 @@ public data class ClientboundSelectAdvancementsTabPacket(val tabId: Identifier?)
internal companion object Codec : PacketCodec<ClientboundSelectAdvancementsTabPacket> { internal companion object Codec : PacketCodec<ClientboundSelectAdvancementsTabPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundSelectAdvancementsTabPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundSelectAdvancementsTabPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundSelectAdvancementsTabPacket { override fun decode(buffer: BytesBuffer): ClientboundSelectAdvancementsTabPacket {
return ClientboundSelectAdvancementsTabPacket(buffer.readOptional { readIdentifier() }) return ClientboundSelectAdvancementsTabPacket(buffer.readPrefixOptional { readIdentifier() })
} }
} }
} }
@@ -7,20 +7,20 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundServerDataPacket(val motd: TextComponent, val icon: ByteArray?) : MinecraftPacket { public data class ClientboundServerDataPacket(val motd: TextComponent, val icon: ByteArray?) : MinecraftPacket {
internal companion object Codec : PacketCodec<ClientboundServerDataPacket> { internal companion object Codec : PacketCodec<ClientboundServerDataPacket> {
override fun encode(buffer: BytesBuffer, value: ClientboundServerDataPacket) {} override fun encode(buffer: BytesBuffer, value: ClientboundServerDataPacket) {}
override fun decode(buffer: BytesBuffer): ClientboundServerDataPacket { override fun decode(buffer: BytesBuffer): ClientboundServerDataPacket {
val motd = buffer.readTextComponent() val motd = buffer.readTextComponent()
val icon = buffer.readOptional { val length = readVarInt(); readBytes(length) } val icon = buffer.readPrefixOptional { val length = readVarInt(); readBytes(length) }
return ClientboundServerDataPacket(motd, icon) return ClientboundServerDataPacket(motd, icon)
} }
} }
@@ -7,12 +7,12 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readOptional
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundTestInstanceBlockStatusPacket( public data class ClientboundTestInstanceBlockStatusPacket(
val status: TextComponent, val status: TextComponent,
@@ -26,9 +26,9 @@ public data class ClientboundTestInstanceBlockStatusPacket(
override fun decode(buffer: BytesBuffer): ClientboundTestInstanceBlockStatusPacket { override fun decode(buffer: BytesBuffer): ClientboundTestInstanceBlockStatusPacket {
val status = buffer.readTextComponent() val status = buffer.readTextComponent()
val hasSize = buffer.readBoolean() val hasSize = buffer.readBoolean()
val sizeX = buffer.readOptional { readDouble() } // ? val sizeX = buffer.readOptional(hasSize) { readDouble() }
val sizeY = buffer.readOptional { readDouble() } // ? val sizeY = buffer.readOptional(hasSize) { readDouble() }
val sizeZ = buffer.readOptional { readDouble() } // ? val sizeZ = buffer.readOptional(hasSize) { readDouble() }
return ClientboundTestInstanceBlockStatusPacket(status, hasSize, sizeX, sizeY, sizeZ) return ClientboundTestInstanceBlockStatusPacket(status, hasSize, sizeX, sizeY, sizeZ)
} }
} }
@@ -8,16 +8,16 @@
package cn.rtast.libmc.protocol.packet.play.clientbound package cn.rtast.libmc.protocol.packet.play.clientbound
import cn.rtast.libmc.nbt.NBTTag import cn.rtast.libmc.nbt.NBTTag
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.readMcString import cn.rtast.libmc.primitives.readMcString
import cn.rtast.libmc.primitives.readOptional import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.protocol.protocol.game.scoreboard.ScoreNumberFormat import cn.rtast.libmc.protocol.protocol.game.scoreboard.ScoreNumberFormat
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
import cn.rtast.libmc.network.BytesBuffer
public data class ClientboundUpdateScorePacket( public data class ClientboundUpdateScorePacket(
val entityName: String, val entityName: String,
@@ -32,8 +32,8 @@ public data class ClientboundUpdateScorePacket(
val entityName = buffer.readMcString() val entityName = buffer.readMcString()
val objectiveName = buffer.readMcString() val objectiveName = buffer.readMcString()
val value = buffer.readVarInt() val value = buffer.readVarInt()
val displayName = buffer.readOptional { readTextComponent() } val displayName = buffer.readPrefixOptional { readTextComponent() }
val numberFormat = buffer.readOptional { val numberFormat = buffer.readPrefixOptional {
when (val type = readVarInt()) { when (val type = readVarInt()) {
0 -> ScoreNumberFormat.Blank 0 -> ScoreNumberFormat.Blank
1 -> ScoreNumberFormat.Styled(styling = readNetworkNBTCompound().element as NBTTag.CompoundTag) // fix me 1 -> ScoreNumberFormat.Styled(styling = readNetworkNBTCompound().element as NBTTag.CompoundTag) // fix me
@@ -34,7 +34,7 @@ public data class ClientboundWaypointPacket(
readRight = { readIdentifier() } readRight = { readIdentifier() }
) )
val iconStyle = buffer.readIdentifier() val iconStyle = buffer.readIdentifier()
val color = buffer.readOptional { val color = buffer.readPrefixOptional {
val red = readUByte() val red = readUByte()
val green = readUByte() val green = readUByte()
val blue = readUByte() val blue = readUByte()
@@ -0,0 +1,10 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.block
public data class BlockStateProperty(val name: String, val value: String)
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.color
public enum class DyeColor(public val id: Int) {
WHITE(0),
ORANGE(1),
MAGENTA(2),
LIGHT_BLUE(3),
YELLOW(4),
LIME(5),
PINK(6),
GRAY(7),
LIGHT_GRAY(8),
CYAN(9),
PURPLE(10),
BLUE(11),
BROWN(12),
GREEN(13),
RED(14),
BLACK(15);
public companion object {
public fun fromID(id: Int): DyeColor = entries.first { it.id == id }
}
}
@@ -0,0 +1,18 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.protocol.protocol.game.Identifier
public data class AttributeModifierEntry(
val attributeId: Int,
val modifierId: Identifier,
val value: Double,
val operation: AttributeOperation,
val slot: AttributeSlot,
)
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class AttributeOperation(public val id: Int) {
ADD(0),
MULTIPLY_BASE(1),
MULTIPLY_TOTAL(2);
public companion object {
public fun fromID(id: Int): AttributeOperation = entries.first { it.id == id }
}
}
@@ -0,0 +1,24 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class AttributeSlot(public val id: Int) {
ANY(0),
MAINHAND(1),
OFFHAND(2),
HAND(3),
FEET(4),
LEGS(5),
CHEST(6),
HEAD(7),
ARMOR(8),
BODY(9);
public companion object {
public fun fromID(id: Int): AttributeSlot = entries.first { it.id == id }
}
}
@@ -0,0 +1,41 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.*
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.color.DyeColor
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class BannerPattern(val assetId: Identifier, val translationKey: String)
public data class BannerPatternLayer(val pattern: IdOrX<BannerPattern>, val color: DyeColor)
internal fun BytesBuffer.readBannerPatternLayer(): BannerPatternLayer {
val pattern = readIdOrX { readBannerPattern() }
val color = DyeColor.fromID(readVarInt())
return BannerPatternLayer(pattern, color)
}
internal fun BytesBuffer.writeBannerPatternLayer(layer: BannerPatternLayer) {
writeIdOrX(layer.pattern) { writeBannerPattern(it) }
writeVarInt(layer.color.id)
}
internal fun BytesBuffer.readBannerPattern(): BannerPattern {
val assetId = readIdentifier()
val translationKey = readMcString()
return BannerPattern(assetId, translationKey)
}
internal fun BytesBuffer.writeBannerPattern(pattern: BannerPattern) {
writeIdentifier(pattern.assetId)
writeMcString(pattern.translationKey)
}
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.nbt.NBTCompound
public data class BeeAttribute(
val entityType: Int,
val entityData: NBTCompound,
val ticksInHive: Int,
val minTicksInHive: Int,
)
@@ -0,0 +1,137 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.nbt.NBTCompound
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.*
import cn.rtast.libmc.protocol.protocol.game.data.component.DataComponent
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
import cn.rtast.libmc.protocol.protocol.util.writeNetworkNBTCompound
import cn.rtast.libmc.protocol.registry.readDataComponent
import cn.rtast.libmc.protocol.registry.writeDataComponent
public data class BlockPredicate(
val blocks: IdSet?,
val properties: List<PropertyMatcher>?,
val nbt: NBTCompound?,
val components: List<ExactDataComponentMatcher>,
val partialComponents: List<PartialDataComponentMatcher>,
)
public sealed interface PropertyMatcher {
public val name: String
public data class Exact(override val name: String, val value: String) :
PropertyMatcher
public data class Ranged(override val name: String, val minValue: String?, val maxValue: String?) :
PropertyMatcher
}
public data class ExactDataComponentMatcher(val typeId: Int, val value: DataComponent)
public enum class PartialComponentPredicateType(public val id: Int) {
DAMAGE(0),
ENCHANTMENTS(1),
STORED_ENCHANTMENTS(2),
POTION_CONTENTS(3),
CUSTOM_DATA(4),
CONTAINER(5),
BUNDLE_CONTENTS(6),
FIREWORK_EXPLOSION(7),
FIREWORKS(8),
WRITABLE_BOOK_CONTENT(9),
WRITTEN_BOOK_CONTENT(10),
ATTRIBUTE_MODIFIERS(11),
TRIM(12),
JUKEBOX_PLAYABLE(13);
public companion object {
public fun fromID(id: Int): PartialComponentPredicateType = entries.first { it.id == id }
}
}
public data class PartialDataComponentMatcher(val type: PartialComponentPredicateType, val nbt: NBTCompound)
internal fun BytesBuffer.readBlockPredicate(): BlockPredicate {
val blocks = readPrefixOptional { readIdSet() }
val properties = readPrefixOptional { readPrefixed { readPropertyMatcher() } }
val nbt = readPrefixOptional { readNetworkNBTCompound() }
val components = readPrefixed { readExactDataComponentMatcher() }
val partialComponents = readPrefixed { readPartialDataComponentMatcher() }
return BlockPredicate(
blocks = blocks,
properties = properties,
nbt = nbt,
components = components,
partialComponents = partialComponents
)
}
internal fun BytesBuffer.writeBlockPredicate(value: BlockPredicate) {
writePrefixedOptional(value.blocks) { writeIdSet(it) }
writePrefixedOptional(value.properties) { list ->
writePrefixed(list) { writePropertyMatcher(it) }
}
writePrefixedOptional(value.nbt) { writeNetworkNBTCompound(it) }
writePrefixed(value.components) { writeExactDataComponentMatcher(it) }
writePrefixed(value.partialComponents) { writePartialDataComponentMatcher(it) }
}
internal fun BytesBuffer.readPropertyMatcher(): PropertyMatcher {
val name = readMcString()
val isExactMatch = readBoolean()
return if (isExactMatch) {
PropertyMatcher.Exact(name, readMcString())
} else {
val minValue = readPrefixOptional { readMcString() }
val maxValue = readPrefixOptional { readMcString() }
PropertyMatcher.Ranged(name, minValue, maxValue)
}
}
internal fun BytesBuffer.writePropertyMatcher(value: PropertyMatcher) {
writeMcString(value.name)
when (value) {
is PropertyMatcher.Exact -> {
writeBoolean(true)
writeMcString(value.value)
}
is PropertyMatcher.Ranged -> {
writeBoolean(false)
writePrefixedOptional(value.minValue) { writeMcString(it) }
writePrefixedOptional(value.maxValue) { writeMcString(it) }
}
}
}
internal fun BytesBuffer.readExactDataComponentMatcher(): ExactDataComponentMatcher {
val typeId = readVarInt()
val component = readDataComponent(typeId)
return ExactDataComponentMatcher(typeId, component)
}
internal fun BytesBuffer.writeExactDataComponentMatcher(value: ExactDataComponentMatcher) {
writeVarInt(value.typeId)
writeDataComponent(value.value)
}
internal fun BytesBuffer.readPartialDataComponentMatcher(): PartialDataComponentMatcher {
val typeId = readVarInt()
val predicateNbt = readNetworkNBTCompound()
return PartialDataComponentMatcher(PartialComponentPredicateType.fromID(typeId), predicateNbt)
}
internal fun BytesBuffer.writePartialDataComponentMatcher(value: PartialDataComponentMatcher) {
writeVarInt(value.type.id)
writeNetworkNBTCompound(value.nbt)
}
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.primitives.IdSet
public data class DamageReduction(
val horizontalBlockingAngle: Float,
val type: IdSet?,
val base: Float,
val factor: Float,
)
@@ -0,0 +1,10 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public data class EnchantmentEntry(val typeId: Int, val level: Int)
@@ -0,0 +1,22 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class EquipmentSlot(public val id: Int) {
MAINHAND(0),
FEET(1),
LEGS(2),
CHEST(3),
HEAD(4),
OFFHAND(5),
BODY(6);
public companion object {
public fun fromID(id: Int): EquipmentSlot = entries.first { it.id == id }
}
}
@@ -0,0 +1,47 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readPrefixed
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writePrefixed
import cn.rtast.libmc.primitives.writeVarInt
public data class FireworkExplosionAttribute(
val shape: FireworkExplosionShape,
val colors: List<Int>,
val fadeColors: List<Int>,
val hasTrail: Boolean,
val hasTwinkle: Boolean,
)
public enum class FireworkExplosionShape(public val id: Int) {
SmallBall(0), LargeBall(1), Star(2), Creeper(3), Burst(4);
public companion object {
public fun fromID(id: Int): FireworkExplosionShape = entries.first { it.id == id }
}
}
internal fun BytesBuffer.readFireworkExplosion(): FireworkExplosionAttribute {
val shape = FireworkExplosionShape.Companion.fromID(readVarInt())
val colors = readPrefixed { readInt() }
val fadeColors = readPrefixed { readInt() }
val hasTrail = readBoolean()
val hasTwinkle = readBoolean()
return FireworkExplosionAttribute(shape, colors, fadeColors, hasTrail, hasTwinkle)
}
internal fun BytesBuffer.writeFireworkExplosion(explosion: FireworkExplosionAttribute) {
writeVarInt(explosion.shape.id)
writePrefixed(explosion.colors) { writeInt(it) }
writePrefixed(explosion.fadeColors) { writeInt(it) }
writeBoolean(explosion.hasTrail)
writeBoolean(explosion.hasTwinkle)
}
@@ -0,0 +1,19 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.primitives.IdOrX
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.sound.SoundEvent
public data class InstrumentAttribute(
val sound: IdOrX<SoundEvent>,
val useDuration: Float,
val range: Float,
val description: TextComponent,
)
@@ -0,0 +1,19 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class ItemRarity(public val id: Int) {
COMMON(0),
UNCOMMON(1),
RARE(2),
EPIC(3);
public companion object {
public fun fromID(id: Int): ItemRarity = entries.first { it.id == id }
}
}
@@ -0,0 +1,19 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.primitives.IdOrX
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.sound.SoundEvent
public data class JukeboxSong(
val sound: IdOrX<SoundEvent>,
val description: TextComponent,
val duration: Float,
val output: Int,
)
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt
public data class KineticWeaponCondition(
val maxDurationTicks: Int,
val minSpeed: Float,
val minRelativeSpeed: Float,
)
internal fun BytesBuffer.readKineticWeaponCondition(): KineticWeaponCondition {
val duration = readVarInt()
val minSpeed = readFloat()
val minRelativeSpeed = readFloat()
return KineticWeaponCondition(duration, minSpeed, minRelativeSpeed)
}
internal fun BytesBuffer.writeKineticWeaponCondition(condition: KineticWeaponCondition) {
writeVarInt(condition.maxDurationTicks)
writeFloat(condition.minSpeed)
writeFloat(condition.minRelativeSpeed)
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class MapPostProcessingType(public val id: Int) {
LOCK(0), SCALE(1);
public companion object {
public fun fromID(id: Int): MapPostProcessingType = entries.first { it.id == id }
}
}
@@ -0,0 +1,10 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public data class StoredEnchantment(val enchantmentId: Int, val level: Int)
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public enum class SwingAnimationType(public val id: Int) {
NONE(0), WHACK(1), STAB(2);
public companion object {
public fun fromID(id: Int): SwingAnimationType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.primitives.IdSet
public data class ToolRule(
val blocks: IdSet,
val speed: Float?,
val correctForDrops: Boolean?,
)
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
public data class TrimMaterial(
val suffix: String,
val overrides: List<TrimMaterialOverride>,
val description: TextComponent,
)
@@ -0,0 +1,12 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.protocol.protocol.game.Identifier
public data class TrimMaterialOverride(val armorMaterialType: Identifier, val overriddenAssetName: String)
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
public data class TrimPattern(
val assetName: String,
val templateItem: Int,
val description: TextComponent,
val decal: Boolean,
)
@@ -0,0 +1,10 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
public data class WritablePage(val rawContent: String, val filteredContent: String?)
@@ -0,0 +1,12 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.data.component.attributes
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
public data class WrittenPage(val rawContent: TextComponent, val filteredContent: TextComponent?)
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class AxolotlVariantType(public val id: Int) {
LUCY(0), WILD(1), GOLD(2), CYAN(3), BLUE(4);
public companion object {
public fun fromID(id: Int): AxolotlVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class FoxVariantType(public val id: Int) {
RED(0), SNOW(1);
public companion object {
public fun fromID(id: Int): FoxVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class HorseVariantType(public val id: Int) {
WHITE(0), CREAMY(1), CHESTNUT(2), BROWN(3), BLACK(4), GRAY(5), DARK_BROWN(6);
public companion object {
public fun fromID(id: Int): HorseVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class LlamaVariantType(public val id: Int) {
CREAMY(0), WHITE(1), BROWN(2), GRAY(3);
public companion object {
public fun fromID(id: Int): LlamaVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class MooshroomVariantType(public val id: Int) {
RED(0), BROWN(1);
public companion object {
public fun fromID(id: Int): MooshroomVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class ParrotVariantType(public val id: Int) {
RED_BLUE(0), BLUE(1), GREEN(2), YELLOW_BLUE(3), GRAY(4);
public companion object {
public fun fromID(id: Int): ParrotVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class RabbitVariantType(public val id: Int) {
BROWN(0), WHITE(1), BLACK(2), WHITE_SPLOTCHED(3),
GOLD(4), SALT(5), EVIL(6);
public companion object {
public fun fromID(id: Int): RabbitVariantType = entries.first { it.id == id }
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class SalmonSizeType(public val id: Int) {
SMALL(0), MEDIUM(1), LARGE(2);
public companion object {
public fun fromID(id: Int): SalmonSizeType = entries.first { it.id == id }
}
}
@@ -0,0 +1,27 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.entity
public enum class TropicalFishPatternType(public val id: Int) {
KOB(0),
SUNSTREAK(1),
SNOOPER(2),
DASHER(3),
BRINELY(4),
SPOTTY(5),
FLOPPER(6),
STRIPEY(7),
GLITTER(8),
BLOCKFISH(9),
BETTY(10),
CLAYFISH(11);
public companion object {
public fun fromID(id: Int): TropicalFishPatternType = entries.first { it.id == id }
}
}
@@ -0,0 +1,25 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.item
public enum class ItemUseAnimation(public val id: Int) {
NONE(0),
EAT(1),
DRINK(2),
BLOCK(3),
BOW(4),
SPEAR(5),
CROSSBOW(6),
SPYGLASS(7),
TOOT_HORN(8),
BRUSH(9);
public companion object {
public fun fromID(id: Int): ItemUseAnimation = entries.first { it.id == id }
}
}
@@ -0,0 +1,43 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.item
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.writePrefixedOptional
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
import cn.rtast.libmc.protocol.protocol.game.chat.writeTextComponent
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
public data class PaintingVariantType(
val width: Int,
val height: Int,
val assetId: Identifier,
val title: TextComponent?,
val author: TextComponent?,
)
internal fun BytesBuffer.readPaintingVariantType(): PaintingVariantType {
val width = readInt()
val height = readInt()
val assetId = readIdentifier()
val title = readPrefixOptional { readTextComponent() }
val author = readPrefixOptional { readTextComponent() }
return PaintingVariantType(width, height, assetId, title, author)
}
internal fun BytesBuffer.writePaintingVariantType(variant: PaintingVariantType) {
writeInt(variant.width)
writeInt(variant.height)
writeIdentifier(variant.assetId)
writePrefixedOptional(variant.title) { writeTextComponent(it) }
writePrefixedOptional(variant.author) { writeTextComponent(it) }
}
@@ -0,0 +1,9 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.item.slot
@@ -0,0 +1,59 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.item.slot
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.data.component.DataComponent
import cn.rtast.libmc.protocol.registry.readDataComponent
import cn.rtast.libmc.protocol.registry.writeDataComponent
public typealias ItemStack = Slot
public data class Slot(
val count: Int,
val itemId: Int?,
val componentsToAdd: List<DataComponent>,
val componentsToRemove: List<Int>,
) {
val isEmpty: Boolean get() = count <= 0
public companion object {
public val EMPTY: Slot = Slot(0, null, emptyList(), emptyList())
}
}
internal fun BytesBuffer.readSlot(): Slot {
val count = readVarInt()
if (count <= 0) return Slot.EMPTY
val itemId = readVarInt()
val addCount = readVarInt()
val removeCount = readVarInt()
val componentsToAdd = ArrayList<DataComponent>(addCount)
repeat(addCount) {
val componentId = readVarInt()
componentsToAdd.add(readDataComponent(componentId))
}
val componentsToRemove = ArrayList<Int>(removeCount)
repeat(removeCount) { componentsToRemove.add(readVarInt()) }
return Slot(count, itemId, componentsToAdd, componentsToRemove)
}
internal fun BytesBuffer.writeSlot(slot: Slot) {
if (slot.isEmpty || slot.itemId == null) {
writeVarInt(0)
return
}
writeVarInt(slot.count)
writeVarInt(slot.itemId)
writeVarInt(slot.componentsToAdd.size)
writeVarInt(slot.componentsToRemove.size)
for (component in slot.componentsToAdd) writeDataComponent(component)
for (typeId in slot.componentsToRemove) writeVarInt(typeId)
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.player.skin
public enum class TextureModelType(public val id: Int) {
WIDE(0), SLIM(1);
public companion object {
public fun fromID(id: Int): TextureModelType = entries.first { it.id == id }
}
}
@@ -0,0 +1,90 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.potion
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.IdSet
import cn.rtast.libmc.primitives.readIdSet
import cn.rtast.libmc.primitives.readPrefixed
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeIdSet
import cn.rtast.libmc.primitives.writePrefixed
import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.sound.SoundEvent
import cn.rtast.libmc.protocol.protocol.game.sound.readSoundEvent
import cn.rtast.libmc.protocol.protocol.game.sound.writeSoundEvent
public sealed interface ConsumeEffect {
public data class ApplyEffects(val effects: List<PotionEffect>, val probability: Float) : ConsumeEffect
public data class RemoveEffects(val effects: IdSet) : ConsumeEffect
public data object ClearAllEffects : ConsumeEffect
public data class TeleportRandomly(val diameter: Float) : ConsumeEffect
public data class PlaySound(val sound: SoundEvent) : ConsumeEffect
}
internal fun BytesBuffer.readConsumeEffects(): List<ConsumeEffect> {
return readPrefixed {
when (val typeId = readVarInt()) {
0 -> {
val effects = readPrefixed { readPotionEffect() }
val probability = readFloat()
ConsumeEffect.ApplyEffects(effects, probability)
}
1 -> {
val effects = readIdSet()
ConsumeEffect.RemoveEffects(effects)
}
2 -> ConsumeEffect.ClearAllEffects
3 -> {
val diameter = readFloat()
ConsumeEffect.TeleportRandomly(diameter)
}
4 -> {
val sound = readSoundEvent()
ConsumeEffect.PlaySound(sound)
}
else -> throw IllegalArgumentException("Unknown ConsumeEffect type ID: $typeId")
}
}
}
internal fun BytesBuffer.writeConsumeEffects(effects: List<ConsumeEffect>) {
writePrefixed(effects) { effect ->
when (effect) {
is ConsumeEffect.ApplyEffects -> {
writeVarInt(0)
writePrefixed(effect.effects) { writePotionEffect(it) }
writeFloat(effect.probability)
}
is ConsumeEffect.RemoveEffects -> {
writeVarInt(1)
writeIdSet(effect.effects)
}
is ConsumeEffect.ClearAllEffects -> {
writeVarInt(2)
}
is ConsumeEffect.TeleportRandomly -> {
writeVarInt(3)
writeFloat(effect.diameter)
}
is ConsumeEffect.PlaySound -> {
writeVarInt(4)
writeSoundEvent(effect.sound)
}
}
}
}
@@ -0,0 +1,45 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.potion
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readPrefixOptional
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writePrefixedOptional
import cn.rtast.libmc.primitives.writeVarInt
public data class PotionEffect(
val effectId: Int,
val amplifier: Int,
val duration: Int,
val ambient: Boolean,
val showParticles: Boolean,
val showIcon: Boolean,
val hideEffect: PotionEffect?,
)
internal fun BytesBuffer.readPotionEffect(): PotionEffect {
val effectId = readVarInt()
val amplifier = readVarInt()
val duration = readVarInt()
val ambient = readBoolean()
val showParticles = readBoolean()
val showIcon = readBoolean()
val hideEffect = readPrefixOptional { readPotionEffect() }
return PotionEffect(effectId, amplifier, duration, ambient, showParticles, showIcon, hideEffect)
}
internal fun BytesBuffer.writePotionEffect(effect: PotionEffect) {
writeVarInt(effect.effectId)
writeVarInt(effect.amplifier)
writeVarInt(effect.duration)
writeBoolean(effect.ambient)
writeBoolean(effect.showParticles)
writeBoolean(effect.showIcon)
writePrefixedOptional(effect.hideEffect) { writePotionEffect(it) }
}
@@ -0,0 +1,28 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.potion
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt
public data class SuspiciousStewEffect(val effectId: Int, val duration: Int)
internal fun BytesBuffer.readSuspiciousStewEffect(): SuspiciousStewEffect {
val effectId = readVarInt()
val duration = readVarInt()
return SuspiciousStewEffect(
effectId = effectId,
duration = duration
)
}
internal fun BytesBuffer.writeSuspiciousStewEffect(effect: SuspiciousStewEffect) {
writeVarInt(effect.effectId)
writeVarInt(effect.duration)
}
@@ -0,0 +1,69 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.session
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.primitives.*
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.player.skin.TextureModelType
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
import kotlin.uuid.Uuid
public data class ResolvableProfile(
val profile: UnpackedProfile,
val body: Identifier?,
val cape: Identifier?,
val elytra: Identifier?,
val model: TextureModelType?,
) {
public sealed interface UnpackedProfile {
public data class PartialProfile(
val username: String?,
val uuid: Uuid?,
val properties: List<GameProfile.Property>,
) : UnpackedProfile
public data class CompleteProfile(val profile: GameProfile) : UnpackedProfile
}
}
internal fun BytesBuffer.readResolvableProfile(): ResolvableProfile {
val kind = readVarInt()
val profile = if (kind == 0) {
val username = readPrefixOptional { readMcString() }
val uuid = readPrefixOptional { readUuid() }
val properties = readPrefixed { GameProfile.Property.decode(this) }
ResolvableProfile.UnpackedProfile.PartialProfile(username, uuid, properties)
} else ResolvableProfile.UnpackedProfile.CompleteProfile(GameProfile.decode(this))
val body = readPrefixOptional { readIdentifier() }
val cape = readPrefixOptional { readIdentifier() }
val elytra = readPrefixOptional { readIdentifier() }
val model = readPrefixOptional { TextureModelType.fromID(readVarInt()) }
return ResolvableProfile(profile, body, cape, elytra, model)
}
internal fun BytesBuffer.writeResolvableProfile(profile: ResolvableProfile) {
when (profile.profile) {
is ResolvableProfile.UnpackedProfile.CompleteProfile -> {
writeVarInt(1)
GameProfile.encode(this, profile.profile.profile)
}
is ResolvableProfile.UnpackedProfile.PartialProfile -> {
writeVarInt(0)
writePrefixedOptional(profile.profile.username) { writeMcString(it) }
writePrefixedOptional(profile.profile.uuid) { writeUuid(it) }
writePrefixed(profile.profile.properties) { GameProfile.Property.encode(this, it) }
}
}
writePrefixedOptional(profile.body) { writeIdentifier(it) }
writePrefixedOptional(profile.cape) { writeIdentifier(it) }
writePrefixedOptional(profile.elytra) { writeIdentifier(it) }
writePrefixedOptional(profile.model) { writeVarInt(it.id) }
}
@@ -19,7 +19,7 @@ public data class SoundEvent(val name: Identifier, val hasFixedRange: Boolean, v
internal fun BytesBuffer.readSoundEvent(): SoundEvent { internal fun BytesBuffer.readSoundEvent(): SoundEvent {
val name = readIdentifier() val name = readIdentifier()
val hasFixedRange = readBoolean() val hasFixedRange = readBoolean()
val fixedRange = readOptional { readFloat() } val fixedRange = readOptional(hasFixedRange) { readFloat() }
return SoundEvent(name, hasFixedRange, fixedRange) return SoundEvent(name, hasFixedRange, fixedRange)
} }
@@ -0,0 +1,13 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.protocol.game.world
import cn.rtast.libmc.protocol.protocol.game.Identifier
import cn.rtast.libmc.protocol.protocol.game.block.BlockPos
public data class GlobalPos(val dimension: Identifier, val pos: BlockPos)
@@ -13,4 +13,12 @@ import cn.rtast.libmc.protocol.protocol.state.ProtocolState
public sealed interface SessionEvent { public sealed interface SessionEvent {
public data class DisconnectedEvent(val reason: TextComponent, val state: ProtocolState) : SessionEvent public data class DisconnectedEvent(val reason: TextComponent, val state: ProtocolState) : SessionEvent
public object ConnectedEvent : SessionEvent public object ConnectedEvent : SessionEvent
public sealed interface ChangedState : SessionEvent {
public data object STATUS : ChangedState
public data object HANDSHAKE : ChangedState
public data object LOGIN : ChangedState
public data object CONFIGURATION : ChangedState
public data object PLAY : ChangedState
public data object DISCONNECTED : ChangedState
}
} }
@@ -0,0 +1,171 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/10
*/
package cn.rtast.libmc.protocol.registry
import cn.rtast.libmc.network.BytesBuffer
import cn.rtast.libmc.packet.PacketCodec
import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.protocol.game.data.component.DataComponent
import kotlin.reflect.KClass
internal object DataComponentRegistry {
private val codecMap = mutableMapOf<Int, PacketCodec<out DataComponent>>()
private val classToIdMap = mutableMapOf<KClass<out DataComponent>, Int>()
private lateinit var codecArray: Array<PacketCodec<DataComponent>>
private fun <T : DataComponent> register(kClass: KClass<T>, codec: PacketCodec<T>) {
val id = codecMap.size
codecMap[id] = codec
classToIdMap[kClass] = id
}
inline fun <reified T : DataComponent> register(codec: PacketCodec<T>) = register(T::class, codec)
fun freeze() {
val maxId = codecMap.keys.maxOrNull() ?: -1
codecArray = Array(maxId + 1) { index ->
@Suppress("UNCHECKED_CAST")
codecMap[index] as? PacketCodec<DataComponent> ?: error("Missing component codec for $index")
}
codecMap.clear()
}
internal fun read(typeId: Int, buffer: BytesBuffer): DataComponent {
val codec = codecArray.getOrNull(typeId) ?: error("Unknown DataComponent ID: $typeId")
return codec.decode(buffer)
}
internal fun write(buffer: BytesBuffer, component: DataComponent) {
val typeId = classToIdMap[component::class] ?: error("Unregistered DataComponent class: ${component::class}")
val codec = codecArray[typeId]
buffer.writeVarInt(typeId)
codec.encode(buffer, component)
}
init {
register(DataComponent.CustomDataComponent)
register(DataComponent.MaxStackSizeComponent)
register(DataComponent.MaxDamageComponent)
register(DataComponent.DamageComponent)
register(DataComponent.UnbreakableComponent)
register(DataComponent.UseEffectsComponent)
register(DataComponent.CustomNameComponent)
register(DataComponent.MinimumAttackChargeComponent)
register(DataComponent.DamageTypeComponent)
register(DataComponent.ItemNameComponent)
register(DataComponent.ItemModelComponent)
register(DataComponent.LoreComponent)
register(DataComponent.RarityComponent)
register(DataComponent.EnchantmentsComponent)
register(DataComponent.CanPlaceOnComponent)
register(DataComponent.CanBreakComponent)
register(DataComponent.AttributeModifiersComponent)
register(DataComponent.CustomModelDataComponent)
register(DataComponent.TooltipDisplayComponent)
register(DataComponent.RepairCostComponent)
register(DataComponent.CreativeSlotLockComponent)
register(DataComponent.EnchantmentGlintOverrideComponent)
register(DataComponent.IntangibleProjectileComponent)
register(DataComponent.FoodComponent)
register(DataComponent.ConsumableComponent)
register(DataComponent.UseRemainderComponent)
register(DataComponent.UseCooldownComponent)
register(DataComponent.DamageResistantComponent)
register(DataComponent.ToolComponent)
register(DataComponent.WeaponComponent)
register(DataComponent.AttackRangeComponent)
register(DataComponent.EnchantableComponent)
register(DataComponent.EquippableComponent)
register(DataComponent.RepairableComponent)
register(DataComponent.GliderComponent)
register(DataComponent.TooltipStyleComponent)
register(DataComponent.DeathProtectionComponent)
register(DataComponent.BlocksAttacksComponent)
register(DataComponent.PiercingWeaponComponent)
register(DataComponent.KineticWeaponComponent)
register(DataComponent.SwingAnimationComponent)
register(DataComponent.AdditionalTradeCostComponent)
register(DataComponent.StoredEnchantmentsComponent)
register(DataComponent.DyeComponent)
register(DataComponent.DyedColorComponent)
register(DataComponent.MapColorComponent)
register(DataComponent.MapIdComponent)
register(DataComponent.MapDecorationsComponent)
register(DataComponent.MapPostProcessingComponent)
register(DataComponent.ChargedProjectilesComponent)
register(DataComponent.BundleContentsComponent)
register(DataComponent.PotionContentsComponent)
register(DataComponent.PotionDurationScaleComponent)
register(DataComponent.SuspiciousStewEffectsComponent)
register(DataComponent.WritableBookContentComponent)
register(DataComponent.WrittenBookContentComponent)
register(DataComponent.TrimComponent)
register(DataComponent.DebugStickStateComponent)
register(DataComponent.EntityDataComponent)
register(DataComponent.BucketEntityDataComponent)
register(DataComponent.BlockEntityDataComponent)
register(DataComponent.InstrumentComponent)
register(DataComponent.ProvidesTrimMaterialComponent)
register(DataComponent.OminousBottleAmplifierComponent)
register(DataComponent.JukeboxPlayableComponent)
register(DataComponent.ProvidesBannerPatternsComponent)
register(DataComponent.RecipesComponent)
register(DataComponent.LodestoneTrackerComponent)
register(DataComponent.FireworkExplosionComponent)
register(DataComponent.FireworksComponent)
register(DataComponent.ProfileComponent)
register(DataComponent.NoteBlockSoundComponent)
register(DataComponent.BannerPatternsComponent)
register(DataComponent.BaseColorComponent)
register(DataComponent.PotDecorationsComponent)
register(DataComponent.ContainerComponent)
register(DataComponent.BlockStateComponent)
register(DataComponent.BeesComponent)
register(DataComponent.SulfurCubeContentComponent)
register(DataComponent.LockComponent)
register(DataComponent.ContainerLootComponent)
register(DataComponent.BreakSoundComponent)
register(DataComponent.VillagerVariantComponent)
register(DataComponent.WolfVariantComponent)
register(DataComponent.WolfSoundVariantComponent)
register(DataComponent.WolfCollarComponent)
register(DataComponent.FoxVariantComponent)
register(DataComponent.SalmonSizeComponent)
register(DataComponent.ParrotVariantComponent)
register(DataComponent.TropicalFishPatternComponent)
register(DataComponent.TropicalFishBaseColorComponent)
register(DataComponent.TropicalFishPatternColorComponent)
register(DataComponent.MooshroomVariantComponent)
register(DataComponent.RabbitVariantComponent)
register(DataComponent.PigVariantComponent)
register(DataComponent.PigSoundVariantComponent)
register(DataComponent.CowVariantComponent)
register(DataComponent.CowSoundVariantComponent)
register(DataComponent.ChickenVariantComponent)
register(DataComponent.ChickenSoundVariantComponent)
register(DataComponent.ZombieNautilusVariantComponent)
register(DataComponent.FrogVariantComponent)
register(DataComponent.HorseVariantComponent)
register(DataComponent.PaintingVariantComponent)
register(DataComponent.LlamaVariantComponent)
register(DataComponent.AxolotlVariantComponent)
register(DataComponent.CatVariantComponent)
register(DataComponent.CatSoundVariantComponent)
register(DataComponent.CatCollarComponent)
register(DataComponent.SheepColorComponent)
register(DataComponent.ShulkerColorComponent)
freeze()
}
}
internal fun BytesBuffer.readDataComponent(typeId: Int): DataComponent =
DataComponentRegistry.read(typeId, this)
internal fun BytesBuffer.writeDataComponent(component: DataComponent) {
DataComponentRegistry.write(this, component)
}
@@ -4,7 +4,7 @@
* Date: 2026/9/5 * Date: 2026/9/5
*/ */
package cn.rtast.libmc.protocol.protocol package cn.rtast.libmc.protocol.registry
import cn.rtast.libmc.protocol.packet.configuration.clientbound.* import cn.rtast.libmc.protocol.packet.configuration.clientbound.*
import cn.rtast.libmc.protocol.packet.configuration.serverbound.* import cn.rtast.libmc.protocol.packet.configuration.serverbound.*
@@ -23,7 +23,7 @@ import cn.rtast.libmc.protocol.packet.status.serverbound.ServerboundStatusReques
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
internal object GamePacketsProtocolCodec { internal object GamePacketsProtocolRegistry {
val clientboundGameProtocols = ProtocolStateRegistry().apply { val clientboundGameProtocols = ProtocolStateRegistry().apply {
register(ProtocolState.STATUS) { register(ProtocolState.STATUS) {
register(0x00, ClientboundStatusResponsePacket) register(0x00, ClientboundStatusResponsePacket)
@@ -78,9 +78,9 @@ internal object GamePacketsProtocolCodec {
register(0x0F, ClientboundCommandSuggestionsPacket) register(0x0F, ClientboundCommandSuggestionsPacket)
register(0x10, ClientboundCommandsPacket) register(0x10, ClientboundCommandsPacket)
register(0x11, ClientboundContainerClosePacket) register(0x11, ClientboundContainerClosePacket)
// register(0x12, ClientboundContainerSetContentPacket) register(0x12, ClientboundContainerSetContentPacket)
register(0x13, ClientboundContainerSetDataPacket) register(0x13, ClientboundContainerSetDataPacket)
// register(0x14, ClientboundContainerSetSlotPacket) register(0x14, ClientboundContainerSetSlotPacket)
register(0x15, ClientboundCookieRequestPacket) register(0x15, ClientboundCookieRequestPacket)
register(0x16, ClientboundCooldownPacket) register(0x16, ClientboundCooldownPacket)
register(0x17, ClientboundCustomChatCompletionsPacket) register(0x17, ClientboundCustomChatCompletionsPacket)
@@ -9,7 +9,10 @@ package client
import cn.rtast.libmc.crypto.AuthenticationProvider import cn.rtast.libmc.crypto.AuthenticationProvider
import cn.rtast.libmc.protocol.client.createMinecraftClient import cn.rtast.libmc.protocol.client.createMinecraftClient
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundContainerSetContentPacket
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundContainerSetSlotPacket
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundPlayerChatMessagePacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundPlayerChatMessagePacket
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundServerDataPacket
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundStepTickPacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundStepTickPacket
import cn.rtast.libmc.protocol.protocol.session.SessionEvent import cn.rtast.libmc.protocol.protocol.session.SessionEvent
import cn.rtast.libmc.protocol.protocol.session.onEvent import cn.rtast.libmc.protocol.protocol.session.onEvent
@@ -95,13 +98,14 @@ class TestClient {
login() login()
// disconnect() // disconnect()
} }
cli.onEvent<SessionEvent.DisconnectedEvent> { cli.onEvent<SessionEvent.ChangedState> {
println(it.reason.toJsonString()) println(it)
} }
cli.onPacket<ClientboundPlayerChatMessagePacket> { chatTracker.onReceivePlayerChat(it.messageSignature) } cli.onPacket<ClientboundPlayerChatMessagePacket> { chatTracker.onReceivePlayerChat(it.messageSignature) }
cli.onPacket<ClientboundStepTickPacket> { println(it) } cli.onPacket<ClientboundContainerSetSlotPacket> { println(it) }
cli.on { packet, direction -> println("$direction -> $packet") } // cli.on { packet, direction -> println("$direction -> $packet") }
cli.connect() cli.connect()
// awaitCancellation() // awaitCancellation()
while (true) { while (true) {
} }