amulet-core 1.9.19__py3-none-any.whl → 1.9.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of amulet-core might be problematic. Click here for more details.

Files changed (198) hide show
  1. amulet/__init__.py +27 -27
  2. amulet/__pyinstaller/__init__.py +2 -2
  3. amulet/__pyinstaller/hook-amulet.py +4 -4
  4. amulet/_version.py +21 -21
  5. amulet/api/__init__.py +2 -2
  6. amulet/api/abstract_base_entity.py +128 -128
  7. amulet/api/block.py +630 -630
  8. amulet/api/block_entity.py +71 -71
  9. amulet/api/cache.py +107 -107
  10. amulet/api/chunk/__init__.py +6 -6
  11. amulet/api/chunk/biomes.py +207 -207
  12. amulet/api/chunk/block_entity_dict.py +175 -175
  13. amulet/api/chunk/blocks.py +46 -46
  14. amulet/api/chunk/chunk.py +389 -389
  15. amulet/api/chunk/entity_list.py +75 -75
  16. amulet/api/chunk/status.py +167 -167
  17. amulet/api/data_types/__init__.py +4 -4
  18. amulet/api/data_types/generic_types.py +4 -4
  19. amulet/api/data_types/operation_types.py +16 -16
  20. amulet/api/data_types/world_types.py +49 -49
  21. amulet/api/data_types/wrapper_types.py +71 -71
  22. amulet/api/entity.py +74 -74
  23. amulet/api/errors.py +119 -119
  24. amulet/api/history/__init__.py +36 -36
  25. amulet/api/history/base/__init__.py +3 -3
  26. amulet/api/history/base/base_history.py +26 -26
  27. amulet/api/history/base/history_manager.py +63 -63
  28. amulet/api/history/base/revision_manager.py +73 -73
  29. amulet/api/history/changeable.py +15 -15
  30. amulet/api/history/data_types.py +7 -7
  31. amulet/api/history/history_manager/__init__.py +3 -3
  32. amulet/api/history/history_manager/container.py +102 -102
  33. amulet/api/history/history_manager/database.py +279 -279
  34. amulet/api/history/history_manager/meta.py +93 -93
  35. amulet/api/history/history_manager/object.py +116 -116
  36. amulet/api/history/revision_manager/__init__.py +2 -2
  37. amulet/api/history/revision_manager/disk.py +33 -33
  38. amulet/api/history/revision_manager/ram.py +12 -12
  39. amulet/api/item.py +75 -75
  40. amulet/api/level/__init__.py +4 -4
  41. amulet/api/level/base_level/__init__.py +1 -1
  42. amulet/api/level/base_level/base_level.py +1035 -1026
  43. amulet/api/level/base_level/chunk_manager.py +227 -227
  44. amulet/api/level/base_level/clone.py +389 -389
  45. amulet/api/level/base_level/player_manager.py +101 -101
  46. amulet/api/level/immutable_structure/__init__.py +1 -1
  47. amulet/api/level/immutable_structure/immutable_structure.py +94 -94
  48. amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
  49. amulet/api/level/structure.py +22 -22
  50. amulet/api/level/world.py +19 -19
  51. amulet/api/partial_3d_array/__init__.py +2 -2
  52. amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
  53. amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
  54. amulet/api/partial_3d_array/data_types.py +15 -15
  55. amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
  56. amulet/api/partial_3d_array/util.py +152 -152
  57. amulet/api/player.py +65 -65
  58. amulet/api/registry/__init__.py +2 -2
  59. amulet/api/registry/base_registry.py +34 -34
  60. amulet/api/registry/biome_manager.py +153 -153
  61. amulet/api/registry/block_manager.py +156 -156
  62. amulet/api/selection/__init__.py +2 -2
  63. amulet/api/selection/abstract_selection.py +315 -315
  64. amulet/api/selection/box.py +805 -805
  65. amulet/api/selection/group.py +488 -488
  66. amulet/api/structure.py +37 -37
  67. amulet/api/wrapper/__init__.py +8 -8
  68. amulet/api/wrapper/chunk/interface.py +441 -441
  69. amulet/api/wrapper/chunk/translator.py +567 -567
  70. amulet/api/wrapper/format_wrapper.py +772 -772
  71. amulet/api/wrapper/structure_format_wrapper.py +116 -116
  72. amulet/api/wrapper/world_format_wrapper.py +63 -63
  73. amulet/level/__init__.py +1 -1
  74. amulet/level/formats/anvil_forge_world.py +40 -40
  75. amulet/level/formats/anvil_world/__init__.py +3 -3
  76. amulet/level/formats/anvil_world/_sector_manager.py +291 -384
  77. amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
  78. amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
  79. amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
  80. amulet/level/formats/anvil_world/dimension.py +177 -177
  81. amulet/level/formats/anvil_world/format.py +769 -769
  82. amulet/level/formats/anvil_world/region.py +384 -384
  83. amulet/level/formats/construction/__init__.py +3 -3
  84. amulet/level/formats/construction/format_wrapper.py +515 -515
  85. amulet/level/formats/construction/interface.py +134 -134
  86. amulet/level/formats/construction/section.py +60 -60
  87. amulet/level/formats/construction/util.py +165 -165
  88. amulet/level/formats/leveldb_world/__init__.py +3 -3
  89. amulet/level/formats/leveldb_world/chunk.py +33 -33
  90. amulet/level/formats/leveldb_world/dimension.py +385 -419
  91. amulet/level/formats/leveldb_world/format.py +659 -641
  92. amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
  93. amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
  94. amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
  95. amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
  96. amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
  97. amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
  98. amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
  99. amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
  100. amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
  101. amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
  102. amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
  103. amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
  104. amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
  105. amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
  106. amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
  107. amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
  108. amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
  109. amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
  110. amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
  111. amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
  112. amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
  113. amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
  114. amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
  115. amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
  116. amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
  117. amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
  118. amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
  119. amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
  120. amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
  121. amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
  122. amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
  123. amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
  124. amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
  125. amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
  126. amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
  127. amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
  128. amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
  129. amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
  130. amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
  131. amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
  132. amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
  133. amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
  134. amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
  135. amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
  136. amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
  137. amulet/level/formats/mcstructure/__init__.py +3 -3
  138. amulet/level/formats/mcstructure/chunk.py +50 -50
  139. amulet/level/formats/mcstructure/format_wrapper.py +408 -408
  140. amulet/level/formats/mcstructure/interface.py +175 -175
  141. amulet/level/formats/schematic/__init__.py +3 -3
  142. amulet/level/formats/schematic/chunk.py +55 -55
  143. amulet/level/formats/schematic/data_types.py +4 -4
  144. amulet/level/formats/schematic/format_wrapper.py +373 -373
  145. amulet/level/formats/schematic/interface.py +142 -142
  146. amulet/level/formats/sponge_schem/__init__.py +4 -4
  147. amulet/level/formats/sponge_schem/chunk.py +62 -62
  148. amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
  149. amulet/level/formats/sponge_schem/interface.py +118 -118
  150. amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
  151. amulet/level/formats/sponge_schem/varint/varint.py +87 -87
  152. amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
  153. amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
  154. amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
  155. amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
  156. amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
  157. amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
  158. amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
  159. amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
  160. amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
  161. amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
  162. amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
  163. amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
  164. amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
  165. amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
  166. amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
  167. amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
  168. amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
  169. amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
  170. amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
  171. amulet/level/load.py +59 -59
  172. amulet/level/loader.py +95 -95
  173. amulet/level/translators/chunk/bedrock/__init__.py +267 -267
  174. amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
  175. amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
  176. amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
  177. amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
  178. amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
  179. amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
  180. amulet/libs/leveldb/__init__.py +7 -7
  181. amulet/operations/__init__.py +5 -5
  182. amulet/operations/clone.py +18 -18
  183. amulet/operations/delete_chunk.py +32 -32
  184. amulet/operations/fill.py +30 -30
  185. amulet/operations/paste.py +65 -65
  186. amulet/operations/replace.py +58 -58
  187. amulet/utils/__init__.py +14 -14
  188. amulet/utils/format_utils.py +41 -41
  189. amulet/utils/generator.py +15 -15
  190. amulet/utils/matrix.py +243 -243
  191. amulet/utils/numpy_helpers.py +46 -46
  192. amulet/utils/world_utils.py +349 -349
  193. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
  194. amulet_core-1.9.20.dist-info/RECORD +208 -0
  195. amulet_core-1.9.19.dist-info/RECORD +0 -208
  196. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
  197. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
  198. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
