Exemple #1
0
    def __init__(self,
                 directory: str,
                 format_wrapper: "FormatWrapper",
                 temp_dir: str = None):
        self._directory = directory
        if temp_dir is None:
            self._temp_directory = get_temp_dir(self._directory)
        else:
            self._temp_directory = temp_dir

        self._level_wrapper = format_wrapper
        self.level_wrapper.open()

        self._block_palette = BlockManager()
        self._block_palette.get_add_block(
            UniversalAirBlock)  # ensure that index 0 is always air

        self._biome_palette = BiomeManager()
        self._biome_palette.get_add_biome("universal_minecraft:plains")

        self._history_manager = MetaHistoryManager()

        self._chunks: ChunkManager = ChunkManager(
            os.path.join(self._temp_directory, "chunks"), self)

        self.history_manager.register(self._chunks, True)
Exemple #2
0
    def __init__(self,
                 directory: str,
                 world_wrapper: "WorldFormatWrapper",
                 temp_dir: str = None):
        self._directory = directory
        if temp_dir is None:
            self._temp_directory = get_temp_dir(self._directory)
        else:
            self._temp_directory = temp_dir

        self._world_wrapper = world_wrapper
        self._world_wrapper.open()

        self._block_palette = BlockManager()
        self._block_palette.get_add_block(
            Block(namespace="universal_minecraft",
                  base_name="air"))  # ensure that index 0 is always air

        self._biome_palette = BiomeManager()
        self._biome_palette.get_add_biome("universal_minecraft:plains")

        self._history_manager = MetaHistoryManager()

        self._chunks: ChunkManager = ChunkManager(
            os.path.join(self._temp_directory, "chunks"), self)

        self._history_manager.register(self._chunks, True)
Exemple #3
0
    def __init__(
        self, directory: str, world_wrapper: "WorldFormatWrapper", temp_dir: str = None
    ):
        self._directory = directory
        if temp_dir is None:
            self._temp_directory = get_temp_dir(self._directory)
        else:
            self._temp_directory = temp_dir

        self._world_wrapper = world_wrapper
        self._world_wrapper.open()

        self._block_palette = BlockManager()
        self._block_palette.get_add_block(
            Block(namespace="universal_minecraft", base_name="air")
        )  # ensure that index 0 is always air

        self._biome_palette = BiomeManager()

        self._chunk_cache: ChunkCache = {}
        shutil.rmtree(self._temp_directory, ignore_errors=True)
        self._chunk_history_manager = ChunkHistoryManager(
            os.path.join(self._temp_directory, "chunks")
        )
        self._needs_undo_point: bool = False
