示例#1
0
 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}"
         )
示例#2
0
    def _create(
        self,
        overwrite: bool,
        bounds: Union[SelectionGroup,
                      Dict[Dimension, Optional[SelectionGroup]], None] = None,
        **kwargs,
    ):
        if os.path.isdir(self.path):
            if overwrite:
                shutil.rmtree(self.path)
            else:
                raise ObjectWriteError(
                    f"A world already exists at the path {self.path}")
        self._version = self.translation_manager.get_version(
            self.platform, self.version).data_version

        self.root_tag = root = nbt.TAG_Compound()
        root["Data"] = data = nbt.TAG_Compound()
        data["version"] = nbt.TAG_Int(19133)
        data["DataVersion"] = nbt.TAG_Int(self._version)
        data["LastPlayed"] = nbt.TAG_Long(int(time.time() * 1000))
        data["LevelName"] = nbt.TAG_String("World Created By Amulet")

        os.makedirs(self.path, exist_ok=True)
        self.root_tag.save_to(os.path.join(self.path, "level.dat"))
        self._reload_world()
示例#3
0
 def _encode_palette(blockstates: list) -> amulet_nbt.TAG_List:
     palette = amulet_nbt.TAG_List()
     for block in blockstates:
         entry = amulet_nbt.TAG_Compound()
         entry["Name"] = amulet_nbt.TAG_String(
             f"{block.namespace}:{block.base_name}")
         entry["Properties"] = amulet_nbt.TAG_Compound(block.properties)
         palette.append(entry)
     return palette
示例#4
0
    def encode(
        self,
        chunk: "Chunk",
        palette: AnyNDArray,
        max_world_version: VersionIdentifierType,
        box: SelectionBox,
    ) -> MCStructureChunk:
        """
        Take a version-specific chunk and encode it to raw data for the format to store.
        :param chunk: The already translated version-specfic chunk to encode.
        :param palette: The block_palette the ids in the chunk correspond to.
        :type palette: numpy.ndarray[Block]
        :param max_world_version: The key to use to find the encoder.
        :param box: The volume of the chunk to pack.
        :return: Raw data to be stored by the Format.
        """
        entities = []
        for e in chunk.entities:
            if e.location in box:
                entities.append(
                    self._encode_entity(e, self._entity_id_type,
                                        self._entity_coord_type).value)
        block_entities = []
        for e in chunk.block_entities:
            if e.location in box:
                block_entities.append(
                    self._encode_block_entity(
                        e, self._block_entity_id_type,
                        self._block_entity_coord_type).value)

        slices = box.create_moved_box((chunk.cx * 16, 0, chunk.cz * 16),
                                      subtract=True).slice

        out_palette = numpy.empty(palette.shape, dtype=object)
        for index, block_layers in enumerate(palette):
            blocks_out = []
            for version, block in block_layers:
                block = amulet_nbt.TAG_Compound({
                    "name":
                    amulet_nbt.TAG_String(
                        f"{block.namespace}:{block.base_name}"),
                    "states":
                    amulet_nbt.TAG_Compound(block.properties),
                })
                if version:
                    block["version"] = amulet_nbt.TAG_Int(version)
                blocks_out.append(block)
            out_palette[index] = blocks_out

        return MCStructureChunk(
            box,
            numpy.asarray(chunk.blocks[slices]),
            out_palette,
            block_entities,
            entities,
        )
示例#5
0
    def close(self):
        compact_palette, lut = brute_sort_objects_no_hash(
            numpy.concatenate(self._palette))
        self._blocks = lut[self._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)[self._blocks].T

        self._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(self._entities),
            "block_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 - self._selection.min_x) *
                            self._selection.size_y +
                            (block_entity["y"].value -
                             self._selection.min_y)) * self._selection.size_z +
                            block_entity["z"].value - self._selection.min_z):
                        amulet_nbt.TAG_Compound(
                            {"block_entity_data": block_entity})
                        for block_entity in self._block_entities
                    }),
                })
            }),
        })
        self._data.save_to(self._path_or_buffer,
                           compressed=False,
                           little_endian=True)
