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
|
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()
|
val count = this.readVarInt()
|
||||||
return LongArray(count) { this.readLong() }
|
return LongArray(count) { this.readLong() }
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun BytesBuffer.writeBitSet(data: LongArray) {
|
public suspend fun BytesBuffer.writeBitSet(data: BitSet) {
|
||||||
this.writeVarInt(data.size)
|
this.writeVarInt(data.size)
|
||||||
for (i in data.indices) this.writeLong(data[i])
|
for (i in data.indices) this.writeLong(data[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun LongArray.countSetBits(): Int {
|
public fun BitSet.countSetBits(): Int {
|
||||||
var count = 0
|
var count = 0
|
||||||
for (i in indices) count += this[i].countOneBits()
|
for (i in indices) count += this[i].countOneBits()
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun LongArray.getBit(bitIndex: Int): Boolean {
|
public fun BitSet.getBit(bitIndex: Int): Boolean {
|
||||||
val longIndex = bitIndex shr 6
|
val longIndex = bitIndex shr 6
|
||||||
if (longIndex !in this.indices) return false
|
if (longIndex !in this.indices) return false
|
||||||
val bitOffset = bitIndex and 63
|
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> {
|
public suspend inline fun <T> BytesBuffer.readPrefixed(reader: BytesBuffer.() -> T): List<T> {
|
||||||
val count = this.readVarInt()
|
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)
|
val list = ArrayList<T>(count)
|
||||||
repeat(count) { _ -> list.add(this.reader()) }
|
repeat(count) { _ -> list.add(this.reader()) }
|
||||||
return list
|
return list
|
||||||
|
|||||||
+3
-3
@@ -14,7 +14,7 @@ import cn.rtast.libmc.primitives.readVarInt
|
|||||||
import cn.rtast.libmc.primitives.writeVarInt
|
import cn.rtast.libmc.primitives.writeVarInt
|
||||||
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
||||||
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
|
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.BytesBuffer
|
||||||
import cn.rtast.libmc.stream.wrap
|
import cn.rtast.libmc.stream.wrap
|
||||||
import cn.rtast.libmc.zlibCompress
|
import cn.rtast.libmc.zlibCompress
|
||||||
@@ -53,7 +53,7 @@ public class NetworkChannel internal constructor(
|
|||||||
}
|
}
|
||||||
val currentState = stateMachine.currentState
|
val currentState = stateMachine.currentState
|
||||||
val packetId = payloadBuf.readVarInt()
|
val packetId = payloadBuf.readVarInt()
|
||||||
val packet = GameProtocols.clientboundGameProtocols
|
val packet = GamePacketsProtocolCodec.clientboundGameProtocols
|
||||||
.getRegistry(currentState)
|
.getRegistry(currentState)
|
||||||
.decodePacket(packetId, payloadBuf)
|
.decodePacket(packetId, payloadBuf)
|
||||||
dispatcher.dispatchReceive(packet)
|
dispatcher.dispatchReceive(packet)
|
||||||
@@ -67,7 +67,7 @@ public class NetworkChannel internal constructor(
|
|||||||
*/
|
*/
|
||||||
public suspend fun sendPacket(packet: MinecraftPacket) {
|
public suspend fun sendPacket(packet: MinecraftPacket) {
|
||||||
val uncompressedBodyBuf = BytesBuffer()
|
val uncompressedBodyBuf = BytesBuffer()
|
||||||
GameProtocols.serverboundGameProtocols
|
GamePacketsProtocolCodec.serverboundGameProtocols
|
||||||
.getRegistry(stateMachine.currentState)
|
.getRegistry(stateMachine.currentState)
|
||||||
.encodePacket(uncompressedBodyBuf, packet)
|
.encodePacket(uncompressedBodyBuf, packet)
|
||||||
val uncompressedData = uncompressedBodyBuf.toByteArray()
|
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.ProtocolState
|
||||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry
|
import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry
|
||||||
|
|
||||||
internal object GameProtocols {
|
internal object GamePacketsProtocolCodec {
|
||||||
val clientboundGameProtocols = ProtocolStateRegistry().apply {
|
val clientboundGameProtocols = ProtocolStateRegistry().apply {
|
||||||
register(ProtocolState.STATUS) {
|
register(ProtocolState.STATUS) {
|
||||||
register(0x00, ClientboundStatusResponsePacket)
|
register(0x00, ClientboundStatusResponsePacket)
|
||||||
@@ -111,7 +111,7 @@ internal object GameProtocols {
|
|||||||
// register(0x30, ClientboundLightUpdatePacket)
|
// register(0x30, ClientboundLightUpdatePacket)
|
||||||
register(0x31, ClientboundLoginPlayPacket)
|
register(0x31, ClientboundLoginPlayPacket)
|
||||||
register(0x32, ClientboundLowDiskSpaceWarningPacket)
|
register(0x32, ClientboundLowDiskSpaceWarningPacket)
|
||||||
// register(0x33, ClientboundMapItemDataPacket)
|
register(0x33, ClientboundMapItemDataPacket)
|
||||||
// register(0x34, ClientboundMerchantOffersPacket)
|
// register(0x34, ClientboundMerchantOffersPacket)
|
||||||
register(0x35, ClientboundUpdateEntityPositionPacket)
|
register(0x35, ClientboundUpdateEntityPositionPacket)
|
||||||
register(0x36, ClientboundUpdateEntityPositionAndRotationPacket)
|
register(0x36, ClientboundUpdateEntityPositionAndRotationPacket)
|
||||||
+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,
|
null,
|
||||||
crypto = DefaultProtocolContext
|
crypto = DefaultProtocolContext
|
||||||
)
|
)
|
||||||
// cli.on { packet, direction -> println("$direction -> $packet") }
|
cli.on { packet, direction -> println("$direction -> $packet") }
|
||||||
cli.onPacket<ClientboundSystemChatMessagePacket> { println(it.content.content) }
|
// cli.onPacket<ClientboundSystemChatMessagePacket> { println(it.content.content) }
|
||||||
cli.launch { cli.connect() }
|
cli.launch { cli.connect() }
|
||||||
while (true) {
|
while (true) {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user