Skip to content

Commit

Permalink
WIP Make every serializable type have an implicit NbtName
Browse files Browse the repository at this point in the history
TODO: Test the default. Document in @NbtName.

Types that don't have an explicit `@NbtName` will default to having
an empty name.

The root name is largely redundant, since Minecraft's implementation
entirely discards it and just uses an empty string when writing NBT.
Giving all types a default name matches Minecraft's behavior, makes
serializing with named NBT formats more straightforward for those who
don't care about the NBT name, and makes any (3rd-party) serializable
type compatible with named NBT out of the box.
  • Loading branch information
BenWoodworth committed Sep 5, 2024
1 parent 349191a commit 5141383
Show file tree
Hide file tree
Showing 5 changed files with 8 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/NbtFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public open class NbtFormat internal constructor(
*/
public fun <T> decodeFromNbtTag(deserializer: DeserializationStrategy<T>, tag: NbtTag): T {
val context = SerializationNbtContext()
val reader = TreeNbtReader(NbtNamed(deserializer.descriptor.nbtName ?: "", tag)) // TODO Propagate NbtNamed
val reader = TreeNbtReader(NbtNamed(deserializer.descriptor.nbtName, tag)) // TODO Propagate NbtNamed
val decoder = NbtReaderDecoder(this, context, reader)

return decoder.decodeSerializableValue(deserializer)
Expand Down
4 changes: 0 additions & 4 deletions src/commonMain/kotlin/internal/NbtReaderDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ internal abstract class BaseNbtDecoder : AbstractNbtDecoder() {
if (!nbt.capabilities.namedRoot || !context.isSerializingRootValue) return // No need to verify

val name = descriptor.nbtName
if (name == null) {
throw NbtException(context, "The ${nbt.name} format requires root values to have an NbtName")
}

if (name != decodedTagName && !descriptor.nbtNameIsDynamic) {
throw NbtDecodingException(context, "Expected tag named '$name', but got '$decodedTagName'")
}
Expand Down
9 changes: 2 additions & 7 deletions src/commonMain/kotlin/internal/NbtWriterEncoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,8 @@ internal class NbtWriterEncoder(
private fun beginEncodingValue(type: NbtTagType) {
when (val structureType = structureTypeStack.lastOrNull()) {
null -> {
val name = nbtNameToWrite

if (name == null && nbt.capabilities.namedRoot) {
throw NbtException(context, "The ${nbt.name} format requires root values to have an NbtName")
}

writer.beginRootTag(type, name ?: "")
val name = checkNotNull(nbtNameToWrite) { "${::nbtNameToWrite.name} was not set" }
writer.beginRootTag(type, name)
}

TAG_Compound -> {
Expand Down
5 changes: 2 additions & 3 deletions src/commonMain/kotlin/internal/SerialDescriptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ internal fun SerialDescriptor.getElementNbtListKind(context: NbtContext, index:


@OptIn(ExperimentalSerializationApi::class)
internal val SerialDescriptor.nbtName: String?
internal val SerialDescriptor.nbtName: String
get() = annotations
.firstOrNull { it is NbtName }
?.let { it as NbtName }
?.name
?: "".takeIf { nbtNameIsDynamic } // TODO Remove later?
?.name ?: ""


@OptIn(ExperimentalSerializationApi::class, ExperimentalNbtApi::class)
Expand Down
6 changes: 3 additions & 3 deletions src/commonTest/kotlin/NbtNameTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class NbtNameTest {
.filter { it.capabilities.namedRoot }

val value by parameter(valuesWithStaticNbtNames)
val name = value.serializer.descriptor.nbtName!!
val name = value.serializer.descriptor.nbtName

val differentlyNamedNbtTag = buildNbtCompound("different_than_$name") {
// No elements, since the decoder should fail before reaching this point anyway
Expand Down Expand Up @@ -235,7 +235,7 @@ class NbtNameTest {
val serializableType by parameterOfSerializableTypeEdgeCases()

val serializer = DynamicNameWithoutDynamicAnnotationSerializer(serializableType)
val nbtName = requireNotNull(serializer.descriptor.nbtName) { "Serializer nbtName is null" }
val nbtName = serializer.descriptor.nbtName

val failure = assertFailsWith<IllegalArgumentException> {
nbt.verifyEncoderOrDecoder(
Expand Down Expand Up @@ -269,7 +269,7 @@ class NbtNameTest {
decoder.decodeSerializableValue(delegate)
}

val nbtName = requireNotNull(serializer.descriptor.nbtName) { "Serializer nbtName is null" }
val nbtName = serializer.descriptor.nbtName

val failure = assertFailsWith<IllegalArgumentException> {
nbt.verifyEncoderOrDecoder(
Expand Down

0 comments on commit 5141383

Please sign in to comment.