nbtx

Minecraft NBT parser and writer library.

Note that this library uses little-endian byte order by default which differs from most libraries that use big-endian byte order by default.

   1# MIT License
   2#
   3# Copyright (c) 2025 Jonas da Silva
   4#
   5# Permission is hereby granted, free of charge, to any person obtaining a copy
   6# of this software and associated documentation files (the "Software"), to deal
   7# in the Software without restriction, including without limitation the rights
   8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   9# copies of the Software, and to permit persons to whom the Software is
  10# furnished to do so, subject to the following conditions:
  11#
  12# The above copyright notice and this permission notice shall be included in all
  13# copies or substantial portions of the Software.
  14#
  15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21# SOFTWARE.
  22
  23"""
  24Minecraft NBT parser and writer library.
  25
  26Note that this library uses little-endian byte order by default which differs
  27from most libraries that use big-endian byte order by default.
  28"""
  29
  30from __future__ import annotations
  31
  32import struct
  33from abc import ABC, abstractmethod
  34from collections.abc import Iterator, Mapping, Sequence
  35from dataclasses import dataclass, field
  36from typing import IO, Any, Literal, Self, overload, override
  37
  38type Endianness = Literal["little"] | Literal["big"]
  39
  40PRETTY_INDENTATION = "  "
  41"""
  42Indentation used when pretty printing tags, more specifically ones that contain
  43nested tags.
  44"""
  45
  46STRING_LENGTH_LIMIT = 32_767
  47"""
  48The maximum length for strings including string tag values and tag names.
  49"""
  50
  51
  52def _struct_code_for_endianness(endianness: Endianness) -> str:
  53    return "<" if endianness == "little" else ">"
  54
  55
  56def _unpack(
  57    endianness: Endianness,
  58    format: str,
  59    buffer: bytes,
  60) -> tuple[bytes, tuple[Any, ...]]:
  61    """
  62    A wrapper around `struct.unpack` that advances the provided buffer.
  63    """
  64    e = _struct_code_for_endianness(endianness)
  65    fmt = e + format
  66    size = struct.calcsize(fmt)
  67    (data_buffer, advanced_buffer) = (buffer[:size], buffer[size:])
  68    data = struct.unpack(fmt, data_buffer)
  69    return (advanced_buffer, data)
  70
  71
  72def _pack(
  73    endianness: Endianness,
  74    format: str,
  75    *data: Any,
  76) -> bytes:
  77    e = _struct_code_for_endianness(endianness)
  78    fmt = e + format
  79    return struct.pack(fmt, *data)
  80
  81
  82def _read_id(
  83    endianness: Endianness, buffer: bytes, *, compare: int | None = None
  84) -> tuple[bytes, int]:
  85    """
  86    Reads an NBT tag ID.
  87
  88    If `compare` is provided, then this function compares the read ID with that
  89    value and raises a `ValueError` when there is a mismatch.
  90    """
  91    (buffer, (id,)) = _unpack(endianness, "b", buffer)
  92    assert isinstance(id, int)
  93    if compare is not None and id != compare:
  94        raise ValueError(f"read ID 0x{id:>02x}, but expected 0x{compare:>02x}")
  95    return (buffer, id)
  96
  97
  98def _write_id(endianness: Endianness, id: int, stream: IO[bytes]) -> None:
  99    stream.write(_pack(endianness, "b", id))
 100
 101
 102def _read_byte(endianness: Endianness, buffer: bytes) -> tuple[bytes, int]:
 103    """
 104    Reads an NBT encoded byte without the tag ID.
 105    """
 106    (buffer, (value,)) = _unpack(endianness, "b", buffer)
 107    assert isinstance(value, int)
 108    return (buffer, value)
 109
 110
 111def _write_byte(endianness: Endianness, byte: int, stream: IO[bytes]) -> None:
 112    stream.write(_pack(endianness, "b", byte))
 113
 114
 115def _read_short(endianness: Endianness, buffer: bytes) -> tuple[bytes, int]:
 116    """
 117    Reads an NBT encoded 16-bit signed integer without the tag ID.
 118    """
 119    (buffer, (value,)) = _unpack(endianness, "h", buffer)
 120    assert isinstance(value, int)
 121    return (buffer, value)
 122
 123
 124def _write_short(endianness: Endianness, integer: int, stream: IO[bytes]) -> None:
 125    stream.write(_pack(endianness, "h", integer))
 126
 127
 128def _read_int(endianness: Endianness, buffer: bytes) -> tuple[bytes, int]:
 129    """
 130    Reads an NBT encoded 32-bit signed integer without the tag ID.
 131    """
 132    (buffer, (value,)) = _unpack(endianness, "i", buffer)
 133    assert isinstance(value, int)
 134    return (buffer, value)
 135
 136
 137def _write_int(endianness: Endianness, integer: int, stream: IO[bytes]) -> None:
 138    stream.write(_pack(endianness, "i", integer))
 139
 140
 141def _read_long(endianness: Endianness, buffer: bytes) -> tuple[bytes, int]:
 142    """
 143    Reads an NBT encoded 64-bit signed integer without the tag ID.
 144    """
 145    (buffer, (value,)) = _unpack(endianness, "q", buffer)
 146    assert isinstance(value, int)
 147    return (buffer, value)
 148
 149
 150def _write_long(endianness: Endianness, integer: int, stream: IO[bytes]) -> None:
 151    stream.write(_pack(endianness, "q", integer))
 152
 153
 154def _read_float(endianness: Endianness, buffer: bytes) -> tuple[bytes, float]:
 155    """
 156    Reads an NBT encoded 32-bit floating point without the tag ID.
 157    """
 158    (buffer, (value,)) = _unpack(endianness, "f", buffer)
 159    assert isinstance(value, float)
 160    return (buffer, value)
 161
 162
 163def _write_float(endianness: Endianness, floating: float, stream: IO[bytes]) -> None:
 164    stream.write(_pack(endianness, "f", floating))
 165
 166
 167def _read_double(endianness: Endianness, buffer: bytes) -> tuple[bytes, float]:
 168    """
 169    Reads an NBT encoded 32-bit floating point without the tag ID.
 170    """
 171    (buffer, (value,)) = _unpack(endianness, "d", buffer)
 172    assert isinstance(value, float)
 173    return (buffer, value)
 174
 175
 176def _write_double(endianness: Endianness, floating: float, stream: IO[bytes]) -> None:
 177    stream.write(_pack(endianness, "d", floating))
 178
 179
 180def _read_string(endianness: Endianness, buffer: bytes) -> tuple[bytes, str]:
 181    """
 182    Reads a NBT encoded string without the tag ID.
 183
 184    A string starts with a 16-bit signed integer declaring the length of the
 185    string followed by the bytes that form the UTF-8 string.
 186    """
 187    (buffer, (length,)) = _unpack(endianness, "h", buffer)
 188    if length < 0:
 189        raise ValueError(f"unexpected length {length} for string")
 190    data = bytes()
 191    for _ in range(length):
 192        (buffer, (byte,)) = _unpack(endianness, "c", buffer)
 193        data += byte
 194    return (buffer, data.decode("utf8"))
 195
 196
 197def _write_string(endianness: Endianness, string: str, stream: IO[bytes]) -> None:
 198    data = string.encode("utf8")
 199    stream.write(_pack(endianness, "h", len(data)))
 200    stream.write(data)
 201
 202
 203def _read_byte_list(
 204    endianness: Endianness, buffer: bytes
 205) -> tuple[bytes, Sequence[int]]:
 206    (buffer, length) = _read_int(endianness, buffer)
 207    children = []
 208    for _ in range(length):
 209        (buffer, byte) = _read_byte(endianness, buffer)
 210        children.append(byte)
 211    return (buffer, children)
 212
 213
 214def _write_byte_list(
 215    endianness: Endianness, children: Sequence[int], stream: IO[bytes]
 216) -> None:
 217    _write_int(endianness, len(children), stream)
 218    for byte in children:
 219        _write_byte(endianness, byte, stream)
 220
 221
 222def _read_long_list(
 223    endianness: Endianness, buffer: bytes
 224) -> tuple[bytes, Sequence[int]]:
 225    (buffer, length) = _read_int(endianness, buffer)
 226    children = []
 227    for _ in range(length):
 228        (buffer, long) = _read_long(endianness, buffer)
 229        children.append(long)
 230    return (buffer, children)
 231
 232
 233def _write_long_list(
 234    endianness: Endianness, children: Sequence[int], stream: IO[bytes]
 235) -> None:
 236    _write_int(endianness, len(children), stream)
 237    for long in children:
 238        _write_long(endianness, long, stream)
 239
 240
 241def _read_int_list(
 242    endianness: Endianness, buffer: bytes
 243) -> tuple[bytes, Sequence[int]]:
 244    (buffer, length) = _read_int(endianness, buffer)
 245    children = []
 246    for _ in range(length):
 247        (buffer, long) = _read_int(endianness, buffer)
 248        children.append(long)
 249    return (buffer, children)
 250
 251
 252def _write_int_list(
 253    endianness: Endianness, children: Sequence[int], stream: IO[bytes]
 254) -> None:
 255    _write_int(endianness, len(children), stream)
 256    for integer in children:
 257        _write_int(endianness, integer, stream)
 258
 259
 260def _read_list(
 261    endianness: Endianness, buffer: bytes
 262) -> tuple[bytes, Sequence[Any], int]:
 263    (buffer, child_id) = _read_byte(endianness, buffer)
 264    (buffer, length) = _read_int(endianness, buffer)
 265    children: list[Any] = []
 266    for _ in range(length):
 267        match child_id:
 268            case 0x01:
 269                (buffer, byte_data) = _read_byte(endianness, buffer)
 270                children.append(TagByte("", byte_data))
 271            case 0x02:
 272                (buffer, short_data) = _read_short(endianness, buffer)
 273                children.append(TagShort("", short_data))
 274            case 0x03:
 275                (buffer, int_data) = _read_int(endianness, buffer)
 276                children.append(TagInt("", int_data))
 277            case 0x04:
 278                (buffer, long_data) = _read_long(endianness, buffer)
 279                children.append(TagLong("", long_data))
 280            case 0x05:
 281                (buffer, float_data) = _read_float(endianness, buffer)
 282                children.append(TagFloat("", float_data))
 283            case 0x06:
 284                (buffer, double_data) = _read_double(endianness, buffer)
 285                children.append(TagDouble("", double_data))
 286            case 0x07:
 287                (buffer, byte_list_data) = _read_byte_list(endianness, buffer)
 288                children.append(TagByteList("", byte_list_data))
 289            case 0x08:
 290                (buffer, string_data) = _read_string(endianness, buffer)
 291                children.append(TagString("", string_data))
 292            case 0x09:
 293                (buffer, list_data, sublist_child_id) = _read_list(endianness, buffer)
 294                children.append(TagList("", list_data, child_id=sublist_child_id))
 295            case 0x0A:
 296                (buffer, compound_data) = _read_compound(endianness, buffer)
 297                children.append(TagCompound("", compound_data))
 298            case 0x0B:
 299                (buffer, int_list) = _read_int_list(endianness, buffer)
 300                children.append(TagIntList("", int_list))
 301            case 0x0C:
 302                (buffer, long_list) = _read_long_list(endianness, buffer)
 303                children.append(TagLongList("", long_list))
 304            case _:
 305                raise FormatError("expected valid ID")
 306    return (buffer, children, child_id)
 307
 308
 309def _write_list(
 310    endianness: Endianness, children: Sequence[Any], child_id: int, stream: IO[bytes]
 311) -> None:
 312    _write_byte(endianness, child_id, stream)
 313    _write_int(endianness, len(children), stream)
 314    for child in children:
 315        match child.id():
 316            case 0x01:
 317                _write_byte(endianness, child.value, stream)
 318            case 0x02:
 319                _write_short(endianness, child.value, stream)
 320            case 0x03:
 321                _write_int(endianness, child.value, stream)
 322            case 0x04:
 323                _write_long(endianness, child.value, stream)
 324            case 0x05:
 325                _write_float(endianness, child.value, stream)
 326            case 0x06:
 327                _write_double(endianness, child.value, stream)
 328            case 0x07:
 329                _write_byte_list(endianness, child.value, stream)
 330            case 0x08:
 331                _write_string(endianness, child.value, stream)
 332            case 0x09:
 333                _write_list(endianness, child.value, child.child_id, stream)
 334            case 0x0A:
 335                _write_compound(endianness, child.value, stream)
 336            case 0x0B:
 337                _write_int_list(endianness, child.value, stream)
 338            case 0x0C:
 339                _write_long_list(endianness, child.value, stream)
 340            case _:
 341                raise FormatError("expected valid ID")
 342
 343
 344def _read_compound(
 345    endianness: Endianness, buffer: bytes
 346) -> tuple[bytes, Sequence[Tag[Any, Any]]]:
 347    compound: list[Tag[Any, Any]] = []
 348    while True:
 349        (new_buffer_if_end, child_id) = _read_id(endianness, buffer)
 350        if child_id == 0x00:
 351            buffer = new_buffer_if_end
 352            break
 353        tag = _tag_class_by_id(child_id)
 354        (child, buffer) = tag._read(buffer, endianness=endianness)
 355        for index, old_child in enumerate(compound):
 356            if child.name == old_child.name:
 357                raise ValueError(
 358                    f"duplicate key in compound at index {index}: {old_child.name}"
 359                )
 360        compound.append(child)
 361    return (buffer, compound)
 362
 363
 364def _write_compound(
 365    endianness: Endianness, children: Sequence[Tag[Any, Any]], stream: IO[bytes]
 366) -> None:
 367    for child in children:
 368        child._write(stream, endianness=endianness)
 369    stream.write(b"\0")
 370
 371
 372def _tag_class_by_id(id: int) -> type[Tag[Any, Any]]:
 373    if id == TagByte.id():
 374        return TagByte
 375    if id == TagShort.id():
 376        return TagShort
 377    if id == TagInt.id():
 378        return TagInt
 379    if id == TagLong.id():
 380        return TagLong
 381    if id == TagFloat.id():
 382        return TagFloat
 383    if id == TagDouble.id():
 384        return TagDouble
 385    if id == TagList.id():
 386        return TagList
 387    if id == TagByteList.id():
 388        return TagByteList
 389    if id == TagIntList.id():
 390        return TagIntList
 391    if id == TagLongList.id():
 392        return TagLongList
 393    if id == TagCompound.id():
 394        return TagCompound
 395    if id == TagString.id():
 396        return TagString
 397    raise ValueError(f"read ID 0x{id:>02x}, but expected 0x00/.../0x12")
 398
 399
 400@dataclass
 401class NBTException(Exception):
 402    """
 403    Base exceptions for NBT related exceptions.
 404    """
 405
 406    pass
 407
 408
 409@dataclass
 410class UnexpectedNameException(NBTException):
 411    """
 412    Exception raised when a named tag is used when an unnamed one was expected.
 413    """
 414
 415    def __str__(self) -> str:
 416        return "unexpected name"
 417
 418
 419@dataclass
 420class ExpectedNameException(NBTException):
 421    """
 422    Exception raised when an named tag is expected when an unnamed one was
 423    provided.
 424    """
 425
 426    def __str__(self) -> str:
 427        return "expected name"
 428
 429
 430@dataclass
 431class TypeConflictException(NBTException):
 432    """
 433    Exception raised when there is a conflict of tags within a container.
 434    """
 435
 436    id_actual: int
 437    id_expected: int
 438
 439    def __str__(self) -> str:
 440        return f"conflicting type: {self.id_actual:>02x} vs {self.id_expected:>02x}"
 441
 442
 443@dataclass
 444class StringTooLongException(NBTException):
 445    """
 446    Exception raised for string that exceed the limit of 32.767.
 447    """
 448
 449    length: int
 450
 451    def __str__(self) -> str:
 452        return f"string exceeds limit: {self.length > STRING_LENGTH_LIMIT}"
 453
 454
 455@dataclass
 456class UnemptyBufferException(NBTException):
 457    """
 458    Exception raised when a buffer was not empty after full parse.
 459    """
 460
 461    def __str__(self) -> str:
 462        return "buffer was not empty after full parse"
 463
 464
 465@dataclass
 466class FormatError(NBTException):
 467    """
 468    Exception raised when the input is not valid NBT format.
 469    """
 470
 471    message: str
 472
 473    def __str__(self) -> str:
 474        return f"malformed data: {self.message}"
 475
 476
 477@dataclass(frozen=True)
 478class Tag[T, P](ABC):
 479    """
 480    Base class for NBT tags.
 481    """
 482
 483    name: str
 484    """
 485    The name of the tag.
 486
 487    Commonly the name is irrelevant such as in a list. In that case an empty
 488    string must be used.
 489    """
 490
 491    value: T
 492    """
 493    The value of the tag.
 494    """
 495
 496    @staticmethod
 497    @abstractmethod
 498    def id() -> int:
 499        """
 500        The ID that represents the tag in binary format.
 501        """
 502
 503    @abstractmethod
 504    def as_python(self) -> P:
 505        """
 506        Returns a python representation of this tag.
 507        """
 508
 509    @classmethod
 510    @abstractmethod
 511    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Tag[T, P], bytes]:
 512        """
 513        Reads bytes from a buffer and interprets them as this tag.
 514
 515        This is a low-level function. If you intend to read an NBT input
 516        use `load` instead.
 517
 518        This process includes parsing the ID, the name and the value of the tag.
 519        This function returns a tuple containing the tag instance and the
 520        remaining bytes.
 521        """
 522
 523    @abstractmethod
 524    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 525        """
 526        Writes bytes to a buffer.
 527
 528        This is a low-level function. If you intend to write NBT tags use
 529        `dump` instead.
 530        """
 531
 532    @abstractmethod
 533    def pretty(self) -> str:
 534        """
 535        Returns a human readable pretty representation of the tag structure.
 536        """
 537
 538
 539@dataclass(frozen=True)
 540class TagByte(Tag[int, int]):
 541    """
 542    NBT tag for a single byte.
 543    """
 544
 545    # docstr-coverage:inherited
 546    @override
 547    @staticmethod
 548    def id() -> int:
 549        return 0x01
 550
 551    # docstr-coverage:inherited
 552    @override
 553    def as_python(self) -> int:
 554        return self.value
 555
 556    @override
 557    @classmethod
 558    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 559        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 560        (buffer, name) = _read_string(endianness, buffer)
 561        (buffer, value) = _read_byte(endianness, buffer)
 562        return (cls(name, value), buffer)
 563
 564    @override
 565    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 566        _write_id(endianness, self.id(), stream)
 567        _write_string(endianness, self.name, stream)
 568        _write_byte(endianness, self.value, stream)
 569
 570    # docstr-coverage:inherited
 571    @override
 572    def pretty(self) -> str:
 573        return f"Byte({self.name!r}): {self.value}"
 574
 575
 576@dataclass(frozen=True)
 577class TagShort(Tag[int, int]):
 578    """
 579    NBT tag for a short integer (signed 16-bit integer).
 580    """
 581
 582    # docstr-coverage:inherited
 583    @override
 584    @staticmethod
 585    def id() -> int:
 586        return 0x02
 587
 588    # docstr-coverage:inherited
 589    @override
 590    def as_python(self) -> int:
 591        return self.value
 592
 593    @override
 594    @classmethod
 595    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 596        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 597        (buffer, name) = _read_string(endianness, buffer)
 598        (buffer, value) = _read_short(endianness, buffer)
 599        return (cls(name, value), buffer)
 600
 601    @override
 602    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 603        _write_id(endianness, self.id(), stream)
 604        _write_string(endianness, self.name, stream)
 605        _write_short(endianness, self.value, stream)
 606
 607    # docstr-coverage:inherited
 608    @override
 609    def pretty(self) -> str:
 610        return f"Short({self.name!r}): {self.value}"
 611
 612
 613@dataclass(frozen=True)
 614class TagInt(Tag[int, int]):
 615    """
 616    NBT tag for an integer (signed 32-bit integer).
 617    """
 618
 619    # docstr-coverage:inherited
 620    @override
 621    @staticmethod
 622    def id() -> int:
 623        return 0x03
 624
 625    # docstr-coverage:inherited
 626    @override
 627    def as_python(self) -> int:
 628        return self.value
 629
 630    @override
 631    @classmethod
 632    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 633        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 634        (buffer, name) = _read_string(endianness, buffer)
 635        (buffer, value) = _read_int(endianness, buffer)
 636        return (cls(name, value), buffer)
 637
 638    @override
 639    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 640        _write_id(endianness, self.id(), stream)
 641        _write_string(endianness, self.name, stream)
 642        _write_int(endianness, self.value, stream)
 643
 644    # docstr-coverage:inherited
 645    @override
 646    def pretty(self) -> str:
 647        return f"Int({self.name!r}): {self.value}"
 648
 649
 650@dataclass(frozen=True)
 651class TagLong(Tag[int, int]):
 652    """
 653    NBT tag for a long integer (signed 64-bit integer).
 654    """
 655
 656    # docstr-coverage:inherited
 657    @override
 658    @staticmethod
 659    def id() -> int:
 660        return 0x04
 661
 662    # docstr-coverage:inherited
 663    @override
 664    def as_python(self) -> int:
 665        return self.value
 666
 667    @override
 668    @classmethod
 669    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 670        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 671        (buffer, name) = _read_string(endianness, buffer)
 672        (buffer, value) = _read_long(endianness, buffer)
 673        return (cls(name, value), buffer)
 674
 675    @override
 676    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 677        _write_id(endianness, self.id(), stream)
 678        _write_string(endianness, self.name, stream)
 679        _write_long(endianness, self.value, stream)
 680
 681    # docstr-coverage:inherited
 682    @override
 683    def pretty(self) -> str:
 684        return f"Long({self.name!r}): {self.value}"
 685
 686
 687@dataclass(frozen=True)
 688class TagFloat(Tag[float, float]):
 689    """
 690    NBT tag for an integer (32-bit floating point).
 691    """
 692
 693    # docstr-coverage:inherited
 694    @override
 695    @staticmethod
 696    def id() -> int:
 697        return 0x05
 698
 699    # docstr-coverage:inherited
 700    @override
 701    def as_python(self) -> float:
 702        return self.value
 703
 704    @override
 705    @classmethod
 706    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 707        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 708        (buffer, name) = _read_string(endianness, buffer)
 709        (buffer, value) = _read_float(endianness, buffer)
 710        return (cls(name, value), buffer)
 711
 712    @override
 713    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 714        _write_id(endianness, self.id(), stream)
 715        _write_string(endianness, self.name, stream)
 716        _write_float(endianness, self.value, stream)
 717
 718    # docstr-coverage:inherited
 719    @override
 720    def pretty(self) -> str:
 721        return f"Float({self.name!r}): {self.value}"
 722
 723
 724@dataclass(frozen=True)
 725class TagDouble(Tag[float, float]):
 726    """
 727    NBT tag for an integer (64-bit floating point).
 728    """
 729
 730    # docstr-coverage:inherited
 731    @override
 732    @staticmethod
 733    def id() -> int:
 734        return 0x06
 735
 736    # docstr-coverage:inherited
 737    @override
 738    def as_python(self) -> float:
 739        return self.value
 740
 741    @override
 742    @classmethod
 743    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 744        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 745        (buffer, name) = _read_string(endianness, buffer)
 746        (buffer, value) = _read_double(endianness, buffer)
 747        return (cls(name, value), buffer)
 748
 749    @override
 750    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 751        _write_id(endianness, self.id(), stream)
 752        _write_string(endianness, self.name, stream)
 753        _write_double(endianness, self.value, stream)
 754
 755    # docstr-coverage:inherited
 756    @override
 757    def pretty(self) -> str:
 758        return f"Double({self.name!r}): {self.value}"
 759
 760
 761@dataclass(frozen=True)
 762class TagString(Tag[str, str]):
 763    """
 764    NBT tag for a UTF-8 encoded string.
 765    """
 766
 767    def __post_init__(self) -> None:
 768        if len(self.value) > STRING_LENGTH_LIMIT:
 769            raise StringTooLongException(len(self.value))
 770
 771    # docstr-coverage:inherited
 772    @override
 773    @staticmethod
 774    def id() -> int:
 775        return 0x08
 776
 777    # docstr-coverage:inherited
 778    @override
 779    def as_python(self) -> str:
 780        return self.value
 781
 782    @override
 783    @classmethod
 784    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 785        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 786        (buffer, name) = _read_string(endianness, buffer)
 787        (buffer, string) = _read_string(endianness, buffer)
 788        return (cls(name, string), buffer)
 789
 790    @override
 791    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 792        _write_id(endianness, self.id(), stream)
 793        _write_string(endianness, self.name, stream)
 794        _write_string(endianness, self.value, stream)
 795
 796    # docstr-coverage:inherited
 797    @override
 798    def pretty(self) -> str:
 799        return f"String({self.name!r}): {self.value!r}"
 800
 801
 802@dataclass(frozen=True)
 803class TagList[T, P](Tag[Sequence[Tag[T, P]], list[P]], Sequence[Tag[T, P]]):
 804    """
 805    NBT tag for a list containing nameless tags of one kind.
 806    """
 807
 808    child_id: int = field(kw_only=True)
 809
 810    def __post_init__(self) -> None:
 811        for child in self.value:
 812            if child.id() != self.child_id:
 813                raise TypeConflictException(
 814                    id_actual=child.id(), id_expected=self.child_id
 815                )
 816            if child.name != "":
 817                raise UnexpectedNameException()
 818
 819    # docstr-coverage:inherited
 820    @override
 821    @staticmethod
 822    def id() -> int:
 823        return 0x09
 824
 825    # docstr-coverage:inherited
 826    @override
 827    def as_python(self) -> list[P]:
 828        return [tag.as_python() for tag in self.value]
 829
 830    @override
 831    @classmethod
 832    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 833        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 834        (buffer, name) = _read_string(endianness, buffer)
 835        (buffer, children, child_id) = _read_list(endianness, buffer)
 836        return (cls(name, children, child_id=child_id), buffer)
 837
 838    @override
 839    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 840        _write_id(endianness, self.id(), stream)
 841        _write_string(endianness, self.name, stream)
 842        _write_list(endianness, self.value, self.child_id, stream)
 843
 844    # docstr-coverage:inherited
 845    @override
 846    def pretty(self) -> str:
 847        string = ""
 848        string += f"TagList({self.name!r}): {len(self.value)} entries\n"
 849        string += "{\n"
 850        for child in self.value:
 851            for line in child.pretty().splitlines():
 852                string += f"{PRETTY_INDENTATION}{line}\n"
 853        string += "}"
 854        return string
 855
 856    @overload
 857    def __getitem__(self, index: int) -> Tag[T, P]: ...
 858
 859    @overload
 860    def __getitem__(self, index: slice[int, int, int]) -> Sequence[Tag[T, P]]: ...
 861
 862    @override
 863    def __getitem__(
 864        self, index: int | slice[int, int, int]
 865    ) -> Tag[T, P] | Sequence[Tag[T, P]]:
 866        return self.value[index]
 867
 868    @override
 869    def __len__(self) -> int:
 870        return len(self.value)
 871
 872
 873@dataclass(frozen=True)
 874class TagByteList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
 875    """
 876    NBT tag for a list of bytes.
 877    """
 878
 879    # docstr-coverage:inherited
 880    @override
 881    @staticmethod
 882    def id() -> int:
 883        return 0x07
 884
 885    # docstr-coverage:inherited
 886    @override
 887    def as_python(self) -> Sequence[int]:
 888        return self.value
 889
 890    @override
 891    @classmethod
 892    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 893        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 894        (buffer, name) = _read_string(endianness, buffer)
 895        (buffer, children) = _read_byte_list(endianness, buffer)
 896        return (cls(name, children), buffer)
 897
 898    @override
 899    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 900        _write_id(endianness, self.id(), stream)
 901        _write_string(endianness, self.name, stream)
 902        _write_byte_list(endianness, self.value, stream)
 903
 904    # docstr-coverage:inherited
 905    @override
 906    def pretty(self) -> str:
 907        string = ""
 908        string += f"ByteList({self.name!r}) : {len(self.value)} entries\n"
 909        string += "{\n"
 910        for long in self.value:
 911            string += f"{PRETTY_INDENTATION}{long}\n"
 912        string += "}"
 913        return string
 914
 915    @overload
 916    def __getitem__(self, index: int) -> int: ...
 917
 918    @overload
 919    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
 920
 921    @override
 922    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
 923        return self.value[index]
 924
 925    @override
 926    def __len__(self) -> int:
 927        return len(self.value)
 928
 929
 930@dataclass(frozen=True)
 931class TagIntList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
 932    """
 933    NBT tag for a list of integers.
 934    """
 935
 936    # docstr-coverage:inherited
 937    @override
 938    @staticmethod
 939    def id() -> int:
 940        return 0x0B
 941
 942    # docstr-coverage:inherited
 943    @override
 944    def as_python(self) -> Sequence[int]:
 945        return self.value
 946
 947    @override
 948    @classmethod
 949    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
 950        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
 951        (buffer, name) = _read_string(endianness, buffer)
 952        (buffer, children) = _read_int_list(endianness, buffer)
 953        return (cls(name, children), buffer)
 954
 955    @override
 956    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
 957        _write_id(endianness, self.id(), stream)
 958        _write_string(endianness, self.name, stream)
 959        _write_int_list(endianness, self.value, stream)
 960
 961    # docstr-coverage:inherited
 962    @override
 963    def pretty(self) -> str:
 964        string = ""
 965        string += f"IntList({self.name!r}) : {len(self.value)} entries\n"
 966        string += "{\n"
 967        for integer in self.value:
 968            string += f"{PRETTY_INDENTATION}{integer}\n"
 969        string += "}"
 970        return string
 971
 972    @overload
 973    def __getitem__(self, index: int) -> int: ...
 974
 975    @overload
 976    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
 977
 978    @override
 979    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
 980        return self.value[index]
 981
 982    @override
 983    def __len__(self) -> int:
 984        return len(self.value)
 985
 986
 987@dataclass(frozen=True)
 988class TagLongList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
 989    """
 990    NBT tag for a list of long integers.
 991    """
 992
 993    # docstr-coverage:inherited
 994    @override
 995    @staticmethod
 996    def id() -> int:
 997        return 0x0C
 998
 999    # docstr-coverage:inherited
