• Stars
    star
    119
  • Rank 297,930 (Top 6 %)
  • Language
    Python
  • License
    MIT License
  • Created about 7 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A python library to read and edit nbt data.

nbtlib

GitHub Actions PyPI PyPI - Python Version Code style: black Discord

A python library to read and edit nbt data. Requires python 3.8.

Features

  • Create, read and edit nbt files
  • Supports gzipped and uncompressed files
  • Supports big-endian and little-endian files
  • Parse and serialize raw nbt data
  • Define tag schemas that automatically enforce predefined tag types
  • Convert nbt between binary form and literal notation
  • Use nbt paths to access deeply nested properties
  • Includes a CLI to quickly perform read/write/merge operations

Installation

Notice ๐Ÿšง

Version 2.0 is actively being worked on and is not stable yet. You should probably keep using version 1.12.1 for the moment. You can check out the 2.0 roadmap in the nbtlib 2.0 issue.

The package can be installed with pip.

$ pip install "nbtlib==1.12.1"

Basic usage

The 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 files

The nbtlib.load function can be used to load nbt files as nbtlib.File objects. 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.

import nbtlib

nbt_file = nbtlib.load('bigtest.nbt')
assert nbt_file['intTest'] == 2147483647

For example, instances of nbtlib.File inherit from regular Compound tags, which themselves inherit from the builtin python dictionary dict. Similarly, instances of Int tags inherit from the builtin class int.

For more details on loading nbt files and how to work with nbt tags check out the "Usage" notebook.

Editing files

You can use instances of nbtlib.File as context managers in order to save modifications automatically at the end of the with statement.

import nbtlib
from nbtlib.tag import Int

with nbtlib.load('demo.nbt') as demo:
    demo['counter'] = Int(demo['counter'] + 1)

You can also call the save method manually.

import nbtlib
from nbtlib.tag import Int

demo = nbtlib.load('demo.nbt')
demo['counter'] = Int(demo['counter'] + 1)
demo.save()

For more details on the save method check out the "Usage" notebook.

Using schemas

nbtlib allows you to define Compound schemas that enforce a specific tag type for any given key.

from nbtlib import schema
from nbtlib.tag import Short, String

MySchema = schema('MySchema', {
    'foo': String,
    'bar': Short
})

my_object = MySchema({'foo': 'hello world', 'bar': 21})

assert isinstance(my_object, MySchema)
assert isinstance(my_object['foo'], String)

For more details on schemas check out the "Usage" notebook.

Nbt literals

You can parse nbt literals using the nbtlib.parse_nbt function.

from nbtlib import parse_nbt
from nbtlib.tag import String, List, Compound, IntArray

my_compound = parse_nbt('{foo: [hello, world], bar: [I; 1, 2, 3]}')
assert my_compound == Compound({
    'foo': List[String](['hello', 'world']),
    'bar': IntArray([1, 2, 3])
})

Nbt tags can be serialized to their literal representation with the nbtlib.serialize_tag function.

from nbtlib import serialize_tag
from nbtlib.tag import String, List, Compound, IntArray

my_compound = Compound({
    'foo': List[String](['hello', 'world']),
    'bar': IntArray([1, 2, 3])
})
assert serialize_tag(my_compound) == '{foo: ["hello", "world"], bar: [I; 1, 2, 3]}'

For more details on nbt literals check out the "Usage" notebook.

Nbt paths

Nbt paths can be used to access deeply nested properties in nbt data. The implementation is based on information available on the Minecraft wiki.

from nbtlib import parse_nbt, Path

data = parse_nbt('{a: [{b: {c: 42}}]}')
assert data['a'][0]['b']['c'] == 42
assert data[Path('a[0].b.c')] == 42

You can retrieve, modify and delete multiple properties at the same time.

from nbtlib import parse_nbt, Path
from nbtlib.tag import Int

data = parse_nbt('{foo: [{a: 1, b: {c: 42}}, {a: 2, b: {c: 0}}]}')

data[Path('foo[].a')] = Int(99)
assert str(data) == '{foo: [{a: 99, b: {c: 42}}, {a: 99, b: {c: 0}}]}'

assert data.get_all(Path('foo[].b.c')) == [42, 0]

del data[Path('foo[].b{c: 0}')]
assert str(data) == '{foo: [{a: 99, b: {c: 42}}, {a: 99}]}'

Nbt paths are immutable but can be manipulated and combined together to form new paths.

from nbtlib import Path
from nbtlib.tag import Compound

path = Path()['hello']['world']
assert path[:][Compound({'a': Int(0)})] == 'hello.world[{a: 0}]'

assert path + path == 'hello.world.hello.world'
assert sum('abcdef', Path()) == 'a.b.c.d.e.f'

