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,567 +1,567 @@
1
- from __future__ import annotations
2
-
3
- from typing import Tuple, Optional, TYPE_CHECKING
4
- import copy
5
- import math
6
- import logging
7
-
8
- import numpy
9
-
10
- from amulet import entity_support
11
- from amulet.api.registry import BlockManager, BiomeManager
12
- from amulet.api.block import Block
13
- from amulet.api.block_entity import BlockEntity
14
- from amulet.api.entity import Entity
15
- from amulet.api.chunk import Chunk, BiomesShape
16
- from amulet.api.data_types import (
17
- AnyNDArray,
18
- BlockNDArray,
19
- BlockCoordinates,
20
- GetChunkCallback,
21
- TranslateBlockCallback,
22
- TranslateEntityCallback,
23
- GetBlockCallback,
24
- TranslateBlockCallbackReturn,
25
- TranslateEntityCallbackReturn,
26
- VersionNumberAny,
27
- VersionIdentifierType,
28
- )
29
-
30
- log = logging.getLogger(__name__)
31
-
32
- if TYPE_CHECKING:
33
- from PyMCTranslate import Version, TranslationManager
34
-
35
-
36
- class Translator:
37
- def translator_key(self, version_number: VersionNumberAny) -> VersionIdentifierType:
38
- return self._translator_key(version_number)
39
-
40
- def _translator_key(
41
- self, version_number: VersionNumberAny
42
- ) -> VersionIdentifierType:
43
- """
44
- Return the version key for PyMCTranslate
45
-
46
- :return: The tuple version key for PyMCTranslate
47
- """
48
- raise NotImplementedError
49
-
50
- @staticmethod
51
- def is_valid(key: Tuple) -> bool:
52
- """
53
- Returns whether this translator is able to translate the chunk type with a given identifier key,
54
- generated by the decoder.
55
-
56
- :param key: The key who's decodability needs to be checked.
57
- :return: True if this translator is able to translate the chunk type associated with the key, False otherwise.
58
- """
59
- raise NotImplementedError
60
-
61
- @staticmethod
62
- def _translate(
63
- chunk: Chunk,
64
- get_chunk_callback: Optional[GetChunkCallback],
65
- translate_block: TranslateBlockCallback,
66
- translate_entity: TranslateEntityCallback,
67
- full_translate: bool,
68
- ):
69
- if full_translate:
70
- todo = []
71
- output_block_entities = []
72
- output_entities = []
73
- finished = BlockManager()
74
- palette_mappings = {}
75
-
76
- # translate each block without using the callback
77
- for i, input_block in enumerate(chunk.block_palette):
78
- input_block: Block
79
- (
80
- output_block,
81
- output_block_entity,
82
- output_entity,
83
- extra,
84
- ) = translate_block(input_block, None, (0, 0, 0))
85
- if extra and get_chunk_callback:
86
- todo.append(i)
87
- elif output_block is not None:
88
- palette_mappings[i] = finished.get_add_block(output_block)
89
- if output_block_entity is not None:
90
- for cy in chunk.blocks.sub_chunks:
91
- for x, y, z in zip(
92
- *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
93
- ):
94
- output_block_entities.append(
95
- output_block_entity.new_at_location(
96
- x + chunk.cx * 16,
97
- y + cy * 16,
98
- z + chunk.cz * 16,
99
- )
100
- )
101
- else:
102
- # TODO: this should only happen if the object is an entity, set the block to air
103
- pass
104
-
105
- if output_entity and entity_support:
106
- for cy in chunk.blocks.sub_chunks:
107
- for x, y, z in zip(
108
- *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
109
- ):
110
- x += chunk.cx * 16
111
- y += cy * 16
112
- z += chunk.cz * 16
113
- for entity in output_entity:
114
- e = copy.deepcopy(entity)
115
- e.location += (x, y, z)
116
- output_entities.append(e)
117
-
118
- # re-translate the blocks that require extra information
119
- block_mappings = {}
120
- for index in todo:
121
- for cy in chunk.blocks.sub_chunks:
122
- for x, y, z in zip(
123
- *numpy.where(chunk.blocks.get_sub_chunk(cy) == index)
124
- ):
125
- y += cy * 16
126
-
127
- def get_block_at(
128
- pos: BlockCoordinates,
129
- ) -> Tuple[Block, Optional[BlockEntity]]:
130
- """Get a block at a location relative to the current block"""
131
- nonlocal x, y, z, chunk, cy
132
-
133
- # calculate position relative to chunk base
134
- dx, dy, dz = pos
135
- dx += x
136
- dy += y
137
- dz += z
138
-
139
- abs_x = dx + chunk.cx * 16
140
- abs_y = dy
141
- abs_z = dz + chunk.cz * 16
142
-
143
- # calculate relative chunk position
144
- cx = dx // 16
145
- cz = dz // 16
146
- if cx == 0 and cz == 0:
147
- # if it is the current chunk
148
- block = chunk.block_palette[chunk.blocks[dx, dy, dz]]
149
- return (
150
- block,
151
- chunk.block_entities.get((abs_x, abs_y, abs_z)),
152
- )
153
-
154
- # if it is in a different chunk
155
- local_chunk = get_chunk_callback(cx, cz)
156
- block = local_chunk.block_palette[
157
- local_chunk.blocks[dx % 16, dy, dz % 16]
158
- ]
159
- return (
160
- block,
161
- local_chunk.block_entities.get((abs_x, abs_y, abs_z)),
162
- )
163
-
164
- input_block = chunk.block_palette[chunk.blocks[x, y, z]]
165
- (
166
- output_block,
167
- output_block_entity,
168
- output_entity,
169
- _,
170
- ) = translate_block(
171
- input_block,
172
- get_block_at,
173
- (x + chunk.cx * 16, y, z + chunk.cz * 16),
174
- )
175
- if output_block is not None:
176
- block_mappings[(x, y, z)] = finished.get_add_block(
177
- output_block
178
- )
179
- if output_block_entity is not None:
180
- output_block_entities.append(
181
- output_block_entity.new_at_location(
182
- x + chunk.cx * 16, y, z + chunk.cz * 16
183
- )
184
- )
185
- else:
186
- # TODO: set the block to air
187
- pass
188
-
189
- if output_entity and entity_support:
190
- for entity in output_entity:
191
- e = copy.deepcopy(entity)
192
- e.location += (x, y, z)
193
- output_entities.append(e)
194
-
195
- if entity_support:
196
- for entity in chunk.entities:
197
- output_block, output_block_entity, output_entity = translate_entity(
198
- entity
199
- )
200
- if output_block is not None:
201
- block_location = (
202
- int(math.floor(entity.x)),
203
- int(math.floor(entity.y)),
204
- int(math.floor(entity.z)),
205
- )
206
- block_mappings[block_location] = output_block
207
- if output_block_entity:
208
- output_block_entities.append(
209
- output_block_entity.new_at_location(*block_location)
210
- )
211
- if output_entity:
212
- for e in output_entity:
213
- e.location = entity.location
214
- output_entities.append(e)
215
-
216
- for cy in chunk.blocks.sub_chunks:
217
- old_blocks = chunk.blocks.get_sub_chunk(cy)
218
- new_blocks = numpy.zeros(old_blocks.shape, dtype=old_blocks.dtype)
219
- for old, new in palette_mappings.items():
220
- new_blocks[old_blocks == old] = new
221
- chunk.blocks.add_sub_chunk(cy, new_blocks)
222
- for (x, y, z), new in block_mappings.items():
223
- chunk.blocks[x, y, z] = new
224
- chunk.block_entities = output_block_entities
225
- chunk.entities = output_entities
226
- chunk._block_palette = finished
227
-
228
- def to_universal(
229
- self,
230
- chunk_version: VersionNumberAny,
231
- translation_manager: "TranslationManager",
232
- chunk: Chunk,
233
- get_chunk_callback: Optional[GetChunkCallback],
234
- full_translate: bool,
235
- ) -> Chunk:
236
- """
237
- Translate an interface-specific chunk into the universal format.
238
-
239
- :param chunk_version: The version number (int or tuple) of the input chunk
240
- :param translation_manager: TranslationManager used for the translation
241
- :param chunk: The chunk to translate.
242
- :param get_chunk_callback: function callback to get a chunk's data
243
- :param full_translate: if true do a full translate. If false just unpack the block_palette (used in callback)
244
- :return: Chunk object in the universal format.
245
- """
246
- version = translation_manager.get_version(*self._translator_key(chunk_version))
247
- self._biomes_to_universal(version, chunk)
248
- self._blocks_entities_to_universal(
249
- chunk_version,
250
- translation_manager,
251
- chunk,
252
- get_chunk_callback,
253
- full_translate,
254
- )
255
- return chunk
256
-
257
- def _blocks_entities_to_universal(
258
- self,
259
- chunk_version: VersionNumberAny,
260
- translation_manager: "TranslationManager",
261
- chunk: Chunk,
262
- get_chunk_callback: Optional[GetChunkCallback],
263
- full_translate: bool,
264
- ):
265
- version = translation_manager.get_version(*self._translator_key(chunk_version))
266
-
267
- def translate_block(
268
- input_object: Block,
269
- get_block_callback: Optional[GetBlockCallback],
270
- block_location: BlockCoordinates,
271
- ) -> TranslateBlockCallbackReturn:
272
- final_block = None
273
- final_block_entity = None
274
- final_entities = []
275
- final_extra = False
276
-
277
- for depth, block in enumerate(input_object.block_tuple):
278
- (
279
- output_object,
280
- output_block_entity,
281
- extra,
282
- ) = version.block.to_universal(
283
- block,
284
- get_block_callback=get_block_callback,
285
- block_location=block_location,
286
- )
287
-
288
- if isinstance(output_object, Block):
289
- if not output_object.namespace.startswith("universal"):
290
- log.debug(
291
- f"Error translating {input_object.full_blockstate} to universal. Got {output_object.full_blockstate}"
292
- )
293
- if final_block is None:
294
- final_block = output_object
295
- else:
296
- final_block += output_object
297
- if depth == 0:
298
- final_block_entity = output_block_entity
299
-
300
- elif isinstance(output_object, Entity):
301
- final_entities.append(output_object)
302
- # TODO: offset entity coords
303
-
304
- final_extra |= extra
305
-
306
- return final_block, final_block_entity, final_entities, final_extra
307
-
308
- def translate_entity(input_object: Entity) -> TranslateEntityCallbackReturn:
309
- final_block = None
310
- final_block_entity = None
311
- final_entities = []
312
- # TODO
313
- return final_block, final_block_entity, final_entities
314
-
315
- self._translate(
316
- chunk,
317
- get_chunk_callback,
318
- translate_block,
319
- translate_entity,
320
- full_translate,
321
- )
322
-
323
- @staticmethod
324
- def _biomes_to_universal(translator_version: "Version", chunk: Chunk):
325
- chunk._biome_palette = BiomeManager(
326
- [
327
- translator_version.biome.to_universal(biome)
328
- for biome in chunk.biome_palette
329
- ]
330
- )
331
-
332
- def from_universal(
333
- self,
334
- max_world_version_number: VersionNumberAny,
335
- translation_manager: "TranslationManager",
336
- chunk: Chunk,
337
- get_chunk_callback: Optional[GetChunkCallback],
338
- full_translate: bool,
339
- ) -> Chunk:
340
- """
341
- Translate a universal chunk into the interface-specific format.
342
-
343
- :param max_world_version_number: The version number (int or tuple) of the max world version
344
- :param translation_manager: TranslationManager used for the translation
345
- :param chunk: The chunk to translate.
346
- :param get_chunk_callback: function callback to get a chunk's data
347
- :param full_translate: if true do a full translate. If false just pack the block_palette (used in callback)
348
- :return: Chunk object in the interface-specific format and block_palette.
349
- """
350
- version = translation_manager.get_version(
351
- *self._translator_key(max_world_version_number)
352
- )
353
- self._blocks_entities_from_universal(
354
- max_world_version_number,
355
- translation_manager,
356
- chunk,
357
- get_chunk_callback,
358
- full_translate,
359
- )
360
- self._biomes_from_universal(version, chunk)
361
- return chunk
362
-
363
- def _blocks_entities_from_universal(
364
- self,
365
- max_world_version_number: VersionNumberAny,
366
- translation_manager: "TranslationManager",
367
- chunk: Chunk,
368
- get_chunk_callback: Optional[GetChunkCallback],
369
- full_translate: bool,
370
- ):
371
- version = translation_manager.get_version(
372
- *self._translator_key(max_world_version_number)
373
- )
374
-
375
- # TODO: perhaps find a way so this code isn't duplicated in three places
376
- def translate_block(
377
- input_object: Block,
378
- get_block_callback: Optional[GetBlockCallback],
379
- block_location: BlockCoordinates,
380
- ) -> TranslateBlockCallbackReturn:
381
- final_block = None
382
- final_block_entity = None
383
- final_entities = []
384
- final_extra = False
385
-
386
- for depth, block in enumerate(input_object.block_tuple):
387
- (
388
- output_object,
389
- output_block_entity,
390
- extra,
391
- ) = version.block.from_universal(
392
- block,
393
- get_block_callback=get_block_callback,
394
- block_location=block_location,
395
- )
396
-
397
- if isinstance(output_object, Block):
398
- if __debug__ and output_object.namespace.startswith("universal"):
399
- log.debug(
400
- f"Error translating {input_object.blockstate} from universal. Got {output_object.blockstate}"
401
- )
402
- if final_block is None:
403
- final_block = output_object
404
- else:
405
- final_block += output_object
406
- if depth == 0:
407
- final_block_entity = output_block_entity
408
-
409
- elif isinstance(output_object, Entity):
410
- final_entities.append(output_object)
411
- # TODO: offset entity coords
412
-
413
- final_extra |= extra
414
-
415
- return final_block, final_block_entity, final_entities, final_extra
416
-
417
- def translate_entity(input_object: Entity) -> TranslateEntityCallbackReturn:
418
- final_block = None
419
- final_block_entity = None
420
- final_entities = []
421
- # TODO
422
- return final_block, final_block_entity, final_entities
423
-
424
- self._translate(
425
- chunk,
426
- get_chunk_callback,
427
- translate_block,
428
- translate_entity,
429
- full_translate,
430
- )
431
-
432
- @staticmethod
433
- def _biomes_from_universal(translator_version: "Version", chunk: Chunk):
434
- chunk._biome_palette = BiomeManager(
435
- [
436
- translator_version.biome.from_universal(biome)
437
- for biome in chunk.biome_palette
438
- ]
439
- )
440
-
441
- def unpack(
442
- self,
443
- chunk_version: VersionNumberAny,
444
- translation_manager: "TranslationManager",
445
- chunk: Chunk,
446
- palette: AnyNDArray,
447
- ) -> Chunk:
448
- """
449
- Unpack the version-specific block_palette into the stringified version where needed.
450
-
451
- :return: The block_palette converted to block objects.
452
- """
453
- version_identifier = self._translator_key(chunk_version)
454
- self._unpack_blocks(translation_manager, version_identifier, chunk, palette)
455
- self._unpack_biomes(translation_manager, version_identifier, chunk)
456
- return chunk
457
-
458
- @staticmethod
459
- def _unpack_blocks(
460
- translation_manager: "TranslationManager",
461
- version_identifier: VersionIdentifierType,
462
- chunk: Chunk,
463
- block_palette: AnyNDArray,
464
- ):
465
- """
466
- Unpack the version-specific block_palette into the stringified version where needed.
467
- :return: The block_palette converted to block objects.
468
- """
469
- chunk._block_palette = BlockManager(block_palette)
470
-
471
- @staticmethod
472
- def _unpack_biomes(
473
- translation_manager: "TranslationManager",
474
- version_identifier: VersionIdentifierType,
475
- chunk: Chunk,
476
- ):
477
- """
478
- Unpack the version-specific biome_palette into the stringified version where needed.
479
- :return: The biome_palette converted to biome objects.
480
- """
481
- version = translation_manager.get_version(*version_identifier)
482
-
483
- if chunk.biomes.dimension == BiomesShape.Shape2D:
484
- biome_int_palette, biome_array = numpy.unique(
485
- chunk.biomes, return_inverse=True
486
- )
487
- chunk.biomes = biome_array.reshape(chunk.biomes.shape)
488
- chunk._biome_palette = BiomeManager(
489
- [version.biome.unpack(biome) for biome in biome_int_palette]
490
- )
491
- elif chunk.biomes.dimension == BiomesShape.Shape3D:
492
- biomes = {}
493
- palette = []
494
- palette_length = 0
495
- for sy in chunk.biomes.sections:
496
- biome_int_palette, biome_array = numpy.unique(
497
- chunk.biomes.get_section(sy), return_inverse=True
498
- )
499
- biomes[sy] = (
500
- biome_array.reshape(chunk.biomes.section_shape) + palette_length
501
- )
502
- palette_length += len(biome_int_palette)
503
- palette.append(biome_int_palette)
504
-
505
- if palette:
506
- chunk_palette, lut = numpy.unique(
507
- numpy.concatenate(palette), return_inverse=True
508
- )
509
- lut = lut.astype(numpy.uint32)
510
- for sy in biomes:
511
- biomes[sy] = lut[biomes[sy]]
512
-
513
- chunk.biomes = biomes
514
- chunk._biome_palette = BiomeManager(
515
- numpy.vectorize(version.biome.unpack)(chunk_palette)
516
- )
517
-
518
- def pack(
519
- self,
520
- max_world_version_number: VersionNumberAny,
521
- translation_manager: "TranslationManager",
522
- chunk: Chunk,
523
- ) -> Tuple[Chunk, AnyNDArray]:
524
- """
525
- Translate the list of block objects into a version-specific block_palette.
526
- :return: The block_palette converted into version-specific blocks (ie id, data tuples for 1.12)
527
- """
528
- version_identifier = self._translator_key(max_world_version_number)
529
- version = translation_manager.get_version(*version_identifier)
530
- self._pack_biomes(translation_manager, version_identifier, chunk)
531
- return (
532
- chunk,
533
- self._pack_block_palette(version, numpy.array(chunk.block_palette.blocks)),
534
- )
535
-
536
- def _pack_block_palette(
537
- self, version: "Version", palette: BlockNDArray
538
- ) -> AnyNDArray:
539
- """
540
- Pack the list of block objects into a version-specific block_palette.
541
- :return: The block_palette converted into version-specific blocks (ie id, data tuples for 1.12)
542
- """
543
- return palette
544
-
545
- @staticmethod
546
- def _pack_biomes(
547
- translation_manager: "TranslationManager",
548
- version_identifier: VersionIdentifierType,
549
- chunk: Chunk,
550
- ):
551
- """
552
- Unpack the version-specific biome_palette into the stringified version where needed.
553
- :return: The biome_palette converted to biome objects.
554
- """
555
- version = translation_manager.get_version(*version_identifier)
556
-
557
- biome_palette = numpy.array(
558
- [version.biome.pack(biome) for biome in chunk.biome_palette], numpy.uint32
559
- )
560
- if chunk.biomes.dimension == BiomesShape.Shape2D:
561
- chunk.biomes = biome_palette[chunk.biomes]
562
- elif chunk.biomes.dimension == BiomesShape.Shape3D:
563
- chunk.biomes = {
564
- sy: biome_palette[chunk.biomes.get_section(sy)]
565
- for sy in chunk.biomes.sections
566
- }
567
- chunk._biome_palette = BiomeManager()
1
+ from __future__ import annotations
2
+
3
+ from typing import Tuple, Optional, TYPE_CHECKING
4
+ import copy
5
+ import math
6
+ import logging
7
+
8
+ import numpy
9
+
10
+ from amulet import entity_support
11
+ from amulet.api.registry import BlockManager, BiomeManager
12
+ from amulet.api.block import Block
13
+ from amulet.api.block_entity import BlockEntity
14
+ from amulet.api.entity import Entity
15
+ from amulet.api.chunk import Chunk, BiomesShape
16
+ from amulet.api.data_types import (
17
+ AnyNDArray,
18
+ BlockNDArray,
19
+ BlockCoordinates,
20
+ GetChunkCallback,
21
+ TranslateBlockCallback,
22
+ TranslateEntityCallback,
23
+ GetBlockCallback,
24
+ TranslateBlockCallbackReturn,
25
+ TranslateEntityCallbackReturn,
26
+ VersionNumberAny,
27
+ VersionIdentifierType,
28
+ )
29
+
30
+ log = logging.getLogger(__name__)
31
+
32
+ if TYPE_CHECKING:
33
+ from PyMCTranslate import Version, TranslationManager
34
+
35
+
36
+ class Translator:
37
+ def translator_key(self, version_number: VersionNumberAny) -> VersionIdentifierType:
38
+ return self._translator_key(version_number)
39
+
40
+ def _translator_key(
41
+ self, version_number: VersionNumberAny
42
+ ) -> VersionIdentifierType:
43
+ """
44
+ Return the version key for PyMCTranslate
45
+
46
+ :return: The tuple version key for PyMCTranslate
47
+ """
48
+ raise NotImplementedError
49
+
50
+ @staticmethod
51
+ def is_valid(key: Tuple) -> bool:
52
+ """
53
+ Returns whether this translator is able to translate the chunk type with a given identifier key,
54
+ generated by the decoder.
55
+
56
+ :param key: The key who's decodability needs to be checked.
57
+ :return: True if this translator is able to translate the chunk type associated with the key, False otherwise.
58
+ """
59
+ raise NotImplementedError
60
+
61
+ @staticmethod
62
+ def _translate(
63
+ chunk: Chunk,
64
+ get_chunk_callback: Optional[GetChunkCallback],
65
+ translate_block: TranslateBlockCallback,
66
+ translate_entity: TranslateEntityCallback,
67
+ full_translate: bool,
68
+ ):
69
+ if full_translate:
70
+ todo = []
71
+ output_block_entities = []
72
+ output_entities = []
73
+ finished = BlockManager()
74
+ palette_mappings = {}
75
+
76
+ # translate each block without using the callback
77
+ for i, input_block in enumerate(chunk.block_palette):
78
+ input_block: Block
79
+ (
80
+ output_block,
81
+ output_block_entity,
82
+ output_entity,
83
+ extra,
84
+ ) = translate_block(input_block, None, (0, 0, 0))
85
+ if extra and get_chunk_callback:
86
+ todo.append(i)
87
+ elif output_block is not None:
88
+ palette_mappings[i] = finished.get_add_block(output_block)
89
+ if output_block_entity is not None:
90
+ for cy in chunk.blocks.sub_chunks:
91
+ for x, y, z in zip(
92
+ *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
93
+ ):
94
+ output_block_entities.append(
95
+ output_block_entity.new_at_location(
96
+ x + chunk.cx * 16,
97
+ y + cy * 16,
98
+ z + chunk.cz * 16,
99
+ )
100
+ )
101
+ else:
102
+ # TODO: this should only happen if the object is an entity, set the block to air
103
+ pass
104
+
105
+ if output_entity and entity_support:
106
+ for cy in chunk.blocks.sub_chunks:
107
+ for x, y, z in zip(
108
+ *numpy.where(chunk.blocks.get_sub_chunk(cy) == i)
109
+ ):
110
+ x += chunk.cx * 16
111
+ y += cy * 16
112
+ z += chunk.cz * 16
113
+ for entity in output_entity:
114
+ e = copy.deepcopy(entity)
115
+ e.location += (x, y, z)
116
+ output_entities.append(e)
117
+
118
+ # re-translate the blocks that require extra information
119
+ block_mappings = {}
120
+ for index in todo:
121
+ for cy in chunk.blocks.sub_chunks:
122
+ for x, y, z in zip(
123
+ *numpy.where(chunk.blocks.get_sub_chunk(cy) == index)
124
+ ):
125
+ y += cy * 16
126
+
127
+ def get_block_at(
128
+ pos: BlockCoordinates,
129
+ ) -> Tuple[Block, Optional[BlockEntity]]:
130
+ """Get a block at a location relative to the current block"""
131
+ nonlocal x, y, z, chunk, cy
132
+
133
+ # calculate position relative to chunk base
134
+ dx, dy, dz = pos
135
+ dx += x
136
+ dy += y
137
+ dz += z
138
+
139
+ abs_x = dx + chunk.cx * 16
140
+ abs_y = dy
141
+ abs_z = dz + chunk.cz * 16
142
+
143
+ # calculate relative chunk position
144
+ cx = dx // 16
145
+ cz = dz // 16
146
+ if cx == 0 and cz == 0:
147
+ # if it is the current chunk
148
+ block = chunk.block_palette[chunk.blocks[dx, dy, dz]]
149
+ return (
150
+ block,
151
+ chunk.block_entities.get((abs_x, abs_y, abs_z)),
152
+ )
153
+
154
+ # if it is in a different chunk
155
+ local_chunk = get_chunk_callback(cx, cz)
156
+ block = local_chunk.block_palette[
157
+ local_chunk.blocks[dx % 16, dy, dz % 16]
158
+ ]
159
+ return (
160
+ block,
161
+ local_chunk.block_entities.get((abs_x, abs_y, abs_z)),
162
+ )
163
+
164
+ input_block = chunk.block_palette[chunk.blocks[x, y, z]]
165
+ (
166
+ output_block,
167
+ output_block_entity,
168
+ output_entity,
169
+ _,
170
+ ) = translate_block(
171
+ input_block,
172
+ get_block_at,
173
+ (x + chunk.cx * 16, y, z + chunk.cz * 16),
174
+ )
175
+ if output_block is not None:
176
+ block_mappings[(x, y, z)] = finished.get_add_block(
177
+ output_block
178
+ )
179
+ if output_block_entity is not None:
180
+ output_block_entities.append(
181
+ output_block_entity.new_at_location(
182
+ x + chunk.cx * 16, y, z + chunk.cz * 16
183
+ )
184
+ )
185
+ else:
186
+ # TODO: set the block to air
187
+ pass
188
+
189
+ if output_entity and entity_support:
190
+ for entity in output_entity:
191
+ e = copy.deepcopy(entity)
192
+ e.location += (x, y, z)
193
+ output_entities.append(e)
194
+
195
+ if entity_support:
196
+ for entity in chunk.entities:
197
+ output_block, output_block_entity, output_entity = translate_entity(
198
+ entity
199
+ )
200
+ if output_block is not None:
201
+ block_location = (
202
+ int(math.floor(entity.x)),
203
+ int(math.floor(entity.y)),
204
+ int(math.floor(entity.z)),
205
+ )
206
+ block_mappings[block_location] = output_block
207
+ if output_block_entity:
208
+ output_block_entities.append(
209
+ output_block_entity.new_at_location(*block_location)
210
+ )
211
+ if output_entity:
212
+ for e in output_entity:
213
+ e.location = entity.location
214
+ output_entities.append(e)
215
+
216
+ for cy in chunk.blocks.sub_chunks:
217
+ old_blocks = chunk.blocks.get_sub_chunk(cy)
218
+ new_blocks = numpy.zeros(old_blocks.shape, dtype=old_blocks.dtype)
219
+ for old, new in palette_mappings.items():
220
+ new_blocks[old_blocks == old] = new
221
+ chunk.blocks.add_sub_chunk(cy, new_blocks)
222
+ for (x, y, z), new in block_mappings.items():
223
+ chunk.blocks[x, y, z] = new
224
+ chunk.block_entities = output_block_entities
225
+ chunk.entities = output_entities
226
+ chunk._block_palette = finished
227
+
228
+ def to_universal(
229
+ self,
230
+ chunk_version: VersionNumberAny,
231
+ translation_manager: "TranslationManager",
232
+ chunk: Chunk,
233
+ get_chunk_callback: Optional[GetChunkCallback],
234
+ full_translate: bool,
235
+ ) -> Chunk:
236
+ """
237
+ Translate an interface-specific chunk into the universal format.
238
+
239
+ :param chunk_version: The version number (int or tuple) of the input chunk
240
+ :param translation_manager: TranslationManager used for the translation
241
+ :param chunk: The chunk to translate.
242
+ :param get_chunk_callback: function callback to get a chunk's data
243
+ :param full_translate: if true do a full translate. If false just unpack the block_palette (used in callback)
244
+ :return: Chunk object in the universal format.
245
+ """
246
+ version = translation_manager.get_version(*self._translator_key(chunk_version))
247
+ self._biomes_to_universal(version, chunk)
248
+ self._blocks_entities_to_universal(
249
+ chunk_version,
250
+ translation_manager,
251
+ chunk,
252
+ get_chunk_callback,
253
+ full_translate,
254
+ )
255
+ return chunk
256
+
257
+ def _blocks_entities_to_universal(
258
+ self,
259
+ chunk_version: VersionNumberAny,
260
+ translation_manager: "TranslationManager",
261
+ chunk: Chunk,
262
+ get_chunk_callback: Optional[GetChunkCallback],
263
+ full_translate: bool,
264
+ ):
265
+ version = translation_manager.get_version(*self._translator_key(chunk_version))
266
+
267
+ def translate_block(
268
+ input_object: Block,
269
+ get_block_callback: Optional[GetBlockCallback],
270
+ block_location: BlockCoordinates,
271
+ ) -> TranslateBlockCallbackReturn:
272
+ final_block = None
273
+ final_block_entity = None
274
+ final_entities = []
275
+ final_extra = False
276
+
277
+ for depth, block in enumerate(input_object.block_tuple):
278
+ (
279
+ output_object,
280
+ output_block_entity,
281
+ extra,
282
+ ) = version.block.to_universal(
283
+ block,
284
+ get_block_callback=get_block_callback,
285
+ block_location=block_location,
286
+ )
287
+
288
+ if isinstance(output_object, Block):
289
+ if not output_object.namespace.startswith("universal"):
290
+ log.debug(
291
+ f"Error translating {input_object.full_blockstate} to universal. Got {output_object.full_blockstate}"
292
+ )
293
+ if final_block is None:
294
+ final_block = output_object
295
+ else:
296
+ final_block += output_object
297
+ if depth == 0:
298
+ final_block_entity = output_block_entity
299
+
300
+ elif isinstance(output_object, Entity):
301
+ final_entities.append(output_object)
302
+ # TODO: offset entity coords
303
+
304
+ final_extra |= extra
305
+
306
+ return final_block, final_block_entity, final_entities, final_extra
307
+
308
+ def translate_entity(input_object: Entity) -> TranslateEntityCallbackReturn:
309
+ final_block = None
310
+ final_block_entity = None
311
+ final_entities = []
312
+ # TODO
313
+ return final_block, final_block_entity, final_entities
314
+
315
+ self._translate(
316
+ chunk,
317
+ get_chunk_callback,
318
+ translate_block,
319
+ translate_entity,
320
+ full_translate,
321
+ )
322
+
323
+ @staticmethod
324
+ def _biomes_to_universal(translator_version: "Version", chunk: Chunk):
325
+ chunk._biome_palette = BiomeManager(
326
+ [
327
+ translator_version.biome.to_universal(biome)
328
+ for biome in chunk.biome_palette
329
+ ]
330
+ )
331
+
332
+ def from_universal(
333
+ self,
334
+ max_world_version_number: VersionNumberAny,
335
+ translation_manager: "TranslationManager",
336
+ chunk: Chunk,
337
+ get_chunk_callback: Optional[GetChunkCallback],
338
+ full_translate: bool,
339
+ ) -> Chunk:
340
+ """
341
+ Translate a universal chunk into the interface-specific format.
342
+
343
+ :param max_world_version_number: The version number (int or tuple) of the max world version
344
+ :param translation_manager: TranslationManager used for the translation
345
+ :param chunk: The chunk to translate.
346
+ :param get_chunk_callback: function callback to get a chunk's data
347
+ :param full_translate: if true do a full translate. If false just pack the block_palette (used in callback)
348
+ :return: Chunk object in the interface-specific format and block_palette.
349
+ """
350
+ version = translation_manager.get_version(
351
+ *self._translator_key(max_world_version_number)
352
+ )
353
+ self._blocks_entities_from_universal(
354
+ max_world_version_number,
355
+ translation_manager,
356
+ chunk,
357
+ get_chunk_callback,
358
+ full_translate,
359
+ )
360
+ self._biomes_from_universal(version, chunk)
361
+ return chunk
362
+
363
+ def _blocks_entities_from_universal(
364
+ self,
365
+ max_world_version_number: VersionNumberAny,
366
+ translation_manager: "TranslationManager",
367
+ chunk: Chunk,
368
+ get_chunk_callback: Optional[GetChunkCallback],
369
+ full_translate: bool,
370
+ ):
371
+ version = translation_manager.get_version(
372
+ *self._translator_key(max_world_version_number)
373
+ )
374
+
375
+ # TODO: perhaps find a way so this code isn't duplicated in three places
376
+ def translate_block(
377
+ input_object: Block,
378
+ get_block_callback: Optional[GetBlockCallback],
379
+ block_location: BlockCoordinates,
380
+ ) -> TranslateBlockCallbackReturn:
381
+ final_block = None
382
+ final_block_entity = None
383
+ final_entities = []
384
+ final_extra = False
385
+
386
+ for depth, block in enumerate(input_object.block_tuple):
387
+ (
388
+ output_object,
389
+ output_block_entity,
390
+ extra,
391
+ ) = version.block.from_universal(
392
+ block,
393
+ get_block_callback=get_block_callback,
394
+ block_location=block_location,
395
+ )
396
+
397
+ if isinstance(output_object, Block):
398
+ if __debug__ and output_object.namespace.startswith("universal"):
399
+ log.debug(
400
+ f"Error translating {input_object.blockstate} from universal. Got {output_object.blockstate}"
401
+ )
402
+ if final_block is None:
403
+ final_block = output_object
404
+ else:
405
+ final_block += output_object
406
+ if depth == 0:
407
+ final_block_entity = output_block_entity
408
+
409
+ elif isinstance(output_object, Entity):
410
+ final_entities.append(output_object)
411
+ # TODO: offset entity coords
412
+
413
+ final_extra |= extra
414
+
415
+ return final_block, final_block_entity, final_entities, final_extra
416
+
417
+ def translate_entity(input_object: Entity) -> TranslateEntityCallbackReturn:
418
+ final_block = None
419
+ final_block_entity = None
420
+ final_entities = []
421
+ # TODO
422
+ return final_block, final_block_entity, final_entities
423
+
424
+ self._translate(
425
+ chunk,
426
+ get_chunk_callback,
427
+ translate_block,
428
+ translate_entity,
429
+ full_translate,
430
+ )
431
+
432
+ @staticmethod
433
+ def _biomes_from_universal(translator_version: "Version", chunk: Chunk):
434
+ chunk._biome_palette = BiomeManager(
435
+ [
436
+ translator_version.biome.from_universal(biome)
437
+ for biome in chunk.biome_palette
438
+ ]
439
+ )
440
+
441
+ def unpack(
442
+ self,
443
+ chunk_version: VersionNumberAny,
444
+ translation_manager: "TranslationManager",
445
+ chunk: Chunk,
446
+ palette: AnyNDArray,
447
+ ) -> Chunk:
448
+ """
449
+ Unpack the version-specific block_palette into the stringified version where needed.
450
+
451
+ :return: The block_palette converted to block objects.
452
+ """
453
+ version_identifier = self._translator_key(chunk_version)
454
+ self._unpack_blocks(translation_manager, version_identifier, chunk, palette)
455
+ self._unpack_biomes(translation_manager, version_identifier, chunk)
456
+ return chunk
457
+
458
+ @staticmethod
459
+ def _unpack_blocks(
460
+ translation_manager: "TranslationManager",
461
+ version_identifier: VersionIdentifierType,
462
+ chunk: Chunk,
463
+ block_palette: AnyNDArray,
464
+ ):
465
+ """
466
+ Unpack the version-specific block_palette into the stringified version where needed.
467
+ :return: The block_palette converted to block objects.
468
+ """
469
+ chunk._block_palette = BlockManager(block_palette)
470
+
471
+ @staticmethod
472
+ def _unpack_biomes(
473
+ translation_manager: "TranslationManager",
474
+ version_identifier: VersionIdentifierType,
475
+ chunk: Chunk,
476
+ ):
477
+ """
478
+ Unpack the version-specific biome_palette into the stringified version where needed.
479
+ :return: The biome_palette converted to biome objects.
480
+ """
481
+ version = translation_manager.get_version(*version_identifier)
482
+
483
+ if chunk.biomes.dimension == BiomesShape.Shape2D:
484
+ biome_int_palette, biome_array = numpy.unique(
485
+ chunk.biomes, return_inverse=True
486
+ )
487
+ chunk.biomes = biome_array.reshape(chunk.biomes.shape)
488
+ chunk._biome_palette = BiomeManager(
489
+ [version.biome.unpack(biome) for biome in biome_int_palette]
490
+ )
491
+ elif chunk.biomes.dimension == BiomesShape.Shape3D:
492
+ biomes = {}
493
+ palette = []
494
+ palette_length = 0
495
+ for sy in chunk.biomes.sections:
496
+ biome_int_palette, biome_array = numpy.unique(
497
+ chunk.biomes.get_section(sy), return_inverse=True
498
+ )
499
+ biomes[sy] = (
500
+ biome_array.reshape(chunk.biomes.section_shape) + palette_length
501
+ )
502
+ palette_length += len(biome_int_palette)
503
+ palette.append(biome_int_palette)
504
+
505
+ if palette:
506
+ chunk_palette, lut = numpy.unique(
507
+ numpy.concatenate(palette), return_inverse=True
508
+ )
509
+ lut = lut.astype(numpy.uint32)
510
+ for sy in biomes:
511
+ biomes[sy] = lut[biomes[sy]]
512
+
513
+ chunk.biomes = biomes
514
+ chunk._biome_palette = BiomeManager(
515
+ numpy.vectorize(version.biome.unpack)(chunk_palette)
516
+ )
517
+
518
+ def pack(
519
+ self,
520
+ max_world_version_number: VersionNumberAny,
521
+ translation_manager: "TranslationManager",
522
+ chunk: Chunk,
523
+ ) -> Tuple[Chunk, AnyNDArray]:
524
+ """
525
+ Translate the list of block objects into a version-specific block_palette.
526
+ :return: The block_palette converted into version-specific blocks (ie id, data tuples for 1.12)
527
+ """
528
+ version_identifier = self._translator_key(max_world_version_number)
529
+ version = translation_manager.get_version(*version_identifier)
530
+ self._pack_biomes(translation_manager, version_identifier, chunk)
531
+ return (
532
+ chunk,
533
+ self._pack_block_palette(version, numpy.array(chunk.block_palette.blocks)),
534
+ )
535
+
536
+ def _pack_block_palette(
537
+ self, version: "Version", palette: BlockNDArray
538
+ ) -> AnyNDArray:
539
+ """
540
+ Pack the list of block objects into a version-specific block_palette.
541
+ :return: The block_palette converted into version-specific blocks (ie id, data tuples for 1.12)
542
+ """
543
+ return palette
544
+
545
+ @staticmethod
546
+ def _pack_biomes(
547
+ translation_manager: "TranslationManager",
548
+ version_identifier: VersionIdentifierType,
549
+ chunk: Chunk,
550
+ ):
551
+ """
552
+ Unpack the version-specific biome_palette into the stringified version where needed.
553
+ :return: The biome_palette converted to biome objects.
554
+ """
555
+ version = translation_manager.get_version(*version_identifier)
556
+
557
+ biome_palette = numpy.array(
558
+ [version.biome.pack(biome) for biome in chunk.biome_palette], numpy.uint32
559
+ )
560
+ if chunk.biomes.dimension == BiomesShape.Shape2D:
561
+ chunk.biomes = biome_palette[chunk.biomes]
562
+ elif chunk.biomes.dimension == BiomesShape.Shape3D:
563
+ chunk.biomes = {
564
+ sy: biome_palette[chunk.biomes.get_section(sy)]
565
+ for sy in chunk.biomes.sections
566
+ }
567
+ chunk._biome_palette = BiomeManager()