Esempio n. 1
0
class SchematicReader:
    def __init__(self, path_or_buffer: PathOrBuffer):
        if isinstance(path_or_buffer, str):
            assert path_or_buffer.endswith(
                ".schematic"), "File selected is not a .schematic file"
            assert os.path.isfile(
                path_or_buffer
            ), f"There is no schematic file at path {path_or_buffer}"
            schematic = amulet_nbt.load(path_or_buffer)
            assert not all(key in schematic for key in (
                "Version", "Data Version",
                "BlockData")), "This file is not a legacy schematic file."
        else:
            assert hasattr(path_or_buffer,
                           "read"), "Object does not have a read method"
            schematic = amulet_nbt.load(buffer=path_or_buffer)

        materials = schematic.get("Materials", amulet_nbt.TAG_String()).value
        if materials == "Alpha":
            self._platform = "java"
        elif materials == "Pocket":
            self._platform = "bedrock"
        else:
            raise Exception(
                f'"{materials}" is not a supported platform for a schematic file.'
            )
        self._chunks: Dict[ChunkCoordinates,
                           Tuple[SelectionBox, BlockArrayType,
                                 BlockDataArrayType, list, list], ] = {}
        self._selection = SelectionBox(
            (0, 0, 0),
            (
                schematic["Width"].value,
                schematic["Height"].value,
                schematic["Length"].value,
            ),
        )
        entities: amulet_nbt.TAG_List = schematic.get("Entities",
                                                      amulet_nbt.TAG_List())
        block_entities: amulet_nbt.TAG_List = schematic.get(
            "TileEntities", amulet_nbt.TAG_List())
        blocks: numpy.ndarray = schematic["Blocks"].value.astype(
            numpy.uint8).astype(numpy.uint16)
        if "AddBlocks" in schematic:
            add_blocks = schematic["AddBlocks"]
            blocks = blocks + (numpy.concatenate([
                [(add_blocks & 0xF0) >> 4], [add_blocks & 0xF]
            ]).T.ravel().astype(numpy.uint16) << 8)[:blocks.size]
        max_point = self._selection.max
        temp_shape = (max_point[1], max_point[2], max_point[0])
        blocks = numpy.transpose(blocks.reshape(temp_shape),
                                 (2, 0, 1))  # YZX => XYZ
        data = numpy.transpose(schematic["Data"].value.reshape(temp_shape),
                               (2, 0, 1))
        for cx, cz in self._selection.chunk_locations():
            box = SelectionBox(
                (cx * 16, 0, cz * 16),
                (
                    min((cx + 1) * 16, self._selection.size_x),
                    self._selection.size_y,
                    min((cz + 1) * 16, self._selection.size_z),
                ),
            )
            self._chunks[(cx, cz)] = (box, blocks[box.slice], data[box.slice],
                                      [], [])
        for e in block_entities:
            if all(key in e for key in ("x", "y", "z")):
                x, y, z = e["x"].value, e["y"].value, e["z"].value
                if (x, y, z) in self._selection:
                    cx = x >> 4
                    cz = z >> 4
                    self._chunks[(cx, cz)][3].append(e)
        for e in entities:
            if "Pos" in e:
                pos: PointCoordinates = tuple(
                    map(lambda t: t.value, e["Pos"].value))
                if pos in self._selection:
                    cx = int(pos[0]) >> 4
                    cz = int(pos[2]) >> 4
                    self._chunks[(cx, cz)][4].append(e)

    def read(self, cx: int, cz: int):
        if (cx, cz) in self._chunks:
            return SchematicChunk(*self._chunks[(cx, cz)])
        else:
            raise ChunkDoesNotExist

    @property
    def platform(self) -> str:
        return self._platform

    @property
    def selection(self) -> SelectionBox:
        return self._selection

    @property
    def chunk_coords(self) -> Generator[ChunkCoordinates, None, None]:
        yield from self._chunks.keys()

    def close(self):
        pass
Esempio n. 2
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"
            )
Esempio n. 3
0
 def open_from(self, f: BinaryIO):
     schematic = amulet_nbt.load(f)
     if any(key in schematic
            for key in ("Version", "Data Version", "BlockData")):
         raise ObjectReadError("This file is not a legacy schematic file.")
     materials = schematic.get("Materials", amulet_nbt.TAG_String()).value
     if materials == "Alpha":
         self._platform = "java"
         self._version = (1, 12, 2)
     elif materials == "Pocket":
         self._platform = "bedrock"
         self._version = (1, 12, 0)
     else:
         raise Exception(
             f'"{materials}" is not a supported platform for a schematic file.'
         )
     self._chunks = {}
     selection_box = SelectionBox(
         (0, 0, 0),
         (
             schematic["Width"].value,
             schematic["Height"].value,
             schematic["Length"].value,
         ),
     )
     self._selection = SelectionGroup(selection_box)
     entities: amulet_nbt.TAG_List = schematic.get("Entities",
                                                   amulet_nbt.TAG_List())
     block_entities: amulet_nbt.TAG_List = schematic.get(
         "TileEntities", amulet_nbt.TAG_List())
     blocks: numpy.ndarray = (schematic["Blocks"].value.astype(
         numpy.uint8).astype(numpy.uint16))
     if "AddBlocks" in schematic:
         add_blocks = schematic["AddBlocks"]
         blocks = (blocks + (numpy.concatenate([
             [(add_blocks & 0xF0) >> 4], [add_blocks & 0xF]
         ]).T.ravel().astype(numpy.uint16) << 8)[:blocks.size])
     max_point = selection_box.max
     temp_shape = (max_point[1], max_point[2], max_point[0])
     blocks = numpy.transpose(blocks.reshape(temp_shape),
                              (2, 0, 1))  # YZX => XYZ
     data = numpy.transpose(schematic["Data"].value.reshape(temp_shape),
                            (2, 0, 1)).astype(numpy.uint8)
     for cx, cz in selection_box.chunk_locations():
         box = SelectionBox(
             (cx * self.sub_chunk_size, 0, cz * self.sub_chunk_size),
             (
                 min((cx + 1) * self.sub_chunk_size, selection_box.size_x),
                 selection_box.size_y,
                 min((cz + 1) * self.sub_chunk_size, selection_box.size_z),
             ),
         )
         self._chunks[(cx, cz)] = (box, blocks[box.slice], data[box.slice],
                                   [], [])
     for e in block_entities:
         if all(key in e for key in ("x", "y", "z")):
             x, y, z = e["x"].value, e["y"].value, e["z"].value
             if (x, y, z) in selection_box:
                 cx = x >> 4
                 cz = z >> 4
                 self._chunks[(cx, cz)][3].append(e)
     for e in entities:
         if "Pos" in e:
             pos: PointCoordinates = tuple(
                 map(lambda t: float(t.value), e["Pos"].value))
             if pos in selection_box:
                 cx = int(pos[0]) >> 4
                 cz = int(pos[2]) >> 4
                 self._chunks[(cx, cz)][4].append(e)
Esempio n. 4
0
class MCStructureReader:
    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"
            )

    def read(self, cx: int, cz: int):
        if (cx, cz) in self._chunks:
            return MCStructureChunk(*self._chunks[(cx, cz)])
        else:
            raise ChunkDoesNotExist

    @property
    def selection(self) -> SelectionBox:
        return self._selection

    @property
    def chunk_coords(self) -> Generator[ChunkCoordinates, None, None]:
        yield from self._chunks.keys()

    def close(self):
        pass
Esempio n. 5
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."
            )