Introduce new packet parsing functionality with a PacketDecoder and serializer-based system. Add CLI commands for listing packets, displaying replay info, and extracting schematics. Enhance project dependencies and build configuration to support serialization and CLI tools.
110 lines
5.1 KiB
Kotlin
110 lines
5.1 KiB
Kotlin
package de.chaoscaot.replay.parser
|
|
|
|
import kotlinx.serialization.ExperimentalSerializationApi
|
|
import kotlinx.serialization.KSerializer
|
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
|
import kotlinx.serialization.encoding.AbstractDecoder
|
|
import kotlinx.serialization.encoding.CompositeDecoder
|
|
import kotlinx.serialization.modules.EmptySerializersModule
|
|
import kotlinx.serialization.modules.SerializersModule
|
|
import java.io.DataInputStream
|
|
|
|
@OptIn(ExperimentalSerializationApi::class)
|
|
fun readPacket(type: Int, stream: DataInputStream): Any = PacketType.entries.find { it.id == type }.let {
|
|
if (it == null) {
|
|
println("next bytes: ${stream.readNBytes(40).map { it.toUByte().toString(16) }.joinToString("")}")
|
|
throw IllegalArgumentException("Unknown packet type: $type")
|
|
}
|
|
|
|
it
|
|
}.let { PacketDecoder(stream).decodeSerializableValue(it.serializer) }
|
|
|
|
fun readPackets(stream: DataInputStream): Sequence<Any> = sequence {
|
|
while (stream.available() > 0) {
|
|
yield(readPacket(stream.readByte().toUByte().toInt(), stream))
|
|
}
|
|
}
|
|
|
|
@ExperimentalSerializationApi
|
|
class PacketDecoder(val input: DataInputStream, var elementsCount: Int = 0): AbstractDecoder() {
|
|
private var elementIndex = 0
|
|
override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
|
|
override fun decodeByte(): Byte = input.readByte()
|
|
override fun decodeShort(): Short = input.readShort()
|
|
override fun decodeInt(): Int = input.readInt()
|
|
override fun decodeLong(): Long = input.readLong()
|
|
override fun decodeFloat(): Float = input.readFloat()
|
|
override fun decodeDouble(): Double = input.readDouble()
|
|
override fun decodeChar(): Char = input.readChar()
|
|
override fun decodeString(): String = input.readUTF()
|
|
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
|
|
override val serializersModule: SerializersModule = EmptySerializersModule()
|
|
|
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
|
if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
|
|
return elementIndex++
|
|
}
|
|
|
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
|
|
PacketDecoder(input, descriptor.elementsCount)
|
|
|
|
override fun decodeSequentially(): Boolean = true
|
|
|
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
|
|
decodeInt().also { elementsCount = it }
|
|
|
|
override fun decodeNotNullMark(): Boolean = decodeBoolean()
|
|
}
|
|
|
|
enum class PacketType(val id: Int, val serializer: KSerializer<out Any>) {
|
|
USER_JOIN_PACKET(0x00, UserJoinPacket.serializer()),
|
|
ENTITY_MOVE_PACKET(0x01, EntityMovePacket.serializer()),
|
|
ENTITY_DESPAWNS_PACKET(0x02, EntityDespawnsPacket.serializer()),
|
|
PLAYER_SNEAK_PACKET(0x03, PlayerSneakPacket.serializer()),
|
|
ENTITY_ANIMATION_PACKET(0x04, EntityAnimationPacket.serializer()),
|
|
TNT_SPAWN_PACKET(0x05, TNTSpawnPacket.serializer()),
|
|
ENTITY_SPEED_PACKET(0x06, EntitySpeedPacket.serializer()),
|
|
PLAYER_ITEM_PACKET(0x07, PlayerItemPacket.serializer()),
|
|
ARROW_SPAWN_PACKET(0x08, ArrowSpawnPacket.serializer()),
|
|
FIREBALL_SPAWN_PACKET(0x09, FireballSpawnPacket.serializer()),
|
|
BOW_SPAN_PACKET(0x0a, BowSpanPacket.serializer()),
|
|
PLAYER_DAMAGE_PACKET(0x0b, PlayerDamagePacket.serializer()),
|
|
SET_ON_FIRE_PACKET(0x0c, SetOnFirePacket.serializer()),
|
|
|
|
LEGACY_ARENA_INFO(0x20, LegacyArenaInfo.serializer()),
|
|
ARENA_INFO(0x21, ArenaInfo.serializer()),
|
|
|
|
LEGACY_BLOCK_PACKET(0x30, LegacyBlockPacket.serializer()),
|
|
PARTICLE_PACKET(0x31, ParticlePacket.serializer()),
|
|
SOUND_PACKET(0x32, SoundPacket.serializer()),
|
|
LEGACY_SHORT_BLOCK_PACKET(0x33, LegacyShortBlockPacket.serializer()),
|
|
SOUND_AT_PLAYER_PACKET(0x34, SoundAtPlayerPacket.serializer()),
|
|
SHORT_BLOCK_PACKET(0x035, ShortBlockPacket.serializer()),
|
|
BLOCK_PACKET(0x36, BlockPacket.serializer()),
|
|
|
|
LEGACY_CHAT_PACKET(0xa0, LegacyChatPacket.serializer()),
|
|
LEGACY_ACTION_BAR_PACKET(0xa1, LegacyActionBarPacket.serializer()),
|
|
LEGACY_SYSTEM_PACKET(0xa2, LegacySystemPacket.serializer()),
|
|
COUNTDOWN_PACKET(0xa3, CountdownPacket.serializer()),
|
|
CHAT_PACKET(0xa4, ChatPacket.serializer()),
|
|
SYSTEM_PACKET(0xa5, SystemPacket.serializer()),
|
|
|
|
BLUE_SCHEM_PACKET(0xb0, BlueSchemPacket.serializer()),
|
|
RED_SCHEM_PACKET(0xb1, RedSchemPacket.serializer()),
|
|
TEAM_ID_PACKET(0xb2, TeamIDPacket.serializer()),
|
|
BLUE_EMBEDDED_SCHEM_PACKET(0xb3, BlueEmbeddedSchemPacket.serializer()),
|
|
RED_EMBEDDED_SCHEM_PACKET(0xb4, RedEmbeddedSchemPacket.serializer()),
|
|
|
|
LEGACY_SCOREBOARD_TITLE_PACKET(0xc0, LegacyScoreboardTitlePacket.serializer()),
|
|
LEGACY_SCOREBOARD_DATA_PACKET(0xc1, LegacyScoreboardDataPacket.serializer()),
|
|
LEGACY_BOSS_BAR_PACKET(0xc2, LegacyBossBarPacket.serializer()),
|
|
LEGACY_SUBTITLE_PACKET(0xc3, LegacySubtitlePacket.serializer()),
|
|
LEGACY_PRINT_WIN_PACKET(0xc4, LegacyPrintWinPacket.serializer()),
|
|
|
|
SUBTITLE_PACKET(0xc5, SubtitlePacket.serializer()),
|
|
WIN_PACKET(0xc6, WinPacket.serializer()),
|
|
BOSS_BAR_PACKET(0xc7, BossBarPacket.serializer()),
|
|
|
|
COMMENT_PACKET(0xfe, CommentPacket.serializer()),
|
|
TICK_PACKET(0xff, TickPacket.serializer()),
|
|
} |