An implementation of Minecraft's NBT format for kotlinx.serialization.
Technical information about NBT can be found here.
- Kotlin Multiplatform: JVM, JS, Linux, Windows, macOS, iOS, watchOS
- Serialize any data to/from NBT or SNBT
- Support for all NBT variants: Java, Bedrock Files, Bedrock Network
- Support for all NBT compressions: gzip, zlib
- Type-safe
NbtTag
classes, with convenient builder DSLs
Nbt
and StringifiedNbt
are used to encode/decode @Serializable
data.
import net.benwoodworth.knbt.*
val nbt = Nbt {
variant = NbtVariant. // Java, Bedrock, BedrockNetwork
compression = NbtCompression. // None, Gzip, Zlib
//compressionLevel = null // in 0..9
//encodeDefaults = false
//ignoreUnknownKeys = false
//serializersModule = EmptySerializersModule
}
val snbt = StringifiedNbt {
//prettyPrint = false
//prettyPrintIndent = " "
//encodeDefaults = false
//ignoreUnknownKeys = false
//serializersModule = EmptySerializersModule
}
// ByteArray
byteArray = nbt.encodeToByteArray(data)
data = nbt.decodeFromByteArray(byteArray)
// NbtTag
nbtTag = nbt.encodeToNbtTag(data)
data = nbt.decodeFromNbtTag(nbtTag)
// Okio Sink/Source (Multiplatform)
nbt.encodeToSink(data, sink)
data = nbt.decodeFromSource(source)
// OutputStream/InputStream (JVM)
nbt.encodeToStream(data, outputStream)
data = nbt.decodeFromStream(inputStream)
// SNBT String
string = snbt.encodeToString(data)
data = snbt.decodeFromString(string)
Serializable classes will have their @SerialName
used for the root tag's name.
@Serializable
@SerialName("root")
class Example(val string: String, val int: Int)
// Serializes to: {root : {string : "Hello, world!", int : 42}}
nbt.encodeToNbtTag(Example(string = "Hello, World!", int = 42))
import kotlin.io.path.*
import net.benwoodworth.knbt.*
val file = Path("file.nbt")
val nbt = Nbt {
TODO()
}
// Read from file
val tag: NbtTag = file.inputStream().use { input ->
nbt.decodeFromStream(input)
}
// Write to file
file.outputStream().use { output ->
nbt.encodeToStream(tag, output)
}
sealed interface NbtTag
class NbtByte : NbtTag
class NbtShort : NbtTag
class NbtInt : NbtTag
class NbtLong : NbtTag
class NbtFloat : NbtTag
class NbtDouble : NbtTag
class NbtString : NbtTag
class NbtByteArray : NbtTag, List<Byte>
class NbtIntArray : NbtTag, List<Int>
class NbtLongArray : NbtTag, List<Long>
class NbtList<T : NbtTag> : NbtTag, List<T> // Only contains entries of a single type
class NbtCompound : NbtTag, Map<String, NbtTag>
NbtTag
s can be created with constructors and builder functions:
val nbtByte = NbtByte(5)
val boolean = NbtByte(true)
val nbtIntArray = NbtIntArray(intArrayOf(1, 2, 3, 4, 5))
val nbtListOfStrings = buildNbtList {
add("these")
add("are")
add("strings")
}
val nbtCompound = buildNbtCompound {
put("int", 1)
put("string", ":)")
put("byteArray", byteArrayOf(1, 1, 2, 3, 5, 8))
putNbtList("floatList") {
add(3f)
add(1f)
add(4f)
}
}
Building bigtest.nbt with the DSL (wiki.vg/NBT#bigtest.nbt)
val bigtest = buildNbtCompound("Level") {
put("longTest", 9223372036854775807L)
put("shortTest", 32767.toShort())
put("stringTest", "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!")
put("floatTest", 0.49823147f)
put("intTest", 2147483647)
putNbtCompound("nested compound test") {
putNbtCompound("ham") {
put("name", "Hampus")
put("value", 0.75f)
}
putNbtCompound("egg") {
put("name", "Eggbert")
put("value", 0.5f)
}
}
putNbtList("listTest (long)") {
add(11L)
add(12L)
add(13L)
add(14L)
add(15L)
}
putNbtList("listTest (compound)") {
addNbtCompound {
put("name", "Compound tag #0")
put("created-on", 1264099775885L)
}
addNbtCompound {
put("name", "Compound tag #1")
put("created-on", 1264099775885L)
}
}
put("byteTest", 127.toByte())
put(
"byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))",
ByteArray(1000) { n -> ((n * n * 255 + n * 7) % 100).toByte() }
)
put("doubleTest", 0.4931287132182315)
}
Using the same version of kotlinx.serialization is recommended since parts of its API required for custom formats are still experimental, and newer versions may have binary-incompatible changes that could break knbt's implementation.
While in beta, all new minor releases (v0.#.0) will have breaking API/functionality changes. Read the release notes for information.
Replacement refactorings will be provided where possible for broken APIs. Change the minor version one at a time (e.g. 0.1.0 -> 0.2.0 -> 0.3.0) and apply quick fixes. Deprecated APIs will then be removed in 0.#.1 releases.
plugins {
kotlin("jvm") version "1.9.23" // or kotlin("multiplatform"), etc.
//kotlin("plugin.serialization") version "1.9.23"
}
repositories {
mavenCentral()
//maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
implementation("net.benwoodworth.knbt:knbt:$knbt_version")
//implementation("com.squareup.okio:okio:3.9.0")
}