1000    @override
1001    def as_python(self) -> Sequence[int]:
1002        return self.value
1003
1004    @override
1005    @classmethod
1006    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
1007        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
1008        (buffer, name) = _read_string(endianness, buffer)
1009        (buffer, children) = _read_long_list(endianness, buffer)
1010        return (cls(name, children), buffer)
1011
1012    @override
1013    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
1014        _write_id(endianness, self.id(), stream)
1015        _write_string(endianness, self.name, stream)
1016        _write_long_list(endianness, self.value, stream)
1017
1018    # docstr-coverage:inherited
1019    @override
1020    def pretty(self) -> str:
1021        string = ""
1022        string += f"LongList({self.name!r}) : {len(self.value)} entries\n"
1023        string += "{\n"
1024        for byte in self.value:
1025            string += f"{PRETTY_INDENTATION}{byte}\n"
1026        string += "}"
1027        return string
1028
1029    @overload
1030    def __getitem__(self, index: int) -> int: ...
1031
1032    @overload
1033    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
1034
1035    @override
1036    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
1037        return self.value[index]
1038
1039    @override
1040    def __len__(self) -> int:
1041        return len(self.value)
1042
1043
1044@dataclass(frozen=True)
1045class TagCompound[T, P](
1046    Tag[Sequence[Tag[T, P]], dict[str, P]], Mapping[str, Tag[T, P]]
1047):
1048    """
1049    NBT tag for a compound (list of uniquely named tags).
1050    """
1051
1052    def __post_init__(self) -> None:
1053        for child in self.value:
1054            if child.name == "":
1055                raise ExpectedNameException()
1056
1057    # docstr-coverage:inherited
1058    @override
1059    @staticmethod
1060    def id() -> int:
1061        return 0x0A
1062
1063    # docstr-coverage:inherited
1064    @override
1065    def as_python(self) -> dict[str, P]:
1066        result = {}
1067        for tag in self.value:
1068            result[tag.name] = tag.as_python()
1069        return result
1070
1071    @override
1072    @classmethod
1073    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
1074        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
1075        (buffer, name) = _read_string(endianness, buffer)
1076        (buffer, compound) = _read_compound(endianness, buffer)
1077        return (cls(name, compound), buffer)
1078
1079    @override
1080    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
1081        _write_id(endianness, self.id(), stream)
1082        _write_string(endianness, self.name, stream)
1083        _write_compound(endianness, self.value, stream)
1084
1085    # docstr-coverage:inherited
1086    @override
1087    def pretty(self) -> str:
1088        string = ""
1089        string += f"Compound({self.name!r}): {len(self.value)} entries\n"
1090        string += "{\n"
1091        for child in self.value:
1092            for line in child.pretty().splitlines():
1093                string += f"{PRETTY_INDENTATION}{line}\n"
1094        string += "}"
1095        return string
1096
1097    @override
1098    def __getitem__(self, key: str) -> Tag[T, P]:
1099        for tag in self.value:
1100            if tag.name == key:
1101                return tag
1102        raise KeyError(f"{key!r}")
1103
1104    @override
1105    def __iter__(self) -> Iterator[str]:
1106        return (tag.name for tag in self.value)
1107
1108    @override
1109    def __len__(self) -> int:
1110        return len(self.value)
1111
1112
1113def load(
1114    file: IO[bytes],
1115    *,
1116    endianness: Endianness = "little",
1117    ignore_rest: bool = False,
1118) -> Tag[Any, Any]:
1119    """
1120    Loads an NBT file and returns the contained NBT tag.
1121
1122    # Parameters
1123
1124    - `file` -- The file-like object to read the data from.
1125    - `endianness` -- The byte order to use during parsing. Java Edition usually
1126      uses big endian byte order and Bedrock Edition usually uses little endian
1127      byte order.
1128    - `ignore_rest` -- Whether to ignore remaining bytes after a full parse. By
1129      default, this function expects the buffer to be empty after an NBT tree
1130      has been serialized and raises an exception otherwise.
1131    """
1132    buffer = file.read()
1133    (_, id) = _read_id(endianness, buffer)
1134    tag_cls = _tag_class_by_id(id)
1135    (tag, rest) = tag_cls._read(buffer, endianness=endianness)
1136    if rest and not ignore_rest:
1137        raise UnemptyBufferException()
1138    return tag
1139
1140
1141def dump(
1142    tag: Tag[Any, Any], stream: IO[bytes], *, endianness: Endianness = "little"
1143) -> None:
1144    """
1145    Dumps an NBT tag to a file.
1146
1147    # Parameters
1148
1149    - `tag` -- The root tag to write to the stream.
1150    - `stream` -- The stream to write to.
1151    - `endianness` -- The byte order to use. Java Edition usually uses big
1152      endian byte order and Bedrock Edition usually uses little endian byte
1153      order.
1154    """
1155    tag._write(stream, endianness=endianness)
type Endianness = Union[Literal['little'], Literal['big']]
PRETTY_INDENTATION = ' '