示例#6
0
def generate_block_entry(block: Block, palette_len,
                         extra_blocks) -> amulet_nbt.TAG_Compound:
    return amulet_nbt.TAG_Compound({
        "namespace":
        amulet_nbt.TAG_String(block.namespace),
        "blockname":
        amulet_nbt.TAG_String(block.base_name),
        "properties":
        amulet_nbt.TAG_Compound(block.properties),
        "extra_blocks":
        amulet_nbt.TAG_List([
            amulet_nbt.TAG_Int(palette_len + extra_blocks.index(_extra_block))
            for _extra_block in block.extra_blocks
        ]),
    })
示例#7
0
 def __init__(
     self,
     namespace: str,
     base_name: str,
     properties: dict = None,
     metadata: dict = None,
 ):
     super().__init__(
         {
             "namespace": amulet_nbt.TAG_String(namespace),
             "base_name": amulet_nbt.TAG_String(base_name),
             "properties": amulet_nbt.TAG_Compound(properties or {}),
             "metadata": amulet_nbt.TAG_Compound(metadata or {}),
         }
     )
示例#8
0
    def encode(
        self,
        chunk: "Chunk",
        palette: AnyNDArray,
        max_world_version: Tuple[str, Union[int, Tuple[int, int, int]]],
        box: SelectionBox = None,
    ) -> MCStructureChunk:
        entities = []
        for e in chunk.entities:
            if e.location in box:
                entities.append(
                    self._encode_entity(
                        e, self._entity_id_type, self._entity_coord_type
                    ).value
                )
        block_entities = []
        for e in chunk.block_entities:
            if e.location in box:
                block_entities.append(
                    self._encode_block_entity(
                        e, self._block_entity_id_type, self._block_entity_coord_type
                    ).value
                )

        slices = box.create_moved_box(
            (chunk.cx * 16, 0, chunk.cz * 16), subtract=True
        ).slice

        out_palette = numpy.empty(palette.shape, dtype=object)
        for index, block_layers in enumerate(palette):
            blocks_out = []
            for version, block in block_layers:
                block = amulet_nbt.TAG_Compound(
                    {
                        "name": amulet_nbt.TAG_String(
                            f"{block.namespace}:{block.base_name}"
                        ),
                        "states": amulet_nbt.TAG_Compound(block.properties),
                    }
                )
                if version:
                    block["version"] = amulet_nbt.TAG_Int(version)
                blocks_out.append(block)
            out_palette[index] = blocks_out

        return MCStructureChunk(
            box, chunk.blocks[slices], out_palette, block_entities, entities
        )
示例#9
0
    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 _encode_blocks(
        self, blocks: Blocks, palette: AnyNDArray
    ) -> amulet_nbt.TAG_List:
        sections = amulet_nbt.TAG_List()
        for cy in range(16):
            if cy in blocks:
                block_sub_array = numpy.transpose(
                    blocks.get_sub_chunk(cy), (1, 2, 0)
                ).ravel()

                sub_palette_, block_sub_array = numpy.unique(
                    block_sub_array, return_inverse=True
                )
                sub_palette = self._encode_palette(palette[sub_palette_])
                if (
                    len(sub_palette) == 1
                    and sub_palette[0]["Name"].value == "minecraft:air"
                ):
                    continue

                section = amulet_nbt.TAG_Compound()
                section["Y"] = amulet_nbt.TAG_Byte(cy)
                if self.features["long_array_format"] == "compact":
                    section["BlockStates"] = amulet_nbt.TAG_Long_Array(
                        encode_long_array(block_sub_array)
                    )
                elif self.features["long_array_format"] == "1.16":
                    section["BlockStates"] = amulet_nbt.TAG_Long_Array(
                        encode_long_array(block_sub_array, dense=False)
                    )
                section["Palette"] = sub_palette
                sections.append(section)

        return sections
