From 26f2f3a3362aa884e80a41af217e5ab0f678055d Mon Sep 17 00:00:00 2001 From: SettingDust Date: Tue, 27 Sep 2022 14:32:59 +0800 Subject: [PATCH 1/6] feat(nbt): add serializers for tags and item stack TODO: add unit tests for serializers --- silk-nbt/build.gradle.kts | 4 + .../serializer/ItemStackSerializer.kt | 27 ++++ .../serialization/serializer/NbtSerializer.kt | 120 ++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt create mode 100644 silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt diff --git a/silk-nbt/build.gradle.kts b/silk-nbt/build.gradle.kts index cb34c73e..7d036811 100644 --- a/silk-nbt/build.gradle.kts +++ b/silk-nbt/build.gradle.kts @@ -11,4 +11,8 @@ plugins { kotlin("plugin.serialization") } +dependencies { + api(modProject(":${rootProject.name}-core")) +} + val modName by extra("$projectTitle NBT") diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt new file mode 100644 index 00000000..91983f94 --- /dev/null +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt @@ -0,0 +1,27 @@ +package net.silkmc.silk.nbt.serialization.serializer + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.element +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import net.minecraft.nbt.CompoundTag +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.ItemStack +import net.silkmc.silk.core.serialization.SilkSerializer + +// TODO: Unit test +@ExperimentalSerializationApi +object ItemStackSerializer : SilkSerializer() { + override val descriptor = buildClassSerialDescriptor(descriptorName) { + element("id") + element("Count") + element("tag") + } + + override fun deserialize(decoder: Decoder) = + run { ItemStack.of(decoder.decodeSerializableValue(CompoundTagSerializer))!! } + + override fun serialize(encoder: Encoder, value: ItemStack) = + encoder.encodeSerializableValue(CompoundTagSerializer, CompoundTag().also(value::save)) +} \ No newline at end of file diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt new file mode 100644 index 00000000..5e54f5f0 --- /dev/null +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt @@ -0,0 +1,120 @@ +package net.silkmc.silk.nbt.serialization.serializer + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.PolymorphicSerializer +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.listSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import net.minecraft.nbt.* +import net.silkmc.silk.core.serialization.SilkSerializer + +// TODO: Unit test +@ExperimentalSerializationApi +abstract class TagSerializer( + primitiveKind: PrimitiveKind = PrimitiveKind.STRING +) : SilkSerializer(primitiveKind) + +@ExperimentalSerializationApi +object BaseTagSerializer : TagSerializer() { + private val serializer = PolymorphicSerializer(Tag::class) + override val descriptor = serializer.descriptor + override fun deserialize(decoder: Decoder) = serializer.deserialize(decoder) + override fun serialize(encoder: Encoder, value: Tag) = serializer.serialize(encoder, value) +} + +@ExperimentalSerializationApi +object CompoundTagSerializer : TagSerializer() { + private val serializer = MapSerializer(String.serializer(), BaseTagSerializer) + override val descriptor = serializer.descriptor + override fun deserialize(decoder: Decoder) = CompoundTag().apply { serializer.deserialize(decoder).forEach(::put) } + override fun serialize(encoder: Encoder, value: CompoundTag) = + serializer.serialize(encoder, value.allKeys.map { it to value[it]!! }.toMap()) +} + +@ExperimentalSerializationApi +object EndTagSerializer : TagSerializer(PrimitiveKind.BYTE) { + override fun deserialize(decoder: Decoder) = EndTag.INSTANCE.also { decoder.decodeByte() } + override fun serialize(encoder: Encoder, value: EndTag) = encoder.encodeByte(0) +} + +@ExperimentalSerializationApi +object StringTagSerializer : TagSerializer(PrimitiveKind.STRING) { + override fun deserialize(decoder: Decoder) = StringTag.valueOf(decoder.decodeString())!! + override fun serialize(encoder: Encoder, value: StringTag) = encoder.encodeString(value.asString) +} + +/** + * NumericTag + */ +@ExperimentalSerializationApi +object ByteTagSerializer : TagSerializer(PrimitiveKind.BYTE) { + override fun deserialize(decoder: Decoder) = ByteTag.valueOf(decoder.decodeByte())!! + override fun serialize(encoder: Encoder, value: ByteTag) = encoder.encodeByte(value.asByte) +} + +@ExperimentalSerializationApi +object DoubleTagSerializer : TagSerializer(PrimitiveKind.DOUBLE) { + override fun deserialize(decoder: Decoder) = DoubleTag.valueOf(decoder.decodeDouble())!! + override fun serialize(encoder: Encoder, value: DoubleTag) = encoder.encodeDouble(value.asDouble) +} + +@ExperimentalSerializationApi +object FloatTagSerializer : TagSerializer(PrimitiveKind.FLOAT) { + override fun deserialize(decoder: Decoder) = FloatTag.valueOf(decoder.decodeFloat())!! + override fun serialize(encoder: Encoder, value: FloatTag) = encoder.encodeFloat(value.asFloat) +} + +@ExperimentalSerializationApi +object IntTagSerializer : TagSerializer(PrimitiveKind.INT) { + override fun deserialize(decoder: Decoder) = IntTag.valueOf(decoder.decodeInt())!! + override fun serialize(encoder: Encoder, value: IntTag) = encoder.encodeInt(value.asInt) +} + +@ExperimentalSerializationApi +object LongTagSerializer : TagSerializer(PrimitiveKind.LONG) { + override fun deserialize(decoder: Decoder) = LongTag.valueOf(decoder.decodeLong())!! + override fun serialize(encoder: Encoder, value: LongTag) = encoder.encodeLong(value.asLong) +} + +@ExperimentalSerializationApi +object ShortTagSerializer : TagSerializer(PrimitiveKind.SHORT) { + override fun deserialize(decoder: Decoder) = ShortTag.valueOf(decoder.decodeShort())!! + override fun serialize(encoder: Encoder, value: ShortTag) = encoder.encodeShort(value.asShort) +} + +/** + * CollectionTag + */ +@ExperimentalSerializationApi +open class CollectionTagSerializer>( + private val tag: TagSerializer, + private val constructor: (List) -> T +) : TagSerializer() { + override val descriptor = listSerialDescriptor(tag.descriptor) + override fun deserialize(decoder: Decoder) = constructor(ListSerializer(tag).deserialize(decoder)) + override fun serialize(encoder: Encoder, value: T) = ListSerializer(tag).serialize(encoder, value) +} + +@ExperimentalSerializationApi +object ListTagSerializer : + CollectionTagSerializer(BaseTagSerializer, { + ListTag().apply { + addAll(it) + } + }) + +@ExperimentalSerializationApi +object ByteArrayTagSerializer : + CollectionTagSerializer(ByteTagSerializer, { list -> ByteArrayTag(list.map { it.asByte }) }) + +@ExperimentalSerializationApi +object IntArrayTagSerializer : + CollectionTagSerializer(IntTagSerializer, { list -> IntArrayTag(list.map { it.asInt }) }) + +@ExperimentalSerializationApi +object LongArrayTagSerializer : + CollectionTagSerializer(LongTagSerializer, { list -> LongArrayTag(list.map { it.asLong }) }) \ No newline at end of file From f9132f1db793f7d05680a322a07d0f177ddc1f79 Mon Sep 17 00:00:00 2001 From: SettingDust Date: Tue, 4 Oct 2022 18:31:29 +0800 Subject: [PATCH 2/6] test(nbt): test for serializers --- .../net/silkmc/silk/nbt/NbtSerializerTest.kt | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt diff --git a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt new file mode 100644 index 00000000..28e230b7 --- /dev/null +++ b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt @@ -0,0 +1,79 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package net.silkmc.silk.nbt + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import kotlinx.serialization.ExperimentalSerializationApi +import net.minecraft.nbt.Tag +import net.silkmc.silk.core.serialization.SilkSerializer +import net.silkmc.silk.nbt.dsl.nbtCompound +import net.silkmc.silk.nbt.serialization.Nbt +import net.silkmc.silk.nbt.serialization.serializer.* + +private val nbt = Nbt {} + +private inline fun > Data.shouldSerializable( + serializer: Serializer, + tag: Data.() -> T +) = tag(this).let { nbt.encodeToNbtElement(serializer, it) shouldBe it } + + +private inline fun > Data.shouldDeserializable( + deserializer: Deserializer, + tag: Data.() -> T +) = tag(this).let { nbt.decodeFromNbtElement(deserializer, it) shouldBe it } + +class NbtSerializerTest : StringSpec({ + "primitive should deserializable" { + "foo".shouldDeserializable(StringTagSerializer) { toNbt() } + 25565.toByte().shouldDeserializable(ByteTagSerializer) { toNbt() } + 25565.toDouble().shouldDeserializable(DoubleTagSerializer) { toNbt() } + 25565F.shouldDeserializable(FloatTagSerializer) { toNbt() } + 25565.shouldDeserializable(IntTagSerializer) { toNbt() } + 25565L.shouldDeserializable(LongTagSerializer) { toNbt() } + 25565.toShort().shouldDeserializable(ShortTagSerializer) { toNbt() } + } + + "primitive should serializable" { + "foo".shouldSerializable(StringTagSerializer) { toNbt() } + 25565.toByte().shouldSerializable(ByteTagSerializer) { toNbt() } + 25565.toDouble().shouldSerializable(DoubleTagSerializer) { toNbt() } + 25565F.shouldSerializable(FloatTagSerializer) { toNbt() } + 25565.shouldSerializable(IntTagSerializer) { toNbt() } + 25565L.shouldSerializable(LongTagSerializer) { toNbt() } + 25565.toShort().shouldSerializable(ShortTagSerializer) { toNbt() } + } + + "collection like should serializable" { + byteArrayOf(0, 2, 4, 8).shouldSerializable(ByteArrayTagSerializer) { toNbt() } + intArrayOf(0, 1, 2, 3).shouldSerializable(IntArrayTagSerializer) { toNbt() } + longArrayOf(5, 6, 7, 8).shouldSerializable(LongArrayTagSerializer) { toNbt() } + listOf("foo", "bar").shouldSerializable(ListTagSerializer) { map { it.toNbt() }.toNbt() } + } + + "collection like should deserializable" { + byteArrayOf(0, 2, 4, 8).shouldDeserializable(ByteArrayTagSerializer) { toNbt() } + intArrayOf(0, 1, 2, 3).shouldDeserializable(IntArrayTagSerializer) { toNbt() } + longArrayOf(5, 6, 7, 8).shouldDeserializable(LongArrayTagSerializer) { toNbt() } + listOf("foo", "bar").shouldDeserializable(ListTagSerializer) { map { it.toNbt() }.toNbt() } + } + + "compound should serializable" { + nbtCompound { + put("foo", 0) + compound("bar") { + put("xxx", "abb") + } + }.shouldSerializable(CompoundTagSerializer) { this } + } + + "compound should deserializable" { + nbtCompound { + put("foo", 0) + compound("bar") { + put("xxx", "abb") + } + }.shouldDeserializable(CompoundTagSerializer) { this } + } +}) \ No newline at end of file From c06351f91a5a327c38f4249724ff2e164cb80a51 Mon Sep 17 00:00:00 2001 From: SettingDust Date: Tue, 4 Oct 2022 18:32:18 +0800 Subject: [PATCH 3/6] fix(nbt): fix bugs by test --- .../silk/core/serialization/SilkSerializer.kt | 15 ++- .../serializers/ResourceLocationSerializer.kt | 5 +- .../kotlin/net/silkmc/silk/nbt/Conversion.kt | 1 + .../internal/CollectionLikeSerialization.kt | 6 ++ .../serializer/ItemStackSerializer.kt | 2 +- .../serialization/serializer/NbtSerializer.kt | 91 +++++++++++-------- .../net/silkmc/silk/nbt/NbtEncodingTest.kt | 2 +- 7 files changed, 77 insertions(+), 45 deletions(-) diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializer.kt index a357bae6..4192fc37 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializer.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/SilkSerializer.kt @@ -3,10 +3,17 @@ package net.silkmc.silk.core.serialization import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlin.reflect.KClass -abstract class SilkSerializer( - val primitiveKind: PrimitiveKind = PrimitiveKind.STRING +abstract class SilkSerializer( + baseClass: KClass ) : KSerializer { - val descriptorName = javaClass.name - override val descriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING) + val descriptorName = "SilkSerializer<${baseClass.simpleName}>" } + +abstract class SilkPrimitiveSerializer( + val primitiveKind: PrimitiveKind = PrimitiveKind.STRING, + baseClass: KClass +) : SilkSerializer(baseClass) { + override val descriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING) +} \ No newline at end of file diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt index a1fbd39a..566b1181 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt @@ -1,14 +1,15 @@ package net.silkmc.silk.core.serialization.serializers +import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import net.minecraft.resources.ResourceLocation -import net.silkmc.silk.core.serialization.SilkSerializer +import net.silkmc.silk.core.serialization.SilkPrimitiveSerializer @Deprecated("Has been renamed to ResourceLocationSerializer", replaceWith = ReplaceWith("ResourceLocationSerializer")) typealias IdentifierSerializer = ResourceLocationSerializer -class ResourceLocationSerializer : SilkSerializer() { +class ResourceLocationSerializer : SilkPrimitiveSerializer(PrimitiveKind.STRING, ResourceLocation::class) { override fun deserialize(decoder: Decoder): ResourceLocation { val split = decoder.decodeString().split(':') return ResourceLocation(split[0], split[1]) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/Conversion.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/Conversion.kt index 0c0f561b..745a4ff8 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/Conversion.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/Conversion.kt @@ -19,5 +19,6 @@ fun IntArray.toNbt() = IntArrayTag(this) fun List.toNbt() = IntArrayTag(this) fun LongArray.toNbt() = LongArrayTag(this) fun List.toNbt() = LongArrayTag(this) +fun List.toNbt() = ListTag().also { it.addAll(this) } fun UUID.toNbt(): IntArrayTag = NbtUtils.createUUID(this) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/internal/CollectionLikeSerialization.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/internal/CollectionLikeSerialization.kt index cb153f42..cb416df6 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/internal/CollectionLikeSerialization.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/internal/CollectionLikeSerialization.kt @@ -1,7 +1,9 @@ package net.silkmc.silk.nbt.serialization.internal +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer +import net.silkmc.silk.nbt.serialization.serializer.CollectionTagSerializer import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties @@ -32,9 +34,13 @@ private val collectionLikeElementSerializerField = collectionLikeSerializerClass .first { it.name == "elementSerializer" } .apply { isAccessible = true } as KProperty1> +@OptIn(ExperimentalSerializationApi::class) internal val Any.elementSerializer: KSerializer<*>? get() = if (collectionLikeSerializerClass.isInstance(this)) { collectionLikeElementSerializerField.get(this) + } else if (this is CollectionTagSerializer<*, *>) { + tag } else { null } + diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt index 91983f94..becbe7ab 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt @@ -12,7 +12,7 @@ import net.silkmc.silk.core.serialization.SilkSerializer // TODO: Unit test @ExperimentalSerializationApi -object ItemStackSerializer : SilkSerializer() { +object ItemStackSerializer : SilkSerializer(ItemStack::class) { override val descriptor = buildClassSerialDescriptor(descriptorName) { element("id") element("Count") diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt index 5e54f5f0..6a9631a5 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt @@ -1,48 +1,63 @@ package net.silkmc.silk.nbt.serialization.serializer -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.PolymorphicSerializer +import kotlinx.serialization.* import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.listSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import net.minecraft.nbt.* +import net.silkmc.silk.core.serialization.SilkPrimitiveSerializer import net.silkmc.silk.core.serialization.SilkSerializer +import net.silkmc.silk.nbt.serialization.decoder.TagDecoder +import kotlin.reflect.KClass -// TODO: Unit test -@ExperimentalSerializationApi -abstract class TagSerializer( - primitiveKind: PrimitiveKind = PrimitiveKind.STRING -) : SilkSerializer(primitiveKind) - -@ExperimentalSerializationApi -object BaseTagSerializer : TagSerializer() { - private val serializer = PolymorphicSerializer(Tag::class) - override val descriptor = serializer.descriptor - override fun deserialize(decoder: Decoder) = serializer.deserialize(decoder) - override fun serialize(encoder: Encoder, value: Tag) = serializer.serialize(encoder, value) +/** + * [Source](https://github.com/Kotlin/kotlinx.serialization/issues/1940) + */ +@Suppress("unused") +private val lazy = LazyThreadSafetyMode.PUBLICATION + +@ExperimentalSerializationApi +@OptIn(InternalSerializationApi::class) +object BaseTagSerializer : SilkSerializer(Tag::class) { + override val descriptor: SerialDescriptor = + buildSerialDescriptor(descriptorName, PolymorphicKind.SEALED) + + override fun deserialize(decoder: Decoder) = run { + require(decoder is TagDecoder) + decoder.nextMaybeNullable() + } + + override fun serialize(encoder: Encoder, value: Tag) { + val actualSerializer = + encoder.serializersModule.getPolymorphic(Tag::class, value) + ?: value::class.serializer() as SerializationStrategy + actualSerializer.serialize(encoder, value) + } } @ExperimentalSerializationApi -object CompoundTagSerializer : TagSerializer() { +object CompoundTagSerializer : SilkSerializer(CompoundTag::class) { private val serializer = MapSerializer(String.serializer(), BaseTagSerializer) - override val descriptor = serializer.descriptor + override val descriptor = SerialDescriptor(descriptorName, serializer.descriptor) override fun deserialize(decoder: Decoder) = CompoundTag().apply { serializer.deserialize(decoder).forEach(::put) } override fun serialize(encoder: Encoder, value: CompoundTag) = - serializer.serialize(encoder, value.allKeys.map { it to value[it]!! }.toMap()) + serializer.serialize(encoder, value.allKeys.associateWith { value[it]!! }) } @ExperimentalSerializationApi -object EndTagSerializer : TagSerializer(PrimitiveKind.BYTE) { - override fun deserialize(decoder: Decoder) = EndTag.INSTANCE.also { decoder.decodeByte() } +object EndTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.BYTE, EndTag::class) { + override fun deserialize(decoder: Decoder) = EndTag.INSTANCE.also { decoder.decodeByte() }!! override fun serialize(encoder: Encoder, value: EndTag) = encoder.encodeByte(0) } @ExperimentalSerializationApi -object StringTagSerializer : TagSerializer(PrimitiveKind.STRING) { +object StringTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.STRING, StringTag::class) { override fun deserialize(decoder: Decoder) = StringTag.valueOf(decoder.decodeString())!! override fun serialize(encoder: Encoder, value: StringTag) = encoder.encodeString(value.asString) } @@ -51,37 +66,37 @@ object StringTagSerializer : TagSerializer(PrimitiveKind.STRING) { * NumericTag */ @ExperimentalSerializationApi -object ByteTagSerializer : TagSerializer(PrimitiveKind.BYTE) { +object ByteTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.BYTE, ByteTag::class) { override fun deserialize(decoder: Decoder) = ByteTag.valueOf(decoder.decodeByte())!! override fun serialize(encoder: Encoder, value: ByteTag) = encoder.encodeByte(value.asByte) } @ExperimentalSerializationApi -object DoubleTagSerializer : TagSerializer(PrimitiveKind.DOUBLE) { +object DoubleTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.DOUBLE, DoubleTag::class) { override fun deserialize(decoder: Decoder) = DoubleTag.valueOf(decoder.decodeDouble())!! override fun serialize(encoder: Encoder, value: DoubleTag) = encoder.encodeDouble(value.asDouble) } @ExperimentalSerializationApi -object FloatTagSerializer : TagSerializer(PrimitiveKind.FLOAT) { +object FloatTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.FLOAT, FloatTag::class) { override fun deserialize(decoder: Decoder) = FloatTag.valueOf(decoder.decodeFloat())!! override fun serialize(encoder: Encoder, value: FloatTag) = encoder.encodeFloat(value.asFloat) } @ExperimentalSerializationApi -object IntTagSerializer : TagSerializer(PrimitiveKind.INT) { +object IntTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.INT, IntTag::class) { override fun deserialize(decoder: Decoder) = IntTag.valueOf(decoder.decodeInt())!! override fun serialize(encoder: Encoder, value: IntTag) = encoder.encodeInt(value.asInt) } @ExperimentalSerializationApi -object LongTagSerializer : TagSerializer(PrimitiveKind.LONG) { +object LongTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.LONG, LongTag::class) { override fun deserialize(decoder: Decoder) = LongTag.valueOf(decoder.decodeLong())!! override fun serialize(encoder: Encoder, value: LongTag) = encoder.encodeLong(value.asLong) } @ExperimentalSerializationApi -object ShortTagSerializer : TagSerializer(PrimitiveKind.SHORT) { +object ShortTagSerializer : SilkPrimitiveSerializer(PrimitiveKind.SHORT, ShortTag::class) { override fun deserialize(decoder: Decoder) = ShortTag.valueOf(decoder.decodeShort())!! override fun serialize(encoder: Encoder, value: ShortTag) = encoder.encodeShort(value.asShort) } @@ -90,18 +105,20 @@ object ShortTagSerializer : TagSerializer(PrimitiveKind.SHORT) { * CollectionTag */ @ExperimentalSerializationApi -open class CollectionTagSerializer>( - private val tag: TagSerializer, +sealed class CollectionTagSerializer>( + val tag: SilkSerializer, + baseClass: KClass, private val constructor: (List) -> T -) : TagSerializer() { - override val descriptor = listSerialDescriptor(tag.descriptor) - override fun deserialize(decoder: Decoder) = constructor(ListSerializer(tag).deserialize(decoder)) - override fun serialize(encoder: Encoder, value: T) = ListSerializer(tag).serialize(encoder, value) +) : SilkSerializer(baseClass) { + private val serializer = ListSerializer(tag) + override val descriptor = SerialDescriptor(descriptorName, serializer.descriptor) + override fun deserialize(decoder: Decoder) = constructor(serializer.deserialize(decoder)) + override fun serialize(encoder: Encoder, value: T) = serializer.serialize(encoder, value) } @ExperimentalSerializationApi object ListTagSerializer : - CollectionTagSerializer(BaseTagSerializer, { + CollectionTagSerializer(BaseTagSerializer, ListTag::class, { ListTag().apply { addAll(it) } @@ -109,12 +126,12 @@ object ListTagSerializer : @ExperimentalSerializationApi object ByteArrayTagSerializer : - CollectionTagSerializer(ByteTagSerializer, { list -> ByteArrayTag(list.map { it.asByte }) }) + CollectionTagSerializer(ByteTagSerializer, ByteArrayTag::class, { list -> ByteArrayTag(list.map { it.asByte }) }) @ExperimentalSerializationApi object IntArrayTagSerializer : - CollectionTagSerializer(IntTagSerializer, { list -> IntArrayTag(list.map { it.asInt }) }) + CollectionTagSerializer(IntTagSerializer, IntArrayTag::class, { list -> IntArrayTag(list.map { it.asInt }) }) @ExperimentalSerializationApi object LongArrayTagSerializer : - CollectionTagSerializer(LongTagSerializer, { list -> LongArrayTag(list.map { it.asLong }) }) \ No newline at end of file + CollectionTagSerializer(LongTagSerializer, LongArrayTag::class, { list -> LongArrayTag(list.map { it.asLong }) }) \ No newline at end of file diff --git a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtEncodingTest.kt b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtEncodingTest.kt index 76bbfe58..8b3d2758 100644 --- a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtEncodingTest.kt +++ b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtEncodingTest.kt @@ -56,7 +56,7 @@ class NbtEncodingTest : StringSpec({ } "byte collections should encode to byte array" { - checkAll(Arb.byteArrays(Arb.int(0..0x1000), Arb.byte())) { + checkAll(Arb.byteArray(Arb.int(0..0x1000), Arb.byte())) { val element = Nbt.encodeToNbtElement(it) element.shouldBeInstanceOf() element.asByteArray shouldBe it From a2daab50e602311c72f6536c9eeba326b8529021 Mon Sep 17 00:00:00 2001 From: SettingDust Date: Tue, 4 Oct 2022 18:34:25 +0800 Subject: [PATCH 4/6] feat(nbt): serializers-compatible encoder/decoder --- .../nbt/serialization/NbtSerialization.kt | 41 ++++-- .../silk/nbt/serialization/decoder/Tag.kt | 139 +++++++++++++----- .../silk/nbt/serialization/encoder/Tag.kt | 65 +++++++- 3 files changed, 188 insertions(+), 57 deletions(-) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt index b4ba8592..1302e67c 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt @@ -1,26 +1,47 @@ package net.silkmc.silk.nbt.serialization import kotlinx.serialization.* -import kotlinx.serialization.modules.EmptySerializersModule -import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.* import net.minecraft.nbt.Tag -import net.silkmc.silk.nbt.serialization.decoder.NbtRootDecoder -import net.silkmc.silk.nbt.serialization.encoder.NbtRootEncoder +import net.silkmc.silk.nbt.serialization.decoder.RootTagDecoder +import net.silkmc.silk.nbt.serialization.encoder.RootTagEncoder +import net.silkmc.silk.nbt.serialization.serializer.* + +@ExperimentalSerializationApi +internal val TagsModule = SerializersModule { + polymorphic(Tag::class) { + subclass(CompoundTagSerializer) + subclass(EndTagSerializer) + subclass(StringTagSerializer) + subclass(ByteTagSerializer) + subclass(DoubleTagSerializer) + subclass(FloatTagSerializer) + subclass(IntTagSerializer) + subclass(LongTagSerializer) + subclass(ListTagSerializer) + subclass(ByteArrayTagSerializer) + subclass(IntArrayTagSerializer) + subclass(LongArrayTagSerializer) + defaultDeserializer { BaseTagSerializer } + } +} /** * Instances of this class can encode values to [Tag]s and decode * [Tag]s to values. */ @OptIn(ExperimentalSerializationApi::class) -sealed class Nbt(val config: NbtConfig, val serializersModule: SerializersModule) { - companion object Default : Nbt(NbtConfig(), EmptySerializersModule) +sealed class Nbt(val config: NbtConfig, serializersModule: SerializersModule) { + companion object Default : Nbt(NbtConfig(), EmptySerializersModule()) + + val serializersModule = serializersModule + TagsModule fun encodeToNbtElement(serializer: SerializationStrategy, value: T): Tag = - NbtRootEncoder(this).apply { encodeSerializableValue(serializer, value) }.element + RootTagEncoder(this).apply { encodeSerializableValue(serializer, value) }.element ?: throw SerializationException("Serializer did not encode any element") fun decodeFromNbtElement(deserializer: DeserializationStrategy, element: Tag): T = - NbtRootDecoder(this, element).decodeSerializableValue(deserializer) + RootTagDecoder(this, element).decodeSerializableValue(deserializer) } private class NbtImpl(config: NbtConfig, serializersModule: SerializersModule) : Nbt(config, serializersModule) @@ -52,7 +73,7 @@ class NbtBuilder(from: Nbt) { * function. Otherwise, an [net.minecraft.nbt.CompoundTag] will be created. */ inline fun Nbt.encodeToNbtElement(value: T) = - encodeToNbtElement(serializer(), value) + encodeToNbtElement(serializersModule.serializer(), value) /** * Encodes the given [element] to an instance of the class [T]. @@ -61,7 +82,7 @@ inline fun Nbt.encodeToNbtElement(value: T) = * will be thrown. */ inline fun Nbt.decodeFromNbtElement(element: Tag) = - decodeFromNbtElement(serializer(), element) + decodeFromNbtElement(serializersModule.serializer(), element) /** * Thrown if [NbtConfig.ignoreUnknownKeys] is set to false and an unknown key diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/decoder/Tag.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/decoder/Tag.kt index 85259333..13ef2b50 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/decoder/Tag.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/decoder/Tag.kt @@ -14,9 +14,41 @@ import net.minecraft.nbt.* import net.silkmc.silk.nbt.serialization.Nbt import net.silkmc.silk.nbt.serialization.UnknownKeyException import net.silkmc.silk.nbt.serialization.internal.* +import net.silkmc.silk.nbt.serialization.serializer.ByteTagSerializer +import net.silkmc.silk.nbt.serialization.serializer.IntTagSerializer +import net.silkmc.silk.nbt.serialization.serializer.LongTagSerializer +import net.silkmc.silk.nbt.toNbt @ExperimentalSerializationApi -abstract class NbtTagDecoder(protected val nbt: Nbt) : AbstractDecoder() { +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("TagDecoder")) +typealias NbtTagDecoder = TagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("RootTagDecoder")) +typealias NbtRootDecoder = RootTagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("CompoundTagDecoder")) +typealias NbtCompoundDecoder = CompoundTagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("ListTagDecoder")) +typealias NbtListDecoder = ListTagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("ByteArrayTagDecoder")) +typealias NbtByteArrayDecoder = ByteArrayTagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("IntArrayTagDecoder")) +typealias NbtIntArrayDecoder = IntArrayTagDecoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("LongArrayTagDecoder")) +typealias NbtLongArrayDecoder = LongArrayTagDecoder + +@ExperimentalSerializationApi +abstract class TagDecoder(protected val nbt: Nbt) : AbstractDecoder() { override val serializersModule: SerializersModule = nbt.serializersModule private enum class NextArrayType { @@ -36,9 +68,9 @@ abstract class NbtTagDecoder(protected val nbt: Nbt) : AbstractDecoder() { longArraySerializer -> decodeLongArray() as T else -> { nextArrayType = when (deserializer.elementSerializer) { - byteSerializer -> NextArrayType.Byte - intSerializer -> NextArrayType.Int - longSerializer -> NextArrayType.Long + byteSerializer, ByteTagSerializer -> NextArrayType.Byte + intSerializer, IntTagSerializer -> NextArrayType.Int + longSerializer, LongTagSerializer -> NextArrayType.Long else -> NextArrayType.None } super.decodeSerializableValue(deserializer) @@ -47,12 +79,13 @@ abstract class NbtTagDecoder(protected val nbt: Nbt) : AbstractDecoder() { override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder = when (nextArrayType) { - NextArrayType.Byte -> NbtByteArrayDecoder(nextMaybeNullable() as ByteArrayTag) - NextArrayType.Int -> NbtIntArrayDecoder(nextMaybeNullable() as IntArrayTag) - NextArrayType.Long -> NbtLongArrayDecoder(nextMaybeNullable() as LongArrayTag) + NextArrayType.Byte -> ByteArrayTagDecoder(nextMaybeNullable() as ByteArrayTag) + NextArrayType.Int -> IntArrayTagDecoder(nextMaybeNullable() as IntArrayTag) + NextArrayType.Long -> LongArrayTagDecoder(nextMaybeNullable() as LongArrayTag) NextArrayType.None -> when (descriptor.kind) { - StructureKind.LIST -> NbtListDecoder(nbt, nextMaybeNullable() as ListTag) - else -> NbtCompoundDecoder(nbt, nextMaybeNullable() as CompoundTag) + StructureKind.LIST -> ListTagDecoder(nbt, nextMaybeNullable() as ListTag) + StructureKind.MAP -> CompoundTagMapDecoder(nbt, nextMaybeNullable() as CompoundTag) + else -> CompoundTagDecoder(nbt, nextMaybeNullable() as CompoundTag) } } @@ -71,7 +104,7 @@ abstract class NbtTagDecoder(protected val nbt: Nbt) : AbstractDecoder() { return null } - private fun nextMaybeNullable(): Tag { + fun nextMaybeNullable(): Tag { val next = nextNullable ?: next() nextNullable = null return next @@ -101,20 +134,53 @@ abstract class NbtTagDecoder(protected val nbt: Nbt) : AbstractDecoder() { } @ExperimentalSerializationApi -class NbtRootDecoder( +class RootTagDecoder( nbt: Nbt, private val element: Tag -) : NbtTagDecoder(nbt) { +) : TagDecoder(nbt) { override fun next() = element override fun decodeElementIndex(descriptor: SerialDescriptor) = 0 } @ExperimentalSerializationApi -class NbtCompoundDecoder( +class CompoundTagMapDecoder( + nbt: Nbt, + private val compound: CompoundTag +) : TagDecoder(nbt) { + private var idx = -1 + private val iterator = compound.allKeys.iterator() + private var isKey = true + private lateinit var key: String + + override fun next() = if (isKey) { + key = iterator.next() + isKey = false + key.toNbt() + } else { + isKey = true + compound.get(key)!! + } + + override fun decodeElementIndex(descriptor: SerialDescriptor): Int { + while (idx++ < descriptor.elementsCount * 2 - 1) { + return idx + } + return CompositeDecoder.DECODE_DONE + } + + override fun decodeCollectionSize(descriptor: SerialDescriptor) = compound.size() + + override fun endStructure(descriptor: SerialDescriptor) { + // do nothing, maps do not have strict keys, so strict mode check is omitted + } +} + +@ExperimentalSerializationApi +class CompoundTagDecoder( nbt: Nbt, private val compound: CompoundTag -) : NbtTagDecoder(nbt) { +) : TagDecoder(nbt) { private lateinit var element: Tag private var idx = 0 @@ -146,10 +212,10 @@ class NbtCompoundDecoder( } @ExperimentalSerializationApi -class NbtListDecoder( +class ListTagDecoder( nbt: Nbt, private val list: ListTag -) : NbtTagDecoder(nbt) { +) : TagDecoder(nbt) { private val elements = list.listIterator() override fun next(): Tag = elements.next() @@ -166,43 +232,38 @@ class NbtListDecoder( } @ExperimentalSerializationApi -class NbtByteArrayDecoder(array: ByteArrayTag) : AbstractDecoder() { - private val array = array.asByteArray - - override val serializersModule: SerializersModule = EmptySerializersModule +abstract class CollectionTagDecoder( + override val serializersModule: SerializersModule = EmptySerializersModule() +) : AbstractDecoder() { + protected var idx = 0 + override fun decodeElementIndex(descriptor: SerialDescriptor): Int = idx - private var idx = 0 + override fun decodeSequentially() = true +} - override fun decodeElementIndex(descriptor: SerialDescriptor): Int = idx +@ExperimentalSerializationApi +class ByteArrayTagDecoder( + array: ByteArrayTag +) : CollectionTagDecoder() { + private val array = array.asByteArray override fun decodeCollectionSize(descriptor: SerialDescriptor) = array.size override fun decodeByte(): Byte = array[idx++] - override fun decodeSequentially() = true } @ExperimentalSerializationApi -class NbtIntArrayDecoder(array: IntArrayTag) : AbstractDecoder() { +class IntArrayTagDecoder( + array: IntArrayTag +) : CollectionTagDecoder() { private val array = array.asIntArray - - override val serializersModule: SerializersModule = EmptySerializersModule - - private var idx = 0 - - override fun decodeElementIndex(descriptor: SerialDescriptor): Int = idx override fun decodeCollectionSize(descriptor: SerialDescriptor) = array.size override fun decodeInt(): Int = array[idx++] - override fun decodeSequentially() = true } @ExperimentalSerializationApi -class NbtLongArrayDecoder(array: LongArrayTag) : AbstractDecoder() { +class LongArrayTagDecoder( + array: LongArrayTag +) : CollectionTagDecoder() { private val array = array.asLongArray - - override val serializersModule: SerializersModule = EmptySerializersModule - - private var idx = 0 - - override fun decodeElementIndex(descriptor: SerialDescriptor): Int = idx override fun decodeCollectionSize(descriptor: SerialDescriptor) = array.size override fun decodeLong(): Long = array[idx++] - override fun decodeSequentially() = true } diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/encoder/Tag.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/encoder/Tag.kt index 6ae7ca7a..f124e7e2 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/encoder/Tag.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/encoder/Tag.kt @@ -15,8 +15,25 @@ import net.silkmc.silk.nbt.serialization.Nbt import net.silkmc.silk.nbt.serialization.internal.* import net.silkmc.silk.nbt.toNbt + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("TagEncoder")) +typealias NbtTagEncoder = TagEncoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("RootTagEncoder")) +typealias NbtRootEncoder = RootTagEncoder + +@ExperimentalSerializationApi +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("CompoundTagEncoder")) +typealias NbtCompoundEncoder = CompoundTagEncoder + @ExperimentalSerializationApi -abstract class NbtTagEncoder(protected val nbt: Nbt) : AbstractEncoder() { +@Deprecated(message = "Renamed by mojmap", replaceWith = ReplaceWith("ListTagEncoder")) +typealias NbtListEncoder = ListTagEncoder + +@ExperimentalSerializationApi +abstract class TagEncoder(protected val nbt: Nbt) : AbstractEncoder() { override val serializersModule: SerializersModule = nbt.serializersModule private var isNextNullable = false @@ -53,8 +70,9 @@ abstract class NbtTagEncoder(protected val nbt: Nbt) : AbstractEncoder() { override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = when (descriptor.kind) { - StructureKind.LIST -> NbtListEncoder(nbt, ::consumeStructure) - else -> NbtCompoundEncoder(nbt, ::consumeStructure) + StructureKind.LIST -> ListTagEncoder(nbt, ::consumeStructure) + StructureKind.MAP -> CompoundTagMapEncoder(nbt, ::consumeStructure) + else -> CompoundTagEncoder(nbt, ::consumeStructure) } override fun encodeNotNullMark() { @@ -119,7 +137,7 @@ abstract class NbtTagEncoder(protected val nbt: Nbt) : AbstractEncoder() { } @ExperimentalSerializationApi -class NbtRootEncoder(nbt: Nbt) : NbtTagEncoder(nbt) { +class RootTagEncoder(nbt: Nbt) : TagEncoder(nbt) { var element: Tag? = null private set @@ -132,11 +150,41 @@ class NbtRootEncoder(nbt: Nbt) : NbtTagEncoder(nbt) { } } + +@ExperimentalSerializationApi +class CompoundTagMapEncoder( + nbt: Nbt, + private val consumer: (CompoundTag) -> Unit +) : TagEncoder(nbt) { + private val compound = CompoundTag() + private lateinit var key: String + private var isKey = true + + override fun encodeElement(element: Tag) { + if (isKey) { // writing key + key = element.asString + isKey = false + } else { + compound.put(key, element) + isKey = true + } + } + + override fun consumeStructure(element: Tag) = encodeElement(element) + + override fun endStructure(descriptor: SerialDescriptor) { + consumer(compound) + } + + override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int) = + nbt.config.encodeDefaults +} + @ExperimentalSerializationApi -class NbtCompoundEncoder( +class CompoundTagEncoder( nbt: Nbt, private val consumer: (CompoundTag) -> Unit -) : NbtTagEncoder(nbt) { +) : TagEncoder(nbt) { private val compound = CompoundTag() private val tags = ArrayDeque() @@ -164,10 +212,10 @@ class NbtCompoundEncoder( } @ExperimentalSerializationApi -class NbtListEncoder( +class ListTagEncoder( nbt: Nbt, private val consumer: (ListTag) -> Unit -) : NbtTagEncoder(nbt) { +) : TagEncoder(nbt) { private val list = ListTag() override fun encodeElement(element: Tag) { @@ -182,3 +230,4 @@ class NbtListEncoder( consumer(list) } } + From f87c6375a3f806b986933dac6a2d80988dda2595 Mon Sep 17 00:00:00 2001 From: SettingDust Date: Thu, 13 Oct 2022 11:22:52 +0800 Subject: [PATCH 5/6] test(core): add test for item serializer --- .../serializers/ResourceLocationSerializer.kt | 2 +- .../serializer/ItemStackSerializer.kt | 6 +-- .../serialization/serializer/NbtSerializer.kt | 6 --- .../net/silkmc/silk/nbt/ItemSerializerTest.kt | 43 +++++++++++++++++++ 4 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/ItemSerializerTest.kt diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt index 566b1181..f3645fc3 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/serialization/serializers/ResourceLocationSerializer.kt @@ -9,7 +9,7 @@ import net.silkmc.silk.core.serialization.SilkPrimitiveSerializer @Deprecated("Has been renamed to ResourceLocationSerializer", replaceWith = ReplaceWith("ResourceLocationSerializer")) typealias IdentifierSerializer = ResourceLocationSerializer -class ResourceLocationSerializer : SilkPrimitiveSerializer(PrimitiveKind.STRING, ResourceLocation::class) { +object ResourceLocationSerializer : SilkPrimitiveSerializer(PrimitiveKind.STRING, ResourceLocation::class) { override fun deserialize(decoder: Decoder): ResourceLocation { val split = decoder.decodeString().split(':') return ResourceLocation(split[0], split[1]) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt index becbe7ab..50def032 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/ItemStackSerializer.kt @@ -6,17 +6,17 @@ import kotlinx.serialization.descriptors.element import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import net.minecraft.nbt.CompoundTag -import net.minecraft.resources.ResourceLocation import net.minecraft.world.item.ItemStack import net.silkmc.silk.core.serialization.SilkSerializer +import net.silkmc.silk.core.serialization.serializers.ResourceLocationSerializer // TODO: Unit test @ExperimentalSerializationApi object ItemStackSerializer : SilkSerializer(ItemStack::class) { override val descriptor = buildClassSerialDescriptor(descriptorName) { - element("id") + element("id", ResourceLocationSerializer.descriptor) element("Count") - element("tag") + element("tag", CompoundTagSerializer.descriptor) } override fun deserialize(decoder: Decoder) = diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt index 6a9631a5..3e16ef43 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt @@ -16,12 +16,6 @@ import net.silkmc.silk.core.serialization.SilkSerializer import net.silkmc.silk.nbt.serialization.decoder.TagDecoder import kotlin.reflect.KClass -/** - * [Source](https://github.com/Kotlin/kotlinx.serialization/issues/1940) - */ -@Suppress("unused") -private val lazy = LazyThreadSafetyMode.PUBLICATION - @ExperimentalSerializationApi @OptIn(InternalSerializationApi::class) object BaseTagSerializer : SilkSerializer(Tag::class) { diff --git a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/ItemSerializerTest.kt b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/ItemSerializerTest.kt new file mode 100644 index 00000000..c24e9f04 --- /dev/null +++ b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/ItemSerializerTest.kt @@ -0,0 +1,43 @@ +package net.silkmc.silk.nbt + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.should +import io.kotest.matchers.shouldBe +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual +import net.minecraft.SharedConstants +import net.minecraft.server.Bootstrap +import net.minecraft.world.item.ItemStack +import net.silkmc.silk.core.serialization.serializers.ResourceLocationSerializer +import net.silkmc.silk.nbt.dsl.nbtCompound +import net.silkmc.silk.nbt.serialization.Nbt +import net.silkmc.silk.nbt.serialization.serializer.ItemStackSerializer + + +@OptIn(ExperimentalSerializationApi::class) +private val nbt = Nbt { + serializersModule = SerializersModule { + contextual(ItemStackSerializer) + contextual(ResourceLocationSerializer) + } +} +private val itemTag = nbtCompound { + put("id", "minecraft:grass") + put("Count", 5.toByte()) + put("tag", nbtCompound { put("foo", "bar") }) +} +private val item = ItemStack.of(itemTag) + +@OptIn(ExperimentalSerializationApi::class) +class ItemSerializerTest : StringSpec({ + SharedConstants.tryDetectVersion() + Bootstrap.bootStrap() + "item stack should serialize correctly" { + nbt.encodeToNbtElement(ItemStackSerializer, item) shouldBe itemTag + } + + "item stack should deserialize correctly" { + nbt.decodeFromNbtElement(ItemStackSerializer, itemTag) should { ItemStack.matches(it, item) } + } +}) \ No newline at end of file From aacce90cb7cb79187e921ab33432a679974367fb Mon Sep 17 00:00:00 2001 From: SettingDust Date: Wed, 19 Oct 2022 22:02:59 +0800 Subject: [PATCH 6/6] feat(nbt): support other serial format use serializers Signed-off-by: SettingDust --- .../nbt/serialization/NbtSerialization.kt | 2 +- .../serialization/serializer/NbtSerializer.kt | 10 ++-- .../net/silkmc/silk/nbt/NbtSerializerTest.kt | 46 ++++--------------- 3 files changed, 14 insertions(+), 44 deletions(-) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt index 1302e67c..238aca78 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/NbtSerialization.kt @@ -8,7 +8,7 @@ import net.silkmc.silk.nbt.serialization.encoder.RootTagEncoder import net.silkmc.silk.nbt.serialization.serializer.* @ExperimentalSerializationApi -internal val TagsModule = SerializersModule { +val TagsModule = SerializersModule { polymorphic(Tag::class) { subclass(CompoundTagSerializer) subclass(EndTagSerializer) diff --git a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt index 3e16ef43..0f5fdddf 100644 --- a/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt +++ b/silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/serialization/serializer/NbtSerializer.kt @@ -19,19 +19,17 @@ import kotlin.reflect.KClass @ExperimentalSerializationApi @OptIn(InternalSerializationApi::class) object BaseTagSerializer : SilkSerializer(Tag::class) { + private val serializer = PolymorphicSerializer(Tag::class) override val descriptor: SerialDescriptor = buildSerialDescriptor(descriptorName, PolymorphicKind.SEALED) override fun deserialize(decoder: Decoder) = run { - require(decoder is TagDecoder) - decoder.nextMaybeNullable() + if (decoder is TagDecoder) decoder.nextMaybeNullable() + else serializer.deserialize(decoder) } override fun serialize(encoder: Encoder, value: Tag) { - val actualSerializer = - encoder.serializersModule.getPolymorphic(Tag::class, value) - ?: value::class.serializer() as SerializationStrategy - actualSerializer.serialize(encoder, value) + serializer.serialize(encoder, value) } } diff --git a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt index 28e230b7..0036a799 100644 --- a/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt +++ b/silk-nbt/src/test/kotlin/net/silkmc/silk/nbt/NbtSerializerTest.kt @@ -5,36 +5,24 @@ package net.silkmc.silk.nbt import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.cbor.Cbor +import kotlinx.serialization.modules.plus import net.minecraft.nbt.Tag import net.silkmc.silk.core.serialization.SilkSerializer import net.silkmc.silk.nbt.dsl.nbtCompound -import net.silkmc.silk.nbt.serialization.Nbt +import net.silkmc.silk.nbt.serialization.TagsModule import net.silkmc.silk.nbt.serialization.serializer.* -private val nbt = Nbt {} +val cbor = Cbor { + serializersModule += TagsModule +} -private inline fun > Data.shouldSerializable( - serializer: Serializer, +private fun > Data.shouldSerializable( + serializer: Deserializer, tag: Data.() -> T -) = tag(this).let { nbt.encodeToNbtElement(serializer, it) shouldBe it } - - -private inline fun > Data.shouldDeserializable( - deserializer: Deserializer, - tag: Data.() -> T -) = tag(this).let { nbt.decodeFromNbtElement(deserializer, it) shouldBe it } +) = cbor.decodeFromByteArray(serializer, cbor.encodeToByteArray(serializer, tag(this))) shouldBe tag(this) class NbtSerializerTest : StringSpec({ - "primitive should deserializable" { - "foo".shouldDeserializable(StringTagSerializer) { toNbt() } - 25565.toByte().shouldDeserializable(ByteTagSerializer) { toNbt() } - 25565.toDouble().shouldDeserializable(DoubleTagSerializer) { toNbt() } - 25565F.shouldDeserializable(FloatTagSerializer) { toNbt() } - 25565.shouldDeserializable(IntTagSerializer) { toNbt() } - 25565L.shouldDeserializable(LongTagSerializer) { toNbt() } - 25565.toShort().shouldDeserializable(ShortTagSerializer) { toNbt() } - } - "primitive should serializable" { "foo".shouldSerializable(StringTagSerializer) { toNbt() } 25565.toByte().shouldSerializable(ByteTagSerializer) { toNbt() } @@ -52,13 +40,6 @@ class NbtSerializerTest : StringSpec({ listOf("foo", "bar").shouldSerializable(ListTagSerializer) { map { it.toNbt() }.toNbt() } } - "collection like should deserializable" { - byteArrayOf(0, 2, 4, 8).shouldDeserializable(ByteArrayTagSerializer) { toNbt() } - intArrayOf(0, 1, 2, 3).shouldDeserializable(IntArrayTagSerializer) { toNbt() } - longArrayOf(5, 6, 7, 8).shouldDeserializable(LongArrayTagSerializer) { toNbt() } - listOf("foo", "bar").shouldDeserializable(ListTagSerializer) { map { it.toNbt() }.toNbt() } - } - "compound should serializable" { nbtCompound { put("foo", 0) @@ -67,13 +48,4 @@ class NbtSerializerTest : StringSpec({ } }.shouldSerializable(CompoundTagSerializer) { this } } - - "compound should deserializable" { - nbtCompound { - put("foo", 0) - compound("bar") { - put("xxx", "abb") - } - }.shouldDeserializable(CompoundTagSerializer) { this } - } }) \ No newline at end of file