Exemple #4
0
    def _unpack_blocks(
        translation_manager: "TranslationManager",
        version_identifier: VersionIdentifierType,
        chunk: Chunk,
        block_palette: AnyNDArray,
    ):
        """
        Unpack the version-specific block_palette into the stringified version where needed.

        :return: The block_palette converted to block objects.
        """
        version = translation_manager.get_version(*version_identifier)
        for index, block in enumerate(block_palette):
            block: Block
            if version.block.is_waterloggable(block.namespaced_name):
                properties = block.properties
                if "waterlogged" in properties:
                    waterlogged = properties["waterlogged"]
                    del properties["waterlogged"]
                    block = Block(
                        namespace=block.namespace,
                        base_name=block.base_name,
                        properties=properties,
                    )
                else:
                    waterlogged = amulet_nbt.TAG_String("false")

                if waterlogged == amulet_nbt.TAG_String("true"):
                    block_palette[index] = block + water
                else:
                    block_palette[index] = block
            elif version.block.is_waterloggable(block.namespaced_name, True):
                block_palette[index] = block + water

        chunk._block_palette = BlockManager(block_palette)
    def _operation(self, world: "World", dimension: Dimension,
                   selection: SelectionGroup):
        path = self._file_picker.GetPath()

        if (isinstance(path, str) and path.endswith(".mcstructure")
                and os.path.isfile(path)):
            wrapper = MCStructureFormatWrapper(path, "r")
            wrapper.translation_manager = world.translation_manager
            wrapper.open()
            selection = wrapper.selection

            global_palette = BlockManager()
            chunks = {}
            chunk_count = len(list(wrapper.all_chunk_coords()))
            yield 0, f"Importing {os.path.basename(path)}"
            for chunk_index, (cx, cz) in enumerate(wrapper.all_chunk_coords()):
                try:
                    chunk = chunks[(cx, cz)] = wrapper.load_chunk(cx, cz)
                    chunk.block_palette = global_palette
                except ChunkLoadError:
                    pass
                yield (chunk_index + 1) / chunk_count

            wrapper.close()
            self.canvas.paste(Structure(chunks, global_palette, selection))
        else:
            raise OperationError(
                "Please specify a mcstructure file in the options before running."
            )
    def __init__(self, path: str, format_wrapper: api_wrapper.FormatWrapper):
        """
        Construct a :class:`BaseLevel` object from the given data.

        This should not be used directly. You should instead use :func:`amulet.load_level`.

        :param path: The path to the data being loaded. May be a file or directory. If blank there is no data on disk associated with this.
        :param format_wrapper: The :class:`FormatWrapper` instance that the level will wrap around.
        """
        self._path = path
        self._prefix = str(hash((self._path, time.time())))

        self._level_wrapper = format_wrapper
        self.level_wrapper.open()

        self._block_palette = BlockManager()
        self._block_palette.get_add_block(
            UniversalAirBlock)  # ensure that index 0 is always air

        self._biome_palette = BiomeManager()
        self._biome_palette.get_add_biome("universal_minecraft:plains")

        self._history_manager = MetaHistoryManager()

        self._chunks: ChunkManager = ChunkManager(self._prefix, self)
        self._players = PlayerManager(self)

        self.history_manager.register(self._chunks, True)
        self.history_manager.register(self._players, True)
Exemple #7
0
    def _convert_to_save(
        self,
        chunk: "Chunk",
        chunk_version: VersionNumberAny,
        translator: "Translator",
        recurse: bool = True,
    ) -> "Chunk":
        """Convert the Chunk in Universal format to a Chunk in the version specific format."""
        # create a new streamlined block block_palette and remap the data
        palette: List[numpy.ndarray] = []
        palette_len = 0
        for cy in chunk.blocks.sub_chunks:
            sub_chunk_palette, sub_chunk = numpy.unique(
                chunk.blocks.get_sub_chunk(cy), return_inverse=True)
            chunk.blocks.add_sub_chunk(
                cy,
                sub_chunk.astype(numpy.uint32).reshape(
                    (16, 16, 16)) + palette_len)
            palette_len += len(sub_chunk_palette)
            palette.append(sub_chunk_palette)

        if palette:
            chunk_palette, lut = numpy.unique(numpy.concatenate(palette),
                                              return_inverse=True)
            for cy in chunk.blocks.sub_chunks:
                chunk.blocks.add_sub_chunk(
                    cy,
                    lut.astype(numpy.uint32)[chunk.blocks.get_sub_chunk(cy)])
            chunk._block_palette = BlockManager(
                numpy.vectorize(
                    chunk.block_palette.__getitem__)(chunk_palette))
        else:
            chunk._block_palette = BlockManager()

        def get_chunk_callback(_: int, __: int) -> "Chunk":
            # conversion from universal should not require any data outside the block
            return chunk

        # translate from universal format to version format
        return translator.from_universal(
            chunk_version,
            self.translation_manager,
            chunk,
            get_chunk_callback,
            recurse,
        )
 def from_world(cls, world: World, selection: SelectionGroup,
                dimension: Dimension):
     data = {}
     block_palette = BlockManager()
     for chunk, _ in world.get_chunk_boxes(selection, dimension):
         if chunk.coordinates not in data:
             data[chunk.coordinates] = chunk = copy.deepcopy(chunk)
             chunk.block_palette = block_palette
     return cls(data, block_palette, copy.deepcopy(selection),
                world.chunk_size)
