Esempio n. 1
0
 def close(self):
     self._data["Entities"] = amulet_nbt.TAG_List(self._entities)
     self._data["TileEntities"] = amulet_nbt.TAG_List(self._block_entities)
     self._data["Data"] = amulet_nbt.TAG_Byte_Array(
         numpy.transpose(self._block_data, (1, 2, 0))  # XYZ => YZX
     )
     self._data["Blocks"] = amulet_nbt.TAG_Byte_Array(
         numpy.transpose((self._blocks & 0xFF).astype(numpy.uint8),
                         (1, 2, 0)))
     if numpy.max(self._blocks) > 0xFF:
         add_blocks = numpy.transpose(self._blocks & 0xF00, (1, 2, 0)) >> 8
         self._data["AddBlocks"] = amulet_nbt.TAG_Byte_Array(
             add_blocks[::2] + (add_blocks[1::2] << 4))
     self._data.save_to(self._path_or_buffer)
Esempio n. 2
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
Esempio n. 3
0
 def _exit_write(self):
     """data to be written at close in write mode"""
     if self._format_version == 0:
         metadata_start = self._buffer.tell()
         self._metadata["section_index_table"] = amulet_nbt.TAG_Byte_Array(
             numpy.array(self._section_index_table,
                         dtype=SECTION_ENTRY_TYPE).view(numpy.int8))
         self._metadata["block_palette"] = self._pack_palette()
         self._metadata.save_to(self._buffer)
         self._buffer.write(INT_STRUCT.pack(metadata_start))
         self._buffer.write(magic_num)
     else:
         raise Exception(
             f"This wrapper doesn't support any construction version higher than {max_format_version}"
         )
     self._buffer.close()
Esempio n. 4
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}"
            )
Esempio n. 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
Esempio n. 6
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)
Esempio n. 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."
            )