예제 #1
0
    def test_is_rectangular(self):
        box_1 = SelectionBox((0, 0, 0), (5, 5, 5))
        box_2 = SelectionBox((0, 5, 0), (5, 10, 5))
        box_3 = SelectionBox((0, 0, 5), (5, 5, 10))
        box_4 = SelectionBox((0, 5, 5), (5, 10, 10))

        self.assertTrue(SelectionGroup(box_1).is_rectangular)
        self.assertTrue(SelectionGroup((box_1, box_2)).is_rectangular)
        self.assertFalse(SelectionGroup((box_1, box_2, box_3)).is_rectangular)
        self.assertTrue(SelectionGroup((box_1, box_2, box_3, box_4)).is_rectangular)
        self.assertTrue(
            SelectionGroup((box_1, box_2, box_3, box_4, box_2)).is_rectangular
        )
예제 #2
0
 def _on_input_press(self, evt: InputPressEvent):
     if evt.action_id == ACT_BOX_CLICK:
         if not self._editing:
             self._press_time = time.time()
             self._editing = True
             self._start_box = self._pointer.bounds
             self._update_pointer()
     elif evt.action_id == ACT_DESELECT_ALL_BOXES:
         self._selection.selection_group = SelectionGroup()
         self._create_undo()
     elif evt.action_id == ACT_DELESECT_BOX:
         if ACT_DESELECT_ALL_BOXES not in self.canvas.buttons.pressed_actions:
             self._selection.selection_group = SelectionGroup()
             self._create_undo()
     evt.Skip()
예제 #3
0
    def get_chunk_boxes(
        self,
        selection: Union[SelectionGroup, SelectionBox],
        dimension: Dimension,
        create_missing_chunks=False,
    ) -> Generator[Tuple[Chunk, SelectionBox], None, None]:
        """Given a selection will yield chunks and SubSelectionBoxes into that chunk

        :param selection: SelectionGroup or SelectionBox into the world
        :param dimension: The dimension to take effect in (defaults to overworld)
        :param create_missing_chunks: If a chunk does not exist an empty one will be created (defaults to false)
        """

        if isinstance(selection, SelectionBox):
            selection = SelectionGroup([selection])
        selection: SelectionGroup
        for (cx, cz), box in selection.sub_sections(self.sub_chunk_size):
            try:
                chunk = self.get_chunk(cx, cz, dimension)
            except ChunkDoesNotExist:
                if create_missing_chunks:
                    chunk = Chunk(cx, cz)
                    self.put_chunk(chunk, dimension)
                else:
                    continue
            except ChunkLoadError:
                continue

            yield chunk, box
예제 #4
0
    def set_selection_corners(
        self,
        selection_corners: Tuple[Tuple[Tuple[int, int, int], Tuple[int, int,
                                                                   int]], ...],
    ):
        """Set the minimum and maximum points of each selection
        Note this method will not trigger the history logic.
        You may instead want the selection_corners setter method.

        :param selection_corners: The minimum and maximum points of each selection
        :return:
        """
        selections = []
        for points in selection_corners:
            if len(points) == 2 and all(len(point) == 3 for point in points):
                selections.append(
                    tuple(tuple(int(p) for p in point) for point in points))
            else:
                log.error(
                    f"selection_corners must be of the format Tuple[Tuple[Tuple[int, int, int], Tuple[int, int, int]], ...]"
                )

        self.changed = True
        self._selection_corners = tuple(selections)
        self._selection_group = SelectionGroup(
            [SelectionBox(*box) for box in self._selection_corners])
        wx.PostEvent(self._canvas(), SelectionChangeEvent())
예제 #5
0
        def test_replace_single_block(self):
            subbox1 = SelectionBox((1, 70, 3), (5, 71, 6))
            box1 = SelectionGroup((subbox1, ))

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            replace(
                self.world,
                OVERWORLD,
                box1,
                {
                    "original_blocks": [
                        Block.from_string_blockstate(
                            "universal_minecraft:granite[polished=false]")
                    ],
                    "replacement_blocks": [
                        Block.from_string_blockstate(
                            "universal_minecraft:stone")
                    ],
                },
            )
            self.world.create_undo_point()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.redo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )
예제 #6
0
    def __init__(self, canvas: "EditCanvas"):
        super().__init__()
        self._selection_corners: Tuple[BoxType, ...] = ()
        self._selection_group: SelectionGroup = SelectionGroup()
        self._canvas = weakref.ref(canvas)

        self._timer = wx.Timer(canvas)
예제 #7
0
        def test_replace_single_block(self):
            subbox1 = SelectionBox((1, 70, 3), (5, 71, 6))
            box1 = SelectionGroup((subbox1,))

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.run_operation(
                replace,
                "overworld",
                box1,
                {
                    "original_blocks": [
                        blockstate_to_block(
                            "universal_minecraft:granite[polished=false]"
                        )
                    ],
                    "replacement_blocks": [
                        blockstate_to_block("universal_minecraft:stone")
                    ],
                },
            )

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.redo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )
예제 #8
0
    def get_coord_box(
        self,
        dimension: Dimension,
        selection: Union[SelectionGroup, SelectionBox, None] = None,
        yield_missing_chunks=False,
    ) -> Generator[Tuple[ChunkCoordinates, SelectionBox], None, None]:
        """Given a selection will yield chunk coordinates and `SelectionBox`es into that chunk
        If not given a selection will use the bounds of the object.

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

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

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

                if selection.intersects(box):
                    chunk_selection = selection.intersection(box)
                    for sub_box in chunk_selection.selection_boxes:
                        yield (cx, cz), sub_box
예제 #9
0
    def test_sorted_iterator(self):
        box_1 = SelectionBox((0, 0, 0), (4, 4, 4))
        box_2 = SelectionBox((7, 7, 7), (10, 10, 10))
        box_3 = SelectionBox((15, 15, 15), (20, 20, 20))

        selection_1 = SelectionGroup(
            (
                box_1,
                box_2,
                box_3,
            )
        )
        selection_2 = SelectionGroup(
            (
                box_1,
                box_3,
                box_2,
            )
        )

        self.assertEqual(selection_1, selection_2)
예제 #10
0
 def test_subtract(self):
     box_1 = SelectionGroup(
         SelectionBox(
             (0, 0, 0),
             (32, 32, 32),
         )
     )
     box_2 = SelectionGroup(
         SelectionBox(
             (0, 0, 0),
             (16, 16, 16),
         )
     )
     box_3 = box_1.subtract(box_2)
     box_4 = SelectionGroup(
         (
             SelectionBox((0, 16, 0), (32, 32, 32)),
             SelectionBox((0, 0, 16), (32, 16, 32)),
             SelectionBox((16, 0, 0), (32, 16, 16)),
         )
     )
     self.assertEqual(box_3, box_4)
예제 #11
0
 def _sanitise_selection(self, selection: Union[SelectionGroup,
                                                SelectionBox, None],
                         dimension: Dimension) -> SelectionGroup:
     if isinstance(selection, SelectionBox):
         return SelectionGroup(selection)
     elif isinstance(selection, SelectionGroup):
         return selection
     elif selection is None:
         return self.bounds(dimension)
     else:
         raise ValueError(
             f"Expected SelectionBox, SelectionGroup or None. Got {selection}"
         )
예제 #12
0
        def test_fill_operation(self):
            subbox_1 = SelectionBox((1, 70, 3), (5, 71, 5))
            selection = SelectionGroup((subbox_1, ))

            # Start sanity check
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )
            # End sanity check

            generator_unpacker(
                fill(
                    self.world,
                    OVERWORLD,
                    selection,
                    Block.from_string_blockstate("universal_minecraft:stone"),
                ))
            self.world.create_undo_point()

            for x, y, z in selection.blocks:
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(x, y, z, OVERWORLD).blockstate,
                    f"Failed at coordinate ({x},{y},{z})",
                )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )

            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.redo()

            for x, y, z in selection.blocks:
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(x, y, z, OVERWORLD).blockstate,
                    f"Failed at coordinate ({x},{y},{z})",
                )
예제 #13
0
 def _clean_selection(self, selection: SelectionGroup) -> SelectionGroup:
     if self.multi_selection:
         return selection
     else:
         if selection:
             return SelectionGroup(
                 sorted(
                     selection.selection_boxes,
                     reverse=True,
                     key=lambda b: b.volume,
                 )[0])
         else:
             raise ObjectReadError(
                 "A single selection was required but none were given.")
예제 #14
0
        def test_delete_chunk(self):
            subbox1 = SelectionBox((1, 1, 1), (5, 5, 5))
            box1 = SelectionGroup((subbox1, ))

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            generator_unpacker(delete_chunk(self.world, OVERWORLD, box1))
            self.world.create_undo_point()

            with self.assertRaises(ChunkDoesNotExist):
                _ = self.world.get_block(1, 70, 3, OVERWORLD).blockstate

            self.assertEqual(
                0,
                len([
                    x for x in self.world.get_chunk_slice_box(
                        OVERWORLD, subbox1)
                ]),
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.redo()

            with self.assertRaises(ChunkDoesNotExist):
                _ = self.world.get_block(1, 70, 3, OVERWORLD).blockstate

            self.assertEqual(
                0,
                len([
                    x for x in self.world.get_chunk_slice_box(
                        OVERWORLD, subbox1)
                ]),
            )
예제 #15
0
        def test_get_blocks(self):
            selection_box = SelectionBox((0, 0, 0), (10, 10, 10))
            for selection in [SelectionGroup([selection_box]), selection_box]:
                chunk, box = next(self.world.get_chunk_boxes(selection, "overworld"))
                self.assertIsInstance(chunk, Chunk)
                self.assertIsInstance(box, SelectionBox)

                chunk, slices, _ = next(
                    self.world.get_chunk_slices(selection, "overworld")
                )
                self.assertIsInstance(chunk, Chunk)
                self.assertIsInstance(slices, tuple)
                for s in slices:
                    self.assertIsInstance(s, slice)
예제 #16
0
        def test_delete_chunk(self):
            subbox1 = SelectionBox((1, 1, 1), (5, 5, 5))
            box1 = SelectionGroup((subbox1, ))

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.run_operation(delete_chunk, "overworld", box1)

            with self.assertRaises(ChunkDoesNotExist):
                _ = self.world.get_block(1, 70, 3, "overworld").blockstate

            self.assertEqual(
                0,
                len([
                    x for x in self.world.get_chunk_slice_box(
                        "overworld", subbox1)
                ]),
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.redo()

            with self.assertRaises(ChunkDoesNotExist):
                _ = self.world.get_block(1, 70, 3, "overworld").blockstate

            self.assertEqual(
                0,
                len([
                    x for x in self.world.get_chunk_slice_box(
                        "overworld", subbox1)
                ]),
            )
예제 #17
0
 def _on_input_release(self, evt: InputReleaseEvent):
     if evt.action_id == ACT_BOX_CLICK:
         if self._editing and time.time() - self._press_time > 0.1:
             self._editing = False
             box = SelectionBox(*self._get_editing_selection())
             if SelectionGroup(box).is_subset(
                     self._selection.selection_group):
                 # subtract
                 self._selection.selection_group = (
                     self._selection.selection_group.subtract(box))
             else:
                 self._selection.selection_group = (
                     self._selection.selection_group.union(box))
             self._create_undo()
     evt.Skip()
예제 #18
0
        def test_fill_operation(self):
            subbox_1 = SelectionBox((1, 70, 3), (5, 71, 5))
            box = SelectionGroup((subbox_1,))

            # Start sanity check
            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )
            # End sanity check

            self.world.run_operation(
                fill, "overworld", box, blockstate_to_block("universal_minecraft:stone")
            )

            for x, y, z in box:
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(x, y, z, "overworld").blockstate,
                    f"Failed at coordinate ({x},{y},{z})",
                )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )

            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.redo()

            for x, y, z in box:
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(x, y, z, "overworld").blockstate,
                    f"Failed at coordinate ({x},{y},{z})",
                )
예제 #19
0
        def test_clone_operation(self):
            subbx1 = SelectionBox((1, 70, 3), (2, 71, 4))
            src_box = SelectionGroup((subbx1, ))

            target = {"x": 1, "y": 70, "z": 5}

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, OVERWORLD).blockstate,
            )  # Sanity check
            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            clone(self.world, OVERWORLD, src_box, target)
            self.world.create_undo_point()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.redo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:granite[polished=false]",
                self.world.get_block(1, 70, 5, OVERWORLD).blockstate,
            )
예제 #20
0
    def __init__(self, path: PathOrBuffer, mode: str = "r"):
        super().__init__(path)
        assert mode in ("r", "w"), 'Mode must be either "r" or "w".'
        self._mode = mode
        if isinstance(path, str):
            assert path.endswith(".construction"), 'Path must end with ".construction"'
            if mode == "r":
                assert os.path.isfile(path), "File specified does not exist."
        self._data: Optional[Union[ConstructionWriter, ConstructionReader]] = None
        self._open = False
        self._platform = "java"
        self._version = (1, 15, 2)
        self._selection: SelectionGroup = SelectionGroup()

        # used to look up which sections are in a given chunk when loading
        self._chunk_to_section: Optional[Dict[Tuple[int, int], List[int]]] = None

        # used to look up which selection boxes intersect a given chunk (boxes are clipped to the size of the chunk)
        self._chunk_to_box: Optional[Dict[Tuple[int, int], List[SelectionBox]]] = None
예제 #21
0
        def test_clone_operation(self):
            subbx1 = SelectionBox((1, 70, 3), (2, 71, 4))
            src_box = SelectionGroup((subbx1,))

            target = {"x": 1, "y": 70, "z": 5}

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )  # Sanity check
            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.run_operation(clone, "overworld", src_box, target)

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.undo()

            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.redo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )

            self.world.undo()

            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 5, "overworld").blockstate,
            )
예제 #22
0
 def _chunkify_selection(self):
     selections = []
     for box in self.canvas.selection.selection_group.selection_boxes:
         min_point = (
             numpy.floor(box.min_array / self.canvas.world.sub_chunk_size) *
             self.canvas.world.sub_chunk_size)
         max_point = (
             numpy.ceil(box.max_array / self.canvas.world.sub_chunk_size) *
             self.canvas.world.sub_chunk_size)
         bounds = self.canvas.world.bounds(self.canvas.dimension)
         min_point[1] = bounds.min[1]
         max_point[1] = bounds.max[1]
         selections.append(SelectionBox(min_point, max_point))
     selection_group = SelectionGroup(selections)
     if selection_group != self.canvas.selection.selection_group:
         # if the above code modified the selection
         self.canvas.selection.selection_group = selection_group
         # this will indirectly update the renderer by updating the global selection
     elif selection_group != self._selection.selection_group:
         # if the above code did not change the selection but it does not match the renderer
         self._selection.selection_group = selection_group
예제 #23
0
        def test_get_entities(
            self,
        ):  # TODO: Make a more complete test once we figure out what get_entities() returns
            box1 = SelectionGroup((SelectionBox((0, 0, 0), (17, 20, 17)), ))

            test_entity = {
                "id": "universal_minecraft:cow",
                "CustomName": "TestName",
                "Pos": [1.0, 4.0, 1.0],
            }

            entity_iter = self.world.get_entities_in_box(box1)
            for chunk_coords, entities in entity_iter:
                self.assertEqual(0, len(entities))

            self.world.add_entities([test_entity])

            entity_iter = self.world.get_entities_in_box(box1)
            for chunk_coords, entities in entity_iter:
                if chunk_coords == (0, 0):
                    self.assertEqual(1, len(entities))
                    test_entity["Pos"] = entities[0]["Pos"] = [
                        17.0, 20.0, 17.0
                    ]
                else:
                    self.assertEqual(0, len(entities))

            entity_iter = self.world.get_entities_in_box(box1)
            for chunk_coords, entities in entity_iter:
                if chunk_coords == (1, 1):
                    self.assertEqual(1, len(entities))
                else:
                    self.assertEqual(0, len(entities))

            self.world.delete_entities([test_entity])

            entity_iter = self.world.get_entities_in_box(box1)
            for chunk_coords, entities in entity_iter:
                self.assertEqual(0, len(entities))
예제 #24
0
    def selection_group(self, selection_group: SelectionGroup):
        """Set the selection group of the static and active selection.
        This will only trigger a grapical change and will not update the global selection.
        A call to push_selection is required to push the updated selection to the global selection."""
        self._escape()
        if len(selection_group) == 0:
            # unload the active selection
            if self._active_selection is not None:
                self._unload_active_selection()

        if len(selection_group) <= 1:
            # unload the selection group
            self._selection.selection_group = SelectionGroup()

        if len(selection_group) >= 1:
            # load the active selection
            self._create_active_selection()
            self._active_selection.selection_box = selection_group[-1]

        if len(selection_group) >= 2:
            # load the selection group
            self._selection.selection_group = selection_group[:-1]
예제 #25
0
    def open(self):
        """Open the database for reading and writing"""
        if self._open:
            return
        if self._mode == "r":
            assert (
                isinstance(self.path_or_buffer, str)
                and os.path.isfile(self.path_or_buffer)
            ) or hasattr(self.path_or_buffer, "read"), "File specified does not exist."
            self._data = ConstructionReader(self.path_or_buffer)
            self._platform = self._data.source_edition
            self._version = self._data.source_version
            self._selection = SelectionGroup(
                [
                    SelectionBox((minx, miny, minz), (maxx, maxy, maxz))
                    for minx, miny, minz, maxx, maxy, maxz in self._data.selection
                ]
            )

            self._chunk_to_section = {}
            for index, (x, _, z, _, _, _, _, _) in enumerate(self._data.sections):
                cx = x >> 4
                cz = z >> 4
                self._chunk_to_section.setdefault((cx, cz), []).append(index)
        else:
            self._data = ConstructionWriter(
                self.path_or_buffer,
                self.platform,
                self.version,
                [box.bounds for box in self.selection.selection_boxes],
            )
            self._chunk_to_box = {}
            for box in self.selection.selection_boxes:
                for cx, cz in box.chunk_locations():
                    self._chunk_to_box.setdefault((cx, cz), []).append(
                        box.intersection(SelectionBox.create_chunk_box(cx, cz))
                    )
        self._open = True
예제 #26
0
    def _shallow_load(self):
        if os.path.isfile(self.path):
            with open(self.path, "rb") as f:
                magic_num_1 = f.read(magic_num_len)
                if magic_num_1 == magic_num:
                    format_version = struct.unpack(">B", f.read(1))[0]
                    if format_version == 0:
                        f.seek(-magic_num_len, os.SEEK_END)
                        magic_num_2 = f.read(magic_num_len)
                        if magic_num_2 == magic_num:
                            f.seek(-magic_num_len - INT_STRUCT.size,
                                   os.SEEK_END)
                            metadata_end = f.tell()
                            metadata_start = INT_STRUCT.unpack(
                                f.read(INT_STRUCT.size))[0]
                            f.seek(metadata_start)

                            metadata = amulet_nbt.load(
                                f.read(metadata_end - metadata_start),
                                compressed=True,
                            )

                            self._platform = metadata["export_version"][
                                "edition"].value
                            self._version = tuple(
                                map(
                                    lambda v: v.value,
                                    metadata["export_version"]["version"],
                                ))

                            selection_boxes = (
                                metadata["selection_boxes"].value.reshape(
                                    -1, 6).tolist())
                            self._bounds[self.dimensions[0]] = SelectionGroup([
                                SelectionBox((minx, miny, minz),
                                             (maxx, maxy, maxz)) for minx,
                                miny, minz, maxx, maxy, maxz in selection_boxes
                            ])
예제 #27
0
    def _test_create(
        self,
        cls: Type[FormatWrapper],
        level_name: str,
        platform: str,
        version: VersionNumberAny,
    ):
        path = clean_temp_world(level_name)

        # create, initialise and save the level
        level = cls(path)
        if level.requires_selection:
            level.create_and_open(
                platform,
                version,
                SelectionGroup([SelectionBox((0, 0, 0), (1, 1, 1))]),
                overwrite=True,
            )
        else:
            level.create_and_open(platform, version, overwrite=True)

        self.assertTrue(level.is_open,
                        "The level was not opened by create_and_open()")
        self.assertTrue(level.has_lock,
                        "The lock was not acquired by create_and_open()")

        platform_ = level.platform
        version_ = level.version
        dimension_selections = {
            dim: level.bounds(dim)
            for dim in level.dimensions
        }

        level.save()
        level.close()

        self.assertFalse(level.is_open, "The level was not closed by close()")
        self.assertFalse(level.has_lock, "The lock was not lost by close()")

        self.assertTrue(os.path.exists(level.path))

        # reopen the level
        level2 = load_format(path)
        # check that the class is the same
        self.assertIs(level.__class__, level2.__class__)
        level2.open()

        self.assertTrue(level2.is_open, "The level was not opened by open()")
        self.assertTrue(level2.has_lock, "The lock was not acquired by open()")

        # check that the platform and version are the same
        self.assertEqual(level2.platform, platform_)
        self.assertEqual(level2.version, version_)
        self.assertEqual(set(level2.dimensions), set(dimension_selections))
        for dim, selection in dimension_selections.items():
            self.assertEqual(level2.bounds(dim), selection)
        level2.close()

        self.assertFalse(level2.is_open, "The level was not closed by close()")
        self.assertFalse(level2.has_lock, "The lock was not lost by close()")

        self.assertTrue(os.path.exists(level.path))

        level = cls(path)
        with self.assertRaises(ObjectWriteError):
            if level.requires_selection:
                level.create_and_open(
                    platform,
                    version,
                    SelectionGroup([SelectionBox((0, 0, 0), (1, 1, 1))]),
                )
            else:
                level.create_and_open(platform, version)

        clean_path(path)
예제 #28
0
        def test_replace_multiblock(self):
            subbox1 = SelectionBox((1, 70, 3), (2, 75, 4))
            box1 = SelectionGroup((subbox1,))

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            for y in range(71, 75):
                self.assertEqual(
                    "universal_minecraft:air",
                    self.world.get_block(1, y, 3, "overworld").blockstate,
                    f"Failed at coordinate (1,{y},3)",
                )

            self.world.run_operation(
                replace,
                "overworld",
                box1,
                {
                    "original_blocks": [
                        blockstate_to_block("universal_minecraft:stone"),
                        blockstate_to_block("universal_minecraft:air"),
                    ],
                    "replacement_blocks": [
                        blockstate_to_block(
                            "universal_minecraft:granite[polished=false]"
                        ),
                        blockstate_to_block("universal_minecraft:stone"),
                    ],
                },
            )

            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )

            for y in range(71, 75):
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(1, y, 3, "overworld").blockstate,
                    f"Failed at coordinate (1,{y},3)",
                )

            self.world.undo()

            self.assertEqual(
                "universal_minecraft:stone",
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )
            for y in range(71, 75):
                self.assertEqual(
                    "universal_minecraft:air",
                    self.world.get_block(1, y, 3, "overworld").blockstate,
                    f"Failed at coordinate (1,{y},3)",
                )

            self.world.redo()

            self.assertEqual(
                'universal_minecraft:granite[polished="false"]',
                self.world.get_block(1, 70, 3, "overworld").blockstate,
            )

            for y in range(71, 75):
                self.assertEqual(
                    "universal_minecraft:stone",
                    self.world.get_block(1, y, 3, "overworld").blockstate,
                    f"Failed at coordinate (1,{y},3)",
                )
예제 #29
0
 def selection_group(self) -> SelectionGroup:
     return SelectionGroup(self.selection_box)
예제 #30
0
    def open_from(self, f: BinaryIO):
        f = BytesIO(f.read())
        magic_num_1 = f.read(8)
        assert magic_num_1 == magic_num, f"This file is not a construction file."
        self._format_version = struct.unpack(">B", f.read(1))[0]
        if self._format_version == 0:
            f.seek(-magic_num_len, os.SEEK_END)
            magic_num_2 = f.read(8)
            assert (
                magic_num_2 == magic_num
            ), "It looks like this file is corrupt. It probably wasn't saved properly"

            f.seek(-magic_num_len - INT_STRUCT.size, os.SEEK_END)
            metadata_end = f.tell()
            metadata_start = INT_STRUCT.unpack(f.read(INT_STRUCT.size))[0]
            f.seek(metadata_start)

            metadata = amulet_nbt.load(
                f.read(metadata_end - metadata_start),
                compressed=True,
            )

            try:
                self._platform = metadata["export_version"]["edition"].value
                self._version = tuple(
                    map(lambda v: v.value, metadata["export_version"]["version"])
                )
            except KeyError as e:
                raise KeyError(f'Missing export version identifying key "{e.args[0]}"')

            self._section_version = metadata["section_version"].value

            palette = unpack_palette(metadata["block_palette"])

            selection_boxes = metadata["selection_boxes"].value.reshape(-1, 6).tolist()

            self._selection = SelectionGroup(
                [
                    SelectionBox((minx, miny, minz), (maxx, maxy, maxz))
                    for minx, miny, minz, maxx, maxy, maxz in selection_boxes
                ]
            )

            self._populate_chunk_to_box()

            section_index_table = (
                metadata["section_index_table"].value.view(SECTION_ENTRY_TYPE).tolist()
            )

            if self._section_version == 0:
                for (
                    start_x,
                    start_y,
                    start_z,
                    shape_x,
                    shape_y,
                    shape_z,
                    position,
                    length,
                ) in section_index_table:
                    f.seek(position)
                    nbt_obj = amulet_nbt.load(f.read(length))
                    if nbt_obj["blocks_array_type"].value == -1:
                        blocks = None
                        block_entities = None
                    else:
                        blocks = numpy.reshape(
                            nbt_obj["blocks"].value, (shape_x, shape_y, shape_z)
                        )
                        block_entities = parse_block_entities(nbt_obj["block_entities"])

                    start = numpy.array([start_x, start_y, start_z])
                    chunk_index: numpy.ndarray = start // self.sub_chunk_size
                    shape = numpy.array([shape_x, shape_y, shape_z])
                    if numpy.any(shape <= 0):
                        continue  # skip sections with zero size
                    if numpy.any(
                        start + shape > (chunk_index + 1) * self.sub_chunk_size
                    ):
                        log.error(
                            f"section in construction file did not fit in one sub-chunk. Start: {start}, Shape: {shape}"
                        )
                    cx, cy, cz = chunk_index.tolist()
                    self._chunk_to_section.setdefault((cx, cz), []).append(
                        ConstructionSection(
                            (start_x, start_y, start_z),
                            (shape_x, shape_y, shape_z),
                            blocks,
                            palette,
                            parse_entities(nbt_obj["entities"]),
                            block_entities,
                        )
                    )
            else:
                raise Exception(
                    f"This wrapper does not support any construction section version higher than {max_section_version}"
                )

        else:
            raise Exception(
                f"This wrapper does not support any construction format version higher than {max_format_version}"
            )