示例#11
0
    def _create(
        self,
        overwrite: bool,
        bounds: Union[SelectionGroup,
                      Dict[Dimension, Optional[SelectionGroup]], None] = None,
        **kwargs,
    ):
        if os.path.isdir(self.path):
            if overwrite:
                shutil.rmtree(self.path)
            else:
                raise ObjectWriteError(
                    f"A world already exists at the path {self.path}")

        version = self.translation_manager.get_version(
            self.platform, self.version).version_number
        self._version = version + (0, ) * (5 - len(version))

        self.root_tag = root = nbt.TAG_Compound()
        root["StorageVersion"] = nbt.TAG_Int(8)
        root["lastOpenedWithVersion"] = nbt.TAG_List(
            [nbt.TAG_Int(i) for i in self._version])
        root["Generator"] = nbt.TAG_Int(1)
        root["LastPlayed"] = nbt.TAG_Long(int(time.time()))
        root["LevelName"] = nbt.TAG_String("World Created By Amulet")

        os.makedirs(self.path, exist_ok=True)
        self.root_tag.save(os.path.join(self.path, "level.dat"))

        db = LevelDB(os.path.join(self.path, "db"), True)
        db.close()

        self._reload_world()
示例#12
0
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")\
                        })))
示例#13
0
    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
示例#14
0
    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 _decode_palette(palette: amulet_nbt.TAG_List) -> list:
     blockstates = []
     for entry in palette:
         namespace, base_name = entry["Name"].value.split(":", 1)
         properties = entry.get("Properties", amulet_nbt.TAG_Compound({})).value
         block = Block(
             namespace=namespace, base_name=base_name, properties=properties
         )
         blockstates.append(block)
     return blockstates
示例#16
0
 def _serialise_entities(entities: List[Entity]) -> amulet_nbt.TAG_List:
     return amulet_nbt.TAG_List(
         [
             amulet_nbt.TAG_Compound(
                 {
                     "namespace": amulet_nbt.TAG_String(entity.namespace),
                     "base_name": amulet_nbt.TAG_String(entity.base_name),
                     "x": amulet_nbt.TAG_Double(entity.x),
                     "y": amulet_nbt.TAG_Double(entity.y),
                     "z": amulet_nbt.TAG_Double(entity.z),
                     "nbt": entity.nbt.value,
                 }
             )
             for entity in entities
         ]
     )
示例#17
0
    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}"
            )
示例#18
0
 def _serialise_block_entities(
     block_entities: List[BlockEntity], ) -> amulet_nbt.TAG_List:
     return amulet_nbt.TAG_List([
         amulet_nbt.TAG_Compound({
             "namespace":
             amulet_nbt.TAG_String(block_entity.namespace),
             "base_name":
             amulet_nbt.TAG_String(block_entity.base_name),
             "x":
             amulet_nbt.TAG_Int(block_entity.x),
             "y":
             amulet_nbt.TAG_Int(block_entity.y),
             "z":
             amulet_nbt.TAG_Int(block_entity.z),
             "nbt":
             block_entity.nbt.value,
         }) for block_entity in block_entities
     ])
示例#19
0
    def _encode_blocks(self, blocks: "Blocks",
                       palette: AnyNDArray) -> nbt.TAG_List:
        sections = nbt.TAG_List()
        for cy in range(16):  # perhaps find a way to do this dynamically
            if cy in blocks:
                block_sub_array = palette[numpy.transpose(
                    blocks.get_sub_chunk(cy), (1, 2, 0)).ravel()]

                data_sub_array = block_sub_array[:, 1]
                block_sub_array = block_sub_array[:, 0]
                if not numpy.any(block_sub_array) and not numpy.any(
                        data_sub_array):
                    continue
                section = nbt.TAG_Compound()
                section["Y"] = nbt.TAG_Byte(cy)
                section["Blocks"] = nbt.TAG_Byte_Array(
                    block_sub_array.astype("uint8"))
                section["Data"] = nbt.TAG_Byte_Array(
                    world_utils.to_nibble_array(data_sub_array))
                sections.append(section)

        return sections
示例#20
0
 def _get_version(self) -> int:
     return (self.root_tag.get("Data", nbt.TAG_Compound()).get(
         "DataVersion", nbt.TAG_Int(-1)).value)
示例#21
0
    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
