diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt index b02aeb1..f81e6f5 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt @@ -18,7 +18,6 @@ internal class ClientStateMachine(private val session: Session) { private set suspend fun transitionTo(newState: ProtocolState) { - println("Changing State $currentState to $newState") currentState = newState session.emitEvent( when (newState) { diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetContentPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetContentPacket.kt index f0a9119..9ed66e3 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetContentPacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetContentPacket.kt @@ -12,21 +12,21 @@ 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.ItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.readItemStack +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 itemStackData: List, - val carriedItem: ItemStack?, + val slotData: List, + val carriedItem: Slot?, ) : MinecraftPacket { internal companion object Codec : PacketCodec { 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 { readItemStack() } + val slotData = buffer.readPrefixed { readSlot() } // val carriedItem = buffer.readSlot() // TODO FIX ME return ClientboundContainerSetContentPacket(windowId, stateId, slotData, null) } diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetSlotPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetSlotPacket.kt index 8bf7209..66bd160 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetSlotPacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundContainerSetSlotPacket.kt @@ -11,14 +11,14 @@ 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.ItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.readItemStack +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 itemStackData: ItemStack, + val slotData: Slot, ) : MinecraftPacket { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: ClientboundContainerSetSlotPacket) {} @@ -26,7 +26,7 @@ public data class ClientboundContainerSetSlotPacket( val windowId = buffer.readVarInt() val stateId = buffer.readVarInt() val slot = buffer.readShort() - val slotData = buffer.readItemStack() + val slotData = buffer.readSlot() return ClientboundContainerSetSlotPacket(windowId, stateId, slot, slotData) } } diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateRecipesPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateRecipesPacket.kt new file mode 100644 index 0000000..94832a1 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateRecipesPacket.kt @@ -0,0 +1,44 @@ +/* + * 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.IdSet +import cn.rtast.libmc.primitives.readIdSet +import cn.rtast.libmc.primitives.readPrefixed +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.item.slot.SlotDisplay +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlotDisplay +import cn.rtast.libmc.protocol.protocol.game.readIdentifier + +public data class ClientboundUpdateRecipesPacket( + val propertySets: List, + val stonecutterRecipes: List, +) : MinecraftPacket { + public data class PropertySet(val id: Identifier, val items: List) + public data class StonecutterRecipes(val ingredients: IdSet, val slotDisplay: SlotDisplay) + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundUpdateRecipesPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundUpdateRecipesPacket { + val propertySets = buffer.readPrefixed { + val id = readIdentifier() + val items = readPrefixed { readVarInt() } + PropertySet(id, items) + } + val stonecutterRecipes = buffer.readPrefixed { + val ingredients = readIdSet() + val slotDisplay = readSlotDisplay() + StonecutterRecipes(ingredients, slotDisplay) + } + return ClientboundUpdateRecipesPacket(propertySets, stonecutterRecipes) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundSetCreativeModeSlotPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundSetCreativeModeSlotPacket.kt index f593ae1..2e5d4f2 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundSetCreativeModeSlotPacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundSetCreativeModeSlotPacket.kt @@ -10,14 +10,14 @@ package cn.rtast.libmc.protocol.packet.play.serverbound import cn.rtast.libmc.network.BytesBuffer import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.PacketCodec -import cn.rtast.libmc.protocol.protocol.game.item.slot.ItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.writeItemStack +import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot +import cn.rtast.libmc.protocol.protocol.game.item.slot.writeSlot -public data class ServerboundSetCreativeModeSlotPacket(val slot: Short, val clickedItem: ItemStack) : MinecraftPacket { +public data class ServerboundSetCreativeModeSlotPacket(val slot: Short, val clickedItem: Slot) : MinecraftPacket { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: ServerboundSetCreativeModeSlotPacket) { buffer.writeShort(value.slot) - buffer.writeItemStack(value.clickedItem) + buffer.writeSlot(value.clickedItem) } override fun decode(buffer: BytesBuffer): ServerboundSetCreativeModeSlotPacket = diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/data/component/DataComponent.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/data/component/DataComponent.kt index 0a5e0df..d9edeb3 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/data/component/DataComponent.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/data/component/DataComponent.kt @@ -25,9 +25,9 @@ import cn.rtast.libmc.protocol.protocol.game.entity.* import cn.rtast.libmc.protocol.protocol.game.item.ItemUseAnimation import cn.rtast.libmc.protocol.protocol.game.item.PaintingVariantType import cn.rtast.libmc.protocol.protocol.game.item.readPaintingVariantType -import cn.rtast.libmc.protocol.protocol.game.item.slot.ItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.readItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.writeItemStack +import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot +import cn.rtast.libmc.protocol.protocol.game.item.slot.writeSlot import cn.rtast.libmc.protocol.protocol.game.item.writePaintingVariantType import cn.rtast.libmc.protocol.protocol.game.potion.* import cn.rtast.libmc.protocol.protocol.game.readIdentifier @@ -335,14 +335,14 @@ public sealed interface DataComponent { } } - public data class UseRemainderComponent(val remainder: ItemStack) : DataComponent { + public data class UseRemainderComponent(val remainder: Slot) : DataComponent { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: UseRemainderComponent) { - buffer.writeItemStack(value.remainder) + buffer.writeSlot(value.remainder) } override fun decode(buffer: BytesBuffer): UseRemainderComponent = - UseRemainderComponent(buffer.readItemStack()) + UseRemainderComponent(buffer.readSlot()) } } @@ -722,25 +722,25 @@ public sealed interface DataComponent { } } - public data class ChargedProjectilesComponent(val projectiles: List) : DataComponent { + public data class ChargedProjectilesComponent(val projectiles: List) : DataComponent { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: ChargedProjectilesComponent) { - buffer.writePrefixed(value.projectiles) { writeItemStack(it) } + buffer.writePrefixed(value.projectiles) { writeSlot(it) } } override fun decode(buffer: BytesBuffer): ChargedProjectilesComponent = - ChargedProjectilesComponent(buffer.readPrefixed { readItemStack() }) + ChargedProjectilesComponent(buffer.readPrefixed { readSlot() }) } } - public data class BundleContentsComponent(val items: List) : DataComponent { + public data class BundleContentsComponent(val items: List) : DataComponent { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: BundleContentsComponent) { - buffer.writePrefixed(value.items) { writeItemStack(it) } + buffer.writePrefixed(value.items) { writeSlot(it) } } override fun decode(buffer: BytesBuffer): BundleContentsComponent = - BundleContentsComponent(buffer.readPrefixed { readItemStack() }) + BundleContentsComponent(buffer.readPrefixed { readSlot() }) } } @@ -1257,14 +1257,14 @@ public sealed interface DataComponent { } } - public data class ContainerComponent(val items: List) : DataComponent { + public data class ContainerComponent(val items: List) : DataComponent { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: ContainerComponent) { - buffer.writePrefixed(value.items) { writeItemStack(it) } + buffer.writePrefixed(value.items) { writeSlot(it) } } override fun decode(buffer: BytesBuffer): ContainerComponent { - return ContainerComponent(buffer.readPrefixed { readItemStack() }) + return ContainerComponent(buffer.readPrefixed { readSlot() }) } } } @@ -1341,14 +1341,14 @@ public sealed interface DataComponent { } } - public data class SulfurCubeContentComponent(val content: ItemStack) : DataComponent { + public data class SulfurCubeContentComponent(val content: Slot) : DataComponent { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: SulfurCubeContentComponent) { - buffer.writeItemStack(value.content) + buffer.writeSlot(value.content) } override fun decode(buffer: BytesBuffer): SulfurCubeContentComponent = - SulfurCubeContentComponent(buffer.readItemStack()) + SulfurCubeContentComponent(buffer.readSlot()) } } diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/HashedItemStack.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/HashedSlot.kt similarity index 100% rename from libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/HashedItemStack.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/HashedSlot.kt diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/ItemStack.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/Slot.kt similarity index 61% rename from libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/ItemStack.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/Slot.kt index beb987c..9b2fb2f 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/ItemStack.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/Slot.kt @@ -14,7 +14,7 @@ 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 data class ItemStack( +public data class Slot( val count: Int, val itemId: Int?, val componentsToAdd: List, @@ -23,13 +23,13 @@ public data class ItemStack( val isEmpty: Boolean get() = count <= 0 public companion object { - public val EMPTY: ItemStack = ItemStack(0, null, emptyList(), emptyList()) + public val EMPTY: Slot = Slot(0, null, emptyList(), emptyList()) } } -internal fun BytesBuffer.readItemStack(): ItemStack { +internal fun BytesBuffer.readSlot(): Slot { val count = readVarInt() - if (count <= 0) return ItemStack.EMPTY + if (count <= 0) return Slot.EMPTY val itemId = readVarInt() val addCount = readVarInt() val removeCount = readVarInt() @@ -40,18 +40,18 @@ internal fun BytesBuffer.readItemStack(): ItemStack { } val componentsToRemove = ArrayList(removeCount) repeat(removeCount) { componentsToRemove.add(readVarInt()) } - return ItemStack(count, itemId, componentsToAdd, componentsToRemove) + return Slot(count, itemId, componentsToAdd, componentsToRemove) } -internal fun BytesBuffer.writeItemStack(itemStack: ItemStack) { - if (itemStack.isEmpty || itemStack.itemId == null) { +internal fun BytesBuffer.writeSlot(slot: Slot) { + if (slot.isEmpty || slot.itemId == null) { writeVarInt(0) return } - writeVarInt(itemStack.count) - writeVarInt(itemStack.itemId) - writeVarInt(itemStack.componentsToAdd.size) - writeVarInt(itemStack.componentsToRemove.size) - for (component in itemStack.componentsToAdd) writeDataComponent(component) - for (typeId in itemStack.componentsToRemove) writeVarInt(typeId) + 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) } \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplay.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplay.kt new file mode 100644 index 0000000..c1644b8 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplay.kt @@ -0,0 +1,27 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +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.registry.readSlotDisplayData +import cn.rtast.libmc.protocol.registry.writeSlotDisplayData + +public data class SlotDisplay(val itemDisplayType: Int, val data: SlotDisplayData) + +internal fun BytesBuffer.readSlotDisplay(): SlotDisplay { + val typeId = readVarInt() + val data = readSlotDisplayData(typeId) + return SlotDisplay(typeId, data) +} + +internal fun BytesBuffer.writeSlotDisplay(slot: SlotDisplay) { + writeVarInt(slot.itemDisplayType) + writeSlotDisplayData(slot.data) +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplayData.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplayData.kt new file mode 100644 index 0000000..ffc93af --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/item/slot/SlotDisplayData.kt @@ -0,0 +1,139 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.item.slot + +import cn.rtast.libmc.network.BytesBuffer +import cn.rtast.libmc.packet.EmptyPacketCodec +import cn.rtast.libmc.packet.PacketCodec +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.primitives.writePrefixed +import cn.rtast.libmc.primitives.writeVarInt +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readIdentifier +import cn.rtast.libmc.protocol.protocol.game.writeIdentifier + +public sealed interface SlotDisplayData { + public data object Empty : SlotDisplayData, EmptyPacketCodec(Empty) + public data object AnyFuel : SlotDisplayData, EmptyPacketCodec(Empty) + public data class WithAnyPotion(val base: SlotDisplay) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: WithAnyPotion) { + buffer.writeSlotDisplay(value.base) + } + + override fun decode(buffer: BytesBuffer): WithAnyPotion = WithAnyPotion(buffer.readSlotDisplay()) + } + } + + public data class OnlyWithComponent(val base: SlotDisplay, val componentTypeId: Int) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: OnlyWithComponent) { + buffer.writeSlotDisplay(value.base) + buffer.writeVarInt(value.componentTypeId) + } + + override fun decode(buffer: BytesBuffer): OnlyWithComponent { + val base = buffer.readSlotDisplay() + val componentTypeId = buffer.readVarInt() + return OnlyWithComponent(base, componentTypeId) + } + } + } + + public data class Item(val itemType: Int) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Item) { + buffer.writeVarInt(value.itemType) + } + + override fun decode(buffer: BytesBuffer): Item = Item(buffer.readVarInt()) + } + } + + public data class ItemStack(val item: Slot) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ItemStack) { + buffer.writeSlot(value.item) + } + + override fun decode(buffer: BytesBuffer): ItemStack = ItemStack(buffer.readSlot()) + } + } + + public data class Tags(val tag: Identifier) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Tags) { + buffer.writeIdentifier(value.tag) + } + + override fun decode(buffer: BytesBuffer): Tags = Tags(buffer.readIdentifier()) + } + } + + public data class Dyed(val dye: SlotDisplay, val target: SlotDisplay) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Dyed) { + buffer.writeSlotDisplay(value.dye) + } + + override fun decode(buffer: BytesBuffer): Dyed { + val dye = buffer.readSlotDisplay() + val target = buffer.readSlotDisplay() + return Dyed(dye, target) + } + } + } + + public data class SmithingTrim(val base: SlotDisplay, val material: SlotDisplay, val pattern: Int) : + SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: SmithingTrim) { + buffer.writeSlotDisplay(value.base) + buffer.writeSlotDisplay(value.material) + buffer.writeVarInt(value.pattern) + } + + override fun decode(buffer: BytesBuffer): SmithingTrim { + val base = buffer.readSlotDisplay() + val material = buffer.readSlotDisplay() + val pattern = buffer.readVarInt() + return SmithingTrim(base, material, pattern) + } + } + } + + public data class WithRemainder(val ingredient: SlotDisplay, val remainder: SlotDisplay) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: WithRemainder) { + buffer.writeSlotDisplay(value.ingredient) + buffer.writeSlotDisplay(value.remainder) + } + + override fun decode(buffer: BytesBuffer): WithRemainder { + val ingredient = buffer.readSlotDisplay() + val remainder = buffer.readSlotDisplay() + return WithRemainder(ingredient, remainder) + } + } + } + + public data class Composite(val optionCount: Int, val options: List) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Composite) { + buffer.writePrefixed(value.options) { writeSlotDisplay(it) } + } + + override fun decode(buffer: BytesBuffer): Composite { + val count = buffer.readVarInt() + val options = ArrayList(count) + repeat(count) { options.add(buffer.readSlotDisplay()) } + return Composite(count, options) + } + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/particle/ParticleData.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/particle/ParticleData.kt index 5be7154..4dd4296 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/particle/ParticleData.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/particle/ParticleData.kt @@ -13,8 +13,8 @@ import cn.rtast.libmc.protocol.protocol.game.block.BlockPos import cn.rtast.libmc.protocol.protocol.game.block.readBlockPos import cn.rtast.libmc.protocol.protocol.game.color.Color24 import cn.rtast.libmc.protocol.protocol.game.color.readColor24 -import cn.rtast.libmc.protocol.protocol.game.item.slot.ItemStack -import cn.rtast.libmc.protocol.protocol.game.item.slot.readItemStack +import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot import cn.rtast.libmc.protocol.protocol.game.math.Vec3d import cn.rtast.libmc.protocol.protocol.game.math.readVec3d @@ -42,7 +42,7 @@ public sealed interface ParticleData { public data class Trail(val position: Vec3d, val color: Color24, val durationTicks: Int) : ParticleData public data class Shriek(val delay: Int) : ParticleData - public data class Item(val itemStack: ItemStack) : ParticleData + public data class Item(val slot: Slot) : ParticleData } internal fun BytesBuffer.readParticleData(id: Int): ParticleData = readParticleData(ParticleType.fromID(id)) @@ -184,7 +184,7 @@ internal fun BytesBuffer.readParticleData(type: ParticleType): ParticleData { ParticleType.INSTANT_EFFECT, -> ParticleData.Effect(this.readColor24(), this.readFloat()) - ParticleType.ITEM -> ParticleData.Item(readItemStack()) + ParticleType.ITEM -> ParticleData.Item(readSlot()) ParticleType.VIBRATION -> { val source = when (val sourceTypeId = this.readVarInt()) { 0 -> ParticleData.Vibration.VibrationSource.Block(this.readBlockPos()) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/DataComponentRegistry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/DataComponentRegistry.kt index 71bc30d..1cb2cd4 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/DataComponentRegistry.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/DataComponentRegistry.kt @@ -7,45 +7,9 @@ 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>() - private val classToIdMap = mutableMapOf, Int>() - private lateinit var codecArray: Array> - - private fun register(kClass: KClass, codec: PacketCodec) { - val id = codecMap.size - codecMap[id] = codec - classToIdMap[kClass] = id - } - - inline fun register(codec: PacketCodec) = register(T::class, codec) - - fun freeze() { - val maxId = codecMap.keys.maxOrNull() ?: -1 - codecArray = Array(maxId + 1) { index -> - @Suppress("UNCHECKED_CAST") - codecMap[index] as? PacketCodec ?: 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) - } +internal object DataComponentRegistry : ArrayIndexedRegistry("DataComponentRegistry") { init { register(DataComponent.CustomDataComponent) register(DataComponent.MaxStackSizeComponent) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/GamePacketsProtocolRegistry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/GamePacketsProtocolRegistry.kt index 006b52c..918bb92 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/GamePacketsProtocolRegistry.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/GamePacketsProtocolRegistry.kt @@ -78,7 +78,7 @@ internal object GamePacketsProtocolRegistry { register(0x0F, ClientboundCommandSuggestionsPacket) register(0x10, ClientboundCommandsPacket) register(0x11, ClientboundContainerClosePacket) - register(0x12, ClientboundContainerSetContentPacket) +// register(0x12, ClientboundContainerSetContentPacket) // TODO FIX ME register(0x13, ClientboundContainerSetDataPacket) register(0x14, ClientboundContainerSetSlotPacket) register(0x15, ClientboundCookieRequestPacket) @@ -193,7 +193,7 @@ internal object GamePacketsProtocolRegistry { // register(0x82, ClientboundUpdateAdvancementsPacket) register(0x83, ClientboundUpdateAttributesPacket) register(0x84, ClientboundEntityEffectPacket) -// register(0x85, ClientboundUpdateRecipesPacket) + register(0x85, ClientboundUpdateRecipesPacket) register(0x86, ClientboundUpdateTagsPacket) register(0x87, ClientboundProjectilePowerPacket) register(0x88, ClientboundCustomReportDetailsPacket) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/Registry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/Registry.kt new file mode 100644 index 0000000..00cf2fe --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/Registry.kt @@ -0,0 +1,74 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +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 kotlin.concurrent.Volatile +import kotlin.reflect.KClass + +internal interface Registry { + fun freeze() + fun isFrozen(): Boolean + fun getCodec(id: Int): PacketCodec? + fun getId(kClass: KClass): Int? +} + +internal open class ArrayIndexedRegistry(private val registryName: String = "Registry") : Registry { + private val codecMap = mutableMapOf>() + private val classToIdMap = mutableMapOf, Int>() + + @Volatile + private var frozen = false + private lateinit var codecArray: Array> + + fun register(kClass: KClass, codec: PacketCodec) { + check(!frozen) { "$registryName is frozen and cannot accept new registrations." } + val id = codecMap.size + codecMap[id] = codec + classToIdMap[kClass] = id + } + + inline fun register(codec: PacketCodec) = register(U::class, codec) + + override fun freeze() { + if (frozen) return + val maxId = codecMap.keys.maxOrNull() ?: -1 + codecArray = Array(maxId + 1) { index -> + @Suppress("UNCHECKED_CAST") + codecMap[index] as? PacketCodec ?: error("[$registryName] Missing codec for ID: $index") + } + codecMap.clear() + frozen = true + } + + override fun isFrozen(): Boolean = frozen + + override fun getCodec(id: Int): PacketCodec? { + check(frozen) { "$registryName is not frozen yet!" } + return codecArray.getOrNull(id) + } + + override fun getId(kClass: KClass): Int? { + return classToIdMap[kClass] + } + + fun read(typeId: Int, buffer: BytesBuffer): T { + val codec = getCodec(typeId) ?: error("[$registryName] Unknown type ID: $typeId") + return codec.decode(buffer) + } + + fun write(buffer: BytesBuffer, value: T) { + val kClass = value::class + val typeId = getId(kClass) ?: error("[$registryName] Unregistered class: $kClass") + val codec = getCodec(typeId) ?: error("[$registryName] Missing codec for frozen ID: $typeId") + buffer.writeVarInt(typeId) + codec.encode(buffer, value) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/SlotDisplayDataRegistry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/SlotDisplayDataRegistry.kt new file mode 100644 index 0000000..c8d3f52 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/SlotDisplayDataRegistry.kt @@ -0,0 +1,32 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.registry + +import cn.rtast.libmc.network.BytesBuffer +import cn.rtast.libmc.protocol.protocol.game.item.slot.SlotDisplayData + +internal object SlotDisplayDataRegistry : ArrayIndexedRegistry("SlotDisplayRegistry") { + init { + register(SlotDisplayData.Empty) + register(SlotDisplayData.AnyFuel) + register(SlotDisplayData.WithAnyPotion) + register(SlotDisplayData.OnlyWithComponent) + register(SlotDisplayData.Item) + register(SlotDisplayData.ItemStack) + register(SlotDisplayData.Tags) + register(SlotDisplayData.Dyed) + register(SlotDisplayData.SmithingTrim) + register(SlotDisplayData.WithRemainder) + register(SlotDisplayData.Composite) + + freeze() + } +} + +internal fun BytesBuffer.readSlotDisplayData(typeId: Int): SlotDisplayData = SlotDisplayDataRegistry.read(typeId, this) +internal fun BytesBuffer.writeSlotDisplayData(display: SlotDisplayData) = SlotDisplayDataRegistry.write(this, display) \ No newline at end of file diff --git a/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt b/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt index 40ebbde..b6c0cf2 100644 --- a/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt +++ b/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt @@ -15,6 +15,7 @@ import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundLevelParticleP import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundLightUpdatePacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundPlayerChatMessagePacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundStepTickPacket +import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundUpdateRecipesPacket import cn.rtast.libmc.protocol.protocol.session.SessionEvent import cn.rtast.libmc.protocol.protocol.session.onEvent import cn.rtast.libmc.protocol.util.generateOfflineUuid @@ -75,8 +76,8 @@ class TestClient { @Test fun `test client offline mode`() = runTest { val cli = createMinecraftClient( - "127.0.0.1", 25566, "11", - generateOfflineUuid("11"), null, + "127.0.0.1", 25566, "222", + generateOfflineUuid("222"), null, context = { socketEngine = KtorNetworkEngine() } @@ -103,7 +104,7 @@ class TestClient { println(it) } cli.onPacket { chatTracker.onReceivePlayerChat(it.messageSignature) } - cli.onPacket { println(it) } + cli.onPacket { println(it) } // cli.on { packet, direction -> println("$direction -> $packet") } cli.connect()