From 26f2f3a3362aa884e80a41af217e5ab0f678055d Mon Sep 17 00:00:00 2001 From: SettingDust Date: Tue, 27 Sep 2022 14:32:59 +0800 Subject: [PATCH] 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