示例#22
0
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)
示例#23
0
    def open_from(self, f: BinaryIO):
        mcstructure = amulet_nbt.load(f, little_endian=True)
        if mcstructure["format_version"].value == 1:
            min_point = numpy.array(
                tuple(c.value for c in mcstructure["structure_world_origin"]))
            max_point = min_point + tuple(c.value for c in mcstructure["size"])
            selection = SelectionBox(min_point, max_point)
            self._bounds[self.dimensions[0]] = SelectionGroup(selection)
            translator_version = self.translation_manager.get_version(
                "bedrock", (999, 999, 999))
            self._platform = translator_version.platform
            self._version = translator_version.version_number
            blocks_array: numpy.ndarray = numpy.array(
                [[b.value for b in layer]
                 for layer in mcstructure["structure"]["block_indices"]],
                dtype=numpy.int32,
            ).reshape((len(mcstructure["structure"]["block_indices"]),
                       *selection.shape))

            palette_key = list(mcstructure["structure"]["palette"].keys())[
                0]  # find a way to do this based on user input
            block_palette = list(mcstructure["structure"]["palette"]
                                 [palette_key]["block_palette"])

            if -1 in blocks_array[0]:
                blocks_array[0][blocks_array[0] == -1] = len(block_palette)
                block_palette.append(
                    amulet_nbt.TAG_Compound({
                        "name":
                        amulet_nbt.TAG_String("minecraft:structure_void"),
                        "states":
                        amulet_nbt.TAG_Compound(),
                        "version":
                        amulet_nbt.TAG_Int(17694723),  # 1.13.0
                    }))

            for cx, cz in selection.chunk_locations():
                chunk_box = SelectionBox.create_chunk_box(
                    cx, cz).intersection(selection)
                array_slice = (slice(None), ) + chunk_box.create_moved_box(
                    selection.min, subtract=True).slice
                chunk_blocks_: numpy.ndarray = blocks_array[array_slice]
                chunk_palette_indexes, chunk_blocks = numpy.unique(
                    chunk_blocks_.reshape((chunk_blocks_.shape[0], -1)).T,
                    return_inverse=True,
                    axis=0,
                )
                chunk_blocks = chunk_blocks.reshape(chunk_blocks_.shape[1:])

                chunk_palette = numpy.empty(len(chunk_palette_indexes),
                                            dtype=object)
                for palette_index, indexes in enumerate(chunk_palette_indexes):
                    chunk_palette[palette_index] = tuple(block_palette[index]
                                                         for index in indexes
                                                         if index >= 0)

                self._chunks[(cx, cz)] = (
                    chunk_box,
                    chunk_blocks,
                    chunk_palette,
                    [],
                    [],
                )

            block_entities = {
                int(key): val["block_entity_data"]
                for key, val in mcstructure["structure"]["palette"]
                [palette_key]["block_position_data"].items()
                if "block_entity_data" in val
            }
            for location, block_entity in block_entities.items():
                if all(key in block_entity for key in "xyz"):
                    x, y, z = (
                        block_entity["x"].value,
                        block_entity["y"].value,
                        block_entity["z"].value,
                    )
                    cx, cz = x >> 4, z >> 4
                    if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(
                            cx, cz)][0]:
                        self._chunks[(cx, cz)][3].append(block_entity)

            entities = list(mcstructure["structure"]["entities"])
            for entity in entities:
                if "Pos" in entity:
                    x, y, z = (
                        entity["Pos"][0].value,
                        entity["Pos"][1].value,
                        entity["Pos"][2].value,
                    )
                    cx, cz = numpy.floor([x, z]).astype(int) >> 4
                    if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(
                            cx, cz)][0]:
                        self._chunks[(cx, cz)][4].append(entity)

        else:
            raise Exception(
                f"mcstructure file with format_version=={mcstructure['format_version'].value} cannot be read"
            )
示例#24
0
 def __init__(self, path: str = None):
     self._path: Optional[str] = path
     self._level_dat_version = 8
     super().__init__(nbt.TAG_Compound(), "")
     if path is not None and os.path.isfile(path):
         self.load_from(path)
示例#25
0
    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)
示例#26
0
    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
示例#27
0
    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}"
            )
示例#28
0
    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)
示例#29
0
    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
示例#30
0
    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)