Пример #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 _encode_base_entity(
        entity: Union[Entity, BlockEntity],
        id_type: EntityIDType,
        coord_type: EntityCoordType,
    ) -> Optional[amulet_nbt.NBTFile]:
        if not isinstance(entity.nbt, amulet_nbt.NBTFile) and isinstance(
                entity.nbt.value, amulet_nbt.TAG_Compound):
            return
        nbt = entity.nbt

        if id_type == EntityIDType.namespace_str_id:
            nbt["id"] = amulet_nbt.TAG_String(entity.namespaced_name)
        elif id_type == EntityIDType.namespace_str_Id:
            nbt["Id"] = amulet_nbt.TAG_String(entity.namespaced_name)
        elif id_type == EntityIDType.namespace_str_identifier:
            nbt["identifier"] = amulet_nbt.TAG_String(entity.namespaced_name)
        elif id_type == EntityIDType.str_id:
            nbt["id"] = amulet_nbt.TAG_String(entity.base_name)
        elif id_type == EntityIDType.int_id:
            if not entity.base_name.isnumeric():
                return
            nbt["id"] = amulet_nbt.TAG_Int(int(entity.base_name))
        else:
            raise NotImplementedError(f"Entity id type {id_type}")

        if coord_type == EntityCoordType.Pos_list_double:
            nbt["Pos"] = amulet_nbt.TAG_List([
                amulet_nbt.TAG_Double(float(entity.x)),
                amulet_nbt.TAG_Double(float(entity.y)),
                amulet_nbt.TAG_Double(float(entity.z)),
            ])
        elif coord_type == EntityCoordType.Pos_list_float:
            nbt["Pos"] = amulet_nbt.TAG_List([
                amulet_nbt.TAG_Float(float(entity.x)),
                amulet_nbt.TAG_Float(float(entity.y)),
                amulet_nbt.TAG_Float(float(entity.z)),
            ])
        elif coord_type == EntityCoordType.Pos_list_int:
            nbt["Pos"] = amulet_nbt.TAG_List([
                amulet_nbt.TAG_Int(int(entity.x)),
                amulet_nbt.TAG_Int(int(entity.y)),
                amulet_nbt.TAG_Int(int(entity.z)),
            ])
        elif coord_type == EntityCoordType.Pos_array_int:
            nbt["Pos"] = amulet_nbt.TAG_Int_Array(
                [int(entity.x), int(entity.y),
                 int(entity.z)])
        elif coord_type == EntityCoordType.xyz_int:
            nbt["x"] = amulet_nbt.TAG_Int(int(entity.x))
            nbt["y"] = amulet_nbt.TAG_Int(int(entity.y))
            nbt["z"] = amulet_nbt.TAG_Int(int(entity.z))
        else:
            raise NotImplementedError(f"Entity coord type {coord_type}")

        return nbt
Пример #3
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}"
            )
Пример #4
0
    def decode(self, cx: int, cz: int,
               data: amulet_nbt.NBTFile) -> Tuple["Chunk", AnyNDArray]:
        """
        Create an amulet.api.chunk.Chunk object from raw data given by the format.
        :param cx: chunk x coordinate
        :param cz: chunk z coordinate
        :param data: amulet_nbt.NBTFile
        :return: Chunk object in version-specific format, along with the block_palette for that chunk.
        """
        misc = {}
        chunk = Chunk(cx, cz)

        if self.features["last_update"] == "long":
            misc["last_update"] = (data["Level"].get(
                "LastUpdate", amulet_nbt.TAG_Long(0)).value)

        if self.features["status"] in ["j13", "j14"]:
            chunk.status = data["Level"]["Status"].value
        else:
            status = "empty"
            if (self.features["terrain_populated"] == "byte"
                    and data["Level"].get("TerrainPopulated",
                                          amulet_nbt.TAG_Byte()).value):
                status = "decorated"
            if (self.features["light_populated"] == "byte"
                    and data["Level"].get("LightPopulated",
                                          amulet_nbt.TAG_Byte()).value):
                status = "postprocessed"

            chunk.status = status

        if self.features["V"] == "byte":
            misc["V"] = data["Level"]["V"].value

        if self.features["inhabited_time"] == "long":
            misc["inhabited_time"] = (data["Level"].get(
                "InhabitedTime", amulet_nbt.TAG_Long(0)).value)

        if self.features["biomes"] is not None:
            biomes = data["Level"].get("Biomes",
                                       amulet_nbt.TAG_Int_Array()).value
            if self.features["biomes"] == "256BA":
                biomes = biomes.astype(numpy.uint8)
            elif self.features["biomes"] in ["256IA", "1024IA"]:
                biomes = biomes.astype(numpy.uint32)

            chunk.biomes = biomes

        if self.features["height_map"] == "256IA":
            misc["height_map256IA"] = data["Level"]["HeightMap"].value
        elif self.features["height_map"] in [
                "C|36LA|V1",
                "C|36LA|V2",
                "C|36LA|V3",
                "C|36LA|V4",
        ]:
            if "Heightmaps" in data["Level"]:
                misc["height_mapC|36LA"] = data["Level"]["Heightmaps"]

        if "Sections" in data["Level"]:
            if self.features["blocks"] in [
                    "Sections|(Blocks,Data,Add)",
                    "Sections|(BlockStates,Palette)",
            ]:
                chunk.blocks, palette = self._decode_blocks(
                    data["Level"]["Sections"])
            else:
                raise Exception(
                    f'Unsupported block format {self.features["blocks"]}')

            if self.features["block_light"] == "Sections|2048BA":
                misc["block_light"] = {
                    section["Y"].value: section["BlockLight"]
                    for section in data["Level"]["Sections"]
                    if "BlockLight" in section
                }

            if self.features["sky_light"] == "Sections|2048BA":
                misc["sky_light"] = {
                    section["Y"].value: section["SkyLight"]
                    for section in data["Level"]["Sections"]
                    if "SkyLight" in section
                }
        else:
            palette = numpy.array(
                [Block(namespace="minecraft", base_name="air")])

        if self.features["entities"] == "list":
            if amulet.entity_support:
                chunk.entities = self._decode_entities(data["Level"].get(
                    "Entities", amulet_nbt.TAG_List()))
            else:
                misc["java_entities_temp"] = self._decode_entities(
                    data["Level"].get("Entities", amulet_nbt.TAG_List()))

        if self.features["block_entities"] == "list":
            chunk.block_entities = self._decode_block_entities(
                data["Level"].get("TileEntities", amulet_nbt.TAG_List()))

        if self.features["tile_ticks"] == "list":
            misc["tile_ticks"] = data["Level"].get("TileTicks",
                                                   amulet_nbt.TAG_List())

        if self.features["liquid_ticks"] == "list":
            if "LiquidTicks" in data["Level"]:
                misc["liquid_ticks"] = data["Level"]["LiquidTicks"]

        if self.features["liquids_to_be_ticked"] == "16list|list":
            if "LiquidsToBeTicked" in data["Level"]:
                misc["liquids_to_be_ticked"] = data["Level"][
                    "LiquidsToBeTicked"]

        if self.features["to_be_ticked"] == "16list|list":
            if "ToBeTicked" in data["Level"]:
                misc["to_be_ticked"] = data["Level"]["ToBeTicked"]

        if self.features["post_processing"] == "16list|list":
            if "PostProcessing" in data["Level"]:
                misc["post_processing"] = data["Level"]["PostProcessing"]

        if self.features["structures"] == "compound":
            if "Structures" in data["Level"]:
                misc["structures"] = data["Level"]["Structures"]

        chunk.misc = misc

        return chunk, palette
