def encode( self, chunk: "Chunk", palette: AnyNDArray, max_world_version: Tuple[str, int], bounds: Tuple[int, int], ) -> NBTFile: return NBTFile(self._encode_chunk(chunk, palette, max_world_version, bounds))
def get_translator( self, max_world_version: VersionIdentifierType, data: NBTFile = None, ) -> Tuple["Translator", int]: if data: data_version = data.get("DataVersion", TAG_Int(-1)).value key, version = (("java", data_version), data_version) else: key = max_world_version version = max_world_version[1] return loader.Translators.get(key), version
def get_translator( self, max_world_version: Tuple[str, Union[int, Tuple[int, int, int]]], data: amulet_nbt.NBTFile = None, ) -> Tuple["Translator", int]: if data: data_version = data.get("DataVersion", amulet_nbt.TAG_Int(-1)).value key, version = (("java", data_version), data_version) else: key = max_world_version version = max_world_version[1] return translators.loader.get(key), version
def _decode_entity_list(self, entities: TAG_List) -> List["Entity"]: entities_out = [] if entities.list_data_type == TAG_Compound.tag_id: for nbt in entities: entity = self._decode_entity( NBTFile(nbt), self._features["entity_format"], self._features["entity_coord_format"], ) if entity is not None: entities_out.append(entity) return entities_out
def _decode_block_entity_list( self, block_entities: TAG_List) -> List["BlockEntity"]: entities_out = [] if block_entities.list_data_type == TAG_Compound.tag_id: for nbt in block_entities: if not isinstance(nbt, TAG_Compound): continue entity = self._decode_block_entity( NBTFile(nbt), self._features["block_entity_format"], self._features["block_entity_coord_format"], ) if entity is not None: entities_out.append(entity) return entities_out
def _compress(data: nbt.NBTFile) -> bytes: """Convert an NBTFile into a compressed bytes object""" data = data.save_to(compressed=False) return b"\x02" + zlib.compress(data)
def _decode_base_entity( nbt: amulet_nbt.NBTFile, id_type: EntityIDType, coord_type: EntityCoordType ) -> Optional[Tuple[str, str, Union[int, float], Union[int, float], Union[ int, float], amulet_nbt.NBTFile, ]]: if not (isinstance(nbt, amulet_nbt.NBTFile) and isinstance(nbt.value, amulet_nbt.TAG_Compound)): return if id_type == EntityIDType.namespace_str_id: entity_id = nbt.pop("id", amulet_nbt.TAG_String("")) if (not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" or ":" not in entity_id.value): return namespace, base_name = entity_id.value.split(":", 1) elif id_type == EntityIDType.namespace_str_Id: entity_id = nbt.pop("Id", amulet_nbt.TAG_String("")) if (not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" or ":" not in entity_id.value): return namespace, base_name = entity_id.value.split(":", 1) elif id_type == EntityIDType.str_id: entity_id = nbt.pop("id", amulet_nbt.TAG_String("")) if (not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == ""): return namespace = "" base_name = entity_id.value elif id_type in [ EntityIDType.namespace_str_identifier, EntityIDType.int_id ]: if "identifier" in nbt: entity_id = nbt.pop("identifier") if (not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" or ":" not in entity_id.value): return namespace, base_name = entity_id.value.split(":", 1) elif "id" in nbt: entity_id = nbt.pop("id") if not isinstance(entity_id, amulet_nbt.TAG_Int): return namespace = "" base_name = str(entity_id.value) else: return else: raise NotImplementedError(f"Entity id type {id_type}") if coord_type in [ EntityCoordType.Pos_list_double, EntityCoordType.Pos_list_float, EntityCoordType.Pos_list_int, ]: if "Pos" not in nbt: return pos = nbt.pop("Pos") pos: amulet_nbt.TAG_List if (not isinstance(pos, amulet_nbt.TAG_List) or len(pos) != 3 or PosTypeMap.get(pos.list_data_type) != coord_type): return x, y, z = [c.value for c in pos] elif coord_type == EntityCoordType.Pos_array_int: if "Pos" not in nbt: return pos = nbt.pop("Pos") pos: amulet_nbt.TAG_Int_Array if not isinstance(pos, amulet_nbt.TAG_Int_Array) or len(pos) != 3: return x, y, z = pos elif coord_type == EntityCoordType.xyz_int: if not all(c in nbt and isinstance(nbt[c], amulet_nbt.TAG_Int) for c in ("x", "y", "z")): return x, y, z = [nbt.pop(c).value for c in ("x", "y", "z")] else: raise NotImplementedError(f"Entity coord type {coord_type}") return namespace, base_name, x, y, z, nbt
def _decode_base_entity( nbt: amulet_nbt.NBTFile, id_type: str, coord_type: str ) -> Optional[ Tuple[ str, str, Union[int, float], Union[int, float], Union[int, float], amulet_nbt.NBTFile, ] ]: if not isinstance(nbt, amulet_nbt.NBTFile) and isinstance( nbt.value, amulet_nbt.TAG_Compound ): return if id_type == "namespace-str-id": entity_id = nbt.pop("id", amulet_nbt.TAG_String("")) if ( not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" or ":" not in entity_id.value ): return namespace, base_name = entity_id.value.split(":", 1) elif id_type == "str-id": entity_id = nbt.pop("id", amulet_nbt.TAG_String("")) if ( not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" ): return namespace = "" base_name = entity_id.value elif id_type in ["namespace-str-identifier", "int-id"]: if "identifier" in nbt: entity_id = nbt.pop("identifier") if ( not isinstance(entity_id, amulet_nbt.TAG_String) or entity_id.value == "" or ":" not in entity_id.value ): return namespace, base_name = entity_id.value.split(":", 1) elif "id" in nbt: entity_id = nbt.pop("id") if not isinstance(entity_id, amulet_nbt.TAG_Int): return namespace = "" base_name = str(entity_id.value) else: return else: raise NotImplementedError(f"Entity id type {id_type}") if coord_type in ["Pos-list-double", "Pos-list-float"]: if "Pos" not in nbt: return pos = nbt.pop("Pos") pos: amulet_nbt.TAG_List if not (5 <= pos.list_data_type <= 6 and len(pos) == 3): return x, y, z = [c.value for c in pos] elif coord_type == "xyz-int": if not all( c in nbt and isinstance(nbt[c], amulet_nbt.TAG_Int) for c in ("x", "y", "z") ): return x, y, z = [nbt.pop(c).value for c in ("x", "y", "z")] else: raise NotImplementedError(f"Entity coord type {coord_type}") return namespace, base_name, x, y, z, nbt
def translate( object_input: Union[Block, Entity], input_spec: dict, mappings: List[dict], output_version: "Version", force_blockstate: bool, get_block_callback: Callable[ [BlockCoordinates], Tuple[Block, Union[None, BlockEntity]] ] = None, extra_input: BlockEntity = None, pre_populate_defaults: bool = True, block_location: Optional[BlockCoordinates] = None, ) -> Tuple[Union[Block, Entity], Union[BlockEntity, None], bool, bool]: """ A function to translate the object input to the output version :param object_input: the Block or Entity object to be converted :param input_spec: the specification for the object_input from the input block_format :param mappings: the mapping file for the input_object :param output_version: A way for the function to look at the specification being converted to. (used to load default properties) :param force_blockstate: True to get the blockstate format. False to get the native format (these are sometimes the same) :param get_block_callback: A callable with relative coordinates that returns a Block and optional BlockEntity :param extra_input: secondary to the object_input a block entity can be given. This should only be used in the select block tool or plugins. Not compatible with location :param pre_populate_defaults: should the nbt structure (if exists) be populated with the default values :param block_location: optional coordinate of where the block is in the world. Used in very few situations. :return: output, extra_output, extra_needed, cacheable extra_needed: a bool to specify if more data is needed beyond the object_input cacheable: a bool to specify if the result can be cached to reduce future processing Block, None, bool, bool Block, BlockEntity, bool, bool Entity, None, bool, bool """ if block_location is not None: block_location = ( # force block locations to be ints int(block_location[0]), int(block_location[1]), int(block_location[2]), ) # set up for the _translate function which does the actual conversion if isinstance(object_input, Block): block_input = object_input if ( extra_input is None and "snbt" in input_spec and get_block_callback is not None ): # if the callback function is defined then load the BlockEntity from the world extra_input = get_block_callback((0, 0, 0))[1] if extra_input is None: # if BlockEntity is still None create it based off the specification namespace, base_name = input_spec["nbt_identifier"] extra_input = BlockEntity( namespace, base_name, 0, 0, 0, NBTFile(amulet_nbt.from_snbt(input_spec["snbt"])), ) nbt_input = extra_input.nbt elif extra_input is not None: # if the BlockEntity is already defined in extra_input continue with that assert isinstance(extra_input, BlockEntity) nbt_input = extra_input.nbt else: # if callback and extra_input are both None then continue with the mapping as normal but without the BlockEntity. # The mappings will do as much as it can and will return the extra_needed flag as True telling the caller to run with callback if possible nbt_input = None elif isinstance(object_input, Entity): assert ( extra_input is None ), "When an Entity is the first input the extra input must be None" block_input = None nbt_input = object_input.nbt else: raise Exception # run the conversion output_name, output_type, new_data, extra_needed, cacheable = _translate( block_input, nbt_input, mappings, get_block_callback, block_location ) # sort out the outputs from the _translate function extra_output = None if output_type == "block": # we should have a block output # create the block object based on output_name and new['properties'] namespace, base_name = output_name.split(":", 1) spec = output_version.block.get_specification( namespace, base_name, force_blockstate ) properties = spec.get("defaults", {}) # cast to NBT properties = { prop: amulet_nbt.from_snbt(val) for prop, val in properties.items() } for key, val in new_data["properties"].items(): properties[key] = val output = Block(namespace, base_name, properties) if "snbt" in spec: namespace, base_name = spec.get("nbt_identifier", ["unknown", "unknown"]) if pre_populate_defaults: nbt = nbt_from_list( spec.get("outer_name", ""), spec.get("outer_type", "compound"), new_data["nbt"], spec.get("snbt", "{}"), ) else: nbt = nbt_from_list( spec.get("outer_name", ""), spec.get("outer_type", "compound"), new_data["nbt"], ) extra_output = BlockEntity(namespace, base_name, 0, 0, 0, nbt) # not quite sure how to handle coordinates here. # it makes sense to me to have the wrapper program set the coordinates so none are missed. elif new_data["nbt"]: log.warning( f"New nbt present but no output block entity\nin:{object_input.blockstate}\nout:{output.blockstate}" ) elif output_type == "entity": # we should have an entity output # create the entity object based on output_name and new['nbt'] namespace, base_name = output_name.split(":", 1) spec = output_version.entity.get_specification( namespace, base_name, force_blockstate ) if pre_populate_defaults: nbt = nbt_from_list( spec.get("outer_name", ""), spec.get("outer_type", "compound"), new_data["nbt"], spec.get("snbt", "{}"), ) else: nbt = nbt_from_list( spec.get("outer_name", ""), spec.get("outer_type", "compound"), new_data["nbt"], ) output = Entity(namespace, base_name, 0.0, 0.0, 0.0, nbt) else: raise Exception("No output object given.") return output, extra_output, extra_needed, cacheable
def nbt_from_list( outer_name: str, outer_type: str, nbt_list: List[ Tuple[ str, str, List[Tuple[Union[str, int], str]], Union[str, int], Union[ TAG_Byte, TAG_Short, TAG_Int, TAG_Long, TAG_Float, TAG_Double, TAG_Byte_Array, TAG_String, TAG_List, TAG_Compound, TAG_Int_Array, TAG_Long_Array, ], ] ], default_template: str = None, ) -> NBTFile: if default_template is not None: nbt_object = amulet_nbt.from_snbt(default_template) else: nbt_object = datatype_to_nbt(outer_type)() for nbt_entry in nbt_list: outer_name_, outer_type_, nbt_path, data_path, data = nbt_entry if outer_name == outer_name_ and outer_type == outer_type_: nbt_temp: Union[ TAG_Byte, TAG_Short, TAG_Int, TAG_Long, TAG_Float, TAG_Double, TAG_Byte_Array, TAG_String, TAG_List, TAG_Compound, TAG_Int_Array, TAG_Long_Array, ] = nbt_object for path, nbt_type in nbt_path: # if the nested NBT object does not exist then create it if isinstance(nbt_temp, TAG_Compound): if ( path not in nbt_temp or nbt_to_datatype(nbt_temp[path]) != nbt_type ): nbt_temp[path] = datatype_to_nbt(nbt_type)() elif isinstance(nbt_temp, TAG_List): # if the list is a different type to nbt_type replace it with nbt_type if ( _int_to_datatype[int(nbt_temp.list_data_type)] != nbt_type and len(nbt_temp) > 0 ): nbt_temp.list_data_type = datatype_to_nbt(nbt_type).tag_id for index in range(len(nbt_temp)): nbt_temp[index] = datatype_to_nbt(nbt_type)() # pad out the list to the length of path if path + 1 > len(nbt_temp): # pad out the list to the length of the index for _ in range(path + 1 - len(nbt_temp)): nbt_temp.insert(datatype_to_nbt(nbt_type)()) # we now should have a TAG_List of the same type as nbt_type and length as path nbt_temp = nbt_temp[path] if isinstance(nbt_temp, TAG_Compound): nbt_temp[data_path] = data elif isinstance(nbt_temp, TAG_List): # if the list is a different type to data replace it with type(data) if nbt_temp.list_data_type != data.tag_id and len(nbt_temp) > 0: nbt_temp.list_data_type = data.tag_id for index in range(len(nbt_temp)): nbt_temp[index] = data.__class__() # pad out the list to the length of path if data_path + 1 > len(nbt_temp): # pad out the list to the length of the index for _ in range(data_path + 1 - len(nbt_temp)): nbt_temp.append(data.__class__()) # we now should have a TAG_List of the same type as nbt_type and length as data_path nbt_temp[data_path] = data # TODO: # elif isinstance(nbt_temp, TAG_Byte_Array) and isinstance(data, TAG_Byte): # # pad to the length of data_path if less than the current length # # nbt_temp[data_path] = data.value # elif isinstance(nbt_temp, TAG_Int_Array) and isinstance(data, TAG_Int): # elif isinstance(nbt_temp, TAG_Long_Array) and isinstance(data, TAG_Long): return NBTFile(nbt_object, outer_name)