Exemple #9
0
 def _unpack_blocks(
     translation_manager: "TranslationManager",
     version_identifier: VersionIdentifierType,
     chunk: Chunk,
     block_palette: AnyNDArray,
 ):
     """
     Unpack the version-specific block_palette into the stringified version where needed.
     :return: The block_palette converted to block objects.
     """
     chunk._block_palette = BlockManager(block_palette)
Exemple #10
0
    def __init__(self, cx: int, cz: int):
        self._cx, self._cz = cx, cz
        self._changed = False
        self._changed_time = 0.0

        self._blocks = None
        self.__block_palette = BlockManager()
        self.__biome_palette = BiomeManager()
        self._biomes = None
        self._entities = EntityList()
        self._block_entities = BlockEntityDict()
        self._status = Status(self)
        self.misc = {}  # all entries that are not important enough to get an attribute
Exemple #11
0
    def _unpack_blocks(
        translation_manager: "TranslationManager",
        version_identifier: VersionIdentifierType,
        chunk: Chunk,
        block_palette: AnyNDArray,
    ):
        """
        Unpacks an object array of block data into a numpy object array containing Block objects.
        :param translation_manager:
        :param version_identifier:
        :param chunk:
        :param block_palette:
        :type block_palette: numpy.ndarray[
            Tuple[
                Union[
                    Tuple[None, Tuple[int, int]],
                    Tuple[None, Block],
                    Tuple[int, Block]
                ], ...
            ]
        ]
        :return:
        """
        palette_ = BlockManager()
        for palette_index, entry in enumerate(block_palette):
            entry: BedrockInterfaceBlockType
            block = None
            for version_number, b in entry:
                version_number: Optional[int]
                if isinstance(b, tuple):
                    version = translation_manager.get_version(
                        version_identifier[0], version_number or 17563649)
                    b = version.block.ints_to_block(*b)
                elif isinstance(b, Block):
                    if version_number is not None:
                        properties = b.properties
                        properties["__version__"] = amulet_nbt.TAG_Int(
                            version_number)
                        b = Block(b.namespace, b.base_name, properties,
                                  b.extra_blocks)
                else:
                    raise Exception(f"Unsupported type {b}")
                if block is None:
                    block = b
                else:
                    block += b
            if block is None:
                raise Exception(f"Empty tuple")

            palette_.get_add_block(block)
        chunk._block_palette = palette_
Exemple #12
0
    def setUp(self):
        self.manager = BlockManager()

        initial_dirt = blockstate_to_block("minecraft:dirt")
        initial_stone = blockstate_to_block("minecraft:stone")
        initial_granite = blockstate_to_block("minecraft:granite")

        initial_dirt_water = initial_dirt + blockstate_to_block("minecraft:water")

        # Partially populate the manager
        self.manager.get_add_block(initial_dirt)
        self.manager.get_add_block(initial_stone)
        self.manager.get_add_block(initial_granite)
        self.manager.get_add_block(initial_dirt_water)
Exemple #13
0
 def _unpack(
     self,
     translator: "Translator",
     game_version: VersionNumberAny,
     chunk: "Chunk",
     chunk_palette: AnyNDArray,
 ) -> "Chunk":
     palette = chunk._block_palette = BlockManager()
     lut = numpy.array([palette.get_add_block(block) for block in chunk_palette])
     if len(palette.blocks()) != len(chunk_palette):
         # if a blockstate was defined twice
         for cy in chunk.blocks.sub_chunks:
             chunk.blocks.add_sub_chunk(cy, lut[chunk.blocks.get_sub_chunk(cy)])
     return chunk
 def _unpack_blocks(
     translation_manager: "TranslationManager",
     version_identifier: VersionIdentifierType,
     chunk: Chunk,
     block_palette: AnyNDArray,
 ):
     """
     Unpacks an int array of block ids and block data values [[1, 0], [2, 0]] into a numpy array of Block objects.
     :param version:
     :param block_palette:
     :return:
     """
     version = translation_manager.get_version(*version_identifier)
     chunk._block_palette = BlockManager(
         [version.block.ints_to_block(*entry) for entry in block_palette])