Пример #5
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
Пример #6
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."
            )
Пример #7
0
    def save_to(self, f: BinaryIO):
        if self._schem_version == 1:
            raise SpongeSchemReadError(
                "Sponge Schematic Version 1 is not supported currently.")
        elif self._schem_version == 2:
            selection = self._bounds[self.dimensions[0]].selection_boxes[0]
            if any(s > 2**16 - 1 for s in selection.shape):
                raise SpongeSchemWriteError(
                    "The structure is too large to be exported to a Sponge Schematic file. It must be 2^16 - 1 at most in each dimension."
                )
            overflowed_shape = [
                s if s < 2**15 else s - 2**16 for s in selection.shape
            ]
            data = amulet_nbt.NBTFile(
                amulet_nbt.TAG_Compound({
                    "Version":
                    amulet_nbt.TAG_Int(2),
                    "DataVersion":
                    amulet_nbt.TAG_Int(self._version),
                    "Width":
                    amulet_nbt.TAG_Short(overflowed_shape[0]),
                    "Height":
                    amulet_nbt.TAG_Short(overflowed_shape[1]),
                    "Length":
                    amulet_nbt.TAG_Short(overflowed_shape[2]),
                    "Offset":
                    amulet_nbt.TAG_Int_Array(selection.min),
                }),
                name="Schematic",
            )

            entities = []
            block_entities = []
            blocks = numpy.zeros(selection.shape, dtype=numpy.uint32)
            palette: List[AnyNDArray] = []
            if self._version < 1500:
                raise Exception(
                    "Writing to Sponge Schematic files in pre-1.13 format is not currently supported."
                )
            else:
                arr = numpy.empty(1, dtype=object)
                arr[0] = Block("minecraft", "air")
                palette.append((arr))

            palette_len = 1

            for (
                    selection_,
                    blocks_,
                    palette_,
                    block_entities_,
                    entities_,
            ) in self._chunks.values():
                if selection_.intersects(selection):
                    box = selection_.create_moved_box(selection.min,
                                                      subtract=True)
                    blocks[box.slice] = blocks_ + palette_len
                    palette.append(palette_)
                    palette_len += len(palette_)
                    for be in block_entities_:
                        be = copy.deepcopy(be)
                        be["Pos"] = amulet_nbt.TAG_Int_Array(be["Pos"] -
                                                             selection.min)
                        block_entities.append(be)

                    for e in entities_:
                        e = copy.deepcopy(e)
                        x, y, z = e["Pos"]
                        e["Pos"] = amulet_nbt.TAG_List([
                            amulet_nbt.TAG_Int(x - selection.min_x),
                            amulet_nbt.TAG_Int(y - selection.min_y),
                            amulet_nbt.TAG_Int(z - selection.min_z),
                        ])
                        entities.append(e)

            compact_palette, lut = brute_sort_objects_no_hash(
                numpy.concatenate(palette))
            blocks = numpy.transpose(lut[blocks],
                                     (1, 2, 0)).ravel()  # XYZ => YZX
            block_palette = []
            for index, block in enumerate(compact_palette):
                block: Block
                block_palette.append(block.blockstate)

            data["PaletteMax"] = amulet_nbt.TAG_Int(len(compact_palette))
            data["Palette"] = amulet_nbt.TAG_Compound({
                blockstate: amulet_nbt.TAG_Int(index)
                for index, blockstate in enumerate(block_palette)
            })
            data["BlockData"] = amulet_nbt.TAG_Byte_Array(
                list(encode_array(blocks)))
            if block_entities:
                data["BlockEntities"] = amulet_nbt.TAG_List(block_entities)
            if entities:
                data["Entities"] = amulet_nbt.TAG_List(entities)

            data.save_to(f)
        else:
            raise SpongeSchemReadError(
                f"Sponge Schematic Version {self._schem_version} is not supported currently."
            )