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 = 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) { 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()), }