Support TextComponent AST parser and command binary graph AST parser
This commit is contained in:
46 files changed
+1114
-205
No files matched your search
@@ -22,16 +22,15 @@ A lightweight minecraft client-side protocol library and related library, includ
|
||||
## Implementation details
|
||||
|
||||
- [x] **Online Mode Authentication & Encryption**: See [Implement Encryption](docs/implement-encryption.md)
|
||||
- [ ] **Structured `TextComponent` Parser**: Rich Chat Component AST decoder (currently falling back to raw
|
||||
`NBTCompound`).
|
||||
- [ ] **Command Tree Parser (0x10)**: Full binary graph decoder for brigadier nodes, argument types, and suggestions.
|
||||
- [x] **Structured `TextComponent` Parser**: TextComponent AST decoder (Some packets fallback to Raw NBTCompound)
|
||||
- [x] **Command Tree Parser (0x10)**: Full binary graph decoder for brigadier nodes, argument types, and suggestions
|
||||
- [ ] **Recipe Book & Recipe Data (0x3F, 0x4A, 0x4B, 0x4C, 0x85)**: Recipe layout declarations and client-side recipe
|
||||
settings.
|
||||
settings
|
||||
- [ ] **Chunk & World Data (0x2D)**: Level Chunk Data with Light decoder (Bitsets, Paletted Containers, Direct/Indirect
|
||||
Palettes).
|
||||
- [ ] **Light Engine Update (0x30)**: Sky & Block light nibble array parser.
|
||||
- [ ] **Explosion Event Decoder (0x24)**: Knockback vectors and destroyed block offsets array.
|
||||
- [ ] **Debug Packets Parsing (0x1A - 0x1E)**: Debug subs, block/entity states, and game performance sample events.
|
||||
Palettes)
|
||||
- [ ] **Light Engine Update (0x30)**: Sky & Block light nibble array parser
|
||||
- [ ] **Explosion Event Decoder (0x24)**: Knockback vectors and destroyed block offsets array
|
||||
- [ ] **Debug Packets Parsing (0x1A - 0x1E)**: Debug subs, block/entity states, and game performance sample events
|
||||
- [ ] **Particle Parsing**
|
||||
- [ ] **Slot Data Parsing**
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ public data class ProtocolContext(
|
||||
val rsaEncryptor: RSA1024Encryptor,
|
||||
val sha1Hasher: Sha1Hasher,
|
||||
val cipherFactory: (sharedKey: ByteArray) -> NetworkCipher,
|
||||
val authProvider: AuthenticationProvider,
|
||||
val authProvider: AuthenticationProvider?,
|
||||
)
|
||||
|
||||
public class ProtocolContextBuilder(private val onlineMode: Boolean) {
|
||||
@@ -25,8 +25,9 @@ public class ProtocolContextBuilder(private val onlineMode: Boolean) {
|
||||
rsaEncryptor = if (::rsaEncryptor.isInitialized) rsaEncryptor else error("rsaEncryptor is required"),
|
||||
sha1Hasher = if (::sha1Hasher.isInitialized) sha1Hasher else error("sha1Hasher is required"),
|
||||
cipherFactory = if (::cipherFactory.isInitialized) cipherFactory else error("cipherFactory is required"),
|
||||
authProvider = if (::authProvider.isInitialized) if (onlineMode) authProvider
|
||||
else error("authProvider is required") else error("authProvider is required")
|
||||
authProvider = if (onlineMode) {
|
||||
if (::authProvider.isInitialized) authProvider else error("authProvider is required in online mode")
|
||||
} else if (::authProvider.isInitialized) authProvider else null
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket
|
||||
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundLoginStartPacket
|
||||
import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent
|
||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
||||
import cn.rtast.libmc.protocol.session.TransactionIdManager
|
||||
import cn.rtast.libmc.protocol.util.TransactionIdManager
|
||||
import cn.rtast.libmc.protocol.util.generateOfflineUuid
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ import cn.rtast.libmc.protocol.util.generateRandom16Bytes
|
||||
*/
|
||||
public class InternalPacketDispatcher(
|
||||
private val client: MinecraftClient,
|
||||
private val authProvider: AuthenticationProvider,
|
||||
private val authProvider: AuthenticationProvider?,
|
||||
) {
|
||||
public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
|
||||
when (packet) {
|
||||
@@ -51,7 +51,7 @@ public class InternalPacketDispatcher(
|
||||
val sharedSecret = generateRandom16Bytes()
|
||||
if (client.isOnlineMode) {
|
||||
val serverHash = client.serverIdHasher.hash(packet.serverId, sharedSecret, packet.publicKey)
|
||||
authProvider.joinServer(
|
||||
authProvider!!.joinServer(
|
||||
"https://sessionserver.mojang.com/session/minecraft/join",
|
||||
client.accessToken!!,
|
||||
client.uuid.toString().replace("-", ""),
|
||||
|
||||
+4
-4
@@ -12,9 +12,9 @@ import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readUuid
|
||||
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.stream.BytesBuffer
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public data class ClientboundAddResourcePackPacket(
|
||||
@@ -22,7 +22,7 @@ public data class ClientboundAddResourcePackPacket(
|
||||
val url: String,
|
||||
val hash: String,
|
||||
val forced: Boolean,
|
||||
val prompt: NBTCompound,
|
||||
val prompt: TextComponent,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundAddResourcePackPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundAddResourcePackPacket) {}
|
||||
@@ -32,7 +32,7 @@ public data class ClientboundAddResourcePackPacket(
|
||||
val hash = buffer.readMcString()
|
||||
val forced = buffer.readBoolean()
|
||||
buffer.readVarInt() // ?
|
||||
val prompt = buffer.readNetworkNBTCompound() // ?
|
||||
val prompt = buffer.readTextComponent() // ?
|
||||
return ClientboundAddResourcePackPacket(uuid, url, hash, forced, prompt)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.configuration.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundDisconnectConfigurationPacket(val reason: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundDisconnectConfigurationPacket(val reason: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundDisconnectConfigurationPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket {
|
||||
return ClientboundDisconnectConfigurationPacket(reason = buffer.readNetworkNBTCompound())
|
||||
return ClientboundDisconnectConfigurationPacket(reason = buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.protocol.registry.report.ServerLink
|
||||
import cn.rtast.libmc.protocol.protocol.game.registry.report.ServerLink
|
||||
|
||||
public data class ClientboundServerLinksPacket(val links: List<ServerLink>) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundServerLinksPacket> {
|
||||
|
||||
+5
-5
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.login.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundDisconnectLoginPacket(val reason: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundDisconnectLoginPacket(val reason: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundDisconnectLoginPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket {
|
||||
return ClientboundDisconnectLoginPacket(reason = buffer.readNetworkNBTCompound())
|
||||
return ClientboundDisconnectLoginPacket(reason = buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readUuid
|
||||
import cn.rtast.libmc.protocol.session.GameProfile
|
||||
import cn.rtast.libmc.protocol.protocol.game.session.GameProfile
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public data class ClientboundLoginSuccessPacket(val gameProfile: GameProfile, val sessionId: Uuid) :
|
||||
|
||||
+6
-4
@@ -7,7 +7,6 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readUuid
|
||||
@@ -16,7 +15,8 @@ import cn.rtast.libmc.protocol.protocol.game.bossbar.BossBarAction
|
||||
import cn.rtast.libmc.protocol.protocol.game.bossbar.BossBarColor
|
||||
import cn.rtast.libmc.protocol.protocol.game.bossbar.BossBarDivision
|
||||
import cn.rtast.libmc.protocol.protocol.game.bossbar.BossBarFlags
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public data class ClientboundBossEventPacket(val uuid: Uuid, val action: BossBarAction) : MinecraftPacket {
|
||||
@@ -26,7 +26,7 @@ public data class ClientboundBossEventPacket(val uuid: Uuid, val action: BossBar
|
||||
val uuid = buffer.readUuid()
|
||||
val action = when (val actionId = buffer.readVarInt()) {
|
||||
BossBarAction.ADD_ID -> {
|
||||
val title = buffer.readNetworkNBTCompound()
|
||||
val title = buffer.readTextComponent()
|
||||
val health = buffer.readFloat()
|
||||
val color = BossBarColor.fromID(buffer.readVarInt())
|
||||
val division = BossBarDivision.fromID(buffer.readVarInt())
|
||||
@@ -35,7 +35,7 @@ public data class ClientboundBossEventPacket(val uuid: Uuid, val action: BossBar
|
||||
}
|
||||
|
||||
BossBarAction.REMOVE_ID -> BossBarAction.Remove
|
||||
BossBarAction.UPDATE_TITLE_ID -> BossBarAction.UpdateTitle(buffer.readNetworkNBTCompound())
|
||||
BossBarAction.UPDATE_TITLE_ID -> BossBarAction.UpdateTitle(buffer.readTextComponent())
|
||||
BossBarAction.UPDATE_STYLE_ID -> {
|
||||
val color = BossBarColor.fromID(buffer.readVarInt())
|
||||
val division = BossBarDivision.fromID(buffer.readVarInt())
|
||||
@@ -46,6 +46,8 @@ public data class ClientboundBossEventPacket(val uuid: Uuid, val action: BossBar
|
||||
BossBarAction.UpdateFlags(BossBarFlags.fromBitmask(buffer.readByte().toInt() and 0xFF))
|
||||
}
|
||||
|
||||
BossBarAction.UPDATE_HEALTH_ID -> BossBarAction.UpdateHealth(buffer.readFloat())
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown action $actionId")
|
||||
}
|
||||
return ClientboundBossEventPacket(uuid, action)
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.registry.GameDifficulty
|
||||
import cn.rtast.libmc.protocol.protocol.game.registry.GameDifficulty
|
||||
|
||||
public data class ClientboundChangeDifficultyPacket(val difficulty: GameDifficulty, val locked: Boolean) :
|
||||
MinecraftPacket {
|
||||
|
||||
+5
-12
@@ -11,11 +11,9 @@ import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeMcString
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.writeNetworkNBTCompound
|
||||
|
||||
public data class ClientboundCommandSuggestionsPacket(
|
||||
val id: Int,
|
||||
@@ -23,18 +21,13 @@ public data class ClientboundCommandSuggestionsPacket(
|
||||
val length: Int,
|
||||
val matches: List<CommandSuggestionMatch>,
|
||||
) : MinecraftPacket {
|
||||
public data class CommandSuggestionMatch(val match: String, val tooltip: NBTCompound?) {
|
||||
public data class CommandSuggestionMatch(val match: String, val tooltip: TextComponent?) {
|
||||
internal companion object Codec : PacketCodec<CommandSuggestionMatch> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: CommandSuggestionMatch) {
|
||||
buffer.writeMcString(value.match)
|
||||
buffer.writeBoolean(value.tooltip != null)
|
||||
if (value.tooltip != null) buffer.writeNetworkNBTCompound(value.tooltip)
|
||||
}
|
||||
|
||||
override suspend fun encode(buffer: BytesBuffer, value: CommandSuggestionMatch) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): CommandSuggestionMatch {
|
||||
val match = buffer.readMcString()
|
||||
val hasTooltip = buffer.readBoolean()
|
||||
val tooltip = if (hasTooltip) buffer.readNetworkNBTCompound() else null
|
||||
val tooltip = if (hasTooltip) buffer.readTextComponent() else null
|
||||
return CommandSuggestionMatch(match, tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
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.command.CommandNode
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundCommandsPacket(val nodes: List<CommandNode>, val rootIndex: Int) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundCommandsPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundCommandsPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundCommandsPacket {
|
||||
val nodes = buffer.readPrefixed { CommandNode.decode(buffer) }
|
||||
val rootIndex = buffer.readVarInt()
|
||||
return ClientboundCommandsPacket(nodes, rootIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundDisconnectPlayPacket(val reason: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundDisconnectPlayPacket(val reason: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundDisconnectPlayPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisconnectPlayPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundDisconnectPlayPacket {
|
||||
return ClientboundDisconnectPlayPacket(reason = buffer.readNetworkNBTCompound())
|
||||
return ClientboundDisconnectPlayPacket(reason = buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -12,25 +12,25 @@ 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.stream.BytesBuffer
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
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.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundDisguisedChatMessagePacket(
|
||||
val message: NBTCompound,
|
||||
val message: TextComponent,
|
||||
val chatType: IdOrX<InlineChatType>,
|
||||
val senderName: NBTCompound,
|
||||
val targetName: NBTCompound?,
|
||||
val senderName: TextComponent,
|
||||
val targetName: TextComponent?,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundDisguisedChatMessagePacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundDisguisedChatMessagePacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundDisguisedChatMessagePacket {
|
||||
val message = buffer.readNetworkNBTCompound()
|
||||
val message = buffer.readTextComponent()
|
||||
val chatType = buffer.readIdOrX { readInlineChatType() }
|
||||
val senderName = buffer.readNetworkNBTCompound()
|
||||
val targetName = buffer.readOptional { readNetworkNBTCompound() }
|
||||
val senderName = buffer.readTextComponent()
|
||||
val targetName = buffer.readOptional { readTextComponent() }
|
||||
return ClientboundDisguisedChatMessagePacket(message, chatType, senderName, targetName)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,24 +7,24 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundOpenScreenPacket(
|
||||
val windowId: Int,
|
||||
val windowType: Int,
|
||||
val title: NBTCompound,
|
||||
val title: TextComponent,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundOpenScreenPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundOpenScreenPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundOpenScreenPacket {
|
||||
val windowId = buffer.readVarInt()
|
||||
val windowType = buffer.readVarInt()
|
||||
val title = buffer.readNetworkNBTCompound()
|
||||
val title = buffer.readTextComponent()
|
||||
return ClientboundOpenScreenPacket(windowId, windowType, title)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -7,14 +7,14 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readUuid
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
@@ -29,12 +29,12 @@ public data class ClientboundPlayerChatMessagePacket(
|
||||
val timestamp: Long,
|
||||
val salt: Long,
|
||||
val previousMessages: List<PreviousMessageEntry>,
|
||||
val unsignedContent: NBTCompound?,
|
||||
val unsignedContent: TextComponent?,
|
||||
val filterType: ChatFilterType,
|
||||
val filterMaskBits: LongArray?,
|
||||
val chatType: Int,
|
||||
val senderName: NBTCompound,
|
||||
val targetName: NBTCompound?,
|
||||
val senderName: TextComponent,
|
||||
val targetName: TextComponent?,
|
||||
) : MinecraftPacket {
|
||||
public enum class ChatFilterType(public val id: Int) {
|
||||
PASS_THROUGH(0),
|
||||
@@ -90,7 +90,7 @@ public data class ClientboundPlayerChatMessagePacket(
|
||||
val prevMessages = List(prevMessageCount) { PreviousMessageEntry.decode(buffer) }
|
||||
|
||||
val hasUnsignedContent = buffer.readBoolean()
|
||||
val unsignedContent = if (hasUnsignedContent) buffer.readNetworkNBTCompound() else null // ?
|
||||
val unsignedContent = if (hasUnsignedContent) buffer.readTextComponent() else null // ?
|
||||
|
||||
val filterTypeId = buffer.readVarInt()
|
||||
val filterType = ChatFilterType.fromID(filterTypeId)
|
||||
@@ -101,10 +101,10 @@ public data class ClientboundPlayerChatMessagePacket(
|
||||
} else null
|
||||
|
||||
val chatType = buffer.readVarInt()
|
||||
val senderName = buffer.readNetworkNBTCompound() // ?
|
||||
val senderName = buffer.readTextComponent() // ?
|
||||
|
||||
val hasTargetName = buffer.readBoolean()
|
||||
val targetName = if (hasTargetName) buffer.readNetworkNBTCompound() else null // ?
|
||||
val targetName = if (hasTargetName) buffer.readTextComponent() else null // ?
|
||||
return ClientboundPlayerChatMessagePacket(
|
||||
globalIndex, sender, index,
|
||||
messageSignature, message,
|
||||
|
||||
+6
-6
@@ -7,19 +7,19 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundPlayerCombatDeathPacket(val playerId: Int, val message: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundPlayerCombatDeathPacket(val playerId: Int, val message: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundPlayerCombatDeathPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundPlayerCombatDeathPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundPlayerCombatDeathPacket {
|
||||
val playerId = buffer.readVarInt()
|
||||
val message = buffer.readNetworkNBTCompound()
|
||||
val message = buffer.readTextComponent()
|
||||
return ClientboundPlayerCombatDeathPacket(playerId, message)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,19 +7,19 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.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.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundServerDataPacket(val motd: NBTCompound, val icon: ByteArray?) : MinecraftPacket {
|
||||
public data class ClientboundServerDataPacket(val motd: TextComponent, val icon: ByteArray?) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundServerDataPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundServerDataPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundServerDataPacket {
|
||||
val motd = buffer.readNetworkNBTCompound()
|
||||
val motd = buffer.readTextComponent()
|
||||
val icon = buffer.readOptional { val length = readVarInt(); readBytes(length) }
|
||||
return ClientboundServerDataPacket(motd, icon)
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundSetActionBarTextPacket(val text: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundSetActionBarTextPacket(val text: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundSetActionBarTextPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetActionBarTextPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSetActionBarTextPacket {
|
||||
return ClientboundSetActionBarTextPacket(buffer.readNetworkNBTCompound())
|
||||
return ClientboundSetActionBarTextPacket(buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundSetSubtitleTextPacket(val subTitleText: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundSetSubtitleTextPacket(val subTitleText: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundSetSubtitleTextPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetSubtitleTextPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSetSubtitleTextPacket {
|
||||
return ClientboundSetSubtitleTextPacket(buffer.readNetworkNBTCompound())
|
||||
return ClientboundSetSubtitleTextPacket(buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -7,19 +7,19 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundSetTabListHeaderAndFooterPacket(val header: NBTCompound, val footer: NBTCompound) :
|
||||
public data class ClientboundSetTabListHeaderAndFooterPacket(val header: TextComponent, val footer: TextComponent) :
|
||||
MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundSetTabListHeaderAndFooterPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetTabListHeaderAndFooterPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSetTabListHeaderAndFooterPacket {
|
||||
val header = buffer.readNetworkNBTCompound()
|
||||
val footer = buffer.readNetworkNBTCompound()
|
||||
val header = buffer.readTextComponent()
|
||||
val footer = buffer.readTextComponent()
|
||||
return ClientboundSetTabListHeaderAndFooterPacket(header, footer)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,17 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundSetTitleTextPacket(val text: NBTCompound) : MinecraftPacket {
|
||||
public data class ClientboundSetTitleTextPacket(val text: TextComponent) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundSetTitleTextPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSetTitleTextPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSetTitleTextPacket {
|
||||
return ClientboundSetTitleTextPacket(buffer.readNetworkNBTCompound())
|
||||
return ClientboundSetTitleTextPacket(buffer.readTextComponent())
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -7,18 +7,18 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundSystemChatMessagePacket(val content: NBTCompound, val overlay: Boolean) :
|
||||
public data class ClientboundSystemChatMessagePacket(val content: TextComponent, val overlay: Boolean) :
|
||||
MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundSystemChatMessagePacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundSystemChatMessagePacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundSystemChatMessagePacket {
|
||||
val content = buffer.readNetworkNBTCompound()
|
||||
val content = buffer.readTextComponent()
|
||||
val overlay = buffer.readBoolean()
|
||||
return ClientboundSystemChatMessagePacket(content, overlay)
|
||||
}
|
||||
|
||||
+6
-6
@@ -7,15 +7,15 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.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.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundTestInstanceBlockStatusPacket(
|
||||
val status: NBTCompound,
|
||||
val status: TextComponent,
|
||||
val hasSize: Boolean,
|
||||
val sizeX: Double?,
|
||||
val sizeY: Double?,
|
||||
@@ -24,7 +24,7 @@ public data class ClientboundTestInstanceBlockStatusPacket(
|
||||
internal companion object Codec : PacketCodec<ClientboundTestInstanceBlockStatusPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundTestInstanceBlockStatusPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundTestInstanceBlockStatusPacket {
|
||||
val status = buffer.readNetworkNBTCompound()
|
||||
val status = buffer.readTextComponent()
|
||||
val hasSize = buffer.readBoolean()
|
||||
val sizeX = buffer.readOptional { readDouble() } // ?
|
||||
val sizeY = buffer.readOptional { readDouble() } // ?
|
||||
|
||||
+6
-5
@@ -7,16 +7,17 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.readTextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.scoreboard.ObjectivePayload
|
||||
import cn.rtast.libmc.protocol.protocol.game.scoreboard.ObjectiveRenderType
|
||||
import cn.rtast.libmc.protocol.protocol.game.scoreboard.ScoreNumberFormat
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundUpdateObjectivePacket(
|
||||
val objectiveName: String,
|
||||
@@ -31,14 +32,14 @@ public data class ClientboundUpdateObjectivePacket(
|
||||
val payload = when (mode.toInt()) {
|
||||
1 -> ObjectivePayload.Remove
|
||||
0, 2 -> {
|
||||
val displayName = buffer.readNetworkNBTCompound()
|
||||
val displayName = buffer.readTextComponent()
|
||||
val renderType = ObjectiveRenderType.fromID(buffer.readVarInt())
|
||||
val hasNumberFormat = buffer.readBoolean()
|
||||
val numberFormat = if (hasNumberFormat) {
|
||||
when (val type = buffer.readVarInt()) {
|
||||
0 -> ScoreNumberFormat.Blank
|
||||
1 -> ScoreNumberFormat.Styled(styling = buffer.readNetworkNBTCompound().element as NBTTag.CompoundTag) // fix me
|
||||
2 -> ScoreNumberFormat.Fixed(content = buffer.readNetworkNBTCompound()) // ?
|
||||
2 -> ScoreNumberFormat.Fixed(content = buffer.readTextComponent()) // ?
|
||||
else -> error("Unknown ScoreNumberFormat type: $type")
|
||||
}
|
||||
} else null
|
||||
|
||||
+7
-6
@@ -7,22 +7,23 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.packet.play.clientbound
|
||||
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
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.readVarInt
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
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.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundUpdateScorePacket(
|
||||
val entityName: String,
|
||||
val objectiveName: String,
|
||||
val value: Int,
|
||||
val displayName: NBTCompound?,
|
||||
val displayName: TextComponent?,
|
||||
val numberFormat: ScoreNumberFormat?,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundUpdateScorePacket> {
|
||||
@@ -31,12 +32,12 @@ public data class ClientboundUpdateScorePacket(
|
||||
val entityName = buffer.readMcString()
|
||||
val objectiveName = buffer.readMcString()
|
||||
val value = buffer.readVarInt()
|
||||
val displayName = buffer.readOptional { readNetworkNBTCompound() }
|
||||
val displayName = buffer.readOptional { readTextComponent() }
|
||||
val numberFormat = buffer.readOptional {
|
||||
when (val type = readVarInt()) {
|
||||
0 -> ScoreNumberFormat.Blank
|
||||
1 -> ScoreNumberFormat.Styled(styling = readNetworkNBTCompound().element as NBTTag.CompoundTag) // fix me
|
||||
2 -> ScoreNumberFormat.Fixed(content = readNetworkNBTCompound())
|
||||
2 -> ScoreNumberFormat.Fixed(content = readTextComponent())
|
||||
else -> error("Unknown ScoreNumberFormat type: $type")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ package cn.rtast.libmc.protocol.packet.play.serverbound
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.protocol.registry.GameDifficulty
|
||||
import cn.rtast.libmc.protocol.protocol.game.registry.GameDifficulty
|
||||
|
||||
public data class ServerboundChangeDifficultyPacket(val newDifficulty: GameDifficulty) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ServerboundChangeDifficultyPacket> {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.protocol.registry.ClientAction
|
||||
import cn.rtast.libmc.protocol.protocol.game.registry.ClientAction
|
||||
|
||||
public data class ServerboundClientActionPacket(val action: ClientAction) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ServerboundClientActionPacket> {
|
||||
|
||||
+4
-2
@@ -14,6 +14,8 @@ import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.Identifier
|
||||
import cn.rtast.libmc.protocol.protocol.game.block.*
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.writeTextComponent
|
||||
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
|
||||
import cn.rtast.libmc.protocol.protocol.util.writeNetworkNBTCompound
|
||||
|
||||
@@ -27,7 +29,7 @@ public data class ServerboundTestInstanceBlockActionPacket(
|
||||
val rotation: TestInstanceRotation,
|
||||
val ignoreEntities: Boolean,
|
||||
val status: TestInstanceStatus,
|
||||
val errorMessage: NBTCompound?,
|
||||
val errorMessage: TextComponent?,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ServerboundTestInstanceBlockActionPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ServerboundTestInstanceBlockActionPacket) {
|
||||
@@ -42,7 +44,7 @@ public data class ServerboundTestInstanceBlockActionPacket(
|
||||
buffer.writeBoolean(value.ignoreEntities)
|
||||
buffer.writeVarInt(value.status.id)
|
||||
buffer.writeBoolean(value.errorMessage != null)
|
||||
if (value.errorMessage != null) buffer.writeNetworkNBTCompound(value.errorMessage) // TODO to fix
|
||||
if (value.errorMessage != null) buffer.writeTextComponent(value.errorMessage) // TODO to fix
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerboundTestInstanceBlockActionPacket =
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ internal object GameProtocols {
|
||||
register(0x0d, ClientboundChunksBiomesPacket)
|
||||
register(0x0e, ClientboundClearTitlesPacket)
|
||||
register(0x0f, ClientboundCommandSuggestionsPacket)
|
||||
// register(0x10, ClientboundCommandsPacket)
|
||||
register(0x10, ClientboundCommandsPacket)
|
||||
register(0x11, ClientboundContainerClosePacket)
|
||||
// register(0x12, ClientboundContainerSetContentPacket)
|
||||
register(0x13, ClientboundContainerSetDataPacket)
|
||||
|
||||
+3
-3
@@ -7,11 +7,11 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.bossbar
|
||||
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
|
||||
public sealed interface BossBarAction {
|
||||
public data class Add(
|
||||
val title: NBTCompound,
|
||||
val title: TextComponent,
|
||||
val health: Float,
|
||||
val color: BossBarColor,
|
||||
val division: BossBarDivision,
|
||||
@@ -20,7 +20,7 @@ public sealed interface BossBarAction {
|
||||
|
||||
public object Remove : BossBarAction
|
||||
public data class UpdateHealth(val health: Float) : BossBarAction
|
||||
public data class UpdateTitle(val title: NBTCompound) : BossBarAction
|
||||
public data class UpdateTitle(val title: TextComponent) : BossBarAction
|
||||
public data class UpdateStyle(val color: BossBarColor, val division: BossBarDivision) : BossBarAction
|
||||
public data class UpdateFlags(val flags: BossBarFlags) : BossBarAction
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
public data class ChatTypeDecoration(
|
||||
val translationKey: String,
|
||||
val parameters: List<ChatTypeParameter>,
|
||||
val style: NBTCompound,
|
||||
val style: NBTCompound, // TODO TextComponent or Raw NBT Compound
|
||||
) {
|
||||
public enum class ChatTypeParameter(public val id: Int) {
|
||||
Sender(0), Target(1), Content(2);
|
||||
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.chat
|
||||
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
import cn.rtast.libmc.nbt.NBTType
|
||||
import cn.rtast.libmc.protocol.protocol.game.Identifier
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.writeNetworkNBTCompound
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
public data class TextComponent(
|
||||
val content: Content,
|
||||
val style: Style = Style.EMPTY,
|
||||
val extra: List<TextComponent> = emptyList(),
|
||||
val clickEvent: ClickEvent? = null,
|
||||
val hoverEvent: HoverEvent? = null,
|
||||
val insertion: String? = null,
|
||||
) {
|
||||
public sealed interface Content {
|
||||
public val type: String
|
||||
|
||||
public data class PlainText(val text: String) : Content {
|
||||
override val type: String = "text"
|
||||
}
|
||||
|
||||
public data class Translatable(
|
||||
val key: String,
|
||||
val fallback: String? = null,
|
||||
val args: List<TextComponent> = emptyList(),
|
||||
) : Content {
|
||||
override val type: String = "translatable"
|
||||
}
|
||||
|
||||
public data class Score(val name: String, val objective: String) : Content {
|
||||
override val type: String = "score"
|
||||
}
|
||||
|
||||
public data class Selector(val selector: String, val separator: TextComponent? = null) : Content {
|
||||
override val type: String = "selector"
|
||||
}
|
||||
|
||||
public data class Keybind(val keybind: String) : Content {
|
||||
override val type: String = "keybind"
|
||||
}
|
||||
|
||||
public data class Nbt(
|
||||
val path: String,
|
||||
val interpret: Boolean = false,
|
||||
val plain: Boolean = false,
|
||||
val separator: TextComponent? = null,
|
||||
val source: NbtSource,
|
||||
) : Content {
|
||||
override val type: String = "nbt"
|
||||
|
||||
public sealed interface NbtSource {
|
||||
public data class Entity(val selector: String) : NbtSource
|
||||
public data class Block(val coordinates: String) : NbtSource
|
||||
public data class Storage(val id: Identifier) : NbtSource
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface ObjectContent : Content {
|
||||
override val type: String get() = "object"
|
||||
|
||||
public data class Atlas(
|
||||
val sprite: Identifier,
|
||||
val atlas: Identifier = Identifier.of("minecraft", "blocks"),
|
||||
) : ObjectContent
|
||||
|
||||
public data class Player(
|
||||
val profile: PlayerProfile,
|
||||
val hat: Boolean = true,
|
||||
) : ObjectContent
|
||||
}
|
||||
}
|
||||
|
||||
public data class Style(
|
||||
val color: TextColor? = null,
|
||||
val font: Identifier? = null,
|
||||
val bold: Boolean? = null,
|
||||
val italic: Boolean? = null,
|
||||
val underlined: Boolean? = null,
|
||||
val strikethrough: Boolean? = null,
|
||||
val obfuscated: Boolean? = null,
|
||||
val shadowColor: ShadowColor? = null,
|
||||
) {
|
||||
public companion object {
|
||||
public val EMPTY: Style = Style()
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface PlayerProfile {
|
||||
public data class Name(val name: String) : PlayerProfile
|
||||
public data class FullProfile(
|
||||
val name: String? = null,
|
||||
val id: Uuid? = null,
|
||||
val properties: List<Property> = emptyList(),
|
||||
) : PlayerProfile {
|
||||
public data class Property(
|
||||
val name: String,
|
||||
val value: String,
|
||||
val signature: String? = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface TextColor {
|
||||
public data class Named(val name: String) : TextColor
|
||||
public data class Hex(val hex: String) : TextColor
|
||||
}
|
||||
|
||||
public sealed interface ShadowColor {
|
||||
public data class ArgbInt(val argb: Long) : ShadowColor
|
||||
public data class RgbaFloat(
|
||||
val red: Float,
|
||||
val green: Float,
|
||||
val blue: Float,
|
||||
val alpha: Float,
|
||||
) : ShadowColor
|
||||
}
|
||||
|
||||
public sealed interface ClickEvent {
|
||||
public val action: ClickEventAction
|
||||
|
||||
public enum class ClickEventAction(public val text: String) {
|
||||
OpenURL("open_url"),
|
||||
OpenFile("open_file"),
|
||||
RunCommand("run_command"),
|
||||
SuggestCommand("suggest_command"),
|
||||
ChangePage("change_page"),
|
||||
CopyToClipboard("copy_to_clipboard"),
|
||||
ShowDialog("show_dialog"),
|
||||
Custom("custom");
|
||||
|
||||
public companion object {
|
||||
public fun fromText(text: String): ClickEventAction =
|
||||
entries.first { it.text == text }
|
||||
}
|
||||
}
|
||||
|
||||
public data class OpenUrl(val url: String) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.OpenURL
|
||||
}
|
||||
|
||||
public data class OpenFile(val path: String) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.OpenFile
|
||||
}
|
||||
|
||||
public data class RunCommand(val command: String) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.RunCommand
|
||||
}
|
||||
|
||||
public data class SuggestCommand(val command: String) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.SuggestCommand
|
||||
}
|
||||
|
||||
public data class ChangePage(val page: Int) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.ChangePage
|
||||
}
|
||||
|
||||
public data class CopyToClipboard(val value: String) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.CopyToClipboard
|
||||
}
|
||||
|
||||
public data class ShowDialog(val dialog: DialogPayload) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.ShowDialog
|
||||
|
||||
public sealed interface DialogPayload {
|
||||
public data class Id(val id: Identifier) : DialogPayload
|
||||
public data class Definition(val compound: NBTTag.CompoundTag) : DialogPayload
|
||||
}
|
||||
}
|
||||
|
||||
public data class Custom(val id: Identifier, val payload: String? = null) : ClickEvent {
|
||||
override val action: ClickEventAction = ClickEventAction.Custom
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface HoverEvent {
|
||||
public val action: HoverEventAction
|
||||
|
||||
public enum class HoverEventAction(public val text: String) {
|
||||
ShowText("show_text"),
|
||||
ShowItem("show_item"),
|
||||
ShowEntity("show_entity");
|
||||
|
||||
public companion object {
|
||||
public fun fromText(text: String): HoverEventAction =
|
||||
entries.first { it.text == text }
|
||||
}
|
||||
}
|
||||
|
||||
public data class ShowText(val component: TextComponent) : HoverEvent {
|
||||
override val action: HoverEventAction = HoverEventAction.ShowText
|
||||
}
|
||||
|
||||
public data class ShowItem(
|
||||
val id: Identifier,
|
||||
val count: Int = 1,
|
||||
val components: NBTTag.CompoundTag? = null,
|
||||
) : HoverEvent {
|
||||
override val action: HoverEventAction = HoverEventAction.ShowItem
|
||||
}
|
||||
|
||||
public data class ShowEntity(
|
||||
val id: Identifier,
|
||||
val uuid: UUIDRepresentation,
|
||||
val name: TextComponent? = null,
|
||||
) : HoverEvent {
|
||||
override val action: HoverEventAction = HoverEventAction.ShowEntity
|
||||
|
||||
public sealed interface UUIDRepresentation {
|
||||
public data class StringFormat(val uuid: Uuid) : UUIDRepresentation
|
||||
public data class ArrayFormat(val i1: Int, val i2: Int, val i3: Int, val i4: Int) : UUIDRepresentation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public fun of(text: String): TextComponent = TextComponent(content = Content.PlainText(text))
|
||||
}
|
||||
}
|
||||
|
||||
public fun NBTTag.toTextComponent(): TextComponent {
|
||||
return when (this) {
|
||||
is NBTTag.StringTag -> TextComponent.of(this.value)
|
||||
is NBTTag.ListTag -> {
|
||||
val tags = this.value
|
||||
if (tags.isEmpty()) return TextComponent.of("")
|
||||
val head = tags.first().toTextComponent()
|
||||
val tail = tags.drop(1).map { it.toTextComponent() }
|
||||
head.copy(extra = head.extra + tail)
|
||||
}
|
||||
|
||||
is NBTTag.CompoundTag -> {
|
||||
val content = parseContentFromNbt(this)
|
||||
val style = parseStyleFromNbt(this)
|
||||
val extra = this.getListOrNull("extra")?.map { it.toTextComponent() } ?: emptyList()
|
||||
val clickEvent = this.getCompoundOrNull("click_event")?.let { parseClickEventFromNbt(it) }
|
||||
val hoverEvent = this.getCompoundOrNull("hover_event")?.let { parseHoverEventFromNbt(it) }
|
||||
val insertion = this.getStringOrNull("insertion")
|
||||
|
||||
TextComponent(
|
||||
content = content,
|
||||
style = style,
|
||||
extra = extra,
|
||||
clickEvent = clickEvent,
|
||||
hoverEvent = hoverEvent,
|
||||
insertion = insertion
|
||||
)
|
||||
}
|
||||
|
||||
else -> TextComponent.of(this.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseContentFromNbt(tag: NBTTag.CompoundTag): TextComponent.Content {
|
||||
val type = tag.getStringOrNull("type")
|
||||
return when {
|
||||
type == "text" || (type == null && tag.containsKey("text")) ->
|
||||
TextComponent.Content.PlainText(tag.getString("text"))
|
||||
|
||||
type == "translatable" || (type == null && tag.containsKey("translate")) -> {
|
||||
TextComponent.Content.Translatable(
|
||||
key = tag.getString("translate"),
|
||||
fallback = tag.getStringOrNull("fallback"),
|
||||
args = tag.getListOrNull("with")?.map { it.toTextComponent() } ?: emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
type == "score" || (type == null && tag.containsKey("score")) -> {
|
||||
val scoreObj = tag.getCompound("score")
|
||||
TextComponent.Content.Score(
|
||||
name = scoreObj.getString("name"),
|
||||
objective = scoreObj.getString("objective")
|
||||
)
|
||||
}
|
||||
|
||||
type == "selector" || (type == null && tag.containsKey("selector")) -> {
|
||||
TextComponent.Content.Selector(
|
||||
selector = tag.getString("selector"),
|
||||
separator = tag.getOrNull("separator")?.toTextComponent()
|
||||
)
|
||||
}
|
||||
|
||||
type == "keybind" || (type == null && tag.containsKey("keybind")) -> {
|
||||
TextComponent.Content.Keybind(tag.getString("keybind"))
|
||||
}
|
||||
|
||||
type == "nbt" || (type == null && tag.containsKey("nbt")) -> {
|
||||
val path = tag.getString("nbt")
|
||||
val interpret = tag.getBooleanOrNull("interpret") ?: false
|
||||
val plain = tag.getBooleanOrNull("plain") ?: false
|
||||
val separator = tag.getOrNull("separator")?.toTextComponent()
|
||||
|
||||
val source = when {
|
||||
tag.containsKey("entity") -> TextComponent.Content.Nbt.NbtSource.Entity(tag.getString("entity"))
|
||||
tag.containsKey("block") -> TextComponent.Content.Nbt.NbtSource.Block(tag.getString("block"))
|
||||
tag.containsKey("storage") -> TextComponent.Content.Nbt.NbtSource.Storage(Identifier.of(tag.getString("storage")))
|
||||
else -> throw IllegalArgumentException("Missing NBT source (entity, block, or storage)")
|
||||
}
|
||||
|
||||
TextComponent.Content.Nbt(path, interpret, plain, separator, source)
|
||||
}
|
||||
|
||||
type == "object" || (type == null && (tag.containsKey("atlas") || tag.containsKey("sprite") || tag.containsKey("player"))) -> {
|
||||
val objectType = tag.getStringOrNull("object") ?: if (tag.containsKey("player")) "player" else "atlas"
|
||||
if (objectType == "player") {
|
||||
val hat = tag.getBooleanOrNull("hat") ?: true
|
||||
val playerTag = tag.getOrNull("player")
|
||||
val profile = if (playerTag is NBTTag.StringTag) {
|
||||
TextComponent.PlayerProfile.Name(playerTag.value)
|
||||
} else {
|
||||
val pComp = playerTag as NBTTag.CompoundTag
|
||||
TextComponent.PlayerProfile.FullProfile(
|
||||
name = pComp.getStringOrNull("name"),
|
||||
id = pComp.getStringOrNull("id")?.let { Uuid.parse(it) }
|
||||
)
|
||||
}
|
||||
TextComponent.Content.ObjectContent.Player(profile, hat)
|
||||
} else {
|
||||
TextComponent.Content.ObjectContent.Atlas(
|
||||
sprite = Identifier.of(tag.getString("sprite")),
|
||||
atlas = tag.getStringOrNull("atlas")?.let { Identifier.of(it) } ?: Identifier.of(
|
||||
"minecraft",
|
||||
"blocks"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> TextComponent.Content.PlainText("")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseStyleFromNbt(tag: NBTTag.CompoundTag): TextComponent.Style {
|
||||
val color = tag.getStringOrNull("color")?.let {
|
||||
if (it.startsWith("#")) TextComponent.TextColor.Hex(it) else TextComponent.TextColor.Named(it)
|
||||
}
|
||||
val font = tag.getStringOrNull("font")?.let { Identifier.of(it) }
|
||||
val shadowColor = when (val sc = tag.getOrNull("shadow_color")) {
|
||||
is NBTTag.IntTag -> TextComponent.ShadowColor.ArgbInt(sc.value.toLong())
|
||||
is NBTTag.ListTag -> {
|
||||
val floats = sc.value.filterIsInstance<NBTTag.FloatTag>().map { it.value }
|
||||
if (floats.size == 4) {
|
||||
TextComponent.ShadowColor.RgbaFloat(floats[0], floats[1], floats[2], floats[3])
|
||||
} else null
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
return TextComponent.Style(
|
||||
color = color,
|
||||
font = font,
|
||||
bold = tag.getBooleanOrNull("bold"),
|
||||
italic = tag.getBooleanOrNull("italic"),
|
||||
underlined = tag.getBooleanOrNull("underlined"),
|
||||
strikethrough = tag.getBooleanOrNull("strikethrough"),
|
||||
obfuscated = tag.getBooleanOrNull("obfuscated"),
|
||||
shadowColor = shadowColor
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseClickEventFromNbt(tag: NBTTag.CompoundTag): TextComponent.ClickEvent {
|
||||
val action = TextComponent.ClickEvent.ClickEventAction.fromText(tag.getString("action"))
|
||||
return when (action) {
|
||||
TextComponent.ClickEvent.ClickEventAction.OpenURL -> TextComponent.ClickEvent.OpenUrl(tag.getString("url"))
|
||||
TextComponent.ClickEvent.ClickEventAction.OpenFile -> TextComponent.ClickEvent.OpenFile(tag.getString("path"))
|
||||
TextComponent.ClickEvent.ClickEventAction.RunCommand -> TextComponent.ClickEvent.RunCommand(tag.getString("command"))
|
||||
TextComponent.ClickEvent.ClickEventAction.SuggestCommand -> TextComponent.ClickEvent.SuggestCommand(
|
||||
tag.getString(
|
||||
"command"
|
||||
)
|
||||
)
|
||||
|
||||
TextComponent.ClickEvent.ClickEventAction.ChangePage -> TextComponent.ClickEvent.ChangePage(tag.getInt("page"))
|
||||
TextComponent.ClickEvent.ClickEventAction.CopyToClipboard -> TextComponent.ClickEvent.CopyToClipboard(
|
||||
tag.getString(
|
||||
"value"
|
||||
)
|
||||
)
|
||||
|
||||
TextComponent.ClickEvent.ClickEventAction.ShowDialog -> {
|
||||
val dialogTag = tag.getOrNull("dialog")
|
||||
val payload = if (dialogTag is NBTTag.StringTag) {
|
||||
TextComponent.ClickEvent.ShowDialog.DialogPayload.Id(Identifier.of(dialogTag.value))
|
||||
} else {
|
||||
TextComponent.ClickEvent.ShowDialog.DialogPayload.Definition(dialogTag as NBTTag.CompoundTag)
|
||||
}
|
||||
TextComponent.ClickEvent.ShowDialog(payload)
|
||||
}
|
||||
|
||||
TextComponent.ClickEvent.ClickEventAction.Custom -> TextComponent.ClickEvent.Custom(
|
||||
id = Identifier.of(tag.getString("id")),
|
||||
payload = tag.getStringOrNull("payload")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseHoverEventFromNbt(tag: NBTTag.CompoundTag): TextComponent.HoverEvent {
|
||||
val action = TextComponent.HoverEvent.HoverEventAction.fromText(tag.getString("action"))
|
||||
return when (action) {
|
||||
TextComponent.HoverEvent.HoverEventAction.ShowText -> {
|
||||
TextComponent.HoverEvent.ShowText(tag.getCompound("value").toTextComponent())
|
||||
}
|
||||
|
||||
TextComponent.HoverEvent.HoverEventAction.ShowItem -> {
|
||||
TextComponent.HoverEvent.ShowItem(
|
||||
id = Identifier.of(tag.getString("id")),
|
||||
count = tag.getIntOrNull("count") ?: 1,
|
||||
components = tag.getCompoundOrNull("components")
|
||||
)
|
||||
}
|
||||
|
||||
TextComponent.HoverEvent.HoverEventAction.ShowEntity -> {
|
||||
val uuidTag = tag.getOrNull("uuid")
|
||||
val uuid = if (uuidTag is NBTTag.StringTag) {
|
||||
TextComponent.HoverEvent.ShowEntity.UUIDRepresentation.StringFormat(Uuid.parse(uuidTag.value))
|
||||
} else {
|
||||
val list = (uuidTag as NBTTag.IntArrayTag).value
|
||||
TextComponent.HoverEvent.ShowEntity.UUIDRepresentation.ArrayFormat(list[0], list[1], list[2], list[3])
|
||||
}
|
||||
TextComponent.HoverEvent.ShowEntity(
|
||||
id = Identifier.of(tag.getString("id")),
|
||||
uuid = uuid,
|
||||
name = tag.getOrNull("name")?.toTextComponent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun TextComponent.toNbt(): NBTTag.CompoundTag {
|
||||
val map = mutableMapOf<String, NBTTag>()
|
||||
when (val c = this.content) {
|
||||
is TextComponent.Content.PlainText -> {
|
||||
map["type"] = NBTTag.StringTag("text")
|
||||
map["text"] = NBTTag.StringTag(c.text)
|
||||
}
|
||||
|
||||
is TextComponent.Content.Translatable -> {
|
||||
map["type"] = NBTTag.StringTag("translatable")
|
||||
map["translate"] = NBTTag.StringTag(c.key)
|
||||
c.fallback?.let { map["fallback"] = NBTTag.StringTag(it) }
|
||||
if (c.args.isNotEmpty()) {
|
||||
map["with"] = NBTTag.ListTag(NBTType.List, c.args.map { it.toNbt() as NBTTag }.toMutableList())
|
||||
}
|
||||
}
|
||||
|
||||
is TextComponent.Content.Score -> {
|
||||
map["type"] = NBTTag.StringTag("score")
|
||||
map["score"] = NBTTag.CompoundTag(
|
||||
mutableMapOf(
|
||||
"name" to NBTTag.StringTag(c.name),
|
||||
"objective" to NBTTag.StringTag(c.objective)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is TextComponent.Content.Selector -> {
|
||||
map["type"] = NBTTag.StringTag("selector")
|
||||
map["selector"] = NBTTag.StringTag(c.selector)
|
||||
c.separator?.let { map["separator"] = it.toNbt() }
|
||||
}
|
||||
|
||||
is TextComponent.Content.Keybind -> {
|
||||
map["type"] = NBTTag.StringTag("keybind")
|
||||
map["keybind"] = NBTTag.StringTag(c.keybind)
|
||||
}
|
||||
|
||||
is TextComponent.Content.Nbt -> {
|
||||
map["type"] = NBTTag.StringTag("nbt")
|
||||
map["nbt"] = NBTTag.StringTag(c.path)
|
||||
map["interpret"] = NBTTag.ByteTag(if (c.interpret) 1 else 0)
|
||||
map["plain"] = NBTTag.ByteTag(if (c.plain) 1 else 0)
|
||||
c.separator?.let { map["separator"] = it.toNbt() }
|
||||
when (val s = c.source) {
|
||||
is TextComponent.Content.Nbt.NbtSource.Entity -> map["entity"] = NBTTag.StringTag(s.selector)
|
||||
is TextComponent.Content.Nbt.NbtSource.Block -> map["block"] = NBTTag.StringTag(s.coordinates)
|
||||
is TextComponent.Content.Nbt.NbtSource.Storage -> map["storage"] = NBTTag.StringTag(s.id.toString())
|
||||
}
|
||||
}
|
||||
|
||||
is TextComponent.Content.ObjectContent.Atlas -> {
|
||||
map["type"] = NBTTag.StringTag("object")
|
||||
map["object"] = NBTTag.StringTag("atlas")
|
||||
map["sprite"] = NBTTag.StringTag(c.sprite.toString())
|
||||
map["atlas"] = NBTTag.StringTag(c.atlas.toString())
|
||||
}
|
||||
|
||||
is TextComponent.Content.ObjectContent.Player -> {
|
||||
map["type"] = NBTTag.StringTag("object")
|
||||
map["object"] = NBTTag.StringTag("player")
|
||||
map["hat"] = NBTTag.ByteTag(if (c.hat) 1 else 0)
|
||||
when (val p = c.profile) {
|
||||
is TextComponent.PlayerProfile.Name -> map["player"] = NBTTag.StringTag(p.name)
|
||||
is TextComponent.PlayerProfile.FullProfile -> {
|
||||
val pMap = mutableMapOf<String, NBTTag>()
|
||||
p.name?.let { pMap["name"] = NBTTag.StringTag(it) }
|
||||
p.id?.let { pMap["id"] = NBTTag.StringTag(it.toString()) }
|
||||
map["player"] = NBTTag.CompoundTag(pMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.style.color?.let {
|
||||
map["color"] = NBTTag.StringTag(
|
||||
when (it) {
|
||||
is TextComponent.TextColor.Named -> it.name
|
||||
is TextComponent.TextColor.Hex -> it.hex
|
||||
}
|
||||
)
|
||||
}
|
||||
this.style.font?.let { map["font"] = NBTTag.StringTag(it.toString()) }
|
||||
this.style.bold?.let { map["bold"] = NBTTag.ByteTag(if (it) 1 else 0) }
|
||||
this.style.italic?.let { map["italic"] = NBTTag.ByteTag(if (it) 1 else 0) }
|
||||
this.style.underlined?.let { map["underlined"] = NBTTag.ByteTag(if (it) 1 else 0) }
|
||||
this.style.strikethrough?.let { map["strikethrough"] = NBTTag.ByteTag(if (it) 1 else 0) }
|
||||
this.style.obfuscated?.let { map["obfuscated"] = NBTTag.ByteTag(if (it) 1 else 0) }
|
||||
this.style.shadowColor?.let {
|
||||
when (it) {
|
||||
is TextComponent.ShadowColor.ArgbInt -> map["shadow_color"] = NBTTag.IntTag(it.argb.toInt())
|
||||
is TextComponent.ShadowColor.RgbaFloat -> map["shadow_color"] = NBTTag.ListTag(
|
||||
NBTType.List, mutableListOf(
|
||||
NBTTag.FloatTag(it.red),
|
||||
NBTTag.FloatTag(it.green),
|
||||
NBTTag.FloatTag(it.blue),
|
||||
NBTTag.FloatTag(it.alpha)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.extra.isNotEmpty()) map["extra"] =
|
||||
NBTTag.ListTag(NBTType.List, this.extra.map { it.toNbt() as NBTTag }.toMutableList())
|
||||
this.insertion?.let { map["insertion"] = NBTTag.StringTag(it) }
|
||||
this.clickEvent?.let { map["click_event"] = encodeClickEventToNbt(it) }
|
||||
this.hoverEvent?.let { map["hover_event"] = encodeHoverEventToNbt(it) }
|
||||
|
||||
return NBTTag.CompoundTag(map)
|
||||
}
|
||||
|
||||
private fun encodeClickEventToNbt(event: TextComponent.ClickEvent): NBTTag.CompoundTag {
|
||||
val map = mutableMapOf<String, NBTTag>("action" to NBTTag.StringTag(event.action.text))
|
||||
when (event) {
|
||||
is TextComponent.ClickEvent.OpenUrl -> map["url"] = NBTTag.StringTag(event.url)
|
||||
is TextComponent.ClickEvent.OpenFile -> map["path"] = NBTTag.StringTag(event.path)
|
||||
is TextComponent.ClickEvent.RunCommand -> map["command"] = NBTTag.StringTag(event.command)
|
||||
is TextComponent.ClickEvent.SuggestCommand -> map["command"] = NBTTag.StringTag(event.command)
|
||||
is TextComponent.ClickEvent.ChangePage -> map["page"] = NBTTag.IntTag(event.page)
|
||||
is TextComponent.ClickEvent.CopyToClipboard -> map["value"] = NBTTag.StringTag(event.value)
|
||||
is TextComponent.ClickEvent.ShowDialog -> {
|
||||
map["dialog"] = when (val d = event.dialog) {
|
||||
is TextComponent.ClickEvent.ShowDialog.DialogPayload.Id -> NBTTag.StringTag(d.id.toString())
|
||||
is TextComponent.ClickEvent.ShowDialog.DialogPayload.Definition -> d.compound
|
||||
}
|
||||
}
|
||||
|
||||
is TextComponent.ClickEvent.Custom -> {
|
||||
map["id"] = NBTTag.StringTag(event.id.toString())
|
||||
event.payload?.let { map["payload"] = NBTTag.StringTag(it) }
|
||||
}
|
||||
}
|
||||
return NBTTag.CompoundTag(map)
|
||||
}
|
||||
|
||||
private fun encodeHoverEventToNbt(event: TextComponent.HoverEvent): NBTTag.CompoundTag {
|
||||
val map = mutableMapOf<String, NBTTag>("action" to NBTTag.StringTag(event.action.text))
|
||||
when (event) {
|
||||
is TextComponent.HoverEvent.ShowText -> map["value"] = event.component.toNbt()
|
||||
is TextComponent.HoverEvent.ShowItem -> {
|
||||
map["id"] = NBTTag.StringTag(event.id.toString())
|
||||
map["count"] = NBTTag.IntTag(event.count)
|
||||
event.components?.let { map["components"] = it }
|
||||
}
|
||||
|
||||
is TextComponent.HoverEvent.ShowEntity -> {
|
||||
map["id"] = NBTTag.StringTag(event.id.toString())
|
||||
map["uuid"] = when (val u = event.uuid) {
|
||||
is TextComponent.HoverEvent.ShowEntity.UUIDRepresentation.StringFormat -> NBTTag.StringTag(u.uuid.toString())
|
||||
is TextComponent.HoverEvent.ShowEntity.UUIDRepresentation.ArrayFormat -> NBTTag.IntArrayTag(
|
||||
intArrayOf(u.i1, u.i2, u.i3, u.i4)
|
||||
)
|
||||
}
|
||||
event.name?.let { map["name"] = it.toNbt() }
|
||||
}
|
||||
}
|
||||
return NBTTag.CompoundTag(map)
|
||||
}
|
||||
|
||||
private fun NBTTag.CompoundTag.getOrNull(key: String): NBTTag? = this.value[key]
|
||||
private fun NBTTag.CompoundTag.containsKey(key: String): Boolean = this.value.containsKey(key)
|
||||
private fun NBTTag.CompoundTag.getString(key: String): String = (this.value[key] as NBTTag.StringTag).value
|
||||
private fun NBTTag.CompoundTag.getStringOrNull(key: String): String? = (this.value[key] as? NBTTag.StringTag)?.value
|
||||
private fun NBTTag.CompoundTag.getInt(key: String): Int = (this.value[key] as NBTTag.IntTag).value
|
||||
private fun NBTTag.CompoundTag.getIntOrNull(key: String): Int? = (this.value[key] as? NBTTag.IntTag)?.value
|
||||
private fun NBTTag.CompoundTag.getBooleanOrNull(key: String): Boolean? = when (val t = this.value[key]) {
|
||||
is NBTTag.ByteTag -> t.value != 0.toByte()
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun NBTTag.CompoundTag.getCompound(key: String): NBTTag.CompoundTag = this.value[key] as NBTTag.CompoundTag
|
||||
private fun NBTTag.CompoundTag.getCompoundOrNull(key: String): NBTTag.CompoundTag? =
|
||||
this.value[key] as? NBTTag.CompoundTag
|
||||
|
||||
private fun NBTTag.CompoundTag.getListOrNull(key: String): List<NBTTag>? = (this.value[key] as? NBTTag.ListTag)?.value
|
||||
|
||||
internal suspend fun BytesBuffer.readTextComponent(): TextComponent =
|
||||
this.readNetworkNBTCompound().element.toTextComponent()
|
||||
|
||||
internal suspend fun BytesBuffer.writeTextComponent(component: TextComponent) {
|
||||
val nbt = component.toNbt()
|
||||
this.writeNetworkNBTCompound(NBTCompound("", nbt))
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.command
|
||||
|
||||
import cn.rtast.libmc.protocol.protocol.game.Identifier
|
||||
|
||||
public sealed interface CommandArgumentProperties {
|
||||
public object Empty : CommandArgumentProperties
|
||||
|
||||
public data class IntProp(val min: Int = Int.MIN_VALUE, val max: Int = Int.MAX_VALUE) :
|
||||
CommandArgumentProperties
|
||||
|
||||
public data class LongProp(val min: Long = Long.MIN_VALUE, val max: Long = Long.MAX_VALUE) :
|
||||
CommandArgumentProperties
|
||||
|
||||
public data class FloatProp(val min: Float = Float.MIN_VALUE, val max: Float = Float.MAX_VALUE) :
|
||||
CommandArgumentProperties
|
||||
|
||||
public data class DoubleProp(val min: Double = Double.MIN_VALUE, val max: Double = Double.MAX_VALUE) :
|
||||
CommandArgumentProperties
|
||||
|
||||
public data class StringProp(val behavior: StringBehavior) : CommandArgumentProperties {
|
||||
public enum class StringBehavior(public val id: Int) {
|
||||
SingleWord(0), QuotablePhrase(1), GreedyPhrase(2);
|
||||
|
||||
public companion object {
|
||||
public fun fromId(id: Int): StringBehavior = entries.first { it.id == id }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public data class Entity(val singleOnly: Boolean, val playersOnly: Boolean) : CommandArgumentProperties
|
||||
|
||||
public data class ScoreHolder(val allowMultiple: Boolean) : CommandArgumentProperties
|
||||
public data class Time(val min: Int) : CommandArgumentProperties
|
||||
public data class Registry(val registry: Identifier) : CommandArgumentProperties
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.command
|
||||
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeMcString
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.protocol.protocol.game.readIdentifier
|
||||
import cn.rtast.libmc.protocol.protocol.game.writeIdentifier
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public data class CommandNode(
|
||||
val type: NodeType,
|
||||
val isExecutable: Boolean,
|
||||
val isRestricted: Boolean,
|
||||
val childrenIndexes: List<Int>,
|
||||
val redirectNodeIndex: Int?,
|
||||
val name: String?,
|
||||
val parserId: Int?,
|
||||
val properties: CommandArgumentProperties?,
|
||||
val suggestionsType: CommandSuggestionsType?,
|
||||
) {
|
||||
public enum class NodeType(public val id: Int) {
|
||||
Root(0), Literal(1), Argument(2);
|
||||
|
||||
public companion object {
|
||||
public const val NODE_TYPE_MASK: Int = 0x03
|
||||
public const val IS_EXECUTABLE_MASK: Int = 0x04
|
||||
public const val HAS_REDIRECT_MASK: Int = 0x08
|
||||
public const val HAS_SUGGESTIONS_TYPE_MASK: Int = 0x10
|
||||
public const val IS_RESTRICTED_MASK: Int = 0x20
|
||||
|
||||
public fun fromID(id: Int): NodeType = entries.first { it.id == id }
|
||||
}
|
||||
}
|
||||
|
||||
internal companion object Codec : PacketCodec<CommandNode> {
|
||||
private const val PARSER_INTEGER = 3
|
||||
private const val PARSER_LONG = 4
|
||||
private const val PARSER_FLOAT = 1
|
||||
private const val PARSER_DOUBLE = 2
|
||||
private const val PARSER_STRING = 5
|
||||
private const val PARSER_ENTITY = 6
|
||||
private const val PARSER_SCORE_HOLDER = 31
|
||||
private const val PARSER_TIME = 43
|
||||
private const val PARSER_RESOURCE_OR_TAG = 44
|
||||
private const val PARSER_RESOURCE_OR_TAG_KEY = 45
|
||||
private const val PARSER_RESOURCE = 46
|
||||
private const val PARSER_RESOURCE_KEY = 47
|
||||
private const val PARSER_RESOURCE_SELECTOR = 48
|
||||
|
||||
override suspend fun encode(buffer: BytesBuffer, value: CommandNode) {
|
||||
var flags = value.type.id and NodeType.NODE_TYPE_MASK
|
||||
if (value.isExecutable) flags = flags or NodeType.IS_EXECUTABLE_MASK
|
||||
if (value.redirectNodeIndex != null) flags = flags or NodeType.HAS_REDIRECT_MASK
|
||||
if (value.suggestionsType != null) flags = flags or NodeType.HAS_SUGGESTIONS_TYPE_MASK
|
||||
if (value.isRestricted) flags = flags or NodeType.IS_RESTRICTED_MASK
|
||||
buffer.writeByte(flags.toByte())
|
||||
buffer.writeVarInt(value.childrenIndexes.size)
|
||||
value.childrenIndexes.forEach { buffer.writeVarInt(it) }
|
||||
if (value.redirectNodeIndex != null) buffer.writeVarInt(value.redirectNodeIndex)
|
||||
if (value.type == NodeType.Literal || value.type == NodeType.Argument) buffer.writeMcString(
|
||||
value.name ?: ""
|
||||
)
|
||||
|
||||
if (value.type == NodeType.Argument) {
|
||||
val parserId = value.parserId ?: 0
|
||||
buffer.writeVarInt(parserId)
|
||||
value.properties?.let { buffer.encodeProperties(it) } ?: buffer.encodeProperties(
|
||||
CommandArgumentProperties.Empty
|
||||
)
|
||||
}
|
||||
if (value.suggestionsType != null) buffer.writeIdentifier(value.suggestionsType.id)
|
||||
}
|
||||
|
||||
override suspend fun decode(buffer: BytesBuffer): CommandNode {
|
||||
val flags = buffer.readByte().toInt() and 0xFF
|
||||
val nodeType = NodeType.fromID(flags and NodeType.NODE_TYPE_MASK)
|
||||
val isExecutable = (flags and NodeType.IS_EXECUTABLE_MASK) != 0
|
||||
val hasRedirect = (flags and NodeType.HAS_REDIRECT_MASK) != 0
|
||||
val hasSuggestionsType = (flags and NodeType.HAS_SUGGESTIONS_TYPE_MASK) != 0
|
||||
val isRestricted = (flags and NodeType.IS_RESTRICTED_MASK) != 0
|
||||
val childrenCount = buffer.readVarInt()
|
||||
val childrenIndexes = ArrayList<Int>(childrenCount)
|
||||
repeat(childrenCount) { childrenIndexes.add(buffer.readVarInt()) }
|
||||
val redirectNodeIndex = if (hasRedirect) buffer.readVarInt() else null
|
||||
val name =
|
||||
if (nodeType == NodeType.Literal || nodeType == NodeType.Argument) buffer.readMcString() else null
|
||||
var parserId: Int? = null
|
||||
var properties: CommandArgumentProperties? = null
|
||||
if (nodeType == NodeType.Argument) {
|
||||
val pId = buffer.readVarInt()
|
||||
parserId = pId
|
||||
properties = buffer.decodeProperties(pId)
|
||||
}
|
||||
val suggestionsType =
|
||||
if (hasSuggestionsType) CommandSuggestionsType.fromIdentifier(buffer.readIdentifier()) else null
|
||||
return CommandNode(
|
||||
type = nodeType,
|
||||
isExecutable = isExecutable,
|
||||
isRestricted = isRestricted,
|
||||
childrenIndexes = childrenIndexes,
|
||||
redirectNodeIndex = redirectNodeIndex,
|
||||
name = name,
|
||||
parserId = parserId,
|
||||
properties = properties,
|
||||
suggestionsType = suggestionsType
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun BytesBuffer.decodeProperties(parserId: Int): CommandArgumentProperties {
|
||||
return when (parserId) {
|
||||
PARSER_INTEGER -> {
|
||||
val flags = readByte().toInt()
|
||||
val min = if ((flags and 0x01) != 0) readInt() else Int.MIN_VALUE
|
||||
val max = if ((flags and 0x02) != 0) readInt() else Int.MAX_VALUE
|
||||
CommandArgumentProperties.IntProp(min, max)
|
||||
}
|
||||
|
||||
PARSER_LONG -> {
|
||||
val flags = readByte().toInt()
|
||||
val min = if ((flags and 0x01) != 0) readLong() else Long.MIN_VALUE
|
||||
val max = if ((flags and 0x02) != 0) readLong() else Long.MAX_VALUE
|
||||
CommandArgumentProperties.LongProp(min, max)
|
||||
}
|
||||
|
||||
PARSER_FLOAT -> {
|
||||
val flags = readByte().toInt()
|
||||
val min = if ((flags and 0x01) != 0) readFloat() else -Float.MAX_VALUE
|
||||
val max = if ((flags and 0x02) != 0) readFloat() else Float.MAX_VALUE
|
||||
CommandArgumentProperties.FloatProp(min, max)
|
||||
}
|
||||
|
||||
PARSER_DOUBLE -> {
|
||||
val flags = readByte().toInt()
|
||||
val min = if ((flags and 0x01) != 0) readDouble() else -Double.MAX_VALUE
|
||||
val max = if ((flags and 0x02) != 0) readDouble() else Double.MAX_VALUE
|
||||
CommandArgumentProperties.DoubleProp(min, max)
|
||||
}
|
||||
|
||||
PARSER_STRING -> {
|
||||
val behaviorId = readVarInt()
|
||||
CommandArgumentProperties.StringProp(
|
||||
CommandArgumentProperties.StringProp.StringBehavior.fromId(behaviorId)
|
||||
)
|
||||
}
|
||||
|
||||
PARSER_ENTITY -> {
|
||||
val flags = readByte().toInt()
|
||||
CommandArgumentProperties.Entity(
|
||||
singleOnly = (flags and 0x01) != 0,
|
||||
playersOnly = (flags and 0x02) != 0
|
||||
)
|
||||
}
|
||||
|
||||
PARSER_SCORE_HOLDER -> {
|
||||
val flags = readByte().toInt()
|
||||
CommandArgumentProperties.ScoreHolder(allowMultiple = (flags and 0x01) != 0)
|
||||
}
|
||||
|
||||
PARSER_TIME -> CommandArgumentProperties.Time(min = readInt())
|
||||
PARSER_RESOURCE_OR_TAG, PARSER_RESOURCE_OR_TAG_KEY, PARSER_RESOURCE, PARSER_RESOURCE_KEY, PARSER_RESOURCE_SELECTOR -> {
|
||||
CommandArgumentProperties.Registry(readIdentifier())
|
||||
}
|
||||
|
||||
else -> CommandArgumentProperties.Empty
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun BytesBuffer.encodeProperties(properties: CommandArgumentProperties) {
|
||||
when (properties) {
|
||||
is CommandArgumentProperties.IntProp -> {
|
||||
var flags = 0
|
||||
if (properties.min != Int.MIN_VALUE) flags = flags or 0x01
|
||||
if (properties.max != Int.MAX_VALUE) flags = flags or 0x02
|
||||
writeByte(flags.toByte())
|
||||
if ((flags and 0x01) != 0) writeInt(properties.min)
|
||||
if ((flags and 0x02) != 0) writeInt(properties.max)
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.LongProp -> {
|
||||
var flags = 0
|
||||
if (properties.min != Long.MIN_VALUE) flags = flags or 0x01
|
||||
if (properties.max != Long.MAX_VALUE) flags = flags or 0x02
|
||||
writeByte(flags.toByte())
|
||||
if ((flags and 0x01) != 0) writeLong(properties.min)
|
||||
if ((flags and 0x02) != 0) writeLong(properties.max)
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.FloatProp -> {
|
||||
var flags = 0
|
||||
if (properties.min != -Float.MAX_VALUE) flags = flags or 0x01
|
||||
if (properties.max != Float.MAX_VALUE) flags = flags or 0x02
|
||||
writeByte(flags.toByte())
|
||||
if ((flags and 0x01) != 0) writeFloat(properties.min)
|
||||
if ((flags and 0x02) != 0) writeFloat(properties.max)
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.DoubleProp -> {
|
||||
var flags = 0
|
||||
if (properties.min != -Double.MAX_VALUE) flags = flags or 0x01
|
||||
if (properties.max != Double.MAX_VALUE) flags = flags or 0x02
|
||||
writeByte(flags.toByte())
|
||||
if ((flags and 0x01) != 0) writeDouble(properties.min)
|
||||
if ((flags and 0x02) != 0) writeDouble(properties.max)
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.StringProp -> writeVarInt(properties.behavior.id)
|
||||
is CommandArgumentProperties.Entity -> {
|
||||
var flags = 0
|
||||
if (properties.singleOnly) flags = flags or 0x01
|
||||
if (properties.playersOnly) flags = flags or 0x02
|
||||
writeByte(flags.toByte())
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.ScoreHolder -> {
|
||||
val flags = if (properties.allowMultiple) 0x01 else 0x00
|
||||
writeByte(flags.toByte())
|
||||
}
|
||||
|
||||
is CommandArgumentProperties.Time -> writeInt(properties.min)
|
||||
is CommandArgumentProperties.Registry -> writeIdentifier(properties.registry)
|
||||
CommandArgumentProperties.Empty -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.command
|
||||
|
||||
import cn.rtast.libmc.protocol.protocol.game.Identifier
|
||||
|
||||
public enum class CommandSuggestionsType(public val id: Identifier) {
|
||||
AskServer(Identifier.of("minecraft:ask_server")),
|
||||
AllRecipes(Identifier.of("minecraft:all_recipes")),
|
||||
AvailableSounds(Identifier.of("minecraft:available_sounds")),
|
||||
SummonableEntities(Identifier.of("minecraft:summonable_entities"));
|
||||
|
||||
public companion object {
|
||||
public fun fromIdentifier(id: Identifier?): CommandSuggestionsType? =
|
||||
entries.firstOrNull { it.id.raw == id?.raw }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.registry
|
||||
package cn.rtast.libmc.protocol.protocol.game.registry
|
||||
|
||||
public enum class ClientAction(public val actionID: Int) {
|
||||
/**
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.registry
|
||||
package cn.rtast.libmc.protocol.protocol.game.registry
|
||||
|
||||
public enum class GameDifficulty(public val id: Byte) {
|
||||
Peaceful(0),
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.registry.report
|
||||
package cn.rtast.libmc.protocol.protocol.game.registry.report
|
||||
|
||||
public enum class BuiltinServerLinkType(public val id: Int) {
|
||||
BUG_REPORT(0),
|
||||
+7
-7
@@ -5,17 +5,17 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.registry.report
|
||||
package cn.rtast.libmc.protocol.protocol.game.registry.report
|
||||
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeMcString
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
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.stream.BytesBuffer
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.readNetworkNBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.util.writeNetworkNBTCompound
|
||||
|
||||
public data class ServerLink(val label: ServerLinkLabel, val url: String) {
|
||||
internal companion object Codec : PacketCodec<ServerLink> {
|
||||
@@ -28,7 +28,7 @@ public data class ServerLink(val label: ServerLinkLabel, val url: String) {
|
||||
|
||||
is ServerLinkLabel.Custom -> {
|
||||
buffer.writeBoolean(false)
|
||||
buffer.writeNetworkNBTCompound(value.label.text)
|
||||
buffer.writeTextComponent(value.label.text)
|
||||
}
|
||||
}
|
||||
buffer.writeMcString(value.url)
|
||||
@@ -37,7 +37,7 @@ public data class ServerLink(val label: ServerLinkLabel, val url: String) {
|
||||
override suspend fun decode(buffer: BytesBuffer): ServerLink {
|
||||
val isBuiltin = buffer.readBoolean()
|
||||
val label = if (isBuiltin) ServerLinkLabel.Builtin(BuiltinServerLinkType.fromID(buffer.readVarInt()))
|
||||
else ServerLinkLabel.Custom(buffer.readNetworkNBTCompound())
|
||||
else ServerLinkLabel.Custom(buffer.readTextComponent())
|
||||
val url = buffer.readMcString()
|
||||
return ServerLink(label, url)
|
||||
}
|
||||
@@ -46,5 +46,5 @@ public data class ServerLink(val label: ServerLinkLabel, val url: String) {
|
||||
|
||||
public sealed interface ServerLinkLabel {
|
||||
public data class Builtin(val type: BuiltinServerLinkType) : ServerLinkLabel
|
||||
public data class Custom(val text: NBTCompound) : ServerLinkLabel
|
||||
public data class Custom(val text: TextComponent) : ServerLinkLabel
|
||||
}
|
||||
+2
-2
@@ -7,12 +7,12 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.scoreboard
|
||||
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
|
||||
public sealed interface ObjectivePayload {
|
||||
public object Remove : ObjectivePayload
|
||||
public data class Upsert(
|
||||
val displayName: NBTCompound,
|
||||
val displayName: TextComponent,
|
||||
val renderType: ObjectiveRenderType,
|
||||
val defaultNumberFormat: ScoreNumberFormat?,
|
||||
) : ObjectivePayload
|
||||
|
||||
+2
-2
@@ -7,11 +7,11 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.scoreboard
|
||||
|
||||
import cn.rtast.libmc.nbt.NBTCompound
|
||||
import cn.rtast.libmc.nbt.NBTTag
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
|
||||
public sealed interface ScoreNumberFormat {
|
||||
public object Blank : ScoreNumberFormat
|
||||
public data class Styled(val styling: NBTTag.CompoundTag) : ScoreNumberFormat
|
||||
public data class Fixed(val content: NBTCompound) : ScoreNumberFormat
|
||||
public data class Fixed(val content: TextComponent) : ScoreNumberFormat
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.session
|
||||
package cn.rtast.libmc.protocol.protocol.game.session
|
||||
|
||||
import cn.rtast.libmc.packet.PacketCodec
|
||||
import cn.rtast.libmc.primitives.readMcString
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.session
|
||||
package cn.rtast.libmc.protocol.util
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -7,18 +7,17 @@
|
||||
|
||||
package test
|
||||
|
||||
import cn.rtast.libmc.crypto.AuthenticationProvider
|
||||
import cn.rtast.libmc.crypto.RSA1024Encryptor
|
||||
import cn.rtast.libmc.crypto.Sha1Hasher
|
||||
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
||||
import cn.rtast.libmc.protocol.crypto.DefaultProtocolContext
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundCommandsPacket
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundDisconnectPlayPacket
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundDisguisedChatMessagePacket
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSystemChatMessagePacket
|
||||
import cn.rtast.libmc.protocol.util.generateOfflineUuid
|
||||
import kotlinx.coroutines.launch
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.math.BigInteger
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.security.KeyFactory
|
||||
import java.security.MessageDigest
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
@@ -64,24 +63,9 @@ class TestClientTestInJvm {
|
||||
accessToken,
|
||||
crypto = DefaultProtocolContext
|
||||
)
|
||||
// {
|
||||
// rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) }
|
||||
// sha1Hasher =
|
||||
// Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) }
|
||||
// cipherFactory = { key -> JvmAesCipher(key) }
|
||||
// authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
|
||||
// val connection = URL(url).openConnection() as HttpURLConnection
|
||||
// connection.requestMethod = "POST"
|
||||
// connection.doOutput = true
|
||||
// connection.setRequestProperty("Content-Type", "application/json")
|
||||
// connection.getOutputStream()
|
||||
// .use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) }
|
||||
// connection.disconnect()
|
||||
// }
|
||||
// }
|
||||
// cli.onPacket<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
// cli.onPacket<ClientboundLoginSuccessPacket> { println(it) }
|
||||
cli.on { packet, direction -> println("${direction} -> $packet") }
|
||||
cli.on { packet, direction -> println("$direction -> $packet") }
|
||||
cli.launch { cli.connect() }
|
||||
while (true) {
|
||||
}
|
||||
@@ -95,29 +79,10 @@ class TestClientTestInJvm {
|
||||
"11",
|
||||
generateOfflineUuid("11"),
|
||||
null,
|
||||
) {
|
||||
rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) }
|
||||
sha1Hasher =
|
||||
Sha1Hasher { serverId, secretKey, publicKey ->
|
||||
minecraftServerIdHash(
|
||||
serverId,
|
||||
secretKey,
|
||||
publicKey
|
||||
)
|
||||
}
|
||||
cipherFactory = { key -> JvmAesCipher(key) }
|
||||
// authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
|
||||
// val connection = URL(url).openConnection() as HttpURLConnection
|
||||
// connection.requestMethod = "POST"
|
||||
// connection.doOutput = true
|
||||
// connection.setRequestProperty("Content-Type", "application/json")
|
||||
// connection.getOutputStream()
|
||||
// .use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) }
|
||||
// connection.disconnect()
|
||||
// }
|
||||
}
|
||||
// cli.on<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
// cli.on<ClientboundLoginSuccessPacket> { println(it) }
|
||||
crypto = DefaultProtocolContext
|
||||
)
|
||||
// cli.on { packet, direction -> println("$direction -> $packet") }
|
||||
cli.onPacket<ClientboundSystemChatMessagePacket> { println(it.content.content) }
|
||||
cli.launch { cli.connect() }
|
||||
while (true) {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user