def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, Union[int, Tuple[int, int, int]]], boxes: List[SelectionBox] = None, ) -> List[ConstructionSection]: sections = [] for box in boxes: cx, cz = chunk.cx, chunk.cz for cy in box.chunk_y_locations(): sub_box = box.intersection( SelectionBox.create_sub_chunk_box(cx, cy, cz)) entities = [e for e in chunk.entities if e.location in sub_box] if cy in chunk.blocks: sections.append( ConstructionSection( sub_box.min, sub_box.shape, chunk.blocks[sub_box.chunk_slice(cx, cz)], list(palette), entities, [ e for e in chunk.block_entities if e.location in sub_box ], )) elif entities: sections.append( ConstructionSection(sub_box.min, sub_box.shape, None, [], entities, [])) return sections
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, )
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())
def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, Union[int, Tuple[int, int, int]]], box: SelectionBox = None, ) -> SchematicChunk: entities = [] for e in chunk.entities: if e.location in box: entities.append( self._encode_entity(e, self._entity_id_type, self._entity_coord_type).value) block_entities = [] for e in chunk.block_entities: if e.location in box: block_entities.append( self._encode_block_entity( e, self._block_entity_id_type, self._block_entity_coord_type).value) slices = box.create_moved_box((chunk.cx * 16, 0, chunk.cz * 16), subtract=True).slice blocks_merged = palette[chunk.blocks[slices]] return SchematicChunk( box, blocks_merged[:, :, :, 0].astype(numpy.uint16), blocks_merged[:, :, :, 1].astype(numpy.uint8), block_entities, entities, )
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, )
def _sub_chunks( self, blocks: Blocks ) -> List[Tuple[numpy.ndarray, Tuple[int, int, int]]]: """Create sub-chunk arrays that extend into the neighbour sub-chunks by one block. :param blocks: The Blocks array for the chunk. :return: A list of tuples containing the larger block array and the location of the sub-chunk """ sub_chunks = [] neighbour_chunks = {} for dx, dz in ((-1, 0), (1, 0), (0, -1), (0, 1)): try: neighbour_chunks[(dx, dz)] = self._level.get_chunk( self.cx + dx, self.cz + dz, self.dimension ).blocks except ChunkLoadError: continue for cy in blocks.sub_chunks: sub_chunk = blocks.get_sub_chunk(cy) larger_blocks = numpy.zeros( sub_chunk.shape + numpy.array((2, 2, 2)), sub_chunk.dtype ) sub_chunk_box = SelectionBox.create_sub_chunk_box(self.cx, cy, self.cz) if self._level.selection_bounds.intersects(sub_chunk_box): boxes = self._level.selection_bounds.intersection(sub_chunk_box) for box in boxes.selection_boxes: larger_blocks[1:-1, 1:-1, 1:-1][ box.sub_chunk_slice(self.cx, cy, self.cz) ] = sub_chunk[box.sub_chunk_slice(self.cx, cy, self.cz)] for chunk_offset, neighbour_blocks in neighbour_chunks.items(): if cy not in neighbour_blocks: continue if chunk_offset == (-1, 0): larger_blocks[0, 1:-1, 1:-1] = neighbour_blocks.get_sub_chunk( cy )[-1, :, :] elif chunk_offset == (1, 0): larger_blocks[-1, 1:-1, 1:-1] = neighbour_blocks.get_sub_chunk( cy )[0, :, :] elif chunk_offset == (0, -1): larger_blocks[1:-1, 1:-1, 0] = neighbour_blocks.get_sub_chunk( cy )[:, :, -1] elif chunk_offset == (0, 1): larger_blocks[1:-1, 1:-1, -1] = neighbour_blocks.get_sub_chunk( cy )[:, :, 0] if cy - 1 in blocks: larger_blocks[1:-1, 0, 1:-1] = blocks.get_sub_chunk(cy - 1)[ :, -1, : ] if cy + 1 in blocks: larger_blocks[1:-1, -1, 1:-1] = blocks.get_sub_chunk(cy + 1)[ :, 0, : ] sub_chunks.append((larger_blocks, (0, cy * 16, 0))) return sub_chunks
def test_single_block_box(self): box_1 = SelectionBox((0, 0, 0), (1, 1, 2)) self.assertEqual((1, 1, 2), box_1.shape) self.assertEqual(2, len([x for x in box_1])) self.assertIn((0, 0, 0), box_1) self.assertNotIn((1, 1, 2), box_1)
def _chunk_box(self, cx: int, cz: int, sub_chunk_size: Optional[int] = None): """Get a SelectionBox containing the whole of a given chunk""" if sub_chunk_size is None: sub_chunk_size = self.sub_chunk_size return SelectionBox.create_chunk_box(cx, cz, sub_chunk_size)
def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: VersionIdentifierType, box: SelectionBox, ) -> MCStructureChunk: """ Take a version-specific chunk and encode it to raw data for the format to store. :param chunk: The already translated version-specfic chunk to encode. :param palette: The block_palette the ids in the chunk correspond to. :type palette: numpy.ndarray[Block] :param max_world_version: The key to use to find the encoder. :param box: The volume of the chunk to pack. :return: Raw data to be stored by the Format. """ entities = [] for e in chunk.entities: if e.location in box: entities.append( self._encode_entity(e, self._entity_id_type, self._entity_coord_type).value) block_entities = [] for e in chunk.block_entities: if e.location in box: block_entities.append( self._encode_block_entity( e, self._block_entity_id_type, self._block_entity_coord_type).value) slices = box.create_moved_box((chunk.cx * 16, 0, chunk.cz * 16), subtract=True).slice out_palette = numpy.empty(palette.shape, dtype=object) for index, block_layers in enumerate(palette): blocks_out = [] for version, block in block_layers: block = amulet_nbt.TAG_Compound({ "name": amulet_nbt.TAG_String( f"{block.namespace}:{block.base_name}"), "states": amulet_nbt.TAG_Compound(block.properties), }) if version: block["version"] = amulet_nbt.TAG_Int(version) blocks_out.append(block) out_palette[index] = blocks_out return MCStructureChunk( box, numpy.asarray(chunk.blocks[slices]), out_palette, block_entities, entities, )
def _sub_chunks( self, blocks: Blocks ) -> List[Tuple[numpy.ndarray, numpy.ndarray, Tuple[int, int, int]]]: sub_chunks = [] neighbour_chunks = {} for dx, dz in ((-1, 0), (1, 0), (0, -1), (0, 1)): try: neighbour_chunks[(dx, dz)] = self._level.get_chunk( self.cx + dx, self.cz + dz, self.dimension).blocks except ChunkLoadError: continue for cy in blocks.sub_chunks: sub_chunk = blocks.get_sub_chunk(cy) larger_blocks = numpy.zeros( sub_chunk.shape + numpy.array((2, 2, 2)), sub_chunk.dtype) sub_chunk_box = SelectionBox.create_sub_chunk_box( self.cx, cy, self.cz) if self._level.selection_bounds.intersects(sub_chunk_box): boxes = self._level.selection_bounds.intersection( sub_chunk_box) for box in boxes.selection_boxes: larger_blocks[1:-1, 1:-1, 1:-1][box.sub_chunk_slice( self.cx, cy, self.cz)] = sub_chunk[box.sub_chunk_slice( self.cx, cy, self.cz)] for chunk_offset, neighbour_blocks in neighbour_chunks.items(): if cy not in neighbour_blocks: continue if chunk_offset == (-1, 0): larger_blocks[0, 1:-1, 1:-1] = neighbour_blocks.get_sub_chunk( cy)[-1, :, :] elif chunk_offset == (1, 0): larger_blocks[-1, 1:-1, 1:-1] = neighbour_blocks.get_sub_chunk( cy)[0, :, :] elif chunk_offset == (0, -1): larger_blocks[1:-1, 1:-1, 0] = neighbour_blocks.get_sub_chunk( cy)[:, :, -1] elif chunk_offset == (0, 1): larger_blocks[1:-1, 1:-1, -1] = neighbour_blocks.get_sub_chunk( cy)[:, :, 0] if cy - 1 in blocks: larger_blocks[1:-1, 0, 1:-1] = blocks.get_sub_chunk(cy - 1)[:, -1, :] if cy + 1 in blocks: larger_blocks[1:-1, -1, 1:-1] = blocks.get_sub_chunk(cy + 1)[:, 0, :] unique_blocks = numpy.unique(larger_blocks) sub_chunks.append( (larger_blocks, unique_blocks, (0, cy * 16, 0))) return sub_chunks
def _encode( self, chunk: Chunk, chunk_palette: AnyNDArray, interface: SchematicInterface, ): return interface.encode( chunk, chunk_palette, self.max_world_version, SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection( self._selection ), )
def load_level_from_world(level_folder, coord_box): # World is a amulet data structure world = load_level(level_folder) coord_start, coord_end = coord_box box = SelectionBox(coord_start, coord_end) # Level is a np array of Blocks coord_offset = np.array(box.min) print("Offset: {}".format(coord_offset)) level = level_from_world(world, box) signs = get_signs(world, box, coord_offset) return level, signs
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)
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(".schematic"), 'Path must end with ".schematic"' if mode == "r": assert os.path.isfile(path), "File specified does not exist." self._data: Optional[Union[SchematicWriter, SchematicReader]] = None self._open = False self._platform = "java" self._version = (1, 12, 2) self._selection: SelectionBox = SelectionBox((0, 0, 0), (0, 0, 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)
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})", )
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
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)
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) ]), )
def _encode( self, interface: MCStructureInterface, chunk: Chunk, dimension: Dimension, chunk_palette: AnyNDArray, ): return interface.encode( chunk, chunk_palette, self.max_world_version, SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection( self._bounds[dimension].to_box()), )
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) ]), )
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()
def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, Union[int, Tuple[int, int, int]]], box: SelectionBox = None, ) -> MCStructureChunk: entities = [] for e in chunk.entities: if e.location in box: entities.append( self._encode_entity( e, self._entity_id_type, self._entity_coord_type ).value ) block_entities = [] for e in chunk.block_entities: if e.location in box: block_entities.append( self._encode_block_entity( e, self._block_entity_id_type, self._block_entity_coord_type ).value ) slices = box.create_moved_box( (chunk.cx * 16, 0, chunk.cz * 16), subtract=True ).slice out_palette = numpy.empty(palette.shape, dtype=object) for index, block_layers in enumerate(palette): blocks_out = [] for version, block in block_layers: block = amulet_nbt.TAG_Compound( { "name": amulet_nbt.TAG_String( f"{block.namespace}:{block.base_name}" ), "states": amulet_nbt.TAG_Compound(block.properties), } ) if version: block["version"] = amulet_nbt.TAG_Int(version) blocks_out.append(block) out_palette[index] = blocks_out return MCStructureChunk( box, chunk.blocks[slices], out_palette, block_entities, entities )
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})", )
def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: VersionIdentifierType, box: SelectionBox, ) -> SchematicChunk: """ Take a version-specific chunk and encode it to raw data for the format to store. :param chunk: The already translated version-specfic chunk to encode. :param palette: The block_palette the ids in the chunk correspond to. :type palette: numpy.ndarray[Block] :param max_world_version: The key to use to find the encoder. :param box: The volume of the chunk to pack. :return: Raw data to be stored by the Format. """ entities = [] for e in chunk.entities: if e.location in box: entities.append( self._encode_entity( e, self._entity_id_type, self._entity_coord_type ).value ) block_entities = [] for e in chunk.block_entities: if e.location in box: block_entities.append( self._encode_block_entity( e, self._block_entity_id_type, self._block_entity_coord_type ).value ) slices = box.create_moved_box( (chunk.cx * 16, 0, chunk.cz * 16), subtract=True ).slice blocks_merged = palette[chunk.blocks[slices]] return SchematicChunk( box, blocks_merged[:, :, :, 0].astype(numpy.uint16), blocks_merged[:, :, :, 1].astype(numpy.uint8), block_entities, entities, )
def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: VersionIdentifierType, boxes: List[SelectionBox], ) -> List[ConstructionSection]: """ Take a version-specific chunk and encode it to raw data for the format to store. :param chunk: The already translated version-specfic chunk to encode. :param palette: The block_palette the ids in the chunk correspond to. :type palette: numpy.ndarray[Block] :param max_world_version: The key to use to find the encoder. :param boxes: The volume(s) of the chunk to pack. :return: Raw data to be stored by the Format. """ sections = [] for box in boxes: cx, cz = chunk.cx, chunk.cz for cy in box.chunk_y_locations(): sub_box = box.intersection( SelectionBox.create_sub_chunk_box(cx, cy, cz)) entities = [e for e in chunk.entities if e.location in sub_box] if cy in chunk.blocks: sections.append( ConstructionSection( sub_box.min, sub_box.shape, numpy.asarray(chunk.blocks[sub_box.chunk_slice( cx, cz)]), list(palette), entities, [ e for e in chunk.block_entities if e.location in sub_box ], )) elif entities: sections.append( ConstructionSection(sub_box.min, sub_box.shape, None, [], entities, [])) return sections
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
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, )
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, )
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