Indentation used when pretty printing tags, more specifically ones that contain nested tags.

STRING_LENGTH_LIMIT = 32767

The maximum length for strings including string tag values and tag names.

@dataclass
class NBTException(builtins.Exception):
401@dataclass
402class NBTException(Exception):
403    """
404    Base exceptions for NBT related exceptions.
405    """
406
407    pass

Base exceptions for NBT related exceptions.

@dataclass
class UnexpectedNameException(NBTException):
410@dataclass
411class UnexpectedNameException(NBTException):
412    """
413    Exception raised when a named tag is used when an unnamed one was expected.
414    """
415
416    def __str__(self) -> str:
417        return "unexpected name"

Exception raised when a named tag is used when an unnamed one was expected.

@dataclass
class ExpectedNameException(NBTException):
420@dataclass
421class ExpectedNameException(NBTException):
422    """
423    Exception raised when an named tag is expected when an unnamed one was
424    provided.
425    """
426
427    def __str__(self) -> str:
428        return "expected name"

Exception raised when an named tag is expected when an unnamed one was provided.

@dataclass
class TypeConflictException(NBTException):
431@dataclass
432class TypeConflictException(NBTException):
433    """
434    Exception raised when there is a conflict of tags within a container.
435    """
436
437    id_actual: int
438    id_expected: int
439
440    def __str__(self) -> str:
441        return f"conflicting type: {self.id_actual:>02x} vs {self.id_expected:>02x}"

