From e47a0bff04abddd083460867782ce41887a6bf7c Mon Sep 17 00:00:00 2001 From: RTAkland Date: Fri, 11 Sep 2026 15:52:22 +0800 Subject: [PATCH] Fix serverbound client command; Implemented 0x85-ClientboundUpdateRecipesPacket 0x82-ClientboundUpdateAdvancementsPacket 0x6d-ClientboundSetPlayerTeamPacket 0x6c-ClientboundSetPlayerInventorySlotPacket 0x66-ClientboundSetEquipmentPacket 0x60-ClientboundSetCursorItemPacket 0x4a-ClientboundRecipeBookAddPacket 0x3f-ClientboundPlaceGhostRecipePacket 0x34-ClientboundMerchantOffersPacket 0x30-ClientboundLightUpdatePacket --- README.md | 36 ++-- .../ClientboundMerchantOffersPacket.kt | 93 +++++++++++ .../ClientboundPlaceGhostRecipePacket.kt | 27 +++ .../ClientboundRecipeBookAddPacket.kt | 38 +++++ .../ClientboundSetCursorItemPacket.kt | 22 +++ .../ClientboundSetEquipmentPacket.kt | 37 +++++ ...ClientboundSetPlayerInventorySlotPacket.kt | 26 +++ .../ClientboundSetPlayerTeamPacket.kt | 57 +++++++ .../ClientboundUpdateAdvancementsPacket.kt | 64 ++++++++ .../ServerboundClientActionPacket.kt | 25 --- .../ServerboundClientCommandPacket.kt | 6 +- .../protocol/protocol/game/Identifier.kt | 6 +- .../protocol/game/advancement/Advancement.kt | 44 +++++ .../game/advancement/AdvancementDisplay.kt | 27 +++ .../game/advancement/AdvancementFrameType.kt | 16 ++ .../game/advancement/AdvancementProgress.kt | 12 ++ .../protocol/game/entity/VillagerLevel.kt | 16 ++ .../inventory/equipment/EntityEquipment.kt | 12 ++ .../equipment/EntityEquipmentSlot.kt | 23 +++ .../game/item/slot/SlotDisplayData.kt | 22 ++- .../protocol/game/recipe/RecipeDisplay.kt | 27 +++ .../protocol/game/recipe/RecipeDisplayData.kt | 154 ++++++++++++++++++ .../protocol/game/recipe/RecipeEntry.kt | 22 +++ .../protocol/game/registry/ClientAction.kt | 4 +- .../protocol/game/team/CollisionRule.kt | 19 +++ .../protocol/game/team/NameTagVisibility.kt | 19 +++ .../protocol/protocol/game/team/TeamAction.kt | 50 ++++++ .../protocol/protocol/session/Session.kt | 2 + .../protocol/protocol/session/SessionImpl.kt | 3 + .../registry/GamePacketsProtocolRegistry.kt | 16 +- .../registry/RecipeDisplayDataRegistry.kt | 26 +++ .../rtast/libmc/protocol/registry/Registry.kt | 11 -- .../registry/SlotDisplayDataRegistry.kt | 2 +- .../commonTest/kotlin/client/TestClient.kt | 16 +- scritps/registries_struct.py | 2 +- 35 files changed, 895 insertions(+), 87 deletions(-) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundMerchantOffersPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundPlaceGhostRecipePacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundRecipeBookAddPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetCursorItemPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetEquipmentPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerInventorySlotPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerTeamPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateAdvancementsPacket.kt delete mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientActionPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/Advancement.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementDisplay.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementFrameType.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementProgress.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/entity/VillagerLevel.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipment.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipmentSlot.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplay.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplayData.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeEntry.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/CollisionRule.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/NameTagVisibility.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/TeamAction.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/RecipeDisplayDataRegistry.kt diff --git a/README.md b/README.md index 613fe9b..baf190a 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,14 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM # Minecraft Protocol Library Status -## Implementation details +## Unimplemented -- [x] **Online Mode Authentication & Encryption/Decryption**: See [Embedded cryptography](docs/Embedded-cryptography.md) -- [x] **Structured `TextComponent` Parser**: TextComponent AST decoder -- [x] **Command Tree Parser**: Full binary graph decoder for brigadier nodes, argument types, and suggestions -- [x] **Recipe Book & Recipe Data** - [ ] **Chunk & World Data**: Level Chunk Data with Light decoder (Paletted Containers, Direct/Indirect Palettes) - [ ] **Light Engine Update**: Sky & Block light nibble array parser -- [ ] **Explosion Event Decoder**: Knockback vectors and destroyed block offsets array - [ ] **Debug Packets Parsing**: Debug subs, block/entity states, and game performance sample events -- [ ] **Particle Parsing** -- [x] **Slot Data Codec** + + +> Encryption was implemented --- @@ -160,7 +156,7 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x21` Disguised Chat Message (`ClientboundDisguisedChatMessagePacket`) - [x] `0x22` Entity Event (`ClientboundEntityEventPacket`) - [x] `0x23` Teleport Entity (`ClientboundTeleportEntityPacket`) -- [ ] `0x24` Explode (`ClientboundExplodePacket`) +- [x] `0x24` Explode (`ClientboundExplodePacket`) - [x] `0x25` Unload Chunk (`ClientboundUnloadChunkPacket`) - [x] `0x26` Game Event (`ClientboundGameEventPacket`) - [x] `0x27` Game Rule Values (`ClientboundGameRuleValuesPacket`) @@ -171,12 +167,12 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x2C` Keep Alive (`ClientboundKeepAlivePlayPacket`) - [ ] `0x2D` Level Chunk Update With Light (`ClientboundLevelChunkUpdateWithLightPacket`) - [x] `0x2E` Level Event (`ClientboundLevelEventPacket`) -- [x] `0x2F` Particle (`ClientboundLevelParticlePacket`) **NOTE: Slot data are parsed as raw ByteArray** -- [ ] `0x30` Light Update (`ClientboundLightUpdatePacket`) +- [x] `0x2F` Particle (`ClientboundLevelParticlePacket`) +- [x] `0x30` Light Update (`ClientboundLightUpdatePacket`) - [x] `0x31` Login Play (`ClientboundLoginPlayPacket`) - [x] `0x32` Low Disk Space Warning (`ClientboundLowDiskSpaceWarningPacket`) - [x] `0x33` Map Item Data (`ClientboundMapItemDataPacket`) -- [ ] `0x34` Merchant Offers (`ClientboundMerchantOffersPacket`) +- [x] `0x34` Merchant Offers (`ClientboundMerchantOffersPacket`) - [x] `0x35` Update Entity Position (`ClientboundUpdateEntityPositionPacket`) - [x] `0x36` Update Entity Position and Rotation (`ClientboundUpdateEntityPositionAndRotationPacket`) - [x] `0x37` Move Minecart Along Track (`ClientboundMoveMinecartAlongTrackPacket`) @@ -187,7 +183,7 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x3C` Open Sign Editor (`ClientboundOpenSignEditorPacket`) - [x] `0x3D` Ping (`ClientboundPingPacket`) - [x] `0x3E` Pong Response (`ClientboundPongResponsePacket`) -- [ ] `0x3F` Place Ghost Recipe (`ClientboundPlaceGhostRecipePacket`) +- [x] `0x3F` Place Ghost Recipe (`ClientboundPlaceGhostRecipePacket`) - [x] `0x40` Player Abilities (`ClientboundPlayerAbilitiesPacket`) - [x] `0x41` Player Chat Message (`ClientboundPlayerChatMessagePacket`) - [x] `0x42` Player End Combat (`ClientboundPlayerEndCombatPacket`) @@ -198,7 +194,7 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x47` Player Look At (`ClientboundPlayerLookAtPacket`) - [x] `0x48` Synchronize Player Position (`ClientboundSynchronizePlayerPositionPacket`) - [x] `0x49` Player Rotation (`ClientboundPlayerRotationPacket`) -- [ ] `0x4A` Recipe Book Add (`ClientboundRecipeBookAddPacket`) +- [x] `0x4A` Recipe Book Add (`ClientboundRecipeBookAddPacket`) - [x] `0x4B` Recipe Book Remove (`ClientboundRecipeBookRemovePacket`) - [x] `0x4C` Recipe Book Settings (`ClientboundRecipeBookSettingsPacket`) - [x] `0x4D` Remove Entities (`ClientboundRemoveEntitiesPacket`) @@ -220,20 +216,20 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x5D` Set Camera (`ClientboundSetCameraPacket`) - [x] `0x5E` Set Center Chunk (`ClientboundSetCenterChunkPacket`) - [x] `0x5F` Set Render Distance (`ClientboundSetRenderDistancePacket`) -- [ ] `0x60` Set Cursor Item (`ClientboundSetCursorItemPacket`) +- [x] `0x60` Set Cursor Item (`ClientboundSetCursorItemPacket`) - [x] `0x61` Set Default Spawn Position (`ClientboundSetDefaultSpawnPositionPacket`) - [x] `0x62` Set Display Objective (`ClientboundSetDisplayObjectivePacket`) - [ ] `0x63` Set Entity Metadata (`ClientboundSetEntityMetadataPacket`) - [x] `0x64` Link Entities (`ClientboundLinkEntitiesPacket`) - [x] `0x65` Set Entity Velocity (`ClientboundSetEntityVelocityPacket`) -- [ ] `0x66` Set Equipment (`ClientboundSetEquipmentPacket`) +- [x] `0x66` Set Equipment (`ClientboundSetEquipmentPacket`) - [x] `0x67` Set Experience (`ClientboundSetExperiencePacket`) - [x] `0x68` Set Health (`ClientboundSetHealthPacket`) - [x] `0x69` Set Carried Item (`ClientboundSetCarriedItemPacket`) - [x] `0x6A` Update Objective (`ClientboundUpdateObjectivePacket`) - [x] `0x6B` Set Passengers (`ClientboundSetPassengersPacket`) -- [ ] `0x6C` Set Player Inventory Slot (`ClientboundSetPlayerInventorySlotPacket`) -- [ ] `0x6D` Set Player Team (`ClientboundSetPlayerTeamPacket`) +- [x] `0x6C` Set Player Inventory Slot (`ClientboundSetPlayerInventorySlotPacket`) +- [x] `0x6D` Set Player Team (`ClientboundSetPlayerTeamPacket`) - [x] `0x6E` Update Score (`ClientboundUpdateScorePacket`) - [x] `0x6F` Set Simulation Distance (`ClientboundSetSimulationDistancePacket`) - [x] `0x70` Set Subtitle Text (`ClientboundSetSubtitleTextPacket`) @@ -254,10 +250,10 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM - [x] `0x7F` Set Ticking State (`ClientboundSetTickingStatePacket`) - [x] `0x80` Step Tick (`ClientboundStepTickPacket`) - [x] `0x81` Transfer (`ClientboundTransferPacket`) -- [ ] `0x82` Update Advancements (`ClientboundUpdateAdvancementsPacket`) +- [x] `0x82` Update Advancements (`ClientboundUpdateAdvancementsPacket`) - [x] `0x83` Update Attributes (`ClientboundUpdateAttributesPacket`) - [x] `0x84` Entity Effect (`ClientboundEntityEffectPacket`) -- [ ] `0x85` Update Recipes (`ClientboundUpdateRecipesPacket`) +- [x] `0x85` Update Recipes (`ClientboundUpdateRecipesPacket`) - [x] `0x86` Update Tags (`ClientboundUpdateTagsPacket`) - [x] `0x87` Projectile Power (`ClientboundProjectilePowerPacket`) - [x] `0x88` Custom Report Details (`ClientboundCustomReportDetailsPacket`) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundMerchantOffersPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundMerchantOffersPacket.kt new file mode 100644 index 0000000..ae94549 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundMerchantOffersPacket.kt @@ -0,0 +1,93 @@ +/* + * 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.readPrefixOptional +import cn.rtast.libmc.primitives.readPrefixed +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.protocol.protocol.game.data.component.DataComponent +import cn.rtast.libmc.protocol.protocol.game.entity.VillagerLevel +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.registry.readDataComponent + +public data class ClientboundMerchantOffersPacket( + val windowId: Int, + val trades: List, + val villagerLevel: VillagerLevel, + val experience: Int, + val isRegularVillager: Boolean, + val canRestock: Boolean, +) : MinecraftPacket { + public data class MerchantTrade( + val inputItem1: TradeItem, + val outputItem: Slot, + val inputItem2: TradeItem?, + val tradable: Boolean, + val tradeUsed: Int, + val maxTradeUsed: Int, + val xp: Int, + val specialPrice: Int, + val priceMultiplier: Float, + val demand: Int, + ) + + public data class TradeItem( + val itemId: Int, + val itemCount: Int, + val components: Map, + ) + + internal companion object Codec : PacketCodec { + private fun BytesBuffer.readTradeItem(): TradeItem { + val itemId = readVarInt() + val itemCount = readVarInt() + val componentCount = readVarInt() + val components = HashMap(componentCount) + repeat(componentCount) { + val typeId = readVarInt() + val componentData = readDataComponent(typeId) + components[typeId] = componentData + } + return TradeItem(itemId, itemCount, components) + } + + override fun encode(buffer: BytesBuffer, value: ClientboundMerchantOffersPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundMerchantOffersPacket { + val windowId = buffer.readVarInt() + val trades = buffer.readPrefixed { + val inputItem1 = readTradeItem() + val outputItem = readSlot() + val inputItem2 = readPrefixOptional { readTradeItem() } + val tradable = !readBoolean() // reverse + val tradeUsed = readInt() + val maxTradeUsed = readInt() + val xp = readInt() + val specialPrice = readInt() + val priceMultiplier = readFloat() + val demand = readInt() + MerchantTrade( + inputItem1, outputItem, inputItem2, tradable, + tradeUsed, maxTradeUsed, xp, specialPrice, + priceMultiplier, demand, + ) + } + val villagerLevel = VillagerLevel.fromLevel(buffer.readVarInt()) + val experience = buffer.readVarInt() + val isRegularVillager = buffer.readBoolean() + val canRestock = buffer.readBoolean() + return ClientboundMerchantOffersPacket( + windowId, trades, villagerLevel, + experience, isRegularVillager, canRestock + ) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundPlaceGhostRecipePacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundPlaceGhostRecipePacket.kt new file mode 100644 index 0000000..b051334 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundPlaceGhostRecipePacket.kt @@ -0,0 +1,27 @@ +/* + * 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.recipe.RecipeDisplay +import cn.rtast.libmc.protocol.protocol.game.recipe.readRecipeDisplay + +public data class ClientboundPlaceGhostRecipePacket(val windowId: Int, val recipeDisplay: RecipeDisplay) : + MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundPlaceGhostRecipePacket) {} + override fun decode(buffer: BytesBuffer): ClientboundPlaceGhostRecipePacket { + val windowId = buffer.readVarInt() + val recipeDisplay = buffer.readRecipeDisplay() + return ClientboundPlaceGhostRecipePacket(windowId, recipeDisplay) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundRecipeBookAddPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundRecipeBookAddPacket.kt new file mode 100644 index 0000000..37b8ef5 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundRecipeBookAddPacket.kt @@ -0,0 +1,38 @@ +/* + * 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.readIdSet +import cn.rtast.libmc.primitives.readPrefixOptional +import cn.rtast.libmc.primitives.readPrefixed +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.protocol.protocol.game.recipe.RecipeEntry +import cn.rtast.libmc.protocol.protocol.game.recipe.readRecipeDisplay + +public data class ClientboundRecipeBookAddPacket(val recipes: List, val replace: Boolean) : + MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundRecipeBookAddPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundRecipeBookAddPacket { + val recipes = buffer.readPrefixed { + val recipeId = readVarInt() + val display = readRecipeDisplay() + val groupId = readVarInt() + val categoryId = readVarInt() + val ingredients = readPrefixOptional { readPrefixed { readIdSet() } } + val flags = readByte() + RecipeEntry(recipeId, display, groupId, categoryId, ingredients, flags) + } + val replace = buffer.readBoolean() + return ClientboundRecipeBookAddPacket(recipes, replace) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetCursorItemPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetCursorItemPacket.kt new file mode 100644 index 0000000..ab77636 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetCursorItemPacket.kt @@ -0,0 +1,22 @@ +/* + * 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.protocol.protocol.game.item.slot.Slot +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot + +public data class ClientboundSetCursorItemPacket(val carriedItem: Slot) : MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundSetCursorItemPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundSetCursorItemPacket = + ClientboundSetCursorItemPacket(buffer.readSlot()) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetEquipmentPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetEquipmentPacket.kt new file mode 100644 index 0000000..91e1212 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetEquipmentPacket.kt @@ -0,0 +1,37 @@ +/* + * 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.inventory.equipment.EntityEquipment +import cn.rtast.libmc.protocol.protocol.game.inventory.equipment.EntityEquipmentSlot +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot + +public data class ClientboundSetEquipmentPacket(val entityId: Int, val equipments: List) : + MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundSetEquipmentPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundSetEquipmentPacket { + val entityId = buffer.readVarInt() + val equipments = ArrayList() + while (true) { + val rawByte = buffer.readByte().toInt() + val hasNext = (rawByte and 0x80) != 0 + val slotId = rawByte and 0x7F + val slot = EntityEquipmentSlot.fromID(slotId) + val item = buffer.readSlot() + equipments.add(EntityEquipment(slot, item)) + if (!hasNext) break + } + return ClientboundSetEquipmentPacket(entityId, equipments) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerInventorySlotPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerInventorySlotPacket.kt new file mode 100644 index 0000000..c76e229 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerInventorySlotPacket.kt @@ -0,0 +1,26 @@ +/* + * 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 ClientboundSetPlayerInventorySlotPacket(val slot: Int, val slotData: Slot) : MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundSetPlayerInventorySlotPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundSetPlayerInventorySlotPacket { + val slot = buffer.readVarInt() + val slotData = buffer.readSlot() + return ClientboundSetPlayerInventorySlotPacket(slot, slotData) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerTeamPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerTeamPacket.kt new file mode 100644 index 0000000..3eca041 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundSetPlayerTeamPacket.kt @@ -0,0 +1,57 @@ +/* + * 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.readMcString +import cn.rtast.libmc.primitives.readPrefixed +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent +import cn.rtast.libmc.protocol.protocol.game.team.CollisionRule +import cn.rtast.libmc.protocol.protocol.game.team.NameTagVisibility +import cn.rtast.libmc.protocol.protocol.game.team.TeamAction + +public data class ClientboundSetPlayerTeamPacket(val teamName: String, val action: TeamAction) : MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundSetPlayerTeamPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundSetPlayerTeamPacket { + val teamName = buffer.readMcString() + val action = when (val teamAction = buffer.readByte().toInt()) { + 0 -> { + val info = buffer.decodeTeamInfo() + val entities = buffer.readPrefixed { readMcString() } + TeamAction.CreateTeam(info, entities) + } + + 1 -> TeamAction.RemoveTeam + 2 -> TeamAction.UpdateTeamInfo(buffer.decodeTeamInfo()) + 3 -> TeamAction.AddEntities(buffer.readPrefixed { readMcString() }) + 4 -> TeamAction.RemoveEntities(buffer.readPrefixed { readMcString() }) + else -> error("Unknown team action method id $teamAction") + } + return ClientboundSetPlayerTeamPacket(teamName, action) + } + + private fun BytesBuffer.decodeTeamInfo(): TeamAction.TeamInfo { + val displayName = readTextComponent() + val prefix = readTextComponent() + val suffix = readTextComponent() + val nameTagVisibility = NameTagVisibility.fromID(readVarInt()) + val collisionRule = CollisionRule.fromID(readVarInt()) + val teamColor = readVarInt() + val friendlyFlags = readByte() + return TeamAction.TeamInfo( + displayName, prefix, suffix, + nameTagVisibility, collisionRule, + teamColor, friendlyFlags + ) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateAdvancementsPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateAdvancementsPacket.kt new file mode 100644 index 0000000..379a3e5 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/clientbound/ClientboundUpdateAdvancementsPacket.kt @@ -0,0 +1,64 @@ +/* + * 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.readPrefixOptional +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.advancement.Advancement +import cn.rtast.libmc.protocol.protocol.game.advancement.AdvancementProgress +import cn.rtast.libmc.protocol.protocol.game.advancement.readAdvancement +import cn.rtast.libmc.protocol.protocol.game.readIdentifier + +public data class ClientboundUpdateAdvancementsPacket( + val reset: Boolean, + val advancementMapping: Map, + /** + * The identifiers of the advancements that should be removed + */ + val identifiers: List, + val progressMapping: Map, + val showAdvancements: Boolean, +) : MinecraftPacket { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundUpdateAdvancementsPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundUpdateAdvancementsPacket { + val reset = buffer.readBoolean() + val mappingCount = buffer.readVarInt() + val advancementMapping = HashMap(mappingCount) + repeat(mappingCount) { + val key = buffer.readIdentifier() + val advancement = buffer.readAdvancement() + advancementMapping[key] = advancement + } + val identifiers = buffer.readPrefixed { readIdentifier() } + val progressMappingCount = buffer.readVarInt() + val progressMapping = HashMap(progressMappingCount) + repeat(progressMappingCount) { + val key = buffer.readIdentifier() + val criteriaCount = buffer.readVarInt() + val criteria = HashMap(criteriaCount) + repeat(criteriaCount) { + val k = buffer.readIdentifier() + val date = buffer.readPrefixOptional { readLong() } + criteria[k] = date + } + progressMapping[key] = AdvancementProgress(criteria) + } + val showAdvancements = buffer.readBoolean() + return ClientboundUpdateAdvancementsPacket( + reset, advancementMapping, identifiers, + progressMapping, showAdvancements + ) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientActionPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientActionPacket.kt deleted file mode 100644 index e522f42..0000000 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientActionPacket.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/5 - */ - - -package cn.rtast.libmc.protocol.packet.play.serverbound - -import cn.rtast.libmc.network.BytesBuffer -import cn.rtast.libmc.packet.PacketCodec -import cn.rtast.libmc.packet.MinecraftPacket -import cn.rtast.libmc.primitives.writeVarInt -import cn.rtast.libmc.protocol.protocol.game.registry.ClientAction - -public data class ServerboundClientActionPacket(val action: ClientAction) : MinecraftPacket { - internal companion object Codec : PacketCodec { - override fun encode(buffer: BytesBuffer, value: ServerboundClientActionPacket) { - buffer.writeVarInt(value.action.actionID) - } - - override fun decode(buffer: BytesBuffer): ServerboundClientActionPacket = - throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientCommandPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientCommandPacket.kt index ba4de2c..b113cb6 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientCommandPacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/serverbound/ServerboundClientCommandPacket.kt @@ -8,12 +8,12 @@ package cn.rtast.libmc.protocol.packet.play.serverbound import cn.rtast.libmc.network.BytesBuffer -import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.packet.MinecraftPacket +import cn.rtast.libmc.packet.PacketCodec import cn.rtast.libmc.primitives.writeVarInt -import cn.rtast.libmc.protocol.protocol.game.player.action.PlayerActionStatus +import cn.rtast.libmc.protocol.protocol.game.registry.ClientAction -public data class ServerboundClientCommandPacket(val action: PlayerActionStatus) : MinecraftPacket { +public data class ServerboundClientCommandPacket(val action: ClientAction) : MinecraftPacket { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: ServerboundClientCommandPacket) { buffer.writeVarInt(value.action.id) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt index 498a844..755bc25 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt @@ -23,9 +23,9 @@ public value class Identifier internal constructor(public val raw: String) { override fun toString(): String = "$namespace:$path" - internal companion object Codec : PacketCodec { - fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path") - fun of(full: String): Identifier = Identifier(full) + public companion object Codec : PacketCodec { + public fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path") + public fun of(full: String): Identifier = Identifier(full) override fun encode(buffer: BytesBuffer, value: Identifier) { buffer.writeMcString(value.toString()) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/Advancement.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/Advancement.kt new file mode 100644 index 0000000..b7a1270 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/Advancement.kt @@ -0,0 +1,44 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.advancement + +import cn.rtast.libmc.network.BytesBuffer +import cn.rtast.libmc.primitives.readMcString +import cn.rtast.libmc.primitives.readPrefixOptional +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.chat.readTextComponent +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlot +import cn.rtast.libmc.protocol.protocol.game.readIdentifier + +public data class Advancement( + val parentId: Identifier?, + val displayData: AdvancementDisplay?, + val requirements: List>, + val sendsTelemetryData: Boolean, +) + +internal fun BytesBuffer.readAdvancement(): Advancement { + val parentId = readPrefixOptional { readIdentifier() } + val displayData = readPrefixOptional { + val title = readTextComponent() + val description = readTextComponent() + val icon = readSlot() + val frameType = AdvancementFrameType.fromID(readVarInt()) + val flags = readInt() + val hasBackground = (flags and 0x01) != 0 + val backgroundTexture = if (hasBackground) readIdentifier() else null + val x = readFloat() + val y = readFloat() + AdvancementDisplay(title, description, icon, frameType, flags, backgroundTexture, x, y) + } + val requirements = readPrefixed { readPrefixed { readMcString() } } + val sendsTelemetryData = readBoolean() + return Advancement(parentId, displayData, requirements, sendsTelemetryData) +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementDisplay.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementDisplay.kt new file mode 100644 index 0000000..5164e3d --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementDisplay.kt @@ -0,0 +1,27 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.advancement + +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent +import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot + +public data class AdvancementDisplay( + val title: TextComponent, + val description: TextComponent, + val icon: Slot, + val frameType: AdvancementFrameType, + val flags: Int, + val backgroundTexture: Identifier?, + val x: Float, + val y: Float +) { + public val hasBackgroundTexture: Boolean get() = (flags and 0x01) != 0 + public val showToast: Boolean get() = (flags and 0x02) != 0 + public val isHidden: Boolean get() = (flags and 0x04) != 0 +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementFrameType.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementFrameType.kt new file mode 100644 index 0000000..4d968e8 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementFrameType.kt @@ -0,0 +1,16 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.advancement + +public enum class AdvancementFrameType(public val id: Int) { + TASK(0), CHALLENGE(1), GOAL(2); + + public companion object { + public fun fromID(id: Int): AdvancementFrameType = entries.first { it.id == id } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementProgress.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementProgress.kt new file mode 100644 index 0000000..f71e30b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/advancement/AdvancementProgress.kt @@ -0,0 +1,12 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.advancement + +import cn.rtast.libmc.protocol.protocol.game.Identifier + +public data class AdvancementProgress(val criteria: Map) \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/entity/VillagerLevel.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/entity/VillagerLevel.kt new file mode 100644 index 0000000..37c9c52 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/entity/VillagerLevel.kt @@ -0,0 +1,16 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.entity + +public enum class VillagerLevel(public val level: Int) { + Novice(1), Apprentice(2), Journeyman(3), Expert(4), Master(5); + + public companion object { + public fun fromLevel(level: Int): VillagerLevel = entries.first { it.level == level } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipment.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipment.kt new file mode 100644 index 0000000..073942b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipment.kt @@ -0,0 +1,12 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.inventory.equipment + +import cn.rtast.libmc.protocol.protocol.game.item.slot.Slot + +public data class EntityEquipment(val slot: EntityEquipmentSlot, val item: Slot) \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipmentSlot.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipmentSlot.kt new file mode 100644 index 0000000..dccca53 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/inventory/equipment/EntityEquipmentSlot.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.inventory.equipment + +public enum class EntityEquipmentSlot(public val id: Int) { + MAIN_HAND(0), + OFF_HAND(1), + BOOTS(2), + LEGGINGS(3), + CHESTPLATE(4), + HELMET(5), + BODY(6), + SADDLE(7); + + public companion object { + public fun fromID(id: Int): EntityEquipmentSlot = entries.firstOrNull { it.id == id } ?: MAIN_HAND + } +} \ 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 index ffc93af..8c24b42 100644 --- 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 @@ -8,7 +8,6 @@ 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 @@ -18,8 +17,16 @@ 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 object Empty : SlotDisplayData, PacketCodec { + override fun encode(buffer: BytesBuffer, value: Empty) {} + override fun decode(buffer: BytesBuffer): Empty = Empty + } + + public data object AnyFuel : SlotDisplayData, PacketCodec { + override fun encode(buffer: BytesBuffer, value: AnyFuel) {} + override fun decode(buffer: BytesBuffer): AnyFuel = AnyFuel + } + public data class WithAnyPotion(val base: SlotDisplay) : SlotDisplayData { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: WithAnyPotion) { @@ -65,13 +72,13 @@ public sealed interface SlotDisplayData { } } - public data class Tags(val tag: Identifier) : SlotDisplayData { - internal companion object Codec : PacketCodec { - override fun encode(buffer: BytesBuffer, value: Tags) { + public data class Tag(val tag: Identifier) : SlotDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Tag) { buffer.writeIdentifier(value.tag) } - override fun decode(buffer: BytesBuffer): Tags = Tags(buffer.readIdentifier()) + override fun decode(buffer: BytesBuffer): Tag = Tag(buffer.readIdentifier()) } } @@ -79,6 +86,7 @@ public sealed interface SlotDisplayData { internal companion object Codec : PacketCodec { override fun encode(buffer: BytesBuffer, value: Dyed) { buffer.writeSlotDisplay(value.dye) + buffer.writeSlotDisplay(value.target) } override fun decode(buffer: BytesBuffer): Dyed { diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplay.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplay.kt new file mode 100644 index 0000000..462a89f --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplay.kt @@ -0,0 +1,27 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.recipe + +import cn.rtast.libmc.network.BytesBuffer +import cn.rtast.libmc.primitives.readVarInt +import cn.rtast.libmc.primitives.writeVarInt +import cn.rtast.libmc.protocol.registry.readRecipeDisplayData +import cn.rtast.libmc.protocol.registry.writeRecipeDisplayData + +public data class RecipeDisplay(val displayType: Int, val data: RecipeDisplayData) + +internal fun BytesBuffer.readRecipeDisplay(): RecipeDisplay { + val displayType = readVarInt() + val data = readRecipeDisplayData(displayType) + return RecipeDisplay(displayType, data) +} + +internal fun BytesBuffer.writeRecipeDisplay(display: RecipeDisplay) { + writeVarInt(display.displayType) + writeRecipeDisplayData(display.data) +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplayData.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplayData.kt new file mode 100644 index 0000000..fa07c65 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeDisplayData.kt @@ -0,0 +1,154 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.recipe + +import cn.rtast.libmc.network.BytesBuffer +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.item.slot.SlotDisplay +import cn.rtast.libmc.protocol.protocol.game.item.slot.readSlotDisplay +import cn.rtast.libmc.protocol.protocol.game.item.slot.writeSlotDisplay + +public sealed interface RecipeDisplayData { + public val result: SlotDisplay + public val craftingStation: SlotDisplay + + public data class CraftingShapeless( + val ingredientCount: Int, + val ingredients: List, + override val result: SlotDisplay, + override val craftingStation: SlotDisplay, + ) : RecipeDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: CraftingShapeless) { + buffer.writePrefixed(value.ingredients) { writeSlotDisplay(it) } + buffer.writeSlotDisplay(value.result) + buffer.writeSlotDisplay(value.craftingStation) + } + + override fun decode(buffer: BytesBuffer): CraftingShapeless { + val ingredientCount = buffer.readVarInt() + val ingredients = ArrayList(ingredientCount) + repeat(ingredientCount) { ingredients.add(buffer.readSlotDisplay()) } + val result = buffer.readSlotDisplay() + val craftingStation = buffer.readSlotDisplay() + return CraftingShapeless(ingredientCount, ingredients, result, craftingStation) + } + } + } + + public data class CraftingShaped( + val width: Int, + val height: Int, + val ingredientCount: Int, + val ingredients: List, + override val result: SlotDisplay, + override val craftingStation: SlotDisplay, + ) : RecipeDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: CraftingShaped) { + buffer.writeVarInt(value.width) + buffer.writeVarInt(value.height) + buffer.writePrefixed(value.ingredients) { writeSlotDisplay(it) } + buffer.writeSlotDisplay(value.result) + buffer.writeSlotDisplay(value.craftingStation) + } + + override fun decode(buffer: BytesBuffer): CraftingShaped { + val width = buffer.readVarInt() + val height = buffer.readVarInt() + val ingredientCount = buffer.readVarInt() + val ingredients = ArrayList(ingredientCount) + repeat(ingredientCount) { ingredients.add(buffer.readSlotDisplay()) } + val result = buffer.readSlotDisplay() + val craftingStation = buffer.readSlotDisplay() + return CraftingShaped(width, height, ingredientCount, ingredients, result, craftingStation) + } + } + } + + public data class Furnace( + val ingredient: SlotDisplay, + val fuel: SlotDisplay, + override val result: SlotDisplay, + override val craftingStation: SlotDisplay, + val cookingTime: Int, // ticks + val experience: Float, + ) : RecipeDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Furnace) { + buffer.writeSlotDisplay(value.ingredient) + buffer.writeSlotDisplay(value.fuel) + buffer.writeSlotDisplay(value.result) + buffer.writeSlotDisplay(value.craftingStation) + buffer.writeVarInt(value.cookingTime) + buffer.writeFloat(value.experience) + } + + override fun decode(buffer: BytesBuffer): Furnace { + val ingredient = buffer.readSlotDisplay() + val fuel = buffer.readSlotDisplay() + val result = buffer.readSlotDisplay() + val craftingStation = buffer.readSlotDisplay() + val cookingTime = buffer.readVarInt() + val experience = buffer.readFloat() + return Furnace(ingredient, fuel, result, craftingStation, cookingTime, experience) + } + } + } + + public data class Stonecutter( + val ingredient: SlotDisplay, + override val result: SlotDisplay, + override val craftingStation: SlotDisplay, + ) : RecipeDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Stonecutter) { + buffer.writeSlotDisplay(value.ingredient) + buffer.writeSlotDisplay(value.result) + buffer.writeSlotDisplay(value.craftingStation) + } + + override fun decode(buffer: BytesBuffer): Stonecutter { + val ingredient = buffer.readSlotDisplay() + val result = buffer.readSlotDisplay() + val craftingStation = buffer.readSlotDisplay() + return Stonecutter(ingredient, result, craftingStation) + } + } + } + + public data class Smithing( + val template: SlotDisplay, + val base: SlotDisplay, + val addition: SlotDisplay, + override val result: SlotDisplay, + override val craftingStation: SlotDisplay, + ) : RecipeDisplayData { + internal companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: Smithing) { + buffer.writeSlotDisplay(value.template) + buffer.writeSlotDisplay(value.base) + buffer.writeSlotDisplay(value.addition) + buffer.writeSlotDisplay(value.result) + buffer.writeSlotDisplay(value.craftingStation) + } + + override fun decode(buffer: BytesBuffer): Smithing { + val template = buffer.readSlotDisplay() + val base = buffer.readSlotDisplay() + val addition = buffer.readSlotDisplay() + val result = buffer.readSlotDisplay() + val craftingStation = buffer.readSlotDisplay() + return Smithing(template, base, addition, result, craftingStation) + } + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeEntry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeEntry.kt new file mode 100644 index 0000000..9550ef9 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/recipe/RecipeEntry.kt @@ -0,0 +1,22 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.recipe + +import cn.rtast.libmc.primitives.IdSet + +public data class RecipeEntry( + val recipeId: Int, + val display: RecipeDisplay, + val groupId: Int, + val categoryId: Int, + val ingredients: List?, + val flags: Byte +) { + val showNotification: Boolean get() = (flags.toInt() and 0x01) != 0 + val highlightAsNew: Boolean get() = (flags.toInt() and 0x02) != 0 +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/registry/ClientAction.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/registry/ClientAction.kt index c884f23..fa2fa78 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/registry/ClientAction.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/registry/ClientAction.kt @@ -7,7 +7,7 @@ package cn.rtast.libmc.protocol.protocol.game.registry -public enum class ClientAction(public val actionID: Int) { +public enum class ClientAction(public val id: Int) { /** * Sent when the client is ready to respawn after death. */ @@ -24,6 +24,6 @@ public enum class ClientAction(public val actionID: Int) { RequestGameRuleValues(2); public companion object { - public fun fromID(id: Int): ClientAction = entries.first { it.actionID == id } + public fun fromID(id: Int): ClientAction = entries.first { it.id == id } } } \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/CollisionRule.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/CollisionRule.kt new file mode 100644 index 0000000..aee235d --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/CollisionRule.kt @@ -0,0 +1,19 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.team + +public enum class CollisionRule(public val id: Int) { + ALWAYS(0), + NEVER(1), + PUSH_OTHER_TEAMS(2), + PUSH_OWN_TEAM(3); + + public companion object { + public fun fromID(id: Int): CollisionRule = entries.firstOrNull { it.id == id } ?: ALWAYS + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/NameTagVisibility.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/NameTagVisibility.kt new file mode 100644 index 0000000..e3caa4b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/NameTagVisibility.kt @@ -0,0 +1,19 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.team + +public enum class NameTagVisibility(public val id: Int) { + ALWAYS(0), + NEVER(1), + HIDE_FOR_OTHER_TEAMS(2), + HIDE_FOR_OWN_TEAMS(3); + + public companion object { + public fun fromID(id: Int): NameTagVisibility = entries.first { it.id == id } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/TeamAction.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/TeamAction.kt new file mode 100644 index 0000000..841f304 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/team/TeamAction.kt @@ -0,0 +1,50 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/11 + */ + + +package cn.rtast.libmc.protocol.protocol.game.team + +import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent + +public sealed interface TeamAction { + public val methodId: Int + + public data class CreateTeam( + val teamInfo: TeamInfo, + val entities: List, + ) : TeamAction { + override val methodId: Int = 0 + } + + public data object RemoveTeam : TeamAction { + override val methodId: Int = 1 + } + + public data class UpdateTeamInfo(val teamInfo: TeamInfo) : TeamAction { + override val methodId: Int = 2 + } + + public data class AddEntities(val entities: List) : TeamAction { + override val methodId: Int = 3 + } + + public data class RemoveEntities(val entities: List) : TeamAction { + override val methodId: Int = 4 + } + + public data class TeamInfo( + val displayName: TextComponent, + val prefix: TextComponent, + val suffix: TextComponent, + val nameTagVisibility: NameTagVisibility, + val collisionRule: CollisionRule, + val teamColor: Int, + val friendlyFlags: Byte, + ) { + public val allowFriendlyFire: Boolean get() = (friendlyFlags.toInt() and 0x01) != 0 + public val canSeeFriendlyInvisible: Boolean get() = (friendlyFlags.toInt() and 0x02) != 0 + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/Session.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/Session.kt index 3d107ab..cd1cc7d 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/Session.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/Session.kt @@ -7,6 +7,7 @@ package cn.rtast.libmc.protocol.protocol.session +import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.protocol.client.CURRENT_MINECRAFT_PROTOCOL_VERSION import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent import kotlin.reflect.KClass @@ -24,6 +25,7 @@ public interface Session { public suspend fun status(protocolVersion: Int = CURRENT_MINECRAFT_PROTOCOL_VERSION): String public suspend fun disconnect() public suspend fun init() + public suspend fun sendPacket(packet: MinecraftPacket) } public inline fun Session.onEvent(noinline block: suspend Session.(T) -> Unit) { diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/SessionImpl.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/SessionImpl.kt index e8b6a3a..822a0d4 100644 --- a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/SessionImpl.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/session/SessionImpl.kt @@ -1,5 +1,6 @@ package cn.rtast.libmc.protocol.protocol.session +import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.protocol.client.MinecraftClient import cn.rtast.libmc.protocol.crypto.minecraftServerIdHash import cn.rtast.libmc.protocol.crypto.rsaEncrypt @@ -76,6 +77,8 @@ public class SessionImpl internal constructor() : Session { } } + public override suspend fun sendPacket(packet: MinecraftPacket): Unit = client.networkChannel.sendPacket(packet) + override fun _onEvent(clazz: KClass, block: suspend Session.(T) -> Unit) { @Suppress("UNCHECKED_CAST") this.eventListener.getOrPut(clazz) { mutableListOf() }.add { block(it as T) } 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 918bb92..bc38065 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 @@ -112,7 +112,7 @@ internal object GamePacketsProtocolRegistry { register(0x31, ClientboundLoginPlayPacket) register(0x32, ClientboundLowDiskSpaceWarningPacket) register(0x33, ClientboundMapItemDataPacket) -// register(0x34, ClientboundMerchantOffersPacket) + register(0x34, ClientboundMerchantOffersPacket) register(0x35, ClientboundUpdateEntityPositionPacket) register(0x36, ClientboundUpdateEntityPositionAndRotationPacket) register(0x37, ClientboundMoveMinecartAlongTrackPacket) @@ -123,7 +123,7 @@ internal object GamePacketsProtocolRegistry { register(0x3C, ClientboundOpenSignEditorPacket) register(0x3D, ClientboundPingPacket) register(0x3E, ClientboundPongResponsePacket) -// register(0x3F, ClientboundPlaceGhostRecipePacket) + register(0x3F, ClientboundPlaceGhostRecipePacket) register(0x40, ClientboundPlayerAbilitiesPacket) register(0x41, ClientboundPlayerChatMessagePacket) register(0x42, ClientboundPlayerEndCombatPacket) @@ -134,7 +134,7 @@ internal object GamePacketsProtocolRegistry { register(0x47, ClientboundPlayerLookAtPacket) register(0x48, ClientboundSynchronizePlayerPositionPacket) register(0x49, ClientboundPlayerRotationPacket) -// register(0x4A, ClientboundRecipeBookAddPacket) + register(0x4A, ClientboundRecipeBookAddPacket) register(0x4B, ClientboundRecipeBookRemovePacket) register(0x4C, ClientboundRecipeBookSettingsPacket) register(0x4D, ClientboundRemoveEntitiesPacket) @@ -156,20 +156,20 @@ internal object GamePacketsProtocolRegistry { register(0x5D, ClientboundSetCameraPacket) register(0x5E, ClientboundSetCenterChunkPacket) register(0x5F, ClientboundSetRenderDistancePacket) -// register(0x60, ClientboundSetCursorItemPacket) + register(0x60, ClientboundSetCursorItemPacket) register(0x61, ClientboundSetDefaultSpawnPositionPacket) register(0x62, ClientboundSetDisplayObjectivePacket) // register(0x63, ClientboundSetEntityMetadataPacket) register(0x64, ClientboundLinkEntitiesPacket) register(0x65, ClientboundSetEntityVelocityPacket) -// register(0x66, ClientboundSetEquipmentPacket) + register(0x66, ClientboundSetEquipmentPacket) register(0x67, ClientboundSetExperiencePacket) register(0x68, ClientboundSetHealthPacket) register(0x69, ClientboundSetCarriedItemPacket) register(0x6A, ClientboundUpdateObjectivePacket) register(0x6B, ClientboundSetPassengersPacket) -// register(0x6C, ClientboundSetPlayerInventorySlotPacket) -// register(0x6D, ClientboundSetPlayerTeamPacket) + register(0x6C, ClientboundSetPlayerInventorySlotPacket) + register(0x6D, ClientboundSetPlayerTeamPacket) register(0x6E, ClientboundUpdateScorePacket) register(0x6F, ClientboundSetSimulationDistancePacket) register(0x70, ClientboundSetSubtitleTextPacket) @@ -190,7 +190,7 @@ internal object GamePacketsProtocolRegistry { register(0x7F, ClientboundSetTickingStatePacket) register(0x80, ClientboundStepTickPacket) register(0x81, ClientboundTransferPacket) -// register(0x82, ClientboundUpdateAdvancementsPacket) + register(0x82, ClientboundUpdateAdvancementsPacket) register(0x83, ClientboundUpdateAttributesPacket) register(0x84, ClientboundEntityEffectPacket) register(0x85, ClientboundUpdateRecipesPacket) diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/RecipeDisplayDataRegistry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/RecipeDisplayDataRegistry.kt new file mode 100644 index 0000000..6f0045b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/registry/RecipeDisplayDataRegistry.kt @@ -0,0 +1,26 @@ +/* + * 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.recipe.RecipeDisplayData + +internal object RecipeDisplayDataRegistry : ArrayIndexedRegistry("RecipeDisplayDataRegistry") { + init { + register(RecipeDisplayData.CraftingShapeless) + register(RecipeDisplayData.CraftingShaped) + register(RecipeDisplayData.Furnace) + register(RecipeDisplayData.Stonecutter) + register(RecipeDisplayData.Smithing) + + freeze() + } +} + +internal fun BytesBuffer.readRecipeDisplayData(typeId: Int) = RecipeDisplayDataRegistry.read(typeId, this) +internal fun BytesBuffer.writeRecipeDisplayData(data: RecipeDisplayData) = RecipeDisplayDataRegistry.write(this, data) \ No newline at end of file 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 index 00cf2fe..756774e 100644 --- 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 @@ -10,12 +10,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 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? } @@ -23,13 +21,9 @@ internal interface Registry { 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 @@ -38,20 +32,15 @@ internal open class ArrayIndexedRegistry(private val registryName: Stri 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) } 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 index c8d3f52..ea52d40 100644 --- 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 @@ -18,7 +18,7 @@ internal object SlotDisplayDataRegistry : ArrayIndexedRegistry( register(SlotDisplayData.OnlyWithComponent) register(SlotDisplayData.Item) register(SlotDisplayData.ItemStack) - register(SlotDisplayData.Tags) + register(SlotDisplayData.Tag) register(SlotDisplayData.Dyed) register(SlotDisplayData.SmithingTrim) register(SlotDisplayData.WithRemainder) diff --git a/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt b/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt index b6c0cf2..bbed044 100644 --- a/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt +++ b/libmc-protocol/src/commonTest/kotlin/client/TestClient.kt @@ -9,13 +9,14 @@ package client import cn.rtast.libmc.crypto.AuthenticationProvider import cn.rtast.libmc.protocol.client.createMinecraftClient -import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundContainerSetSlotPacket -import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundExplodePacket -import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundLevelParticlePacket -import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundLightUpdatePacket +import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundAwardStatisticsPacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundPlayerChatMessagePacket +import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSetCursorItemPacket +import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSetHealthPacket import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundStepTickPacket -import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundUpdateRecipesPacket +import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundClientCommandPacket +import cn.rtast.libmc.protocol.packet.status.clientbound.ClientboundStatusResponsePacket +import cn.rtast.libmc.protocol.protocol.game.registry.ClientAction import cn.rtast.libmc.protocol.protocol.session.SessionEvent import cn.rtast.libmc.protocol.protocol.session.onEvent import cn.rtast.libmc.protocol.util.generateOfflineUuid @@ -104,7 +105,10 @@ class TestClient { println(it) } cli.onPacket { chatTracker.onReceivePlayerChat(it.messageSignature) } - cli.onPacket { println(it) } + cli.onPacket { + if (it.health <= 0) sendPacket(ServerboundClientCommandPacket(ClientAction.PerformRespawn)) + } + cli.onPacket { println(it) } // cli.on { packet, direction -> println("$direction -> $packet") } cli.connect() diff --git a/scritps/registries_struct.py b/scritps/registries_struct.py index 35d2644..03c3c75 100644 --- a/scritps/registries_struct.py +++ b/scritps/registries_struct.py @@ -4,7 +4,7 @@ import os path = "./registries.json" with open(path, "r") as f: - data = json.loads(f.read())["minecraft:particle_type"] + data = json.loads(f.read())["minecraft:recipe_display"] sorted_entries = sorted( data.get('entries', {}).items(),