def _encode_blocks( self, blocks: Blocks, palette: AnyNDArray ) -> amulet_nbt.TAG_List: sections = amulet_nbt.TAG_List() for cy in range(16): if cy in blocks: block_sub_array = numpy.transpose( blocks.get_sub_chunk(cy), (1, 2, 0) ).ravel() sub_palette_, block_sub_array = numpy.unique( block_sub_array, return_inverse=True ) sub_palette = self._encode_palette(palette[sub_palette_]) if ( len(sub_palette) == 1 and sub_palette[0]["Name"].value == "minecraft:air" ): continue section = amulet_nbt.TAG_Compound() section["Y"] = amulet_nbt.TAG_Byte(cy) if self.features["long_array_format"] == "compact": section["BlockStates"] = amulet_nbt.TAG_Long_Array( encode_long_array(block_sub_array) ) elif self.features["long_array_format"] == "1.16": section["BlockStates"] = amulet_nbt.TAG_Long_Array( encode_long_array(block_sub_array, dense=False) ) section["Palette"] = sub_palette sections.append(section) return sections
def _init_write(self): """data to be written at init in write mode""" self._buffer.write(magic_num) self._buffer.write(struct.pack(">B", self._format_version)) if self._format_version == 0: self._metadata = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound({ "created_with": amulet_nbt.TAG_String("amulet_python_wrapper"), "selection_boxes": amulet_nbt.TAG_Int_Array([ c for box in self._selection_boxes if len(box) == 6 and all( isinstance(c, int) for c in box) for c in box ]), "section_version": amulet_nbt.TAG_Byte(self._section_version), "export_version": amulet_nbt.TAG_Compound({ "edition": amulet_nbt.TAG_String(self._source_edition), "version": amulet_nbt.TAG_List([ amulet_nbt.TAG_Int(v) for v in self._source_version ]), }), })) else: raise Exception( f"This wrapper doesn't support any construction version higher than {max_format_version}" )
def add_tag(self): selected_tag = self.tree.GetFocusedItem() name, data = self.tree.GetItemData(selected_tag) def save_func(new_name, new_tag_value, new_tag_type, _): tag_type = [ tag_class for tag_class in self.image_map if tag_class.__name__ == new_tag_type ][0] self.nbt_data[new_name] = nbt_tag = tag_type(new_tag_value) new_child = self.tree.AppendItem(selected_tag, f"{new_name}: {new_tag_value}") self.tree.SetItemImage(new_child, self.image_map.get(tag_type, self.other)) self.tree.SetItemData(new_child, (new_name, nbt_tag)) add_dialog = EditTagDialog( self, "", nbt.TAG_Byte(0), [ tag_type.__name__ for tag_type in self.image_map.keys() if "TAG_" in tag_type.__name__ ], create=True, save_callback=save_func, ) add_dialog.Show()
def write(self, section: ConstructionSection): if self._section_version == 0: sx, sy, sz = section.location shapex, shapey, shapez = section.shape blocks = section.blocks entities = section.entities block_entities = section.block_entities palette = section.palette for point, shape in zip((sx, sy, sz), (shapex, shapey, shapez)): assert shape >= 0, "Shape must be positive" assert point + shape <= ( ((point >> 4) + 1) << 4 ), "Section does not fit in a sub-chunk" position = self._buffer.tell() _tag = amulet_nbt.TAG_Compound( {"entities": self._serialise_entities(entities)} ) if blocks is None: _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(-1) else: flattened_array = blocks.ravel() index, flattened_array = numpy.unique( flattened_array, return_inverse=True ) palette = numpy.array(palette, dtype=object)[index] lut = numpy.vectorize(self._palette.get_add_block)(palette) flattened_array = lut[flattened_array] array_type = self._find_fitting_array_type(flattened_array) _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(array_type().tag_id) _tag["blocks"] = array_type(flattened_array) _tag["block_entities"] = self._serialise_block_entities( block_entities or [] ) amulet_nbt.NBTFile(_tag).save_to(self._buffer) length = self._buffer.tell() - position self._section_index_table.append( (sx, sy, sz, shapex, shapey, shapez, position, length) ) else: raise Exception( f"This wrapper doesn't support any section version higher than {max_section_version}" )
def _reload_world(self): try: self.close() except: pass try: self._level_manager = LevelDBDimensionManager(self.path) self._is_open = True self._has_lock = True experiments = self.root_tag.get("experiments", {}) if (experiments.get("caves_and_cliffs", nbt.TAG_Byte()).value or experiments.get("caves_and_cliffs_internal", nbt.TAG_Byte()).value or self.version >= (1, 18)): self._bounds[OVERWORLD] = SelectionGroup( SelectionBox((-30_000_000, -64, -30_000_000), (30_000_000, 320, 30_000_000))) else: self._bounds[OVERWORLD] = SelectionGroup( SelectionBox((-30_000_000, 0, -30_000_000), (30_000_000, 256, 30_000_000))) self._bounds[THE_NETHER] = SelectionGroup( SelectionBox((-30_000_000, 0, -30_000_000), (30_000_000, 128, 30_000_000))) self._bounds[THE_END] = SelectionGroup( SelectionBox((-30_000_000, 0, -30_000_000), (30_000_000, 256, 30_000_000))) except OSError as e: self._is_open = self._has_lock = False raise LevelDBException( "It looks like this world is from the marketplace.\nThese worlds are encrypted and cannot be edited." ) from e except LevelDBException as e: msg = str(e) self._is_open = self._has_lock = False # I don't know if there is a better way of handling this. if msg.startswith("IO error:") and msg.endswith( ": Permission denied"): traceback.print_exc() raise LevelDBException( f"Failed to load the database. The world may be open somewhere else.\n{msg}" ) from e else: raise e
def generateSignEntity(x, y, z, direction): """Generates the entity to make the sign display its position""" return BlockEntity("java", "acacia_wall_sign", x, y, z,\ amulet_nbt.NBTFile(\ value = amulet_nbt.TAG_Compound(\ {\ "utags": amulet_nbt.TAG_Compound(\ {\ "keepPacked": amulet_nbt.TAG_Byte(0),\ "Text4": amulet_nbt.TAG_String("{\"text\":\"\"}"),\ "Text3": amulet_nbt.TAG_String("{\"text\":\"\"}"),\ "Text2": amulet_nbt.TAG_String("{\"text\":\"%d - %d\"}"%(z + direction, z + direction * 6)), \ "Text1": amulet_nbt.TAG_String("{\"text\":\"%d\"}"%x)\ }),\ "Color": amulet_nbt.TAG_String("black")\ })))
def _get_interface_key( self, raw_chunk_data: Optional[Dict[bytes, bytes]] = None) -> Tuple[str, int]: if raw_chunk_data: if b"," in raw_chunk_data: chunk_version = raw_chunk_data[b","][0] else: chunk_version = raw_chunk_data.get(b"v", b"\x00")[0] else: chunk_version = game_to_chunk_version( self.max_world_version[1], self.root_tag.get("experiments", {}).get("caves_and_cliffs", nbt.TAG_Byte()).value, ) return self.platform, chunk_version # TODO: work out a valid default
def _encode_blocks(self, blocks: "Blocks", palette: AnyNDArray) -> nbt.TAG_List: sections = nbt.TAG_List() for cy in range(16): # perhaps find a way to do this dynamically if cy in blocks: block_sub_array = palette[numpy.transpose( blocks.get_sub_chunk(cy), (1, 2, 0)).ravel()] data_sub_array = block_sub_array[:, 1] block_sub_array = block_sub_array[:, 0] if not numpy.any(block_sub_array) and not numpy.any( data_sub_array): continue section = nbt.TAG_Compound() section["Y"] = nbt.TAG_Byte(cy) section["Blocks"] = nbt.TAG_Byte_Array( block_sub_array.astype("uint8")) section["Data"] = nbt.TAG_Byte_Array( world_utils.to_nibble_array(data_sub_array)) sections.append(section) return sections
def save_to(self, f: BinaryIO): palette: BlockManager = BlockManager() f.write(magic_num) f.write(struct.pack(">B", self._format_version)) if self._format_version == 0: metadata = amulet_nbt.NBTFile( amulet_nbt.TAG_Compound( { "created_with": amulet_nbt.TAG_String( "amulet_python_wrapper_v2" ), "selection_boxes": amulet_nbt.TAG_Int_Array( [ c for box in self._selection.selection_boxes for c in (*box.min, *box.max) ] ), "section_version": amulet_nbt.TAG_Byte(self._section_version), "export_version": amulet_nbt.TAG_Compound( { "edition": amulet_nbt.TAG_String(self._platform), "version": amulet_nbt.TAG_List( [amulet_nbt.TAG_Int(v) for v in self._version] ), } ), } ) ) section_index_table: List[ Tuple[int, int, int, int, int, int, int, int] ] = [] if self._section_version == 0: for section_list in self._chunk_to_section.values(): for section in section_list: sx, sy, sz = section.location shapex, shapey, shapez = section.shape blocks = section.blocks entities = section.entities block_entities = section.block_entities section_palette = section.palette position = f.tell() _tag = amulet_nbt.TAG_Compound( {"entities": serialise_entities(entities)} ) if blocks is None: _tag["blocks_array_type"] = amulet_nbt.TAG_Byte(-1) else: flattened_array = blocks.ravel() index, flattened_array = numpy.unique( flattened_array, return_inverse=True ) section_palette = numpy.array( section_palette, dtype=object )[index] lut = numpy.vectorize(palette.get_add_block)( section_palette ) flattened_array = lut[flattened_array] array_type = find_fitting_array_type(flattened_array) _tag["blocks_array_type"] = amulet_nbt.TAG_Byte( array_type().tag_id ) _tag["blocks"] = array_type(flattened_array) _tag["block_entities"] = serialise_block_entities( block_entities or [] ) amulet_nbt.NBTFile(_tag).save_to(f) length = f.tell() - position section_index_table.append( (sx, sy, sz, shapex, shapey, shapez, position, length) ) else: raise Exception( f"This wrapper doesn't support any section version higher than {max_section_version}" ) metadata_start = f.tell() metadata["section_index_table"] = amulet_nbt.TAG_Byte_Array( numpy.array(section_index_table, dtype=SECTION_ENTRY_TYPE).view( numpy.int8 ) ) metadata["block_palette"] = pack_palette(palette) metadata.save_to(f) f.write(INT_STRUCT.pack(metadata_start)) f.write(magic_num) else: raise Exception( f"This wrapper doesn't support any construction version higher than {max_format_version}" )
def fillbarrels(chunk, barrelPositionList, barrelBlock, currentArticle, booksPerBarrel, zimFilePath, chunkList, target_pos): """Generates all barrels in the chunk and fills them with books/articles""" for barrelPos in barrelPositionList: books = [] titles = [] start = time.perf_counter() if booksPerBarrel > 30: pool = Pool( processes=4 ) #on my laptop ~4 processes was faster than any amount of threads (4 = logic core count) else: pool = ThreadPool( processes=3 ) #the article reading is mostly cpu limited, so going high on process count doesnt help outputs = pool.map( partial(tryGetArticle, zimFilePath=zimFilePath, barrelPositionList=barrelPositionList, booksPerBarrel=booksPerBarrel, chunkList=chunkList, target_pos=target_pos), range(currentArticle, currentArticle + booksPerBarrel)) pool.close() #outputs = [] #for id in range(currentArticle, currentArticle + booksPerBarrel): # outputs.append(tryGetArticle(id, zimFilePath)) currentArticle += booksPerBarrel for output in outputs: if output[0] == None: continue titles.append(output[1]) books.append(output[0]) stop = time.perf_counter() #print("generating a book", (stop-start)/booksPerBarrel) chunk.blocks[barrelPos] = barrelBlock barrelEntity = BlockEntity("java", "barrel", barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16,\ amulet_nbt.NBTFile(\ value = amulet_nbt.TAG_Compound(\ {\ "utags": amulet_nbt.TAG_Compound(\ {\ "keepPacked": amulet_nbt.TAG_Byte(0),\ "isMovable": amulet_nbt.TAG_Byte(1),\ "Findable": amulet_nbt.TAG_Byte(0),\ "CustomName": amulet_nbt.TAG_String("{\"text\":\"x:%d y:%d z:%d\"}"%(barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16)),\ "Items": amulet_nbt.TAG_List(\ value = [ amulet_nbt.TAG_Compound(\ {\ "Slot": amulet_nbt.TAG_Byte(iBook),\ "Count": amulet_nbt.TAG_Byte(1),\ "id": amulet_nbt.TAG_String("minecraft:written_book"),\ "tag": amulet_nbt.TAG_Compound(\ { "pages": amulet_nbt.TAG_List(\ value=[amulet_nbt.TAG_String(page) for page in books[iBook]],\ list_data_type = 8\ ),\ "title": amulet_nbt.TAG_String(titles[iBook]),\ "author": amulet_nbt.TAG_String("Pos: x:%d y:%d z:%d, ID: %d"%(barrelPos[0] + chunk.cx * 16, barrelPos[1], barrelPos[2] + chunk.cz * 16, currentArticle + iBook)), }) }) for iBook in range(len(books)) ], list_data_type = 9\ ) })\ }))) chunk.block_entities.insert(barrelEntity)
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 encode(self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, int]) -> amulet_nbt.NBTFile: """ Encode a version-specific chunk to raw data for the format to store. :param chunk: The version-specific chunk to translate and encode. :param palette: The block_palette the ids in the chunk correspond to. :param max_world_version: The key to use to find the encoder. :return: amulet_nbt.NBTFile """ misc = chunk.misc data = amulet_nbt.NBTFile(amulet_nbt.TAG_Compound(), "") data["Level"] = amulet_nbt.TAG_Compound() data["Level"]["xPos"] = amulet_nbt.TAG_Int(chunk.cx) data["Level"]["zPos"] = amulet_nbt.TAG_Int(chunk.cz) if self.features["data_version"] == "int": data["DataVersion"] = amulet_nbt.TAG_Int(max_world_version[1]) if self.features["last_update"] == "long": data["Level"]["LastUpdate"] = amulet_nbt.TAG_Long( misc.get("last_update", 0)) # Order the float value based on the order they would be run. Newer replacements for the same come just after # to save back find the next lowest valid value. if self.features["status"] in ["j13", "j14"]: status = chunk.status.as_type(self.features["status"]) data["Level"]["Status"] = amulet_nbt.TAG_String(status) else: status = chunk.status.as_type("float") if self.features["terrain_populated"] == "byte": data["Level"]["TerrainPopulated"] = amulet_nbt.TAG_Byte( int(status > -0.3)) if self.features["light_populated"] == "byte": data["Level"]["LightPopulated"] = amulet_nbt.TAG_Byte( int(status > -0.2)) if self.features["V"] == "byte": data["Level"]["V"] = amulet_nbt.TAG_Byte(misc.get("V", 1)) if self.features["inhabited_time"] == "long": data["Level"]["InhabitedTime"] = amulet_nbt.TAG_Long( misc.get("inhabited_time", 0)) if self.features["biomes"] == "256BA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Byte_Array( chunk.biomes.convert_to_format(256).astype( dtype=numpy.uint8)) elif self.features["biomes"] == "256IA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Int_Array( chunk.biomes.convert_to_format(256).astype( dtype=numpy.uint32)) elif self.features["biomes"] == "1024IA": if chunk.status.value > -0.7: data["Level"]["Biomes"] = amulet_nbt.TAG_Int_Array( chunk.biomes.convert_to_format(1024).astype( dtype=numpy.uint32)) if self.features["height_map"] == "256IA": data["Level"]["HeightMap"] = amulet_nbt.TAG_Int_Array( misc.get("height_map256IA", numpy.zeros(256, dtype=numpy.uint32))) elif self.features["height_map"] in [ "C|36LA|V1", "C|36LA|V2", "C|36LA|V3", "C|36LA|V4", ]: maps = [ "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", ] if self.features["height_map"] == "C|36LA|V1": # 1466 maps = ("LIQUID", "SOILD", "LIGHT", "RAIN") elif self.features["height_map"] == "C|36LA|V2": # 1484 maps.append("LIGHT_BLOCKING") elif self.features["height_map"] == "C|36LA|V3": # 1503 maps.append("LIGHT_BLOCKING") maps.append("WORLD_SURFACE") elif self.features["height_map"] == "C|36LA|V4": # 1908 maps.append("WORLD_SURFACE") else: raise Exception heightmaps = misc.get("height_mapC|36LA", amulet_nbt.TAG_Compound()) for heightmap in maps: if heightmap not in heightmaps: heightmaps[heightmap] = amulet_nbt.TAG_Long_Array( numpy.zeros(36, dtype=">i8")) data["Level"]["Heightmaps"] = heightmaps if self.features["blocks"] in [ "Sections|(Blocks,Data,Add)", "Sections|(BlockStates,Palette)", ]: data["Level"]["Sections"] = self._encode_blocks( chunk.blocks, palette) else: raise Exception( f'Unsupported block format {self.features["blocks"]}') if self.features["block_light"] in [ "Sections|2048BA" ] or self.features["sky_light"] in ["Sections|2048BA"]: for section in data["Level"]["Sections"]: y = section["Y"].value if self.features["block_light"] == "Sections|2048BA": block_light = misc.get("block_light", {}) if y in block_light: section["BlockLight"] = block_light[y] else: section["BlockLight"] = amulet_nbt.TAG_Byte_Array( numpy.full(2048, 255, dtype=numpy.uint8)) if self.features["sky_light"] == "Sections|2048BA": sky_light = misc.get("sky_light", {}) if y in sky_light: section["SkyLight"] = sky_light[y] else: section["SkyLight"] = amulet_nbt.TAG_Byte_Array( numpy.full(2048, 255, dtype=numpy.uint8)) if self.features["entities"] == "list": if amulet.entity_support: data["Level"]["Entities"] = self._encode_entities( chunk.entities) else: data["Level"]["Entities"] = self._encode_entities( misc.get("java_entities_temp", amulet_nbt.TAG_List())) if self.features["block_entities"] == "list": data["Level"]["TileEntities"] = self._encode_block_entities( chunk.block_entities) if self.features["tile_ticks"] in ["list", "list(optional)"]: ticks = misc.get("tile_ticks", amulet_nbt.TAG_List()) if self.features["tile_ticks"] == "list(optional)": if len(ticks) > 0: data["Level"]["TileTicks"] = ticks elif self.features["tile_ticks"] == "list": data["Level"]["TileTicks"] = ticks if self.features["liquid_ticks"] == "list": data["Level"]["LiquidTicks"] = misc.get("liquid_ticks", amulet_nbt.TAG_List()) if self.features["liquids_to_be_ticked"] == "16list|list": data["Level"]["LiquidsToBeTicked"] = misc.get( "liquids_to_be_ticked", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["to_be_ticked"] == "16list|list": data["Level"]["ToBeTicked"] = misc.get( "to_be_ticked", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["post_processing"] == "16list|list": data["Level"]["PostProcessing"] = misc.get( "post_processing", amulet_nbt.TAG_List([amulet_nbt.TAG_List() for _ in range(16)]), ) if self.features["structures"] == "compound": data["Level"]["Structures"] = misc.get( "structures", amulet_nbt.TAG_Compound({ "References": amulet_nbt.TAG_Compound(), "Starts": amulet_nbt.TAG_Compound(), }), ) return data