Exception raised when there is a conflict of tags within a container.

TypeConflictException(id_actual: int, id_expected: int)
id_actual: int
id_expected: int
@dataclass
class StringTooLongException(NBTException):
444@dataclass
445class StringTooLongException(NBTException):
446    """
447    Exception raised for string that exceed the limit of 32.767.
448    """
449
450    length: int
451
452    def __str__(self) -> str:
453        return f"string exceeds limit: {self.length > STRING_LENGTH_LIMIT}"

Exception raised for string that exceed the limit of 32.767.

StringTooLongException(length: int)
length: int
@dataclass
class UnemptyBufferException(NBTException):
456@dataclass
457class UnemptyBufferException(NBTException):
458    """
459    Exception raised when a buffer was not empty after full parse.
460    """
461
462    def __str__(self) -> str:
463        return "buffer was not empty after full parse"

Exception raised when a buffer was not empty after full parse.

@dataclass
class FormatError(NBTException):
466@dataclass
467class FormatError(NBTException):
468    """
469    Exception raised when the input is not valid NBT format.
470    """
471
472    message: str
473
474    def __str__(self) -> str:
475        return f"malformed data: {self.message}"

Exception raised when the input is not valid NBT format.

FormatError(message: str)
message: str
@dataclass(frozen=True)
class Tag(abc.ABC, typing.Generic[T, P]):
478@dataclass(frozen=True)
479class Tag[T, P](ABC):
480    """
481    Base class for NBT tags.
482    """
483
484    name: str
485    """
486    The name of the tag.
487
488    Commonly the name is irrelevant such as in a list. In that case an empty
489    string must be used.
490    """
491
492    value: T
493    """
494    The value of the tag.
495    """
496
497    @staticmethod
498    @abstractmethod
499    def id() -> int:
500        """
501        The ID that represents the tag in binary format.
502        """
503
504    @abstractmethod
505    def as_python(self) -> P:
506        """
507        Returns a python representation of this tag.
508        """
509
510    @classmethod
511    @abstractmethod
512    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Tag[T, P], bytes]:
513        """
514        Reads bytes from a buffer and interprets them as this tag.
515
516        This is a low-level function. If you intend to read an NBT input
517        use `load` instead.
518
519        This process includes parsing the ID, the name and the value of the tag.
520        This function returns a tuple containing the tag instance and the
521        remaining bytes.
522        """
523
524    @abstractmethod
525    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
526        """
527        Writes bytes to a buffer.
528
529        This is a low-level function. If you intend to write NBT tags use
530        `dump` instead.
531        """
532
533    @abstractmethod
534    def pretty(self) -> str:
535        """
536        Returns a human readable pretty representation of the tag structure.
537        """

