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