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
+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
|
||||
|
||||
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<ServerboundClientCommandPacket> {
|
||||
override fun encode(buffer: BytesBuffer, value: ServerboundClientCommandPacket) {
|
||||
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"
|
||||
|
||||
internal companion object Codec : PacketCodec<Identifier> {
|
||||
fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path")
|
||||
fun of(full: String): Identifier = Identifier(full)
|
||||
public companion object Codec : PacketCodec<Identifier> {
|
||||
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())
|
||||
|
||||
+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
|
||||
|
||||
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>(Empty)
|
||||
public data object AnyFuel : SlotDisplayData, EmptyPacketCodec<Empty>(Empty)
|
||||
public data object Empty : SlotDisplayData, PacketCodec<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 {
|
||||
internal companion object Codec : PacketCodec<WithAnyPotion> {
|
||||
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<Tags> {
|
||||
override fun encode(buffer: BytesBuffer, value: Tags) {
|
||||
public data class Tag(val tag: Identifier) : SlotDisplayData {
|
||||
internal companion object Codec : PacketCodec<Tag> {
|
||||
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<Dyed> {
|
||||
override fun encode(buffer: BytesBuffer, value: Dyed) {
|
||||
buffer.writeSlotDisplay(value.dye)
|
||||
buffer.writeSlotDisplay(value.target)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
+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
|
||||
|
||||
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 <reified T : SessionEvent> Session.onEvent(noinline block: suspend Session.(T) -> Unit) {
|
||||
|
||||
+3
@@ -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 <T : SessionEvent> _onEvent(clazz: KClass<T>, block: suspend Session.(T) -> Unit) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this.eventListener.getOrPut(clazz) { mutableListOf() }.add { block(it as T) }
|
||||
|
||||
+8
-8
@@ -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)
|
||||
|
||||
+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.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import kotlin.concurrent.Volatile
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal interface Registry<T : Any> {
|
||||
fun freeze()
|
||||
fun isFrozen(): Boolean
|
||||
fun getCodec(id: Int): PacketCodec<out T>?
|
||||
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> {
|
||||
private val codecMap = mutableMapOf<Int, PacketCodec<out T>>()
|
||||
private val classToIdMap = mutableMapOf<KClass<out T>, Int>()
|
||||
|
||||
@Volatile
|
||||
private var frozen = false
|
||||
private lateinit var codecArray: Array<PacketCodec<T>>
|
||||
|
||||
fun <U : T> register(kClass: KClass<U>, codec: PacketCodec<U>) {
|
||||
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<T : Any>(private val registryName: Stri
|
||||
inline fun <reified U : T> register(codec: PacketCodec<U>) = 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<T> ?: error("[$registryName] Missing codec for ID: $index")
|
||||
}
|
||||
codecMap.clear()
|
||||
frozen = true
|
||||
}
|
||||
|
||||
override fun isFrozen(): Boolean = frozen
|
||||
|
||||
override fun getCodec(id: Int): PacketCodec<T>? {
|
||||
check(frozen) { "$registryName is not frozen yet!" }
|
||||
return codecArray.getOrNull(id)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ internal object SlotDisplayDataRegistry : ArrayIndexedRegistry<SlotDisplayData>(
|
||||
register(SlotDisplayData.OnlyWithComponent)
|
||||
register(SlotDisplayData.Item)
|
||||
register(SlotDisplayData.ItemStack)
|
||||
register(SlotDisplayData.Tags)
|
||||
register(SlotDisplayData.Tag)
|
||||
register(SlotDisplayData.Dyed)
|
||||
register(SlotDisplayData.SmithingTrim)
|
||||
register(SlotDisplayData.WithRemainder)
|
||||
|
||||
@@ -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<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.connect()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user