Exemple #15
0
    def __init__(self, cx: int, cz: int):
        """
        Construct an instance of :class:`Chunk` at the given coordinates.

        :param cx: The x coordinate of the chunk (is not the same as block coordinates)
        :param cz: The z coordinate of the chunk (is not the same as block coordinates)
        """
        super().__init__()
        self._cx, self._cz = cx, cz
        self._changed_time = 0.0

        self._blocks = None
        self.__block_palette = BlockManager()
        self.__biome_palette = BiomeManager()
        self._biomes = None
        self._entities = EntityList()
        self._block_entities = BlockEntityDict()
        self._status = Status()
        self._misc = {
        }  # all entries that are not important enough to get an attribute
    def _unpack(
        self,
        translator: "Translator",
        game_version: VersionNumberAny,
        chunk: "Chunk",
        chunk_palette: AnyNDArray,
    ) -> "Chunk":
        version = self.translation_manager.get_version(
            *translator.translator_key(game_version))
        palette = chunk._block_palette = BlockManager()
        lut = numpy.array([
            palette.get_add_block(version.block.ints_to_block(block, data))
            for block, data in chunk_palette
        ])
        if len(palette.blocks()) != len(chunk_palette):
            # if a blockstate was defined twice
            for cy in chunk.blocks.sub_chunks:
                chunk.blocks.add_sub_chunk(cy,
                                           lut[chunk.blocks.get_sub_chunk(cy)])

        return chunk
Exemple #17
0
    def __init__(
        self,
        file_or_buffer: Union[str, IO],
        source_edition: str,
        source_version: INT_TRIPLET,
        selection_boxes: Optional[List[Tuple[int, int, int, int, int,
                                             int]]] = None,
        format_version: int = max_format_version,
        section_version: int = max_section_version,
    ):
        assert (
            format_version <= max_format_version
        ), f"This construction writer does not support format versions above {max_format_version}"
        assert (
            section_version <= max_section_version
        ), f"This construction writer does not support section versions above {max_section_version}"
        self._format_version = format_version
        self._section_version = section_version

        if isinstance(file_or_buffer, str):
            self._buffer = open(file_or_buffer, "wb")
        else:
            assert hasattr(
                file_or_buffer,
                "write"), "Construction file buffer does not have a write mode"
            self._buffer = file_or_buffer

        self._source_edition = source_edition
        self._source_version = source_version

        self._selection_boxes = selection_boxes or []

        self._metadata: Optional[amulet_nbt.NBTFile] = None
        self._section_index_table: List[Tuple[int, int, int, int, int, int,
                                              int, int]] = []
        self._palette: BlockManager = BlockManager()
        self._init_write()