Base class for NBT tags.

name: str

The name of the tag.

Commonly the name is irrelevant such as in a list. In that case an empty string must be used.

value: 'T'

The value of the tag.

@staticmethod
@abstractmethod
def id() -> int:
497    @staticmethod
498    @abstractmethod
499    def id() -> int:
500        """
501        The ID that represents the tag in binary format.
502        """

The ID that represents the tag in binary format.

@abstractmethod
def as_python(self) -> 'P':
504    @abstractmethod
505    def as_python(self) -> P:
506        """
507        Returns a python representation of this tag.
508        """

Returns a python representation of this tag.

@abstractmethod
def pretty(self) -> str:
533    @abstractmethod
534    def pretty(self) -> str:
535        """
536        Returns a human readable pretty representation of the tag structure.
537        """

Returns a human readable pretty representation of the tag structure.

@dataclass(frozen=True)
class TagByte(nbtx.Tag[int, int]):
540@dataclass(frozen=True)
541class TagByte(Tag[int, int]):
542    """
543    NBT tag for a single byte.
544    """
545
546    # docstr-coverage:inherited
547    @override
548    @staticmethod
549    def id() -> int:
550        return 0x01
551
552    # docstr-coverage:inherited
553    @override
554    def as_python(self) -> int:
555        return self.value
556
557    @override
558    @classmethod
559    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
560        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
561        (buffer, name) = _read_string(endianness, buffer)
562        (buffer, value) = _read_byte(endianness, buffer)
563        return (cls(name, value), buffer)
564
565    @override
566    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
567        _write_id(endianness, self.id(), stream)
568        _write_string(endianness, self.name, stream)
569        _write_byte(endianness, self.value, stream)
570
571    # docstr-coverage:inherited
572    @override
573    def pretty(self) -> str:
574        return f"Byte({self.name!r}): {self.value}"

