'''
    Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [2022] [Sloimay]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
'''

'''
BY SLOIMAY 2022 :D
Based on Fearless' implementation that he took from someone else
that he doesn't remember the name of
Well now mschematic kinda grew out of hands so like now pretty much 20
lines out of the 2,000 is fearless' implementation lMFAO

Versions (last is current):
1 - 15/06/2022
    - MCSchematic starts its journey, I only started logging what happened on version 8
      so I have no idea how the first version looked like lmfAO

2 - 18/06/2022

3 - 28/06/2022

4 - 30/06/2022

5 - 10/08/2022

6 - 14/09/2022

7 - 17/09/2022
    - New load feature :DDDDDDDDDDDDDDDDDDDD
    - Also new fancy BlockDataDB
    
8 - 18/09/2022 
    - Made mcschematic 3 to 30 times faster while saving, 
      and 2 to 10 times faster while loading from a file!! :DDDD
    - Also BlockDataDB overhaul!!

9 - 20/09/2022: The editing update!!!
    - Added a lot of getters
    - Changed the internals of MCSchematic for the blocks and blockPalettes to be
      in a form of a MCStructure class, for easier editing
    - editing tools added:
        - schematic pasting into another schematic
        - translation
        - xyz scaling
        - naive rotation (no rotation of block states)
        - flip

10 - 25/09/2022: The big and cool update for big schematics!!
    - Changed the "fromSS" method in classes in BlockDataDB because it
      didn't work :skull emoji:   python "self" keyword moment
    - Fixed a bug in .setBlock where if you placed a blockEntity at a spot,
      then a blockState in the same spot, the blockEntity data will still remain,
      and could potentially also be returned by .getBlockDataAt()
    - Went from using floor() to round() in transform methods like translate etc,
      because it makes transforms such as rotate symmetric. Now what does that mean?
      Basically rotate(14 degrees) and rotate(-14 degrees) are supposed to be a flipped
      version of each other. But using floor() makes the rotated blocks have a snapping 
      bias towards -XYZ, so they end up not being the same. Whereas round() removes that bias.
    - Also added the possibility of using non-ints anchor points in transform methods.
    - Added generation methods:
        - cuboidFilled
        - cuboidHollow
        - cuboidOutlined
    - Added varint support!!! Before having more than 128 blockStates in the blockPalette of
      a structure or schematic would make it not save correctly. That's because schem files
      uses varint encoding and not "one byte is one block state", and "the next byte is another blockState".
      Now for palettes under 128 in length, saving can be pretty quick. However if more, even for the
      same size of schematics, it might be way, wayyy slower.
      Also loading a schematic from disk also takes longer if the palette is more than 128 long. 
      As we need to decode each varint, we get faced with in the byte stream!

11 - 27/09/2022: the moar editing update??? :OOOOO
                 nope! It's the full release 1! More commonly known as "MCSchematic 11-Release"
    - added horizontal rotation of block states when using the rotate() transform
    - added horizontal flipping of block states when using the flip() transform
    - added "getSubStructure()" and "getSubSchematic()" methods making you able to
      get only a portion of a main schematic.
    - added the "centerAround()" transformation which will center the structure at the inputted
      anchor point as best as it can with integer coordinates
    - added the "center()" transformation which will center the structure around 0, 0, 0 as
      best as it can with integer coordinates
    - fixed bug in .getBounds() where the xyzMins and Maxes would be initialized to 0, meaning
      that for example if a structure was in the XYZ+ octant of the 3d space, it's minXYZ would
      still be 0, 0, 0. Now initializing mins to -2^30 and maxes to 2^30, not sure how python
      handles numbers close to the integer limit, if its gonna add bits to them or not, so I prefer
      playing it safe and make sure they stay 32 bit numbers so that bounds computation can be as
      fast as possible!
    - Added docstrings for all "publicly" available methods! (this took 3 hours it was so boring help me)
    - 11.2 : Fixed a bug that made schematics be billions of blocks long or have negative volume
             if it is small enough and the right conditions are met bruh moment
'''

from enum import Enum
from typing import Union
import os
import math
import io
import itertools

from immutable_views import *
import nbtlib
from nbtlib import File
from nbtlib.tag import *


























