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 test_hash(self): stone = Block.from_string_blockstate("minecraft:stone") water = Block.from_string_blockstate("minecraft:water[level=1]") granite = Block.from_string_blockstate("minecraft:granite") dirt = Block.from_string_blockstate("minecraft:dirt") conglomerate_1 = stone + water + dirt conglomerate_2 = stone + dirt + water self.assertNotEqual(conglomerate_1, conglomerate_2) self.assertNotEqual(hash(conglomerate_1), hash(conglomerate_2)) conglomerate_3 = dirt + water + dirt conglomerate_4 = dirt + dirt + water self.assertNotEqual(conglomerate_3, conglomerate_4) self.assertNotEqual(hash(conglomerate_3), hash(conglomerate_4)) conglomerate_5 = conglomerate_1 + granite conglomerate_6 = conglomerate_3 + granite self.assertNotEqual(conglomerate_1, conglomerate_5) self.assertNotEqual(conglomerate_3, conglomerate_6) self.assertNotEqual(conglomerate_5, conglomerate_6) self.assertNotEqual(hash(conglomerate_5), hash(conglomerate_6))
def test_extra_blocks_immutable(self): stone = Block.from_string_blockstate("minecraft:stone") dirt = Block.from_string_blockstate("minecraft:dirt") stone2 = stone self.assertIs(stone, stone2) stone2 += dirt self.assertIsNot(stone, stone2) stone3 = stone2 self.assertIs(stone2, stone3) stone3 -= dirt self.assertIsNot(stone, stone3)
def test_get_block_from_manager(self): dirt = Block.from_string_blockstate("minecraft:dirt") stone = Block.from_string_blockstate("minecraft:stone") granite = Block.from_string_blockstate("minecraft:granite") water = Block.from_string_blockstate("minecraft:water") dirt_water = dirt + water self.assertEqual(dirt, self.manager[0]) self.assertEqual(stone, self.manager[1]) self.assertEqual(granite, self.manager[2]) self.assertEqual(dirt_water, self.manager[3]) with self.assertRaises(KeyError): brain_coral = Block.from_string_blockstate("minecraft:brain_coral") internal_id = self.manager[brain_coral]
def setUp(self): self.manager = BlockManager() initial_dirt = Block.from_string_blockstate("minecraft:dirt") initial_stone = Block.from_string_blockstate("minecraft:stone") initial_granite = Block.from_string_blockstate("minecraft:granite") initial_dirt_water = initial_dirt + Block.from_string_blockstate( "minecraft:water" ) # Partially populate the manager self.manager.get_add_block(initial_dirt) self.manager.get_add_block(initial_stone) self.manager.get_add_block(initial_granite) self.manager.get_add_block(initial_dirt_water)
def test_get_index_from_manager(self): dirt = Block.from_string_blockstate("minecraft:dirt") stone = Block.from_string_blockstate("minecraft:stone") granite = Block.from_string_blockstate("minecraft:granite") self.assertEqual(0, self.manager[dirt]) self.assertEqual(1, self.manager[stone]) self.assertEqual(2, self.manager[granite]) water = Block.from_string_blockstate("minecraft:water") dirt_water = dirt + water self.assertNotEqual(dirt, dirt_water) self.assertIsNot(dirt, dirt_water) self.assertEqual(3, self.manager[dirt_water]) with self.assertRaises(KeyError): random_block = self.manager[10000]
def test_extra_blocks(self): stone = Block.from_string_blockstate("minecraft:stone") water = Block.from_string_blockstate("minecraft:water[level=1]") granite = Block.from_string_blockstate("minecraft:granite") dirt = Block.from_string_blockstate("minecraft:dirt") conglomerate_1 = stone + water + dirt self.assertIsInstance(conglomerate_1, Block) self.assertEqual("minecraft", conglomerate_1.namespace) self.assertEqual("stone", conglomerate_1.base_name) self.assertEqual({}, conglomerate_1.properties) self.assertEqual(2, len(conglomerate_1.extra_blocks)) for block_1, block_2 in zip(conglomerate_1.extra_blocks, (water, dirt)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) conglomerate_2 = conglomerate_1 + granite self.assertIsInstance(conglomerate_2, Block) self.assertEqual("minecraft", conglomerate_2.namespace) self.assertEqual("stone", conglomerate_2.base_name) self.assertEqual({}, conglomerate_2.properties) self.assertEqual(3, len(conglomerate_2.extra_blocks)) for block_1, block_2 in zip( conglomerate_2.extra_blocks, (water, dirt, granite) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertNotEqual(conglomerate_1, conglomerate_2) conglomerate_3 = conglomerate_2 - dirt self.assertIsInstance(conglomerate_3, Block) self.assertEqual("minecraft", conglomerate_3.namespace) self.assertEqual("stone", conglomerate_3.base_name) self.assertEqual({}, conglomerate_3.properties) self.assertEqual(2, len(conglomerate_3.extra_blocks)) for block_1, block_2 in zip(conglomerate_3.extra_blocks, (water, granite)): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertRaises(TypeError, lambda: stone + 1) self.assertRaises(TypeError, lambda: stone - 1)
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})", )
from bidict import bidict from collections import deque from amulet.api.block import Block from amulet.api.selection import SelectionBox, SelectionGroup #from amulet.operations.fill import fill from amulet import load_level import amulet_nbt # Deprecated now that Block.from_string_blockstate exists def blockstate_to_block(s): namespace, base_name, properties = Block.parse_blockstate_string(s) print(properties) return Block(namespace, base_name, properties) define_block = Block.from_string_blockstate("universal_minecraft:bedrock[infiniburn=false]") blockfunction_block = Block.from_string_blockstate("universal_minecraft:wool[color=blue]") pattern_block = Block.from_string_blockstate("universal_minecraft:wool[color=red]") lambda_block = Block.from_string_blockstate("universal_minecraft:wool[color=light_blue]") namespace_block = Block.from_string_blockstate("universal_minecraft:wool[color=pink]") string_block = Block.from_string_blockstate("universal_minecraft:wool[color=lime]") funcall_block = Block.from_string_blockstate("universal_minecraft:wool[color=cyan]") union_block = Block.from_string_blockstate("universal_minecraft:wool[color=purple]") torch_up = Block.from_string_blockstate("universal_minecraft:torch[facing=up]") air = Block.from_string_blockstate("universal_minecraft:air") def is_sign(b): return b.base_name == "oak_wall_sign" # Rotation matrix around z-axis (right hand rule) # (1,0,0) -> (0,1,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)", ) replace( self.world, OVERWORLD, box1, { "original_blocks": [ Block.from_string_blockstate( "universal_minecraft:stone"), Block.from_string_blockstate( "universal_minecraft:air"), ], "replacement_blocks": [ Block.from_string_blockstate( "universal_minecraft:granite[polished=false]"), Block.from_string_blockstate( "universal_minecraft:stone"), ], }, ) self.world.create_undo_point() 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)", )
def test_get_from_blockstate(self): # This is mostly just sanity checks air = Block.from_string_blockstate("minecraft:air") self.assertIsInstance(air, Block) self.assertEqual("minecraft", air.namespace) self.assertEqual("air", air.base_name) self.assertEqual({}, air.properties) self.assertEqual((), air.extra_blocks) self.assertEqual("minecraft:air", air.blockstate) self.assertEqual("minecraft:air", air.snbt_blockstate) self.assertEqual("minecraft:air", air.full_blockstate) stone = Block.from_string_blockstate("minecraft:stone") self.assertIsInstance(stone, Block) self.assertEqual("minecraft", stone.namespace) self.assertEqual("stone", stone.base_name) self.assertEqual({}, stone.properties) self.assertEqual((), stone.extra_blocks) self.assertEqual("minecraft:stone", stone.blockstate) self.assertEqual("minecraft:stone", stone.snbt_blockstate) self.assertEqual("minecraft:stone", stone.full_blockstate) self.assertNotEqual(air, stone) oak_leaves = Block.from_snbt_blockstate( 'minecraft:oak_leaves[distance="1",persistent="true"]' ) self.assertIsInstance(oak_leaves, Block) self.assertEqual("minecraft", oak_leaves.namespace) self.assertEqual("oak_leaves", oak_leaves.base_name) self.assertEqual( {"distance": nbt.TAG_String("1"), "persistent": nbt.TAG_String("true")}, oak_leaves.properties, ) self.assertEqual((), oak_leaves.extra_blocks) self.assertEqual( "minecraft:oak_leaves[distance=1,persistent=true]", oak_leaves.blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves.snbt_blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves.full_blockstate, ) oak_leaves_2 = Block( namespace="minecraft", base_name="oak_leaves", properties={ "persistent": nbt.TAG_String("true"), "distance": nbt.TAG_String("1"), }, ) self.assertEqual("minecraft", oak_leaves_2.namespace) self.assertEqual("oak_leaves", oak_leaves_2.base_name) self.assertEqual( {"distance": nbt.TAG_String("1"), "persistent": nbt.TAG_String("true")}, oak_leaves_2.properties, ) self.assertEqual((), oak_leaves_2.extra_blocks) self.assertEqual( "minecraft:oak_leaves[distance=1,persistent=true]", oak_leaves_2.blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves_2.snbt_blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves_2.full_blockstate, ) self.assertEqual(oak_leaves, oak_leaves_2) oak_leaves_3 = Block.from_snbt_blockstate( 'minecraft:oak_leaves[persistent="true",distance="1"]' ) self.assertEqual("minecraft", oak_leaves_3.namespace) self.assertEqual("oak_leaves", oak_leaves_3.base_name) self.assertEqual( {"distance": nbt.TAG_String("1"), "persistent": nbt.TAG_String("true")}, oak_leaves_3.properties, ) self.assertEqual((), oak_leaves_3.extra_blocks) self.assertEqual( "minecraft:oak_leaves[distance=1,persistent=true]", oak_leaves_3.blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves_3.snbt_blockstate, ) self.assertEqual( 'minecraft:oak_leaves[distance="1",persistent="true"]', oak_leaves_3.full_blockstate, ) self.assertEqual(oak_leaves, oak_leaves_3)
def test_remove_layer(self): stone = Block.from_string_blockstate("minecraft:stone") water = Block.from_string_blockstate("minecraft:water[level=1]") granite = Block.from_string_blockstate("minecraft:granite") dirt = Block.from_string_blockstate("minecraft:dirt") oak_log_axis_x = Block.from_string_blockstate("minecraft:oak_log[axis=x]") conglomerate_1 = stone + water + dirt + dirt + granite self.assertIsInstance(conglomerate_1, Block) self.assertEqual("minecraft", conglomerate_1.namespace) self.assertEqual("stone", conglomerate_1.base_name) self.assertEqual({}, conglomerate_1.properties) self.assertEqual(4, len(conglomerate_1.extra_blocks)) for block_1, block_2 in zip( conglomerate_1.extra_blocks, (water, dirt, dirt, granite) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_conglomerate = conglomerate_1.remove_layer(2) for block_1, block_2 in zip( new_conglomerate.extra_blocks, (water, dirt, granite) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) conglomerate_2 = granite + water + stone + dirt + oak_log_axis_x self.assertIsInstance(conglomerate_2, Block) self.assertEqual("minecraft", conglomerate_2.namespace) self.assertEqual("granite", conglomerate_2.base_name) self.assertEqual({}, conglomerate_2.properties) self.assertEqual(4, len(conglomerate_2.extra_blocks)) for block_1, block_2 in zip( conglomerate_2.extra_blocks, (water, stone, dirt, oak_log_axis_x) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_conglomerate = conglomerate_2.remove_layer(3) self.assertEqual(3, len(new_conglomerate.extra_blocks)) for block_1, block_2 in zip( new_conglomerate.extra_blocks, (water, stone, oak_log_axis_x) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) new_base = conglomerate_2.remove_layer(0) self.assertEqual(3, len(new_base.extra_blocks)) self.assertEqual("minecraft", new_base.namespace) self.assertEqual("water", new_base.base_name) self.assertEqual({"level": nbt.TAG_String("1")}, new_base.properties) for block_1, block_2 in zip( new_base.extra_blocks, (stone, dirt, oak_log_axis_x) ): self.assertEqual(block_1, block_2) self.assertEqual(0, len(block_1.extra_blocks)) self.assertNotEqual(new_base, new_base.remove_layer(1)) with self.assertRaises(BlockException): no_block = granite.remove_layer(0) with self.assertRaises(BlockException): non_present_layer = granite.remove_layer(7) non_present_layer = conglomerate_2.remove_layer(5) conglomerate_2.remove_layer(4) # Check if last layer can still be removed
def open_from(self, f: BinaryIO): sponge_schem = amulet_nbt.load(f) version = sponge_schem.get("Version") if not isinstance(version, amulet_nbt.TAG_Int): raise SpongeSchemReadError( "Version key must exist and be an integer.") if version == 1: raise SpongeSchemReadError( "Sponge Schematic Version 1 is not supported currently.") elif version == 2: offset = sponge_schem.get("Offset") if isinstance(offset, amulet_nbt.TAG_Int_Array) and len(offset) == 3: min_point = numpy.array(offset) else: min_point = numpy.array([0, 0, 0], dtype=numpy.int32) size = [] for key in ("Width", "Height", "Length"): val = sponge_schem.get(key) if not isinstance(val, amulet_nbt.TAG_Short): raise SpongeSchemReadError( f"Key {key} must exist and be a TAG_Short.") # convert to an unsigned short val = val.value if val < 0: val += 2**16 size.append(val) max_point = min_point + size selection = SelectionBox(min_point, max_point) self._bounds[self.dimensions[0]] = SelectionGroup(selection) data_version = sponge_schem.get("DataVersion") if not isinstance(data_version, amulet_nbt.TAG_Int): raise SpongeSchemReadError("DataVersion must be a TAG_Int.") translator_version = self.translation_manager.get_version( "java", int(data_version)) self._platform = translator_version.platform self._version = translator_version.data_version packed_block_data = sponge_schem.get("BlockData") if not isinstance(packed_block_data, amulet_nbt.TAG_Byte_Array): raise SpongeSchemReadError( "BlockData must be a TAG_Byte_Array") unpacked_block_data = decode_byte_array( numpy.array(packed_block_data, dtype=numpy.uint8)) if len(unpacked_block_data) != numpy.prod(size): raise SpongeSchemReadError( "The data contained in BlockData does not match the size of the schematic." ) dx, dy, dz = selection.shape blocks_array: numpy.ndarray = numpy.transpose( numpy.array( unpacked_block_data, dtype=numpy.uint32, ).reshape((dy, dz, dx)), (2, 0, 1), # YZX => XYZ ) if "Palette" not in sponge_schem: raise SpongeSchemReadError( "Amulet is not able to read Sponge Schem files with no block palette." ) palette_data = sponge_schem.get("Palette") if not isinstance(palette_data, amulet_nbt.TAG_Compound): raise SpongeSchemReadError("Palette must be a TAG_Compound.") block_palette: Dict[int, Block] = {} for blockstate, index in palette_data.items(): if index.value in block_palette: raise SpongeSchemReadError( f"Duplicate block index {index} found in the palette.") block_palette[index.value] = Block.from_string_blockstate( blockstate) if not numpy.all(numpy.isin(blocks_array, list(block_palette))): raise SpongeSchemReadError( "Some values in BlockData were not present in Palette") for cx, cz in selection.chunk_locations(): chunk_box = SelectionBox.create_chunk_box( cx, cz).intersection(selection) array_slice = chunk_box.create_moved_box(selection.min, subtract=True).slice chunk_blocks_: numpy.ndarray = blocks_array[array_slice] chunk_palette_indexes, chunk_blocks = numpy.unique( chunk_blocks_, return_inverse=True, ) chunk_blocks = chunk_blocks.reshape(chunk_blocks_.shape) chunk_palette = numpy.empty(len(chunk_palette_indexes), dtype=object) for palette_index, index in enumerate(chunk_palette_indexes): chunk_palette[palette_index] = block_palette[index] self._chunks[(cx, cz)] = ( chunk_box, chunk_blocks, chunk_palette, [], [], ) if "BlockEntities" in sponge_schem: block_entities = sponge_schem["BlockEntities"] if (not isinstance(block_entities, amulet_nbt.TAG_List) or block_entities.list_data_type != 10 # amulet_nbt.TAG_Compound.tag_id ): raise SpongeSchemReadError( "BlockEntities must be a TAG_List of compound tags.") for block_entity in block_entities: if "Pos" in block_entity: pos = block_entity["Pos"] if isinstance( pos, amulet_nbt.TAG_Int_Array) and len(pos) == 3: pos = pos + min_point x, y, z = ( pos[0], pos[1], pos[2], ) block_entity["Pos"] = amulet_nbt.TAG_Int_Array(pos) cx, cz = x >> 4, z >> 4 if (cx, cz) in self._chunks and ( x, y, z) in self._chunks[(cx, cz)][0]: self._chunks[(cx, cz)][3].append(block_entity) if "Entities" in sponge_schem: entities = sponge_schem["Entities"] if (not isinstance(entities, amulet_nbt.TAG_List) or entities.list_data_type != 10 # amulet_nbt.TAG_Compound.tag_id ): raise SpongeSchemReadError( "Entities must be a TAG_List of compound tags.") for entity in entities: if "Pos" in entity: pos = entity["Pos"] if (isinstance(pos, amulet_nbt.TAG_List) and len(pos) == 3 and pos.list_data_type == 6): # amulet_nbt.TAG_Double.tag_id: x, y, z = ( pos[0].value + offset[0], pos[1].value + offset[0], pos[2].value + offset[0], ) entity["Pos"] = amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(x), amulet_nbt.TAG_Int(y), amulet_nbt.TAG_Int(z), ]) cx, cz = numpy.floor([x, z]).astype(int) >> 4 if (cx, cz) in self._chunks and ( x, y, z) in self._chunks[(cx, cz)][0]: self._chunks[(cx, cz)][4].append(entity) else: raise SpongeSchemReadError( f"Sponge Schematic Version {version.value} is not supported currently." )
import amulet_nbt from amulet.api.chunk import Chunk from amulet.api.wrapper import Translator from amulet.api.block import Block from amulet.api.registry import BlockManager from amulet.api.data_types import ( VersionIdentifierType, AnyNDArray, BlockNDArray, ) if TYPE_CHECKING: from PyMCTranslate import Version, TranslationManager water = Block.from_string_blockstate("minecraft:water[level=0]") class JavaBlockstateTranslator(Translator): def _translator_key(self, version_number: int) -> VersionIdentifierType: return "java", version_number @staticmethod def is_valid(key): return key[0] == "java" and 1444 <= key[1] < 2836 @staticmethod def _unpack_blocks( translation_manager: "TranslationManager", version_identifier: VersionIdentifierType, chunk: Chunk,