Add packet parsing, CLI commands, and serialization support
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.
This commit is contained in:
6
.idea/gradle.xml
generated
6
.idea/gradle.xml
generated
@ -1,10 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@ -12,6 +12,9 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.10"
|
||||
kotlin("plugin.serialization") version "2.1.0"
|
||||
application
|
||||
}
|
||||
|
||||
group = "de.chaoscaot"
|
||||
@ -11,6 +13,13 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
implementation("com.github.ajalt.clikt:clikt:5.0.3")
|
||||
implementation("com.github.ajalt.clikt:clikt-markdown:5.0.3")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
||||
implementation("com.github.ajalt.mordant:mordant:3.0.2")
|
||||
implementation("com.github.ajalt.mordant:mordant-coroutines:3.0.2")
|
||||
implementation("com.github.ajalt.mordant:mordant-markdown:3.0.2")
|
||||
implementation("dev.dewy:nbt:1.5.1")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
||||
@ -1,2 +1,33 @@
|
||||
package de.chaoscaot.replay
|
||||
|
||||
import com.github.ajalt.clikt.core.*
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.arguments.optional
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import de.chaoscaot.replay.commands.*
|
||||
import java.io.File
|
||||
|
||||
data class RunConfig(var replay: File? = null)
|
||||
|
||||
object ReplayUtils : CliktCommand() {
|
||||
override val printHelpOnEmptyArgs = true
|
||||
|
||||
private val config by findOrSetObject { RunConfig() }
|
||||
private val replay by argument(help = "Replay ID or Path").optional()
|
||||
private val path by option("-p", "--path", help = "Replay is a Path").flag()
|
||||
|
||||
override fun run() {
|
||||
if (replay != null) {
|
||||
if (path) {
|
||||
config.replay = File(replay!!)
|
||||
} else {
|
||||
config.replay = File("/mnt/storage/replays/$replay.replay")
|
||||
}
|
||||
} else {
|
||||
config.replay = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) = ReplayUtils.subcommands(Info, ListPackets, ExtractSchematics).main(readln().split(" "))
|
||||
@ -1,4 +1,43 @@
|
||||
package de.chaoscaot.replay.commands
|
||||
|
||||
object ExtractSchematics {
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import de.chaoscaot.replay.RunConfig
|
||||
import de.chaoscaot.replay.parser.BlueEmbeddedSchemPacket
|
||||
import de.chaoscaot.replay.parser.RedEmbeddedSchemPacket
|
||||
import de.chaoscaot.replay.parser.ReplayParser
|
||||
import com.github.ajalt.mordant.rendering.TextColors.*
|
||||
import com.github.ajalt.mordant.rendering.TextStyles
|
||||
import dev.dewy.nbt.Nbt
|
||||
import dev.dewy.nbt.io.CompressionType
|
||||
import java.io.File
|
||||
|
||||
object ExtractSchematics: CliktCommand() {
|
||||
private val config by requireObject<RunConfig>()
|
||||
|
||||
override fun run() {
|
||||
if (config.replay == null) {
|
||||
error("No Replay specified")
|
||||
}
|
||||
|
||||
val nbt by lazy { Nbt() }
|
||||
|
||||
val packets = ReplayParser.read(config.replay!!).toList()
|
||||
|
||||
val blueSchem = packets.filterIsInstance<BlueEmbeddedSchemPacket>().firstOrNull()
|
||||
|
||||
if (blueSchem == null) {
|
||||
echo((brightRed + TextStyles.bold)("No Blue Embedded Schem found"))
|
||||
} else {
|
||||
nbt.toFile(blueSchem.schematic.data, File("blue.dump"), CompressionType.GZIP)
|
||||
}
|
||||
|
||||
val redSchem = packets.filterIsInstance<RedEmbeddedSchemPacket>().firstOrNull()
|
||||
|
||||
if (redSchem == null) {
|
||||
echo((brightRed + TextStyles.bold)("No Red Embedded Schem found"))
|
||||
} else {
|
||||
nbt.toFile(redSchem.schematic.data, File("red.dump"), CompressionType.GZIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,53 @@
|
||||
package de.chaoscaot.replay.commands
|
||||
|
||||
object Info {
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.requireObject
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.multiple
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.github.ajalt.mordant.table.table
|
||||
import de.chaoscaot.replay.RunConfig
|
||||
import de.chaoscaot.replay.parser.ReplayParser
|
||||
import de.chaoscaot.replay.parser.UserJoinPacket
|
||||
|
||||
object Info : CliktCommand() {
|
||||
private val config by requireObject<RunConfig>()
|
||||
|
||||
private val players by option("-p", "--players").flag()
|
||||
private val toCount by option("-c", "--count").multiple(required = false)
|
||||
|
||||
override fun run() {
|
||||
if (config.replay == null) {
|
||||
error("No Replay specified")
|
||||
}
|
||||
|
||||
val packets = ReplayParser.read(config.replay!!).toList()
|
||||
|
||||
if (players) {
|
||||
echo("Players:")
|
||||
packets.filterIsInstance<UserJoinPacket>().map { it.userId }.forEach { echo("\t$it") }
|
||||
echo()
|
||||
}
|
||||
|
||||
if (toCount.isNotEmpty()) {
|
||||
echo(table {
|
||||
header {
|
||||
row("PacketType", "Count")
|
||||
}
|
||||
|
||||
body {
|
||||
toCount.forEach { name ->
|
||||
val klass = packets.firstOrNull { it.javaClass.simpleName.lowercase() == "${name}packet".lowercase() }?.javaClass
|
||||
|
||||
if (klass == null) {
|
||||
row(name, 0)
|
||||
} else {
|
||||
row(klass.simpleName, packets.count { it.javaClass == klass })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,11 @@
|
||||
package de.chaoscaot.replay.commands
|
||||
|
||||
class ListPackets {
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import de.chaoscaot.replay.parser.PacketType
|
||||
|
||||
object ListPackets : CliktCommand() {
|
||||
override fun run() {
|
||||
echo("Packets:")
|
||||
PacketType.entries.forEach { echo("\t${it.name.lowercase().replace("_packet", "").replace("_", "").padEnd(25, ' ')}: 0x${it.id.toString(16).padStart(2, '0')}") }
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,32 @@
|
||||
package de.chaoscaot.replay.parser
|
||||
|
||||
class EmbeddedSchematic {
|
||||
import dev.dewy.nbt.Nbt
|
||||
import dev.dewy.nbt.tags.collection.CompoundTag
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.ByteArraySerializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.io.DataInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
@Serializable(with = EmbeddedSchematicSerializer::class)
|
||||
data class EmbeddedSchematic(val data: CompoundTag)
|
||||
|
||||
object EmbeddedSchematicSerializer: KSerializer<EmbeddedSchematic> {
|
||||
private val delegateSerializer = ByteArraySerializer()
|
||||
private val nbt = Nbt()
|
||||
|
||||
override val descriptor: SerialDescriptor = SerialDescriptor("de.chaoscaot.EmbeddedSchematic", delegateSerializer.descriptor)
|
||||
|
||||
override fun deserialize(decoder: Decoder): EmbeddedSchematic = EmbeddedSchematic(nbt.fromStream(DataInputStream(DecoderStream(decoder))))
|
||||
|
||||
override fun serialize(encoder: Encoder, value: EmbeddedSchematic) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class DecoderStream(private val decoder: Decoder): InputStream() {
|
||||
override fun read(): Int = decoder.decodeByte().toUByte().toInt()
|
||||
}
|
||||
@ -1,3 +1,57 @@
|
||||
package de.chaoscaot.replay.parser
|
||||
|
||||
data class Message()
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
/*
|
||||
* Message-Format
|
||||
* String message + byte prefixed Object-Params + byte 0x00
|
||||
* 0x00: End of message
|
||||
* 0x01: boolean following
|
||||
* 0x02: byte following
|
||||
* 0x03: short following
|
||||
* 0x04: int following
|
||||
* 0x05: float following
|
||||
* 0x06: double following
|
||||
* 0x07: String following
|
||||
* 0x08: Message following
|
||||
*/
|
||||
|
||||
@Serializable(with = MessageSerializer::class)
|
||||
data class Message(val message: String, val params: List<Any>)
|
||||
|
||||
object MessageSerializer: KSerializer<Message> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("de.chaoscaot.Message", PrimitiveKind.STRING)
|
||||
|
||||
override fun deserialize(decoder: Decoder): Message {
|
||||
val msg = decoder.decodeString()
|
||||
val params = mutableListOf<Any>()
|
||||
|
||||
var type: Byte
|
||||
|
||||
while (decoder.decodeByte().also { type = it } != 0.toByte()) {
|
||||
when (type.toInt()) {
|
||||
1 -> params.add(decoder.decodeBoolean())
|
||||
2 -> params.add(decoder.decodeByte())
|
||||
3 -> params.add(decoder.decodeShort())
|
||||
4 -> params.add(decoder.decodeInt())
|
||||
5 -> params.add(decoder.decodeFloat())
|
||||
6 -> params.add(decoder.decodeDouble())
|
||||
7 -> params.add(decoder.decodeString())
|
||||
8 -> params.add(decoder.decodeSerializableValue(this))
|
||||
}
|
||||
}
|
||||
|
||||
return Message(msg, params)
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Message) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,110 @@
|
||||
package de.chaoscaot.replay.parser.packets
|
||||
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
|
||||
|
||||
abstract class Packet(val type: PacketType)
|
||||
@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")
|
||||
}
|
||||
|
||||
interface PacketReader<T> {
|
||||
fun readPacket(stream: DataInputStream): T
|
||||
it
|
||||
}.let { PacketDecoder(stream).decodeSerializableValue(it.serializer) }
|
||||
|
||||
companion object {
|
||||
fun readPacket(type: Byte, stream: DataInputStream): Packet =
|
||||
PacketType.entries.find { it.id == type }?.reader?.readPacket(stream) ?: throw IllegalArgumentException("Unknown Packet")
|
||||
fun readPackets(stream: DataInputStream): Sequence<Any> = sequence {
|
||||
while (stream.available() > 0) {
|
||||
yield(readPacket(stream.readByte().toUByte().toInt(), stream))
|
||||
}
|
||||
}
|
||||
|
||||
enum class PacketType(val id: Byte, val reader: PacketReader<out Packet>) {
|
||||
PLAYER_JOIN_PACKET(0x00, UserJoinPacketReader),
|
||||
@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()),
|
||||
}
|
||||
@ -1,2 +1,327 @@
|
||||
package de.chaoscaot.replay.parser
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
/*
|
||||
* PlayerJoinPacket (0x00) + int EntityId + int SWUserId
|
||||
* EntityMovePacket (0x01) + int EntityId + double x, y, z + float pitch, yaw + byte headyaw
|
||||
* EntityDespawnsPacket (0x02) + int EntityId
|
||||
* PlayerSneakPacket (0x03) + int EntityId + boolean sneaks
|
||||
* EntityAnimationPacket (0x04) + int EntityId + byte animation
|
||||
* TNTSpawnPacket (0x05) + int EntityId
|
||||
* EntitySpeedPacket (0x06) + int EntityId + double dx, dy, dz
|
||||
* PlayerItemPacket (0x07) + int EntityId + String item + boolean enchanted + String slot
|
||||
* ArrowSpawnPacket (0x08) + int EntityId
|
||||
* FireballSpawnPacket (0x09) + int EntityId
|
||||
* BowSpanPacket (0x0a) + int EntityId + boolean start + hand
|
||||
* PlayerDamagePacket (0x0b) + int EntityId
|
||||
* SetOnFire (0x0c) + int EntityId + boolean perma
|
||||
*
|
||||
* DEPRECATED ArenaInfo (0x20) + bool blueNegZ + byte arenaY + int arenaMinX + int arenaMinZ
|
||||
* ArenaInfo (0x21) + bool blueNegZ + int arenaMinX + int arenaMinY + int arenaMinZ
|
||||
*
|
||||
* DEPRECATED BlockPacket (0x30) + pos int, byte, int + int BlockState
|
||||
* ParticlePacket (0x31) + double x, y, z + string particleType
|
||||
* SoundPacket (0x32) + int x, y, z + string soundType + string soundCategory + float volume, pitch
|
||||
* DEPRECATED ShortBlockPacket (0x33) + pos relative to ArenaMinX,ArenaMinZ byte, byte, byte + short BlockState
|
||||
* SoundAtPlayerPacket (0x34) + string (soundType, soundCategory) + float volume, pitch
|
||||
* ShortBlockPacket (0x35) + pos relative to ArenaMinX,BluePasteY,ArenaMinZ byte, byte, byte + short BlockState
|
||||
* BlockPacket (0x36) + pos int, short, int + int BlockState
|
||||
*
|
||||
*
|
||||
* DEPRECATED ChatPacket (0xa0) + String message
|
||||
* DEPRECATED ActionBarPacket (0xa1) + String message
|
||||
* DEPRECATED SystemPacket (0xa2) + String message
|
||||
* CountdownPacket (0xa3) + String message, int displaytime, Message appendix
|
||||
* ChatPacket (0xa4) + Message
|
||||
* SystemPacket (0xa5) + Message
|
||||
*
|
||||
* BlueSchemPacket (0xb0) + int blueSchemId
|
||||
* RedSchemPacket (0xb1) + int redSchemId
|
||||
* TeamIDPacket (0xb2) + int blueTeamId, redTeamId
|
||||
* BlueEmbeddedSchemPacket (0xb3) + int blueSchemId + gzipt NBT blob
|
||||
* RedEmbeddedSchemPacket (0xb4) + int redSchemId + gzipt NBT blob
|
||||
*
|
||||
* DEPRECATED ScoreboardTitlePacket (0xc0) + String scoreboardTitle
|
||||
* DEPRECATED ScoreboardDataPacket (0xc1) + String key + int value
|
||||
* DEPRECATED BossBarPacket (0xc2) + double leftBlueProgress, leftRedProgress + String leftBlueText, leftRedText
|
||||
* DEPRECATED SubtitlePacket (0xc3) + String subtitle
|
||||
* DEPRECATED PrintWinPacket (0xc4) + String title, subtitle
|
||||
* SubtitlePacket (0xc5) + Message
|
||||
* WinPacket (0xc6) + byte team + Message subtitle
|
||||
* BossBarPacket (0xc7) + double leftBlueProgress, leftRedProgress + Message leftBlueText, leftRedText
|
||||
*
|
||||
* CommentPacket (0xfe) + String comment
|
||||
* TickPacket (0xff)
|
||||
*
|
||||
* Message-Format
|
||||
* String message + byte prefixed Object-Params + byte 0x00
|
||||
* 0x00: End of message
|
||||
* 0x01: boolean following
|
||||
* 0x02: byte following
|
||||
* 0x03: short following
|
||||
* 0x04: int following
|
||||
* 0x05: float following
|
||||
* 0x06: double following
|
||||
* 0x07: String following
|
||||
* 0x08: Message following
|
||||
* */
|
||||
|
||||
@Serializable
|
||||
data class UserJoinPacket(val entityId: Int, val userId: Int)
|
||||
|
||||
@Serializable
|
||||
data class EntityMovePacket(val entityId: Int, val x: Double, val y: Double, val z: Double, val pitch: Float, val yaw: Float, val headYaw: Byte)
|
||||
|
||||
@Serializable
|
||||
data class EntityDespawnsPacket(val entityId: Int)
|
||||
|
||||
@Serializable
|
||||
data class EntitySpeedPacket(val entityId: Int, val dx: Double, val dy: Double, val dz: Double)
|
||||
|
||||
@Serializable
|
||||
data class PlayerSneakPacket(
|
||||
val entityId: Int,
|
||||
val sneaks: Boolean
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class EntityAnimationPacket(
|
||||
val entityId: Int,
|
||||
val animation: Byte
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TNTSpawnPacket(
|
||||
val entityId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PlayerItemPacket(
|
||||
val entityId: Int,
|
||||
val item: String,
|
||||
val enchanted: Boolean,
|
||||
val slot: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArrowSpawnPacket(
|
||||
val entityId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FireballSpawnPacket(
|
||||
val entityId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BowSpanPacket(
|
||||
val entityId: Int,
|
||||
val start: Boolean,
|
||||
val hand: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PlayerDamagePacket(
|
||||
val entityId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SetOnFirePacket(
|
||||
val entityId: Int,
|
||||
val perma: Boolean
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ArenaInfo(
|
||||
val blueNegZ: Boolean,
|
||||
val arenaMinX: Int,
|
||||
val arenaMinY: Int,
|
||||
val arenaMinZ: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ParticlePacket(
|
||||
val x: Double,
|
||||
val y: Double,
|
||||
val z: Double,
|
||||
val particleType: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SoundPacket(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val z: Int,
|
||||
val soundType: String,
|
||||
val soundCategory: String,
|
||||
val volume: Float,
|
||||
val pitch: Float
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SoundAtPlayerPacket(
|
||||
val soundType: String,
|
||||
val volume: Float,
|
||||
val pitch: Float
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ShortBlockPacket(
|
||||
val x: Byte,
|
||||
val y: Byte,
|
||||
val z: Byte,
|
||||
val blockState: Short
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BlockPacket(
|
||||
val x: Int,
|
||||
val y: Short,
|
||||
val z: Int,
|
||||
val blockState: Int
|
||||
)
|
||||
|
||||
// Verbleibende aktive Pakete
|
||||
@Serializable
|
||||
data class BlueEmbeddedSchemPacket(
|
||||
val blueSchemId: Int,
|
||||
val schematic: EmbeddedSchematic
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class RedEmbeddedSchemPacket(
|
||||
val redSchemId: Int,
|
||||
val schematic: EmbeddedSchematic
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BlueSchemPacket(
|
||||
val blueSchemId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class RedSchemPacket(
|
||||
val redSchemId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TeamIDPacket(
|
||||
val blueTeamId: Int,
|
||||
val redTeamId: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class CountdownPacket(
|
||||
val message: String,
|
||||
val displayTime: Int,
|
||||
val appendix: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChatPacket(
|
||||
val message: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SystemPacket(
|
||||
val message: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SubtitlePacket(
|
||||
val message: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class WinPacket(
|
||||
val team: Byte,
|
||||
val subtitle: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BossBarPacket(
|
||||
val leftBlueProgress: Double,
|
||||
val leftRedProgress: Double,
|
||||
val leftBlueText: Message,
|
||||
val leftRedText: Message
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class CommentPacket(
|
||||
val comment: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class TickPacket
|
||||
|
||||
// Legacy (Deprecated) Pakete
|
||||
@Serializable
|
||||
data class LegacyArenaInfo(
|
||||
val blueNegZ: Boolean,
|
||||
val arenaY: Byte,
|
||||
val arenaMinX: Int,
|
||||
val arenaMinZ: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyBlockPacket(
|
||||
val x: Int,
|
||||
val y: Byte,
|
||||
val z: Int,
|
||||
val blockState: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyShortBlockPacket(
|
||||
val x: Byte,
|
||||
val y: Byte,
|
||||
val z: Byte,
|
||||
val blockState: Short
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyChatPacket(
|
||||
val message: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyActionBarPacket(
|
||||
val message: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacySystemPacket(
|
||||
val message: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyScoreboardTitlePacket(
|
||||
val scoreboardTitle: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyScoreboardDataPacket(
|
||||
val key: String,
|
||||
val value: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyBossBarPacket(
|
||||
val leftBlueProgress: Double,
|
||||
val leftRedProgress: Double,
|
||||
val leftBlueText: String,
|
||||
val leftRedText: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacySubtitlePacket(
|
||||
val subtitle: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyPrintWinPacket(
|
||||
val title: String,
|
||||
val subtitle: String
|
||||
)
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
package de.chaoscaot.replay.parser
|
||||
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
object ReplayParser {
|
||||
fun read(file: File): Sequence<Any> = read(file.inputStream())
|
||||
|
||||
fun read(inputStream: InputStream, gzipped: Boolean = true): Sequence<Any> = readPackets(DataInputStream(if (gzipped) GZIPInputStream(inputStream) else inputStream))
|
||||
}
|
||||
Reference in New Issue
Block a user