NBT tag for a single byte.

TagByte(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
547    @override
548    @staticmethod
549    def id() -> int:
550        return 0x01

The ID that represents the tag in binary format.

@override
def as_python(self) -> int:
553    @override
554    def as_python(self) -> int:
555        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
572    @override
573    def pretty(self) -> str:
574        return f"Byte({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagShort(nbtx.Tag[int, int]):
577@dataclass(frozen=True)
578class TagShort(Tag[int, int]):
579    """
580    NBT tag for a short integer (signed 16-bit integer).
581    """
582
583    # docstr-coverage:inherited
584    @override
585    @staticmethod
586    def id() -> int:
587        return 0x02
588
589    # docstr-coverage:inherited
590    @override
591    def as_python(self) -> int:
592        return self.value
593
594    @override
595    @classmethod
596    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
597        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
598        (buffer, name) = _read_string(endianness, buffer)
599        (buffer, value) = _read_short(endianness, buffer)
600        return (cls(name, value), buffer)
601
602    @override
603    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
604        _write_id(endianness, self.id(), stream)
605        _write_string(endianness, self.name, stream)
606        _write_short(endianness, self.value, stream)
607
608    # docstr-coverage:inherited
609    @override
610    def pretty(self) -> str:
611        return f"Short({self.name!r}): {self.value}"

NBT tag for a short integer (signed 16-bit integer).

TagShort(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
584    @override
585    @staticmethod
586    def id() -> int:
587        return 0x02

The ID that represents the tag in binary format.

@override
def as_python(self) -> int:
590    @override
591    def as_python(self) -> int:
592        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
609    @override
610    def pretty(self) -> str:
611        return f"Short({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagInt(nbtx.Tag[int, int]):
614@dataclass(frozen=True)
615class TagInt(Tag[int, int]):
616    """
617    NBT tag for an integer (signed 32-bit integer).
618    """
619
620    # docstr-coverage:inherited
621    @override
622    @staticmethod
623    def id() -> int:
624        return 0x03
625
626    # docstr-coverage:inherited
627    @override
628    def as_python(self) -> int:
629        return self.value
630
631    @override
632    @classmethod
633    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
634        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
635        (buffer, name) = _read_string(endianness, buffer)
636        (buffer, value) = _read_int(endianness, buffer)
637        return (cls(name, value), buffer)
638
639    @override
640    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
641        _write_id(endianness, self.id(), stream)
642        _write_string(endianness, self.name, stream)
643        _write_int(endianness, self.value, stream)
644
645    # docstr-coverage:inherited
646    @override
647    def pretty(self) -> str:
648        return f"Int({self.name!r}): {self.value}"

NBT tag for an integer (signed 32-bit integer).

TagInt(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
621    @override
622    @staticmethod
623    def id() -> int:
624        return 0x03

The ID that represents the tag in binary format.

@override
def as_python(self) -> int:
627    @override
628    def as_python(self) -> int:
629        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
646    @override
647    def pretty(self) -> str:
648        return f"Int({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagLong(nbtx.Tag[int, int]):
651@dataclass(frozen=True)
652class TagLong(Tag[int, int]):
653    """
654    NBT tag for a long integer (signed 64-bit integer).
655    """
656
657    # docstr-coverage:inherited
658    @override
659    @staticmethod
660    def id() -> int:
661        return 0x04
662
663    # docstr-coverage:inherited
664    @override
665    def as_python(self) -> int:
666        return self.value
667
668    @override
669    @classmethod
670    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
671        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
672        (buffer, name) = _read_string(endianness, buffer)
673        (buffer, value) = _read_long(endianness, buffer)
674        return (cls(name, value), buffer)
675
676    @override
677    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
678        _write_id(endianness, self.id(), stream)
679        _write_string(endianness, self.name, stream)
680        _write_long(endianness, self.value, stream)
681
682    # docstr-coverage:inherited
683    @override
684    def pretty(self) -> str:
685        return f"Long({self.name!r}): {self.value}"

NBT tag for a long integer (signed 64-bit integer).

TagLong(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
658    @override
659    @staticmethod
660    def id() -> int:
661        return 0x04

The ID that represents the tag in binary format.

@override
def as_python(self) -> int:
664    @override
665    def as_python(self) -> int:
666        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
683    @override
684    def pretty(self) -> str:
685        return f"Long({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagFloat(nbtx.Tag[float, float]):
688@dataclass(frozen=True)
689class TagFloat(Tag[float, float]):
690    """
691    NBT tag for an integer (32-bit floating point).
692    """
693
694    # docstr-coverage:inherited
695    @override
696    @staticmethod
697    def id() -> int:
698        return 0x05
699
700    # docstr-coverage:inherited
701    @override
702    def as_python(self) -> float:
703        return self.value
704
705    @override
706    @classmethod
707    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
708        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
709        (buffer, name) = _read_string(endianness, buffer)
710        (buffer, value) = _read_float(endianness, buffer)
711        return (cls(name, value), buffer)
712
713    @override
714    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
715        _write_id(endianness, self.id(), stream)
716        _write_string(endianness, self.name, stream)
717        _write_float(endianness, self.value, stream)
718
719    # docstr-coverage:inherited
720    @override
721    def pretty(self) -> str:
722        return f"Float({self.name!r}): {self.value}"

NBT tag for an integer (32-bit floating point).

TagFloat(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
695    @override
696    @staticmethod
697    def id() -> int:
698        return 0x05

The ID that represents the tag in binary format.

@override
def as_python(self) -> float:
701    @override
702    def as_python(self) -> float:
703        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
720    @override
721    def pretty(self) -> str:
722        return f"Float({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagDouble(nbtx.Tag[float, float]):
725@dataclass(frozen=True)
726class TagDouble(Tag[float, float]):
727    """
728    NBT tag for an integer (64-bit floating point).
729    """
730
731    # docstr-coverage:inherited
732    @override
733    @staticmethod
734    def id() -> int:
735        return 0x06
736
737    # docstr-coverage:inherited
738    @override
739    def as_python(self) -> float:
740        return self.value
741
742    @override
743    @classmethod
744    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
745        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
746        (buffer, name) = _read_string(endianness, buffer)
747        (buffer, value) = _read_double(endianness, buffer)
748        return (cls(name, value), buffer)
749
750    @override
751    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
752        _write_id(endianness, self.id(), stream)
753        _write_string(endianness, self.name, stream)
754        _write_double(endianness, self.value, stream)
755
756    # docstr-coverage:inherited
757    @override
758    def pretty(self) -> str:
759        return f"Double({self.name!r}): {self.value}"

NBT tag for an integer (64-bit floating point).

TagDouble(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
732    @override
733    @staticmethod
734    def id() -> int:
735        return 0x06

The ID that represents the tag in binary format.

@override
def as_python(self) -> float:
738    @override
739    def as_python(self) -> float:
740        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
757    @override
758    def pretty(self) -> str:
759        return f"Double({self.name!r}): {self.value}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagString(nbtx.Tag[str, str]):
762@dataclass(frozen=True)
763class TagString(Tag[str, str]):
764    """
765    NBT tag for a UTF-8 encoded string.
766    """
767
768    def __post_init__(self) -> None:
769        if len(self.value) > STRING_LENGTH_LIMIT:
770            raise StringTooLongException(len(self.value))
771
772    # docstr-coverage:inherited
773    @override
774    @staticmethod
775    def id() -> int:
776        return 0x08
777
778    # docstr-coverage:inherited
779    @override
780    def as_python(self) -> str:
781        return self.value
782
783    @override
784    @classmethod
785    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
786        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
787        (buffer, name) = _read_string(endianness, buffer)
788        (buffer, string) = _read_string(endianness, buffer)
789        return (cls(name, string), buffer)
790
791    @override
792    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
793        _write_id(endianness, self.id(), stream)
794        _write_string(endianness, self.name, stream)
795        _write_string(endianness, self.value, stream)
796
797    # docstr-coverage:inherited
798    @override
799    def pretty(self) -> str:
800        return f"String({self.name!r}): {self.value!r}"

NBT tag for a UTF-8 encoded string.

TagString(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
773    @override
774    @staticmethod
775    def id() -> int:
776        return 0x08

The ID that represents the tag in binary format.

@override
def as_python(self) -> str:
779    @override
780    def as_python(self) -> str:
781        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
798    @override
799    def pretty(self) -> str:
800        return f"String({self.name!r}): {self.value!r}"

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagList(nbtx.Tag[collections.abc.Sequence[nbtx.Tag[T, P]], list[P]], collections.abc.Sequence[nbtx.Tag[T, P]], typing.Generic[T, P]):
803@dataclass(frozen=True)
804class TagList[T, P](Tag[Sequence[Tag[T, P]], list[P]], Sequence[Tag[T, P]]):
805    """
806    NBT tag for a list containing nameless tags of one kind.
807    """
808
809    child_id: int = field(kw_only=True)
810
811    def __post_init__(self) -> None:
812        for child in self.value:
813            if child.id() != self.child_id:
814                raise TypeConflictException(
815                    id_actual=child.id(), id_expected=self.child_id
816                )
817            if child.name != "":
818                raise UnexpectedNameException()
819
820    # docstr-coverage:inherited
821    @override
822    @staticmethod
823    def id() -> int:
824        return 0x09
825
826    # docstr-coverage:inherited
827    @override
828    def as_python(self) -> list[P]:
829        return [tag.as_python() for tag in self.value]
830
831    @override
832    @classmethod
833    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
834        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
835        (buffer, name) = _read_string(endianness, buffer)
836        (buffer, children, child_id) = _read_list(endianness, buffer)
837        return (cls(name, children, child_id=child_id), buffer)
838
839    @override
840    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
841        _write_id(endianness, self.id(), stream)
842        _write_string(endianness, self.name, stream)
843        _write_list(endianness, self.value, self.child_id, stream)
844
845    # docstr-coverage:inherited
846    @override
847    def pretty(self) -> str:
848        string = ""
849        string += f"TagList({self.name!r}): {len(self.value)} entries\n"
850        string += "{\n"
851        for child in self.value:
852            for line in child.pretty().splitlines():
853                string += f"{PRETTY_INDENTATION}{line}\n"
854        string += "}"
855        return string
856
857    @overload
858    def __getitem__(self, index: int) -> Tag[T, P]: ...
859
860    @overload
861    def __getitem__(self, index: slice[int, int, int]) -> Sequence[Tag[T, P]]: ...
862
863    @override
864    def __getitem__(
865        self, index: int | slice[int, int, int]
866    ) -> Tag[T, P] | Sequence[Tag[T, P]]:
867        return self.value[index]
868
869    @override
870    def __len__(self) -> int:
871        return len(self.value)

NBT tag for a list containing nameless tags of one kind.

TagList(name: str, value: 'T', *, child_id: int)
child_id: int
@override
@staticmethod
def id() -> int:
821    @override
822    @staticmethod
823    def id() -> int:
824        return 0x09

The ID that represents the tag in binary format.

@override
def as_python(self) -> 'list[P]':
827    @override
828    def as_python(self) -> list[P]:
829        return [tag.as_python() for tag in self.value]

Returns a python representation of this tag.

@override
def pretty(self) -> str:
846    @override
847    def pretty(self) -> str:
848        string = ""
849        string += f"TagList({self.name!r}): {len(self.value)} entries\n"
850        string += "{\n"
851        for child in self.value:
852            for line in child.pretty().splitlines():
853                string += f"{PRETTY_INDENTATION}{line}\n"
854        string += "}"
855        return string

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagByteList(nbtx.Tag[collections.abc.Sequence[int], collections.abc.Sequence[int]], collections.abc.Sequence[int]):
874@dataclass(frozen=True)
875class TagByteList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
876    """
877    NBT tag for a list of bytes.
878    """
879
880    # docstr-coverage:inherited
881    @override
882    @staticmethod
883    def id() -> int:
884        return 0x07
885
886    # docstr-coverage:inherited
887    @override
888    def as_python(self) -> Sequence[int]:
889        return self.value
890
891    @override
892    @classmethod
893    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
894        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
895        (buffer, name) = _read_string(endianness, buffer)
896        (buffer, children) = _read_byte_list(endianness, buffer)
897        return (cls(name, children), buffer)
898
899    @override
900    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
901        _write_id(endianness, self.id(), stream)
902        _write_string(endianness, self.name, stream)
903        _write_byte_list(endianness, self.value, stream)
904
905    # docstr-coverage:inherited
906    @override
907    def pretty(self) -> str:
908        string = ""
909        string += f"ByteList({self.name!r}) : {len(self.value)} entries\n"
910        string += "{\n"
911        for long in self.value:
912            string += f"{PRETTY_INDENTATION}{long}\n"
913        string += "}"
914        return string
915
916    @overload
917    def __getitem__(self, index: int) -> int: ...
918
919    @overload
920    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
921
922    @override
923    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
924        return self.value[index]
925
926    @override
927    def __len__(self) -> int:
928        return len(self.value)

NBT tag for a list of bytes.

TagByteList(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
881    @override
882    @staticmethod
883    def id() -> int:
884        return 0x07

The ID that represents the tag in binary format.

@override
def as_python(self) -> Sequence[int]:
887    @override
888    def as_python(self) -> Sequence[int]:
889        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
906    @override
907    def pretty(self) -> str:
908        string = ""
909        string += f"ByteList({self.name!r}) : {len(self.value)} entries\n"
910        string += "{\n"
911        for long in self.value:
912            string += f"{PRETTY_INDENTATION}{long}\n"
913        string += "}"
914        return string

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagIntList(nbtx.Tag[collections.abc.Sequence[int], collections.abc.Sequence[int]], collections.abc.Sequence[int]):
931@dataclass(frozen=True)
932class TagIntList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
933    """
934    NBT tag for a list of integers.
935    """
936
937    # docstr-coverage:inherited
938    @override
939    @staticmethod
940    def id() -> int:
941        return 0x0B
942
943    # docstr-coverage:inherited
944    @override
945    def as_python(self) -> Sequence[int]:
946        return self.value
947
948    @override
949    @classmethod
950    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
951        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
952        (buffer, name) = _read_string(endianness, buffer)
953        (buffer, children) = _read_int_list(endianness, buffer)
954        return (cls(name, children), buffer)
955
956    @override
957    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
958        _write_id(endianness, self.id(), stream)
959        _write_string(endianness, self.name, stream)
960        _write_int_list(endianness, self.value, stream)
961
962    # docstr-coverage:inherited
963    @override
964    def pretty(self) -> str:
965        string = ""
966        string += f"IntList({self.name!r}) : {len(self.value)} entries\n"
967        string += "{\n"
968        for integer in self.value:
969            string += f"{PRETTY_INDENTATION}{integer}\n"
970        string += "}"
971        return string
972
973    @overload
974    def __getitem__(self, index: int) -> int: ...
975
976    @overload
977    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
978
979    @override
980    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
981        return self.value[index]
982
983    @override
984    def __len__(self) -> int:
985        return len(self.value)

NBT tag for a list of integers.

TagIntList(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
938    @override
939    @staticmethod
940    def id() -> int:
941        return 0x0B

The ID that represents the tag in binary format.

@override
def as_python(self) -> Sequence[int]:
944    @override
945    def as_python(self) -> Sequence[int]:
946        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
963    @override
964    def pretty(self) -> str:
965        string = ""
966        string += f"IntList({self.name!r}) : {len(self.value)} entries\n"
967        string += "{\n"
968        for integer in self.value:
969            string += f"{PRETTY_INDENTATION}{integer}\n"
970        string += "}"
971        return string

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagLongList(nbtx.Tag[collections.abc.Sequence[int], collections.abc.Sequence[int]], collections.abc.Sequence[int]):
 988@dataclass(frozen=True)
 989class TagLongList(Tag[Sequence[int], Sequence[int]], Sequence[int]):
 990    """
 991    NBT tag for a list of long integers.
 992    """
 993
 994    # docstr-coverage:inherited
 995    @override
 996    @staticmethod
 997    def id() -> int:
 998        return 0x0C
 999
1000    # docstr-coverage:inherited
1001    @override
1002    def as_python(self) -> Sequence[int]:
1003        return self.value
1004
1005    @override
1006    @classmethod
1007    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
1008        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
1009        (buffer, name) = _read_string(endianness, buffer)
1010        (buffer, children) = _read_long_list(endianness, buffer)
1011        return (cls(name, children), buffer)
1012
1013    @override
1014    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
1015        _write_id(endianness, self.id(), stream)
1016        _write_string(endianness, self.name, stream)
1017        _write_long_list(endianness, self.value, stream)
1018
1019    # docstr-coverage:inherited
1020    @override
1021    def pretty(self) -> str:
1022        string = ""
1023        string += f"LongList({self.name!r}) : {len(self.value)} entries\n"
1024        string += "{\n"
1025        for byte in self.value:
1026            string += f"{PRETTY_INDENTATION}{byte}\n"
1027        string += "}"
1028        return string
1029
1030    @overload
1031    def __getitem__(self, index: int) -> int: ...
1032
1033    @overload
1034    def __getitem__(self, index: slice[int, int, int]) -> Sequence[int]: ...
1035
1036    @override
1037    def __getitem__(self, index: int | slice[int, int, int]) -> int | Sequence[int]:
1038        return self.value[index]
1039
1040    @override
1041    def __len__(self) -> int:
1042        return len(self.value)

NBT tag for a list of long integers.

TagLongList(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
995    @override
996    @staticmethod
997    def id() -> int:
998        return 0x0C

The ID that represents the tag in binary format.

@override
def as_python(self) -> Sequence[int]:
1001    @override
1002    def as_python(self) -> Sequence[int]:
1003        return self.value

Returns a python representation of this tag.

@override
def pretty(self) -> str:
1020    @override
1021    def pretty(self) -> str:
1022        string = ""
1023        string += f"LongList({self.name!r}) : {len(self.value)} entries\n"
1024        string += "{\n"
1025        for byte in self.value:
1026            string += f"{PRETTY_INDENTATION}{byte}\n"
1027        string += "}"
1028        return string

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
@dataclass(frozen=True)
class TagCompound(nbtx.Tag[collections.abc.Sequence[nbtx.Tag[T, P]], dict[str, P]], collections.abc.Mapping[str, nbtx.Tag[T, P]], typing.Generic[T, P]):
1045@dataclass(frozen=True)
1046class TagCompound[T, P](
1047    Tag[Sequence[Tag[T, P]], dict[str, P]], Mapping[str, Tag[T, P]]
1048):
1049    """
1050    NBT tag for a compound (list of uniquely named tags).
1051    """
1052
1053    def __post_init__(self) -> None:
1054        for child in self.value:
1055            if child.name == "":
1056                raise ExpectedNameException()
1057
1058    # docstr-coverage:inherited
1059    @override
1060    @staticmethod
1061    def id() -> int:
1062        return 0x0A
1063
1064    # docstr-coverage:inherited
1065    @override
1066    def as_python(self) -> dict[str, P]:
1067        result = {}
1068        for tag in self.value:
1069            result[tag.name] = tag.as_python()
1070        return result
1071
1072    @override
1073    @classmethod
1074    def _read(cls, buffer: bytes, *, endianness: Endianness) -> tuple[Self, bytes]:
1075        (buffer, _) = _read_id(endianness, buffer, compare=cls.id())
1076        (buffer, name) = _read_string(endianness, buffer)
1077        (buffer, compound) = _read_compound(endianness, buffer)
1078        return (cls(name, compound), buffer)
1079
1080    @override
1081    def _write(self, stream: IO[bytes], *, endianness: Endianness) -> None:
1082        _write_id(endianness, self.id(), stream)
1083        _write_string(endianness, self.name, stream)
1084        _write_compound(endianness, self.value, stream)
1085
1086    # docstr-coverage:inherited
1087    @override
1088    def pretty(self) -> str:
1089        string = ""
1090        string += f"Compound({self.name!r}): {len(self.value)} entries\n"
1091        string += "{\n"
1092        for child in self.value:
1093            for line in child.pretty().splitlines():
1094                string += f"{PRETTY_INDENTATION}{line}\n"
1095        string += "}"
1096        return string
1097
1098    @override
1099    def __getitem__(self, key: str) -> Tag[T, P]:
1100        for tag in self.value:
1101            if tag.name == key:
1102                return tag
1103        raise KeyError(f"{key!r}")
1104
1105    @override
1106    def __iter__(self) -> Iterator[str]:
1107        return (tag.name for tag in self.value)
1108
1109    @override
1110    def __len__(self) -> int:
1111        return len(self.value)

NBT tag for a compound (list of uniquely named tags).

TagCompound(name: str, value: 'T')
@override
@staticmethod
def id() -> int:
1059    @override
1060    @staticmethod
1061    def id() -> int:
1062        return 0x0A

The ID that represents the tag in binary format.

@override
def as_python(self) -> 'dict[str, P]':
1065    @override
1066    def as_python(self) -> dict[str, P]:
1067        result = {}
1068        for tag in self.value:
1069            result[tag.name] = tag.as_python()
1070        return result

Returns a python representation of this tag.

@override
def pretty(self) -> str:
1087    @override
1088    def pretty(self) -> str:
1089        string = ""
1090        string += f"Compound({self.name!r}): {len(self.value)} entries\n"
1091        string += "{\n"
1092        for child in self.value:
1093            for line in child.pretty().splitlines():
1094                string += f"{PRETTY_INDENTATION}{line}\n"
1095        string += "}"
1096        return string

Returns a human readable pretty representation of the tag structure.

Inherited Members
Tag
name
value
def load( file: IO[bytes], *, endianness: Endianness = 'little', ignore_rest: bool = False) -> Tag[typing.Any, typing.Any]:
1114def load(
1115    file: IO[bytes],
1116    *,
1117    endianness: Endianness = "little",
1118    ignore_rest: bool = False,
1119) -> Tag[Any, Any]:
1120    """
1121    Loads an NBT file and returns the contained NBT tag.
1122
1123    # Parameters
1124
1125    - `file` -- The file-like object to read the data from.
1126    - `endianness` -- The byte order to use during parsing. Java Edition usually
1127      uses big endian byte order and Bedrock Edition usually uses little endian
1128      byte order.
1129    - `ignore_rest` -- Whether to ignore remaining bytes after a full parse. By
1130      default, this function expects the buffer to be empty after an NBT tree
1131      has been serialized and raises an exception otherwise.
1132    """
1133    buffer = file.read()
1134    (_, id) = _read_id(endianness, buffer)
1135    tag_cls = _tag_class_by_id(id)
1136    (tag, rest) = tag_cls._read(buffer, endianness=endianness)
1137    if rest and not ignore_rest:
1138        raise UnemptyBufferException()
1139    return tag

Loads an NBT file and returns the contained NBT tag.

Parameters

  • file -- The file-like object to read the data from.
  • endianness -- The byte order to use during parsing. Java Edition usually uses big endian byte order and Bedrock Edition usually uses little endian byte order.
  • ignore_rest -- Whether to ignore remaining bytes after a full parse. By default, this function expects the buffer to be empty after an NBT tree has been serialized and raises an exception otherwise.
def dump( tag: Tag[typing.Any, typing.Any], stream: IO[bytes], *, endianness: Endianness = 'little') -> None:
1142def dump(
1143    tag: Tag[Any, Any], stream: IO[bytes], *, endianness: Endianness = "little"
1144) -> None:
1145    """
1146    Dumps an NBT tag to a file.
1147
1148    # Parameters
1149
1150    - `tag` -- The root tag to write to the stream.
1151    - `stream` -- The stream to write to.
1152    - `endianness` -- The byte order to use. Java Edition usually uses big
1153      endian byte order and Bedrock Edition usually uses little endian byte
1154      order.
1155    """
1156    tag._write(stream, endianness=endianness)

Dumps an NBT tag to a file.

Parameters

  • tag -- The root tag to write to the stream.
  • stream -- The stream to write to.
  • endianness -- The byte order to use. Java Edition usually uses big endian byte order and Bedrock Edition usually uses little endian byte order.