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
예제 #2
0
 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}"
         )
예제 #3
0
    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()
예제 #4
0
    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}"
            )
예제 #5
0
 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
예제 #6
0
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")\
                        })))
예제 #7
0
 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
예제 #8
0
    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
예제 #9
0
    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}"
            )
예제 #10
0
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)
예제 #11
0
    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
예제 #12
0
    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