"""
Contains random static utils useful for mcschematic!
"""
class RandomUtils:
    ''''''
    ### --- Public static methods

    @staticmethod
    def getRotatedList(li: list, rotationCount: int) -> list:
        """
        Rotates, or "barrel shifts" a list by the inputted amount.
        Found on StackOverflow, by D.R:
            https://stackoverflow.com/questions/2150108/efficient-way-to-rotate-a-list-in-python

        :param li: The list to rotate
        :param rotationCount: The number of rotations to do on the list, cannot be greater than the length
                              of the list, or less than minus the length of it.

        :return: A new rotated list.
        """

        return li[rotationCount:] + li[:rotationCount]

    '''
    
    '''
    @staticmethod
    def mathModulo(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:
        """
        Returns the math modulo of the inputed 2 numbers.
        Python % operator don't behave like the
        modulo operations in mathematics, so this method tries
        to recreate it.

        Algorithm from andrewmu here:
            https://stackoverflow.com/questions/5385024/mod-in-java-produces-negative-numbers

        :param x: X
        :param y: Y

        :return: The modulo of X by Y.
        """

        result = x % y
        if result < 0:
            result += y
        return result

    ### ---
























"""
Class containing static methods to manipulate block states, like for
example rotating or flipping them.
"""
class MCBlockStateManipulator:
    ''''''
    ### --- Fields

    # The possible horizontal rotations properties for a blockState. Also
    # in order for ease of 90 degree turning.
    _horizontalFacingPropertyRotationList: list[str] = ["north", "east", "south", "west"]

    # The suffixes of blocks to detect with .endswith() and their rotation types
    # we'll call the key the "endswith Id"
    # When we want to use a complete blockId put ":" at the start, as every blockId
    # is normalised to have "minecraft:" or at least have a ":" before the blockId.
    # Format: (endswithId, rotType, isNotEndswithId)
    # The "isNotEndswithId" list is here to prevent stuff like "mushroom_stem" interfering
    # with "crimson_stem" which have 2 different rotation types
    rotatableBlockIdSuffixToHorizontalRotationType: list[tuple[str, str]] = [
        # All horizontal facing property IMPLEMENTED
        ("_stairs", "horizontal", []),
        (":repeater", "horizontal", []),
        (":comparator", "horizontal", []),
        ("_wall_torch", "horizontal", []),
        (":wall_torch", "horizontal", []),
        (":furnace", "horizontal", []),
        (":smoker", "horizontal", []),
        ("_furnace", "horizontal", []),
        (":chest", "horizontal", []),
        ("_chest", "horizontal", []),
        (":tripwire_hook", "horizontal", []),
        (":lectern", "horizontal", []),
        (":jack_o_lantern", "horizontal", []),
        (":carved_pumpkin", "horizontal", []),
        (":ladder", "horizontal", []),
        ("_glazed_terracotta", "horizontal", []),
        ("_coral_wall_fan", "horizontal", []),
        ("_anvil", "horizontal", []),
        ("_wall_sign", "horizontal", []),
        ("_wall_head", "horizontal", []),
        ("_wall_skull", "horizontal", []),
        ("_fence_gate", "horizontal", []),
        (":grindstone", "horizontal", []),
        (":stone_cutter", "horizontal", []),
        (":bell", "horizontal", []),
        (":campfire", "horizontal", []),
        ("_campfire", "horizontal", []),
        ("_wall_banner", "horizontal", []),
        (":loom", "horizontal", []),
        (":lever", "horizontal", []),
        ("_button", "horizontal", []),
        ("_trapdoor", "horizontal", []),
        ("_door", "horizontal", []),

        # All horizontal_down facing property (Horizontal facing + down) IMPLEMENTED
        (":hopper", "horizontal_down", []),

        # All horizontal_up_down facing property (Horizontal facing + up + down) IMPLEMENTED
        (":end_rod", "horizontal_up_down", []),
        ("_shulker_box", "horizontal_up_down", []),
        (":shulker_box", "horizontal_up_down", []),
        (":shulker_box", "horizontal_up_down", []),
        ("_amethyst_bud", "horizontal_up_down", []),
        (":amethyst_cluster", "horizontal_up_down", []),
        (":barrel", "horizontal_up_down", []),
        (":dropper", "horizontal_up_down", []),
        (":dispenser", "horizontal_up_down", []),
        (":observer", "horizontal_up_down", []),
        (":piston", "horizontal_up_down", []),
        ("_piston", "horizontal_up_down", []),
        (":lightning_rod", "horizontal_up_down", []),
        ("_command_block", "horizontal_up_down", []),
        (":command_block", "horizontal_up_down", []),

        # Axis based rotatable blocks IMPLEMENTED
        ("_log", "axis", []),
        ("_wood", "axis", []),
        ("_stem", "axis", [":mushroom_stem"]),
        ("_hyphae", "axis", []),
        (":chain", "axis", []),
        (":basalt", "axis", []),
        (":polished_basalt", "axis", []),
        ("_pillar", "axis", []),
        (":bone_block", "axis", []),
        (":deepslate", "axis", []),
        (":infected_deepslate", "axis", []),
        ("_froglight", "axis", []),

        # Very specific case for 2 different block states IMPLEMENTED
        ("redstone_wire", "four_cardinal_direction_properties_that_defaults_to_none", []),
        ("_wall", "four_cardinal_direction_properties_that_defaults_to_none", []),

        # Sign-like (with 16 rotations)
        ("_sign", "sign_like", []),
        ("_skull", "sign_like", []),
        ("_head", "sign_like", []),
        ("_banner", "sign_like", []),

        # Glass-pane likes
        (":glass_pane", "glass_pane_like", []),
        ("_fence", "glass_pane_like", []),
        ("_glass_pane", "glass_pane_like", []),
        (":iron_bars", "glass_pane_like", []),
        (":vine", "glass_pane_like", []),
        (":glow_lichen", "glass_pane_like", []),
        (":sculk_vein", "glass_pane_like", []),
        (":mushroom_stem", "glass_pane_like", []),
        ("_mushroom_block", "glass_pane_like", []),

        # Rails
        (":rail", "rail", []),
        ("_rail", "rail", []),
    ]

    ### ---



    ### --- Public static methods

    @classmethod
    def getHorizontallyRotatedBlockState(cls, blockState: str, ninetyDegreeTurnCount: int) -> str:
        """
        Returns a horizontally rotated version of the inputted blockState in increments of 90
        degree turns defined by the .rotate() yaw transform.

        :param blockState: The block state to rotate
        :param ninetyDegreeTurnCount: The amount of 90 degree turns to do

        :return: A rotated version of the inputted blockState.
        """

        ## Sort the blockState in blockStateBlockIds and blockStateProperties (or whats
        ## inside the "[ ]" )
        blockStateBlockId = blockState
        blockStateProperties = ""
        if "[" in blockState:
            blockStateBlockId = blockState[:blockState.find("[")]
            blockStateProperties = blockState[blockState.find("["):]
        ## Normalize the blockStateBlockId to have "minecraft:" if there's no
        ## ":" prefix
        if ":" not in blockStateBlockId:
            blockStateBlockId = "minecraft:" + blockStateBlockId

        ## Create a rudimentary and very probably uncomplete property map
        ## of our blockStateProperties. Will be processed and then will
        ## be converted to string for our output blockState.
        propertyMap = MCBlockStateManipulator.parseBlockStateProperties(blockStateProperties)

        ## Variable setups
        # The turn count, which is a number between 0 and 3 representing how many
        # 90 degree CCW turns we need
        turnCount = RandomUtils.mathModulo(ninetyDegreeTurnCount, 4)

        ## Process / rotate the propertyMap

        # Check what the rotation type of the endswith Id
        rotationType = "Not found"
        for endswithId, rotType, isNotEndswithId in cls.rotatableBlockIdSuffixToHorizontalRotationType:

            if blockStateBlockId.endswith(endswithId):

                # Check if the id we have is not in the banned endswithIds, if it is
                # then this endswithId is wrong and we continue the main loop we're in
                continueMainLoop = False
                for bannedEndswithId in isNotEndswithId:
                    if blockStateBlockId.endswith(bannedEndswithId):
                        continueMainLoop = True
                if continueMainLoop:
                    continue

                # Since we found our rotation type, break as we don't wanna check for any other
                rotationType = rotType
                break

        # -- Dispatch the rotation type to see which rotation we doin

        # If we haven't found a rotation type, then we just return the blockState, since there's
        # nothing to rotate
        if rotationType == "Not found":
            return blockState

        # horizontal facing
        if rotationType == "horizontal":
            # Make sure that the property map has a facing property, if not,
            # initialise it to "north" since it's the default value for the horizontal
            # facing property
            propertyMap.setdefault("facing", "north")
            # To rotate horizontally we just need to rotate the facing
            newFacing = cls.getRotatedHorizontalFacingProperty(propertyMap["facing"],
                                                               ninetyDegreeTurnCount)
            # Put the new facing in the hashmap
            propertyMap["facing"] = newFacing

        # horizontal down facing which is basically hopper only lmfao
        if rotationType == "horizontal_down":
            # Make sure that the property map has a facing property, if not,
            # initialise it to "down" since it's the default value for the horizontal + down
            # facing property
            propertyMap.setdefault("facing", "down")

            # To rotate horizontally we just need to rotate the facing if it's not down
            if propertyMap["facing"] != "down":
                newFacing = cls.getRotatedHorizontalFacingProperty(propertyMap["facing"],
                                                                   ninetyDegreeTurnCount)
                # Put the new facing in the hashmap
                propertyMap["facing"] = newFacing

        if rotationType == "horizontal_up_down":
            # Make sure that the property map has a facing property, if not,
            # initialise it to "north" since it's the default value for the horizontal + up + down
            # facing property
            propertyMap.setdefault("facing", "north")

            # To rotate horizontally we just need to rotate the facing if it's not down or up
            if propertyMap["facing"] != "down" and propertyMap["facing"] != "up":
                newFacing = cls.getRotatedHorizontalFacingProperty(propertyMap["facing"],
                                                                   ninetyDegreeTurnCount)
                # Put the new facing in the hashmap
                propertyMap["facing"] = newFacing

        # Axis blocks such as logs and stuff
        if rotationType == "axis":
            # axis default is y
            propertyMap.setdefault("axis", "y")
            # if facing is not y then rotate, because the y axis doesn't rotate when
            # rotating on the xz plane!
            if propertyMap["axis"] != "y":
                # an even amount of 90 degree turns don't change the axis, only an
                # odd amount does
                if ninetyDegreeTurnCount % 2 == 1:
                    propertyMap["axis"] = "x" if propertyMap["axis"] == "z" else "z"

                    # funny code-golfer way of switching from x to z, or z to x lol
                    # propertyMap["facing"] = ["x", "z"][propertyMap["facing"] == "x"]

        # Redstone wire and walls
        if rotationType == "four_cardinal_direction_properties_that_defaults_to_none":
            # Fill up the property map's needed potentially absent values, which are the side values.
            # They are initialized to "none" if they don't exist
            propertyMap.setdefault("north", "none")
            propertyMap.setdefault("east", "none")
            propertyMap.setdefault("south", "none")
            propertyMap.setdefault("west", "none")

            # To rotate a redstone wire we put the 4 cardinal directions' values in a
            # list that we barrel shift depending on how many rotations
            # Order: north, east, south, west
            sideValues = [
                propertyMap["north"],
                propertyMap["east"],
                propertyMap["south"],
                propertyMap["west"]
            ]

            # Rotate the list, minus sign because organising the sideValues in the
            # NESW order which I will use a standard for convenience (ironic, I know),
            # makes it so that rotating the list actually makes it rotate the other way
            rotatedList = RandomUtils.getRotatedList(sideValues, -turnCount)

            # Re-assing the properties now that we epicly rotateted
            propertyMap["north"] = rotatedList[0]
            propertyMap["east"] = rotatedList[1]
            propertyMap["south"] = rotatedList[2]
            propertyMap["west"] = rotatedList[3]

        # Blocks like the sign's 16 rotations
        if rotationType == "sign_like":
            # rotation default is 0
            propertyMap.setdefault("rotation", "0")
            # calculate a new rotation, which is just the number of 90 degree
            # turns times 4 then we need to modulo it to be between 0 and 15
            propertyMap["rotation"] = str( (int(propertyMap["rotation"]) + 4 * ninetyDegreeTurnCount) % 16 )

        # 4 cardinals that are booleans, might use up and down too but since we
        # don't fiddle with those we fine!
        if rotationType == "glass_pane_like":
            # 4 cardinal directions defaults to false
            propertyMap.setdefault("north", "false")
            propertyMap.setdefault("east", "false")
            propertyMap.setdefault("south", "false")
            propertyMap.setdefault("west", "false")

            # Rotate using the redstone implementation
            sideValues: list[str] = [
                propertyMap["north"],
                propertyMap["east"],
                propertyMap["south"],
                propertyMap["west"]
            ]

            # Rotate the list, minus sign because organising the sideValues in the
            # NESW order which I will use a standard for convenience (ironic, I know),
            # makes it so that rotating the list actually makes it rotate the other way
            rotatedList = RandomUtils.getRotatedList(sideValues, -turnCount)

            # Re-assing the properties now that we epicly rotateted
            propertyMap["north"] = rotatedList[0]
            propertyMap["east"] = rotatedList[1]
            propertyMap["south"] = rotatedList[2]
            propertyMap["west"] = rotatedList[3]

        # rails FUN FUN FUN
        if rotationType == "rail":
            # for any rail, "shape" defaults to "north_south"
            propertyMap.setdefault("shape", "north_south")

            # setup
            shapeProp = propertyMap["shape"]
            # check if the rail is ascending, because we're gonna use the horizontal facing
            # rotation algorithm if so!
            if shapeProp.startswith("ascending_"):
                # Only get the cardinal part of the shape property
                facingOfAscension = shapeProp[shapeProp.find("_") + 1:]
                # Put it through the horizontal facing alg
                newFacingOfAscension = cls.getRotatedHorizontalFacingProperty(facingOfAscension,
                                                                              ninetyDegreeTurnCount)
                # Rebuild the shape prop
                newShape = "ascending_" + newFacingOfAscension
                # Set the prop
                propertyMap["shape"] = newShape

            else:
                # The shape right now is in the form of "cardinalDir_cardinalDir"

                # If the shapeProp is in an axis-type of shape, rotate the rail
                # with the axis algorithm
                if shapeProp == "north_south" or shapeProp == "east_west":
                    # Axis switch alg
                    if ninetyDegreeTurnCount % 2 == 1:
                        newShape = "north_south" if shapeProp == "east_west" else "east_west"
                        propertyMap["shape"] = newShape
                else:
                    # We are rotating an angled rail which is kind of like rotating
                    # a redstone dust pointing in 2 directions that next to one another

                    # Treat those directions as "true"s and "false"s in a list that we're
                    # gonna rotate, like redstone wires
                    currentDirectionsFaced = shapeProp.split("_")

                    # side trues and falses!
                    sideValues: list[bool] = [
                        "north" in currentDirectionsFaced,
                        "east" in currentDirectionsFaced,
                        "south" in currentDirectionsFaced,
                        "west" in currentDirectionsFaced
                    ]

                    # epic rotatéé
                    rotatedList = RandomUtils.getRotatedList(sideValues, -turnCount)

                    # The redstone wire algorithm stops here, because here we were supposed
                    # to put our result rotatedList inside the propertyMap but like, we just
                    # needed to rotate the map to rotate the shape itself, not 4 different
                    # properties!
                    # So right now we're gonna rebuild the shape. "north" and "south" comes
                    # first before "east" and "west"
                    # -- In the shape there's always either north or south, and either east or west.
                    #    So if the north in rotated list is false, we know the rail is south then.
                    shapeFirstPart = "north" if rotatedList[0] else "south"
                    shapeSecondPart = "east" if rotatedList[1] else "west"

                    # Get the new shape and put it in the hashmap!
                    newShape = shapeFirstPart + "_" + shapeSecondPart
                    propertyMap["shape"] = newShape

        ## We've processed the block state, it's time to return
        returnedBlockState = \
            blockStateBlockId + MCBlockStateManipulator.propertyMapToBlockStatePropertiesString(propertyMap)
        return returnedBlockState

    @classmethod
    def getHorizontallyFlippedBlockState(cls, blockState: str, flippingPlane: str) -> str:
        """
        Returns a horizontally flipped version of the inputted blockState by the inputted flippingPlane.

        :param blockState: The blockState to rotate
        :param flippingPlane: The plane by which the blockState will be flipped by

        :return: A flipped version of the inputted blockStaet by the inputted flippingPlane.
        """

        ## Sort the blockState in blockStateBlockIds and blockStateProperties (or whats
        ## inside the "[ ]" )
        blockStateBlockId = blockState
        blockStateProperties = ""
        if "[" in blockState:
            blockStateBlockId = blockState[:blockState.find("[")]
            blockStateProperties = blockState[blockState.find("["):]
        ## Normalize the blockStateBlockId to have "minecraft:" if there's no
        ## ":" prefix
        if ":" not in blockStateBlockId:
            blockStateBlockId = "minecraft:" + blockStateBlockId

        ## Create a rudimentary and very probably uncomplete property map
        ## of our blockStateProperties. Will be processed and then will
        ## be converted to string for our output blockState.
        propertyMap = MCBlockStateManipulator.parseBlockStateProperties(blockStateProperties)


        ## Process / flip the propertyMap
        # Check the rotation type of the endswith Id
        # Will be used to flip instead of rotating
        rotationType = "Not found"
        for endswithId, rotType, isNotEndswithId in cls.rotatableBlockIdSuffixToHorizontalRotationType:

            if blockStateBlockId.endswith(endswithId):

                # Check if the id we have is not in the banned endswithIds, if it is
                # then this endswithId is wrong and we continue the main loop we're in
                continueMainLoop = False
                for bannedEndswithId in isNotEndswithId:
                    if blockStateBlockId.endswith(bannedEndswithId):
                        continueMainLoop = True
                if continueMainLoop:
                    continue

                # Since we found our rotation type, break as we don't wanna check for any other
                rotationType = rotType
                break

        # -- Dispatch, the algorithms are kind of the same as the rotation ones,
        # -- or at least the boilerplate code is, so I wont recomment it lol

        # If we haven't found a rotation type, then we just return the blockState, since there's
        # nothing to rotate
        if rotationType == "Not found":
            return blockState

        # horizontal facing
        if rotationType == "horizontal":
            propertyMap.setdefault("facing", "north")
            newFacing = cls.getHorizontallyFlippedFacingProperty(propertyMap["facing"],
                                                                 flippingPlane)
            propertyMap["facing"] = newFacing

        # horizontal down facing which is basically hopper only lmfao
        if rotationType == "horizontal_down":
            propertyMap.setdefault("facing", "north")
            # Check if not down, in which case we wouldn't be able to flip
            if propertyMap["facing"] != "down":
                newFacing = cls.getHorizontallyFlippedFacingProperty(propertyMap["facing"],
                                                                     flippingPlane)
                propertyMap["facing"] = newFacing

        # horizontal down up facing
        if rotationType == "horizontal_up_down":
            propertyMap.setdefault("facing", "north")
            # Check if not down or up, in which case we wouldn't flip anything
            if propertyMap["facing"] != "down" and propertyMap["facing"] != "up":
                newFacing = cls.getHorizontallyFlippedFacingProperty(propertyMap["facing"],
                                                                     flippingPlane)
                propertyMap["facing"] = newFacing

        # Axis blocks such as logs and stuff
        if rotationType == "axis":
            # Epic fun fact, axis blocks don't change when flipped :exploding_head:
            pass

        # Redstone wire and walls, smaller version than the rotation one
        # using for loops lol
        if rotationType == "four_cardinal_direction_properties_that_defaults_to_none":

            # Set the default for all facing directions
            for facing in cls._horizontalFacingPropertyRotationList:
                propertyMap.setdefault(facing, "none")

            # Put the side values in the NESW order inside a list that we're gonna flip
            sideValues = [ propertyMap[facing] for facing in cls._horizontalFacingPropertyRotationList ]

            # Flip the side values
            flippedSideValues = cls.getHorizontallyFlippedSideValueList(sideValues, flippingPlane)

            # Put the flipped side values into the property map
            for index, facing in enumerate(cls._horizontalFacingPropertyRotationList):
                propertyMap[facing] = flippedSideValues[index]

        # Blocks like the sign's 16 rotations
        if rotationType == "sign_like":
            propertyMap.setdefault("rotation", "0")
            rotationAsInt = int(propertyMap["rotation"])

            newRotationInt = rotationAsInt
            # Depending on the flipping plane, reflect the rotation
            # in a different way
            if flippingPlane == "xy":
                if rotationAsInt < 8:
                    newRotationInt = 8 - rotationAsInt
                if rotationAsInt >= 8:
                    newRotationInt = 24 - rotationAsInt

            if flippingPlane == "yz":
                if 4 <= rotationAsInt < 12:
                    newRotationInt = 16 - rotationAsInt
                else:
                    newRotationInt = 0 - rotationAsInt

            # When we're done flipping, modulo the new rotation so that it's
            # in range of 16
            newRotationInt = RandomUtils.mathModulo(newRotationInt, 16)

            # Put the new rotation inside the property map at the end
            propertyMap["rotation"] = str(newRotationInt)

        # 4 cardinals that are booleans, might use up and down too but since we
        # don't fiddle with those we fine! Basically like redstone and walls but
        # with default to "false" :o
        if rotationType == "glass_pane_like":

            # Set the default for all facing directions
            for facing in cls._horizontalFacingPropertyRotationList:
                propertyMap.setdefault(facing, "false")

            # Put the side values in the NESW order inside a list that we're gonna flip
            sideValues: list[str] = [propertyMap[facing] for facing in cls._horizontalFacingPropertyRotationList]

            # Flip the side values
            flippedSideValues = cls.getHorizontallyFlippedSideValueList(sideValues, flippingPlane)

            # Put the flipped side values into the property map
            for index, facing in enumerate(cls._horizontalFacingPropertyRotationList):
                propertyMap[facing] = flippedSideValues[index]

        # rails FUN FUN FUN
        if rotationType == "rail":
            # for any rail, "shape" defaults to "north_south"
            propertyMap.setdefault("shape", "north_south")

            # setup
            shapeProp = propertyMap["shape"]
            # check if the rail is ascending, because we're gonna use the horizontal facing
            # rotation algorithm if so!
            if shapeProp.startswith("ascending_"):
                facingOfAscension = shapeProp[shapeProp.find("_") + 1:]
                newFacingOfAscension = cls.getHorizontallyFlippedFacingProperty(facingOfAscension,
                                                                                flippingPlane)
                newShape = "ascending_" + newFacingOfAscension
                propertyMap["shape"] = newShape

            else:
                # The shape right now is in the form of "cardinalDir_cardinalDir"

                # If the shapeProp is in an axis-type of shape, flip the rail
                # with the axis algorithm
                if shapeProp == "north_south" or shapeProp == "east_west":
                    # Axis switch alg
                    # Again, flipping axis-type rotatable blocks won't change it
                    # at all!
                    pass
                else:
                    # We are rotating an angled rail which is kind of like rotating
                    # a redstone dust pointing in 2 directions that next to one another

                    # Won't re-comment everything here
                    currentDirectionsFaced = shapeProp.split("_")
                    # side trues and falses!
                    sideValues: list[bool] = [
                        facing in currentDirectionsFaced for facing in cls._horizontalFacingPropertyRotationList
                    ]
                    # epic flip
                    rotatedList = cls.getHorizontallyFlippedSideValueList(sideValues, flippingPlane)

                    shapeFirstPart = "north" if rotatedList[0] else "south"
                    shapeSecondPart = "east" if rotatedList[1] else "west"

                    # Get the new shape and put it in the hashmap!
                    newShape = shapeFirstPart + "_" + shapeSecondPart
                    propertyMap["shape"] = newShape


        # Little hard coding for doors, when a schem is flipped, it looks like (though im not
        # 100% sure it happens in all situations) that the hinge always switches
        if blockStateBlockId.endswith("_door"):
            propertyMap.setdefault("hinge", "left")
            propertyMap["hinge"] = "right" if propertyMap["hinge"] == "left" else "left"

        # Another little hard coding for stairs this time, it also looks like that
        # the shape if anything other than straight (so outer_left|right or inner_left|right)
        # always switches
        if blockStateBlockId.endswith("_stairs"):
            propertyMap.setdefault("shape", "straight")
            shapeProp = propertyMap["shape"]
            if shapeProp != "straight":
                # We either got inner or outer left|right, so
                # we only need to switch which way the shape points
                shapeStart = shapeProp[ :shapeProp.find("_")]
                shapeEnd = shapeProp[shapeProp.find("_")+1: ]
                # Calc new shape end
                newShapeEnd = "right" if shapeEnd == "left" else "left"
                # Reconstruct new shape
                newShape = shapeStart + "_" + newShapeEnd
                # Put in property hashmap at the end, like a return
                propertyMap["shape"] = newShape



        ## We've processed the block state, it's time to return
        returnedBlockState = \
            blockStateBlockId + MCBlockStateManipulator.propertyMapToBlockStatePropertiesString(propertyMap)
        return returnedBlockState

    @staticmethod
    def parseBlockStateProperties(blockStateProperties: str) -> dict[str, str]:
        """
        Converts the inputted blockState properties into a map mapping
        a string which is the property name, to its value.

        Example:
            - blockState = "minecraft:stairs[facing=east,shape=inner_left]"
            - the blockState properties are "[facing=east,shape=inner_left]"
            - and they will get parsed into {"facing": "east", "shape": "inner_left"}

        :param blockStateProperties: The blockState properties to parse

        :return: A parsed map of the inputted blockState properties.
        """

        # Check if the block has no properties at all
        if blockStateProperties == "":
            return {}

        # Remove the "[" and "]" of the blockProperties
        blockStateProperties = blockStateProperties[1:-1]
        # Remove the spaces in case someone uses spaces in their properties
        blockStateProperties = blockStateProperties.replace(" ", "")
        # Split the properties to only get strings like "north=none"
        splittedRawProperties = blockStateProperties.split(",")

        # Creation of the map
        outPropertyMap = {}
        for rawProperty in splittedRawProperties:
            # Split the property at "=" to get the key and value
            propKV = rawProperty.split("=")
            propertyName, propertyValue = propKV[0], propKV[1]
            # Put in map
            outPropertyMap[propertyName] = propertyValue

        # Retrun
        return outPropertyMap

    @classmethod
    def getRotatedHorizontalFacingProperty(cls, horizontalFacingProperty: str, ninetyDegreeTurnCount: int) -> str:
        """
        Returns the inputted horizontal facing property turned by the amount of inputted ninety degree turns.
        A horizontal facing property can be any of the 4 cardinal directions.

        :param horizontalFacingProperty: The horizontal facing property to rotate
        :param ninetyDegreeTurnCount: The number of 90 degree turns to apply

        :return: The rotated inputted horizontal facing property.
        """

        rotations = cls._horizontalFacingPropertyRotationList
        newRotationIndex = \
            RandomUtils.mathModulo((rotations.index(horizontalFacingProperty) + ninetyDegreeTurnCount), 4)
        return rotations[newRotationIndex]

    '''
    
    '''
    @staticmethod
    def fillAbsentPropertiesInPropertyMap(propertyMap: dict[str, str], defaultPropertyMap: dict[str, str]):
        """
        When parsing property maps, there might be properties that aren't there at all.
        If so, add them to the map according to a passed defaultPropertyMap.

        :param propertyMap: The property map to fill up properties with
        :param defaultPropertyMap: The property map with all the default values we want to fill up
                                   the main map with.
        """

        # Loop over every property of the default property map, and if the
        # property isn't in the propertyMap then we add it
        for propertyName in defaultPropertyMap.keys():  # using .keys() because more explicit
            if propertyName not in propertyMap:
                propertyMap[propertyName] = defaultPropertyMap[propertyName]

    @staticmethod
    def propertyMapToBlockStatePropertiesString(propertyMap: dict[str, str]) -> str:
        """
        Returns the inputted property map as a properties string used in blockStates.

        Example:
            - PropertyMap = {"facing": "east", "shape": "inner_left"}
            - PropertyMap to string -> [facing=east,shape=inner_left]

        :param propertyMap: The property map to convert to blockState property string

        :return: The blockState property map to blockState property string.
        """

        # If the property map is empty, then the blockState doesn't have anything
        # after its block Id, but without this check it would return "[]"
        if len(propertyMap) == 0:
            return ""

        entriesAsStrings = list(map(lambda kv: f"{kv[0]}={kv[1]}", propertyMap.items()))
        return "[" + ",".join(entriesAsStrings) + "]"

    @staticmethod
    def rotateHorizontalFacingBlockStatesPropertyMap(propertyMap: dict[str, str],
                                                     defaultPropertyMap: dict[str, str],
                                                     ninetyDegreeTurnCount: int):
        """
        Rotates the inputted property map assuming it is linked to a horizontal facing blockState.
        (some block states have facings like "up" and "down" as well which isn't supported here.)

        :param propertyMap: The property map to rotate the horizontal facing property of
        :param defaultPropertyMap: The default property map of the inputted property map's blockState
        :param ninetyDegreeTurnCount: The amount of 90 degree turns to apply
        """

        # Fill up the property map's absent values
        MCBlockStateManipulator.fillAbsentPropertiesInPropertyMap(propertyMap, defaultPropertyMap)
        # To rotate horizontally we just need to rotate the facing
        newFacing = MCBlockStateManipulator.getRotatedHorizontalFacingProperty(propertyMap["facing"],
                                                                               ninetyDegreeTurnCount)
        # Put the new facing in the hashmap
        propertyMap["facing"] = newFacing

    @classmethod
    def getHorizontallyFlippedFacingProperty(cls, horizontalFacingProperty: str, flippingPlane: str) -> str:
        """
        Returns the inputted horizontal facing property but flipped along the inputted plane.

        :param horizontalFacingProperty: The horizontal facing property to flip
        :param flippingPlane: The plane to flip the facing property by

        :return: The flipped horizontal facing property
        """

        if flippingPlane == "xy":
            if horizontalFacingProperty in ("north", "south"):
                # Flip
                return "south" if horizontalFacingProperty == "north" else "north"

        if flippingPlane == "yz":
            if horizontalFacingProperty in ("east", "west"):
                # Flip
                return "west" if horizontalFacingProperty == "east" else "east"

        return horizontalFacingProperty

    @staticmethod
    def getHorizontallyFlippedSideValueList(sideValueList: list, flippingPlane: str) -> list[str]:
        """
        Returns a new list which is the horizontal flip of the inputted side value list depending on the
        inputted flipping plane.
        Primarily used for redstone and wall flipping!

        A side value list is the list of the 4 values of the 4 cardinal directions of blocks
        that supports such side values, like redstone. Where the north, east, south, west
        properties are either: none, side or up

        :param sideValueList: The side value list to be horizontally flipped
        :param flippingPlane: The plane to flip the side value list by

        :return: A new list which is the flipped inputted side value list
        """

        flippedList = sideValueList.copy()

        if flippingPlane == "xy":
            # If plane is XY, switch the north and south values
            flippedList[0], flippedList[2] = flippedList[2], flippedList[0]

        if flippingPlane == "yz":
            # If plane is YZ, switch the east and west values
            flippedList[1], flippedList[3] = flippedList[3], flippedList[1]

        return flippedList

    ### ---



























class MCStructure:
    """
    Class representing a Minecraft structure, nothing to do with actual minecraft NBT
    structure files though. It's just a class that holds blocks, with additional methods
    for manipulating them such as translation, rotation etc...

    Used in the MCSchematic class as its block container, but an MCStructure can be a
    standalone.
    """

    ### --- Fields

    # The block palette used in this structure
    # The key is the block nbt representation and the content is the
    # id. Air is the first id in the palette, for convenience.
    _blockPalette: {Union[str, int]: Union[str, int]} = {'minecraft:air': 0, 0: 'minecraft:air'}
    _blockPaletteFreeId: int = 1

    # HashMap of all the blocks, with the key being their position,
    # and the content being the id of the block in the block palette.
    # We don't want to populate _blocks with strings otherwise it's going
    # to use an ungodly amount of RAM for nothing
    _blockStates: {tuple[int, int, int]: int} = {}

    # The HashMap of block entities in this schem. We store the string representation
    # for each of them instead of a fancy ID system.
    # We know we have a block entity when the inputed blockData has a { or }.
    # The tuple key is the position in the schem, and the value is the blockData str.
    _blockEntities: {tuple[int, int, int]: str} = {}

    ### ---



    ### --- Init

    def __init__(self):
        self._blockPalette = {'minecraft:air': 0, 0: 'minecraft:air'}
        self._blockPaletteFreeId = 1
        self._blockStates = {}
        self._blockEntities = {}

    ### ---



    ### --- Public methods

    def setBlock(self, position: tuple[int, int, int], blockData: str):
        """
        Sets a blockData in the structure at the inputed position.

        A blockData is a string format representing any block in
        Minecraft, used in commands such as /setblock or /fill.

        :param position: The position to place the blockData at
        :param blockData: The blockData to place
        """

        # We have a different treatment between block entities
        # and normal blocks.
        #if not self._isBlockDataBlockEntity(blockData):
        if blockData[-1] != '}':
            self._setBlockState(position, blockData)
        else:
            self._setBlockEntity(position, blockData)

    def getBlockStateAt(self, position: tuple[int, int, int]) -> str:
        """
        Returns the blockState at the inputted position in the structure.

        :param position: The position in the structure to get the blockState from.

        :return The blockState at the inputted position.
        """

        # If there isn't any blocks at that position, return none
        if position in self._blockStates:
            blockPaletteId = self._blockStates[position]
            return self._blockPalette[blockPaletteId]
        else:
            return "minecraft:air"

    def getBlockDataAt(self, position: tuple[int, int, int]) -> str:
        """
        Return the blockData at the inputted position.

        :param position: The position in the structure to get the blockData from.

        :return: The blockData at the inputted position.
        """

        # The block datas are arranged in a way that blockEntities are a subset
        # of blockStates with just NBT information. So we check for blockEntities first,
        # then blockStates and then if nothing comes up we return minecraft:air!
        if position in self._blockEntities:
            return self._blockEntities[position]
        if position in self._blockStates:
            blockPaletteId = self._blockStates[position]
            return self._blockPalette[blockPaletteId]
        return "minecraft:air"

    def getInternalBlockPalette(self) -> DictView:
        """
        Returns an unmodifiable view of this schematic's current internal block palette, which
        is how the block palette is represented internally in the schematic object, not how
        it really gets saved to the schematic file.

        The unmodifiable view is her ein an attempt to heavily discourage trying to modify the
        block palette yourself. Do not try to modify the block palette unless you know *exactly*
        what you're doing.

        :return: An unmodifiable view of this structure's internal block palette.
        """

        return DictView(self._blockPalette)

    def getBlockPalette(self) -> DictView:
        """
        Returns the block palette of this schematic as how it would be saved in the schematic file,
        so in the format of {blockState: id}.

        Note that this method takes in the internal block palette, and then cleans it up manually
        everytime it's called, so it can be computationally intensive if the palette is big
        and it's called a lot.

        :return: The clean block palette of this structure.
        """

        cleanBlockPalette = \
            {
                blockData: Int(blockPaletteId)
                for blockData, blockPaletteId in self._blockPalette.items()
                if type(blockData) == str
            }
        return DictView(cleanBlockPalette)

    def getBlockStates(self) -> DictView:
        """
        Returns an unmodifiable view of the internal blockStates hashmap present in the schematic object.
        The keys of this dict are the positions of the blockStates, and the values are their ID
        in the hashmap.

        The unmodifiable view is in an attempt to heavily discourage trying to modify the block
        palette yourself. Do not try to modify the block palette unless you know *exactly* what
        you're doing.

        :return: An unmodifiable view of the internal blockStates hashmap.
        """

        return DictView(self._blockStates)

    def getBlockEntities(self) -> DictView:
        """
        Returns an unmodifiable view of the internal blockEntities hashmap present in the schematic object.
        The keys of this dict are the positions of the blockEntities, and the values are their blockData
        in the hashmap.

        The unmodifiable view is in an attempt to heavily discourage trying to modify the block
        palette yourself. Do not try to modify the block palette unless you know *exactly* what
        you're doing.

        :return: An unmodifiable view of the internal blockEntities hashmap
        """

        return DictView(self._blockEntities)

    '''
    
    '''
    def getBounds(self) -> tuple[tuple[int, int, int], tuple[int, int, int]]:
        """
        Computes the -X -Y -Z and +X +Y +Z corners of this structure's bounds.
        Can be thought of as returning the 2 corners of the cuboid
        containing the structure.

        Warning: this method can be very computationally intensive, use it carefully.

        :return: The -XYZ and +XYZ corners of this structure.
        """

        ## Init the mins and maxes with a random block in the hashmap, as any
        ## block in the hashmap is susceptible of being a max and min.
        ## Also solves a bug I had when I was setting those values to -1B and +1B,
        ## if there weren't enough blocks, a max could have not been set altogether

        ## Get the first key in the dict, by creating a generator instead of using
        ## random.choice which needs a list which can be really costly
        randomBlockPos = (k for k in self._blockStates.keys()).__next__()
        xMin, yMin, zMin = randomBlockPos
        xMax, yMax, zMax = randomBlockPos

        keys = self._blockStates.keys()

        for p in keys:
            if p[0] < xMin:
                xMin = p[0]
            elif p[0] > xMax:
                xMax = p[0]

            if p[1] < yMin:
                yMin = p[1]
            elif p[1] > yMax:
                yMax = p[1]

            if p[2] < zMin:
                zMin = p[2]
            elif p[2] > zMax:
                zMax = p[2]

        return (xMin, yMin, zMin), (xMax, yMax, zMax)

    @staticmethod
    def getStructureDimensions(structureBounds: tuple[tuple[int, int, int], tuple[int, int, int]]) \
            -> tuple[int, int, int]:
        """
        Returns the X, Y and Z lengths of this structure, from the bounds.
        We need to input the bounds, because as they're really intensive
        to calculate, I think it's better if it's the programmer's job to
        cache them as needed.

        :param structureBounds: The bounds of this structure

        :return: The X, Y and Z lengths of this structure.
        """

        return structureBounds[1][0] - structureBounds[0][0] + 1, \
               structureBounds[1][1] - structureBounds[0][1] + 1, \
               structureBounds[1][2] - structureBounds[0][2] + 1

    def placeStructure(self, incomingStructure: 'MCStructure', placePosition: tuple[int, int, int]):
        """
        Places the inputted structure inside of this structure, at the inputted position.

        :param incomingStructure: The structure to place
        :param placePosition: The position at which we want to place the structure at
        """

        # Go through each block position of the incoming structure and get their block data, then
        # setBlock in this structure. Not the cheapest way to do it, especially considering
        # I have access to the internals so I shouldn't have to use setBlock, it's still
        # easier to implement for now and will probably not be a problem anytime soon.
        #
        # Also going through the blockStates, as for each blockEntity, there is an
        # associated blockState to it, so I don't need to loop over blockEntities too
        # as I am sure I will loop over every coordinates.
        for blockPosition, blockState in incomingStructure.getBlockStates().items():
            # The block data from the incoming structure
            blockDataHere = incomingStructure.getBlockDataAt(blockPosition)
            # The position of this blockData but in this structure instead
            blockPosInThisStructure = (blockPosition[0] + placePosition[0],
                                       blockPosition[1] + placePosition[1],
                                       blockPosition[2] + placePosition[2])
            # Place!
            self.setBlock(blockPosInThisStructure, blockDataHere)

        # For chaining
        return self

    def makeCopy(self) -> 'MCStructure':
        """
        Makes and returns a deep copy of this structure. So the returned structure
        won't have its internals shared with the new one, it will have totally new
        internals. And thus modifying this structure won't affect another one.

        :return: A copy of this MCStructure.
        """

        # Instantiate new structure that we're gonna return
        newStructure = MCStructure()
        # Copy the data and put it inside the new structure using
        # cheeky private member accessing
        newStructure._blockPalette = self._blockPalette.copy()
        newStructure._blockPaletteFreeId = self._blockPaletteFreeId
        newStructure._blockStates = self._blockStates.copy()
        newStructure._blockEntities = self._blockEntities.copy()
        # retrun
        return newStructure

    def getSubStructure(self, corner1: tuple[int, int, int], corner2: tuple[int, int, int], reCenter:bool = False) \
            -> 'MCStructure':
        """
        Returns a new structure which is all the blocks in the calling structure
        that are inside the inputted cuboid (both coordinates inclusive).

        Note that the position of the blocks in the new structure aren't changed
        from the calling structure, if the cuboid is off-center, the resulting
        subStructure will also be.
        (i.e a block at (0, 3, 2) will stay at (0, 3, 2) in the resulting subStructure)
        Unless the "reCenter" variable is set to True. In which case the structure is going to
        be centered around 0, 0, 0

        :param corner1: The first corner of the inputted cuboid
        :param corner2: The second corner of the inputted cuboid
        :param reCenter: If True, will center the subStructure around 0, 0, 0

        :return: Returns a new structure which is all the blocks in the calling structure
                 that are inside the inputted cuboid (both cuboids corners are inclusive).
        """

        # Instantiate the new subStructure that we're gonna return later
        subStructure = MCStructure()
        # Sort the corners for easier computing later
        corner1, corner2 = self._Utils.sortCuboidCorners(corner1, corner2)

        # Go through each block present in this structure and see if they're
        # inside the cuboid, if so we get the blockData there and .setBlock() it
        # in the newer structure.
        # We still using setBlock() and getBlockDataAt() but at least it's not over
        # every possible block of the cuboid xd
        for blockPosition, blockState in self._blockStates.items():

            # Get if in cuboid, if not just go on to the next block
            isInCuboid = (corner1[0] <= blockPosition[0] <= corner2[0]) and \
                         (corner1[1] <= blockPosition[1] <= corner2[1]) and \
                         (corner1[2] <= blockPosition[2] <= corner2[2])
            if not isInCuboid:
                continue

            # Get the block data then put it inside of the new structure
            blockDataHere = self.getBlockDataAt(blockPosition)
            subStructure.setBlock(blockPosition, blockDataHere)

        # Recenter the sturcture, by definition the bounds of the new structure is the sorted
        # cuboid coordinates inputted, so we don't have to recalculate bounds
        if reCenter:
            subStructure.center((corner1, corner2))

        # Retrun
        return subStructure



    """
    TRANSFORMS
    """
    def translate(self, translationVector: tuple[Union[int, float], Union[int, float], Union[int, float]]):
        """
        [Transformation method]

        Translates every block in this structure
        Can be computationally intensive, like most transformation methods.

        :param translationVector: The amount of blocks in XYZ for each block to be translated by.
        """

        # For translation, we create a new blockState and blockEntities hashmap
        # with every block position translated.
        # We do it this way because modifying the keys of the hashmap directly
        # might overwrite already present blocks that have yet to be iterated upon

        translationVector = (round(translationVector[0]),
                             round(translationVector[1]),
                             round(translationVector[2]))

        newBlockStates = {}
        newBlockEntities = {}

        # Iterating over states and then testing if this state is
        # also a blockEntity
        for blockPosition, blockState in self._blockStates.items():

            # Translate the position and put the block state in the new hashmap
            translatedPosition = (blockPosition[0] + translationVector[0],
                                  blockPosition[1] + translationVector[1],
                                  blockPosition[2] + translationVector[2])
            newBlockStates[translatedPosition] = blockState

            # See if this blockState is also a block entity, if so put it in
            # the new BE hashmap
            if blockPosition in self._blockEntities:
                blockEntityData = self._blockEntities[blockPosition]
                newBlockEntities[translatedPosition] = blockEntityData

        # Set the new hashmaps
        self._blockStates = newBlockStates
        self._blockEntities = newBlockEntities

        # For chaining
        return self

    def scaleXYZ(self,
                 anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
                 scaleX: float, scaleY: float, scaleZ: float):
        """
        [Transformation method]

        Scales the X, Y and Z components of every block's coordinates according to
        an anchor point and a different scalar for each axis.

        :param anchorPoint: The point to scale the structure from
        :param scaleX: The X scalar
        :param scaleY: The Y scalar
        :param scaleZ: The Z scalar
        """

        # Setup
        newBlockStates = {}
        newBlockEntities = {}


        # Go through each block and scale their vertices and put them
        # inside the new hashmaps
        for blockPosition, blockState in self._blockStates.items():

            # Firstly offset the position to the anchor point
            blockPos = (blockPosition[0] - anchorPoint[0],
                        blockPosition[1] - anchorPoint[1],
                        blockPosition[2] - anchorPoint[2])

            # Apply the scaling
            blockPos = (blockPos[0] * scaleX,
                        blockPos[1] * scaleY,
                        blockPos[2] * scaleZ)

            # Offset the positions again to put them where they would be originally
            # but just scaled around the anchor point
            # Also floor the block position so that we have integer coordinates
            blockPos = (round( blockPos[0] + anchorPoint[0] ),
                        round( blockPos[1] + anchorPoint[1] ),
                        round( blockPos[2] + anchorPoint[2] ))

            # Put the blockState in the new hashmap
            newBlockStates[blockPos] = blockState
            # See if this blockState is also a block entity, if so put it in
            # the new BE hashmap
            if blockPosition in self._blockEntities:
                blockEntityData = self._blockEntities[blockPosition]
                newBlockEntities[blockPos] = blockEntityData


        # Set the new hashmaps
        self._blockStates = newBlockStates
        self._blockEntities = newBlockEntities

        # For chaining
        return self

    def scale(self,
              anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
              scalar: float):
        """
        [Transformation method]

        Scales the X, Y and Z components of every block's coordinates according to
        an anchor point and a scalar.

        :param anchorPoint: The point to scale the structure from
        :param scalar: The XYZ scalar
        """

        return self.scaleXYZ(anchorPoint, scalar, scalar, scalar)

    def rotateRadians(self,
                      anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
                      yaw:float =0.0, pitch:float =0.0, roll:float =0.0,
                      rotateBlockStates:bool =True):
        """
        [Transformation method]

        Rotates this schem's block positions by the inputted yaw, pitch and roll.
        Note that it doesn't rotate the block states. So a comparator facing east rotated
        pi/2 degrees will make the comparator face north.

        Angles are assumed to be in a form where negative means CW, and positive angles are
        CCW.

        Rotations and how to determine which way is CCW and which way is CW:
            - Yaw is a rotation around the Y axis, looking up (Y+) at the XZ plane.
            - Pitch is a rotation around the X axis, looking east (X+) at the ZY plane.
            - Roll is a rotation around the Z axis, looking south (Z+) at the XY plane.

        We also have an option to rotate the block states. Right now only horizontal (yaw)
        rotation is available with block state rotation. There is currently no plan to add
        vertical rotation.

        :param anchorPoint: The point to rotate the schem around of
        :param yaw: The yaw component of the rotation
        :param pitch: The pitch component of the rotation
        :param roll: The roll component of the rotation
        :param rotateBlockStates: Whether or not we want to not only rotate the block's positions but also
                                  rotate the block's directions. (Like stairs and stuff)
        """

        # Setup
        newBlockStates = {}
        newBlockEntities = {}
        # The cos and sin values
        # Yaw
        # Negating the yaw because with the definition of this yaw
        # and the rotation matrix definition the angles are
        # actually going opposite ways
        cosYaw = math.cos(-yaw)
        sinYaw = math.sin(-yaw)
        # Pitch
        cosPitch = math.cos(pitch)
        sinPitch = math.sin(pitch)
        # Roll
        # Negating the roll for the same reasons I negated the yaw
        cosRoll = math.cos(-roll)
        sinRoll = math.sin(-roll)


        # Go through each block and rotate their vertices and put them
        # inside the new hashmaps
        for blockPosition, blockState in self._blockStates.items():

            # Firstly offset the position to the anchor point
            blockPos = (blockPosition[0] - anchorPoint[0],
                        blockPosition[1] - anchorPoint[1],
                        blockPosition[2] - anchorPoint[2])

            # Apply rotations
            if yaw != 0.0:
                blockPos = (blockPos[2]*sinYaw + blockPos[0]*cosYaw,
                            blockPos[1],
                            blockPos[2]*cosYaw - blockPos[0]*sinYaw)

            if pitch != 0.0:
                blockPos = (blockPos[0],
                            blockPos[2]*sinPitch + blockPos[1]*cosPitch,
                            blockPos[2]*cosPitch - blockPos[1]*sinPitch)

            if roll != 0.0:
                blockPos = (blockPos[0]*cosRoll - blockPos[1]*sinRoll,
                            blockPos[0]*sinRoll + blockPos[1]*cosRoll,
                            blockPos[2])

            # Offset the positions again to put them where they would be originally
            # but just rotated around the anchor point
            # Also floor the block position so that we have integer coordinates
            blockPos = (round( blockPos[0] + anchorPoint[0] ),
                        round( blockPos[1] + anchorPoint[1] ),
                        round( blockPos[2] + anchorPoint[2] ))

            # Put the blockState in the new hashmap
            newBlockStates[blockPos] = blockState
            # See if this blockState is also a block entity, if so put it in
            # the new BE hashmap
            if blockPosition in self._blockEntities:
                blockEntityData = self._blockEntities[blockPosition]
                newBlockEntities[blockPos] = blockEntityData

        # Rotate the block palette!
        if rotateBlockStates:
            # -- Calculate how many 90 degree turns we need
            # Calc the angle of rotation between 0 and 360
            positiveRotationAngle = RandomUtils.mathModulo(yaw, math.tau)
            # Calc the 90 degree turn count
            ninetyDegreeTurnCount = math.floor( (positiveRotationAngle + math.pi/4) / (math.pi / 2) ) % 4

            # Rotaté if needed
            if ninetyDegreeTurnCount != 0:
                # Epic rotaté of the epic block palette
                newBlockPalette = MCStructure._Utils.getHorizontallyRotatedBlockPalette(self._blockPalette,
                                                                                        ninetyDegreeTurnCount)
                # Set the new block palette
                self._blockPalette = newBlockPalette

        # Set the new hashmaps
        self._blockStates = newBlockStates
        self._blockEntities = newBlockEntities

        # For chaining
        return self

    def rotateDegrees(self,
                      anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
                      yaw:float =0.0, pitch:float =0.0, roll:float =0.0,
                      rotateBlockStates:bool = True):
        """
        [Transformation method]

        Rotates this schem's block positions by the inputted yaw, pitch and roll.
        Note that it doesn't rotate the block states. So a comparator facing east rotated
        90 degrees will make the comparator face north.

        Angles are assumed to be in a form where negative means CW, and positive angles are
        CCW.

        Rotations and how to determine which way is CCW and which way is CW:
            - Yaw is a rotation around the Y axis, looking up (Y+) at the XZ plane.
            - Pitch is a rotation around the X axis, looking east (X+) at the ZY plane.
            - Roll is a rotation around the Z axis, looking south (Z+) at the XY plane.

        We also have an option to rotate the block states. Right now only horizontal (yaw)
        rotation is available with block state rotation. There is currently no plan to add
        vertical rotation.

        :param anchorPoint: The point to rotate the schem around of
        :param yaw: The yaw component of the rotation
        :param pitch: The pitch component of the rotation
        :param roll: The roll component of the rotation
        :param rotateBlockStates: Whether or not we want to not only rotate the block's positions but also
                                  rotate the block's directions. (Like stairs and stuff)
        """

        degreesToRadiansScalar = math.pi / 180
        return self.rotateRadians(anchorPoint,
                                  yaw * degreesToRadiansScalar,
                                  pitch * degreesToRadiansScalar,
                                  roll * degreesToRadiansScalar,
                                  rotateBlockStates)

    def flip(self,
             anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
             flippingPlane: str,
             flipBlockStates:bool =True):
        """
        [Transformation method]

        Flips the structure by the inputted plane, which can be any combination of
        2 axis out of "xyz". So "xy", "yz", "xz" is good. But "yx" is also good.
        However "xx" and "yy" isn't good.
        The inputted plane will also pass through the anchor point.

        "flipBlockStates" if True, will flip the blocks themselves and not only their
        position in the world. Note that it's only horizontally with no plans on going
        vertical anytime soon.

        :param anchorPoint: The point the inputted flipping plane has to go through
        :param flippingPlane: The plane to flip the structure by
        :param flipBlockStates: Whether or not we want to not only flip the block's positions but
                                also flip their direction. (Like stairs and such)
        """

        # Seeing if the plane is a valid one
        # I don't exactly know why this algorithm I came up with works
        # but it seems to work lmfao, just add 1 for each x, y, or z
        # and if == to 2 then we have a combination of 2 different
        # axis
        xInPlane = 'x' in flippingPlane
        yInPlane = 'y' in flippingPlane
        zInPlane = 'z' in flippingPlane

        if (xInPlane + yInPlane + zInPlane) != 2:
            raise ValueError(
                "Incorrect plane inputted. A plane should be the combination of 2 axis between x, y and z."
            )

        # Plane decoding so that I only have either
        # "xy", "yz", or "xz"
        flipPlane = ""
        flipPlane += 'x' * xInPlane
        flipPlane += 'y' * yInPlane
        flipPlane += 'z' * zInPlane

        # Setup for the new hashmaps
        newBlockStates = {}
        newBlockEntities = {}

        # Go through each block and flip their vertices and put them
        # inside the new hashmaps
        for blockPosition, blockState in self._blockStates.items():
            blockPos = None

            # Flip positions
            if   flippingPlane == "xy":
                #blockPos = (blockPosition[0], blockPosition[1], -(blockPosition[2] - anchorPoint[2]) + anchorPoint[2])
                blockPos = (blockPosition[0], blockPosition[1], round(-blockPosition[2] + 2*anchorPoint[2]))
            elif flippingPlane == "xz":
                #blockPos = (blockPosition[0], -(blockPosition[1] - anchorPoint[1]) + anchorPoint[1], blockPosition[2])
                blockPos = (blockPosition[0], round(-blockPosition[1] + 2*anchorPoint[1]), blockPosition[2])
            elif flippingPlane == "yz":
                #blockPos = (-(blockPosition[0] - anchorPoint[0]) + anchorPoint[0], blockPosition[1], blockPosition[2])
                blockPos = (round(-blockPosition[0] + 2*anchorPoint[0]), blockPosition[1], blockPosition[2])

            # Put the blockState in the new hashmap
            newBlockStates[blockPos] = blockState
            # See if this blockState is also a block entity, if so put it in
            # the new BE hashmap
            if blockPosition in self._blockEntities:
                blockEntityData = self._blockEntities[blockPosition]
                newBlockEntities[blockPos] = blockEntityData

        # Flip the palette if we wanna flip the block states!
        if flipBlockStates:
            # If we are flipping around a plane that will make a flip be horizontal,
            # flip the block palette, otherwise don't
            if flippingPlane != "xz":
                # Epic flippance of the epic block palette
                newBlockPalette = MCStructure._Utils.getHorizontallyFlippedBlockPalette(self._blockPalette,
                                                                                        flippingPlane)
                # Set the new block palette
                self._blockPalette = newBlockPalette

        # Set the new hashmaps
        self._blockStates = newBlockStates
        self._blockEntities = newBlockEntities

        # For chaining
        return self

    def centerAround(self,
                     anchorPoint: tuple[Union[int, float], Union[int, float], Union[int, float]],
                     structureBounds: tuple[tuple[int, int, int], tuple[int, int, int]]):
        """
        [Transformation method]

        Centers this structure around the anchor point as best as it can with integer coordinates.
        The centering algorithm is biased to bulge out in the XYZ+ directions if the midpoint
        isn't exact.

        Structure bounds have to be inputted manually as it's better if it's the dev's job to cache
        bounds as it's computationally intensive to compute them.

        :param anchorPoint: The point to center the structure around of
        :param structureBounds: The bounds of this structure
        """

        # Calculate the midpoint of the structure to get a vector from 0, 0, 0 to
        # the middle of it. Then negate it to map the struct middle to 0, 0, 0 and
        # all the remaining blocks are gonna follow it
        # Now add the anchorPoint vector, if we want to center around 1, 1, 1 instead
        # of 0, 0, 0, we're gonna have to add 1, 1, 1 to every coordinate!
        # You can think of the anchorPoint as the vector from 0, 0, 0 to where you
        # want to center your build around to
        # The translation vector is floored so that the structure is biased to
        # buldge out in the XYZ+ directions if the midpoint isn't exact instead
        # of the XYZ- dir.
        translationVector = (-((structureBounds[1][0] + structureBounds[0][0]) // 2),
                             -((structureBounds[1][1] + structureBounds[0][1]) // 2),
                             -((structureBounds[1][2] + structureBounds[0][2]) // 2))
        # Add the anchor point
        translationVector = ( translationVector[0] + anchorPoint[0] ,
                              translationVector[1] + anchorPoint[1] ,
                              translationVector[2] + anchorPoint[2] )

        # Translate and return for chaining
        return self.translate(translationVector)

    def center(self, structureBounds: tuple[tuple[int, int, int], tuple[int, int, int]]):
        """
        [Transformation method]

        Centers this structure around (0, 0, 0) as best as it can with integer coordinates.

        Structure bounds have to be inputted manually as it's better if it's the dev's job to cache
        bounds as it's computationally intensive to compute them.

        :param structureBounds: The bounds of this structure.
        """

        return self.centerAround((0, 0, 0), structureBounds)

    """
    END OF TRANFORMS
    """

    """
    GENERATORS
    """
    def cuboidFilled(self, blockData: str, corner1: tuple[int, int, int], corner2: tuple[int, int, int]):
        """
        [Generation method]

        Generates a filled cuboid with only one blockData from corner1 to corner2. Will be faster than a bunch
        of .setBlocks() as some checks are bypassed because we got access to internals.

        :param blockData: The blockData that makes up this filledCuboid
        :param corner1: The first corner of the cuboid
        :param corner2: The second corner of the cuboid
        """

        corner1, corner2 = self._Utils.sortCuboidCorners(corner1, corner2)

        # Setup the XYZ generator for filling the cuboid
        xyzGen = itertools.product(range(corner1[0], corner2[0] + 1),
                                   range(corner1[1], corner2[1] + 1),
                                   range(corner1[2], corner2[2] + 1))

        # We're not going to use .setBlock() here as cuboids can be really big, but
        # right now only are one block data.
        # So we're basically only just gonna be initializing blockDatas once.

        isBlockDataBlockEntity = blockData[-1] == '}'

        if not isBlockDataBlockEntity:
            ## We got a blockState

            # Just setBlock a blockState at corner1 before filling the cuboid out,
            # so that we have our blockState for sure in the palette.
            self.setBlock(corner1, blockData)
            blockStateId = self._blockPalette[blockData]

            for x, y, z in xyzGen:
                coords = (x, y, z)
                self._blockStates[coords] = blockStateId
                # Gotta remove any blockEntity there like in ._setBlockState
                self._blockEntities.pop(coords, None)

        else:
            ## We got a blockEntity, we need a bit more processing
            ## to fill up a cuboid with a blockEntity

            # setBlock the blockEntity at corner1 so that its state is
            # in the palette for sure.
            self.setBlock(corner1, blockData)
            blockState = self._getBlockStateFromBlockEntityString(blockData)
            blockStateId = self._blockPalette[blockState]

            for x, y, z in xyzGen:
                # While placing blockEntities, we need to place the blockState,
                # and the nbt data in the blockEntities hashmap
                coords = (x, y, z)
                self._blockStates[coords] = blockStateId
                self._blockEntities[coords] = blockData

    def cuboidHollow(self, blockData: str, corner1: tuple[int, int, int], corner2: tuple[int, int, int]):
        """
        Creates a hollow cuboid with the inputted blockData from corner1 to corner2. Will be faster
        than a bunch of .setBlocks() as some checks are bypassed because we got access to internals.

        :param blockData: The blockData that makes up this hollow cuboid.
        :param corner1: The first corner of this cuboid
        :param corner2: The second corner of this cuboid
        """

        # Ok so the idea is to generate 6 cuboids without block overlapping hopefully
        # The 6 sides of a cuboid can be derived from 4 corners of that cuboid.
        corner_NX_NY_NZ, corner_PX_PY_PZ = self._Utils.sortCuboidCorners(corner1, corner2)

        corner_PX_NY_PZ = (corner_PX_PY_PZ[0], corner_NX_NY_NZ[1], corner_PX_PY_PZ[2])
        corner_PX_PY_NZ = (corner_PX_PY_PZ[0], corner_PX_PY_PZ[1], corner_NX_NY_NZ[2])

        corner_NX_PY_PZ = (corner_NX_NY_NZ[0], corner_PX_PY_PZ[1], corner_PX_PY_PZ[2])

        # Bottom side and top side
        # bottom so get the 2 negative Y (NY) corners
        self.cuboidFilled(blockData, corner_PX_NY_PZ, corner_NX_NY_NZ)
        # top so get the 2 positive Y (PY) corners
        self.cuboidFilled(blockData, corner_PX_PY_NZ, corner_NX_PY_PZ)

        # X- and X+ sides
        # X- so get both NX corners
        self.cuboidFilled(blockData, corner_NX_NY_NZ, corner_NX_PY_PZ)
        # X+ so get both PX corners
        self.cuboidFilled(blockData, corner_PX_NY_PZ, corner_PX_PY_NZ)

        # Z- and Z+ sides
        # Z- so get both NZ corners
        self.cuboidFilled(blockData, corner_NX_NY_NZ, corner_PX_PY_NZ)
        # Z+ so get both PZ corners
        self.cuboidFilled(blockData, corner_PX_NY_PZ, corner_NX_PY_PZ)

    def cuboidOutlines(self, blockData: str, corner1: tuple[int, int, int], corner2: tuple[int, int, int]):
        """
        Creates the outlines of the inputted cuboid. Will be faster than a bunch of .setBlocks() as some
        checks are bypassed because we got access to internals.

        :param blockData: The blockData that makes up this cuboid outlines.
        :param corner1: The first corner of this cuboid
        :param corner2: The second corner of this cuboid
        """

        # Get the 8 corners of the cuboid
        corners = self._Utils.generateAll8CuboidCorners(corner1, corner2)

        # Generate the 2 squares at opposite sides of the cuboid
        # No idea which way they're oriented lmfAO
        for i in [0, 4]:
            self.cuboidFilled(blockData, corners[0+i], corners[1+i])
            self.cuboidFilled(blockData, corners[2+i], corners[3+i])
            self.cuboidFilled(blockData, corners[0+i], corners[2+i])
            self.cuboidFilled(blockData, corners[1+i], corners[3+i])

        # Generate the 4 remaining line segments
        for i in range(4):
            self.cuboidFilled(blockData, corners[i], corners[i+4])

    """
    END OF GENERATORS
    """
    ### ---



    ### --- Private methods

    '''
    Returns a new ID for the block palette when called. Always returns an ID
    that has never been used in the palette.
    '''
    def _getNewPaletteId(self) -> int:
        newId = self._blockPaletteFreeId
        self._blockPaletteFreeId += 1
        return newId

    '''
    Removes the NBT part from a blockEntityString, for example, "barrel[]{}" would
    then become "barrel[]". We assume that the blockNBT passed in has
    NBTs.
    '''
    def _getBlockStateFromBlockEntityString(self, blockEntityString: str) -> str:
        firstCurlyBracketIndex = blockEntityString.find('{')
        return blockEntityString[:firstCurlyBracketIndex]

    '''
    Sets the block at the inputted position. The inputted block has to be a normal block state.
    Will overwrite any blockEntity data present at that position if there.
    '''
    def _setBlockState(self, position: tuple[int, int, int], blockState: str):
        # Add the blockState to the palette if it wasn't in it yet
        self._addBlockStateToPaletteIfAbsent(blockState)
        # Remove any block entity that might be here
        self._blockEntities.pop(position, None)
        # Set the block in this schematic to the block we just inputted
        self._blockStates[position] = self._blockPalette[blockState]

    '''
    Sets the block entity at the inputed position.
    '''
    def _setBlockEntity(self, position: tuple[int, int, int], blockEntityData: str):
        # Get the block state of this blockEntity, because we still need to
        # place the block state in the world
        blockEntityState = self._getBlockStateFromBlockEntityString(blockEntityData)
        # Set the block state in the world, be careful, it also deletes any blockEntity
        # data at the inputted position
        self._setBlockState(position, blockEntityState)

        # Add this block entity to the block entities hashmap
        self._blockEntities[position] = blockEntityData

    '''
    Adds the inputted block state in the palette if it is absent from it.
    Commonly used in MCSchematic#setBlock() but also in other methods like
    generators.
    '''
    def _addBlockStateToPaletteIfAbsent(self, blockState: str):
        if blockState not in self._blockPalette:
            # Get a new Id in the palette
            newPaletteId = self._getNewPaletteId()
            # Put the new blockState in the palette
            self._blockPalette[blockState] = newPaletteId
            # Put the inverse in the palette as well when we want to
            # access in O(1) the nbt of a block from its id
            self._blockPalette[newPaletteId] = blockState

    ### ---



    ### --- Private classes

    '''
    Class with a lot of random utils
    '''
    class _Utils:

        ### --- Fields

        # The directions in XYZ of cuboid corners from its center.
        # Used in .generateAll8CuboidCorners()
        _cuboidCornersDirections: list[tuple[int, int, int]] = [(1, 1, 1),
                                                                (1, 1, -1),
                                                                (1, -1, 1),
                                                                (1, -1, -1),
                                                                (-1, 1, 1),
                                                                (-1, 1, -1),
                                                                (-1, -1, 1),
                                                                (-1, -1, -1)]

        ### ---



        ### --- Public static methods

        '''
        Sorts the corner of a cuboid. Corner1 and 2 could be anywhere in the world,
        but in the returned corners, corner1 will be the -XYZ corner of the cuboid,
        and corner2 will be the +XYZ corner of the cuboid.
        '''
        @staticmethod
        def sortCuboidCorners(corner1: tuple[int, int, int], corner2: tuple[int, int, int])\
                -> tuple[tuple[int, int, int], tuple[int, int, int]]:
            newCorner1 = (
                min(corner1[0], corner2[0]),
                min(corner1[1], corner2[1]),
                min(corner1[2], corner2[2])
            )
            newCorner2 = (
                max(corner1[0], corner2[0]),
                max(corner1[1], corner2[1]),
                max(corner1[2], corner2[2])
            )
            return newCorner1, newCorner2

        '''
        Generates all 8 corners of the inputted cuboid with corner1 and 2. Both corners does
        not need to be sorted.
        '''
        @classmethod
        def generateAll8CuboidCorners(cls, corner1: tuple[int, int, int], corner2: tuple[int, int, int])\
                -> tuple[tuple[int, int, int]]:
            # Firstly sort corner1 and 2
            corner1, corner2 = cls.sortCuboidCorners(corner1, corner2)

            # Calculate the distances from the center of the cuboid
            distFromCenterX = (corner2[0] - corner1[0]) / 2
            distFromCenterY = (corner2[1] - corner1[1]) / 2
            distFromCenterZ = (corner2[2] - corner1[2]) / 2

            # Calculate the center of the cuboid
            cuboidCenter = (corner1[0] + distFromCenterX,
                            corner1[1] + distFromCenterY,
                            corner1[2] + distFromCenterZ)

            # Calculate the 8 corners
            corners: list[tuple[int, int, int]] = []
            for cornerDir in MCStructure._Utils._cuboidCornersDirections:
                corner = (int( cuboidCenter[0] + cornerDir[0]*distFromCenterX ),
                          int( cuboidCenter[1] + cornerDir[1]*distFromCenterY ),
                          int( cuboidCenter[2] + cornerDir[2]*distFromCenterZ ))
                corners.append(corner)

            # Retrun
            return tuple(corners)

        '''
        Returns the inputted blockPalette horizontally rotated by the amount of 90 degree
        turns we ask to turn it for. 90 degree horizontal rotation defined by the .rotate() 
        yaw transform.
        '''
        @staticmethod
        def getHorizontallyRotatedBlockPalette(blockPalette: {Union[str, int]: Union[str, int]},
                                               ninetyDegreeTurnCount: int) -> {Union[str, int]: Union[str, int]}:
            # Don't rotate the block palette if the ninetyDegreeTurnCount is a multiple
            # of 4. The check if already done in .rotate() but if I need this method
            # again I'll be happy that this check is here lmfao
            if ninetyDegreeTurnCount % 4 == 0:
                return blockPalette

            # Init the new block palette which is gonna be a rotation of
            # the current one
            rotatedBlockPalette = {}

            # Go through each block palette entry that starts with a number,
            # rotate the linked blockState string to it then put {num: newState} and
            # {newState: num} inside the new palette
            for k, v in blockPalette.items():
                # Filtering out all entries where the key isn't an integer,
                # don't need to rotate a blockState twice
                if type(k) != int: continue

                # Renaming because I love :sparkles: clarity :sparkles:
                blockStatePaletteId = k
                blockState = v

                # Rotate epic block state
                rotatedBlockState = MCBlockStateManipulator.getHorizontallyRotatedBlockState(blockState,
                                                                                             ninetyDegreeTurnCount)

                # Time to put in hashmap bois
                rotatedBlockPalette[blockStatePaletteId] = rotatedBlockState
                rotatedBlockPalette[rotatedBlockState] = blockStatePaletteId

            # Retrun
            return rotatedBlockPalette

        '''
        Returns the inputted block palette but flipped according to the inputted flipping plane.
        Since it's only horizontal flipping, the flipping plane can be anything except "xz".
        '''
        @staticmethod
        def getHorizontallyFlippedBlockPalette(blockPalette: {Union[str, int]: Union[str, int]},
                                               flippingPlane: str) -> {Union[str, int]: Union[str, int]}:
            # If the flipping plane is "xz" then don't flip anything.
            # Check already done in .flip() but it's here just in case!
            if flippingPlane == "xz":
                return blockPalette

            # Init the new block palette which is gonna be a flip of
            # the current one
            flippedBlockPalette = {}

            # Go through each block palette entry that starts with a number,
            # flip the linked blockState string to it then put {num: newState} and
            # {newState: num} inside the new palette
            for k, v in blockPalette.items():
                # Filtering out all entries where the key isn't an integer,
                # don't need to flip a blockState twice
                if type(k) != int: continue

                # Renaming because I love :sparkles: clarity :sparkles:
                blockStatePaletteId = k
                blockState = v

                # Flip epic block state
                flippedBlockState = MCBlockStateManipulator.getHorizontallyFlippedBlockState(blockState,
                                                                                             flippingPlane)

                # Time to put in hashmap bois
                flippedBlockPalette[blockStatePaletteId] = flippedBlockState
                flippedBlockPalette[flippedBlockState] = blockStatePaletteId

            # Retrun
            return flippedBlockPalette

        ### ---

    ### ---































class MCSchematic:
    """
    Main class of mcschematic.
    It represents a Minecraft Schematic.
    Which is really just a wrapper class around an MCStructure that just
    implements loading and saving from files haha
    """

    ### --- Fields

    # The structure class holding all of our block and block entity data
    _structure: MCStructure = None

    ### ---



    ### --- Init

    def __init__(self, schematicToLoadPath_or_mcStructure: Union[str, MCStructure] = None):
        if schematicToLoadPath_or_mcStructure is None:
            self._defaultInit(); return

        if type(schematicToLoadPath_or_mcStructure) == str:
            schematicToLoadPath = schematicToLoadPath_or_mcStructure

            if not os.path.isfile(schematicToLoadPath):
                self._defaultInit(); return

            if not schematicToLoadPath.endswith(".schem"):
                self._defaultInit(); return

            self._initFromFile(schematicToLoadPath)

        if type(schematicToLoadPath_or_mcStructure) == MCStructure:
            mcStructure = schematicToLoadPath_or_mcStructure

            self._initFromMCStructure(mcStructure)


    '''
    The base class Init
    '''
    def _defaultInit(self):
        self._structure = MCStructure()

    '''
    Class init from existing schematic file
    '''
    def _initFromFile(self, schematicToLoadPath: str):
        # Get the schematic file as a nbt map
        schematicFile = nbtlib.load(schematicToLoadPath, gzipped=True)
        fileBase = schematicFile['Schematic'] if 'Schematic' in schematicFile else schematicFile


        ## Init the block palette
        filePalette = fileBase['Palette']
        structureBlockPalette = {}

        for blockState, idTagInPalette in filePalette.items():
            idInPalette = int(idTagInPalette)
            structureBlockPalette[blockState] = idInPalette
            structureBlockPalette[idInPalette] = blockState
        # Nothing has been put inside the block palette, so we default init it.
        if len(structureBlockPalette) == 0:
            structureBlockPalette = {'minecraft:air': 0, 0: 'minecraft:air'}
        # Set the free Id to the length of the block palette // 2 as each Id has 2 entries
        structureBlockPaletteFreeId = (len(structureBlockPalette) // 2)

        # -- Re process the block palette so that ID 0 is air
        # Put air inside the hashmap if it wasn't present yet.
        # used for future processing that's why it's not equal to 0
        if 'minecraft:air' not in structureBlockPalette:
            structureBlockPalette['minecraft:air'] = structureBlockPaletteFreeId
            structureBlockPalette[structureBlockPaletteFreeId] = 'minecraft:air'
            structureBlockPaletteFreeId += 1
        # If the current air ID isn't 0, switch it up with the
        # current 0 id, example:
        #   palette{0: black_wool, 1: air} will get switched to:
        #   palette{0: air, 1: black_wool}
        # And the byte ids will get processed afterwards
        beforeProcessingAirId = structureBlockPalette['minecraft:air']
        airOldId = beforeProcessingAirId
        if beforeProcessingAirId != 0:
            beforeProcessingId0State = structureBlockPalette[0]
            # Pop the 0 id
            structureBlockPalette.pop(0)
            structureBlockPalette.pop(beforeProcessingId0State)
            # Pop the air id
            structureBlockPalette.pop(beforeProcessingAirId)
            structureBlockPalette.pop('minecraft:air')

            # Put the air at 0
            structureBlockPalette[0] = 'minecraft:air'
            structureBlockPalette['minecraft:air'] = 0
            # Put the old currentId0State where air was
            structureBlockPalette[beforeProcessingAirId] = beforeProcessingId0State
            structureBlockPalette[beforeProcessingId0State] = beforeProcessingAirId


        ## Init the blockStates in _blockStates
        structureBlockStates: {tuple[int, int, int]: int} = {}
        if 'BlockData' in fileBase:

            # Get the necessary data for blockState loading
            fileBlockStatesIds = fileBase['BlockData']
            blockStatesIds = bytearray(fileBlockStatesIds)
            fileMetaData = fileBase['Metadata']
            schemOffset = (
                int(fileMetaData['WEOffsetX']),
                int(fileMetaData['WEOffsetY']),
                int(fileMetaData['WEOffsetZ'])
            )
            schemHeight = int(fileBase['Height'])  # y
            schemLength = int(fileBase['Length'])  # z
            schemWidth = int(fileBase['Width'])  # x

            # Variable so that we don't have to do a multiplication every iteration
            schemYSliceArea = schemWidth * schemLength


            if len(structureBlockPalette) < 128:
                # The amount of block states is less than 128, so each block is a byte,
                # so we use the old faster algorithm to load the structure

                for blockStateIndex, blockStateId in enumerate(blockStatesIds):
                    # Process the blockStateId since the palette has been modified
                    processedBlockStateId = blockStateId
                    # -- Do le switcharoo, air new Id is 0 no matter what
                    # If we refer to the old air Id, it means that we refer to air now
                    # which is 0
                    if blockStateId == airOldId:
                        processedBlockStateId = 0
                    # If we refer to the old id at 0, we are referring to the new
                    # content of the old air id spot
                    if blockStateId == 0:
                        processedBlockStateId = airOldId

                    # Since we processed the blockPalette so that air is 0, if the ID is 0
                    # then skip, as we don't keep track of air blocks
                    if processedBlockStateId == 0: continue

                    # Getting the coordinates in the schem not shifted yet
                    blockStateSchemY = blockStateIndex // schemYSliceArea
                    blockStateSchemZ = ( blockStateIndex % schemYSliceArea ) // schemWidth
                    blockStateSchemX = blockStateIndex % schemWidth

                    # Shift the coordinates so that the blocks are back in their right position
                    realY = blockStateSchemY + schemOffset[1]
                    realZ = blockStateSchemZ + schemOffset[2]
                    realX = blockStateSchemX + schemOffset[0]

                    # Place the block
                    structureBlockStates[(realX, realY, realZ)] = processedBlockStateId

            else:
                # The palette contains more (or equal) than 128 entries, so we use
                # the varint algorithm

                # Put the blockState bytes into a BytesIO for a stream
                blockStatesIdStream = io.BytesIO(blockStatesIds)

                # Setup before loopin woo
                blockStateIndex = 0

                # We loopin bois
                while blockStatesIdStream.tell() < len(blockStatesIds):
                    # Get the next varint of the stream
                    blockStateId = MCSchematic._VarintIO.readPositiveVarInt(blockStatesIdStream)
                    # From there, use the normal old algorithm, cba to put it in
                    # a method lmfao

                    # ===
                    # Process the blockStateId since the palette has been modified
                    processedBlockStateId = blockStateId
                    # -- Do le switcharoo, air new Id is 0 no matter what
                    # If we refer to the old air Id, it means that we refer to air now
                    # which is 0
                    if blockStateId == airOldId:
                        processedBlockStateId = 0
                    # If we refer to the old id at 0, we are referring to the new
                    # content of the old air id spot
                    if blockStateId == 0:
                        processedBlockStateId = airOldId

                    # Since we processed the blockPalette so that air is 0, if the ID is 0
                    # then skip, as we don't keep track of air blocks
                    if processedBlockStateId == 0:
                        blockStateIndex += 1
                        continue

                    # Getting the coordinates in the schem not shifted yet
                    blockStateSchemY = blockStateIndex // schemYSliceArea
                    blockStateSchemZ = (blockStateIndex % schemYSliceArea) // schemWidth
                    blockStateSchemX = blockStateIndex % schemWidth

                    # Shift the coordinates so that the blocks are back in their right position
                    realY = blockStateSchemY + schemOffset[1]
                    realZ = blockStateSchemZ + schemOffset[2]
                    realX = blockStateSchemX + schemOffset[0]

                    # Place the block
                    structureBlockStates[(realX, realY, realZ)] = processedBlockStateId
                    # ===

                    # -- Increment block state index as we're done registering that block
                    blockStateIndex += 1


        ## Block entities!
        structureBlockEntities = {}
        if 'BlockEntities' in fileBase:
            # Gather the necessary data
            fileBlockEntities = fileBase['BlockEntities']
            fileMetaData = fileBase['Metadata']
            schemOffset = ( int(fileMetaData['WEOffsetX']), int(fileMetaData['WEOffsetY']), int(fileMetaData['WEOffsetZ']) )

            # Iterate over every block entity
            for blockEntityIndex, blockEntityCompound in enumerate(fileBlockEntities):
                # Our goal is to extract the blockData and position of the blockEntity
                # to then set it in self._blockEntities

                # Get the position of this block entity
                posCompound: IntArray = blockEntityCompound['Pos']
                blockEntityPosX = int(posCompound[0]) + schemOffset[0]
                blockEntityPosY = int(posCompound[1]) + schemOffset[1]
                blockEntityPosZ = int(posCompound[2]) + schemOffset[2]
                blockEntityPos = (blockEntityPosX, blockEntityPosY, blockEntityPosZ)
                # Get its id / blockState from the blockState hashmap
                # (because the Id in the compound isn't complete)

                blockEntityState: str = structureBlockPalette[structureBlockStates[blockEntityPos]]

                # Create a compound without Pos and Id to serialize
                pureCompound = Compound()
                for k, v in blockEntityCompound.items():
                    if not (k == 'Pos' or k == 'Id'):
                        pureCompound.merge(Compound({k: v}))

                # Create the blockEntity blockData string
                blockData: str = blockEntityState + nbtlib.serialize_tag(pureCompound)

                # Put it in _blockEntities
                structureBlockEntities[blockEntityPos] = blockData


        ## Save the data gathered in a new structure class!
        ## Using private member accessing shenanigans, do not recommend
        ## using that anywhere else for whoever is looking at the code rn.
        self._structure = MCStructure()
        self._structure._blockPalette = structureBlockPalette
        self._structure._blockPaletteFreeId = structureBlockPaletteFreeId
        self._structure._blockStates = structureBlockStates
        self._structure._blockEntities = structureBlockEntities

    '''
    Class init when a structure gets inputed. The inputted structure 
    gets bound to the schematic.
    '''
    def _initFromMCStructure(self, mcStructure: MCStructure):
        self._structure = mcStructure

    ### ---



    ### --- Public methods

    '''
    
    '''
    def save(self, outputFolderPath: str, schemName: str, version: 'Version', fastSaving:bool = False):
        """
        Saves this schematic to the inputted folder path.

        Some things to note:
            - If the schematic is more than 2 billion blocks in volume,
              it will not save.

        The "fastSaving" argument makes saving use another algorithm for schematics with a lot of blockStates
        in its block palette, which is faster but increases the file-size, not usually more than twice the size though.

        :param outputFolderPath: The output folder path
        :param schemName: The name of the saved schematic file (without the ".schem" part)
        :param version: The Version enum entry which is the version of Minecraft we want to save the schem for.
        :param fastSaving: Whether or not to use the fast but large file size saving algorithm
        """

        ## Setup
        schemBounds = self._structure.getBounds()
        schemDims = self._structure.getStructureDimensions(schemBounds)
        # The vector amount by which minBounds is offsetted from 0 0 0
        schemOffset = schemBounds[0]

        ## BLOCK PALETTE
        ## We're doing the block palette early because it's gonna
        ## be useful in knowing which algorithm to use in when saving
        ## the blocks to the schematic
        # Get a cleaner version of the block palette, without the duplicates used
        # for back and forth O(1) access
        self._structure.getBlockPalette()
        cleanBlockPalette = self._structure.getBlockPalette()


        ## BLOCK DATA
        encodedBlockStates = self._getEncodedBlockStates(len(cleanBlockPalette),
                                                         schemDims,
                                                         schemOffset,
                                                         fastSaving)


        ## BLOCK ENTITIES
        blockEntitiesCompounds = \
            [
                self._blockEntityStringToSchemCompound(
                    (
                        position[0] - schemOffset[0],
                        position[1] - schemOffset[1],
                        position[2] - schemOffset[2]
                    ), blockEntityStr
                )
                for position, blockEntityStr in self._structure.getBlockEntities().items()
            ]


        ## Generate the schematic file from the byte array
        ## From Fearless's code which was taken from someone else lOl
        schematic = File({

            'Version': Int(2),
            'DataVersion': Int(version.value),
            'Metadata': Compound({
                'WEOffsetX': Int(schemOffset[0]),
                'WEOffsetY': Int(schemOffset[1]),
                'WEOffsetZ': Int(schemOffset[2]),
                'MCSchematicMetadata': Compound({
                    'Generated': String("Generated with love using Sloimay's MCSchematic Python Library, "
                                        "itself dependant on Valentin Berlier's nbtlib library.")
                })
            }),

            'Height': Short(schemDims[1]),
            'Length': Short(schemDims[2]),
            'Width': Short(schemDims[0]),

            'PaletteMax': Int(len(cleanBlockPalette)),
            'Palette': Compound(cleanBlockPalette),
            'BlockData': ByteArray(encodedBlockStates),

            'BlockEntities': List(blockEntitiesCompounds),

        }, gzipped=True, root_name='Schematic')

        # DEBUG
        # print({blockNbt: Int(paletteId) for blockNbt, paletteId in self._blockPalette.items()})
        # print(schemDims)
        # print(self._blockPaletteLatestId)
        # print(self._structure._blockStates)
        # print(self._blockPalette)
        # print(self._blockEntities)
        # print(cleanBlockPalette)
        # print(len(cleanBlockPalette))
        # print(blockEntitiesCompounds)
        # print(List(blockEntitiesCompounds))

        # Save the schematic on the compuper, if no output folder is specified,
        # remove the "/", otherwise the name is gonna be "/<name>" which doesn't
        # work lOl
        schematicPath = \
            schemName + ".schem" if outputFolderPath == '' else \
            outputFolderPath + "/" + schemName + ".schem"
        schematic.save(schematicPath)


    def getStructure(self):
        """
        Returns the MCStructure object of this schematic. Any modifications
        on that structure object will affect this MCSchematic.

        :return: The MCStructure object of this schematic.
        """

        return self._structure

    def setBlock(self, position: tuple[int, int, int], blockData: str):
        """
        Sets a blockData in the structure at the inputed position.

        A blockData is a string format representing any block in
        Minecraft, used in commands such as /setblock or /fill.

        :param position: The position to place the blockData at
        :param blockData: The blockData to place
        """

        return self._structure.setBlock(position, blockData)

    def getBlockStateAt(self, position: tuple[int, int, int]) -> str:
        """
        Returns the blockState at the inputted position in the structure.

        :param position: The position in the structure to get the blockState from.

        :return The blockState at the inputted position.
        """

        return self._structure.getBlockStateAt(position)

    def getBlockDataAt(self, position: tuple[int, int, int]) -> str:
        """
        Return the blockData at the inputted position.

        :param position: The position in the structure to get the blockData from.

        :return: The blockData at the inputted position.
        """

        return self._structure.getBlockDataAt(position)

    def placeSchematic(self, incomingSchematic: 'MCSchematic', placePosition: tuple[int, int, int]):
        """
        Places the inputted MCSchematic's structure inside of this MCSchematic's structure,
        at the inputted position.

        :param incomingSchematic: The schematic to place
        :param placePosition: The position at which we want to place the structure at
        """

        return self._structure.placeStructure(incomingSchematic.getStructure(), placePosition)

    def placeStructure(self, incomingStructure: MCStructure, placePosition: tuple[int, int, int]):
        """
        Places the inputted structure inside of this MCSchematic's structure, at the
        inputted position.

        :param incomingStructure: The structure to place
        :param placePosition: The position at which we want to place the structure at
        """

        return self._structure.placeStructure(incomingStructure, placePosition)

    def makeCopy(self):
        """
        Makes and returns a deep copy of this schematic. So the returned schematic
        won't have its internals shared with the new one, it will have totally new
        internals. And thus modifying its structure won't affect another one.

        :return: A copy of this MCSchematic.
        """

        return MCSchematic(self._structure.makeCopy())

    def getSubSchematic(self, corner1: tuple[int, int, int], corner2: tuple[int, int, int], reCenter:bool = False):
        """
        Returns a new schematic which is all the blocks in the calling schematic
        that are inside the inputted cuboid (both coordinates inclusive).

        Note that the position of the blocks in the new schematic aren't changed
        from the calling schem, if the cuboid is off-center, the resulting
        subSchematic will also be.
        (i.e a block at (0, 3, 2) will stay at (0, 3, 2) in the resulting subSchematic)
        Unless the "reCenter" variable is set to True. In which case the structure is going to
        be centered around 0, 0, 0

        :param corner1: The first corner of the inputted cuboid
        :param corner2: The second corner of the inputted cuboid
        :param reCenter: If True, will center the subStructure around 0, 0, 0

        :return: Returns a new schematic which is all the blocks in the calling schematic
                 that are inside the inputted cuboid (both cuboids corners are inclusive).
        """

        return MCSchematic(self._structure.getSubStructure(corner1, corner2, reCenter))

    ### ---





    ### --- Private methods

    '''
    Returns a Compound object from a blockEntity string, for example:
    "minecraft:barrel[]{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:milk_bucket"}]}"
    Will get converted into a compound tag
    '''
    def _blockEntityStringToSchemCompound(self, blockEntityPosition: tuple[int, int, int],
                                          blockEntityString: str) -> Compound:
        # The block entity nbt we are going to put in the schematic
        blockEntityNBTOut = Compound()

        # Get the NBT portion of the BEString, we know for sure that there is a '{'
        # in the string, as otherwise it would be a block iirc
        firstCurlyBracketIndex = blockEntityString.find('{')
        nbtPortion = blockEntityString[firstCurlyBracketIndex:]
        # Parse that nbt into a compound
        nbtCompound = nbtlib.parse_nbt(nbtPortion)
        # Add the parsed compound to the main compound
        blockEntityNBTOut.merge(nbtCompound)

        # Add the position to the main compound
        posCompound = Compound({'Pos': IntArray(blockEntityPosition)})
        blockEntityNBTOut.merge(posCompound)

        # Add the id to the main compound, the id may or may not have "[]" at the end of
        # it, so we checkin for that
        firstSquareBracketIndex = blockEntityString.find('[')
        if firstSquareBracketIndex == -1 or firstCurlyBracketIndex < firstSquareBracketIndex:
            # We don't have a [], because either we didn't find a "[" char, or
            # the first "[" char is after the first "{" which would mean there is
            # no "[" before "{" meaning the blockNbt's NBT starts directly after the ID,
            # without blockState tags
            idCompound = Compound({'Id': String(blockEntityString[:firstCurlyBracketIndex])})
        else:
            # We have a "[]"
            idCompound = Compound({'Id': String(blockEntityString[:firstSquareBracketIndex])})
        blockEntityNBTOut.merge(idCompound)

        # We're finished pOG
        return blockEntityNBTOut

    def _getEncodedBlockStates(self,
                               cleanBlockPaletteLen: int,
                               schemDims: tuple[int, int, int],
                               schemOffset: tuple[int, int, int],
                               fastSaving: bool):

        # Setup
        encodedBlockStates = None

        # Switch the algorithm depending on how many blockStates
        # in the block palette
        if cleanBlockPaletteLen <= 128:

            # Schems uses the varint encoding to encode data, but if
            # no palette entries takes up more than 7 bits for its number,
            # then every byte in the array is one block.

            # The block data encoded into the schematic, allocating every byte
            # for the schem, and setting them to 0 which conveniently is air
            encodedBlockStates = bytearray(schemDims[1] * schemDims[2] * schemDims[0])
            schemYSliceArea = schemDims[0] * schemDims[2]
            schemXSliceLength = schemDims[0]
            # Loop over every block in our hashmap, deduce their placement in the bytearray
            # from their coordinates then replacing the byte there
            for blockPosition, blockPaletteId in self._structure.getBlockStates().items():
                # compute the position in the bytearray
                realX = blockPosition[0] - schemOffset[0]
                realY = blockPosition[1] - schemOffset[1]
                realZ = blockPosition[2] - schemOffset[2]
                byteArrayPos = realY * schemYSliceArea + realZ * schemXSliceLength + realX
                # Set the byte
                encodedBlockStates[byteArrayPos] = blockPaletteId

        else:

            # We got more than 128 palette entries, so now we're gonna see if we
            # want the fast saving but big file size, or the slow saving but small
            # file size approach.
            if not fastSaving:
                # Create the bytearray, then painfully add every varint to it

                # Setup the buffer that we're gonna write to.
                encodedBlockStates = bytearray()

                # Compute a hashmap of every palette id to their corresponding varint bytes
                # so that we don't have to compute them again when we need them
                intToVarintBytes = {i: self._VarintIO.getPositiveVarInt(i) for i in range(cleanBlockPaletteLen)}

                # Get the structure blockStates
                structureBlockStates = self._structure.getBlockStates()

                for y, z, x in itertools.product(range(schemDims[1]), range(schemDims[2]), range(schemDims[0])):
                    # Get this position where it would be in the hashmap
                    # Seems unoptimized since we could tweak the ranges in the product but
                    # actually doesn't matter performance-wise
                    positionInBlockhashMap = (schemOffset[0] + x, schemOffset[1] + y, schemOffset[2] + z)

                    # Testing if this position is in the blocks hashmap,
                    # meaning that there is a blockState set at this position and not
                    # just air
                    if positionInBlockhashMap in structureBlockStates:
                        encodedBlockStates += intToVarintBytes[structureBlockStates[positionInBlockhashMap]]
                    else:
                        # No blocks so air which is 0x00 even in varint
                        encodedBlockStates += b'\x00'

            else:
                # We fast saving, using the <128 algorithm but with every blockState taking
                # the same amount of bytes.
                # Idea by @keeteeh on Twitter :D

                # -- Figure out the amount of bytes needed
                # Get the amount of bits needed to express the biggest number in the palette
                blockPaletteHighestId = cleanBlockPaletteLen - 1
                bitsNeeded = math.floor(math.log2(max(blockPaletteHighestId, 1)) + 1)
                # Divide them by 7 to see how many bytes you're gonna need
                bytesNeeded = math.ceil(bitsNeeded / 7)

                # Map the whole block palette to fixed length varints
                intToVarintBytes = {i: self._VarintIO.getPositiveVarIntFixedLength(i, bytesNeeded)
                                    for i in range(cleanBlockPaletteLen)}


                # The block data encoded into the schematic, allocating every byte
                # for the schem, and setting them to 0 which conveniently is air. However
                # air when using multiple bytes isn't all 0s anymore, which created an
                # annoying bug I took almost 3 hours to track down xd
                airAsVarintIntArray = [*[0x80 for _ in range(bytesNeeded - 1)], 0x00]
                encodedBlockStates = bytearray(airAsVarintIntArray * (schemDims[1] * schemDims[2] * schemDims[0]))
                # How long a unit of coordinate is in the bytearray
                xSpan = bytesNeeded
                zSpan = schemDims[0] * xSpan
                ySpan = schemDims[2] * zSpan
                # Loop over every block in our hashmap, deduce their placement in the bytearray
                # from their coordinates then replacing the byte there
                for blockPosition, blockPaletteId in self._structure.getBlockStates().items():
                    # compute the position in the bytearray
                    realX = blockPosition[0] - schemOffset[0]
                    realY = blockPosition[1] - schemOffset[1]
                    realZ = blockPosition[2] - schemOffset[2]
                    byteArrayPos = ySpan * realY + zSpan * realZ + xSpan * realX
                    # Set the bytes
                    blockPaletteIdAsVarint = intToVarintBytes[blockPaletteId]
                    for byteOffset in range(bytesNeeded):
                        encodedBlockStates[byteArrayPos + byteOffset] = blockPaletteIdAsVarint[byteOffset]


        # We've finished populating the block states
        return encodedBlockStates

    ### ---



    ### --- Defaults

    '''
    Really costly repr method, as everytime you call it, you have to recompute the
    min and max XYZ of the schematic.
    '''
    #def __repr__(self):
    #    ## Setup
    #    dimensions = self._getSchematicDimensions(self._getSchematicMinAndMaxXYZ())
    #    # The number of block states in the _blocks HashMap
    #    blockStateCount = len(self._blockStates) // 2
    #    # The number of block entities
    #    blockEntityCount = len(self._blockEntities)
    #
    #    return f"[ Dimensions={dimensions}, BlockStateCount={blockStateCount}, BlockEntityCount={blockEntityCount} ]"

    ### ---



    ### --- Private classes

    '''
    Class to handle writing and reading byte arrays of varints.
    Implementation heavily taken from https://wiki.vg/VarInt_And_VarLong.
    '''
    class _VarintIO:
        ''''''

        ### --- Fields

        # Used for reading the int bits from a varint byte
        _INT_BITMASK =      0b0111_1111
        # Used for reading the continue bit in a byte, which is
        # the MSB, if one, we continue reading to the next byte.
        _CONTINUE_BITMASK = 0b1000_0000

        ### ---


        ### --- Public static methods

        '''
        Reads and returns the next varint of a varint byte stream while making it advance.
        Note that the varint read is going to be positive I was too lazy 
        '''
        @staticmethod
        def readPositiveVarInt(stream: io.BytesIO) -> int:
            # Setup
            varint: int = 0
            positionInInt: int = 0

            # Going while we didn't finish reading the int
            while True:
                # Read the next byte of the stream
                currentByte = int.from_bytes(stream.read(1), 'big', signed=False)
                # Add the int bits to our varint
                varint |= (currentByte & MCSchematic._VarintIO._INT_BITMASK) << positionInInt

                # If the continue bit is 0, then stop reading this int
                if (currentByte & MCSchematic._VarintIO._CONTINUE_BITMASK) == 0:
                    break

                # If we are continuing, add 7 to the position in the int, since
                # in varints the int is separated in groups of 7 bits.
                positionInInt += 7

            return varint

        '''
        Writes the inputted number into the inputted varint BytesIO stream.
        '''
        @staticmethod
        def writePositiveVarInt(number: int, stream: io.BytesIO):
            # Loop as long as we have a continue bit
            while True:
                # If the number's continue bit is 0 then we end the loop
                # here and write
                if (number & ~MCSchematic._VarintIO._INT_BITMASK) == 0:
                    # Write to the stream the current number as its less
                    # or equal to a byte in length right now.
                    stream.write(bytes([number]))
                    # Put the stream position at the end of it since it's
                    # supposed to be a stream to write to.
                    # io.SEEK_END == 2
                    stream.seek(0, io.SEEK_END)
                    # Stoppy the loopy
                    return

                # Write the current group of 7 bits we're iterating over to the stream
                stream.write(
                    # Not the most optimised implementation of int to bytes, but a good enough one.
                    # .to_bytes exists and I tried it, gains about 4% of speed
                    bytes([(number & MCSchematic._VarintIO._INT_BITMASK) | MCSchematic._VarintIO._CONTINUE_BITMASK])
                )

                # Logical shift the number by 7.
                # Not ">>" but ">>>" like in Java, which shifts *every* bit to the right.
                # Whereas in python the shifted bits are sign extended to present division
                # by 2 of negative numbers.
                # Basically, the sign bit isn't shifted but we need that.
                # >Thanks to steveha here: https://stackoverflow.com/questions/64129107/logical-right-shift-in-python-meaning-of-the-expression-num-0x100000000
                #  for the ">>>" alternative!
                number = (number & 0xff_ff_ff_ff) >> 7

        '''
        Returns the inputted positive int as a varint.
        Based on the .writePositiveVarInt() method.
        '''
        @staticmethod
        def getPositiveVarInt(number: int):
            out = b''
            while True:
                if (number & ~MCSchematic._VarintIO._INT_BITMASK) == 0:
                    out += bytes([number])
                    return out
                out += bytes([(number & MCSchematic._VarintIO._INT_BITMASK) | MCSchematic._VarintIO._CONTINUE_BITMASK])
                number = (number & 0xff_ff_ff_ff) >> 7

        '''
        Returns the inputted positive int as a varint
        of fixed length. That sounds really ironic but it's used for faster saving!
        Based on the .writePositiveVarInt() method.
        '''
        @staticmethod
        def getPositiveVarIntFixedLength(number: int, length: int):
            out = b''
            for i in range(length):
                # The continue bit will be present until the last iteration
                continueBitMask = MCSchematic._VarintIO._CONTINUE_BITMASK * int(i < (length - 1))
                # Encode those 7 bits
                out += bytes([(number & MCSchematic._VarintIO._INT_BITMASK) | continueBitMask])
                number = (number & 0xff_ff_ff_ff) >> 7
            # We finished populating that byte array
            return out


        ### ---

    ### ---





























"""  ---- ADDITIONAL CLASSES ---- """

class BlockDataDB:
    """
    A database for useful blockData strings such as containers containing
    a certain amount of items for precise signal strengths.
    """

    class BARREL:
        _barrelSS = [
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:42b,Slot:9b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:37b,Slot:11b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:32b,Slot:13b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:28b,Slot:15b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:23b,Slot:17b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:19b,Slot:19b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:14b,Slot:21b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:10b,Slot:23b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:5b,Slot:25b,id:"minecraft:redstone"}]}""",
            """minecraft:barrel[open=false,facing=up]{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:64b,Slot:25b,id:"minecraft:redstone"},{Count:64b,Slot:26b,id:"minecraft:redstone"},{Count:0b,Slot:27b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.BARREL._barrelSS[ss]

    SS_BARREL0: str = BARREL._barrelSS[0]
    SS_BARREL1: str = BARREL._barrelSS[1]
    SS_BARREL2: str = BARREL._barrelSS[2]
    SS_BARREL3: str = BARREL._barrelSS[3]
    SS_BARREL4: str = BARREL._barrelSS[4]
    SS_BARREL5: str = BARREL._barrelSS[5]
    SS_BARREL6: str = BARREL._barrelSS[6]
    SS_BARREL7: str = BARREL._barrelSS[7]
    SS_BARREL8: str = BARREL._barrelSS[8]
    SS_BARREL9: str = BARREL._barrelSS[9]
    SS_BARREL10: str = BARREL._barrelSS[10]
    SS_BARREL11: str = BARREL._barrelSS[11]
    SS_BARREL12: str = BARREL._barrelSS[12]
    SS_BARREL13: str = BARREL._barrelSS[13]
    SS_BARREL14: str = BARREL._barrelSS[14]
    SS_BARREL15: str = BARREL._barrelSS[15]

    class HOPPER:
        _hopperSS = [
            """minecraft:hopper{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:23b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:46b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:5b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:28b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:51b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:10b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:32b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:55b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:14b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:37b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:60b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:19b,Slot:4b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:42b,Slot:4b,id:"minecraft:redstone"}]}""",
            """minecraft:hopper{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:0b,Slot:5b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.HOPPER._hopperSS[ss]

    SS_HOPPER0: str = HOPPER._hopperSS[0]
    SS_HOPPER1: str = HOPPER._hopperSS[1]
    SS_HOPPER2: str = HOPPER._hopperSS[2]
    SS_HOPPER3: str = HOPPER._hopperSS[3]
    SS_HOPPER4: str = HOPPER._hopperSS[4]
    SS_HOPPER5: str = HOPPER._hopperSS[5]
    SS_HOPPER6: str = HOPPER._hopperSS[6]
    SS_HOPPER7: str = HOPPER._hopperSS[7]
    SS_HOPPER8: str = HOPPER._hopperSS[8]
    SS_HOPPER9: str = HOPPER._hopperSS[9]
    SS_HOPPER10: str = HOPPER._hopperSS[10]
    SS_HOPPER11: str = HOPPER._hopperSS[11]
    SS_HOPPER12: str = HOPPER._hopperSS[12]
    SS_HOPPER13: str = HOPPER._hopperSS[13]
    SS_HOPPER14: str = HOPPER._hopperSS[14]
    SS_HOPPER15: str = HOPPER._hopperSS[15]

    class FURNACE:
        _furnaceSS = [
            """minecraft:furnace{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:14b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:28b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:42b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:55b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:5b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:19b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:32b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:46b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:10b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:23b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:37b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:51b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:furnace{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:0b,Slot:3b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.FURNACE._furnaceSS[ss]

    SS_FURNACE0: str = FURNACE._furnaceSS[0]
    SS_FURNACE1: str = FURNACE._furnaceSS[1]
    SS_FURNACE2: str = FURNACE._furnaceSS[2]
    SS_FURNACE3: str = FURNACE._furnaceSS[3]
    SS_FURNACE4: str = FURNACE._furnaceSS[4]
    SS_FURNACE5: str = FURNACE._furnaceSS[5]
    SS_FURNACE6: str = FURNACE._furnaceSS[6]
    SS_FURNACE7: str = FURNACE._furnaceSS[7]
    SS_FURNACE8: str = FURNACE._furnaceSS[8]
    SS_FURNACE9: str = FURNACE._furnaceSS[9]
    SS_FURNACE10: str = FURNACE._furnaceSS[10]
    SS_FURNACE11: str = FURNACE._furnaceSS[11]
    SS_FURNACE12: str = FURNACE._furnaceSS[12]
    SS_FURNACE13: str = FURNACE._furnaceSS[13]
    SS_FURNACE14: str = FURNACE._furnaceSS[14]
    SS_FURNACE15: str = FURNACE._furnaceSS[15]

    class DISPENSER:
        _dispenserSS = [
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:42b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:19b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:37b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:14b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:32b,Slot:4b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:10b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:28b,Slot:6b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:5b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:23b,Slot:8b,id:"minecraft:redstone"}]}""",
            """minecraft:dispenser{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:0b,Slot:9b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.DISPENSER._dispenserSS[ss]

    SS_DISPENSER0: str = DISPENSER._dispenserSS[0]
    SS_DISPENSER1: str = DISPENSER._dispenserSS[1]
    SS_DISPENSER2: str = DISPENSER._dispenserSS[2]
    SS_DISPENSER3: str = DISPENSER._dispenserSS[3]
    SS_DISPENSER4: str = DISPENSER._dispenserSS[4]
    SS_DISPENSER5: str = DISPENSER._dispenserSS[5]
    SS_DISPENSER6: str = DISPENSER._dispenserSS[6]
    SS_DISPENSER7: str = DISPENSER._dispenserSS[7]
    SS_DISPENSER8: str = DISPENSER._dispenserSS[8]
    SS_DISPENSER9: str = DISPENSER._dispenserSS[9]
    SS_DISPENSER10: str = DISPENSER._dispenserSS[10]
    SS_DISPENSER11: str = DISPENSER._dispenserSS[11]
    SS_DISPENSER12: str = DISPENSER._dispenserSS[12]
    SS_DISPENSER13: str = DISPENSER._dispenserSS[13]
    SS_DISPENSER14: str = DISPENSER._dispenserSS[14]
    SS_DISPENSER15: str = DISPENSER._dispenserSS[15]

    class DROPPER:
        _dropperSS = [
            """minecraft:dropper{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:42b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:19b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:37b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:14b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:32b,Slot:4b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:10b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:28b,Slot:6b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:5b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:23b,Slot:8b,id:"minecraft:redstone"}]}""",
            """minecraft:dropper{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:0b,Slot:9b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.DROPPER._dropperSS[ss]

    SS_DROPPER0: str = DROPPER._dropperSS[0]
    SS_DROPPER1: str = DROPPER._dropperSS[1]
    SS_DROPPER2: str = DROPPER._dropperSS[2]
    SS_DROPPER3: str = DROPPER._dropperSS[3]
    SS_DROPPER4: str = DROPPER._dropperSS[4]
    SS_DROPPER5: str = DROPPER._dropperSS[5]
    SS_DROPPER6: str = DROPPER._dropperSS[6]
    SS_DROPPER7: str = DROPPER._dropperSS[7]
    SS_DROPPER8: str = DROPPER._dropperSS[8]
    SS_DROPPER9: str = DROPPER._dropperSS[9]
    SS_DROPPER10: str = DROPPER._dropperSS[10]
    SS_DROPPER11: str = DROPPER._dropperSS[11]
    SS_DROPPER12: str = DROPPER._dropperSS[12]
    SS_DROPPER13: str = DROPPER._dropperSS[13]
    SS_DROPPER14: str = DROPPER._dropperSS[14]
    SS_DROPPER15: str = DROPPER._dropperSS[15]

    class TRAPPED_CHEST:
        _trapped_chestSS = [
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:42b,Slot:9b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:37b,Slot:11b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:32b,Slot:13b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:28b,Slot:15b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:23b,Slot:17b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:19b,Slot:19b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:14b,Slot:21b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:10b,Slot:23b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:5b,Slot:25b,id:"minecraft:redstone"}]}""",
            """minecraft:trapped_chest{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:64b,Slot:25b,id:"minecraft:redstone"},{Count:64b,Slot:26b,id:"minecraft:redstone"},{Count:0b,Slot:27b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.TRAPPED_CHEST._trapped_chestSS[ss]

    SS_TRAPPED_CHEST0: str = TRAPPED_CHEST._trapped_chestSS[0]
    SS_TRAPPED_CHEST1: str = TRAPPED_CHEST._trapped_chestSS[1]
    SS_TRAPPED_CHEST2: str = TRAPPED_CHEST._trapped_chestSS[2]
    SS_TRAPPED_CHEST3: str = TRAPPED_CHEST._trapped_chestSS[3]
    SS_TRAPPED_CHEST4: str = TRAPPED_CHEST._trapped_chestSS[4]
    SS_TRAPPED_CHEST5: str = TRAPPED_CHEST._trapped_chestSS[5]
    SS_TRAPPED_CHEST6: str = TRAPPED_CHEST._trapped_chestSS[6]
    SS_TRAPPED_CHEST7: str = TRAPPED_CHEST._trapped_chestSS[7]
    SS_TRAPPED_CHEST8: str = TRAPPED_CHEST._trapped_chestSS[8]
    SS_TRAPPED_CHEST9: str = TRAPPED_CHEST._trapped_chestSS[9]
    SS_TRAPPED_CHEST10: str = TRAPPED_CHEST._trapped_chestSS[10]
    SS_TRAPPED_CHEST11: str = TRAPPED_CHEST._trapped_chestSS[11]
    SS_TRAPPED_CHEST12: str = TRAPPED_CHEST._trapped_chestSS[12]
    SS_TRAPPED_CHEST13: str = TRAPPED_CHEST._trapped_chestSS[13]
    SS_TRAPPED_CHEST14: str = TRAPPED_CHEST._trapped_chestSS[14]
    SS_TRAPPED_CHEST15: str = TRAPPED_CHEST._trapped_chestSS[15]

    class CHEST:
        _chestSS = [
            """minecraft:chest{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:42b,Slot:9b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:37b,Slot:11b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:32b,Slot:13b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:28b,Slot:15b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:23b,Slot:17b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:19b,Slot:19b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:14b,Slot:21b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:10b,Slot:23b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:5b,Slot:25b,id:"minecraft:redstone"}]}""",
            """minecraft:chest{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:64b,Slot:25b,id:"minecraft:redstone"},{Count:64b,Slot:26b,id:"minecraft:redstone"},{Count:0b,Slot:27b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.CHEST._chestSS[ss]

    SS_CHEST0: str = CHEST._chestSS[0]
    SS_CHEST1: str = CHEST._chestSS[1]
    SS_CHEST2: str = CHEST._chestSS[2]
    SS_CHEST3: str = CHEST._chestSS[3]
    SS_CHEST4: str = CHEST._chestSS[4]
    SS_CHEST5: str = CHEST._chestSS[5]
    SS_CHEST6: str = CHEST._chestSS[6]
    SS_CHEST7: str = CHEST._chestSS[7]
    SS_CHEST8: str = CHEST._chestSS[8]
    SS_CHEST9: str = CHEST._chestSS[9]
    SS_CHEST10: str = CHEST._chestSS[10]
    SS_CHEST11: str = CHEST._chestSS[11]
    SS_CHEST12: str = CHEST._chestSS[12]
    SS_CHEST13: str = CHEST._chestSS[13]
    SS_CHEST14: str = CHEST._chestSS[14]
    SS_CHEST15: str = CHEST._chestSS[15]

    class SHULKER_BOX:
        _shulker_boxSS = [
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:55b,Slot:3b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:51b,Slot:5b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:46b,Slot:7b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:42b,Slot:9b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:37b,Slot:11b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:32b,Slot:13b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:28b,Slot:15b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:23b,Slot:17b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:19b,Slot:19b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:14b,Slot:21b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:10b,Slot:23b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:5b,Slot:25b,id:"minecraft:redstone"}]}""",
            """minecraft:shulker_box{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:64b,Slot:3b,id:"minecraft:redstone"},{Count:64b,Slot:4b,id:"minecraft:redstone"},{Count:64b,Slot:5b,id:"minecraft:redstone"},{Count:64b,Slot:6b,id:"minecraft:redstone"},{Count:64b,Slot:7b,id:"minecraft:redstone"},{Count:64b,Slot:8b,id:"minecraft:redstone"},{Count:64b,Slot:9b,id:"minecraft:redstone"},{Count:64b,Slot:10b,id:"minecraft:redstone"},{Count:64b,Slot:11b,id:"minecraft:redstone"},{Count:64b,Slot:12b,id:"minecraft:redstone"},{Count:64b,Slot:13b,id:"minecraft:redstone"},{Count:64b,Slot:14b,id:"minecraft:redstone"},{Count:64b,Slot:15b,id:"minecraft:redstone"},{Count:64b,Slot:16b,id:"minecraft:redstone"},{Count:64b,Slot:17b,id:"minecraft:redstone"},{Count:64b,Slot:18b,id:"minecraft:redstone"},{Count:64b,Slot:19b,id:"minecraft:redstone"},{Count:64b,Slot:20b,id:"minecraft:redstone"},{Count:64b,Slot:21b,id:"minecraft:redstone"},{Count:64b,Slot:22b,id:"minecraft:redstone"},{Count:64b,Slot:23b,id:"minecraft:redstone"},{Count:64b,Slot:24b,id:"minecraft:redstone"},{Count:64b,Slot:25b,id:"minecraft:redstone"},{Count:64b,Slot:26b,id:"minecraft:redstone"},{Count:0b,Slot:27b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.SHULKER_BOX._shulker_boxSS[ss]

    SS_SHULKER_BOX0: str = SHULKER_BOX._shulker_boxSS[0]
    SS_SHULKER_BOX1: str = SHULKER_BOX._shulker_boxSS[1]
    SS_SHULKER_BOX2: str = SHULKER_BOX._shulker_boxSS[2]
    SS_SHULKER_BOX3: str = SHULKER_BOX._shulker_boxSS[3]
    SS_SHULKER_BOX4: str = SHULKER_BOX._shulker_boxSS[4]
    SS_SHULKER_BOX5: str = SHULKER_BOX._shulker_boxSS[5]
    SS_SHULKER_BOX6: str = SHULKER_BOX._shulker_boxSS[6]
    SS_SHULKER_BOX7: str = SHULKER_BOX._shulker_boxSS[7]
    SS_SHULKER_BOX8: str = SHULKER_BOX._shulker_boxSS[8]
    SS_SHULKER_BOX9: str = SHULKER_BOX._shulker_boxSS[9]
    SS_SHULKER_BOX10: str = SHULKER_BOX._shulker_boxSS[10]
    SS_SHULKER_BOX11: str = SHULKER_BOX._shulker_boxSS[11]
    SS_SHULKER_BOX12: str = SHULKER_BOX._shulker_boxSS[12]
    SS_SHULKER_BOX13: str = SHULKER_BOX._shulker_boxSS[13]
    SS_SHULKER_BOX14: str = SHULKER_BOX._shulker_boxSS[14]
    SS_SHULKER_BOX15: str = SHULKER_BOX._shulker_boxSS[15]

    class SMOKER:
        _smokerSS = [
            """minecraft:smoker{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:14b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:28b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:42b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:55b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:5b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:19b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:32b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:46b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:10b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:23b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:37b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:51b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:smoker{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:0b,Slot:3b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.SMOKER._smokerSS[ss]

    SS_SMOKER0: str = SMOKER._smokerSS[0]
    SS_SMOKER1: str = SMOKER._smokerSS[1]
    SS_SMOKER2: str = SMOKER._smokerSS[2]
    SS_SMOKER3: str = SMOKER._smokerSS[3]
    SS_SMOKER4: str = SMOKER._smokerSS[4]
    SS_SMOKER5: str = SMOKER._smokerSS[5]
    SS_SMOKER6: str = SMOKER._smokerSS[6]
    SS_SMOKER7: str = SMOKER._smokerSS[7]
    SS_SMOKER8: str = SMOKER._smokerSS[8]
    SS_SMOKER9: str = SMOKER._smokerSS[9]
    SS_SMOKER10: str = SMOKER._smokerSS[10]
    SS_SMOKER11: str = SMOKER._smokerSS[11]
    SS_SMOKER12: str = SMOKER._smokerSS[12]
    SS_SMOKER13: str = SMOKER._smokerSS[13]
    SS_SMOKER14: str = SMOKER._smokerSS[14]
    SS_SMOKER15: str = SMOKER._smokerSS[15]

    class BLAST_FURNACE:
        _blast_furnaceSS = [
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"0"}',Items:[{Count:0b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"1"}',Items:[{Count:1b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"2"}',Items:[{Count:14b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"3"}',Items:[{Count:28b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"4"}',Items:[{Count:42b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"5"}',Items:[{Count:55b,Slot:0b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"6"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:5b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"7"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:19b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"8"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:32b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"9"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:46b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"10"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:60b,Slot:1b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"11"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:10b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"12"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:23b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"13"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:37b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"14"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:51b,Slot:2b,id:"minecraft:redstone"}]}""",
            """minecraft:blast_furnace{CustomName:'{"italic":false,"text":"15"}',Items:[{Count:64b,Slot:0b,id:"minecraft:redstone"},{Count:64b,Slot:1b,id:"minecraft:redstone"},{Count:64b,Slot:2b,id:"minecraft:redstone"},{Count:0b,Slot:3b,id:"minecraft:redstone"}]}"""
        ]

        @staticmethod
        def fromSS(ss: int) -> str:
            return BlockDataDB.BLAST_FURNACE._blast_furnaceSS[ss]

    SS_BLAST_FURNACE0: str = BLAST_FURNACE._blast_furnaceSS[0]
    SS_BLAST_FURNACE1: str = BLAST_FURNACE._blast_furnaceSS[1]
    SS_BLAST_FURNACE2: str = BLAST_FURNACE._blast_furnaceSS[2]
    SS_BLAST_FURNACE3: str = BLAST_FURNACE._blast_furnaceSS[3]
    SS_BLAST_FURNACE4: str = BLAST_FURNACE._blast_furnaceSS[4]
    SS_BLAST_FURNACE5: str = BLAST_FURNACE._blast_furnaceSS[5]
    SS_BLAST_FURNACE6: str = BLAST_FURNACE._blast_furnaceSS[6]
    SS_BLAST_FURNACE7: str = BLAST_FURNACE._blast_furnaceSS[7]
    SS_BLAST_FURNACE8: str = BLAST_FURNACE._blast_furnaceSS[8]
    SS_BLAST_FURNACE9: str = BLAST_FURNACE._blast_furnaceSS[9]
    SS_BLAST_FURNACE10: str = BLAST_FURNACE._blast_furnaceSS[10]
    SS_BLAST_FURNACE11: str = BLAST_FURNACE._blast_furnaceSS[11]
    SS_BLAST_FURNACE12: str = BLAST_FURNACE._blast_furnaceSS[12]
    SS_BLAST_FURNACE13: str = BLAST_FURNACE._blast_furnaceSS[13]
    SS_BLAST_FURNACE14: str = BLAST_FURNACE._blast_furnaceSS[14]
    SS_BLAST_FURNACE15: str = BLAST_FURNACE._blast_furnaceSS[15]





class Version(Enum):
    """
    The Enum used to specify which Minecraft version we want to save a schematic
    for in MCSchematic#save().
    """

    JE_1_19_2 = 3120
    JE_1_19_2_RELEASE_CANDIDATE_2 = 3119
    JE_1_19_2_RELEASE_CANDIDATE_1 = 3118
    JE_1_19_1 = 3117
    JE_1_19_1_RELEASE_CANDIDATE_3 = 3116
    JE_1_19_1_RELEASE_CANDIDATE_2 = 3115
    JE_1_19_1_PRE_RELEASE_6 = 3114
    JE_1_19_1_PRE_RELEASE_5 = 3113
    JE_1_19_1_PRE_RELEASE_4 = 3112
    JE_1_19_1_PRE_RELEASE_3 = 3111
    JE_1_19_1_PRE_RELEASE_2 = 3110
    JE_1_19_1_RELEASE_CANDIDATE_1 = 3109
    JE_1_19_1_PRE_RELEASE_1 = 3107
    JE_22W24A = 3106
    JE_1_19 = 3105
    JE_1_19_RELEASE_CANDIDATE_2 = 3104
    JE_1_19_RELEASE_CANDIDATE_1 = 3103
    JE_1_19_PRE_RELEASE_5 = 3102
    JE_1_19_PRE_RELEASE_4 = 3101
    JE_1_19_PRE_RELEASE_3 = 3100
    JE_1_19_PRE_RELEASE_2 = 3099
    JE_1_19_PRE_RELEASE_1 = 3098
    JE_22W19A = 3096
    JE_22W18A = 3095
    JE_22W17A = 3093
    JE_22W16B = 3092
    JE_22W16A = 3091
    JE_22W15A = 3089
    JE_22W14A = 3088
    JE_22W13A = 3085
    JE_22W12A = 3082
    JE_22W11A = 3080
    JE_DEEP_DARK_EXPERIMENTAL_SNAPSHOT_1 = 3066
    JE_1_18_2 = 2975
    JE_1_18_2_RELEASE_CANDIDATE_1 = 2974
    JE_1_18_2_PRE_RELEASE_3 = 2973
    JE_1_18_2_PRE_RELEASE_2 = 2972
    JE_1_18_2_PRE_RELEASE_1 = 2971
    JE_22W07A = 2969
    JE_22W06A = 2968
    JE_22W05A = 2967
    JE_22W03A = 2966
    JE_1_18_1 = 2865
    JE_1_18_1_RELEASE_CANDIDATE_3 = 2864
    JE_1_18_1_RELEASE_CANDIDATE_2 = 2863
    JE_1_18_1_RELEASE_CANDIDATE_1 = 2862
    JE_1_18_1_PRE_RELEASE_1 = 2861
    JE_1_18 = 2860
    JE_1_18_RELEASE_CANDIDATE_4 = 2859
    JE_1_18_RELEASE_CANDIDATE_3 = 2858
    JE_1_18_RELEASE_CANDIDATE_2 = 2857
    JE_1_18_RELEASE_CANDIDATE_1 = 2856
    JE_1_18_PRE_RELEASE_8 = 2855
    JE_1_18_PRE_RELEASE_7 = 2854
    JE_1_18_PRE_RELEASE_6 = 2853
    JE_1_18_PRE_RELEASE_5 = 2851
    JE_1_18_PRE_RELEASE_4 = 2850
    JE_1_18_PRE_RELEASE_3 = 2849
    JE_1_18_PRE_RELEASE_2 = 2848
    JE_1_18_PRE_RELEASE_1 = 2847
    JE_21W44A = 2845
    JE_21W43A = 2844
    JE_21W42A = 2840
    JE_21W41A = 2839
    JE_21W40A = 2838
    JE_21W39A = 2836
    JE_21W38A = 2835
    JE_21W37A = 2834
    JE_1_18_EXPERIMENTAL_SNAPSHOT_7 = 2831
    JE_1_18_EXPERIMENTAL_SNAPSHOT_6 = 2830
    JE_1_18_EXPERIMENTAL_SNAPSHOT_5 = 2829
    JE_1_18_EXPERIMENTAL_SNAPSHOT_4 = 2828
    JE_1_18_EXPERIMENTAL_SNAPSHOT_3 = 2827
    JE_1_18_EXPERIMENTAL_SNAPSHOT_2 = 2826
    JE_1_18_EXPERIMENTAL_SNAPSHOT_1 = 2825
    JE_1_17_1 = 2730
    JE_1_17_1_RELEASE_CANDIDATE_2 = 2729
    JE_1_17_1_RELEASE_CANDIDATE_1 = 2728
    JE_1_17_1_PRE_RELEASE_3 = 2727
    JE_1_17_1_PRE_RELEASE_2 = 2726
    JE_1_17_1_PRE_RELEASE_1 = 2725
    JE_1_17 = 2724
    JE_1_17_RELEASE_CANDIDATE_2 = 2723
    JE_1_17_RELEASE_CANDIDATE_1 = 2722
    JE_1_17_PRE_RELEASE_5 = 2721
    JE_1_17_PRE_RELEASE_4 = 2720
    JE_1_17_PRE_RELEASE_3 = 2719
    JE_1_17_PRE_RELEASE_2 = 2718
    JE_1_17_PRE_RELEASE_1 = 2716
    JE_21W20A = 2715
    JE_21W19A = 2714
    JE_21W18A = 2713
    JE_21W17A = 2712
    JE_21W16A = 2711
    JE_21W15A = 2709
    JE_21W14A = 2706
    JE_21W13A = 2705
    JE_21W11A = 2703
    JE_21W10A = 2699
    JE_21W08B = 2698
    JE_21W08A = 2697
    JE_21W07A = 2695
    JE_21W06A = 2694
    JE_21W05B = 2692
    JE_21W05A = 2690
    JE_21W03A = 2689
    JE_20W51A = 2687
    JE_20W49A = 2685
    JE_20W48A = 2683
    JE_20W46A = 2682
    JE_20W45A = 2681
    JE_COMBAT_TEST_8C = 2707
    JE_COMBAT_TEST_8B = 2706
    JE_COMBAT_TEST_8 = 2705
    JE_COMBAT_TEST_7C = 2704
    JE_COMBAT_TEST_7B = 2703
    JE_COMBAT_TEST_7 = 2702
    JE_COMBAT_TEST_6 = 2701
    JE_1_16_5 = 2586
    JE_1_16_5_RELEASE_CANDIDATE_1 = 2585
    JE_1_16_4 = 2584
    JE_1_16_4_RELEASE_CANDIDATE_1 = 2583
    JE_1_16_4_PRE_RELEASE_2 = 2582
    JE_1_16_4_PRE_RELEASE_1 = 2581
    JE_1_16_3 = 2580
    JE_1_16_3_RELEASE_CANDIDATE_1 = 2579
    JE_1_16_2 = 2578
    JE_1_16_2_RELEASE_CANDIDATE_2 = 2577
    JE_1_16_2_RELEASE_CANDIDATE_1 = 2576
    JE_1_16_2_PRE_RELEASE_3 = 2575
    JE_1_16_2_PRE_RELEASE_2 = 2574
    JE_1_16_2_PRE_RELEASE_1 = 2573
    JE_20W30A = 2572
    JE_20W29A = 2571
    JE_20W28A = 2570
    JE_20W27A = 2569
    JE_1_16_1 = 2567
    JE_1_16 = 2566
    JE_1_16_RELEASE_CANDIDATE_1 = 2565
    JE_1_16_PRE_RELEASE_8 = 2564
    JE_1_16_PRE_RELEASE_7 = 2563
    JE_1_16_PRE_RELEASE_6 = 2562
    JE_1_16_PRE_RELEASE_5 = 2561
    JE_1_16_PRE_RELEASE_4 = 2560
    JE_1_16_PRE_RELEASE_3 = 2559
    JE_1_16_PRE_RELEASE_2 = 2557
    JE_1_16_PRE_RELEASE_1 = 2556
    JE_20W22A = 2555
    JE_20W21A = 2554
    JE_20W20B = 2537
    JE_20W20A = 2536
    JE_20W19A = 2534
    JE_20W18A = 2532
    JE_20W17A = 2529
    JE_20W16A = 2526
    JE_20W15A = 2525
    JE_20W14A = 2524
    JE_20W13B = 2521
    JE_20W13A = 2520
    JE_20W12A = 2515
    JE_20W11A = 2513
    JE_20W10A = 2512
    JE_20W09A = 2510
    JE_20W08A = 2507
    JE_20W07A = 2506
    JE_SNAPSHOT_20W06A = 2504
    JE_COMBAT_TEST_5 = 2321
    JE_COMBAT_TEST_4 = 2320
    JE_1_15_2 = 2230
    JE_1_15_2_PRE_RELEASE_2 = 2229
    JE_1_15_2_PRE_RELEASE_1 = 2228
    JE_1_15_1 = 2227
    JE_1_15_1_PRE_RELEASE_1 = 2226
    JE_1_15 = 2225
    JE_1_15_PRE_RELEASE_7 = 2224
    JE_1_15_PRE_RELEASE_6 = 2223
    JE_1_15_PRE_RELEASE_5 = 2222
    JE_1_15_PRE_RELEASE_4 = 2221
    JE_1_15_PRE_RELEASE_3 = 2220
    JE_1_15_PRE_RELEASE_2 = 2219
    JE_1_15_PRE_RELEASE_1 = 2218
    JE_19W46B = 2217
    JE_19W46A = 2216
    JE_19W45B = 2215
    JE_19W45A = 2214
    JE_19W44A = 2213
    JE_19W42A = 2212
    JE_19W41A = 2210
    JE_19W40A = 2208
    JE_19W39A = 2207
    JE_19W38B = 2206
    JE_19W38A = 2205
    JE_19W37A = 2204
    JE_19W36A = 2203
    JE_19W35A = 2201
    JE_19W34A = 2200
    JE_COMBAT_TEST_3 = 2069
    JE_COMBAT_TEST_2 = 2068
    JE_1_14_3___COMBAT_TEST = 2067
    JE_1_14_4 = 1976
    JE_1_14_4_PRE_RELEASE_7 = 1975
    JE_1_14_4_PRE_RELEASE_6 = 1974
    JE_1_14_4_PRE_RELEASE_5 = 1973
    JE_1_14_4_PRE_RELEASE_4 = 1972
    JE_1_14_4_PRE_RELEASE_3 = 1971
    JE_1_14_4_PRE_RELEASE_2 = 1970
    JE_1_14_4_PRE_RELEASE_1 = 1969
    JE_1_14_3 = 1968
    JE_1_14_3_PRE_RELEASE_4 = 1967
    JE_1_14_3_PRE_RELEASE_3 = 1966
    JE_1_14_3_PRE_RELEASE_2 = 1965
    JE_1_14_3_PRE_RELEASE_1 = 1964
    JE_1_14_2 = 1963
    JE_1_14_2_PRE_RELEASE_4 = 1962
    JE_1_14_2_PRE_RELEASE_3 = 1960
    JE_1_14_2_PRE_RELEASE_2 = 1959
    JE_1_14_2_PRE_RELEASE_1 = 1958
    JE_1_14_1 = 1957
    JE_1_14_1_PRE_RELEASE_2 = 1956
    JE_1_14_1_PRE_RELEASE_1 = 1955
    JE_1_14 = 1952
    JE_1_14_PRE_RELEASE_5 = 1951
    JE_1_14_PRE_RELEASE_4 = 1950
    JE_1_14_PRE_RELEASE_3 = 1949
    JE_1_14_PRE_RELEASE_2 = 1948
    JE_1_14_PRE_RELEASE_1 = 1947
    JE_19W14B = 1945
    JE_19W14A = 1944
    JE_19W13B = 1943
    JE_19W13A = 1942
    JE_19W12B = 1941
    JE_19W12A = 1940
    JE_19W11B = 1938
    JE_19W11A = 1937
    JE_19W09A = 1935
    JE_19W08B = 1934
    JE_19W08A = 1933
    JE_19W07A = 1932
    JE_19W06A = 1931
    JE_19W05A = 1930
    JE_19W04B = 1927
    JE_19W04A = 1926
    JE_19W03C = 1924
    JE_19W03B = 1923
    JE_19W03A = 1922
    JE_19W02A = 1921
    JE_18W50A = 1919
    JE_18W49A = 1916
    JE_18W48B = 1915
    JE_18W48A = 1914
    JE_18W47B = 1913
    JE_18W47A = 1912
    JE_18W46A = 1910
    JE_18W45A = 1908
    JE_18W44A = 1907
    JE_18W43C = 1903
    JE_18W43B = 1902
    JE_18W43A = 1901
    JE_1_13_2 = 1631
    JE_1_13_2_PRE2 = 1630
    JE_1_13_2_PRE1 = 1629
    JE_1_13_1 = 1628
    JE_1_13_1_PRE2 = 1627
    JE_1_13_1_PRE1 = 1626
    JE_18W33A = 1625
    JE_18W32A = 1623
    JE_18W31A = 1622
    JE_18W30B = 1621
    JE_18W30A = 1620
    JE_1_13 = 1519
    JE_1_13_PRE10 = 1518
    JE_1_13_PRE9 = 1517
    JE_1_13_PRE8 = 1516
    JE_1_13_PRE7 = 1513
    JE_1_13_PRE6 = 1512
    JE_1_13_PRE5 = 1511
    JE_1_13_PRE4 = 1504
    JE_1_13_PRE3 = 1503
    JE_1_13_PRE2 = 1502
    JE_1_13_PRE1 = 1501
    JE_18W22C = 1499
    JE_18W22B = 1498
    JE_18W22A = 1497
    JE_18W21B = 1496
    JE_18W21A = 1495
    JE_18W20C = 1493
    JE_18W20B = 1491
    JE_18W20A = 1489
    JE_18W19B = 1485
    JE_18W19A = 1484
    JE_18W16A = 1483
    JE_18W15A = 1482
    JE_18W14B = 1481
    JE_18W14A = 1479
    JE_18W11A = 1478
    JE_18W10D = 1477
    JE_18W10C = 1476
    JE_18W10B = 1474
    JE_18W10A = 1473
    JE_18W09A = 1472
    JE_18W08B = 1471
    JE_18W08A = 1470
    JE_18W07C = 1469
    JE_18W07B = 1468
    JE_18W07A = 1467
    JE_18W06A = 1466
    JE_18W05A = 1464
    JE_18W03B = 1463
    JE_18W03A = 1462
    JE_18W02A = 1461
    JE_18W01A = 1459
    JE_17W50A = 1457
    JE_17W49B = 1455
    JE_17W49A = 1454
    JE_17W48A = 1453
    JE_17W47B = 1452
    JE_17W47A = 1451
    JE_17W46A = 1449
    JE_17W45B = 1448
    JE_17W45A = 1447
    JE_17W43B = 1445
    JE_17W43A = 1444
    JE_1_12_2 = 1343
    JE_1_12_2_PRE2 = 1342
    JE_1_12_2_PRE1 = 1341
    JE_1_12_1 = 1241
    JE_1_12_1_PRE1 = 1240
    JE_17W31A = 1239
    JE_1_12 = 1139
    JE_1_12_PRE7 = 1138
    JE_1_12_PRE6 = 1137
    JE_1_12_PRE5 = 1136
    JE_1_12_PRE4 = 1135
    JE_1_12_PRE3 = 1134
    JE_1_12_PRE2 = 1133
    JE_1_12_PRE1 = 1132
    JE_17W18B = 1131
    JE_17W18A = 1130
    JE_17W17B = 1129
    JE_17W17A = 1128
    JE_17W16B = 1127
    JE_17W16A = 1126
    JE_17W15A = 1125
    JE_17W14A = 1124
    JE_17W13B = 1123
    JE_17W13A = 1122
    JE_17W06A = 1022
    JE_1_11_2 = 922
    JE_1_11_1 = 921
    JE_16W50A = 920
    JE_1_11 = 819
    JE_1_11_PRE1 = 818
    JE_16W44A = 817
    JE_16W43A = 816
    JE_16W42A = 815
    JE_16W41A = 814
    JE_16W40A = 813
    JE_16W39C = 812
    JE_16W39B = 811
    JE_16W39A = 809
    JE_16W38A = 807
    JE_16W36A = 805
    JE_16W35A = 803
    JE_16W33A = 802
    JE_16W32B = 801
    JE_16W32A = 800
    JE_1_10_2 = 512
    JE_1_10_1 = 511
    JE_1_10 = 510
    JE_1_10_PRE2 = 507
    JE_1_10_PRE1 = 506
    JE_16W21B = 504
    JE_16W21A = 503
    JE_16W20A = 501
    JE_1_9_4 = 184
    JE_1_9_3 = 183
    JE_1_9_3_PRE3 = 182
    JE_1_9_3_PRE2 = 181
    JE_1_9_3_PRE1 = 180
    JE_16W15B = 179
    JE_16W15A = 178
    JE_16W14A = 177
    JE_1_9_2 = 176
    JE_1_9_1 = 175
    JE_1_9_1_PRE3 = 172
    JE_1_9_1_PRE2 = 171
    JE_1_9_1_PRE1 = 170
    JE_1_9 = 169
    JE_1_9_PRE4 = 168
    JE_1_9_PRE3 = 167
    JE_1_9_PRE2 = 165
    JE_1_9_PRE1 = 164
    JE_16W07B = 163
    JE_16W07A = 162
    JE_16W06A = 161
    JE_16W05B = 160
    JE_16W05A = 159
    JE_16W04A = 158
    JE_16W03A = 157
    JE_16W02A = 156
    JE_15W51B = 155
    JE_15W51A = 154
    JE_15W50A = 153
    JE_15W49B = 152
    JE_15W49A = 151
    JE_15W47C = 150
    JE_15W47B = 149
    JE_15W47A = 148
    JE_15W46A = 146
    JE_15W45A = 145
    JE_15W44B = 143
    JE_15W44A = 142
    JE_15W43C = 141
    JE_15W43B = 140
    JE_15W43A = 139
    JE_15W42A = 138
    JE_15W41B = 137
    JE_15W41A = 136
    JE_15W40B = 134
    JE_15W40A = 133
    JE_15W39C = 132
    JE_15W39B = 131
    JE_15W39A = 130
    JE_15W38B = 129
    JE_15W38A = 128
    JE_15W37A = 127
    JE_15W36D = 126
    JE_15W36C = 125
    JE_15W36B = 124
    JE_15W36A = 123
    JE_15W35E = 122
    JE_15W35D = 121
    JE_15W35C = 120
    JE_15W35B = 119
    JE_15W35A = 118
    JE_15W34D = 117
    JE_15W34C = 116
    JE_15W34B = 115
    JE_15W34A = 114
    JE_15W33C = 112
    JE_15W33B = 111
    JE_15W33A = 55
    JE_15W32C = 104
    JE_15W32B = 103
    JE_15W32A = 100