Exemple #18
0
    def _translate(
        chunk: Chunk,
        get_chunk_callback: Optional[GetChunkCallback],
        translate_block: TranslateBlockCallback,
        translate_entity: TranslateEntityCallback,
        full_translate: bool,
    ):
        if full_translate:
            todo = []
            output_block_entities = []
            output_entities = []
            finished = BlockManager()
            palette_mappings = {}

            # translate each block without using the callback
            for i, input_block in enumerate(chunk.block_palette):
                input_block: BlockType
                (
                    output_block,
                    output_block_entity,
                    output_entity,
                    extra,
                ) = translate_block(input_block, None, (0, 0, 0))
                if extra and get_chunk_callback:
                    todo.append(i)
                elif output_block is not None:
                    palette_mappings[i] = finished.get_add_block(output_block)
                    if output_block_entity is not None:
                        for cy in chunk.blocks.sub_chunks:
                            for x, y, z in zip(
                                *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
                            ):
                                output_block_entities.append(
                                    output_block_entity.new_at_location(
                                        x + chunk.cx * 16,
                                        y + cy * 16,
                                        z + chunk.cz * 16,
                                    )
                                )
                else:
                    # TODO: this should only happen if the object is an entity, set the block to air
                    pass

                if output_entity and entity_support:
                    for cy in chunk.blocks.sub_chunks:
                        for x, y, z in zip(
                            *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
                        ):
                            x += chunk.cx * 16
                            y += cy * 16
                            z += chunk.cz * 16
                            for entity in output_entity:
                                e = copy.deepcopy(entity)
                                e.location += (x, y, z)
                                output_entities.append(e)

            # re-translate the blocks that require extra information
            block_mappings = {}
            for index in todo:
                for cy in chunk.blocks.sub_chunks:
                    for x, y, z in zip(
                        *numpy.where(chunk.blocks.get_sub_chunk(cy) == index)
                    ):
                        y += cy * 16

                        def get_block_at(
                            pos: Tuple[int, int, int]
                        ) -> Tuple[Block, Optional[BlockEntity]]:
                            """Get a block at a location relative to the current block"""
                            nonlocal x, y, z, chunk, cy

                            # calculate position relative to chunk base
                            dx, dy, dz = pos
                            dx += x
                            dy += y
                            dz += z

                            abs_x = dx + chunk.cx * 16
                            abs_y = dy
                            abs_z = dz + chunk.cz * 16

                            # calculate relative chunk position
                            cx = dx // 16
                            cz = dz // 16
                            if cx == 0 and cz == 0:
                                # if it is the current chunk
                                block = chunk.block_palette[chunk.blocks[dx, dy, dz]]
                                return (
                                    block,
                                    chunk.block_entities.get((abs_x, abs_y, abs_z)),
                                )

                            # if it is in a different chunk
                            local_chunk = get_chunk_callback(cx, cz)
                            block = local_chunk.block_palette[
                                local_chunk.blocks[dx % 16, dy, dz % 16]
                            ]
                            return (
                                block,
                                local_chunk.block_entities.get((abs_x, abs_y, abs_z)),
                            )

                        input_block = chunk.block_palette[chunk.blocks[x, y, z]]
                        (
                            output_block,
                            output_block_entity,
                            output_entity,
                            _,
                        ) = translate_block(
                            input_block,
                            get_block_at,
                            (x + chunk.cx * 16, y, z + chunk.cz * 16),
                        )
                        if output_block is not None:
                            block_mappings[(x, y, z)] = finished.get_add_block(
                                output_block
                            )
                            if output_block_entity is not None:
                                output_block_entities.append(
                                    output_block_entity.new_at_location(
                                        x + chunk.cx * 16, y, z + chunk.cz * 16
                                    )
                                )
                        else:
                            # TODO: set the block to air
                            pass

                        if output_entity and entity_support:
                            for entity in output_entity:
                                e = copy.deepcopy(entity)
                                e.location += (x, y, z)
                                output_entities.append(e)

            if entity_support:
                for entity in chunk.entities:
                    output_block, output_block_entity, output_entity = translate_entity(
                        entity
                    )
                    if output_block is not None:
                        block_location = (
                            int(math.floor(entity.x)),
                            int(math.floor(entity.y)),
                            int(math.floor(entity.z)),
                        )
                        block_mappings[block_location] = output_block
                        if output_block_entity:
                            output_block_entities.append(
                                output_block_entity.new_at_location(*block_location)
                            )
                    if output_entity:
                        for e in output_entity:
                            e.location = entity.location
                            output_entities.append(e)

            for cy in chunk.blocks.sub_chunks:
                old_blocks = chunk.blocks.get_sub_chunk(cy)
                new_blocks = numpy.zeros(old_blocks.shape, dtype=old_blocks.dtype)
                for old, new in palette_mappings.items():
                    new_blocks[old_blocks == old] = new
                chunk.blocks.add_sub_chunk(cy, new_blocks)
            for (x, y, z), new in block_mappings.items():
                chunk.blocks[x, y, z] = new
            chunk.block_entities = output_block_entities
            chunk.entities = output_entities
            chunk._block_palette = finished
Exemple #19
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}"
            )