Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nbt): add serializers for tags and item stack #40

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(
val primitiveKind: PrimitiveKind = PrimitiveKind.STRING
abstract class SilkSerializer<T : Any>(
baseClass: KClass<T>
) : KSerializer<T> {
val descriptorName = javaClass.name
override val descriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
val descriptorName = "SilkSerializer<${baseClass.simpleName}>"
}

abstract class SilkPrimitiveSerializer<T : Any>(
val primitiveKind: PrimitiveKind = PrimitiveKind.STRING,
baseClass: KClass<T>
) : SilkSerializer<T>(baseClass) {
override val descriptor = PrimitiveSerialDescriptor(descriptorName, PrimitiveKind.STRING)
}
Original file line number Diff line number Diff line change
@@ -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<ResourceLocation>() {
object ResourceLocationSerializer : SilkPrimitiveSerializer<ResourceLocation>(PrimitiveKind.STRING, ResourceLocation::class) {
override fun deserialize(decoder: Decoder): ResourceLocation {
val split = decoder.decodeString().split(':')
return ResourceLocation(split[0], split[1])
Expand Down
4 changes: 4 additions & 0 deletions silk-nbt/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ plugins {
kotlin("plugin.serialization")
}

dependencies {
api(modProject(":${rootProject.name}-core"))
}

val modName by extra("$projectTitle NBT")
1 change: 1 addition & 0 deletions silk-nbt/src/main/kotlin/net/silkmc/silk/nbt/Conversion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ fun IntArray.toNbt() = IntArrayTag(this)
fun List<Int>.toNbt() = IntArrayTag(this)
fun LongArray.toNbt() = LongArrayTag(this)
fun List<Long>.toNbt() = LongArrayTag(this)
fun <T : Tag> List<T>.toNbt() = ListTag().also { it.addAll(this) }

fun UUID.toNbt(): IntArrayTag = NbtUtils.createUUID(this)
Original file line number Diff line number Diff line change
@@ -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
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 <T> encodeToNbtElement(serializer: SerializationStrategy<T>, 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 <T> decodeFromNbtElement(deserializer: DeserializationStrategy<T>, element: Tag): T =
NbtRootDecoder(this, element).decodeSerializableValue(deserializer)
RootTagDecoder(this, element).decodeSerializableValue(deserializer)
}

private class NbtImpl(config: NbtConfig, serializersModule: SerializersModule) : Nbt(config, serializersModule)
Expand Down Expand Up @@ -52,7 +73,7 @@ class NbtBuilder(from: Nbt) {
* function. Otherwise, an [net.minecraft.nbt.CompoundTag] will be created.
*/
inline fun <reified T> Nbt.encodeToNbtElement(value: T) =
encodeToNbtElement(serializer(), value)
encodeToNbtElement(serializersModule.serializer(), value)

/**
* Encodes the given [element] to an instance of the class [T].
Expand All @@ -61,7 +82,7 @@ inline fun <reified T> Nbt.encodeToNbtElement(value: T) =
* will be thrown.
*/
inline fun <reified T> Nbt.decodeFromNbtElement(element: Tag) =
decodeFromNbtElement(serializer<T>(), element)
decodeFromNbtElement(serializersModule.serializer<T>(), element)

/**
* Thrown if [NbtConfig.ignoreUnknownKeys] is set to false and an unknown key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
}
}

Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()
Expand All @@ -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
}
Loading