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
This commit is contained in:
35 files changed
+895
-87
No files matched your search
@@ -18,18 +18,14 @@ A lightweight minecraft client-side protocol library for Kotlin Native and JVM
|
|||||||
|
|
||||||
# Minecraft Protocol Library Status
|
# 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)
|
- [ ] **Chunk & World Data**: Level Chunk Data with Light decoder (Paletted Containers, Direct/Indirect Palettes)
|
||||||
- [ ] **Light Engine Update**: Sky & Block light nibble array parser
|
- [ ] **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
|
- [ ] **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] `0x21` Disguised Chat Message (`ClientboundDisguisedChatMessagePacket`)
|
||||||
- [x] `0x22` Entity Event (`ClientboundEntityEventPacket`)
|
- [x] `0x22` Entity Event (`ClientboundEntityEventPacket`)
|
||||||
- [x] `0x23` Teleport Entity (`ClientboundTeleportEntityPacket`)
|
- [x] `0x23` Teleport Entity (`ClientboundTeleportEntityPacket`)
|
||||||
- [ ] `0x24` Explode (`ClientboundExplodePacket`)
|
- [x] `0x24` Explode (`ClientboundExplodePacket`)
|
||||||
- [x] `0x25` Unload Chunk (`ClientboundUnloadChunkPacket`)
|
- [x] `0x25` Unload Chunk (`ClientboundUnloadChunkPacket`)
|
||||||
- [x] `0x26` Game Event (`ClientboundGameEventPacket`)
|
- [x] `0x26` Game Event (`ClientboundGameEventPacket`)
|
||||||
- [x] `0x27` Game Rule Values (`ClientboundGameRuleValuesPacket`)
|
- [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`)
|
- [x] `0x2C` Keep Alive (`ClientboundKeepAlivePlayPacket`)
|
||||||
- [ ] `0x2D` Level Chunk Update With Light (`ClientboundLevelChunkUpdateWithLightPacket`)
|
- [ ] `0x2D` Level Chunk Update With Light (`ClientboundLevelChunkUpdateWithLightPacket`)
|
||||||
- [x] `0x2E` Level Event (`ClientboundLevelEventPacket`)
|
- [x] `0x2E` Level Event (`ClientboundLevelEventPacket`)
|
||||||
- [x] `0x2F` Particle (`ClientboundLevelParticlePacket`) **NOTE: Slot data are parsed as raw ByteArray**
|
- [x] `0x2F` Particle (`ClientboundLevelParticlePacket`)
|
||||||
- [ ] `0x30` Light Update (`ClientboundLightUpdatePacket`)
|
- [x] `0x30` Light Update (`ClientboundLightUpdatePacket`)
|
||||||
- [x] `0x31` Login Play (`ClientboundLoginPlayPacket`)
|
- [x] `0x31` Login Play (`ClientboundLoginPlayPacket`)
|
||||||
- [x] `0x32` Low Disk Space Warning (`ClientboundLowDiskSpaceWarningPacket`)
|
- [x] `0x32` Low Disk Space Warning (`ClientboundLowDiskSpaceWarningPacket`)
|
||||||
- [x] `0x33` Map Item Data (`ClientboundMapItemDataPacket`)
|
- [x] `0x33` Map Item Data (`ClientboundMapItemDataPacket`)
|
||||||
- [ ] `0x34` Merchant Offers (`ClientboundMerchantOffersPacket`)
|
- [x] `0x34` Merchant Offers (`ClientboundMerchantOffersPacket`)
|
||||||
- [x] `0x35` Update Entity Position (`ClientboundUpdateEntityPositionPacket`)
|
- [x] `0x35` Update Entity Position (`ClientboundUpdateEntityPositionPacket`)
|
||||||
- [x] `0x36` Update Entity Position and Rotation (`ClientboundUpdateEntityPositionAndRotationPacket`)
|
- [x] `0x36` Update Entity Position and Rotation (`ClientboundUpdateEntityPositionAndRotationPacket`)
|
||||||
- [x] `0x37` Move Minecart Along Track (`ClientboundMoveMinecartAlongTrackPacket`)
|
- [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] `0x3C` Open Sign Editor (`ClientboundOpenSignEditorPacket`)
|
||||||
- [x] `0x3D` Ping (`ClientboundPingPacket`)
|
- [x] `0x3D` Ping (`ClientboundPingPacket`)
|
||||||
- [x] `0x3E` Pong Response (`ClientboundPongResponsePacket`)
|
- [x] `0x3E` Pong Response (`ClientboundPongResponsePacket`)
|
||||||
- [ ] `0x3F` Place Ghost Recipe (`ClientboundPlaceGhostRecipePacket`)
|
- [x] `0x3F` Place Ghost Recipe (`ClientboundPlaceGhostRecipePacket`)
|
||||||
- [x] `0x40` Player Abilities (`ClientboundPlayerAbilitiesPacket`)
|
- [x] `0x40` Player Abilities (`ClientboundPlayerAbilitiesPacket`)
|
||||||
- [x] `0x41` Player Chat Message (`ClientboundPlayerChatMessagePacket`)
|
- [x] `0x41` Player Chat Message (`ClientboundPlayerChatMessagePacket`)
|
||||||
- [x] `0x42` Player End Combat (`ClientboundPlayerEndCombatPacket`)
|
- [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] `0x47` Player Look At (`ClientboundPlayerLookAtPacket`)
|
||||||
- [x] `0x48` Synchronize Player Position (`ClientboundSynchronizePlayerPositionPacket`)
|
- [x] `0x48` Synchronize Player Position (`ClientboundSynchronizePlayerPositionPacket`)
|
||||||
- [x] `0x49` Player Rotation (`ClientboundPlayerRotationPacket`)
|
- [x] `0x49` Player Rotation (`ClientboundPlayerRotationPacket`)
|
||||||
- [ ] `0x4A` Recipe Book Add (`ClientboundRecipeBookAddPacket`)
|
- [x] `0x4A` Recipe Book Add (`ClientboundRecipeBookAddPacket`)
|
||||||
- [x] `0x4B` Recipe Book Remove (`ClientboundRecipeBookRemovePacket`)
|
- [x] `0x4B` Recipe Book Remove (`ClientboundRecipeBookRemovePacket`)
|
||||||
- [x] `0x4C` Recipe Book Settings (`ClientboundRecipeBookSettingsPacket`)
|
- [x] `0x4C` Recipe Book Settings (`ClientboundRecipeBookSettingsPacket`)
|
||||||
- [x] `0x4D` Remove Entities (`ClientboundRemoveEntitiesPacket`)
|
- [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] `0x5D` Set Camera (`ClientboundSetCameraPacket`)
|
||||||
- [x] `0x5E` Set Center Chunk (`ClientboundSetCenterChunkPacket`)
|
- [x] `0x5E` Set Center Chunk (`ClientboundSetCenterChunkPacket`)
|
||||||
- [x] `0x5F` Set Render Distance (`ClientboundSetRenderDistancePacket`)
|
- [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] `0x61` Set Default Spawn Position (`ClientboundSetDefaultSpawnPositionPacket`)
|
||||||
- [x] `0x62` Set Display Objective (`ClientboundSetDisplayObjectivePacket`)
|
- [x] `0x62` Set Display Objective (`ClientboundSetDisplayObjectivePacket`)
|
||||||
- [ ] `0x63` Set Entity Metadata (`ClientboundSetEntityMetadataPacket`)
|
- [ ] `0x63` Set Entity Metadata (`ClientboundSetEntityMetadataPacket`)
|
||||||
- [x] `0x64` Link Entities (`ClientboundLinkEntitiesPacket`)
|
- [x] `0x64` Link Entities (`ClientboundLinkEntitiesPacket`)
|
||||||
- [x] `0x65` Set Entity Velocity (`ClientboundSetEntityVelocityPacket`)
|
- [x] `0x65` Set Entity Velocity (`ClientboundSetEntityVelocityPacket`)
|
||||||
- [ ] `0x66` Set Equipment (`ClientboundSetEquipmentPacket`)
|
- [x] `0x66` Set Equipment (`ClientboundSetEquipmentPacket`)
|
||||||
- [x] `0x67` Set Experience (`ClientboundSetExperiencePacket`)
|
- [x] `0x67` Set Experience (`ClientboundSetExperiencePacket`)
|
||||||
- [x] `0x68` Set Health (`ClientboundSetHealthPacket`)
|
- [x] `0x68` Set Health (`ClientboundSetHealthPacket`)
|
||||||
- [x] `0x69` Set Carried Item (`ClientboundSetCarriedItemPacket`)
|
- [x] `0x69` Set Carried Item (`ClientboundSetCarriedItemPacket`)
|
||||||
- [x] `0x6A` Update Objective (`ClientboundUpdateObjectivePacket`)
|
- [x] `0x6A` Update Objective (`ClientboundUpdateObjectivePacket`)
|
||||||
- [x] `0x6B` Set Passengers (`ClientboundSetPassengersPacket`)
|
- [x] `0x6B` Set Passengers (`ClientboundSetPassengersPacket`)
|
||||||
- [ ] `0x6C` Set Player Inventory Slot (`ClientboundSetPlayerInventorySlotPacket`)
|
- [x] `0x6C` Set Player Inventory Slot (`ClientboundSetPlayerInventorySlotPacket`)
|
||||||
- [ ] `0x6D` Set Player Team (`ClientboundSetPlayerTeamPacket`)
|
- [x] `0x6D` Set Player Team (`ClientboundSetPlayerTeamPacket`)
|
||||||
- [x] `0x6E` Update Score (`ClientboundUpdateScorePacket`)
|
- [x] `0x6E` Update Score (`ClientboundUpdateScorePacket`)
|
||||||
- [x] `0x6F` Set Simulation Distance (`ClientboundSetSimulationDistancePacket`)
|
- [x] `0x6F` Set Simulation Distance (`ClientboundSetSimulationDistancePacket`)
|
||||||
- [x] `0x70` Set Subtitle Text (`ClientboundSetSubtitleTextPacket`)
|
- [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] `0x7F` Set Ticking State (`ClientboundSetTickingStatePacket`)
|
||||||
- [x] `0x80` Step Tick (`ClientboundStepTickPacket`)
|
- [x] `0x80` Step Tick (`ClientboundStepTickPacket`)
|
||||||
- [x] `0x81` Transfer (`ClientboundTransferPacket`)
|
- [x] `0x81` Transfer (`ClientboundTransferPacket`)
|
||||||
- [ ] `0x82` Update Advancements (`ClientboundUpdateAdvancementsPacket`)
|
- [x] `0x82` Update Advancements (`ClientboundUpdateAdvancementsPacket`)
|
||||||
- [x] `0x83` Update Attributes (`ClientboundUpdateAttributesPacket`)
|
- [x] `0x83` Update Attributes (`ClientboundUpdateAttributesPacket`)
|
||||||
- [x] `0x84` Entity Effect (`ClientboundEntityEffectPacket`)
|
- [x] `0x84` Entity Effect (`ClientboundEntityEffectPacket`)
|
||||||
- [ ] `0x85` Update Recipes (`ClientboundUpdateRecipesPacket`)
|
- [x] `0x85` Update Recipes (`ClientboundUpdateRecipesPacket`)
|
||||||
- [x] `0x86` Update Tags (`ClientboundUpdateTagsPacket`)
|
- [x] `0x86` Update Tags (`ClientboundUpdateTagsPacket`)
|
||||||
- [x] `0x87` Projectile Power (`ClientboundProjectilePowerPacket`)
|
- [x] `0x87` Projectile Power (`ClientboundProjectilePowerPacket`)
|
||||||
- [x] `0x88` Custom Report Details (`ClientboundCustomReportDetailsPacket`)
|
- [x] `0x88` Custom Report Details (`ClientboundCustomReportDetailsPacket`)
|
||||||
|
|||||||
+93
@@ -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<MerchantTrade>,
|
||||||
|
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<Int, DataComponent>,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal companion object Codec : PacketCodec<ClientboundMerchantOffersPacket> {
|
||||||
|
private fun BytesBuffer.readTradeItem(): TradeItem {
|
||||||
|
val itemId = readVarInt()
|
||||||
|
val itemCount = readVarInt()
|
||||||
|
val componentCount = readVarInt()
|
||||||
|
val components = HashMap<Int, DataComponent>(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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -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<ClientboundPlaceGhostRecipePacket> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -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<RecipeEntry>, val replace: Boolean) :
|
||||||
|
MinecraftPacket {
|
||||||
|
internal companion object Codec : PacketCodec<ClientboundRecipeBookAddPacket> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -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<ClientboundSetCursorItemPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundSetCursorItemPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundSetCursorItemPacket =
|
||||||
|
ClientboundSetCursorItemPacket(buffer.readSlot())
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -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<EntityEquipment>) :
|
||||||
|
MinecraftPacket {
|
||||||
|
internal companion object Codec : PacketCodec<ClientboundSetEquipmentPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundSetEquipmentPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundSetEquipmentPacket {
|
||||||
|
val entityId = buffer.readVarInt()
|
||||||
|
val equipments = ArrayList<EntityEquipment>()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -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<ClientboundSetPlayerInventorySlotPacket> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -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<ClientboundSetPlayerTeamPacket> {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -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<Identifier, Advancement>,
|
||||||
|
/**
|
||||||
|
* The identifiers of the advancements that should be removed
|
||||||
|
*/
|
||||||
|
val identifiers: List<Identifier>,
|
||||||
|
val progressMapping: Map<Identifier, AdvancementProgress>,
|
||||||
|
val showAdvancements: Boolean,
|
||||||
|
) : MinecraftPacket {
|
||||||
|
internal companion object Codec : PacketCodec<ClientboundUpdateAdvancementsPacket> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: ClientboundUpdateAdvancementsPacket) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): ClientboundUpdateAdvancementsPacket {
|
||||||
|
val reset = buffer.readBoolean()
|
||||||
|
val mappingCount = buffer.readVarInt()
|
||||||
|
val advancementMapping = HashMap<Identifier, Advancement>(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<Identifier, AdvancementProgress>(progressMappingCount)
|
||||||
|
repeat(progressMappingCount) {
|
||||||
|
val key = buffer.readIdentifier()
|
||||||
|
val criteriaCount = buffer.readVarInt()
|
||||||
|
val criteria = HashMap<Identifier, Long?>(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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-25
@@ -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<ServerboundClientActionPacket> {
|
|
||||||
override fun encode(buffer: BytesBuffer, value: ServerboundClientActionPacket) {
|
|
||||||
buffer.writeVarInt(value.action.actionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun decode(buffer: BytesBuffer): ServerboundClientActionPacket =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-3
@@ -8,12 +8,12 @@
|
|||||||
package cn.rtast.libmc.protocol.packet.play.serverbound
|
package cn.rtast.libmc.protocol.packet.play.serverbound
|
||||||
|
|
||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
import cn.rtast.libmc.packet.PacketCodec
|
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
import cn.rtast.libmc.packet.MinecraftPacket
|
||||||
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
import cn.rtast.libmc.primitives.writeVarInt
|
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<ServerboundClientCommandPacket> {
|
internal companion object Codec : PacketCodec<ServerboundClientCommandPacket> {
|
||||||
override fun encode(buffer: BytesBuffer, value: ServerboundClientCommandPacket) {
|
override fun encode(buffer: BytesBuffer, value: ServerboundClientCommandPacket) {
|
||||||
buffer.writeVarInt(value.action.id)
|
buffer.writeVarInt(value.action.id)
|
||||||
|
|||||||
+3
-3
@@ -23,9 +23,9 @@ public value class Identifier internal constructor(public val raw: String) {
|
|||||||
|
|
||||||
override fun toString(): String = "$namespace:$path"
|
override fun toString(): String = "$namespace:$path"
|
||||||
|
|
||||||
internal companion object Codec : PacketCodec<Identifier> {
|
public companion object Codec : PacketCodec<Identifier> {
|
||||||
fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path")
|
public fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path")
|
||||||
fun of(full: String): Identifier = Identifier(full)
|
public fun of(full: String): Identifier = Identifier(full)
|
||||||
|
|
||||||
override fun encode(buffer: BytesBuffer, value: Identifier) {
|
override fun encode(buffer: BytesBuffer, value: Identifier) {
|
||||||
buffer.writeMcString(value.toString())
|
buffer.writeMcString(value.toString())
|
||||||
|
|||||||
+44
@@ -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<List<String>>,
|
||||||
|
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)
|
||||||
|
}
|
||||||
+27
@@ -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
|
||||||
|
}
|
||||||
+16
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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<Identifier, Long?>)
|
||||||
+16
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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)
|
||||||
+23
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-7
@@ -8,7 +8,6 @@
|
|||||||
package cn.rtast.libmc.protocol.protocol.game.item.slot
|
package cn.rtast.libmc.protocol.protocol.game.item.slot
|
||||||
|
|
||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
import cn.rtast.libmc.packet.EmptyPacketCodec
|
|
||||||
import cn.rtast.libmc.packet.PacketCodec
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
import cn.rtast.libmc.primitives.readVarInt
|
import cn.rtast.libmc.primitives.readVarInt
|
||||||
import cn.rtast.libmc.primitives.writePrefixed
|
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
|
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
|
||||||
|
|
||||||
public sealed interface SlotDisplayData {
|
public sealed interface SlotDisplayData {
|
||||||
public data object Empty : SlotDisplayData, EmptyPacketCodec<Empty>(Empty)
|
public data object Empty : SlotDisplayData, PacketCodec<Empty> {
|
||||||
public data object AnyFuel : SlotDisplayData, EmptyPacketCodec<Empty>(Empty)
|
override fun encode(buffer: BytesBuffer, value: Empty) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): Empty = Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
public data object AnyFuel : SlotDisplayData, PacketCodec<AnyFuel> {
|
||||||
|
override fun encode(buffer: BytesBuffer, value: AnyFuel) {}
|
||||||
|
override fun decode(buffer: BytesBuffer): AnyFuel = AnyFuel
|
||||||
|
}
|
||||||
|
|
||||||
public data class WithAnyPotion(val base: SlotDisplay) : SlotDisplayData {
|
public data class WithAnyPotion(val base: SlotDisplay) : SlotDisplayData {
|
||||||
internal companion object Codec : PacketCodec<WithAnyPotion> {
|
internal companion object Codec : PacketCodec<WithAnyPotion> {
|
||||||
override fun encode(buffer: BytesBuffer, value: WithAnyPotion) {
|
override fun encode(buffer: BytesBuffer, value: WithAnyPotion) {
|
||||||
@@ -65,13 +72,13 @@ public sealed interface SlotDisplayData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public data class Tags(val tag: Identifier) : SlotDisplayData {
|
public data class Tag(val tag: Identifier) : SlotDisplayData {
|
||||||
internal companion object Codec : PacketCodec<Tags> {
|
internal companion object Codec : PacketCodec<Tag> {
|
||||||
override fun encode(buffer: BytesBuffer, value: Tags) {
|
override fun encode(buffer: BytesBuffer, value: Tag) {
|
||||||
buffer.writeIdentifier(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<Dyed> {
|
internal companion object Codec : PacketCodec<Dyed> {
|
||||||
override fun encode(buffer: BytesBuffer, value: Dyed) {
|
override fun encode(buffer: BytesBuffer, value: Dyed) {
|
||||||
buffer.writeSlotDisplay(value.dye)
|
buffer.writeSlotDisplay(value.dye)
|
||||||
|
buffer.writeSlotDisplay(value.target)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decode(buffer: BytesBuffer): Dyed {
|
override fun decode(buffer: BytesBuffer): Dyed {
|
||||||
|
|||||||
+27
@@ -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)
|
||||||
|
}
|
||||||
+154
@@ -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<SlotDisplay>,
|
||||||
|
override val result: SlotDisplay,
|
||||||
|
override val craftingStation: SlotDisplay,
|
||||||
|
) : RecipeDisplayData {
|
||||||
|
internal companion object Codec : PacketCodec<CraftingShapeless> {
|
||||||
|
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<SlotDisplay>(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<SlotDisplay>,
|
||||||
|
override val result: SlotDisplay,
|
||||||
|
override val craftingStation: SlotDisplay,
|
||||||
|
) : RecipeDisplayData {
|
||||||
|
internal companion object Codec : PacketCodec<CraftingShaped> {
|
||||||
|
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<SlotDisplay>(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<Furnace> {
|
||||||
|
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<Stonecutter> {
|
||||||
|
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<Smithing> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -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<IdSet>?,
|
||||||
|
val flags: Byte
|
||||||
|
) {
|
||||||
|
val showNotification: Boolean get() = (flags.toInt() and 0x01) != 0
|
||||||
|
val highlightAsNew: Boolean get() = (flags.toInt() and 0x02) != 0
|
||||||
|
}
|
||||||
+2
-2
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.protocol.protocol.game.registry
|
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.
|
* Sent when the client is ready to respawn after death.
|
||||||
*/
|
*/
|
||||||
@@ -24,6 +24,6 @@ public enum class ClientAction(public val actionID: Int) {
|
|||||||
RequestGameRuleValues(2);
|
RequestGameRuleValues(2);
|
||||||
|
|
||||||
public companion object {
|
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+19
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -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<String>,
|
||||||
|
) : 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<String>) : TeamAction {
|
||||||
|
override val methodId: Int = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public data class RemoveEntities(val entities: List<String>) : 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
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.protocol.protocol.session
|
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.client.CURRENT_MINECRAFT_PROTOCOL_VERSION
|
||||||
import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent
|
import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent
|
||||||
import kotlin.reflect.KClass
|
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 status(protocolVersion: Int = CURRENT_MINECRAFT_PROTOCOL_VERSION): String
|
||||||
public suspend fun disconnect()
|
public suspend fun disconnect()
|
||||||
public suspend fun init()
|
public suspend fun init()
|
||||||
|
public suspend fun sendPacket(packet: MinecraftPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <reified T : SessionEvent> Session.onEvent(noinline block: suspend Session.(T) -> Unit) {
|
public inline fun <reified T : SessionEvent> Session.onEvent(noinline block: suspend Session.(T) -> Unit) {
|
||||||
|
|||||||
+3
@@ -1,5 +1,6 @@
|
|||||||
package cn.rtast.libmc.protocol.protocol.session
|
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.client.MinecraftClient
|
||||||
import cn.rtast.libmc.protocol.crypto.minecraftServerIdHash
|
import cn.rtast.libmc.protocol.crypto.minecraftServerIdHash
|
||||||
import cn.rtast.libmc.protocol.crypto.rsaEncrypt
|
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 <T : SessionEvent> _onEvent(clazz: KClass<T>, block: suspend Session.(T) -> Unit) {
|
override fun <T : SessionEvent> _onEvent(clazz: KClass<T>, block: suspend Session.(T) -> Unit) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
this.eventListener.getOrPut(clazz) { mutableListOf() }.add { block(it as T) }
|
this.eventListener.getOrPut(clazz) { mutableListOf() }.add { block(it as T) }
|
||||||
|
|||||||
+8
-8
@@ -112,7 +112,7 @@ internal object GamePacketsProtocolRegistry {
|
|||||||
register(0x31, ClientboundLoginPlayPacket)
|
register(0x31, ClientboundLoginPlayPacket)
|
||||||
register(0x32, ClientboundLowDiskSpaceWarningPacket)
|
register(0x32, ClientboundLowDiskSpaceWarningPacket)
|
||||||
register(0x33, ClientboundMapItemDataPacket)
|
register(0x33, ClientboundMapItemDataPacket)
|
||||||
// register(0x34, ClientboundMerchantOffersPacket)
|
register(0x34, ClientboundMerchantOffersPacket)
|
||||||
register(0x35, ClientboundUpdateEntityPositionPacket)
|
register(0x35, ClientboundUpdateEntityPositionPacket)
|
||||||
register(0x36, ClientboundUpdateEntityPositionAndRotationPacket)
|
register(0x36, ClientboundUpdateEntityPositionAndRotationPacket)
|
||||||
register(0x37, ClientboundMoveMinecartAlongTrackPacket)
|
register(0x37, ClientboundMoveMinecartAlongTrackPacket)
|
||||||
@@ -123,7 +123,7 @@ internal object GamePacketsProtocolRegistry {
|
|||||||
register(0x3C, ClientboundOpenSignEditorPacket)
|
register(0x3C, ClientboundOpenSignEditorPacket)
|
||||||
register(0x3D, ClientboundPingPacket)
|
register(0x3D, ClientboundPingPacket)
|
||||||
register(0x3E, ClientboundPongResponsePacket)
|
register(0x3E, ClientboundPongResponsePacket)
|
||||||
// register(0x3F, ClientboundPlaceGhostRecipePacket)
|
register(0x3F, ClientboundPlaceGhostRecipePacket)
|
||||||
register(0x40, ClientboundPlayerAbilitiesPacket)
|
register(0x40, ClientboundPlayerAbilitiesPacket)
|
||||||
register(0x41, ClientboundPlayerChatMessagePacket)
|
register(0x41, ClientboundPlayerChatMessagePacket)
|
||||||
register(0x42, ClientboundPlayerEndCombatPacket)
|
register(0x42, ClientboundPlayerEndCombatPacket)
|
||||||
@@ -134,7 +134,7 @@ internal object GamePacketsProtocolRegistry {
|
|||||||
register(0x47, ClientboundPlayerLookAtPacket)
|
register(0x47, ClientboundPlayerLookAtPacket)
|
||||||
register(0x48, ClientboundSynchronizePlayerPositionPacket)
|
register(0x48, ClientboundSynchronizePlayerPositionPacket)
|
||||||
register(0x49, ClientboundPlayerRotationPacket)
|
register(0x49, ClientboundPlayerRotationPacket)
|
||||||
// register(0x4A, ClientboundRecipeBookAddPacket)
|
register(0x4A, ClientboundRecipeBookAddPacket)
|
||||||
register(0x4B, ClientboundRecipeBookRemovePacket)
|
register(0x4B, ClientboundRecipeBookRemovePacket)
|
||||||
register(0x4C, ClientboundRecipeBookSettingsPacket)
|
register(0x4C, ClientboundRecipeBookSettingsPacket)
|
||||||
register(0x4D, ClientboundRemoveEntitiesPacket)
|
register(0x4D, ClientboundRemoveEntitiesPacket)
|
||||||
@@ -156,20 +156,20 @@ internal object GamePacketsProtocolRegistry {
|
|||||||
register(0x5D, ClientboundSetCameraPacket)
|
register(0x5D, ClientboundSetCameraPacket)
|
||||||
register(0x5E, ClientboundSetCenterChunkPacket)
|
register(0x5E, ClientboundSetCenterChunkPacket)
|
||||||
register(0x5F, ClientboundSetRenderDistancePacket)
|
register(0x5F, ClientboundSetRenderDistancePacket)
|
||||||
// register(0x60, ClientboundSetCursorItemPacket)
|
register(0x60, ClientboundSetCursorItemPacket)
|
||||||
register(0x61, ClientboundSetDefaultSpawnPositionPacket)
|
register(0x61, ClientboundSetDefaultSpawnPositionPacket)
|
||||||
register(0x62, ClientboundSetDisplayObjectivePacket)
|
register(0x62, ClientboundSetDisplayObjectivePacket)
|
||||||
// register(0x63, ClientboundSetEntityMetadataPacket)
|
// register(0x63, ClientboundSetEntityMetadataPacket)
|
||||||
register(0x64, ClientboundLinkEntitiesPacket)
|
register(0x64, ClientboundLinkEntitiesPacket)
|
||||||
register(0x65, ClientboundSetEntityVelocityPacket)
|
register(0x65, ClientboundSetEntityVelocityPacket)
|
||||||
// register(0x66, ClientboundSetEquipmentPacket)
|
register(0x66, ClientboundSetEquipmentPacket)
|
||||||
register(0x67, ClientboundSetExperiencePacket)
|
register(0x67, ClientboundSetExperiencePacket)
|
||||||
register(0x68, ClientboundSetHealthPacket)
|
register(0x68, ClientboundSetHealthPacket)
|
||||||
register(0x69, ClientboundSetCarriedItemPacket)
|
register(0x69, ClientboundSetCarriedItemPacket)
|
||||||
register(0x6A, ClientboundUpdateObjectivePacket)
|
register(0x6A, ClientboundUpdateObjectivePacket)
|
||||||
register(0x6B, ClientboundSetPassengersPacket)
|
register(0x6B, ClientboundSetPassengersPacket)
|
||||||
// register(0x6C, ClientboundSetPlayerInventorySlotPacket)
|
register(0x6C, ClientboundSetPlayerInventorySlotPacket)
|
||||||
// register(0x6D, ClientboundSetPlayerTeamPacket)
|
register(0x6D, ClientboundSetPlayerTeamPacket)
|
||||||
register(0x6E, ClientboundUpdateScorePacket)
|
register(0x6E, ClientboundUpdateScorePacket)
|
||||||
register(0x6F, ClientboundSetSimulationDistancePacket)
|
register(0x6F, ClientboundSetSimulationDistancePacket)
|
||||||
register(0x70, ClientboundSetSubtitleTextPacket)
|
register(0x70, ClientboundSetSubtitleTextPacket)
|
||||||
@@ -190,7 +190,7 @@ internal object GamePacketsProtocolRegistry {
|
|||||||
register(0x7F, ClientboundSetTickingStatePacket)
|
register(0x7F, ClientboundSetTickingStatePacket)
|
||||||
register(0x80, ClientboundStepTickPacket)
|
register(0x80, ClientboundStepTickPacket)
|
||||||
register(0x81, ClientboundTransferPacket)
|
register(0x81, ClientboundTransferPacket)
|
||||||
// register(0x82, ClientboundUpdateAdvancementsPacket)
|
register(0x82, ClientboundUpdateAdvancementsPacket)
|
||||||
register(0x83, ClientboundUpdateAttributesPacket)
|
register(0x83, ClientboundUpdateAttributesPacket)
|
||||||
register(0x84, ClientboundEntityEffectPacket)
|
register(0x84, ClientboundEntityEffectPacket)
|
||||||
register(0x85, ClientboundUpdateRecipesPacket)
|
register(0x85, ClientboundUpdateRecipesPacket)
|
||||||
|
|||||||
+26
@@ -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<RecipeDisplayData>("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)
|
||||||
@@ -10,12 +10,10 @@ package cn.rtast.libmc.protocol.registry
|
|||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
import cn.rtast.libmc.packet.PacketCodec
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
import cn.rtast.libmc.primitives.writeVarInt
|
import cn.rtast.libmc.primitives.writeVarInt
|
||||||
import kotlin.concurrent.Volatile
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
internal interface Registry<T : Any> {
|
internal interface Registry<T : Any> {
|
||||||
fun freeze()
|
fun freeze()
|
||||||
fun isFrozen(): Boolean
|
|
||||||
fun getCodec(id: Int): PacketCodec<out T>?
|
fun getCodec(id: Int): PacketCodec<out T>?
|
||||||
fun getId(kClass: KClass<out T>): Int?
|
fun getId(kClass: KClass<out T>): Int?
|
||||||
}
|
}
|
||||||
@@ -23,13 +21,9 @@ internal interface Registry<T : Any> {
|
|||||||
internal open class ArrayIndexedRegistry<T : Any>(private val registryName: String = "Registry") : Registry<T> {
|
internal open class ArrayIndexedRegistry<T : Any>(private val registryName: String = "Registry") : Registry<T> {
|
||||||
private val codecMap = mutableMapOf<Int, PacketCodec<out T>>()
|
private val codecMap = mutableMapOf<Int, PacketCodec<out T>>()
|
||||||
private val classToIdMap = mutableMapOf<KClass<out T>, Int>()
|
private val classToIdMap = mutableMapOf<KClass<out T>, Int>()
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var frozen = false
|
|
||||||
private lateinit var codecArray: Array<PacketCodec<T>>
|
private lateinit var codecArray: Array<PacketCodec<T>>
|
||||||
|
|
||||||
fun <U : T> register(kClass: KClass<U>, codec: PacketCodec<U>) {
|
fun <U : T> register(kClass: KClass<U>, codec: PacketCodec<U>) {
|
||||||
check(!frozen) { "$registryName is frozen and cannot accept new registrations." }
|
|
||||||
val id = codecMap.size
|
val id = codecMap.size
|
||||||
codecMap[id] = codec
|
codecMap[id] = codec
|
||||||
classToIdMap[kClass] = id
|
classToIdMap[kClass] = id
|
||||||
@@ -38,20 +32,15 @@ internal open class ArrayIndexedRegistry<T : Any>(private val registryName: Stri
|
|||||||
inline fun <reified U : T> register(codec: PacketCodec<U>) = register(U::class, codec)
|
inline fun <reified U : T> register(codec: PacketCodec<U>) = register(U::class, codec)
|
||||||
|
|
||||||
override fun freeze() {
|
override fun freeze() {
|
||||||
if (frozen) return
|
|
||||||
val maxId = codecMap.keys.maxOrNull() ?: -1
|
val maxId = codecMap.keys.maxOrNull() ?: -1
|
||||||
codecArray = Array(maxId + 1) { index ->
|
codecArray = Array(maxId + 1) { index ->
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
codecMap[index] as? PacketCodec<T> ?: error("[$registryName] Missing codec for ID: $index")
|
codecMap[index] as? PacketCodec<T> ?: error("[$registryName] Missing codec for ID: $index")
|
||||||
}
|
}
|
||||||
codecMap.clear()
|
codecMap.clear()
|
||||||
frozen = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isFrozen(): Boolean = frozen
|
|
||||||
|
|
||||||
override fun getCodec(id: Int): PacketCodec<T>? {
|
override fun getCodec(id: Int): PacketCodec<T>? {
|
||||||
check(frozen) { "$registryName is not frozen yet!" }
|
|
||||||
return codecArray.getOrNull(id)
|
return codecArray.getOrNull(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ internal object SlotDisplayDataRegistry : ArrayIndexedRegistry<SlotDisplayData>(
|
|||||||
register(SlotDisplayData.OnlyWithComponent)
|
register(SlotDisplayData.OnlyWithComponent)
|
||||||
register(SlotDisplayData.Item)
|
register(SlotDisplayData.Item)
|
||||||
register(SlotDisplayData.ItemStack)
|
register(SlotDisplayData.ItemStack)
|
||||||
register(SlotDisplayData.Tags)
|
register(SlotDisplayData.Tag)
|
||||||
register(SlotDisplayData.Dyed)
|
register(SlotDisplayData.Dyed)
|
||||||
register(SlotDisplayData.SmithingTrim)
|
register(SlotDisplayData.SmithingTrim)
|
||||||
register(SlotDisplayData.WithRemainder)
|
register(SlotDisplayData.WithRemainder)
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ package client
|
|||||||
|
|
||||||
import cn.rtast.libmc.crypto.AuthenticationProvider
|
import cn.rtast.libmc.crypto.AuthenticationProvider
|
||||||
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
||||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundContainerSetSlotPacket
|
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundAwardStatisticsPacket
|
||||||
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.ClientboundPlayerChatMessagePacket
|
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.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.SessionEvent
|
||||||
import cn.rtast.libmc.protocol.protocol.session.onEvent
|
import cn.rtast.libmc.protocol.protocol.session.onEvent
|
||||||
import cn.rtast.libmc.protocol.util.generateOfflineUuid
|
import cn.rtast.libmc.protocol.util.generateOfflineUuid
|
||||||
@@ -104,7 +105,10 @@ class TestClient {
|
|||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
cli.onPacket<ClientboundPlayerChatMessagePacket> { chatTracker.onReceivePlayerChat(it.messageSignature) }
|
cli.onPacket<ClientboundPlayerChatMessagePacket> { chatTracker.onReceivePlayerChat(it.messageSignature) }
|
||||||
cli.onPacket<ClientboundUpdateRecipesPacket> { println(it) }
|
cli.onPacket<ClientboundSetHealthPacket> {
|
||||||
|
if (it.health <= 0) sendPacket(ServerboundClientCommandPacket(ClientAction.PerformRespawn))
|
||||||
|
}
|
||||||
|
cli.onPacket<ClientboundSetCursorItemPacket> { println(it) }
|
||||||
// cli.on { packet, direction -> println("$direction -> $packet") }
|
// cli.on { packet, direction -> println("$direction -> $packet") }
|
||||||
cli.connect()
|
cli.connect()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import os
|
|||||||
path = "./registries.json"
|
path = "./registries.json"
|
||||||
|
|
||||||
with open(path, "r") as f:
|
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(
|
sorted_entries = sorted(
|
||||||
data.get('entries', {}).items(),
|
data.get('entries', {}).items(),
|
||||||
|
|||||||
Reference in New Issue
Block a user