assert Path()[0] + 'foo{a: 1}' + '{b: 2}.bar' == '[0].foo{a: 1, b: 2}.bar'

assert path['key.with.dots'] == 'hello.world."key.with.dots"'
assert path + 'key.with.dots' == 'hello.world.key.with.dots'

Command-line interface

The 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 tag

Read nbt data

You can read nbt files by using the -r option. This will print the literal notation of the binary nbt data.

$ nbt -r my_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 -r my_file.nbt > my_file.snbt

Using the --compact argument will remove all the extra whitespace from the output.

$ nbt -r my_file.nbt --compact
{foo:[1,2,3],bar:"Hello, world!"}

You can use the --pretty argument if you want the command to output indented snbt.

$ nbt -r my_file.nbt --pretty
{
    foo: [1, 2, 3],
    bar: "Hello, world!"
}

The output can be converted to json with the --json flag.

$ nbt -r my_file.nbt --json
{"foo": [1, 2, 3], "bar": "Hello, world!"}

The --path option lets you output tags that match a given path.

$ nbt -r my_file.nbt --path "bar"
"Hello, world!"
$ nbt -r my_file.nbt --path "foo[]"
1
2
3

You can combine this with the --unpack flag to print out the unpacked python objects.

$ nbt -r my_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 --find option to recursively try to match a given path.

$ nbt -r my_file.nbt --find "[1]"
2

You can also perform all these operations on snbt by using the -s option instead of the -r option.

$ nbt -s foo.snbt --path bar
"Hello, world!"

Write nbt data

You can write nbt data to a file by using the -w option. 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.nbt

The file will be created if it doesn't already exist.

You can combine the -w flag with other flags to read, filter and write nbt data.

$ nbt -r my_file.nbt --path "bar" -w bar.nbt

Merge nbt data

Finally, you can merge some nbt data into an already existing file by using the -m option. This will recursively update the file with the values parsed from the literal argument.

$ nbt -m '{bar:{"new key":56f}}' my_file.nbt

You can check the result by using the -r option.

$ nbt -r my_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 -w option instead of -m would overwrite the whole file.

You can combine the -m flag with other flags to read, filter and merge nbt data.

$ nbt -s foo.snbt -m my_file.nbt

Compression and byte order

By 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 --plain option. Similarly, the default byte order is big-endian so you'll need to use the --little option to perform operations on little-endian files.

Reading

$ nbt -r my_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 --little

Merging

$ nbt -m '{name:"Merging in an uncompressed little-endian file"}' my_file.nbt --plain --little

Contributing

Contributions are welcome. This project uses poetry so you'll need to install it first if you want to be able to work with the project locally.

$ curl -sSL https://raw.githubusercontent.com/sdispater/poetry/main/get-poetry.py | python

You should now be able to install the required dependencies.

$ poetry install

You can run the tests with poetry run pytest.

$ poetry run pytest

License - MIT

More Repositories

1

narwhal

A progressive test framework for C.
C
116
star
2

poll

Cloudflare worker for embedding polls anywhere.
Rust
49
star
3

pytest-insta

A practical snapshot testing plugin for pytest.
Python
49
star
4

json-model-viewer

A 3d model viewer for minecraft json models.
JavaScript
37
star
5

endermite

A high-level, opinionated python framework for building Minecraft data packs.
Python
18
star
6

animated-models

A model animator for minecraft json models.
JavaScript
17
star
7

three-mcmodel

A library for working with Minecraft json models using three.js.
TypeScript
16
star
8

react-from-scratch

Supporting code for a presentation about React. Implementing stateful components in 100 lines of code.
JavaScript
15
star
9

tokenstream

A versatile token stream for handwritten parsers.
Python
13
star
10

goomy

A tiny, experimental ECS framework.
C++
13
star
11

mcpack

โš ๏ธ Please migrate to https://github.com/vberlier/beet
Python
11
star
12

mudkip

A friendly Sphinx wrapper.
Python
9
star
13

extract-math

Extract TeX math environments.
TypeScript
9
star
14

mjml-web

Browser-compatible mjml bundle.
JavaScript
7
star
15

narmock

A minimal mocking utility for C projects.
C
6
star
16

nuxt-mjml-web

Browser-compatible mjml module for NuxtJS.
JavaScript
4
star
17

docutils

Simple javascript parser for docutils xml documents.
JavaScript
3
star
18

mcwiki

A scraping library for the Minecraft Wiki.
Python
3
star
19

pigstep

A beet plugin for importing songs into Minecraft.
Python
1
star
20

fnf

Friday Night Funkin in Minecraft.
Python
1
star
21

settlements

A procedural village generator that builds houses, large wheat fields, and windmills!
Java
1
star
22

mostly-black

Muted colors for coding at night.
1
star
23

gridsome-transformer-docutils

Docutils transformer for Gridsome.
JavaScript
1
star