@@ -1,207 +1,207 @@
1
- import numpy
2
- from typing import Union, Optional, Dict, Tuple
3
- from copy import deepcopy
4
- from enum import IntEnum
5
-
6
-
7
- from amulet.api.partial_3d_array import UnboundedPartial3DArray
8
-
9
-
10
- class Biomes3D(UnboundedPartial3DArray):
11
- """
12
- The Biomes3D class is designed to represent a 3D integer array but with no vertical height limit.
13
-
14
- See :class:`UnboundedPartial3DArray` for more information on how this works.
15
- """
16
-
17
- def __init__(
18
- self,
19
- input_array: Optional[Union[Dict[int, numpy.ndarray], "Biomes3D"]] = None,
20
- ):
21
- """
22
- Construct a :class:`Biomes3D` class from the given data.
23
-
24
- :param input_array: Either an instance of :class:`Biomes3D` or a dictionary converting sub-chunk id to a 4x4x4 numpy array.
25
- """
26
- if input_array is None:
27
- input_array = {}
28
- if isinstance(input_array, Biomes3D):
29
- input_array: dict = deepcopy(input_array._sections)
30
- if not isinstance(input_array, dict):
31
- raise Exception(f"Input array must be Biomes3D or dict, got {input_array}")
32
- super().__init__(numpy.uint32, 0, (4, 4, 4), (0, 16), sections=input_array)
33
-
34
-
35
- class BiomesShape(IntEnum):
36
- """
37
- An enum of the different states the :class:`Biomes` class can be in.
38
-
39
- >>> ShapeNull = 0 # The biome array does not exist
40
- >>> Shape2D = 2 # The biome array is a 2D array
41
- >>> Shape3D = 3 # The biome array is a 3D array
42
- """
43
-
44
- ShapeNull = 0 # doc: The biome array does not exist
45
- Shape2D = 2 # doc: The biome array is a 2D array
46
- Shape3D = 3 # doc: The biome array is a 3D array
47
-
48
-
49
- class Biomes:
50
- """
51
- Biomes is a class used to store a 2D biome array and/or 3D biome array and facilitate switching between them.
52
-
53
- There are three states biomes can be in.
54
-
55
- The first is a blank state where no biomes are defined. This is used when a chunk is partially generated and the biomes array has not be created.
56
-
57
- The second is a 2D array of size (16, 16) used for old Java worlds and Bedrock worlds.
58
-
59
- The last is the 3D state used since Java 1.15. See :class:`Biomes3D` for more information.
60
- """
61
-
62
- __slots__ = ("_2d", "_3d", "_dimension")
63
- _2d: Optional[numpy.ndarray]
64
- _3d: Optional[Biomes3D]
65
- _dimension: BiomesShape
66
-
67
- def __init__(
68
- self, array: Union[numpy.ndarray, Biomes3D, Dict[int, numpy.ndarray]] = None
69
- ):
70
- """
71
- Construct a new instance of :class:`Biomes`
72
-
73
- :param array: The array to initialise with. Can be None or not defined to have the empty state.
74
- """
75
- self._2d: Optional[numpy.ndarray] = None
76
- self._3d: Optional[Biomes3D] = None
77
- if array is None:
78
- self._dimension = BiomesShape.ShapeNull
79
- elif isinstance(array, numpy.ndarray):
80
- assert array.shape == (
81
- 16,
82
- 16,
83
- ), "If Biomes is given an ndarray it must be 16x16"
84
- self._2d = array.copy()
85
- self._dimension = BiomesShape.Shape2D
86
- elif isinstance(array, (dict, Biomes3D)):
87
- self._3d = Biomes3D(array)
88
- self._dimension = BiomesShape.Shape3D
89
-
90
- def to_raw(
91
- self,
92
- ) -> Tuple[
93
- BiomesShape, Optional[numpy.ndarray], Optional[Dict[int, numpy.ndarray]]
94
- ]:
95
- """Don't use this method. Use to pickle data."""
96
- if self._3d is None:
97
- sections = None
98
- else:
99
- sections = self._3d._sections
100
- return self._dimension, self._2d, sections
101
-
102
- @classmethod
103
- def from_raw(
104
- cls,
105
- dimension: BiomesShape,
106
- d2: Optional[numpy.ndarray],
107
- d3: Optional[Dict[int, numpy.ndarray]],
108
- ) -> "Biomes":
109
- """Don't use this method. Use to unpickle data."""
110
- biomes = cls()
111
- biomes._dimension = dimension
112
- biomes._2d = d2
113
- if d3 is not None:
114
- biomes._3d = Biomes3D(d3)
115
- return biomes
116
-
117
- @property
118
- def dimension(self) -> BiomesShape:
119
- """
120
- The number of dimensions the data has
121
-
122
- :attr:`BiomesShape.ShapeNull` when there is no data.
123
- Will error if you try accessing data from it without converting to one of the other formats.
124
-
125
- :attr:`BiomesShape.Shape2D` when the data is a 2D (16, 16) numpy array.
126
-
127
- :attr:`BiomesShape.Shape3D` when the data is a 3D (4, inf, 4) :class:`Biome3D` array made of sections of size (4, 4, 4).
128
- """
129
- return self._dimension
130
-
131
- def convert_to_2d(self):
132
- """
133
- Convert the data to the 2D 16x16 format from whatever format it was in.
134
-
135
- If it was in the Null state it will be initialised with the first entry in the :obj:`~amulet.api.registry.biome_manager.BiomeManager`.
136
-
137
- In this mode this class will behave like a numpy array.
138
- """
139
- if self._2d is None:
140
- self._2d = numpy.zeros((16, 16), numpy.uint32)
141
- if self._dimension is BiomesShape.Shape3D and self._3d is not None:
142
- # convert from 3D
143
- self._2d[:, :] = numpy.kron(
144
- numpy.reshape(self._3d[:, 0, :], (4, 4)), numpy.ones((4, 4))
145
- )
146
- self._dimension = BiomesShape.Shape2D
147
-
148
- def convert_to_3d(self):
149
- """
150
- Convert the data to the 3D (4, inf, 4) format from whatever format it was in.
151
-
152
- If it was in the Null state it will be initialised with the first entry in the :class:`~amulet.api.registry.biome_manager.BiomeManager`.
153
-
154
- In this mode this class will behave like the :class:`Biome3D` class.
155
- """
156
- if self._3d is None:
157
- self._3d = Biomes3D()
158
- if self._dimension is BiomesShape.Shape2D and self._2d is not None:
159
- # convert from 2D
160
- self._3d[:, :, :] = numpy.repeat(
161
- self._2d[::4, ::4].reshape(4, 1, 4), 64, axis=1
162
- )
163
- self._dimension = BiomesShape.Shape3D
164
-
165
- def _get_active(self) -> Union[numpy.ndarray, Biomes3D]:
166
- if self._dimension is BiomesShape.ShapeNull:
167
- raise Exception(
168
- "You are trying to use Biomes but have not picked a format. Use one of the convert methods to specify the format."
169
- )
170
- elif self._dimension is BiomesShape.Shape2D:
171
- self.convert_to_2d()
172
- return self._2d
173
- elif self._dimension is BiomesShape.Shape3D:
174
- self.convert_to_3d()
175
- return self._3d
176
- else:
177
- raise Exception("Dimension is invalid. This shouldn't happen")
178
-
179
- def copy(self):
180
- return self.__copy__()
181
-
182
- def __copy__(self):
183
- cls = self.__class__
184
- result = cls.__new__(cls)
185
- for k in self.__slots__:
186
- setattr(result, k, getattr(self, k))
187
- return result
188
-
189
- def __deepcopy__(self, memodict=None):
190
- cls = self.__class__
191
- result = cls.__new__(cls)
192
- memodict[id(self)] = result
193
- for k in self.__slots__:
194
- setattr(result, k, deepcopy(getattr(self, k), memodict))
195
- return result
196
-
197
- def __getattr__(self, item):
198
- return getattr(self._get_active(), item)
199
-
200
- def __contains__(self, item):
201
- return item in self._get_active()
202
-
203
- def __getitem__(self, item):
204
- return self._get_active()[item]
205
-
206
- def __setitem__(self, key, value):
207
- self._get_active()[key] = value
1
+ import numpy
2
+ from typing import Union, Optional, Dict, Tuple
3
+ from copy import deepcopy
4
+ from enum import IntEnum
5
+
6
+
7
+ from amulet.api.partial_3d_array import UnboundedPartial3DArray
8
+
9
+
10
+ class Biomes3D(UnboundedPartial3DArray):
11
+ """
12
+ The Biomes3D class is designed to represent a 3D integer array but with no vertical height limit.
13
+
14
+ See :class:`UnboundedPartial3DArray` for more information on how this works.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ input_array: Optional[Union[Dict[int, numpy.ndarray], "Biomes3D"]] = None,
20
+ ):
21
+ """
22
+ Construct a :class:`Biomes3D` class from the given data.
23
+
24
+ :param input_array: Either an instance of :class:`Biomes3D` or a dictionary converting sub-chunk id to a 4x4x4 numpy array.
25
+ """
26
+ if input_array is None:
27
+ input_array = {}
28
+ if isinstance(input_array, Biomes3D):
29
+ input_array: dict = deepcopy(input_array._sections)
30
+ if not isinstance(input_array, dict):
31
+ raise Exception(f"Input array must be Biomes3D or dict, got {input_array}")
32
+ super().__init__(numpy.uint32, 0, (4, 4, 4), (0, 16), sections=input_array)
33
+
34
+
35
+ class BiomesShape(IntEnum):
36
+ """
37
+ An enum of the different states the :class:`Biomes` class can be in.
38
+
39
+ >>> ShapeNull = 0 # The biome array does not exist
40
+ >>> Shape2D = 2 # The biome array is a 2D array
41
+ >>> Shape3D = 3 # The biome array is a 3D array
42
+ """
43
+
44
+ ShapeNull = 0 # doc: The biome array does not exist
45
+ Shape2D = 2 # doc: The biome array is a 2D array
46
+ Shape3D = 3 # doc: The biome array is a 3D array
47
+
48
+
49
+ class Biomes:
50
+ """
51
+ Biomes is a class used to store a 2D biome array and/or 3D biome array and facilitate switching between them.
52
+
53
+ There are three states biomes can be in.
54
+
55
+ The first is a blank state where no biomes are defined. This is used when a chunk is partially generated and the biomes array has not be created.
56
+
57
+ The second is a 2D array of size (16, 16) used for old Java worlds and Bedrock worlds.
58
+
59
+ The last is the 3D state used since Java 1.15. See :class:`Biomes3D` for more information.
60
+ """
61
+
62
+ __slots__ = ("_2d", "_3d", "_dimension")
63
+ _2d: Optional[numpy.ndarray]
64
+ _3d: Optional[Biomes3D]
65
+ _dimension: BiomesShape
66
+
67
+ def __init__(
68
+ self, array: Union[numpy.ndarray, Biomes3D, Dict[int, numpy.ndarray]] = None
69
+ ):
70
+ """
71
+ Construct a new instance of :class:`Biomes`
72
+
73
+ :param array: The array to initialise with. Can be None or not defined to have the empty state.
74
+ """
75
+ self._2d: Optional[numpy.ndarray] = None
76
+ self._3d: Optional[Biomes3D] = None
77
+ if array is None:
78
+ self._dimension = BiomesShape.ShapeNull
79
+ elif isinstance(array, numpy.ndarray):
80
+ assert array.shape == (
81
+ 16,
82
+ 16,
83
+ ), "If Biomes is given an ndarray it must be 16x16"
84
+ self._2d = array.copy()
85
+ self._dimension = BiomesShape.Shape2D
86
+ elif isinstance(array, (dict, Biomes3D)):
87
+ self._3d = Biomes3D(array)
88
+ self._dimension = BiomesShape.Shape3D
89
+
90
+ def to_raw(
91
+ self,
92
+ ) -> Tuple[
93
+ BiomesShape, Optional[numpy.ndarray], Optional[Dict[int, numpy.ndarray]]
94
+ ]:
95
+ """Don't use this method. Use to pickle data."""
96
+ if self._3d is None:
97
+ sections = None
98
+ else:
99
+ sections = self._3d._sections
100
+ return self._dimension, self._2d, sections
101
+
102
+ @classmethod
103
+ def from_raw(
104
+ cls,
105
+ dimension: BiomesShape,
106
+ d2: Optional[numpy.ndarray],
107
+ d3: Optional[Dict[int, numpy.ndarray]],
108
+ ) -> "Biomes":
109
+ """Don't use this method. Use to unpickle data."""
110
+ biomes = cls()
111
+ biomes._dimension = dimension
112
+ biomes._2d = d2
113
+ if d3 is not None:
114
+ biomes._3d = Biomes3D(d3)
115
+ return biomes
116
+
117
+ @property
118
+ def dimension(self) -> BiomesShape:
119
+ """
120
+ The number of dimensions the data has
121
+
122
+ :attr:`BiomesShape.ShapeNull` when there is no data.
123
+ Will error if you try accessing data from it without converting to one of the other formats.
124
+
125
+ :attr:`BiomesShape.Shape2D` when the data is a 2D (16, 16) numpy array.
126
+
127
+ :attr:`BiomesShape.Shape3D` when the data is a 3D (4, inf, 4) :class:`Biome3D` array made of sections of size (4, 4, 4).
128
+ """
129
+ return self._dimension
130
+
131
+ def convert_to_2d(self):
132
+ """
133
+ Convert the data to the 2D 16x16 format from whatever format it was in.
134
+
135
+ If it was in the Null state it will be initialised with the first entry in the :obj:`~amulet.api.registry.biome_manager.BiomeManager`.
136
+
137
+ In this mode this class will behave like a numpy array.
138
+ """
139
+ if self._2d is None:
140
+ self._2d = numpy.zeros((16, 16), numpy.uint32)
141
+ if self._dimension is BiomesShape.Shape3D and self._3d is not None:
142
+ # convert from 3D
143
+ self._2d[:, :] = numpy.kron(
144
+ numpy.reshape(self._3d[:, 0, :], (4, 4)), numpy.ones((4, 4))
145
+ )
146
+ self._dimension = BiomesShape.Shape2D
147
+
148
+ def convert_to_3d(self):
149
+ """
150
+ Convert the data to the 3D (4, inf, 4) format from whatever format it was in.
151
+
152
+ If it was in the Null state it will be initialised with the first entry in the :class:`~amulet.api.registry.biome_manager.BiomeManager`.
153
+
154
+ In this mode this class will behave like the :class:`Biome3D` class.
155
+ """
156
+ if self._3d is None:
157
+ self._3d = Biomes3D()
158
+ if self._dimension is BiomesShape.Shape2D and self._2d is not None:
159
+ # convert from 2D
160
+ self._3d[:, :, :] = numpy.repeat(
161
+ self._2d[::4, ::4].reshape(4, 1, 4), 64, axis=1
162
+ )
163
+ self._dimension = BiomesShape.Shape3D
164
+
165
+ def _get_active(self) -> Union[numpy.ndarray, Biomes3D]:
166
+ if self._dimension is BiomesShape.ShapeNull:
167
+ raise Exception(
168
+ "You are trying to use Biomes but have not picked a format. Use one of the convert methods to specify the format."
169
+ )
170
+ elif self._dimension is BiomesShape.Shape2D:
171
+ self.convert_to_2d()
172
+ return self._2d
173
+ elif self._dimension is BiomesShape.Shape3D:
174
+ self.convert_to_3d()
175
+ return self._3d
176
+ else:
177
+ raise Exception("Dimension is invalid. This shouldn't happen")
178
+
179
+ def copy(self):
180
+ return self.__copy__()
181
+
182
+ def __copy__(self):
183
+ cls = self.__class__
184
+ result = cls.__new__(cls)
185
+ for k in self.__slots__:
186
+ setattr(result, k, getattr(self, k))
187
+ return result
188
+
189
+ def __deepcopy__(self, memodict=None):
190
+ cls = self.__class__
191
+ result = cls.__new__(cls)
192
+ memodict[id(self)] = result
193
+ for k in self.__slots__:
194
+ setattr(result, k, deepcopy(getattr(self, k), memodict))
195
+ return result
196
+
197
+ def __getattr__(self, item):
198
+ return getattr(self._get_active(), item)
199
+
200
+ def __contains__(self, item):
201
+ return item in self._get_active()
202
+
203
+ def __getitem__(self, item):
204
+ return self._get_active()[item]
205
+
206
+ def __setitem__(self, key, value):
207
+ self._get_active()[key] = value