Package: nbtlib
nbtlibA python library to read and editnbt data. Requires
python 3.8.FeaturesCreate, read and edit nbt filesSupports gzipped and uncompressed filesSupports big-endian and little-endian filesParse and serialize raw nbt dataDefine tag schemas that automatically enforce predefined tag typesConvert nbt between binary form and literal notationUse nbt paths to access deeply nested propertiesIncludes a CLI to quickly perform read/write/merge operationsInstallationNotice 🚧Version2.0is actively being worked on and is not stable yet. You should probably keep using version1.12.1for the moment. You can check out the2.0roadmap in thenbtlib 2.0issue.The package can be installed withpip.$pipinstall"nbtlib==1.12.1"Basic usageThe following examples will give you a very basic overview of what you
can do. For more advanced examples, check out the
"Usage"
notebook in the docs folder.Reading filesThenbtlib.loadfunction can be used to load nbt files asnbtlib.Fileobjects. Every nbt tag inherits from
its python counterpart. This means that all the builtin operations defined on the python counterpart can be used on nbt tags.importnbtlibnbt_file=nbtlib.load('bigtest.nbt')assertnbt_file['intTest']==2147483647For example, instances ofnbtlib.Fileinherit from regularCompoundtags, which themselves inherit from the builtin python dictionarydict. Similarly, instances ofInttags inherit from the builtin classint.For more details on loading nbt files and how to work with nbt tags check out the "Usage"
notebook.Editing filesYou can use instances ofnbtlib.Fileas context managers in order to save modifications
automatically at the end of thewithstatement.importnbtlibfromnbtlib.tagimportIntwithnbtlib.load('demo.nbt')asdemo:demo['counter']=Int(demo['counter']+1)You can also call thesavemethod manually.importnbtlibfromnbtlib.tagimportIntdemo=nbtlib.load('demo.nbt')demo['counter']=Int(demo['counter']+1)demo.save()For more details on thesavemethod check out the "Usage"
notebook.Using schemasnbtliballows you to defineCompoundschemas that enforce a specific tag type
for any given key.fromnbtlibimportschemafromnbtlib.tagimportShort,StringMySchema=schema('MySchema',{'foo':String,'bar':Short})my_object=MySchema({'foo':'hello world','bar':21})assertisinstance(my_object,MySchema)assertisinstance(my_object['foo'],String)For more details on schemas check out the "Usage"
notebook.Nbt literalsYou can parse nbt literals using thenbtlib.parse_nbtfunction.fromnbtlibimportparse_nbtfromnbtlib.tagimportString,List,Compound,IntArraymy_compound=parse_nbt('{foo: [hello, world], bar: [I; 1, 2, 3]}')assertmy_compound==Compound({'foo':List[String](['hello','world']),'bar':IntArray([1,2,3])})Nbt tags can be serialized to their literal representation with thenbtlib.serialize_tagfunction.fromnbtlibimportserialize_tagfromnbtlib.tagimportString,List,Compound,IntArraymy_compound=Compound({'foo':List[String](['hello','world']),'bar':IntArray([1,2,3])})assertserialize_tag(my_compound)=='{foo: ["hello", "world"], bar: [I; 1, 2, 3]}'For more details on nbt literals check out the "Usage"
notebook.Nbt pathsNbt paths can be used to access deeply nested properties in nbt data. The implementation is based on information available on theMinecraft wiki.fromnbtlibimportparse_nbt,Pathdata=parse_nbt('{a: [{b:{c: 42}}]}')assertdata['a'][0]['b']['c']==42assertdata[Path('a[0].b.c')]==42You can retrieve, modify and delete multiple properties at the same time.fromnbtlibimportparse_nbt,Pathfromnbtlib.tagimportIntdata=parse_nbt('{foo: [{a: 1, b:{c: 42}}, {a: 2, b:{c: 0}}]}')data[Path('foo[].a')]=Int(99)assertstr(data)=='{foo: [{a: 99, b:{c: 42}}, {a: 99, b:{c: 0}}]}'assertdata.get_all(Path('foo[].b.c'))==[42,0]deldata[Path('foo[].b{c: 0}')]assertstr(data)=='{foo: [{a: 99, b:{c: 42}},{a: 99}]}'Nbt paths are immutable but can be manipulated and combined together to form new paths.fromnbtlibimportPathfromnbtlib.tagimportCompoundpath=Path()['hello']['world']assertpath[:][Compound({'a':Int(0)})]=='hello.world[{a: 0}]'assertpath+path=='hello.world.hello.world'assertsum('abcdef',Path())=='a.b.c.d.e.f'assertPath()[0]+'foo{a: 1}'+'{b: 2}.bar'=='[0].foo{a: 1, b: 2}.bar'assertpath['key.with.dots']=='hello.world."key.with.dots"'assertpath+'key.with.dots'=='hello.world.key.with.dots'Command-line interfaceThe package comes with a small CLI that makes it easy to quickly perform
basic operations on nbt files.$ nbt --help
usage: nbt [-h] [-r | -s] [-w <nbt> | -m <nbt>] [--plain] [--little]
           [--compact] [--pretty] [--unpack] [--json]
           [--path <path>] [--find <path>]
           <file>

