### Installing Targets Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Installs the main shared library and the Python extension to their respective directories. ```cmake # Install install(TARGETS amulet_nbt DESTINATION ${amulet_nbt_DIR}) install(TARGETS _amulet_nbt DESTINATION ${AMULET_NBT_EXT_DIR}) ``` -------------------------------- ### Install Amulet-NBT for Development Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/README.md Install the library in-place for development purposes from the root directory. This command also installs development dependencies. ```bash pip install -e .[dev] ``` -------------------------------- ### Install Amulet-NBT from PyPi Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/README.md Use this command to install the library from the Python Package Index. ```bash pip install amulet-nbt~=4.0 ``` -------------------------------- ### Basic CMake Project Setup Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Initializes CMake version, sets project name, and defines the project language. ```cmake cmake_minimum_required(VERSION 4.1) project(amulet_nbt LANGUAGES CXX) ``` -------------------------------- ### Complete NamedTag Example: Create, Serialize, and Deserialize Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/named-tag.md This example demonstrates creating a complex CompoundTag, wrapping it in a NamedTag, serializing it to NBT binary and SNBT formats, saving to a file, and then loading it back. ```python import amulet_nbt # Create a compound tag with player data player_data = amulet_nbt.CompoundTag({ "Name": amulet_nbt.StringTag("Steve"), "Health": amulet_nbt.ByteTag(20), "Pos": amulet_nbt.ListTag([ amulet_nbt.DoubleTag(100.5), amulet_nbt.DoubleTag(64.0), amulet_nbt.DoubleTag(200.5) ], element_tag_id=6), "Rotation": amulet_nbt.ListTag([ amulet_nbt.FloatTag(45.0), amulet_nbt.FloatTag(0.0) ], element_tag_id=5) }) # Wrap in NamedTag named = amulet_nbt.NamedTag(player_data, "Player") # Serialize to binary binary_data = named.to_nbt(preset=amulet_nbt.java_encoding) # Save to file named.save_to("player.nbt", preset=amulet_nbt.java_encoding) # Serialize to SNBT (JSON-like) snbt = named.to_snbt(indent=2) print(snbt) # Read back loaded = amulet_nbt.read_nbt("player.nbt") print(loaded.name) # "Player" print(loaded.tag["Name"].py_str) # "Steve" ``` -------------------------------- ### Install Test Module Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/tests/CMakeLists.txt Installs the compiled test module '_test_amulet_nbt' to a specific destination within the build directory, making it available for execution. ```cmake install(TARGETS _test_amulet_nbt DESTINATION "${CMAKE_CURRENT_LIST_DIR}/test_amulet_nbt") ``` -------------------------------- ### Setting Extension Directory Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Sets the installation directory for the Python extension if not already defined. ```cmake if(NOT DEFINED AMULET_NBT_EXT_DIR) set(AMULET_NBT_EXT_DIR ${amulet_nbt_DIR}) endif() ``` -------------------------------- ### Example: Get Length of Any Array Tag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Demonstrates using the ArrayType alias to accept any array tag and return its length. ```python def array_length(tag: amulet_nbt.ArrayType) -> int: """Get length of any array tag.""" return len(tag) ``` -------------------------------- ### Save NBT Data to File Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Shows how to create NBT data, wrap it in a NamedTag, and then save it to a file in either Java or Bedrock format. Also demonstrates how to get the binary NBT data without saving to a file. ```python import amulet_nbt # Create data level_data = amulet_nbt.CompoundTag({ "seed": amulet_nbt.LongTag(123456789), "spawn": amulet_nbt.CompoundTag({ "x": amulet_nbt.IntTag(0), "y": amulet_nbt.IntTag(64), "z": amulet_nbt.IntTag(0) }) }) # Create NamedTag with name root = amulet_nbt.NamedTag(level_data, "root") # Save in Java format root.save_to("output.nbt", preset=amulet_nbt.java_encoding) # Save in Bedrock format root.save_to("output.nbt", preset=amulet_nbt.bedrock_encoding) # Get binary data without saving binary_data = root.to_nbt(preset=amulet_nbt.java_encoding) print(f"NBT size: {len(binary_data)} bytes") ``` -------------------------------- ### Manually Constructing NBT Tags Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/docs_source/getting_started.rst Provides examples of manually constructing NBT tags, including CompoundTag, and various primitive and array tags. It also shows how to create a NamedTag from a constructed tag. ```python # The classes can also be constructed manually like this tag = CompoundTag({ "key1": ByteTag(0), # if no input value is given it will automatically fill these defaults "key2": ShortTag(0), "key3": IntTag(0), "key4": LongTag(0), "key5": FloatTag(0.0), "key6": DoubleTag(0.0), "key7": ByteArrayTag([]), "key8": StringTag(""), "key9": ListTag([]), "key10": CompoundTag({}), "key11": IntArrayTag([]), "key12": LongArrayTag([]) }) ``` ```python named_tag = NamedTag( tag, name="" # Optional name input. ) ``` -------------------------------- ### Example: Process Any NBT Tag Type Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Demonstrates using the AnyNBT alias to accept any NBT tag and process it based on its specific type, printing relevant information. ```python def process_tag(tag: amulet_nbt.AnyNBT) -> None: """Process any NBT tag type.""" if isinstance(tag, amulet_nbt.NumberType): print(f"Number: {float(tag)}") elif isinstance(tag, amulet_nbt.StringTag): print(f"String: {tag.py_str}") elif isinstance(tag, amulet_nbt.ListTag): print(f"List of {tag.element_class.__name__}") elif isinstance(tag, amulet_nbt.CompoundTag): print(f"Compound with {len(tag)} keys") ``` -------------------------------- ### Example: Get Magnitude of Any Numeric Tag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Illustrates using the NumberType alias to accept any numeric tag and return its absolute value as a float. ```python def magnitude(tag: amulet_nbt.NumberType) -> float: """Get absolute value of any numeric tag.""" return abs(float(tag)) ``` -------------------------------- ### Example: Double Integer Tag Value Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Demonstrates using the IntType alias to accept any integer tag, double its value, and return it as an IntTag. ```python import amulet_nbt from typing import Union def double_value(tag: amulet_nbt.IntType) -> amulet_nbt.IntTag: """Double an integer tag value and return as IntTag.""" return amulet_nbt.IntTag(int(tag) * 2) ``` -------------------------------- ### Example: Convert Float Tag to DoubleTag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Shows how to use the FloatType alias to accept any float tag and convert it into a DoubleTag. ```python def as_double(tag: amulet_nbt.FloatType) -> amulet_nbt.DoubleTag: """Convert any float tag to DoubleTag.""" return amulet_nbt.DoubleTag(float(tag)) ``` -------------------------------- ### Example: Serialize Tag to SNBT Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/types.md Shows how to use the SNBTType alias for the return type of a function that serializes an NBT tag to its SNBT string format. ```python def serialize_tag(tag: amulet_nbt.AbstractBaseTag) -> amulet_nbt.SNBTType: """Serialize tag to SNBT format.""" return tag.to_snbt() ``` -------------------------------- ### Access CompoundTag Data with Default Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Safely retrieve data from a CompoundTag using the get method, providing a default value if the key is not found. This example checks for a byte tag representing health. ```python compound = amulet_nbt.CompoundTag(...) health = compound.get_byte("health", default=20) if health > 0: print("Alive") ``` -------------------------------- ### Safe Dictionary Access with get() Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/errors.md Use the get() method for safe dictionary-like access to CompoundTags, providing a default value if the key is not found. ```python import amulet_nbt compound = amulet_nbt.CompoundTag() # Pattern 1: Use get() with default name = compound.get("name", amulet_nbt.StringTag("Unknown")) ``` -------------------------------- ### Create and Save Nested NBT Structure Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Construct a nested NBT structure, including CompoundTags, ListTags, and basic data types. The example creates player data and saves it to a file. ```python player = amulet_nbt.CompoundTag({ "name": amulet_nbt.StringTag("Steve"), "pos": amulet_nbt.ListTag([ amulet_nbt.DoubleTag(100.5), amulet_nbt.DoubleTag(64.0), amulet_nbt.DoubleTag(200.5) ], element_tag_id=6), "inventory": amulet_nbt.ListTag([], element_tag_id=10) }) root = amulet_nbt.NamedTag(player, "Player") root.save_to("player.nbt") ``` -------------------------------- ### Choose NBT Serialization Format Wisely Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Demonstrates selecting appropriate NBT serialization formats based on use case: binary for storage/network and pretty SNBT for debugging. ```python import amulet_nbt tag = amulet_nbt.CompoundTag(...) # For storage: use binary with appropriate encoding tag.save_to("data.nbt", preset=amulet_nbt.java_encoding) # For debugging: use pretty SNBT snbt = tag.to_snbt(indent=2) with open("data.snbt", "w") as f: f.write(snbt) # For network: use binary (more compact) binary = tag.to_nbt(preset=amulet_nbt.java_encoding) send_over_network(binary) ``` -------------------------------- ### Create and Use ByteTag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/tag-classes.md Demonstrates how to create a ByteTag, access its integer value, and serialize it to binary NBT and SNBT formats. Requires the amulet_nbt library to be imported. ```python import amulet_nbt # Create a byte tag tag = amulet_nbt.ByteTag(42) print(tag.py_int) # 42 # Serialize to binary NBT binary_data = tag.to_nbt(name="test") # Serialize to SNBT snbt = tag.to_snbt() # "42b" ``` -------------------------------- ### Creating and Using a ListTag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/tag-classes.md Demonstrates how to create a ListTag containing IntTags, check its length, access elements by index, and append new elements. ```Python import amulet_nbt # Create list of integers tag = amulet_nbt.ListTag([ amulet_nbt.IntTag(1), amulet_nbt.IntTag(2), amulet_nbt.IntTag(3) ], element_tag_id=3) print(len(tag)) # 3 print(tag[0]) # IntTag(1) tag.append(amulet_nbt.IntTag(4)) ``` -------------------------------- ### Creating the Shared Library (amulet_nbt) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Defines the main shared library, sets visibility properties, links dependencies, and adds source files. ```cmake add_library(amulet_nbt SHARED) set_target_properties(amulet_nbt PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(amulet_nbt PROPERTIES FOLDER "CPP") target_compile_definitions(amulet_nbt PRIVATE ExportAmuletNBT) target_link_libraries(amulet_nbt PUBLIC amulet_io) target_include_directories(amulet_nbt PUBLIC ${SOURCE_PATH}) target_sources(amulet_nbt PRIVATE ${SOURCES} ${HEADERS}) foreach(FILE ${SOURCES} ${HEADERS}) file(RELATIVE_PATH REL_PATH ${SOURCE_PATH} ${FILE}) get_filename_component(GROUP ${REL_PATH} DIRECTORY) string(REPLACE "/" \\ GROUP ${GROUP}) source_group(${GROUP} FILES ${FILE}) endforeach() ``` -------------------------------- ### Import amulet_nbt Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Import the amulet_nbt library to begin using its functionalities. ```python import amulet_nbt ``` -------------------------------- ### Project Configuration and Build Options Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Sets project directory variables and controls whether tests should be built. ```cmake set(amulet_nbt_DIR ${CMAKE_CURRENT_LIST_DIR}/src/amulet/nbt CACHE PATH "") set(BUILD_AMULET_NBT_TESTS OFF CACHE BOOL "Should tests be built?") ``` -------------------------------- ### NamedTag String Representation Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/named-tag.md Get the string representation of a NamedTag, showing its contained tag and name. ```python repr(named_tag) # NamedTag(CompoundTag({...}), 'level') ``` -------------------------------- ### Create and Inspect Basic NBT Tags Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Demonstrates how to create individual NBT tags like ByteTag, StringTag, and IntArrayTag, and how to access their Python representations. ```python import amulet_nbt # Create individual tags byte_tag = amulet_nbt.ByteTag(42) string_tag = amulet_nbt.StringTag("Hello") int_array = amulet_nbt.IntArrayTag([1, 2, 3, 4]) # Access data print(f"Byte value: {byte_tag.py_int}") print(f"String value: {string_tag.py_str}") print(f"Array length: {len(int_array)}") print(f"First element: {int_array[0]}") ``` -------------------------------- ### Get String Tag Value Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Retrieve the string or bytes representation from a string NBT tag. ```python tag = amulet_nbt.StringTag("Hello") text = tag.py_str # "Hello" (str) bytes_data = tag.py_bytes # b'Hello' (bytes) ``` -------------------------------- ### Creating and Using a CompoundTag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/tag-classes.md Shows how to initialize a CompoundTag with a dictionary of various NBT tags, access string values by key, and check the number of items. ```Python import amulet_nbt # Create compound tag tag = amulet_nbt.CompoundTag({ "name": amulet_nbt.StringTag("Steve"), "health": amulet_nbt.ByteTag(20), "pos": amulet_nbt.ListTag([ amulet_nbt.DoubleTag(0.5), amulet_nbt.DoubleTag(64.0), amulet_nbt.DoubleTag(0.5) ], element_tag_id=6) }) print(tag["name"].py_str) # "Steve" print(len(tag)) # 3 ``` -------------------------------- ### Get Numeric Tag Value Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Retrieve the Python primitive value from a numeric NBT tag. ```python tag = amulet_nbt.IntTag(42) value = tag.py_int # 42 (Python int) value = int(tag) # 42 (via __int__) value = float(tag) # 42.0 (via __float__) ``` -------------------------------- ### Importing Amulet NBT Library Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/docs_source/getting_started.rst Demonstrates two ways to import the Amulet NBT library: importing the entire package or importing specific attributes. It also lists all available tag types and encoding options. ```python # Import the nbt library # Option 1 - import the package then refer to attributes on the package import amulet.nbt # Option 2 - import attributes from the package from amulet.nbt import ( ByteTag, ShortTag, IntTag, LongTag, FloatTag, DoubleTag, StringTag, ListTag, CompoundTag, ByteArrayTag, IntArrayTag, LongArrayTag, NamedTag, mutf8_encoding, utf8_encoding, utf8_escape_encoding, java_encoding, bedrock_encoding, ) ``` -------------------------------- ### Get Binary NBT without Saving Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/named-tag.md Serialize the NamedTag to binary NBT format and retrieve the bytes without writing to a file. ```python import amulet_nbt named_tag = amulet_nbt.NamedTag(amulet_nbt.CompoundTag(), "level") # Get binary without saving to file binary = named_tag.save_to() ``` -------------------------------- ### Type Hinting for Numeric and Container Tags Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/INDEX.md Demonstrates how to use type hints for numeric and container tags, improving code clarity and enabling static analysis. ```python import amulet_nbt from typing import Union def process_numeric(tag: amulet_nbt.NumberType) -> float: """Process any numeric tag.""" return float(tag) def process_container(tag: Union[amulet_nbt.ListTag, amulet_nbt.CompoundTag]) -> int: """Process container tags.""" return len(tag) ``` -------------------------------- ### Get Array Tag Data Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Access data from byte, int, or long array NBT tags, including NumPy conversion and individual element access. ```python tag = amulet_nbt.ByteArrayTag([1, 2, 3]) array = tag.np_array # NumPy array length = len(tag) # 3 item = tag[0] # 1 ``` -------------------------------- ### Custom NBT Encoding Configuration Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/INDEX.md Shows how to create a custom NBT encoding by explicitly specifying parameters like compression, endianness, and string encoding. ```python tag = amulet_nbt.read_nbt( "file.nbt", compressed=False, little_endian=False, string_encoding=amulet_nbt.utf8_encoding ) ``` -------------------------------- ### NamedTag Type-Specific Properties Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/named-tag.md Access the contained tag with type-specific properties. Raises TypeError if the tag is not of the expected type. Examples include accessing as ByteTag, IntTag, CompoundTag, etc. ```APIDOC ## Type-Specific Properties NamedTag provides properties to access the contained tag if it matches a specific type. Raises `TypeError` if the tag is not of the expected type. - `byte` (read-only): Access as ByteTag - `short` (read-only): Access as ShortTag - `int` (read-only): Access as IntTag - `long` (read-only): Access as LongTag - `float` (read-only): Access as FloatTag - `double` (read-only): Access as DoubleTag - `byte_array` (read-only): Access as ByteArrayTag - `string` (read-only): Access as StringTag - `list` (read-only): Access as ListTag - `compound` (read-only): Access as CompoundTag - `int_array` (read-only): Access as IntArrayTag - `long_array` (read-only): Access as LongArrayTag **Example:** ```python import amulet_nbt named_tag = amulet_nbt.NamedTag(amulet_nbt.IntTag(42), "test") print(named_tag.int.py_int) # 42 named_tag.tag = amulet_nbt.CompoundTag() compound = named_tag.compound # Access as CompoundTag ``` ``` -------------------------------- ### Create and Use FloatTag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/tag-classes.md Shows how to instantiate a FloatTag with a floating-point value and retrieve its Python float representation. The amulet_nbt library must be imported. ```python import amulet_nbt tag = amulet_nbt.FloatTag(3.14) print(tag.py_float) # 3.14 ``` -------------------------------- ### Load NBT from Bedrock Edition File Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Illustrates how to load NBT data from Bedrock Edition files, which are usually uncompressed, little-endian, and use UTF-8 escape encoding for strings. Shows both preset and explicit parameter usage. ```python import amulet_nbt # Read Bedrock level (uncompressed, little-endian, UTF-8 escape) level_tag = amulet_nbt.read_nbt("level.dat", preset=amulet_nbt.bedrock_encoding) # Or with explicit parameters level_tag = amulet_nbt.read_nbt( "level.dat", compressed=False, little_endian=True, string_encoding=amulet_nbt.utf8_escape_encoding ) ``` -------------------------------- ### Process Multiple Region Files Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Iterates through all '.mca' region files in a specified world directory to perform batch processing. This example shows basic file discovery and size reporting. ```python import amulet_nbt from pathlib import Path def process_all_regions(world_dir): """Process all region files in a world directory.""" world_path = Path(world_dir) region_dir = world_path / "region" if not region_dir.exists(): print(f"Region directory not found: {region_dir}") return # Find all .mca files mca_files = list(region_dir.glob("*.mca")) print(f"Found {len(mca_files)} region files") # Process each file for mca_file in mca_files: print(f"\nProcessing {mca_file.name}...") try: # You would normally parse binary MCR format here # This is a simplified example print(f" Size: {mca_file.stat().st_size} bytes") except Exception as e: print(f" Error: {e}") ``` -------------------------------- ### Set C++ Standard and Options Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/tests/CMakeLists.txt Configures the C++ standard to C++20, ensuring it's required and disabling extensions. ```cmake set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) ``` -------------------------------- ### Filter and Transform ListTag Elements Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Filter elements within a ListTag based on a condition and perform a mapping operation to create a new ListTag. Examples show filtering for values greater than 50 and doubling values. ```python list_tag = amulet_nbt.ListTag([...], element_tag_id=3) # Get all values > 50 filtered = [tag for tag in list_tag if int(tag) > 50] # Map operation doubled = amulet_nbt.ListTag( [amulet_nbt.IntTag(int(tag) * 2) for tag in list_tag], element_tag_id=3 ) ``` -------------------------------- ### Parsing and Saving Stringified NBT (SNBT) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/docs_source/getting_started.rst Demonstrates parsing SNBT strings into NBT tags and converting NBT tags back to SNBT format. It also shows how to save a tag to a file with a specified name. ```python # You can also parse the stringified NBT format used in Java commands. tag = amulet.nbt.read_snbt('{key1: "value", key2: 0b, key3: 0.0f}') # tag should look like this # TAG_Compound( # key1: TAG_String("value"), # key2: TAG_Byte(0) # key3: TAG_Float(0.0) # ) ``` ```python # Tags can be saved like the NamedTag class but they do not have a name. tag.save_to( 'filepath', # see the NamedTag save_to documentation above for other options. name="" # Tag classes do not store their name so you can define it here. ) ``` ```python tag.to_snbt() # convert back to SNBT ``` -------------------------------- ### Test NBT Loading and Access Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Demonstrates how to load NBT data from a file and access nested compound tags within a unittest framework. Ensure test data is available at the specified path. ```python import amulet_nbt import unittest from pathlib import Path class TestNBTProcessing(unittest.TestCase): @classmethod def setUpClass(cls): # Load test data once test_data = Path(__file__).parent / "test_data" cls.test_nbt = amulet_nbt.read_nbt(str(test_data / "sample.nbt")) def test_load_nbt(self): """Test NBT loading.""" self.assertIsNotNone(self.test_nbt) self.assertIsInstance(self.test_nbt.tag, amulet_nbt.CompoundTag) def test_access_nested_data(self): """Test accessing nested structures.""" compound = self.test_nbt.tag self.assertIn("Player", compound) ``` -------------------------------- ### Saving Binary NBT Data Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/docs_source/getting_started.rst Illustrates how to save NBT data to a file path or a file object. Options for compression, endianness, and encoding presets are available. ```python # Save the data back to a file named_tag.save_to( "the/path/to/write/to", compressed=True, # These inputs must be specified as keyword inputs like this. preset=java_encoding ) ``` ```python named_tag.save_to( "the/path/to/write/to", compressed=True, # These inputs must be specified as keyword inputs like this. little_endian=False, # If you do not define them they will default to these values string_encoding=mutf8_encoding ) ``` ```python # save_to can also be given a file object to write to. with open('filepath', 'wb') as f: named_tag.save_to(f) ``` ```python # Like earlier you will need to give the correct options for the platform you are using. # Java java_named_tag.save_to( "the/path/to/write/to", preset=java_encoding ) ``` ```python # Bedrock bedrock_named_tag.save_to( "the/path/to/write/to", compressed=False, preset=bedrock_encoding ) ``` -------------------------------- ### Load NBT from Java Edition File Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Demonstrates reading an NBT file saved in the Java Edition format, which is typically compressed and big-endian. It shows how to access the root tag and extract specific data like the world seed. ```python import amulet_nbt # Read level.dat (compressed, big-endian, Java format) level_tag = amulet_nbt.read_nbt("level.dat") print(f"Level name: {level_tag.name}") print(f"Tag type: {type(level_tag.tag).__name__}") # Access level data if isinstance(level_tag.tag, amulet_nbt.CompoundTag): compound = level_tag.tag if "Data" in compound: data = compound["Data"] if isinstance(data, amulet_nbt.CompoundTag): seed = data.get_long("RandomSeed", default=0) print(f"World seed: {seed}") ``` -------------------------------- ### Finding Required Packages Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Locates and includes necessary external libraries and tools like Python3, pybind11, amulet_pybind11_extensions, amulet_io, and amulet_zlib. ```cmake find_package(Python3 COMPONENTS Interpreter Development REQUIRED) # Find libraries if (NOT TARGET pybind11::module) find_package(pybind11 CONFIG REQUIRED) endif() if (NOT TARGET amulet_pybind11_extensions) find_package(amulet_pybind11_extensions CONFIG REQUIRED) endif() if (NOT TARGET amulet_io) find_package(amulet_io CONFIG REQUIRED) endif() if (NOT TARGET amulet_zlib) find_package(amulet_zlib CONFIG REQUIRED) endif() ``` -------------------------------- ### Platform-Specific Configurations Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/tests/CMakeLists.txt Sets platform-specific build options, such as the minimum Windows version, macOS deployment target, or enabling position-independent code on Unix-like systems. It also includes an error message for unsupported platforms. ```cmake if (WIN32) # set windows 7 as the minimum version add_definitions(-D_WIN32_WINNT=0x0601) elseif(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") elseif(UNIX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) else() message( FATAL_ERROR "Unsupported platform. Please submit a pull request to support this platform." ) endif() ``` -------------------------------- ### Add Comprehensive Logging to World Loading Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Implements detailed logging for the world loading process, including file size, success messages, and error handling for file not found or corrupted data. Configure logging to DEBUG level for verbose output. ```python import amulet_nbt import logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) def load_world(filepath: str) -> amulet_nbt.CompoundTag: """Load world data with detailed logging.""" logger.info(f"Loading world from {filepath}") try: start_size = os.path.getsize(filepath) logger.debug(f"File size: {start_size} bytes") tag = amulet_nbt.read_nbt(filepath) logger.info(f"Successfully loaded tag: {tag.name}") return tag.tag except FileNotFoundError: logger.error(f"File not found: {filepath}") raise except IndexError as e: logger.error(f"Corrupted NBT data: {e}") raise ``` -------------------------------- ### CMakeLists.txt Configuration Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/get_compiler/CMakeLists.txt Configures CMake to use C++20 and writes compiler information to files. Ensure the C++ standard is set appropriately for your project. ```cmake cmake_minimum_required(VERSION 4.1) project(get_compiler LANGUAGES CXX) # Set C++20 set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) write_file("${CMAKE_BINARY_DIR}/compiler_id.txt" "${CMAKE_CXX_COMPILER_ID}") write_file("${CMAKE_BINARY_DIR}/compiler_version.txt" "${CMAKE_CXX_COMPILER_VERSION}") ``` -------------------------------- ### Mock Complex NBT Structures Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Illustrates creating complex NBT structures using Amulet-NBT tags for testing purposes. This is useful for mocking data when real files are not available or for testing specific tag interactions. ```python import amulet_nbt import unittest from unittest.mock import Mock class TestPlayerData(unittest.TestCase): def setUp(self): # Create test data structures self.player = amulet_nbt.CompoundTag({ "Name": amulet_nbt.StringTag("TestPlayer"), "Health": amulet_nbt.ByteTag(20), "Pos": amulet_nbt.ListTag([ amulet_nbt.DoubleTag(0.0), amulet_nbt.DoubleTag(64.0), amulet_nbt.DoubleTag(0.0) ], element_tag_id=6) }) def test_health_property(self): """Test health access.""" health = self.player["Health"].py_int self.assertEqual(health, 20) ``` -------------------------------- ### Read Binary NBT from File or Buffer (Preset) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/encoding-functions.md Loads a single binary NBT object using a specified encoding preset. Use this for standard Java or Bedrock NBT data. ```python import amulet_nbt # Read from file named_tag = amulet_nbt.read_nbt("level.dat", preset=amulet_nbt.java_encoding) print(named_tag.name) # Tag name print(named_tag.tag) # The actual tag (usually CompoundTag) # Read from bytes data = b'\n\x00\x00...' # Binary NBT data named_tag = amulet_nbt.read_nbt(data, compressed=False) # Track read position offset = amulet_nbt.ReadOffset() named_tag = amulet_nbt.read_nbt(data, read_offset=offset) print(offset.offset) # Bytes read ``` -------------------------------- ### Serialize to Binary NBT (Preset) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/named-tag.md Serialize the NamedTag to binary NBT format using a predefined encoding preset like Java. ```python import amulet_nbt named_tag = amulet_nbt.NamedTag(amulet_nbt.IntTag(42), "test") # Serialize with Java preset binary = named_tag.to_nbt(preset=amulet_nbt.java_encoding) ``` -------------------------------- ### Reading Binary NBT Data Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/docs_source/getting_started.rst Shows how to read binary NBT data from a file path or a bytes object. It covers specifying encoding presets, compression, and endianness for Java and Bedrock editions. ```python named_tag = amulet.nbt.read_nbt( "the/path/to/your/binary/nbt/file", preset=java_encoding, compressed=True, # These inputs must be specified as keyword inputs like this. ) # from a file ``` ```python named_tag = amulet.nbt.read_nbt( "the/path/to/your/binary/nbt/file", compressed=True, # These inputs must be specified as keyword inputs like this. little_endian=False, # If you do not define them they will default to these values string_encoding=mutf8_encoding ) # from a file ``` ```python named_tag = amulet.nbt.read_nbt(b'') # from a bytes object ``` ```python # Note that Java Edition usually uses compressed modified UTF-8. java_named_tag = amulet.nbt.read_nbt( "the/path/to/your/binary/java/nbt/file", string_encoding=mutf8_encoding ) ``` ```python # Bedrock edition data is stored in little endian format and uses non-compressed UTF-8 but can also have arbitrary bytes. bedrock_named_tag = amulet.nbt.read_nbt( "the/path/to/your/binary/bedrock/nbt/file", preset=bedrock_encoding, compressed=False, ) ``` ```python bedrock_named_tag = amulet.nbt.read_nbt( "the/path/to/your/binary/bedrock/nbt/file", compressed=False, little_endian=True, string_encoding=utf8_escape_encoding # This decoder will escape all invalid bytes to the string ␛xHH ) ``` -------------------------------- ### Create String Tag Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Create a string NBT tag to store text data. ```python amulet_nbt.StringTag("Hello, World!") ``` -------------------------------- ### Complete NBT File Loading and Processing with Error Handling Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/errors.md This snippet demonstrates how to load and process an NBT file while handling various potential errors such as invalid input, file not found, file corruption, OS errors, and unexpected processing exceptions. It uses Python's logging module for error reporting. ```Python import amulet_nbt import logging from pathlib import Path logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def load_and_process(filepath: str) -> bool: """Load and process NBT file with comprehensive error handling.""" # Validate input if not isinstance(filepath, str): logger.error("Filepath must be string") return False path = Path(filepath) # Check existence if not path.exists(): logger.error(f"File not found: {filepath}") return False # Try to read try: named_tag = amulet_nbt.read_nbt(filepath, preset=amulet_nbt.java_encoding) logger.info(f"Loaded tag: {named_tag.name}") except IndexError as e: logger.error(f"File corrupted: {e}") return False except OSError as e: logger.error(f"Cannot read file: {e}") return False except Exception as e: logger.error(f"Unexpected error: {e}", exc_info=True) return False # Process tag try: tag = named_tag.tag if isinstance(tag, amulet_nbt.CompoundTag): if "data" in tag: logger.info(f"Found data key with {len(tag)} total keys") else: logger.warning("Missing expected 'data' key") except Exception as e: logger.error(f"Processing error: {e}") return False return True if __name__ == "__main__": success = load_and_process("level.dat") exit(0 if success else 1) ``` -------------------------------- ### Adding the Python Extension (_amulet_nbt) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Configures and builds the Python extension module using pybind11, linking necessary libraries and defining compilation flags. ```cmake pybind11_add_module(_amulet_nbt) set_target_properties(_amulet_nbt PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(_amulet_nbt PROPERTIES FOLDER "Python") target_link_libraries(_amulet_nbt PRIVATE amulet_pybind11_extensions) target_link_libraries(_amulet_nbt PRIVATE amulet_zlib) target_link_libraries(_amulet_nbt PRIVATE amulet_nbt) target_compile_definitions(_amulet_nbt PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) target_compile_definitions(_amulet_nbt PRIVATE PYBIND11_VERSION="${pybind11_VERSION}") target_compile_definitions(_amulet_nbt PRIVATE COMPILER_ID="${CMAKE_CXX_COMPILER_ID}") target_compile_definitions(_amulet_nbt PRIVATE COMPILER_VERSION="${CMAKE_CXX_COMPILER_VERSION}") target_sources(_amulet_nbt PRIVATE ${EXTENSION_SOURCES} ${EXTENSION_HEADERS}) foreach(FILE ${EXTENSION_SOURCES} ${EXTENSION_HEADERS}) file(RELATIVE_PATH REL_PATH ${SOURCE_PATH} ${FILE}) get_filename_component(GROUP ${REL_PATH} DIRECTORY) string(REPLACE "/" \\ GROUP ${GROUP}) source_group(${GROUP} FILES ${FILE}) endforeach() ``` -------------------------------- ### Be Specific with Exception Handling Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Handle specific exceptions rather than broad ones like `Exception`. This allows for more targeted error recovery and provides clearer feedback on the cause of issues. ```python import amulet_nbt # Bad - too broad try: tag = amulet_nbt.read_nbt("file.nbt") except Exception as e: print("Something went wrong") # Unhelpful # Good - specific handlers try: tag = amulet_nbt.read_nbt("file.nbt") except FileNotFoundError: print("File not found - please check path") except IndexError: print("File corrupted - cannot parse NBT data") except OSError as e: print(f"I/O error: {e}") except Exception as e: print(f"Unexpected error: {e}", exc_info=True) ``` -------------------------------- ### Read Multiple NBT Files Sequentially Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/examples.md Demonstrates how to read multiple NBT tags from the same binary data stream by tracking the read offset. This is useful when a file contains several concatenated NBT structures. ```python import amulet_nbt # Track reading position offset = amulet_nbt.ReadOffset() # Read file with tracking with open("chunk.nbt", "rb") as f: data = f.read() # Parse first tag tag1 = amulet_nbt.read_nbt(data, read_offset=offset) print(f"First tag: {tag1.name}, Read {offset.offset} bytes so far") # Parse second tag from same data tag2 = amulet_nbt.read_nbt(data, read_offset=offset) print(f"Second tag: {tag2.name}, Total bytes read: {offset.offset}") ``` -------------------------------- ### Serialize Tag to Binary NBT with Presets Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/encoding-functions.md Serialize a tag object into binary NBT format using a specified encoding preset or by defining custom encoding parameters like compression, endianness, and string encoding. ```python import amulet_nbt tag = amulet_nbt.CompoundTag({"test": amulet_nbt.IntTag(42)}) # Serialize with preset binary = tag.to_nbt(preset=amulet_nbt.java_encoding, name="root") # Custom encoding binary = tag.to_nbt(compressed=False, little_endian=False, name="test") ``` -------------------------------- ### Create Debugging Utilities for NBT Tags Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Provides utility functions to create human-readable descriptions of NBT tags and print the detailed structure of compound tags. Useful for inspecting NBT data during debugging. ```python import amulet_nbt from typing import Optional def describe_tag(tag: amulet_nbt.AnyNBT, indent: int = 0) -> str: """Create human-readable tag description.""" prefix = " " * indent if isinstance(tag, amulet_nbt.NumberType): return f"{prefix}{type(tag).__name__}({float(tag)})" elif isinstance(tag, amulet_nbt.StringTag): value = tag.py_str[:50] + "..." if len(tag.py_str) > 50 else tag.py_str return f"{prefix}StringTag(\"{value}\")" elif isinstance(tag, amulet_nbt.ListTag): return f"{prefix}ListTag({len(tag)} x {tag.element_class.__name__})" elif isinstance(tag, amulet_nbt.CompoundTag): lines = [f"{prefix}CompoundTag({{ "] for key in list(tag.keys())[:5]: # Show first 5 keys lines.append(f"{prefix} {key}: {describe_tag(tag[key], indent+2).strip()}") if len(tag) > 5: lines.append(f"{prefix} ... and {len(tag)-5} more") lines.append(f"{prefix}}}") return "\n".join(lines) else: return f"{prefix}{type(tag).__name__}(...)" def debug_compound(compound: amulet_nbt.CompoundTag) -> None: """Print detailed structure of compound tag.""" print(describe_tag(compound)) ``` -------------------------------- ### Read Multiple Binary NBT Objects (Preset) Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/encoding-functions.md Loads multiple binary NBT objects from a contiguous buffer using a specified encoding preset. Use '-1' for count to read all available objects. ```python import amulet_nbt # Read 10 NBT objects tags = amulet_nbt.read_nbt_array("data.bin", count=10, preset=amulet_nbt.java_encoding) print(len(tags)) # 10 # Read until buffer exhausted all_tags = amulet_nbt.read_nbt_array("data.bin", count=-1) # Track position offset = amulet_nbt.ReadOffset() tags = amulet_nbt.read_nbt_array("data.bin", count=5, read_offset=offset) ``` -------------------------------- ### StringTag Usage Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/tag-classes.md Create a StringTag and serialize it to SNBT format. This snippet demonstrates basic string tag manipulation. ```python import amulet_nbt tag = amulet_nbt.StringTag("Hello, World!") print(tag.py_str) # "Hello, World!" snbt = tag.to_snbt() # '"Hello, World!"' ``` -------------------------------- ### Link Libraries and Sources Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/tests/CMakeLists.txt Links the necessary libraries (amulet_pybind11_extensions, amulet_nbt) to the test module and adds all found Python C++ source files to the target. It also organizes sources into groups based on their directory structure. ```cmake target_link_libraries(_test_amulet_nbt PRIVATE amulet_pybind11_extensions) target_link_libraries(_test_amulet_nbt PRIVATE amulet_nbt) target_sources(_test_amulet_nbt PRIVATE ${SOURCES}) foreach(FILE ${SOURCES}) file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_LIST_DIR} ${FILE}) get_filename_component(GROUP ${REL_PATH} DIRECTORY) string(REPLACE "/" "\\" GROUP "${GROUP}") source_group(${GROUP} FILES ${FILE}) endforeach() ``` -------------------------------- ### Use Type Hints for Clarity Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Employ type hints to define clear contracts for function arguments and return values. This enhances code readability and allows static analysis tools to catch type errors. ```python # Without type hints - unclear what types are expected def get_spawn_point(data): if "SpawnX" in data: return data["SpawnX"] return 0 # With type hints - clear contract import amulet_nbt from typing import Tuple def get_spawn_point(data: amulet_nbt.CompoundTag) -> Tuple[int, int, int]: """Get spawn point coordinates from level data.""" spawn_x = data.get_int("SpawnX", default=0) spawn_y = data.get_int("SpawnY", default=64) spawn_z = data.get_int("SpawnZ", default=0) return (spawn_x, spawn_y, spawn_z) ``` -------------------------------- ### Direct vs. Safe NBT Access for Player Position Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/best-practices.md Compares a type-safe NBT access method ('get_player_pos_safe') with a faster, direct access method ('get_player_pos_fast'). Use safe access for untrusted data and fast access for guaranteed structures. ```python import amulet_nbt from typing import Optional # Robust but slightly slower - type-safe access def get_player_pos_safe(player_data: amulet_nbt.CompoundTag) -> Optional[tuple]: pos = player_data.get("Pos", cls=amulet_nbt.ListTag) if pos and len(pos) == 3: return tuple(float(x) for x in pos) return None # Faster for known structure - but requires valid data def get_player_pos_fast(player_data: amulet_nbt.CompoundTag) -> tuple: pos = player_data["Pos"] return (float(pos[0]), float(pos[1]), float(pos[2])) # Choose based on context: # - Use _safe when data source is untrusted # - Use _fast when data structure is guaranteed ``` -------------------------------- ### Platform-Specific Settings Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/CMakeLists.txt Sets platform-dependent variables and definitions for Windows, macOS, and Unix-like systems. ```cmake if (WIN32) # set windows 7 as the minimum version add_definitions(-D_WIN32_WINNT=0x0601) elseif(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") elseif(UNIX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) else() message( FATAL_ERROR "Unsupported platform. Please submit a pull request to support this platform." ) endif() if (MSVC) add_definitions("/MP") endif() ``` -------------------------------- ### Select NBT Encoding Preset Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/quick-reference.md Choose an encoding preset for NBT data, such as Java Edition (default) or Bedrock Edition. Presets define compression, endianness, and string encoding. ```python # Java Edition (default) preset = amulet_nbt.java_encoding # compressed=True, little_endian=False, string_encoding=mutf8_encoding # Bedrock Edition preset = amulet_nbt.bedrock_encoding # compressed=False, little_endian=True, string_encoding=utf8_escape_encoding ``` -------------------------------- ### EncodingPreset Source: https://github.com/amulet-team/amulet-nbt/blob/5.0/_autodocs/api-reference/encoding-functions.md Configuration object for binary NBT encoding/decoding. It defines properties like compression, endianness, and string encoding. ```APIDOC ## Class: EncodingPreset Configuration object for binary NBT encoding/decoding. **Properties:** - `compressed` (read-only, bool): Whether data is gzip compressed - `little_endian` (read-only, bool): Whether to use little-endian byte order - `string_encoding` (read-only, StringEncoding): String encoding method **Predefined presets:** - `java_encoding`: Compressed, big-endian, MUTF-8 strings (Java Edition) - `bedrock_encoding`: Uncompressed, little-endian, UTF-8 escape strings (Bedrock Edition) **Example:** ```python import amulet_nbt # Java Edition (default) tag = amulet_nbt.read_nbt("level.dat", preset=amulet_nbt.java_encoding) # Bedrock Edition tag = amulet_nbt.read_nbt("level.dat", preset=amulet_nbt.bedrock_encoding) # Custom encoding custom = amulet_nbt.EncodingPreset() # Access properties print(custom.compressed) # bool print(custom.little_endian) # bool print(custom.string_encoding) # StringEncoding ``` ```