Implemented clientbound packet map_item_data 0x33
This commit is contained in:
7 files changed
+136
-11
No files matched your search
@@ -9,23 +9,25 @@ package cn.rtast.libmc.primitives
|
||||
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
|
||||
public suspend fun BytesBuffer.readBitSet(): LongArray {
|
||||
public typealias BitSet = LongArray
|
||||
|
||||
public suspend fun BytesBuffer.readBitSet(): BitSet {
|
||||
val count = this.readVarInt()
|
||||
return LongArray(count) { this.readLong() }
|
||||
}
|
||||
|
||||
public suspend fun BytesBuffer.writeBitSet(data: LongArray) {
|
||||
public suspend fun BytesBuffer.writeBitSet(data: BitSet) {
|
||||
this.writeVarInt(data.size)
|
||||
for (i in data.indices) this.writeLong(data[i])
|
||||
}
|
||||
|
||||
public fun LongArray.countSetBits(): Int {
|
||||
public fun BitSet.countSetBits(): Int {
|
||||
var count = 0
|
||||
for (i in indices) count += this[i].countOneBits()
|
||||
return count
|
||||
}
|
||||
|
||||
public fun LongArray.getBit(bitIndex: Int): Boolean {
|
||||
public fun BitSet.getBit(bitIndex: Int): Boolean {
|
||||
val longIndex = bitIndex shr 6
|
||||
if (longIndex !in this.indices) return false
|
||||
val bitOffset = bitIndex and 63
|
||||
|
||||
@@ -45,6 +45,10 @@ public suspend fun BytesBuffer.writePrefixedStringArray(value: List<String>) {
|
||||
|
||||
public suspend 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."
|
||||
}
|
||||
val list = ArrayList<T>(count)
|
||||
repeat(count) { _ -> list.add(this.reader()) }
|
||||
return list
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
||||
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
|
||||
import cn.rtast.libmc.protocol.protocol.GameProtocols
|
||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec
|
||||
import cn.rtast.libmc.stream.BytesBuffer
|
||||
import cn.rtast.libmc.stream.wrap
|
||||
import cn.rtast.libmc.zlibCompress
|
||||
@@ -53,7 +53,7 @@ public class NetworkChannel internal constructor(
|
||||
}
|
||||
val currentState = stateMachine.currentState
|
||||
val packetId = payloadBuf.readVarInt()
|
||||
val packet = GameProtocols.clientboundGameProtocols
|
||||
val packet = GamePacketsProtocolCodec.clientboundGameProtocols
|
||||
.getRegistry(currentState)
|
||||
.decodePacket(packetId, payloadBuf)
|
||||
dispatcher.dispatchReceive(packet)
|
||||
@@ -67,7 +67,7 @@ public class NetworkChannel internal constructor(
|
||||
*/
|
||||
public suspend fun sendPacket(packet: MinecraftPacket) {
|
||||
val uncompressedBodyBuf = BytesBuffer()
|
||||
GameProtocols.serverboundGameProtocols
|
||||
GamePacketsProtocolCodec.serverboundGameProtocols
|
||||
.getRegistry(stateMachine.currentState)
|
||||
.encodePacket(uncompressedBodyBuf, packet)
|
||||
val uncompressedData = uncompressedBodyBuf.toByteArray()
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.readOptional
|
||||
import cn.rtast.libmc.primitives.readPrefixed
|
||||
import cn.rtast.libmc.primitives.readPrefixedByteArray
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
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.stream.BytesBuffer
|
||||
|
||||
public data class ClientboundMapItemDataPacket(
|
||||
val mapId: Int,
|
||||
val scale: Byte,
|
||||
val locked: Boolean,
|
||||
val icons: List<MapItemIcon>?,
|
||||
val colorPatch: MapItemColorPatch?,
|
||||
) : MinecraftPacket {
|
||||
internal companion object Codec : PacketCodec<ClientboundMapItemDataPacket> {
|
||||
override suspend fun encode(buffer: BytesBuffer, value: ClientboundMapItemDataPacket) {}
|
||||
override suspend fun decode(buffer: BytesBuffer): ClientboundMapItemDataPacket {
|
||||
val mapId = buffer.readVarInt()
|
||||
val scale = buffer.readByte()
|
||||
val locked = buffer.readBoolean()
|
||||
val icons = buffer.readOptional {
|
||||
readPrefixed {
|
||||
val type = MapItemIconType.fromID(readVarInt())
|
||||
val x = readByte()
|
||||
val z = readByte()
|
||||
val direction = readByte()
|
||||
val displayName = readOptional { readTextComponent() }
|
||||
MapItemIcon(type, x, z, direction, displayName)
|
||||
}
|
||||
}
|
||||
val columns = buffer.readUByte()
|
||||
val colorPatch = if (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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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 GameProtocols {
|
||||
internal object GamePacketsProtocolCodec {
|
||||
val clientboundGameProtocols = ProtocolStateRegistry().apply {
|
||||
register(ProtocolState.STATUS) {
|
||||
register(0x00, ClientboundStatusResponsePacket)
|
||||
@@ -111,7 +111,7 @@ internal object GameProtocols {
|
||||
// register(0x30, ClientboundLightUpdatePacket)
|
||||
register(0x31, ClientboundLoginPlayPacket)
|
||||
register(0x32, ClientboundLowDiskSpaceWarningPacket)
|
||||
// register(0x33, ClientboundMapItemDataPacket)
|
||||
register(0x33, ClientboundMapItemDataPacket)
|
||||
// register(0x34, ClientboundMerchantOffersPacket)
|
||||
register(0x35, ClientboundUpdateEntityPositionPacket)
|
||||
register(0x36, ClientboundUpdateEntityPositionAndRotationPacket)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/7
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol.game.item
|
||||
|
||||
import cn.rtast.libmc.protocol.protocol.game.chat.TextComponent
|
||||
|
||||
public enum class MapItemIconType(public val id: Int) {
|
||||
WHITE_ARROW(0), GREEN_ARROW(1), RED_ARROW(2), BLUE_ARROW(3),
|
||||
WHITE_CROSS(4), RED_POINTER(5), WHITE_CIRCLE(6), SMALL_WHITE_CIRCLE(7),
|
||||
MANSION(8), MONUMENT(9), WHITE_BANNER(10), ORANGE_BANNER(11),
|
||||
MAGENTA_BANNER(12), LIGHT_BLUE_BANNER(13), YELLOW_BANNER(14),
|
||||
LIME_BANNER(15), PINK_BANNER(16), GRAY_BANNER(17), LIGHT_GRAY_BANNER(18),
|
||||
CYAN_BANNER(19), PURPLE_BANNER(20), BLUE_BANNER(21), BROWN_BANNER(22),
|
||||
GREEN_BANNER(23), RED_BANNER(24), BLACK_BANNER(25), TREASURE_MARKER(26),
|
||||
DESERT_VILLAGE(27), PLAINS_VILLAGE(28), SAVANNA_VILLAGE(29), SNOWY_VILLAGE(30),
|
||||
TAIGA_VILLAGE(31), JUNGLE_TEMPLE(32), SWAMP_HUT(33), TRIAL_CHAMBERS(34);
|
||||
|
||||
public companion object {
|
||||
public fun fromID(id: Int): MapItemIconType = entries.find { it.id == id } ?: WHITE_ARROW
|
||||
}
|
||||
}
|
||||
|
||||
public data class MapItemIcon(
|
||||
val type: MapItemIconType,
|
||||
val x: Byte,
|
||||
val z: Byte,
|
||||
val direction: Byte,
|
||||
val displayName: TextComponent?
|
||||
)
|
||||
|
||||
public data class MapItemColorPatch(
|
||||
val columns: UByte,
|
||||
val rows: UByte,
|
||||
val xOffset: UByte,
|
||||
val zOffset: UByte,
|
||||
val data: ByteArray
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
other as MapItemColorPatch
|
||||
if (columns != other.columns) return false
|
||||
if (rows != other.rows) return false
|
||||
if (xOffset != other.xOffset) return false
|
||||
if (zOffset != other.zOffset) return false
|
||||
if (!data.contentEquals(other.data)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = columns.hashCode()
|
||||
result = 31 * result + rows.hashCode()
|
||||
result = 31 * result + xOffset.hashCode()
|
||||
result = 31 * result + zOffset.hashCode()
|
||||
result = 31 * result + data.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,8 @@ class TestClientTestInJvm {
|
||||
null,
|
||||
crypto = DefaultProtocolContext
|
||||
)
|
||||
// cli.on { packet, direction -> println("$direction -> $packet") }
|
||||
cli.onPacket<ClientboundSystemChatMessagePacket> { println(it.content.content) }
|
||||
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