コード例 #1
0
ファイル: base_level.py プロジェクト: Amulet-Team/Amulet-Core
 def _chunk_box(self,
                cx: int,
                cz: int,
                sub_chunk_size: Optional[int] = None):
     """Get a SelectionBox containing the whole of a given chunk"""
     if sub_chunk_size is None:
         sub_chunk_size = self.sub_chunk_size
     return SelectionBox.create_chunk_box(cx, cz, sub_chunk_size)
コード例 #2
0
 def _encode(
     self, chunk: Chunk, chunk_palette: AnyNDArray, interface: SchematicInterface,
 ):
     return interface.encode(
         chunk,
         chunk_palette,
         self.max_world_version,
         SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
             self._selection
         ),
     )
コード例 #3
0
 def _encode(
     self,
     interface: MCStructureInterface,
     chunk: Chunk,
     dimension: Dimension,
     chunk_palette: AnyNDArray,
 ):
     return interface.encode(
         chunk,
         chunk_palette,
         self.max_world_version,
         SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
             self._bounds[dimension].to_box()),
     )
コード例 #4
0
    def get_coord_box(
        self,
        dimension: Dimension,
        selection: Union[SelectionGroup, SelectionBox, None] = None,
        yield_missing_chunks=False,
    ) -> Generator[Tuple[ChunkCoordinates, SelectionBox], None, None]:
        """Given a selection will yield chunk coordinates and `SelectionBox`es into that chunk
        If not given a selection will use the bounds of the object.

        :param selection: SelectionGroup or SelectionBox into the level
        :param dimension: The dimension to take effect in
        :param yield_missing_chunks: If a chunk does not exist an empty one will be created (defaults to false). Use this with care.
        """
        if isinstance(selection, SelectionBox):
            selection = SelectionGroup(selection)
        elif selection is None:
            selection = self.selection_bounds
        elif not isinstance(selection, SelectionGroup):
            raise TypeError(
                f"Expected a SelectionGroup but got {type(selection)}")

        selection: SelectionGroup
        if yield_missing_chunks or selection.footprint_area < 1_000_000:
            if yield_missing_chunks:
                for coord, box in selection.chunk_boxes(self.sub_chunk_size):
                    yield coord, box
            else:
                for (cx,
                     cz), box in selection.chunk_boxes(self.sub_chunk_size):
                    if self.has_chunk(cx, cz, dimension):
                        yield (cx, cz), box

        else:
            # if the selection gets very large iterating over the whole selection and accessing chunks can get slow
            # instead we are going to iterate over the chunks and get the intersection of the selection
            for cx, cz in self.all_chunk_coords(dimension):
                box = SelectionGroup(
                    SelectionBox.create_chunk_box(cx, cz, self.sub_chunk_size))

                if selection.intersects(box):
                    chunk_selection = selection.intersection(box)
                    for sub_box in chunk_selection.selection_boxes:
                        yield (cx, cz), sub_box
コード例 #5
0
    def open(self):
        """Open the database for reading and writing"""
        if self._open:
            return
        if self._mode == "r":
            assert (
                isinstance(self.path_or_buffer, str)
                and os.path.isfile(self.path_or_buffer)
            ) or hasattr(self.path_or_buffer, "read"), "File specified does not exist."
            self._data = ConstructionReader(self.path_or_buffer)
            self._platform = self._data.source_edition
            self._version = self._data.source_version
            self._selection = SelectionGroup(
                [
                    SelectionBox((minx, miny, minz), (maxx, maxy, maxz))
                    for minx, miny, minz, maxx, maxy, maxz in self._data.selection
                ]
            )

            self._chunk_to_section = {}
            for index, (x, _, z, _, _, _, _, _) in enumerate(self._data.sections):
                cx = x >> 4
                cz = z >> 4
                self._chunk_to_section.setdefault((cx, cz), []).append(index)
        else:
            self._data = ConstructionWriter(
                self.path_or_buffer,
                self.platform,
                self.version,
                [box.bounds for box in self.selection.selection_boxes],
            )
            self._chunk_to_box = {}
            for box in self.selection.selection_boxes:
                for cx, cz in box.chunk_locations():
                    self._chunk_to_box.setdefault((cx, cz), []).append(
                        box.intersection(SelectionBox.create_chunk_box(cx, cz))
                    )
        self._open = True
コード例 #6
0
 def _populate_chunk_to_box(self):
     for box in self._selection.selection_boxes:
         for cx, cz in box.chunk_locations():
             self._chunk_to_box.setdefault((cx, cz), []).append(
                 SelectionBox.create_chunk_box(cx, cz).intersection(box)
             )
コード例 #7
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._selection = 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"]
            )

            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(numpy.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"
            )
