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
amulet/api/chunk/chunk.py CHANGED
@@ -1,389 +1,389 @@
1
- from __future__ import annotations
2
-
3
- from typing import Union, Iterable, Dict
4
- import time
5
- import numpy
6
- import pickle
7
-
8
- from amulet.api.block import Block
9
- from amulet.api.registry import BlockManager
10
- from amulet.api.registry.biome_manager import BiomeManager
11
- from amulet.api.chunk import (
12
- Biomes,
13
- BiomesShape,
14
- Blocks,
15
- Status,
16
- BlockEntityDict,
17
- EntityList,
18
- )
19
- from amulet.api.entity import Entity
20
- from amulet.api.data_types import ChunkCoordinates, VersionIdentifierType
21
- from amulet.api.history.changeable import Changeable
22
-
23
-
24
- class Chunk(Changeable):
25
- """
26
- A class to represent a chunk that exists in a Minecraft world
27
- """
28
-
29
- def __init__(self, cx: int, cz: int):
30
- """
31
- Construct an instance of :class:`Chunk` at the given coordinates.
32
-
33
- :param cx: The x coordinate of the chunk (is not the same as block coordinates)
34
- :param cz: The z coordinate of the chunk (is not the same as block coordinates)
35
- """
36
- super().__init__()
37
- self._cx, self._cz = cx, cz
38
- self._changed_time = 0.0
39
-
40
- self._blocks = None
41
- self.__block_palette = BlockManager()
42
- self.__biome_palette = BiomeManager()
43
- self._biomes = None
44
- self._entities = EntityList()
45
- self._block_entities = BlockEntityDict()
46
- self._status = Status()
47
- self._misc = {} # all entries that are not important enough to get an attribute
48
-
49
- # TODO: remove these variables. They are temporary until the translator supports entities
50
- self._native_version: VersionIdentifierType = ("java", 0)
51
- self._native_entities = EntityList()
52
-
53
- def __repr__(self):
54
- return f"Chunk({self.cx}, {self.cz}, {repr(self._blocks)}, {repr(self._entities)}, {repr(self._block_entities)})"
55
-
56
- def pickle(self) -> bytes:
57
- """
58
- Serialise the data in the chunk using pickle and return the resulting bytes.
59
-
60
- :return: Pickled output.
61
- """
62
- chunk_data = (
63
- self._cx,
64
- self._cz,
65
- self._changed_time,
66
- {sy: self.blocks.get_sub_chunk(sy) for sy in self.blocks.sub_chunks},
67
- self.biomes.to_raw(),
68
- self._entities.data,
69
- tuple(self._block_entities.data.values()),
70
- self._status.value,
71
- self.misc,
72
- self._native_entities,
73
- self._native_version,
74
- )
75
- return pickle.dumps(chunk_data)
76
-
77
- @classmethod
78
- def unpickle(
79
- cls,
80
- pickled_bytes: bytes,
81
- block_palette: BlockManager,
82
- biome_palette: BiomeManager,
83
- ) -> Chunk:
84
- """
85
- Deserialise the pickled input and unpack the data into an instance of :class:`Chunk`
86
-
87
- :param pickled_bytes: The bytes returned from :func:`pickle`
88
- :param block_palette: The instance of :class:`BlockManager` associated with the level.
89
- :param biome_palette: The instance of :class:`BiomeManager` associated with the level.
90
- :return: An instance of :class:`Chunk` containing the unpickled data.
91
- """
92
- chunk_data = pickle.loads(pickled_bytes)
93
- self = cls(*chunk_data[:2])
94
- (
95
- self.blocks,
96
- biomes,
97
- self.entities,
98
- self.block_entities,
99
- self.status,
100
- self.misc,
101
- self._native_entities,
102
- self._native_version,
103
- ) = chunk_data[3:]
104
-
105
- self._biomes = Biomes.from_raw(*biomes)
106
-
107
- self._changed_time = chunk_data[2]
108
- self._block_palette = block_palette
109
- self._biome_palette = biome_palette
110
- self.changed = False
111
- return self
112
-
113
- @property
114
- def cx(self) -> int:
115
- """The chunk's x coordinate"""
116
- return self._cx
117
-
118
- @property
119
- def cz(self) -> int:
120
- """The chunk's z coordinate"""
121
- return self._cz
122
-
123
- @property
124
- def coordinates(self) -> ChunkCoordinates:
125
- """The chunk's x and z coordinates"""
126
- return self._cx, self._cz
127
-
128
- @property
129
- def changed(self) -> bool:
130
- """
131
- Has the chunk changed since the last undo point. Is used to track which chunks have changed.
132
-
133
- >>> chunk = Chunk(0, 0)
134
- >>> # Run this to notify that the chunk data has changed.
135
- >>> chunk.changed = True
136
-
137
- :setter: Set this to ``True`` if you have modified the chunk in any way.
138
- :return: ``True`` if the chunk has been changed since the last undo point, ``False`` otherwise.
139
- """
140
- return self._changed
141
-
142
- @changed.setter
143
- def changed(self, changed: bool):
144
- assert isinstance(changed, bool), "Changed value must be a bool"
145
- self._changed = changed
146
- if changed:
147
- self._changed_time = time.time()
148
-
149
- @property
150
- def changed_time(self) -> float:
151
- """
152
- The last time the chunk was changed
153
-
154
- Used to track if the chunk was changed since the last save snapshot and if the chunk model needs rebuilding.
155
- """
156
- return self._changed_time
157
-
158
- @property
159
- def blocks(self) -> Blocks:
160
- """
161
- The block array for the chunk.
162
-
163
- This is a custom class that stores a numpy array per sub-chunk.
164
-
165
- The values in the arrays are indexes into :attr:`block_palette`.
166
- """
167
- if self._blocks is None:
168
- self._blocks = Blocks()
169
- return self._blocks
170
-
171
- @blocks.setter
172
- def blocks(self, value: Union[Dict[int, numpy.ndarray], Blocks, None]):
173
- if isinstance(value, dict):
174
- value: Dict[int, numpy.ndarray]
175
- value = {k: v.astype(numpy.uint32) for k, v in value.items()}
176
- self._blocks = Blocks(value)
177
-
178
- def get_block(self, dx: int, y: int, dz: int) -> Block:
179
- """
180
- Get the universal Block object at the given location within the chunk.
181
-
182
- :param dx: The x coordinate within the chunk. 0-15 inclusive
183
- :param y: The y coordinate within the chunk. This can be any integer.
184
- :param dz: The z coordinate within the chunk. 0-15 inclusive
185
- :return: The universal Block object representation of the block at that location
186
- """
187
- return self.block_palette[self.blocks[dx, y, dz]]
188
-
189
- def set_block(self, dx: int, y: int, dz: int, block: Block):
190
- """
191
- Set the universal Block object at the given location within the chunk.
192
-
193
- :param dx: The x coordinate within the chunk. 0-15 inclusive
194
- :param y: The y coordinate within the chunk. This can be any integer.
195
- :param dz: The z coordinate within the chunk. 0-15 inclusive
196
- :param block: The universal Block object to set at the given location
197
- """
198
- self.blocks[dx, y, dz] = self.block_palette.get_add_block(block)
199
-
200
- @property
201
- def _block_palette(self) -> BlockManager:
202
- """The block block_palette for the chunk.
203
- Usually will refer to a global block block_palette."""
204
- return self.__block_palette
205
-
206
- @_block_palette.setter
207
- def _block_palette(self, new_block_palette: BlockManager):
208
- """Change the block block_palette for the chunk.
209
- This will change the block block_palette but leave the block array unchanged.
210
- Only use this if you know what you are doing.
211
- Designed for internal use. You probably want to use Chunk.block_palette"""
212
- assert isinstance(new_block_palette, BlockManager)
213
- self.__block_palette = new_block_palette
214
-
215
- @property
216
- def block_palette(self) -> BlockManager:
217
- """
218
- The block block_palette for the chunk.
219
-
220
- Usually will refer to the level's global block_palette.
221
- """
222
- return self._block_palette
223
-
224
- @block_palette.setter
225
- def block_palette(self, new_block_palette: BlockManager):
226
- """
227
- Change the block block_palette for the chunk.
228
-
229
- This will copy over all block states from the old block_palette and remap the block indexes to use the new block_palette.
230
- """
231
- assert isinstance(new_block_palette, BlockManager)
232
- if new_block_palette is not self._block_palette:
233
- # if current block block_palette and the new block block_palette are not the same object
234
- if self._block_palette:
235
- # if there are blocks in the current block block_palette remap the data
236
- block_lut = numpy.array(
237
- [
238
- new_block_palette.get_add_block(block)
239
- for block in self._block_palette.blocks
240
- ],
241
- dtype=numpy.uint32,
242
- )
243
- for cy in self.blocks.sub_chunks:
244
- self.blocks.add_sub_chunk(
245
- cy, block_lut[self.blocks.get_sub_chunk(cy)]
246
- )
247
-
248
- self.__block_palette = new_block_palette
249
-
250
- @property
251
- def biomes(self) -> Biomes:
252
- """
253
- The biome array for the chunk.
254
-
255
- This is a custom class that stores numpy arrays. See the :class:`Biomes` documentation for more information.
256
-
257
- The values in the arrays are indexes into :attr:`biome_palette`.
258
- """
259
- if self._biomes is None:
260
- self._biomes = Biomes()
261
- return self._biomes
262
-
263
- @biomes.setter
264
- def biomes(self, value: Union[Biomes, Dict[int, numpy.ndarray]]):
265
- self._biomes = Biomes(value)
266
-
267
- @property
268
- def _biome_palette(self) -> BiomeManager:
269
- """
270
- The biome_palette for the chunk.
271
-
272
- Usually will refer to a global biome_palette.
273
- """
274
- return self.__biome_palette
275
-
276
- @_biome_palette.setter
277
- def _biome_palette(self, new_biome_palette: BiomeManager):
278
- """
279
- Change the biome_palette for the chunk.
280
-
281
- This will change the biome_palette but leave the biome array unchanged.
282
-
283
- Only use this if you know what you are doing.
284
-
285
- Designed for internal use. You probably want to use Chunk.biome_palette
286
- """
287
- assert isinstance(new_biome_palette, BiomeManager)
288
- self.__biome_palette = new_biome_palette
289
-
290
- @property
291
- def biome_palette(self) -> BiomeManager:
292
- """
293
- The biome block_palette for the chunk.
294
-
295
- Usually will refer to the level's global biome_palette.
296
- """
297
- return self._biome_palette
298
-
299
- @biome_palette.setter
300
- def biome_palette(self, new_biome_palette: BiomeManager):
301
- """
302
- Change the biome_palette for the chunk.
303
-
304
- This will copy over all biome states from the old biome_palette and remap the biome indexes to use the new_biome_palette.
305
- """
306
- assert isinstance(new_biome_palette, BiomeManager)
307
- if new_biome_palette is not self._biome_palette:
308
- # if current biome_palette and the new biome_palette are not the same object
309
- if self._biome_palette:
310
- # if there are biomes in the current biome_palette remap the data
311
- biome_lut = numpy.array(
312
- [
313
- new_biome_palette.get_add_biome(biome)
314
- for biome in self._biome_palette.biomes
315
- ],
316
- dtype=numpy.uint32,
317
- )
318
- if self.biomes.dimension == BiomesShape.Shape2D:
319
- self.biomes = biome_lut[self.biomes]
320
- elif self.biomes.dimension == BiomesShape.Shape3D:
321
- self.biomes = {
322
- sy: biome_lut[self.biomes.get_section(sy)]
323
- for sy in self.biomes.sections
324
- }
325
-
326
- self.__biome_palette = new_biome_palette
327
-
328
- @property
329
- def entities(self) -> EntityList:
330
- """
331
- Property that returns the chunk's entity list. Setting this property replaces the chunk's entity list
332
-
333
- :return: A list of all the entities contained in the chunk
334
- """
335
- return self._entities
336
-
337
- @entities.setter
338
- def entities(self, value: Iterable[Entity]):
339
- """
340
- :param value: The new entity list
341
- :type value: list
342
- :return:
343
- """
344
- if self._entities != value:
345
- self._entities = EntityList(value)
346
-
347
- @property
348
- def block_entities(self) -> BlockEntityDict:
349
- """
350
- Property that returns the chunk's block entity list. Setting this property replaces the chunk's block entity list
351
-
352
- :return: A list of all the block entities contained in the chunk
353
- """
354
- return self._block_entities
355
-
356
- @block_entities.setter
357
- def block_entities(self, value: BlockEntityDict.InputType):
358
- """
359
- :param value: The new block entity list
360
- :type value: list
361
- :return:
362
- """
363
- if self._block_entities != value:
364
- self._block_entities = BlockEntityDict(value)
365
-
366
- @property
367
- def status(self) -> Status:
368
- """
369
- A class containing the chunk's generation status.
370
- """
371
- return self._status
372
-
373
- @status.setter
374
- def status(self, value: Union[float, int, str]):
375
- self._status.value = value
376
-
377
- @property
378
- def misc(self) -> dict:
379
- """
380
- Extra data that exists in a chunk but does not have its own location.
381
-
382
- Data in here is not guaranteed to exist and may get moved at a later date.
383
- """
384
- return self._misc
385
-
386
- @misc.setter
387
- def misc(self, misc: dict):
388
- assert isinstance(misc, dict), "misc must be a dictionary."
389
- self._misc = misc
1
+ from __future__ import annotations
2
+
3
+ from typing import Union, Iterable, Dict
4
+ import time
5
+ import numpy
6
+ import pickle
7
+
8
+ from amulet.api.block import Block
9
+ from amulet.api.registry import BlockManager
10
+ from amulet.api.registry.biome_manager import BiomeManager
11
+ from amulet.api.chunk import (
12
+ Biomes,
13
+ BiomesShape,
14
+ Blocks,
15
+ Status,
16
+ BlockEntityDict,
17
+ EntityList,
18
+ )
19
+ from amulet.api.entity import Entity
20
+ from amulet.api.data_types import ChunkCoordinates, VersionIdentifierType
21
+ from amulet.api.history.changeable import Changeable
22
+
23
+
24
+ class Chunk(Changeable):
25
+ """
26
+ A class to represent a chunk that exists in a Minecraft world
27
+ """
28
+
29
+ def __init__(self, cx: int, cz: int):
30
+ """
31
+ Construct an instance of :class:`Chunk` at the given coordinates.
32
+
33
+ :param cx: The x coordinate of the chunk (is not the same as block coordinates)
34
+ :param cz: The z coordinate of the chunk (is not the same as block coordinates)
35
+ """
36
+ super().__init__()
37
+ self._cx, self._cz = cx, cz
38
+ self._changed_time = 0.0
39
+
40
+ self._blocks = None
41
+ self.__block_palette = BlockManager()
42
+ self.__biome_palette = BiomeManager()
43
+ self._biomes = None
44
+ self._entities = EntityList()
45
+ self._block_entities = BlockEntityDict()
46
+ self._status = Status()
47
+ self._misc = {} # all entries that are not important enough to get an attribute
48
+
49
+ # TODO: remove these variables. They are temporary until the translator supports entities
50
+ self._native_version: VersionIdentifierType = ("java", 0)
51
+ self._native_entities = EntityList()
52
+
53
+ def __repr__(self):
54
+ return f"Chunk({self.cx}, {self.cz}, {repr(self._blocks)}, {repr(self._entities)}, {repr(self._block_entities)})"
55
+
56
+ def pickle(self) -> bytes:
57
+ """
58
+ Serialise the data in the chunk using pickle and return the resulting bytes.
59
+
60
+ :return: Pickled output.
61
+ """
62
+ chunk_data = (
63
+ self._cx,
64
+ self._cz,
65
+ self._changed_time,
66
+ {sy: self.blocks.get_sub_chunk(sy) for sy in self.blocks.sub_chunks},
67
+ self.biomes.to_raw(),
68
+ self._entities.data,
69
+ tuple(self._block_entities.data.values()),
70
+ self._status.value,
71
+ self.misc,
72
+ self._native_entities,
73
+ self._native_version,
74
+ )
75
+ return pickle.dumps(chunk_data)
76
+
77
+ @classmethod
78
+ def unpickle(
79
+ cls,
80
+ pickled_bytes: bytes,
81
+ block_palette: BlockManager,
82
+ biome_palette: BiomeManager,
83
+ ) -> Chunk:
84
+ """
85
+ Deserialise the pickled input and unpack the data into an instance of :class:`Chunk`
86
+
87
+ :param pickled_bytes: The bytes returned from :func:`pickle`
88
+ :param block_palette: The instance of :class:`BlockManager` associated with the level.
89
+ :param biome_palette: The instance of :class:`BiomeManager` associated with the level.
90
+ :return: An instance of :class:`Chunk` containing the unpickled data.
91
+ """
92
+ chunk_data = pickle.loads(pickled_bytes)
93
+ self = cls(*chunk_data[:2])
94
+ (
95
+ self.blocks,
96
+ biomes,
97
+ self.entities,
98
+ self.block_entities,
99
+ self.status,
100
+ self.misc,
101
+ self._native_entities,
102
+ self._native_version,
103
+ ) = chunk_data[3:]
104
+
105
+ self._biomes = Biomes.from_raw(*biomes)
106
+
107
+ self._changed_time = chunk_data[2]
108
+ self._block_palette = block_palette
109
+ self._biome_palette = biome_palette
110
+ self.changed = False
111
+ return self
112
+
113
+ @property
114
+ def cx(self) -> int:
115
+ """The chunk's x coordinate"""
116
+ return self._cx
117
+
118
+ @property
119
+ def cz(self) -> int:
120
+ """The chunk's z coordinate"""
121
+ return self._cz
122
+
123
+ @property
124
+ def coordinates(self) -> ChunkCoordinates:
125
+ """The chunk's x and z coordinates"""
126
+ return self._cx, self._cz
127
+
128
+ @property
129
+ def changed(self) -> bool:
130
+ """
131
+ Has the chunk changed since the last undo point. Is used to track which chunks have changed.
132
+
133
+ >>> chunk = Chunk(0, 0)
134
+ >>> # Run this to notify that the chunk data has changed.
135
+ >>> chunk.changed = True
136
+
137
+ :setter: Set this to ``True`` if you have modified the chunk in any way.
138
+ :return: ``True`` if the chunk has been changed since the last undo point, ``False`` otherwise.
139
+ """
140
+ return self._changed
141
+
142
+ @changed.setter
143
+ def changed(self, changed: bool):
144
+ assert isinstance(changed, bool), "Changed value must be a bool"
145
+ self._changed = changed
146
+ if changed:
147
+ self._changed_time = time.time()
148
+
149
+ @property
150
+ def changed_time(self) -> float:
151
+ """
152
+ The last time the chunk was changed
153
+
154
+ Used to track if the chunk was changed since the last save snapshot and if the chunk model needs rebuilding.
155
+ """
156
+ return self._changed_time
157
+
158
+ @property
159
+ def blocks(self) -> Blocks:
160
+ """
161
+ The block array for the chunk.
162
+
163
+ This is a custom class that stores a numpy array per sub-chunk.
164
+
165
+ The values in the arrays are indexes into :attr:`block_palette`.
166
+ """
167
+ if self._blocks is None:
168
+ self._blocks = Blocks()
169
+ return self._blocks
170
+
171
+ @blocks.setter
172
+ def blocks(self, value: Union[Dict[int, numpy.ndarray], Blocks, None]):
173
+ if isinstance(value, dict):
174
+ value: Dict[int, numpy.ndarray]
175
+ value = {k: v.astype(numpy.uint32) for k, v in value.items()}
176
+ self._blocks = Blocks(value)
177
+
178
+ def get_block(self, dx: int, y: int, dz: int) -> Block:
179
+ """
180
+ Get the universal Block object at the given location within the chunk.
181
+
182
+ :param dx: The x coordinate within the chunk. 0-15 inclusive
183
+ :param y: The y coordinate within the chunk. This can be any integer.
184
+ :param dz: The z coordinate within the chunk. 0-15 inclusive
185
+ :return: The universal Block object representation of the block at that location
186
+ """
187
+ return self.block_palette[self.blocks[dx, y, dz]]
188
+
189
+ def set_block(self, dx: int, y: int, dz: int, block: Block):
190
+ """
191
+ Set the universal Block object at the given location within the chunk.
192
+
193
+ :param dx: The x coordinate within the chunk. 0-15 inclusive
194
+ :param y: The y coordinate within the chunk. This can be any integer.
195
+ :param dz: The z coordinate within the chunk. 0-15 inclusive
196
+ :param block: The universal Block object to set at the given location
197
+ """
198
+ self.blocks[dx, y, dz] = self.block_palette.get_add_block(block)
199
+
200
+ @property
201
+ def _block_palette(self) -> BlockManager:
202
+ """The block block_palette for the chunk.
203
+ Usually will refer to a global block block_palette."""
204
+ return self.__block_palette
205
+
206
+ @_block_palette.setter
207
+ def _block_palette(self, new_block_palette: BlockManager):
208
+ """Change the block block_palette for the chunk.
209
+ This will change the block block_palette but leave the block array unchanged.
210
+ Only use this if you know what you are doing.
211
+ Designed for internal use. You probably want to use Chunk.block_palette"""
212
+ assert isinstance(new_block_palette, BlockManager)
213
+ self.__block_palette = new_block_palette
214
+
215
+ @property
216
+ def block_palette(self) -> BlockManager:
217
+ """
218
+ The block block_palette for the chunk.
219
+
220
+ Usually will refer to the level's global block_palette.
221
+ """
222
+ return self._block_palette
223
+
224
+ @block_palette.setter
225
+ def block_palette(self, new_block_palette: BlockManager):
226
+ """
227
+ Change the block block_palette for the chunk.
228
+
229
+ This will copy over all block states from the old block_palette and remap the block indexes to use the new block_palette.
230
+ """
231
+ assert isinstance(new_block_palette, BlockManager)
232
+ if new_block_palette is not self._block_palette:
233
+ # if current block block_palette and the new block block_palette are not the same object
234
+ if self._block_palette:
235
+ # if there are blocks in the current block block_palette remap the data
236
+ block_lut = numpy.array(
237
+ [
238
+ new_block_palette.get_add_block(block)
239
+ for block in self._block_palette.blocks
240
+ ],
241
+ dtype=numpy.uint32,
242
+ )
243
+ for cy in self.blocks.sub_chunks:
244
+ self.blocks.add_sub_chunk(
245
+ cy, block_lut[self.blocks.get_sub_chunk(cy)]
246
+ )
247
+
248
+ self.__block_palette = new_block_palette
249
+
250
+ @property
251
+ def biomes(self) -> Biomes:
252
+ """
253
+ The biome array for the chunk.
254
+
255
+ This is a custom class that stores numpy arrays. See the :class:`Biomes` documentation for more information.
256
+
257
+ The values in the arrays are indexes into :attr:`biome_palette`.
258
+ """
259
+ if self._biomes is None:
260
+ self._biomes = Biomes()
261
+ return self._biomes
262
+
263
+ @biomes.setter
264
+ def biomes(self, value: Union[Biomes, Dict[int, numpy.ndarray]]):
265
+ self._biomes = Biomes(value)
266
+
267
+ @property
268
+ def _biome_palette(self) -> BiomeManager:
269
+ """
270
+ The biome_palette for the chunk.
271
+
272
+ Usually will refer to a global biome_palette.
273
+ """
274
+ return self.__biome_palette
275
+
276
+ @_biome_palette.setter
277
+ def _biome_palette(self, new_biome_palette: BiomeManager):
278
+ """
279
+ Change the biome_palette for the chunk.
280
+
281
+ This will change the biome_palette but leave the biome array unchanged.
282
+
283
+ Only use this if you know what you are doing.
284
+
285
+ Designed for internal use. You probably want to use Chunk.biome_palette
286
+ """
287
+ assert isinstance(new_biome_palette, BiomeManager)
288
+ self.__biome_palette = new_biome_palette
289
+
290
+ @property
291
+ def biome_palette(self) -> BiomeManager:
292
+ """
293
+ The biome block_palette for the chunk.
294
+
295
+ Usually will refer to the level's global biome_palette.
296
+ """
297
+ return self._biome_palette
298
+
299
+ @biome_palette.setter
300
+ def biome_palette(self, new_biome_palette: BiomeManager):
301
+ """
302
+ Change the biome_palette for the chunk.
303
+
304
+ This will copy over all biome states from the old biome_palette and remap the biome indexes to use the new_biome_palette.
305
+ """
306
+ assert isinstance(new_biome_palette, BiomeManager)
307
+ if new_biome_palette is not self._biome_palette:
308
+ # if current biome_palette and the new biome_palette are not the same object
309
+ if self._biome_palette:
310
+ # if there are biomes in the current biome_palette remap the data
311
+ biome_lut = numpy.array(
312
+ [
313
+ new_biome_palette.get_add_biome(biome)
314
+ for biome in self._biome_palette.biomes
315
+ ],
316
+ dtype=numpy.uint32,
317
+ )
318
+ if self.biomes.dimension == BiomesShape.Shape2D:
319
+ self.biomes = biome_lut[self.biomes]
320
+ elif self.biomes.dimension == BiomesShape.Shape3D:
321
+ self.biomes = {
322
+ sy: biome_lut[self.biomes.get_section(sy)]
323
+ for sy in self.biomes.sections
324
+ }
325
+
326
+ self.__biome_palette = new_biome_palette
327
+
328
+ @property
329
+ def entities(self) -> EntityList:
330
+ """
331
+ Property that returns the chunk's entity list. Setting this property replaces the chunk's entity list
332
+
333
+ :return: A list of all the entities contained in the chunk
334
+ """
335
+ return self._entities
336
+
337
+ @entities.setter
338
+ def entities(self, value: Iterable[Entity]):
339
+ """
340
+ :param value: The new entity list
341
+ :type value: list
342
+ :return:
343
+ """
344
+ if self._entities != value:
345
+ self._entities = EntityList(value)
346
+
347
+ @property
348
+ def block_entities(self) -> BlockEntityDict:
349
+ """
350
+ Property that returns the chunk's block entity list. Setting this property replaces the chunk's block entity list
351
+
352
+ :return: A list of all the block entities contained in the chunk
353
+ """
354
+ return self._block_entities
355
+
356
+ @block_entities.setter
357
+ def block_entities(self, value: BlockEntityDict.InputType):
358
+ """
359
+ :param value: The new block entity list
360
+ :type value: list
361
+ :return:
362
+ """
363
+ if self._block_entities != value:
364
+ self._block_entities = BlockEntityDict(value)
365
+
366
+ @property
367
+ def status(self) -> Status:
368
+ """
369
+ A class containing the chunk's generation status.
370
+ """
371
+ return self._status
372
+
373
+ @status.setter
374
+ def status(self, value: Union[float, int, str]):
375
+ self._status.value = value
376
+
377
+ @property
378
+ def misc(self) -> dict:
379
+ """
380
+ Extra data that exists in a chunk but does not have its own location.
381
+
382
+ Data in here is not guaranteed to exist and may get moved at a later date.
383
+ """
384
+ return self._misc
385
+
386
+ @misc.setter
387
+ def misc(self, misc: dict):
388
+ assert isinstance(misc, dict), "misc must be a dictionary."
389
+ self._misc = misc