Closed Ayfri closed 1 year ago
Hey! I think you have the right approach, but there was a bug that made @Serializable sealed interface
s (like NbtTag
) not work correctly out of the box. I'm planning on having that fixed in knbt v0.12, since serializable sealed interfaces are now supported, but it might be a bit.
I can get you a more complete answer later, but something like this might work for now. (this is from memory, so it might not be quite right)
val polymorphicNbtSerializersModule = SerializersModule {
polymorphic(NbtTag::class, NbtTag.serializer()) {
subclass(NbtByte::class, NbtByte.serializer())
subclass(NbtShort::class, NbtShort.serializer())
// ...and all the other NbtTag types
}
}
val json = Json {
// ...
serializersModule = polymorphicNbtSerializersModule
}
I'll play around with it later to get something that working :)
Although on second thought I'm not sure that'll work. The NbtTag serializers require the NbtEncoder/Decoder: https://github.com/BenWoodworth/knbt/blob/d288924186a991ccfa6c0b61556d3a49a9f92fd0/src/commonMain/kotlin/NbtTagSerializers.kt#L17
Same as how the JsonElements require the JsonEncoder/Decoder:
So you'd need a custom serializer, which I can help you with.
How do you feel about storing the NBT as a JSON string value? Writing it as SNBT is probably the easiest thing to do
It's complicated, because I'm serializing a data class DataPack
, containing a serializable data class Pack
, containing a property description
which is a NbtTag
(it's for generating the pack.mcmeta
from datapacks). So if I serialize all this to a SNBT, it won't be a valid JSON.
Ah, gotcha! Just got off work so I'm starting to look into it.
Is this the one you're talking about? And changing it from String
to NbtTag
https://github.com/Ayfri/Datapack-DSL/blob/e57be5bb68330216958d23034730ff5616704dd3/src/main/kotlin/DataPack.kt#L16
I haven't done much with data packs personally, but the pack.mcmeta page makes it look like description
is also JSON. Is that right? I might be missing something
Also that's an awesome project! I love what I'm seeing :grin:
Thanks !
It can be a string, a NBTList
or a NBTCompound
, as it is considered as a Text Component.
How's this look? Serializing seems to work like a charm. I played around with deserializing for a good two hours and... yeah. It's tricky... trying to figure out the best way to map things around :grimacing:
It seems like you only need to serialize for now though so here's a start:
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.serialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
import net.benwoodworth.knbt.*
private object NbtAsJsonTextComponentSerializer : KSerializer<NbtTag> {
@OptIn(ExperimentalSerializationApi::class)
override val descriptor: SerialDescriptor =
SerialDescriptor("TextComponentJsonSerializer", serialDescriptor<JsonElement>())
override fun serialize(encoder: Encoder, value: NbtTag): Unit =
encoder.encodeSerializableValue(JsonElement.serializer(), value.toJsonElement())
override fun deserialize(decoder: Decoder): NbtTag =
throw UnsupportedOperationException("Deserializing not supported")
private fun NbtTag.toJsonElement(): JsonElement = when (this) {
is NbtCompound -> JsonObject(mapValues { it.value.toJsonElement() })
is NbtList<*> -> JsonArray(map { it.toJsonElement() })
is NbtByteArray -> JsonArray(map { JsonPrimitive(it) })
is NbtIntArray -> JsonArray(map { JsonPrimitive(it) })
is NbtLongArray -> JsonArray(map { JsonPrimitive(it) })
is NbtString -> JsonPrimitive(value)
is NbtByte -> when(value) { // Or just convert to number?
0.toByte() -> JsonPrimitive(false)
1.toByte() -> JsonPrimitive(true)
else -> JsonPrimitive(value)
}
is NbtShort -> JsonPrimitive(value)
is NbtInt -> JsonPrimitive(value)
is NbtLong -> JsonPrimitive(value)
is NbtFloat -> JsonPrimitive(value)
is NbtDouble -> JsonPrimitive(value)
}
// private fun JsonElement.toNbtTag(): NbtCompound = when (this) {
// is JsonObject -> NbtCompound(mapValues { (_, value) -> value })
//
// is JsonArray -> // NbtList elements must all be the same type
//
// is JsonPrimitive -> when (val value = contentOrNull) {
// null -> error("$value is not supported in text components")
// else -> NbtString(value)
// }
// }
}
@Serializable
data class Pack(
var packFormat: Int,
@Serializable(NbtAsJsonTextComponentSerializer::class)
var description: NbtTag,
)
Nice, it works well, thank you so much ! Can't wait to have it integrated by default :)
Hi, for a specific reason, I have to serialize an object as a JSON that also contains a NbtTag property, but I can't figure out how to make it works. By having the default Serializer I get the following error :