def __init__(self, path_or_buffer: str, platform: str, selection: SelectionBox): self._path_or_buffer = path_or_buffer if platform == "java": self._materials = "Alpha" elif platform == "bedrock": self._materials = "Pocket" else: raise Exception( f'"{platform}" is not a supported platform for a schematic file.' ) self._selection = selection self._data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound( { "TileTicks": amulet_nbt.TAG_List(), "Width": amulet_nbt.TAG_Short(selection.size_x), "Height": amulet_nbt.TAG_Short(selection.size_y), "Length": amulet_nbt.TAG_Short(selection.size_z), "Materials": amulet_nbt.TAG_String(self._materials), } ), "Schematic", ) self._entities = [] self._block_entities = [] self._blocks = numpy.zeros( selection.shape, dtype=numpy.uint16 ) # only 12 bits are actually used at most self._block_data = numpy.zeros( selection.shape, dtype=numpy.uint8 ) # only 4 bits are used
def __init__(self, directory: str): super().__init__(directory) self._lock = False self._platform = "bedrock" self.root_tag: nbt.NBTFile = nbt.NBTFile() self._load_level_dat() self._level_manager: Optional[LevelDBDimensionManager] = None
def _init_write(self): """data to be written at init in write mode""" self._buffer.write(magic_num) self._buffer.write(struct.pack(">B", self._format_version)) if self._format_version == 0: self._metadata = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "created_with": amulet_nbt.TAG_String("amulet_python_wrapper"), "selection_boxes": amulet_nbt.TAG_Int_Array([ c for box in self._selection_boxes if len(box) == 6 and all( isinstance(c, int) for c in box) for c in box ]), "section_version": amulet_nbt.TAG_Byte(self._section_version), "export_version": amulet_nbt.TAG_Compound({ "edition": amulet_nbt.TAG_String(self._source_edition), "version": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(v) for v in self._source_version ]), }), })) else: raise Exception( f"This wrapper doesn't support any construction version higher than {max_format_version}" )
def _save_subchunks_1(self, blocks: "Blocks", palette: AnyNDArray) -> List[Optional[bytes]]: for index, block in enumerate(palette): block: Tuple[Tuple[None, Block], ...] block_data = block[0][1].properties.get("block_data", amulet_nbt.TAG_Int(0)) if isinstance(block_data, amulet_nbt.TAG_Int): block_data = block_data.value # if block_data >= 16: # block_data = 0 else: block_data = 0 palette[index] = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String(block[0][1].namespaced_name), "val": amulet_nbt.TAG_Short(block_data), })) chunk = [] for cy in range(16): if cy in blocks: palette_index, sub_chunk = fast_unique( blocks.get_sub_chunk(cy)) sub_chunk_palette = list(palette[palette_index]) chunk.append(b"\x01" + self._save_palette_subchunk( sub_chunk.ravel(), sub_chunk_palette)) else: chunk.append(None) return chunk
def __init__(self, path_or_buffer: str, selection: SelectionBox): self._path_or_buffer = path_or_buffer self._selection = selection self._data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "format_version": amulet_nbt.TAG_Int(1), "structure_world_origin": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(selection.min_x), amulet_nbt.TAG_Int(selection.min_y), amulet_nbt.TAG_Int(selection.min_z), ]), "size": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(selection.size_x), amulet_nbt.TAG_Int(selection.size_y), amulet_nbt.TAG_Int(selection.size_z), ]), })) self._entities = [] self._block_entities = [] self._blocks = numpy.zeros( selection.shape, dtype=numpy.uint32) # only 12 bits are actually used at most self._palette: List[AnyNDArray] = [] self._palette_len = 0
def root_tag(self, root_tag: Union[nbt.NBTFile, nbt.TAG_Compound]): if isinstance(root_tag, nbt.TAG_Compound): self._root_tag = nbt.NBTFile(root_tag) elif isinstance(root_tag, nbt.NBTFile): self._root_tag = root_tag else: raise ValueError("root_tag must be a TAG_Compound or NBTFile")
def __init__( self, namespace: str, base_name: str, x: _Coord, y: _Coord, z: _Coord, nbt: amulet_nbt.NBTFile, ): assert isinstance(namespace, str), "namespace must be a string" assert isinstance(base_name, str), "base_name must be a string" self._namespace = namespace self._base_name = base_name self._namespaced_name = None self._gen_namespaced_name() assert all( isinstance(c, self.coord_types) for c in (x, y, z) ), f"coordinates type must be in {self.coord_types}" self._x = self.coord_types[0](x) self._y = self.coord_types[0](y) self._z = self.coord_types[0](z) if isinstance(nbt, amulet_nbt.TAG_Compound): self._nbt = amulet_nbt.NBTFile(nbt) elif isinstance(nbt, amulet_nbt.NBTFile): self._nbt = nbt else: raise Exception(f"nbt must be an NBTFile. Got {nbt}")
def __init__(self, directory: str): super().__init__(directory) self.root_tag: nbt.NBTFile = nbt.NBTFile() self._load_level_dat() self._levels: Dict[InternalDimension, AnvilLevelManager] = {} self._dimension_name_map: Dict["Dimension", InternalDimension] = {} self._mcc_support: Optional[bool] = None self._lock = None
def _parse_entities(entities: amulet_nbt.TAG_List) -> List[Entity]: return [ Entity( entity["namespace"].value, entity["base_name"].value, entity["x"].value, entity["y"].value, entity["z"].value, amulet_nbt.NBTFile(entity["nbt"]), ) for entity in entities ]
def _parse_block_entities( block_entities: amulet_nbt.TAG_List) -> List[BlockEntity]: return [ BlockEntity( block_entity["namespace"].value, block_entity["base_name"].value, block_entity["x"].value, block_entity["y"].value, block_entity["z"].value, amulet_nbt.NBTFile(block_entity["nbt"]), ) for block_entity in block_entities ]
def _decode_block_entities( self, block_entities: amulet_nbt.TAG_List) -> List["BlockEntity"]: entities_out = [] for nbt in block_entities: entity = self._decode_block_entity( amulet_nbt.NBTFile(nbt), self.features["block_entity_format"], self.features["block_entity_coord_format"], ) if entity is not None: entities_out.append(entity) return entities_out
def generateSignEntity(x, y, z, direction): """Generates the entity to make the sign display its position""" return BlockEntity("java", "acacia_wall_sign", x, y, z,\ amulet_nbt.NBTFile(\ value = amulet_nbt.TAG_Compound(\ {\ "utags": amulet_nbt.TAG_Compound(\ {\ "keepPacked": amulet_nbt.TAG_Byte(0),\ "Text4": amulet_nbt.TAG_String("{\"text\":\"\"}"),\ "Text3": amulet_nbt.TAG_String("{\"text\":\"\"}"),\ "Text2": amulet_nbt.TAG_String("{\"text\":\"%d - %d\"}"%(z + direction, z + direction * 6)), \ "Text1": amulet_nbt.TAG_String("{\"text\":\"%d\"}"%x)\ }),\ "Color": amulet_nbt.TAG_String("black")\ })))
def write(self, section: ConstructionSection): if self._section_version == 0: sx, sy, sz = section.location shapex, shapey, shapez = section.shape blocks = section.blocks entities = section.entities block_entities = section.block_entities palette = section.palette for point, shape in zip((sx, sy, sz), (shapex, shapey, shapez)): assert shape >= 0, "Shape must be positive" assert point + shape <= ( ((point >> 4) + 1) << 4 ), "Section does not fit in a sub-chunk" position = self._buffer.tell() _tag = amulet_nbt.TAG_Compound( {"entities": self._serialise_entities(entities)} ) if blocks is None: _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(-1) else: flattened_array = blocks.ravel() index, flattened_array = numpy.unique( flattened_array, return_inverse=True ) palette = numpy.array(palette, dtype=object)[index] lut = numpy.vectorize(self._palette.get_add_block)(palette) flattened_array = lut[flattened_array] array_type = self._find_fitting_array_type(flattened_array) _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(array_type().tag_id) _tag["blocks"] = array_type(flattened_array) _tag["block_entities"] = self._serialise_block_entities( block_entities or [] ) amulet_nbt.NBTFile(_tag).save_to(self._buffer) length = self._buffer.tell() - position self._section_index_table.append( (sx, sy, sz, shapex, shapey, shapez, position, length) ) else: raise Exception( f"This wrapper doesn't support any section version higher than {max_section_version}" )
def __init__(self, path: str): """ Construct a new instance of :class:`AnvilFormat`. This should not be used directly. You should instead use :func:`amulet.load_format`. :param path: The file path to the serialised data. """ super().__init__(path) self._platform = "java" self._root_tag: nbt.NBTFile = nbt.NBTFile() self._levels: Dict[InternalDimension, AnvilDimensionManager] = {} self._dimension_name_map: Dict[Dimension, InternalDimension] = {} self._mcc_support: Optional[bool] = None self._lock_time = None self._data_pack: Optional[DataPackManager] = None self._shallow_load()
def save_to(self, f: BinaryIO): if self._platform == "java": materials = "Alpha" elif self._platform == "bedrock": materials = "Pocket" else: raise ObjectWriteError( f'"{self._platform}" is not a supported platform for a schematic file.' ) selection = self._selection.selection_boxes[0] data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "TileTicks": amulet_nbt.TAG_List(), "Width": amulet_nbt.TAG_Short(selection.size_x), "Height": amulet_nbt.TAG_Short(selection.size_y), "Length": amulet_nbt.TAG_Short(selection.size_z), "Materials": amulet_nbt.TAG_String(materials), }), "Schematic", ) entities = [] block_entities = [] blocks = numpy.zeros( selection.shape, dtype=numpy.uint16) # only 12 bits are actually used at most block_data = numpy.zeros(selection.shape, dtype=numpy.uint8) # only 4 bits are used for ( selection_, blocks_, data_, block_entities_, entities_, ) in self._chunks.values(): if selection_.intersects(selection): box = selection_.create_moved_box(selection.min, subtract=True) blocks[box.slice] = blocks_ block_data[box.slice] = data_ for be in block_entities_: coord_type = be["x"].__class__ be["x"] = coord_type(be["x"] - selection.min_x) be["y"] = coord_type(be["y"] - selection.min_y) be["z"] = coord_type(be["z"] - selection.min_z) block_entities.append(be) for e in entities_: coord_type = e["Pos"][0].__class__ e["Pos"][0] = coord_type(e["Pos"][0] - selection.min_x) e["Pos"][1] = coord_type(e["Pos"][1] - selection.min_y) e["Pos"][2] = coord_type(e["Pos"][2] - selection.min_z) entities.append(e) data["Entities"] = amulet_nbt.TAG_List(entities) data["TileEntities"] = amulet_nbt.TAG_List(block_entities) data["Data"] = amulet_nbt.TAG_Byte_Array( numpy.transpose(block_data, (1, 2, 0)) # XYZ => YZX ) data["Blocks"] = amulet_nbt.TAG_Byte_Array( numpy.transpose((blocks & 0xFF).astype(numpy.uint8), (1, 2, 0))) if numpy.max(blocks) > 0xFF: add_blocks = numpy.transpose(blocks & 0xF00, (1, 2, 0)) >> 8 data["AddBlocks"] = amulet_nbt.TAG_Byte_Array( add_blocks[::2] + (add_blocks[1::2] << 4)) data.save_to(f)
def save_to(self, f: BinaryIO): if self._schem_version == 1: raise SpongeSchemReadError( "Sponge Schematic Version 1 is not supported currently.") elif self._schem_version == 2: selection = self._bounds[self.dimensions[0]].selection_boxes[0] if any(s > 2**16 - 1 for s in selection.shape): raise SpongeSchemWriteError( "The structure is too large to be exported to a Sponge Schematic file. It must be 2^16 - 1 at most in each dimension." ) overflowed_shape = [ s if s < 2**15 else s - 2**16 for s in selection.shape ] data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "Version": amulet_nbt.TAG_Int(2), "DataVersion": amulet_nbt.TAG_Int(self._version), "Width": amulet_nbt.TAG_Short(overflowed_shape[0]), "Height": amulet_nbt.TAG_Short(overflowed_shape[1]), "Length": amulet_nbt.TAG_Short(overflowed_shape[2]), "Offset": amulet_nbt.TAG_Int_Array(selection.min), }), name="Schematic", ) entities = [] block_entities = [] blocks = numpy.zeros(selection.shape, dtype=numpy.uint32) palette: List[AnyNDArray] = [] if self._version < 1500: raise Exception( "Writing to Sponge Schematic files in pre-1.13 format is not currently supported." ) else: arr = numpy.empty(1, dtype=object) arr[0] = Block("minecraft", "air") palette.append((arr)) palette_len = 1 for ( selection_, blocks_, palette_, block_entities_, entities_, ) in self._chunks.values(): if selection_.intersects(selection): box = selection_.create_moved_box(selection.min, subtract=True) blocks[box.slice] = blocks_ + palette_len palette.append(palette_) palette_len += len(palette_) for be in block_entities_: be = copy.deepcopy(be) be["Pos"] = amulet_nbt.TAG_Int_Array(be["Pos"] - selection.min) block_entities.append(be) for e in entities_: e = copy.deepcopy(e) x, y, z = e["Pos"] e["Pos"] = amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(x - selection.min_x), amulet_nbt.TAG_Int(y - selection.min_y), amulet_nbt.TAG_Int(z - selection.min_z), ]) entities.append(e) compact_palette, lut = brute_sort_objects_no_hash( numpy.concatenate(palette)) blocks = numpy.transpose(lut[blocks], (1, 2, 0)).ravel() # XYZ => YZX block_palette = [] for index, block in enumerate(compact_palette): block: Block block_palette.append(block.blockstate) data["PaletteMax"] = amulet_nbt.TAG_Int(len(compact_palette)) data["Palette"] = amulet_nbt.TAG_Compound({ blockstate: amulet_nbt.TAG_Int(index) for index, blockstate in enumerate(block_palette) }) data["BlockData"] = amulet_nbt.TAG_Byte_Array( list(encode_array(blocks))) if block_entities: data["BlockEntities"] = amulet_nbt.TAG_List(block_entities) if entities: data["Entities"] = amulet_nbt.TAG_List(entities) data.save_to(f) else: raise SpongeSchemReadError( f"Sponge Schematic Version {self._schem_version} is not supported currently." )
def __init__(self, directory: str): super().__init__(directory) self._lock = False self.root_tag: nbt.NBTFile = nbt.NBTFile() self._load_level_dat() self._level_manager: Optional[LevelDBLevelManager] = None
def encode(self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, int]) -> amulet_nbt.NBTFile: """ Encode a version-specific chunk to raw data for the format to store. :param chunk: The version-specific chunk to translate and encode. :param palette: The block_palette the ids in the chunk correspond to. :param max_world_version: The key to use to find the encoder. :return: amulet_nbt.NBTFile """ misc = chunk.misc data = amulet_nbt.NBTFile(amulet_nbt.TAG_Compound(), "") data["Level"] = amulet_nbt.TAG_Compound() data["Level"]["xPos"] = amulet_nbt.TAG_Int(chunk.cx) data["Level"]["zPos"] = amulet_nbt.TAG_Int(chunk.cz) if self.features["data_version"] == "int": data["DataVersion"] = amulet_nbt.TAG_Int(max_world_version[1]) if self.features["last_update"] == "long": data["Level"]["LastUpdate"] = amulet_nbt.TAG_Long( misc.get("last_update", 0)) # Order the float value based on the order they would be run. Newer replacements for the same come just after # to save back find the next lowest valid value. if self.features["status"] in ["j13", "j14"]: status = chunk.status.as_type(self.features["status"]) data["Level"]["Status"] = amulet_nbt.TAG_String(status) else: status = chunk.status.as_type("float") if self.features["terrain_populated"] == "byte": data["Level"]["TerrainPopulated"] = amulet_nbt.TAG_Byte( int(status > -0.3)) if self.features["light_populated"] == "byte": data["Level"]["LightPopulated"] = amulet_nbt.TAG_Byte( int(status > -0.2)) if self.features["V"] == "byte": data["Level"]["V"] = amulet_nbt.TAG_Byte(misc.get("V", 1)) if self.features["inhabited_time"] == "long": data["Level"]["InhabitedTime"] = amulet_nbt.TAG_Long( misc.get("inhabited_time", 0)) if self.features["biomes"] == "256BA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Byte_Array( chunk.biomes.convert_to_format(256).astype( dtype=numpy.uint8)) elif self.features["biomes"] == "256IA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Int_Array( chunk.biomes.convert_to_format(256).astype( dtype=numpy.uint32)) elif self.features["biomes"] == "1024IA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Int_Array( chunk.biomes.convert_to_format(1024).astype( dtype=numpy.uint32)) if self.features["height_map"] == "256IA": data["Level"]["HeightMap"] = amulet_nbt.TAG_Int_Array( misc.get("height_map256IA", numpy.zeros(256, dtype=numpy.uint32))) elif self.features["height_map"] in [ "C|36LA|V1", "C|36LA|V2", "C|36LA|V3", "C|36LA|V4", ]: maps = [ "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", ] if self.features["height_map"] == "C|36LA|V1": # 1466 maps = ("LIQUID", "SOILD", "LIGHT", "RAIN") elif self.features["height_map"] == "C|36LA|V2": # 1484 maps.append("LIGHT_BLOCKING") elif self.features["height_map"] == "C|36LA|V3": # 1503 maps.append("LIGHT_BLOCKING") maps.append("WORLD_SURFACE") elif self.features["height_map"] == "C|36LA|V4": # 1908 maps.append("WORLD_SURFACE") else: raise Exception heightmaps = misc.get("height_mapC|36LA", amulet_nbt.TAG_Compound()) for heightmap in maps: if heightmap not in heightmaps: heightmaps[heightmap] = amulet_nbt.TAG_Long_Array( numpy.zeros(36, dtype=">i8")) data["Level"]["Heightmaps"] = heightmaps if self.features["blocks"] in [ "Sections|(Blocks,Data,Add)", "Sections|(BlockStates,Palette)", ]: data["Level"]["Sections"] = self._encode_blocks( chunk.blocks, palette) else: raise Exception( f'Unsupported block format {self.features["blocks"]}') if self.features["block_light"] in [ "Sections|2048BA" ] or self.features["sky_light"] in ["Sections|2048BA"]: for section in data["Level"]["Sections"]: y = section["Y"].value if self.features["block_light"] == "Sections|2048BA": block_light = misc.get("block_light", {}) if y in block_light: section["BlockLight"] = block_light[y] else: section["BlockLight"] = amulet_nbt.TAG_Byte_Array( numpy.full(2048, 255, dtype=numpy.uint8)) if self.features["sky_light"] == "Sections|2048BA": sky_light = misc.get("sky_light", {}) if y in sky_light: section["SkyLight"] = sky_light[y] else: section["SkyLight"] = amulet_nbt.TAG_Byte_Array( numpy.full(2048, 255, dtype=numpy.uint8)) if self.features["entities"] == "list": if amulet.entity_support: data["Level"]["Entities"] = self._encode_entities( chunk.entities) else: data["Level"]["Entities"] = self._encode_entities( misc.get("java_entities_temp", amulet_nbt.TAG_List())) if self.features["block_entities"] == "list": data["Level"]["TileEntities"] = self._encode_block_entities( chunk.block_entities) if self.features["tile_ticks"] in ["list", "list(optional)"]: ticks = misc.get("tile_ticks", amulet_nbt.TAG_List()) if self.features["tile_ticks"] == "list(optional)": if len(ticks) > 0: data["Level"]["TileTicks"] = ticks elif self.features["tile_ticks"] == "list": data["Level"]["TileTicks"] = ticks if self.features["liquid_ticks"] == "list": data["Level"]["LiquidTicks"] = misc.get("liquid_ticks", amulet_nbt.TAG_List()) if self.features["liquids_to_be_ticked"] == "16list|list": data["Level"]["LiquidsToBeTicked"] = misc.get( "liquids_to_be_ticked", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["to_be_ticked"] == "16list|list": data["Level"]["ToBeTicked"] = misc.get( "to_be_ticked", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["post_processing"] == "16list|list": data["Level"]["PostProcessing"] = misc.get( "post_processing", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["structures"] == "compound": data["Level"]["Structures"] = misc.get( "structures", amulet_nbt.TAG_Compound({ "References": amulet_nbt.TAG_Compound(), "Starts": amulet_nbt.TAG_Compound(), }), ) return data
def _encode_subchunks( self, blocks: "Blocks", palette: AnyNDArray, bounds: Tuple[int, int], max_world_version: VersionNumberTuple, ) -> Dict[int, Optional[bytes]]: # Encode sub-chunk block format 8 palette_depth = numpy.array([len(block) for block in palette]) min_y = bounds[0] // 16 max_y = bounds[1] // 16 if palette.size: if palette[0][0][0] is None: air = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String("minecraft:air"), "val": amulet_nbt.TAG_Short(0), })) else: air = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String("minecraft:air"), "states": amulet_nbt.TAG_Compound({}), "version": amulet_nbt.TAG_Int(17_629_184), # 1, 13, 0, 0 })) for index, block in enumerate(palette): block: Tuple[Tuple[Optional[int], Block], ...] full_block = [] for sub_block_version, sub_block in block: properties = sub_block.properties if sub_block_version is None: block_data = properties.get("block_data", amulet_nbt.TAG_Int(0)) if isinstance(block_data, amulet_nbt.TAG_Int): block_data = block_data.value # if block_data >= 16: # block_data = 0 else: block_data = 0 sub_block_ = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String( sub_block.namespaced_name), "val": amulet_nbt.TAG_Short(block_data), })) else: sub_block_ = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String( sub_block.namespaced_name), "states": amulet_nbt.TAG_Compound({ key: val for key, val in properties.items() if isinstance(val, PropertyDataTypes) }), "version": amulet_nbt.TAG_Int(sub_block_version), })) full_block.append(sub_block_) palette[index] = tuple(full_block) chunk = {} for cy in range(min_y, max_y): if cy in blocks: palette_index, sub_chunk = fast_unique( blocks.get_sub_chunk(cy)) sub_chunk_palette = palette[palette_index] sub_chunk_depth = palette_depth[palette_index].max() if (sub_chunk_depth == 1 and len(sub_chunk_palette) == 1 and sub_chunk_palette[0][0]["name"].value == "minecraft:air"): chunk[cy] = None else: # pad block_palette with air in the extra layers sub_chunk_palette_full = numpy.empty( (sub_chunk_palette.size, sub_chunk_depth), dtype=object) sub_chunk_palette_full.fill(air) for index, block_tuple in enumerate(sub_chunk_palette): for sub_index, block in enumerate(block_tuple): sub_chunk_palette_full[index, sub_index] = block # should now be a 2D array with an amulet_nbt.NBTFile in each element if max_world_version[1] >= ( 1, 17, 30, ): # Why do I need to check against game version and not chunk version sub_chunk_bytes = [ b"\x09", bytes([sub_chunk_depth]), struct.pack("b", cy), ] else: sub_chunk_bytes = [ b"\x08", bytes([sub_chunk_depth]) ] for sub_chunk_layer_index in range(sub_chunk_depth): # TODO: sort out a way to do this quicker without brute forcing it. ( sub_chunk_layer_palette, sub_chunk_remap, ) = brute_sort_objects_no_hash( sub_chunk_palette_full[:, sub_chunk_layer_index]) sub_chunk_layer = sub_chunk_remap[ sub_chunk.ravel()] # sub_chunk_layer, sub_chunk_layer_palette = sub_chunk, sub_chunk_palette_full[:, sub_chunk_layer_index] sub_chunk_bytes.append( self._save_palette_subchunk( sub_chunk_layer.reshape(16, 16, 16), list(sub_chunk_layer_palette.ravel()), )) chunk[cy] = b"".join(sub_chunk_bytes) else: chunk[cy] = None else: chunk = {i: None for i in range(min_y, max_y)} return chunk
def save_to(self, f: BinaryIO): selection = self._selection.selection_boxes[0] data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound( { "format_version": amulet_nbt.TAG_Int(1), "structure_world_origin": amulet_nbt.TAG_List( [ amulet_nbt.TAG_Int(selection.min_x), amulet_nbt.TAG_Int(selection.min_y), amulet_nbt.TAG_Int(selection.min_z), ] ), "size": amulet_nbt.TAG_List( [ amulet_nbt.TAG_Int(selection.size_x), amulet_nbt.TAG_Int(selection.size_y), amulet_nbt.TAG_Int(selection.size_z), ] ), } ) ) entities = [] block_entities = [] blocks = numpy.zeros( selection.shape, dtype=numpy.uint32 ) # only 12 bits are actually used at most palette: List[AnyNDArray] = [] palette_len = 0 for ( selection_, blocks_, palette_, block_entities_, entities_, ) in self._chunks.values(): if selection_.intersects(selection): box = selection_.create_moved_box(selection.min, subtract=True) blocks[box.slice] = blocks_ + palette_len palette.append(palette_) palette_len += len(palette_) block_entities += block_entities_ entities += entities_ compact_palette, lut = brute_sort_objects_no_hash(numpy.concatenate(palette)) blocks = lut[blocks].ravel() block_palette = [] block_palette_indices = [] for block_list in compact_palette: indexed_block = [-1] * 2 for block_layer, block in enumerate(block_list): if block_layer >= 2: break if block in block_palette: indexed_block[block_layer] = block_palette.index(block) else: indexed_block[block_layer] = len(block_palette) block_palette.append(block) block_palette_indices.append(indexed_block) block_indices = numpy.array(block_palette_indices, dtype=numpy.int32)[blocks].T data["structure"] = amulet_nbt.TAG_Compound( { "block_indices": amulet_nbt.TAG_List( [ # a list of tag ints that index into the block_palette. One list per block layer amulet_nbt.TAG_List( [amulet_nbt.TAG_Int(block) for block in layer] ) for layer in block_indices ] ), "entities": amulet_nbt.TAG_List(entities), "palette": amulet_nbt.TAG_Compound( { "default": amulet_nbt.TAG_Compound( { "block_palette": amulet_nbt.TAG_List(block_palette), "block_position_data": amulet_nbt.TAG_Compound( { str( ( ( block_entity["x"].value - selection.min_x ) * selection.size_y + ( block_entity["y"].value - selection.min_y ) ) * selection.size_z + block_entity["z"].value - selection.min_z ): amulet_nbt.TAG_Compound( {"block_entity_data": block_entity} ) for block_entity in block_entities } ), } ) } ), } ) data.save_to(f, compressed=False, little_endian=True)
def fillbarrels(chunk, barrelPositionList, barrelBlock, currentArticle, booksPerBarrel, zimFilePath, chunkList, target_pos): """Generates all barrels in the chunk and fills them with books/articles""" for barrelPos in barrelPositionList: books = [] titles = [] start = time.perf_counter() if booksPerBarrel > 30: pool = Pool( processes=4 ) #on my laptop ~4 processes was faster than any amount of threads (4 = logic core count) else: pool = ThreadPool( processes=3 ) #the article reading is mostly cpu limited, so going high on process count doesnt help outputs = pool.map( partial(tryGetArticle, zimFilePath=zimFilePath, barrelPositionList=barrelPositionList, booksPerBarrel=booksPerBarrel, chunkList=chunkList, target_pos=target_pos), range(currentArticle, currentArticle + booksPerBarrel)) pool.close() #outputs = [] #for id in range(currentArticle, currentArticle + booksPerBarrel): # outputs.append(tryGetArticle(id, zimFilePath)) currentArticle += booksPerBarrel for output in outputs: if output[0] == None: continue titles.append(output[1]) books.append(output[0]) stop = time.perf_counter() #print("generating a book", (stop-start)/booksPerBarrel) chunk.blocks[barrelPos] = barrelBlock barrelEntity = BlockEntity("java", "barrel", barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16,\ amulet_nbt.NBTFile(\ value = amulet_nbt.TAG_Compound(\ {\ "utags": amulet_nbt.TAG_Compound(\ {\ "keepPacked": amulet_nbt.TAG_Byte(0),\ "isMovable": amulet_nbt.TAG_Byte(1),\ "Findable": amulet_nbt.TAG_Byte(0),\ "CustomName": amulet_nbt.TAG_String("{\"text\":\"x:%d y:%d z:%d\"}"%(barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16)),\ "Items": amulet_nbt.TAG_List(\ value = [ amulet_nbt.TAG_Compound(\ {\ "Slot": amulet_nbt.TAG_Byte(iBook),\ "Count": amulet_nbt.TAG_Byte(1),\ "id": amulet_nbt.TAG_String("minecraft:written_book"),\ "tag": amulet_nbt.TAG_Compound(\ { "pages": amulet_nbt.TAG_List(\ value=[amulet_nbt.TAG_String(page) for page in books[iBook]],\ list_data_type = 8\ ),\ "title": amulet_nbt.TAG_String(titles[iBook]),\ "author": amulet_nbt.TAG_String("Pos: x:%d y:%d z:%d, ID: %d"%(barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16, currentArticle + iBook)), }) }) for iBook in range(len(books)) ], list_data_type = 9\ ) })\ }))) chunk.block_entities.insert(barrelEntity)
def save_to(self, f: BinaryIO): palette: BlockManager = BlockManager() f.write(magic_num) f.write(struct.pack(">B", self._format_version)) if self._format_version == 0: metadata = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound( { "created_with": amulet_nbt.TAG_String( "amulet_python_wrapper_v2" ), "selection_boxes": amulet_nbt.TAG_Int_Array( [ c for box in self._selection.selection_boxes for c in (*box.min, *box.max) ] ), "section_version": amulet_nbt.TAG_Byte(self._section_version), "export_version": amulet_nbt.TAG_Compound( { "edition": amulet_nbt.TAG_String(self._platform), "version": amulet_nbt.TAG_List( [amulet_nbt.TAG_Int(v) for v in self._version] ), } ), } ) ) section_index_table: List[ Tuple[int, int, int, int, int, int, int, int] ] = [] if self._section_version == 0: for section_list in self._chunk_to_section.values(): for section in section_list: sx, sy, sz = section.location shapex, shapey, shapez = section.shape blocks = section.blocks entities = section.entities block_entities = section.block_entities section_palette = section.palette position = f.tell() _tag = amulet_nbt.TAG_Compound( {"entities": serialise_entities(entities)} ) if blocks is None: _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(-1) else: flattened_array = blocks.ravel() index, flattened_array = numpy.unique( flattened_array, return_inverse=True ) section_palette = numpy.array( section_palette, dtype=object )[index] lut = numpy.vectorize(palette.get_add_block)( section_palette ) flattened_array = lut[flattened_array] array_type = find_fitting_array_type(flattened_array) _tag["blocks_array_type"] = amulet_nbt.TAG_Byte( array_type().tag_id ) _tag["blocks"] = array_type(flattened_array) _tag["block_entities"] = serialise_block_entities( block_entities or [] ) amulet_nbt.NBTFile(_tag).save_to(f) length = f.tell() - position section_index_table.append( (sx, sy, sz, shapex, shapey, shapez, position, length) ) else: raise Exception( f"This wrapper doesn't support any section version higher than {max_section_version}" ) metadata_start = f.tell() metadata["section_index_table"] = amulet_nbt.TAG_Byte_Array( numpy.array(section_index_table, dtype=SECTION_ENTRY_TYPE).view( numpy.int8 ) ) metadata["block_palette"] = pack_palette(palette) metadata.save_to(f) f.write(INT_STRUCT.pack(metadata_start)) f.write(magic_num) else: raise Exception( f"This wrapper doesn't support any construction version higher than {max_format_version}" )
""" Create a shallow copy of the entity container. """ return EntityList(self) def __iter__(self) -> Iterable[Entity]: """ An iterable of all the :class:`Entity` objects. """ yield from self.data def __repr__(self) -> str: """Return repr(self).""" return f"EntityList({super().__repr__()})" if __name__ == "__main__": import amulet_nbt entities_ = EntityList() block_ents = [ Entity("minecraft", "creeper", 0.0, 0.0, 0.0, amulet_nbt.NBTFile()), Entity("minecraft", "cow", 0.0, 0.0, 0.0, amulet_nbt.NBTFile()), Entity("minecraft", "pig", 0.0, 0.0, 0.0, amulet_nbt.NBTFile()), Entity("minecraft", "sheep", 0.0, 0.0, 0.0, amulet_nbt.NBTFile()), ] entities_.append( Entity("minecraft", "cow", 0.0, 0.0, 0.0, amulet_nbt.NBTFile())) entities_ += block_ents print(entities_)
def _save_subchunks_8(self, blocks: "Blocks", palette: AnyNDArray) -> List[Optional[bytes]]: palette_depth = numpy.array([len(block) for block in palette]) if palette.size: if palette[0][0][0] is None: air = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String("minecraft:air"), "val": amulet_nbt.TAG_Short(0), })) else: air = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String("minecraft:air"), "states": amulet_nbt.TAG_Compound({}), "version": amulet_nbt.TAG_Int(17_629_184), # 1, 13, 0, 0 })) for index, block in enumerate(palette): block: Tuple[Tuple[Optional[int], Block], ...] full_block = [] for sub_block_version, sub_block in block: properties = sub_block.properties if sub_block_version is None: block_data = properties.get("block_data", amulet_nbt.TAG_Int(0)) if isinstance(block_data, amulet_nbt.TAG_Int): block_data = block_data.value # if block_data >= 16: # block_data = 0 else: block_data = 0 sub_block_ = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String( sub_block.namespaced_name), "val": amulet_nbt.TAG_Short(block_data), })) else: sub_block_ = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String( sub_block.namespaced_name), "states": amulet_nbt.TAG_Compound({ key: val for key, val in properties.items() if isinstance(val, PropertyDataTypes) }), "version": amulet_nbt.TAG_Int(sub_block_version), })) full_block.append(sub_block_) palette[index] = tuple(full_block) chunk = [] for cy in range(16): if cy in blocks: palette_index, sub_chunk = fast_unique( blocks.get_sub_chunk(cy)) sub_chunk_palette = palette[palette_index] sub_chunk_depth = palette_depth[palette_index].max() if (sub_chunk_depth == 1 and len(sub_chunk_palette) == 1 and sub_chunk_palette[0][0]["name"].value == "minecraft:air"): chunk.append(None) else: # pad block_palette with air in the extra layers sub_chunk_palette_full = numpy.empty( (sub_chunk_palette.size, sub_chunk_depth), dtype=object) sub_chunk_palette_full.fill(air) for index, block_tuple in enumerate(sub_chunk_palette): for sub_index, block in enumerate(block_tuple): sub_chunk_palette_full[index, sub_index] = block # should now be a 2D array with an amulet_nbt.NBTFile in each element sub_chunk_bytes = [b"\x08", bytes([sub_chunk_depth])] for sub_chunk_layer_index in range(sub_chunk_depth): # TODO: sort out a way to do this quicker without brute forcing it. ( sub_chunk_layer_palette, sub_chunk_remap, ) = brute_sort_objects_no_hash( sub_chunk_palette_full[:, sub_chunk_layer_index]) sub_chunk_layer = sub_chunk_remap[ sub_chunk.ravel()] # sub_chunk_layer, sub_chunk_layer_palette = sub_chunk, sub_chunk_palette_full[:, sub_chunk_layer_index] sub_chunk_bytes.append( self._save_palette_subchunk( sub_chunk_layer.reshape(16, 16, 16), list(sub_chunk_layer_palette.ravel()), )) chunk.append(b"".join(sub_chunk_bytes)) else: chunk.append(None) else: chunk = [None] * 16 return chunk
def save_to(self, f: BinaryIO): selection = self._bounds[self.dimensions[0]].selection_boxes[0] data = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "format_version": amulet_nbt.TAG_Int(1), "structure_world_origin": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(selection.min_x), amulet_nbt.TAG_Int(selection.min_y), amulet_nbt.TAG_Int(selection.min_z), ]), "size": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(selection.size_x), amulet_nbt.TAG_Int(selection.size_y), amulet_nbt.TAG_Int(selection.size_z), ]), })) entities = [] block_entities = [] blocks = numpy.zeros(selection.shape, dtype=numpy.uint32) palette: List[AnyNDArray] = [] if self.version < (1, 13, 0): raise Exception( "Writing to mcstructre files in pre-1.13 format is not currently supported." ) else: arr = numpy.empty(1, dtype=object) arr[0] = [ amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String("minecraft:air"), "states": amulet_nbt.TAG_Compound(), "version": amulet_nbt.TAG_Int(17694723), }) ] palette.append(arr) palette_len = 1 for ( selection_, blocks_, palette_, block_entities_, entities_, ) in self._chunks.values(): if selection_.intersects(selection): box = selection_.create_moved_box(selection.min, subtract=True) blocks[box.slice] = blocks_ + palette_len palette.append(palette_) palette_len += len(palette_) block_entities += block_entities_ entities += entities_ compact_palette, lut = brute_sort_objects_no_hash( numpy.concatenate(palette)) blocks = lut[blocks].ravel() block_palette = [] block_palette_indices = [] for block_list in compact_palette: indexed_block = [-1] * 2 for block_layer, block in enumerate(block_list): if block_layer >= 2: break if block["name"] != "minecraft:structure_void": if block in block_palette: indexed_block[block_layer] = block_palette.index(block) else: indexed_block[block_layer] = len(block_palette) block_palette.append(block) block_palette_indices.append(indexed_block) block_indices = numpy.array(block_palette_indices, dtype=numpy.int32)[blocks].T data["structure"] = amulet_nbt.TAG_Compound({ "block_indices": amulet_nbt.TAG_List( [ # a list of tag ints that index into the block_palette. One list per block layer amulet_nbt.TAG_List( [amulet_nbt.TAG_Int(block) for block in layer]) for layer in block_indices ]), "entities": amulet_nbt.TAG_List(entities), "palette": amulet_nbt.TAG_Compound({ "default": amulet_nbt.TAG_Compound({ "block_palette": amulet_nbt.TAG_List(block_palette), "block_position_data": amulet_nbt.TAG_Compound({ str(((block_entity["x"].value - selection.min_x) * selection.size_y + (block_entity["y"].value - selection.min_y)) * selection.size_z + block_entity["z"].value - selection.min_z): amulet_nbt.TAG_Compound( {"block_entity_data": block_entity}) for block_entity in block_entities }), }) }), }) data.save_to(f, compressed=False, little_endian=True)