def _encode_structures(chunk: Chunk, compound: TAG_Compound): compound["Structures"] = chunk.misc.get( "structures", TAG_Compound({ "References": TAG_Compound(), "Starts": TAG_Compound(), }), )
def _encode_block_palette(blockstates: list) -> TAG_List: palette = TAG_List() for block in blockstates: entry = TAG_Compound() entry["Name"] = TAG_String(f"{block.namespace}:{block.base_name}") if block.properties: entry["Properties"] = TAG_Compound(block.properties) palette.append(entry) return palette
def _decode_block_section( self, section: TAG_Compound) -> Optional[Tuple[numpy.ndarray, list]]: if "Palette" not in section: # 1.14 makes block_palette/blocks optional. return None section_palette = self._decode_block_palette(section.pop("Palette")) decoded = decode_long_array( section.pop("BlockStates").value, 4096, max(4, (len(section_palette) - 1).bit_length()), dense=self.LongArrayDense, ).astype(numpy.uint32) arr = numpy.transpose(decoded.reshape((16, 16, 16)), (2, 0, 1)) return arr, section_palette
def _encode_block_section( self, chunk: Chunk, sections: Dict[int, TAG_Compound], palette: AnyNDArray, cy: int, ) -> bool: if cy in chunk.blocks: block_sub_array = palette[ numpy.transpose( chunk.blocks.get_sub_chunk(cy), (1, 2, 0) ).ravel() # XYZ -> YZX ] 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): # return False section = sections.setdefault(cy, TAG_Compound()) section["Blocks"] = TAG_Byte_Array(block_sub_array.astype("uint8")) section["Data"] = TAG_Byte_Array( world_utils.to_nibble_array(data_sub_array) ) return True return False
def _encode_block_section( self, chunk: Chunk, sections: Dict[int, TAG_Compound], palette: AnyNDArray, cy: int, ) -> bool: if cy in chunk.blocks: block_sub_array = numpy.transpose(chunk.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_block_palette(palette[sub_palette_]) if (len(sub_palette) == 1 and sub_palette[0]["Name"].value == "minecraft:air"): return False section = sections.setdefault(cy, TAG_Compound()) section["BlockStates"] = TAG_Long_Array( encode_long_array(block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4)) section["Palette"] = sub_palette return True return False
def _init_encode(chunk: "Chunk"): """Get or create the root tag.""" if "java_chunk_data" in chunk.misc and isinstance( chunk.misc["java_chunk_data"], TAG_Compound ): return chunk.misc["java_chunk_data"] else: return TAG_Compound()
def _decode_chunk(self, chunk: Chunk, root: TAG_Compound, bounds: Tuple[int, int]) -> Tuple["Chunk", AnyNDArray]: self._decode_root(chunk, root) floor_cy = root.pop("yPos").value self._decode_level(chunk, root, bounds, floor_cy) sections = self._extract_sections(chunk, root) self._decode_sections(chunk, sections) palette = self._decode_blocks(chunk, sections) return chunk, palette
def _decode_block_palette(palette: TAG_List) -> list: blockstates = [] for entry in palette: namespace, base_name = entry["Name"].value.split(":", 1) properties = entry.get("Properties", TAG_Compound({})).value block = Block(namespace=namespace, base_name=base_name, properties=properties) blockstates.append(block) return blockstates
def test_decode_encode(self): """Test decoding chunk data and then encoding it again to make sure it matches.""" with WorldTemp(self.WorldPath) as world_temp: level = load_format(world_temp.temp_path) level.open() chunk_count = 0 for dimension in level.dimensions: for cx, cz in level.all_chunk_coords(dimension): raw_chunk_data_in = level._get_raw_chunk_data(cx, cz, dimension) raw_chunk_data = copy.deepcopy(raw_chunk_data_in) interface = level._get_interface(raw_chunk_data) if ( world_temp.metadata["world_data"]["platform"] == "java" and world_temp.metadata["world_data"]["origin"] == "vanilla" ): # store references to the data level_tag = raw_chunk_data.get("Level", TAG_Compound()) # decode the raw chunk data chunk, chunk_palette = level._decode( interface, dimension, cx, cz, raw_chunk_data ) # TODO: uncomment this when the last few things in the Java format are sorted # if ( # world_temp.metadata["world_data"]["platform"] == "java" # and world_temp.metadata["world_data"]["origin"] == "vanilla" # ): # self.assertFalse(raw_chunk_data, msg=self.WorldPath) # self.assertFalse(level_tag, msg=self.WorldPath) raw_chunk_data_out = level._encode( interface, chunk, dimension, chunk_palette ) if chunk_count > 100: break chunk_count += 1 # if raw_chunk_data_in != raw_chunk_data_out: # print("Difference") # print(raw_chunk_data_in.value.find_diff(raw_chunk_data_out.value)) # print(raw_chunk_data_in.to_snbt()) # print(raw_chunk_data_out.to_snbt()) # self.assertEqual(raw_chunk_data_in, raw_chunk_data_out) raw_chunk_data2 = copy.deepcopy(raw_chunk_data_out) if world_temp.metadata["world_data"]["platform"] == "bedrock": raw_chunk_data2 = { key: val for key, val in raw_chunk_data2.items() if val is not None } level._decode(interface, dimension, cx, cz, raw_chunk_data2) level.close()
def _decode_chunk( self, chunk: Chunk, root: TAG_Compound, bounds: Tuple[int, int] ) -> Tuple["Chunk", AnyNDArray]: self._decode_root(chunk, root) assert self.check_type(root, "Level", TAG_Compound) level = root.pop("Level") self._decode_level(chunk, level, bounds, bounds[0] >> 4) sections = self._extract_sections(chunk, level) self._decode_sections(chunk, sections) palette = self._decode_blocks(chunk, sections) return chunk, palette
def _encode_biome_section( self, chunk: Chunk, sections: Dict[int, TAG_Compound], cy: int, ) -> bool: chunk.biomes.convert_to_3d() if cy in chunk.biomes: biome_sub_array = numpy.transpose(chunk.biomes.get_section(cy), (1, 2, 0)).ravel() sub_palette_, biome_sub_array = numpy.unique(biome_sub_array, return_inverse=True) sub_palette = self._encode_biome_palette( chunk.biome_palette[sub_palette_]) section = sections.setdefault(cy, TAG_Compound()) biomes = section["biomes"] = TAG_Compound({"palette": sub_palette}) if len(sub_palette) != 1: biomes["data"] = TAG_Long_Array( encode_long_array(biome_sub_array, dense=self.LongArrayDense)) return True return False
def _encode_block_section( self, chunk: Chunk, sections: Dict[int, TAG_Compound], palette: AnyNDArray, cy: int, ): if cy in chunk.blocks: block_sub_array = numpy.transpose(chunk.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_block_palette(palette[sub_palette_]) section = sections.setdefault(cy, TAG_Compound()) block_states = section["block_states"] = TAG_Compound( {"palette": sub_palette}) if len(sub_palette) != 1: block_states["data"] = TAG_Long_Array( encode_long_array(block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4)) return True return False
def _encode_ticks(ticks: Dict[BlockCoordinates, Tuple[str, int, int]]) -> TAG_List: ticks_out = TAG_List() if isinstance(ticks, dict): for k, v in ticks.items(): try: (x, y, z), (i, t, p) = k, v ticks_out.append( TAG_Compound( { "i": TAG_String(i), "p": TAG_Int(p), "t": TAG_Int(t), "x": TAG_Int(x), "y": TAG_Int(y), "z": TAG_Int(x), } ) ) except Exception: amulet.log.error(f"Could not serialise tick data {k}: {v}") return ticks_out
def _decode_biomes(self, chunk: Chunk, compound: TAG_Compound, floor_cy: int): biomes = compound.pop("Biomes", None) if isinstance(biomes, TAG_Int_Array): if (len(biomes) / 16) % 4: log.error( f"The biome array size must be 4x4x4xN but got an array of size {biomes.value.size}" ) else: arr = numpy.transpose( biomes.astype(numpy.uint32).reshape((-1, 4, 4)), (2, 0, 1), ) # YZX -> XYZ chunk.biomes = { sy + floor_cy: arr for sy, arr in enumerate( numpy.split( arr, arr.shape[1] // 4, 1, ) ) }
def _encode_height( self, chunk: Chunk, level: TAG_Compound, bounds: Tuple[int, int] ): maps = [ "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", ] if self._features["height_map"] == "C|V1": # 1466 maps = ("LIQUID", "SOLID", "LIGHT", "RAIN") elif self._features["height_map"] == "C|V2": # 1484 maps.append("LIGHT_BLOCKING") elif self._features["height_map"] == "C|V3": # 1503 maps.append("LIGHT_BLOCKING") maps.append("WORLD_SURFACE") elif self._features["height_map"] == "C|V4": # 1908 maps.append("WORLD_SURFACE") else: raise Exception heightmaps_temp: Dict[str, numpy.ndarray] = chunk.misc.get("height_mapC", {}) heightmaps = TAG_Compound() for heightmap in maps: if ( heightmap in heightmaps_temp and isinstance(heightmaps_temp[heightmap], numpy.ndarray) and heightmaps_temp[heightmap].size == 256 ): heightmaps[heightmap] = TAG_Long_Array( encode_long_array( heightmaps_temp[heightmap].ravel() - bounds[0], (bounds[1] - bounds[0]).bit_length(), self.LongArrayDense, ) ) level["Heightmaps"] = heightmaps
def _decode_biomes(self, chunk: Chunk, compound: TAG_Compound, floor_cy: int): biomes = compound.pop("Biomes", None) if isinstance(biomes, TAG_Byte_Array) and biomes.value.size == 256: chunk.biomes = biomes.astype(numpy.uint32).reshape((16, 16))
def _decode_coords(chunk: Chunk, level: TAG_Compound): assert chunk.coordinates == (level.pop("xPos"), level.pop("zPos"))