コード例 #8
0
    def __init__(self, path_or_buffer: Union[str, IO]):
        if isinstance(path_or_buffer, str):
            assert path_or_buffer.endswith(
                ".mcstructure"), "File selected is not a .mcstructure file"
            assert os.path.isfile(
                path_or_buffer
            ), f"There is no mcstructure file at path {path_or_buffer}"
            mcstructure = amulet_nbt.load(path_or_buffer, little_endian=True)
        else:
            assert hasattr(path_or_buffer,
                           "read"), "Object does not have a read method"
            mcstructure = amulet_nbt.load(buffer=path_or_buffer,
                                          little_endian=True)

        self._chunks: Dict[ChunkCoordinates,
                           Tuple[SelectionBox, numpy.ndarray, AnyNDArray,
                                 List[amulet_nbt.TAG_Compound],
                                 List[amulet_nbt.TAG_Compound], ], ] = {}
        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"])
            self._selection = SelectionBox(min_point, max_point)
            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"]),
                       *self._selection.shape))

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

            for cx, cz in self._selection.chunk_locations():
                chunk_box = SelectionBox.create_chunk_box(cx, cz).intersection(
                    self._selection)
                array_slice = (slice(None), ) + chunk_box.create_moved_box(
                    self._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"]["block_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(numpy.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"
            )
コード例 #9
0
    def open_from(self, f: BinaryIO):
        sponge_schem = amulet_nbt.load(f)
        version = sponge_schem.get("Version")
        if not isinstance(version, amulet_nbt.TAG_Int):
            raise SpongeSchemReadError(
                "Version key must exist and be an integer.")
        if version == 1:
            raise SpongeSchemReadError(
                "Sponge Schematic Version 1 is not supported currently.")
        elif version == 2:
            offset = sponge_schem.get("Offset")
            if isinstance(offset,
                          amulet_nbt.TAG_Int_Array) and len(offset) == 3:
                min_point = numpy.array(offset)
            else:
                min_point = numpy.array([0, 0, 0], dtype=numpy.int32)

            size = []
            for key in ("Width", "Height", "Length"):
                val = sponge_schem.get(key)
                if not isinstance(val, amulet_nbt.TAG_Short):
                    raise SpongeSchemReadError(
                        f"Key {key} must exist and be a TAG_Short.")
                # convert to an unsigned short
                val = val.value
                if val < 0:
                    val += 2**16
                size.append(val)

            max_point = min_point + size
            selection = SelectionBox(min_point, max_point)
            self._bounds[self.dimensions[0]] = SelectionGroup(selection)
            data_version = sponge_schem.get("DataVersion")
            if not isinstance(data_version, amulet_nbt.TAG_Int):
                raise SpongeSchemReadError("DataVersion must be a TAG_Int.")
            translator_version = self.translation_manager.get_version(
                "java", int(data_version))
            self._platform = translator_version.platform
            self._version = translator_version.data_version

            packed_block_data = sponge_schem.get("BlockData")
            if not isinstance(packed_block_data, amulet_nbt.TAG_Byte_Array):
                raise SpongeSchemReadError(
                    "BlockData must be a TAG_Byte_Array")

            unpacked_block_data = decode_byte_array(
                numpy.array(packed_block_data, dtype=numpy.uint8))
            if len(unpacked_block_data) != numpy.prod(size):
                raise SpongeSchemReadError(
                    "The data contained in BlockData does not match the size of the schematic."
                )
            dx, dy, dz = selection.shape
            blocks_array: numpy.ndarray = numpy.transpose(
                numpy.array(
                    unpacked_block_data,
                    dtype=numpy.uint32,
                ).reshape((dy, dz, dx)),
                (2, 0, 1),  # YZX => XYZ
            )

            if "Palette" not in sponge_schem:
                raise SpongeSchemReadError(
                    "Amulet is not able to read Sponge Schem files with no block palette."
                )

            palette_data = sponge_schem.get("Palette")
            if not isinstance(palette_data, amulet_nbt.TAG_Compound):
                raise SpongeSchemReadError("Palette must be a TAG_Compound.")

            block_palette: Dict[int, Block] = {}
            for blockstate, index in palette_data.items():
                if index.value in block_palette:
                    raise SpongeSchemReadError(
                        f"Duplicate block index {index} found in the palette.")
                block_palette[index.value] = Block.from_string_blockstate(
                    blockstate)

            if not numpy.all(numpy.isin(blocks_array, list(block_palette))):
                raise SpongeSchemReadError(
                    "Some values in BlockData were not present in Palette")

            for cx, cz in selection.chunk_locations():
                chunk_box = SelectionBox.create_chunk_box(
                    cx, cz).intersection(selection)
                array_slice = 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_,
                    return_inverse=True,
                )
                chunk_blocks = chunk_blocks.reshape(chunk_blocks_.shape)

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

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

            if "BlockEntities" in sponge_schem:
                block_entities = sponge_schem["BlockEntities"]
                if (not isinstance(block_entities, amulet_nbt.TAG_List)
                        or block_entities.list_data_type !=
                        10  # amulet_nbt.TAG_Compound.tag_id
                    ):
                    raise SpongeSchemReadError(
                        "BlockEntities must be a TAG_List of compound tags.")

                for block_entity in block_entities:
                    if "Pos" in block_entity:
                        pos = block_entity["Pos"]
                        if isinstance(
                                pos,
                                amulet_nbt.TAG_Int_Array) and len(pos) == 3:
                            pos = pos + min_point
                            x, y, z = (
                                pos[0],
                                pos[1],
                                pos[2],
                            )
                            block_entity["Pos"] = amulet_nbt.TAG_Int_Array(pos)
                            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)

            if "Entities" in sponge_schem:
                entities = sponge_schem["Entities"]
                if (not isinstance(entities, amulet_nbt.TAG_List)
                        or entities.list_data_type !=
                        10  # amulet_nbt.TAG_Compound.tag_id
                    ):
                    raise SpongeSchemReadError(
                        "Entities must be a TAG_List of compound tags.")

                for entity in entities:
                    if "Pos" in entity:
                        pos = entity["Pos"]
                        if (isinstance(pos, amulet_nbt.TAG_List)
                                and len(pos) == 3 and pos.list_data_type
                                == 6):  # amulet_nbt.TAG_Double.tag_id:
                            x, y, z = (
                                pos[0].value + offset[0],
                                pos[1].value + offset[0],
                                pos[2].value + offset[0],
                            )
                            entity["Pos"] = amulet_nbt.TAG_List([
                                amulet_nbt.TAG_Int(x),
                                amulet_nbt.TAG_Int(y),
                                amulet_nbt.TAG_Int(z),
                            ])
                            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 SpongeSchemReadError(
                f"Sponge Schematic Version {version.value} is not supported currently."
            )