Perform operations on nbt files.

positional arguments:
  <file>         the target file

optional arguments:
  -h, --help     show this help message and exit
  -r             read nbt data from a file
  -s             read snbt from a file
  -w <nbt>       write nbt to a file
  -m <nbt>       merge nbt into a file
  --plain        don't use gzip compression
  --little       use little-endian format
  --compact      output compact snbt
  --pretty       output indented snbt
  --unpack       output interpreted nbt
  --json         output nbt as json
  --path <path>  output all the matching tags
  --find <path>  recursively find the first matching tagRead nbt dataYou can read nbt files by using the-roption. This will print the
literal notation of the binary nbt data.$nbt-rmy_file.nbt{foo:[1,2,3],bar:"Hello, world!"}You can use the following command if you want to save the output into a
file.$nbt-rmy_file.nbt>my_file.snbtUsing the--compactargument will remove all the extra whitespace from the output.$nbt-rmy_file.nbt--compact{foo:[1,2,3],bar:"Hello, world!"}You can use the--prettyargument if you want the command to output indented snbt.$nbt-rmy_file.nbt--pretty{foo:[1,2,3],bar:"Hello, world!"}The output can be converted to json with the--jsonflag.$nbt-rmy_file.nbt--json{"foo":[1,2,3],"bar":"Hello, world!"}The--pathoption lets you output tags that match a given path.$nbt-rmy_file.nbt--path"bar""Hello, world!"$nbt-rmy_file.nbt--path"foo[]"123You can combine this with the--unpackflag to print out the unpacked python objects.$nbt-rmy_file.nbt--path"bar"--unpack
Hello,world!If you don't know exactly how to access the data you're interested in you can use the--findoption to recursively try to match a given path.$nbt-rmy_file.nbt--find"[1]"2You can also perform all these operations on snbt by using the-soption instead of the-roption.$nbt-sfoo.snbt--pathbar"Hello, world!"Write nbt dataYou can write nbt data to a file by using the-woption. This will
convert the literal nbt notation to its binary form and save it in the
specified file.$nbt-w'{foo:[1,2,3],bar:{hello:[B;1b,1b,0b,1b]}}'my_file.nbtThe file will be created if it doesn't already exist.You can combine the-wflag with other flags to read, filter and write nbt data.$nbt-rmy_file.nbt--path"bar"-wbar.nbtMerge nbt dataFinally, you can merge some nbt data into an already existing file by
using the-moption. This will recursively update the file with
the values parsed from the literal argument.$nbt-m'{bar:{"new key":56f}}'my_file.nbtYou can check the result by using the-roption.$nbt-rmy_file.nbt{foo:[1,2,3],bar:{hello:[B;1B,1B,0B,1B],"new key":56.0f}}Here, the compound values that aren't present in the input literal are
left untouched. Using the-woption instead of-mwould
overwrite the whole file.You can combine the-mflag with other flags to read, filter and merge nbt data.$nbt-sfoo.snbt-mmy_file.nbtCompression and byte orderBy default, the CLI will assume that you're working with gzipped nbt
files. If you want to read, write or merge uncompressed nbt files, you
can use the--plainoption. Similarly, the default byte order is
big-endian so you'll need to use the--littleoption to perform
operations on little-endian files.Reading$nbt-rmy_file.nbt--plain--little{name:"Reading an uncompressed little-endian file"}Writing$nbt-w'{name:"Writing in an uncompressed little-endian file"}'my_file.nbt--plain--littleMerging$nbt-m'{name:"Merging in an uncompressed little-endian file"}'my_file.nbt--plain--littleContributingContributions are welcome. This project usespoetryso you'll need to install it first if you want to be able to work with the project locally.$curl-sSLhttps://raw.githubusercontent.com/sdispater/poetry/main/get-poetry.py|pythonYou should now be able to install the required dependencies.$poetryinstallYou can run the tests withpoetry run pytest.$poetryrunpytestLicense -MIT