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
def decode(self, cx: int, cz: int, section: SchematicChunk) -> Tuple["Chunk", AnyNDArray]: chunk = Chunk(cx, cz) block_palette, blocks = numpy.unique( (section.blocks << 4) + (section.data & 0xF), return_inverse=True) blocks = blocks.reshape(section.blocks.shape) palette = numpy.empty(len(block_palette) + 1, dtype=numpy.object) palette[0] = (0, 0) for index, block_num in enumerate(block_palette): palette[index + 1] = (block_num >> 4, block_num & 0xF) box = section.selection.create_moved_box((cx * 16, 0, cz * 16), subtract=True) chunk.blocks[box.slice] = blocks + 1 for b in section.block_entities: b = self._decode_block_entity(b, self._block_entity_id_type, self._block_entity_coord_type) if b is not None: chunk.block_entities.insert(b) for b in section.entities: b = self._decode_entity(b, self._block_entity_id_type, self._block_entity_coord_type) if b is not None: chunk.entities.append(b) return chunk, palette
def decode(self, cx: int, cz: int, data: SpongeSchemChunk) -> Tuple["Chunk", AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: Raw chunk data provided by the format. :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ palette = numpy.empty(len(data.palette) + 1, dtype=object) palette[0] = Block(namespace="minecraft", base_name="air") palette[1:] = data.palette[:] chunk = Chunk(cx, cz) box = data.selection.create_moved_box((cx * 16, 0, cz * 16), subtract=True) chunk.blocks[box.slice] = data.blocks + 1 for b in data.block_entities: b = self._decode_block_entity(b, self._block_entity_id_type, self._block_entity_coord_type) if b is not None: chunk.block_entities.insert(b) for e in data.entities: e = self._decode_entity(e, self._block_entity_id_type, self._block_entity_coord_type) if e is not None: chunk.entities.append(e) return chunk, palette
def decode( self, cx: int, cz: int, section: MCStructureChunk ) -> Tuple["Chunk", AnyNDArray]: palette = numpy.empty(len(section.palette) + 1, dtype=numpy.object) palette[0] = ( ( 17563649, Block( namespace="minecraft", base_name="air", properties={"block_data": amulet_nbt.TAG_Int(0)}, ), ), ) for index, blocks in enumerate(section.palette): block_layers: List[Tuple[Optional[int], Block]] = [] for block in blocks: namespace, base_name = block["name"].value.split(":", 1) if "version" in block: version: Optional[int] = block["version"].value else: version = None if "states" in block: # 1.13 format properties = block["states"].value if version is None: version = 17694720 # 1, 14, 0, 0 else: properties = {"block_data": amulet_nbt.TAG_Int(block["val"].value)} block_layers.append( ( version, Block( namespace=namespace, base_name=base_name, properties=properties, ), ) ) palette[index + 1] = block_layers chunk = Chunk(cx, cz) box = section.selection.create_moved_box((cx * 16, 0, cz * 16), subtract=True) chunk.blocks[box.slice] = section.blocks + 1 for b in section.block_entities: b = self._decode_block_entity( b, self._block_entity_id_type, self._block_entity_coord_type ) if b is not None: chunk.block_entities.insert(b) for b in section.entities: b = self._decode_entity( b, self._block_entity_id_type, self._block_entity_coord_type ) if b is not None: chunk.entities.append(b) return chunk, palette
def create_chunks( world: BaseLevel, dimension: Dimension, selection: SelectionGroup, ): for cx, cz in selection.chunk_locations(): if not world.has_chunk(cx, cz, dimension): world.put_chunk(Chunk(cx, cz), dimension)
def _init_decode(cx: int, cz: int, data: NBTFile) -> Tuple[Chunk, TAG_Compound]: """Get the decode started by creating a chunk object.""" chunk = Chunk(cx, cz) assert isinstance(data.value, TAG_Compound), "Raw data must be a compound." chunk.misc = { # store the chunk data so that any non-versioned data can get saved back "java_chunk_data": data.value } return chunk, data.value
def initialize_world(world: World): minxcoord = -1000 minzcoord = -1000 maxxcoord = 5000 maxzcoord = 7000 minx, minz = block_coords_to_chunk_coords(minxcoord, minzcoord) maxx, maxz = block_coords_to_chunk_coords(maxxcoord, maxzcoord) for x in range(minx, maxx): for z in range(minz, maxz): world.put_chunk(Chunk(x, z))
def create_chunk(self, cx: int, cz: int, dimension: Dimension) -> Chunk: """ Create an empty chunk and put it at the given location. If a chunk exists at the given location it will be overwritten. :param cx: The X coordinate of the chunk :param cz: The Z coordinate of the chunk :param dimension: The dimension to put the chunk in. :return: The newly created :class:`Chunk`. """ chunk = Chunk(cx, cz) self.put_chunk(chunk, dimension) return chunk
def set_block(world: World, x: int, y: int, z: int, block: str): if not (0 <= y <= 255): raise IndexError("The supplied Y coordinate must be between 0 and 255") cx, cz = block_coords_to_chunk_coords(x, z) try: chunk = world.get_chunk(cx, cz) except ChunkDoesNotExist: world.put_chunk(Chunk(cx, cz)) chunk = world.get_chunk(cx, cz) offset_x, offset_z = x - 16 * cx, z - 16 * cz chunk.blocks[offset_x, y, offset_z] = world.palette.get_add_block( Block(namespace="universal_minecraft", base_name=block)) # print("set block x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " to block " + block) chunk.changed = True
def decode(self, cx: int, cz: int, data: List[ConstructionSection]) -> Tuple["Chunk", AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: Raw chunk data provided by the format. :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ chunk = Chunk(cx, cz) palette = [] for section in data: if any(s == 0 for s in section.shape): continue if section.blocks is not None: shapex, shapey, shapez = section.shape sx = section.sx % 16 sy = section.sy sz = section.sz % 16 chunk.blocks[sx:sx + shapex, sy:sy + shapey, sz:sz + shapez, ] = section.blocks.astype( numpy.uint32) + len(palette) chunk.block_entities.update(section.block_entities) chunk.entities.extend(section.entities) palette += section.palette np_palette, inverse = numpy.unique(palette, return_inverse=True) np_palette = numpy.insert( np_palette, 0, Block( namespace="minecraft", base_name="air", properties={"block_data": amulet_nbt.TAG_Int(0)}, ), ) inverse += 1 np_palette: AnyNDArray inverse: numpy.ndarray for cy in chunk.blocks.sub_chunks: chunk.blocks.add_sub_chunk( cy, inverse[chunk.blocks.get_sub_chunk(cy)].astype(numpy.uint32)) return chunk, np_palette
def delete_chunk( world: "BaseLevel", dimension: Dimension, source_box: SelectionGroup, load_original: bool = True, ): chunks = [(cx, cz) for (cx, cz) in source_box.chunk_locations() if world.has_chunk(cx, cz, dimension)] iter_count = len(chunks) for count, (cx, cz) in enumerate(chunks): world.delete_chunk(cx, cz, dimension) if not load_original: # this part is kind of hacky. # Work out a propery API to do this. key = dimension, cx, cz if key not in world.chunks._history_database: world.chunks._register_original_entry(key, Chunk(cx, cz)) yield (count + 1) / iter_count
def decode( self, cx: int, cz: int, data: List[ConstructionSection] ) -> Tuple["Chunk", AnyNDArray]: chunk = Chunk(cx, cz) palette = [] for section in data: if any(s == 0 for s in section.shape): continue if section.blocks is not None: shapex, shapey, shapez = section.shape sx = section.sx - ((section.sx >> 4) << 4) sy = section.sy sz = section.sz - ((section.sz >> 4) << 4) chunk.blocks[ sx : sx + shapex, sy : sy + shapey, sz : sz + shapez, ] = section.blocks.astype(numpy.uint32) + len(palette) chunk.block_entities.update(section.block_entities) chunk.entities.extend(section.entities) palette += section.palette np_palette, inverse = numpy.unique(palette, return_inverse=True) np_palette = numpy.insert( np_palette, 0, Block( namespace="minecraft", base_name="air", properties={"block_data": amulet_nbt.TAG_Int(0)}, ), ) inverse += 1 np_palette: AnyNDArray inverse: numpy.ndarray for cy in chunk.blocks.sub_chunks: chunk.blocks.add_sub_chunk( cy, inverse[chunk.blocks.get_sub_chunk(cy)].astype(numpy.uint32) ) return chunk, np_palette
def decode( self, cx: int, cz: int, data: SchematicChunk ) -> Tuple["Chunk", AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: Raw chunk data provided by the format. :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ chunk = Chunk(cx, cz) block_palette, blocks = numpy.unique( (data.blocks << 4) + (data.data & 0xF), return_inverse=True ) blocks = blocks.reshape(data.blocks.shape) palette = numpy.empty(len(block_palette) + 1, dtype=object) palette[0] = (0, 0) for index, block_num in enumerate(block_palette): palette[index + 1] = (block_num >> 4, block_num & 0xF) box = data.selection.create_moved_box((cx * 16, 0, cz * 16), subtract=True) chunk.blocks[box.slice] = blocks + 1 for b in data.block_entities: b = self._decode_block_entity( b, self._block_entity_id_type, self._block_entity_coord_type ) if b is not None: chunk.block_entities.insert(b) for b in data.entities: b = self._decode_entity( b, self._block_entity_id_type, self._block_entity_coord_type ) if b is not None: chunk.entities.append(b) return chunk, palette
def decode(self, cx: int, cz: int, data: Dict[bytes, bytes]) -> Tuple[Chunk, AnyNDArray]: # chunk_key_base = struct.pack("<ii", cx, cz) chunk = Chunk(cx, cz) chunk_palette = numpy.empty(0, dtype=object) if self.features["terrain"].startswith( "2f"): # ["2farray", "2f1palette", "2fnpalette"] subchunks = [ data.get(b"\x2F" + bytes([i]), None) for i in range(16) ] chunk.blocks, chunk_palette = self._load_subchunks(subchunks) elif self.features["terrain"] == "30array": chunk_data = data.get(b"\x30", None) if chunk_data is not None: block_ids = numpy.frombuffer(chunk_data[:2**15], dtype=numpy.uint8).astype( numpy.uint16) block_data = from_nibble_array( numpy.frombuffer(chunk_data[2**15:2**15 + 2**14], dtype=numpy.uint8)) # there is other data here but we are going to skip over it combined_palette, block_array = fast_unique( numpy.transpose( ((block_ids << 4) + block_data).reshape(16, 16, 128), (0, 2, 1))) chunk.blocks = { i: block_array[:, i * 16:(i + 1) * 16, :] for i in range(8) } palette: AnyNDArray = numpy.array( [combined_palette >> 4, combined_palette & 15]).T chunk_palette = numpy.empty(len(palette), dtype=object) for i, b in enumerate(palette): chunk_palette[i] = ((None, tuple(b)), ) else: raise Exception if self.features["finalised_state"] == "int0-2": if b"\x36" in data: val = struct.unpack("<i", data[b"\x36"])[0] else: val = 2 chunk.status = val if self.features["data_2d"] in [ "height512|biome256", "unused_height512|biome256", ]: d2d = data.get(b"\x2D", b"\x00" * 768) height, biome = d2d[:512], d2d[512:] if self.features["data_2d"] == "height512|biome256": pass # TODO: put this data somewhere chunk.biomes = numpy.frombuffer(biome, dtype="uint8").reshape(16, 16) # TODO: impliment key support # \x2D heightmap and biomes # \x31 block entity # \x32 entity # \x33 ticks # \x34 block extra data # \x35 biome state # \x39 7 ints and an end (03)? Honestly don't know what this is # \x3A fire tick? # \x2E 2d legacy # \x30 legacy terrain # unpack block entities and entities if self.features["block_entities"] == "31list": block_entities = self._unpack_nbt_list(data.get(b"\x31", b"")) chunk.block_entities = self._decode_block_entities(block_entities) if self.features["entities"] == "32list" and amulet.entity_support: entities = self._unpack_nbt_list(data.get(b"\x32", b"")) chunk.entities = self._decode_entities(entities) return chunk, chunk_palette
def decode(self, cx: int, cz: int, data: MCStructureChunk) -> Tuple["Chunk", AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: Raw chunk data provided by the format. :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ palette = numpy.empty(len(data.palette) + 1, dtype=object) palette[0] = (( 17563649, Block( namespace="minecraft", base_name="air", properties={"block_data": amulet_nbt.TAG_Int(0)}, ), ), ) for index, blocks in enumerate(data.palette): block_layers: List[Tuple[Optional[int], Block]] = [] for block in blocks: namespace, base_name = block["name"].value.split(":", 1) if "version" in block: version: Optional[int] = block["version"].value else: version = None if "states" in block: # 1.13 format properties = block["states"].value if version is None: version = 17694720 # 1, 14, 0, 0 else: properties = { "block_data": amulet_nbt.TAG_Int(block["val"].value) } block_layers.append(( version, Block( namespace=namespace, base_name=base_name, properties=properties, ), )) palette[index + 1] = block_layers chunk = Chunk(cx, cz) box = data.selection.create_moved_box((cx * 16, 0, cz * 16), subtract=True) chunk.blocks[box.slice] = data.blocks + 1 for b in data.block_entities: b = self._decode_block_entity(b, self._block_entity_id_type, self._block_entity_coord_type) if b is not None: chunk.block_entities.insert(b) for b in data.entities: b = self._decode_entity(b, self._block_entity_id_type, self._block_entity_coord_type) if b is not None: chunk.entities.append(b) return chunk, palette
import amulet from amulet.api.errors import ChunkLoadError, ChunkDoesNotExist from amulet.api.chunk import Chunk # load the level level = amulet.load_level("level") # create a new chunk at cx=1, cz=2. This is an empty chunk containing no data. new_chunk = Chunk(1, 2) # populate the chunk with any data you want (if any) # add the newly created chunk to the given dimension. level.put_chunk(new_chunk, "minecraft:overworld") # we have changed the chunk data so we need to set this value otherwise it won't get saved. new_chunk.changed = True # save the changes to the world level.save() # close the world level.close()
def decode(self, cx: int, cz: int, data: Dict[bytes, bytes], bounds: Tuple[int, int]) -> Tuple[Chunk, AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: Raw chunk data provided by the format. :param bounds: The minimum and maximum height of the chunk. :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ chunk = Chunk(cx, cz) chunk_palette = numpy.empty(0, dtype=object) chunk.misc = {"bedrock_chunk_data": data} data.pop(b"v", None) data.pop(b",", None) if self._features["terrain"].startswith( "2f"): # ["2farray", "2f1palette", "2fnpalette"] subchunks = {} for key in data.copy().keys(): if len(key) == 2 and key[0:1] == b"\x2F": cy = struct.unpack("b", key[1:2])[0] subchunks[self._chunk_key_to_sub_chunk( cy, bounds[0] >> 4)] = data.pop(key) chunk.blocks, chunk_palette = self._load_subchunks(subchunks) elif self._features["terrain"] == "30array": chunk_data = data.pop(b"\x30", None) if chunk_data is not None: block_ids = numpy.frombuffer(chunk_data[:2**15], dtype=numpy.uint8).astype( numpy.uint16) block_data = from_nibble_array( numpy.frombuffer(chunk_data[2**15:2**15 + 2**14], dtype=numpy.uint8)) # there is other data here but we are going to skip over it combined_palette, block_array = fast_unique( numpy.transpose( ((block_ids << 4) + block_data).reshape(16, 16, 128), (0, 2, 1))) chunk.blocks = { i: block_array[:, i * 16:(i + 1) * 16, :] for i in range(8) } palette: AnyNDArray = numpy.array( [combined_palette >> 4, combined_palette & 15]).T chunk_palette = numpy.empty(len(palette), dtype=object) for i, b in enumerate(palette): chunk_palette[i] = ((None, tuple(b)), ) else: raise Exception if self._features["finalised_state"] == "int0-2": state = data.pop(b"\x36", None) val = 2 if isinstance(state, bytes): if len(state) == 1: # old versions of the game store this as a byte val = struct.unpack("b", state)[0] elif len(state) == 4: # newer versions store it as an int val = struct.unpack("<i", state)[0] chunk.status = val if b"+" in data: height, biome = self._decode_height_3d_biomes( data[b"+"], bounds[0] >> 4) chunk.misc["height"] = height chunk.biomes = biome elif b"\x2D" in data: d2d = data[b"\x2D"] height, biome = ( numpy.frombuffer(d2d[:512], "<i2").reshape((16, 16)), d2d[512:], ) chunk.misc["height"] = height chunk.biomes = numpy.frombuffer(biome, dtype="uint8").reshape(16, 16).T # TODO: implement key support # \x2D heightmap and biomes # \x31 block entity # \x32 entity # \x33 ticks # \x34 block extra data # \x35 biome state # \x39 7 ints and an end (03)? Honestly don't know what this is # \x3A fire tick? # \x2E 2d legacy # \x30 legacy terrain # unpack block entities and entities if self._features["block_entities"] == "31list": block_entities = self._unpack_nbt_list(data.pop(b"\x31", b"")) chunk.block_entities = self._decode_block_entity_list( block_entities) if self._features["entities"] == "32list" and amulet.entity_support: entities = self._unpack_nbt_list(data.pop(b"\x32", b"")) chunk.entities = self._decode_entity_list(entities) return chunk, chunk_palette
def decode(self, cx: int, cz: int, data: amulet_nbt.NBTFile) -> Tuple["Chunk", AnyNDArray]: """ Create an amulet.api.chunk.Chunk object from raw data given by the format. :param cx: chunk x coordinate :param cz: chunk z coordinate :param data: amulet_nbt.NBTFile :return: Chunk object in version-specific format, along with the block_palette for that chunk. """ misc = {} chunk = Chunk(cx, cz) if self.features["last_update"] == "long": misc["last_update"] = (data["Level"].get( "LastUpdate", amulet_nbt.TAG_Long(0)).value) if self.features["status"] in ["j13", "j14"]: chunk.status = data["Level"]["Status"].value else: status = "empty" if (self.features["terrain_populated"] == "byte" and data["Level"].get("TerrainPopulated", amulet_nbt.TAG_Byte()).value): status = "decorated" if (self.features["light_populated"] == "byte" and data["Level"].get("LightPopulated", amulet_nbt.TAG_Byte()).value): status = "postprocessed" chunk.status = status if self.features["V"] == "byte": misc["V"] = data["Level"]["V"].value if self.features["inhabited_time"] == "long": misc["inhabited_time"] = (data["Level"].get( "InhabitedTime", amulet_nbt.TAG_Long(0)).value) if self.features["biomes"] is not None: biomes = data["Level"].get("Biomes", amulet_nbt.TAG_Int_Array()).value if self.features["biomes"] == "256BA": biomes = biomes.astype(numpy.uint8) elif self.features["biomes"] in ["256IA", "1024IA"]: biomes = biomes.astype(numpy.uint32) chunk.biomes = biomes if self.features["height_map"] == "256IA": misc["height_map256IA"] = data["Level"]["HeightMap"].value elif self.features["height_map"] in [ "C|36LA|V1", "C|36LA|V2", "C|36LA|V3", "C|36LA|V4", ]: if "Heightmaps" in data["Level"]: misc["height_mapC|36LA"] = data["Level"]["Heightmaps"] if "Sections" in data["Level"]: if self.features["blocks"] in [ "Sections|(Blocks,Data,Add)", "Sections|(BlockStates,Palette)", ]: chunk.blocks, palette = self._decode_blocks( data["Level"]["Sections"]) else: raise Exception( f'Unsupported block format {self.features["blocks"]}') if self.features["block_light"] == "Sections|2048BA": misc["block_light"] = { section["Y"].value: section["BlockLight"] for section in data["Level"]["Sections"] if "BlockLight" in section } if self.features["sky_light"] == "Sections|2048BA": misc["sky_light"] = { section["Y"].value: section["SkyLight"] for section in data["Level"]["Sections"] if "SkyLight" in section } else: palette = numpy.array( [Block(namespace="minecraft", base_name="air")]) if self.features["entities"] == "list": if amulet.entity_support: chunk.entities = self._decode_entities(data["Level"].get( "Entities", amulet_nbt.TAG_List())) else: misc["java_entities_temp"] = self._decode_entities( data["Level"].get("Entities", amulet_nbt.TAG_List())) if self.features["block_entities"] == "list": chunk.block_entities = self._decode_block_entities( data["Level"].get("TileEntities", amulet_nbt.TAG_List())) if self.features["tile_ticks"] == "list": misc["tile_ticks"] = data["Level"].get("TileTicks", amulet_nbt.TAG_List()) if self.features["liquid_ticks"] == "list": if "LiquidTicks" in data["Level"]: misc["liquid_ticks"] = data["Level"]["LiquidTicks"] if self.features["liquids_to_be_ticked"] == "16list|list": if "LiquidsToBeTicked" in data["Level"]: misc["liquids_to_be_ticked"] = data["Level"][ "LiquidsToBeTicked"] if self.features["to_be_ticked"] == "16list|list": if "ToBeTicked" in data["Level"]: misc["to_be_ticked"] = data["Level"]["ToBeTicked"] if self.features["post_processing"] == "16list|list": if "PostProcessing" in data["Level"]: misc["post_processing"] = data["Level"]["PostProcessing"] if self.features["structures"] == "compound": if "Structures" in data["Level"]: misc["structures"] = data["Level"]["Structures"] chunk.misc = misc return chunk, palette
def create_chunk(self, cx: int, cz: int, dimension: Dimension) -> Chunk: chunk = Chunk(cx, cz) self.put_chunk(chunk, dimension) return chunk
else: print("Not enough arguments given. Format must be:") print("convert <origin_world_path> <destination_world_path>") elif mode == "create": if len(args) >= 4: world_path = args[1] cx, cz = int(args[2]), int(args[3]) print(f"Loading world at {world_path}") world = load_world(world_path) if (cx, cz) in world.world_wrapper.all_chunk_coords(): world.delete_chunk(*(cx, cz)) world.save() chunk = Chunk(cx, cz) bedrock = world.palette.get_add_block( Block(namespace="universal_minecraft", base_name="bedrock") ) stone = world.palette.get_add_block( Block(namespace="universal_minecraft", base_name="stone") ) dirt = world.palette.get_add_block( Block(namespace="universal_minecraft", base_name="dirt") ) grass = world.palette.get_add_block( Block(namespace="universal_minecraft", base_name="grass_block") ) chunk.blocks[:, 0, :] = bedrock chunk.blocks[:, 1:3, :] = stone
def paste_iter( world: "World", dimension: Dimension, structure: Structure, location: BlockCoordinates, scale: FloatTriplet, rotation: FloatTriplet, copy_air=True, copy_water=True, copy_lava=True, ): gab = numpy.vectorize(world.palette.get_add_block, otypes=[numpy.uint32]) lut = gab(structure.palette.blocks()) filtered_mode = not all([copy_air, copy_lava, copy_water]) filtered_blocks = [] if not copy_air: filtered_blocks.append("universal_minecraft:air") if not copy_water: filtered_blocks.append("universal_minecraft:water") if not copy_lava: filtered_blocks.append("universal_minecraft:lava") if filtered_mode: paste_blocks = numpy.array([ any(sub_block.namespaced_name not in filtered_blocks for sub_block in block.block_tuple) for block in structure.palette.blocks() ]) else: paste_blocks = None rotation_point = numpy.floor( (structure.selection.max + structure.selection.min) / 2).astype(int) if any(rotation) or any(s != 1 for s in scale): yield 0, "Rotating!" transformed_structure = yield from structure.transform_iter( scale, rotation) rotation_point = (numpy.matmul( transform_matrix((0, 0, 0), scale, -numpy.radians(numpy.flip(rotation)), "zyx"), numpy.array([*rotation_point, 1]), ).T[:3].round().astype(int)) else: transformed_structure = structure offset = location - rotation_point moved_min_location = transformed_structure.selection.min + offset iter_count = len( list(transformed_structure.get_moved_chunk_slices(moved_min_location))) count = 0 yield 0, "Pasting!" for ( src_chunk, src_slices, src_box, (dst_cx, dst_cz), dst_slices, dst_box, ) in transformed_structure.get_moved_chunk_slices(moved_min_location): try: dst_chunk = world.get_chunk(dst_cx, dst_cz, dimension) except ChunkDoesNotExist: dst_chunk = Chunk(dst_cx, dst_cz) world.put_chunk(dst_chunk, dimension) except ChunkLoadError: continue remove_block_entities = [] for block_entity_location in dst_chunk.block_entities.keys(): if block_entity_location in dst_box: if copy_air: remove_block_entities.append(block_entity_location) else: chunk_block_entity_location = ( numpy.array(block_entity_location) - offset) chunk_block_entity_location[[0, 2]] %= 16 if paste_blocks[src_chunk.blocks[tuple( chunk_block_entity_location)]]: remove_block_entities.append(block_entity_location) for block_entity_location in remove_block_entities: del dst_chunk.block_entities[block_entity_location] for block_entity_location, block_entity in src_chunk.block_entities.items( ): if block_entity_location in src_box: dst_chunk.block_entities.insert( block_entity.new_at_location(*offset + block_entity_location)) if not copy_air: # dst_blocks_copy = dst_chunk.blocks[dst_slices] # mask = paste_blocks[src_chunk.blocks[src_slices]] # dst_blocks_copy[mask] = lut[src_chunk.blocks[src_slices]][mask] dst_blocks_copy = numpy.asarray(dst_chunk.blocks[dst_slices]) mask = paste_blocks[src_chunk.blocks[src_slices]] dst_blocks_copy[mask] = lut[src_chunk.blocks[src_slices]][mask] dst_chunk.blocks[dst_slices] = dst_blocks_copy else: dst_chunk.blocks[dst_slices] = lut[src_chunk.blocks[src_slices]] dst_chunk.changed = True count += 1 yield count / iter_count