Beispiel #1
0
 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
Beispiel #3
0
 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)
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
0
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
Beispiel #10
0
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)