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,336 +1,336 @@
1
- from __future__ import annotations
2
-
3
- import logging
4
- from typing import Dict, Set, Tuple, Iterable, Optional, TYPE_CHECKING
5
-
6
- import numpy
7
- from amulet_nbt import (
8
- CompoundTag,
9
- ListTag,
10
- StringTag,
11
- LongArrayTag,
12
- LongTag,
13
- ShortTag,
14
- )
15
-
16
- import amulet
17
- from amulet.api.data_types import AnyNDArray, BlockCoordinates
18
- from amulet.api.block import Block
19
- from amulet.api.chunk import StatusFormats
20
- from .base_anvil_interface import ChunkDataType, ChunkPathType
21
- from .anvil_0 import Anvil0Interface as ParentInterface
22
- from amulet.utils.world_utils import (
23
- decode_long_array,
24
- encode_long_array,
25
- )
26
-
27
- if TYPE_CHECKING:
28
- from amulet.api.chunk import Chunk
29
-
30
- log = logging.getLogger(__name__)
31
-
32
-
33
- class Anvil1444Interface(ParentInterface):
34
- """
35
- Moved TerrainPopulated and LightPopulated to Status
36
- Made blocks paletted
37
- Added more tick tags
38
- Added structures tag
39
- """
40
-
41
- TerrainPopulated = None
42
- LightPopulated = None
43
- Status: ChunkPathType = (
44
- "region",
45
- [("Level", CompoundTag), ("Status", StringTag)],
46
- StringTag("full"),
47
- )
48
-
49
- ToBeTicked: ChunkPathType = (
50
- "region",
51
- [("Level", CompoundTag), ("ToBeTicked", ListTag)],
52
- ListTag,
53
- )
54
-
55
- LiquidTicks: ChunkPathType = (
56
- "region",
57
- [("Level", CompoundTag), ("LiquidTicks", ListTag)],
58
- ListTag,
59
- )
60
- LiquidsToBeTicked: ChunkPathType = (
61
- "region",
62
- [("Level", CompoundTag), ("LiquidsToBeTicked", ListTag)],
63
- ListTag,
64
- )
65
-
66
- PostProcessing: ChunkPathType = (
67
- "region",
68
- [("Level", CompoundTag), ("PostProcessing", ListTag)],
69
- ListTag,
70
- )
71
-
72
- Structures: ChunkPathType = (
73
- "region",
74
- [("Level", CompoundTag), ("Structures", CompoundTag)],
75
- CompoundTag,
76
- )
77
-
78
- LongArrayDense = True
79
-
80
- def __init__(self):
81
- super().__init__()
82
- self._set_feature("status", StatusFormats.Java_13)
83
-
84
- self._register_decoder(self._decode_fluid_ticks)
85
- self._register_decoder(self._decode_post_processing)
86
- self._register_decoder(self._decode_structures)
87
-
88
- self._register_encoder(self._encode_fluid_ticks)
89
- self._register_encoder(self._encode_post_processing)
90
- self._register_encoder(self._encode_structures)
91
-
92
- @staticmethod
93
- def minor_is_valid(key: int):
94
- return 1444 <= key < 1466
95
-
96
- def _decode_status(
97
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
98
- ):
99
- chunk.status = self.get_layer_obj(data, self.Status, pop_last=True).py_str
100
-
101
- def _decode_block_section(
102
- self, section: CompoundTag
103
- ) -> Optional[Tuple[numpy.ndarray, list]]:
104
- if "Palette" not in section: # 1.14 makes block_palette/blocks optional.
105
- return None
106
- section_palette = self._decode_block_palette(section.pop("Palette"))
107
- decoded = decode_long_array(
108
- section.get_long_array("BlockStates").np_array,
109
- 4096,
110
- max(4, (len(section_palette) - 1).bit_length()),
111
- dense=self.LongArrayDense,
112
- ).astype(numpy.uint32)
113
- arr = numpy.transpose(decoded.reshape((16, 16, 16)), (2, 0, 1))
114
- return arr, section_palette
115
-
116
- def _decode_blocks(
117
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
118
- ):
119
- blocks: Dict[int, numpy.ndarray] = {}
120
- palette = [Block(namespace="minecraft", base_name="air")]
121
-
122
- for cy, section in self._iter_sections(data):
123
- data = self._decode_block_section(section)
124
- if data is not None:
125
- arr, section_palette = data
126
- blocks[cy] = arr + len(palette)
127
- palette += section_palette
128
-
129
- np_palette, inverse = numpy.unique(palette, return_inverse=True)
130
- np_palette: numpy.ndarray
131
- inverse: numpy.ndarray
132
- inverse = inverse.astype(numpy.uint32)
133
- for cy in blocks:
134
- blocks[cy] = inverse[blocks[cy]]
135
- chunk.blocks = blocks
136
- chunk.misc["block_palette"] = np_palette
137
-
138
- @staticmethod
139
- def _decode_block_palette(palette: ListTag) -> list:
140
- blockstates = []
141
- for entry in palette:
142
- namespace, base_name = entry.get_string("Name").py_str.split(":", 1)
143
- properties = entry.get_compound("Properties", CompoundTag({})).py_dict
144
- block = Block(
145
- namespace=namespace, base_name=base_name, properties=properties
146
- )
147
- blockstates.append(block)
148
- return blockstates
149
-
150
- @staticmethod
151
- def _decode_to_be_ticked(ticks: ListTag, floor_cy: int) -> Set[BlockCoordinates]:
152
- section_ticks: ListTag
153
- pos: ShortTag
154
- ticks_out = set()
155
- for cy, section_ticks in enumerate(ticks):
156
- block_cy = (floor_cy + cy) << 4
157
- for pos in section_ticks:
158
- pos_int = pos.py_int
159
- # TODO: check if these are correct. The order may be wrong.
160
- x = pos_int & 0xF
161
- y = (pos_int >> 4) & 0xF
162
- z = (pos_int >> 8) & 0xF
163
- ticks_out.add((x, block_cy + y, z))
164
- return ticks_out
165
-
166
- def _decode_block_ticks(
167
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
168
- ):
169
- super()._decode_block_ticks(chunk, data, floor_cy, height_cy)
170
- chunk.misc["to_be_ticked"] = self._decode_to_be_ticked(
171
- self.get_layer_obj(data, self.ToBeTicked, pop_last=True), floor_cy
172
- )
173
-
174
- def _decode_fluid_ticks(self, chunk: Chunk, data: ChunkDataType, floor_cy: int, __):
175
- chunk.misc.setdefault("fluid_ticks", {}).update(
176
- self._decode_ticks(
177
- self.get_layer_obj(data, self.LiquidTicks, pop_last=True)
178
- )
179
- )
180
- chunk.misc["liquids_to_be_ticked"] = self._decode_to_be_ticked(
181
- self.get_layer_obj(data, self.LiquidsToBeTicked, pop_last=True), floor_cy
182
- )
183
-
184
- def _decode_post_processing(
185
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
186
- ):
187
- chunk.misc["post_processing"] = self._decode_to_be_ticked(
188
- self.get_layer_obj(data, self.PostProcessing, pop_last=True), floor_cy
189
- )
190
-
191
- def _decode_structures(
192
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
193
- ):
194
- chunk.misc["structures"] = self.get_layer_obj(
195
- data, self.Structures, pop_last=True
196
- )
197
-
198
- def _encode_status(
199
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
200
- ):
201
- # Order the float value based on the order they would be run. Newer replacements for the same come just after
202
- # to save back find the next lowest valid value.
203
- status = chunk.status.as_type(self._features["status"])
204
- self.set_layer_obj(data, self.Status, StringTag(status))
205
-
206
- def _encode_inhabited_time(
207
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
208
- ):
209
- self.set_layer_obj(
210
- data, self.InhabitedTime, LongTag(chunk.misc.get("inhabited_time", 0))
211
- )
212
-
213
- def _encode_block_section(
214
- self,
215
- chunk: Chunk,
216
- sections: Dict[int, CompoundTag],
217
- palette: AnyNDArray,
218
- cy: int,
219
- ) -> bool:
220
- block_sub_array = numpy.transpose(
221
- chunk.blocks.get_sub_chunk(cy), (1, 2, 0)
222
- ).ravel()
223
-
224
- sub_palette_, block_sub_array = numpy.unique(
225
- block_sub_array, return_inverse=True
226
- )
227
- sub_palette = self._encode_block_palette(palette[sub_palette_])
228
- if (
229
- len(sub_palette) == 1
230
- and sub_palette[0].get_string("Name").py_str == "minecraft:air"
231
- ):
232
- return False
233
-
234
- section = sections.setdefault(cy, CompoundTag())
235
- section["BlockStates"] = LongArrayTag(
236
- encode_long_array(
237
- block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4
238
- )
239
- )
240
- section["Palette"] = sub_palette
241
-
242
- @staticmethod
243
- def _encode_block_palette(blockstates: Iterable[Block]) -> ListTag:
244
- palette = ListTag()
245
- for block in blockstates:
246
- entry = CompoundTag()
247
- entry["Name"] = StringTag(f"{block.namespace}:{block.base_name}")
248
- if block.properties:
249
- string_properties = {
250
- k: v
251
- for k, v in block.properties.items()
252
- if isinstance(v, StringTag)
253
- }
254
- if string_properties:
255
- entry["Properties"] = CompoundTag(string_properties)
256
- palette.append(entry)
257
- return palette
258
-
259
- @staticmethod
260
- def _encode_to_be_ticked(
261
- ticks: Set[BlockCoordinates], floor_cy: int, height_cy: int
262
- ) -> ListTag:
263
- ceil_cy = floor_cy + height_cy
264
- ticks_out = ListTag([ListTag([], 2) for _ in range(floor_cy, ceil_cy)])
265
- if isinstance(ticks, set):
266
- for k in ticks:
267
- try:
268
- (x, y, z) = k
269
- cy = y >> 4
270
- if floor_cy <= cy < ceil_cy:
271
- x = x & 15
272
- y = y & 15
273
- z = z & 15
274
- ticks_out[cy].append(ShortTag((z << 8) + (y << 4) + x))
275
- except Exception:
276
- log.error(f"Could not serialise tick data {k}")
277
- return ticks_out
278
-
279
- def _encode_block_ticks(
280
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
281
- ):
282
- super()._encode_block_ticks(chunk, data, floor_cy, height_cy)
283
- self.set_layer_obj(
284
- data,
285
- self.ToBeTicked,
286
- self._encode_to_be_ticked(
287
- chunk.misc.get("to_be_ticked"), floor_cy, height_cy
288
- ),
289
- )
290
-
291
- def _encode_fluid_ticks(
292
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
293
- ):
294
- self.set_layer_obj(
295
- data,
296
- self.LiquidTicks,
297
- self._encode_ticks(chunk.misc.get("fluid_ticks", {})),
298
- )
299
- self.set_layer_obj(
300
- data,
301
- self.LiquidsToBeTicked,
302
- self._encode_to_be_ticked(
303
- chunk.misc.get("liquids_to_be_ticked"), floor_cy, height_cy
304
- ),
305
- )
306
-
307
- def _encode_post_processing(
308
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
309
- ):
310
- self.set_layer_obj(
311
- data,
312
- self.PostProcessing,
313
- self._encode_to_be_ticked(
314
- chunk.misc.get("post_processing"), floor_cy, height_cy
315
- ),
316
- )
317
-
318
- def _encode_structures(
319
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
320
- ):
321
- self.set_layer_obj(
322
- data,
323
- self.Structures,
324
- chunk.misc.get(
325
- "structures",
326
- CompoundTag(
327
- {
328
- "References": CompoundTag(),
329
- "Starts": CompoundTag(),
330
- }
331
- ),
332
- ),
333
- )
334
-
335
-
336
- export = Anvil1444Interface
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import Dict, Set, Tuple, Iterable, Optional, TYPE_CHECKING
5
+
6
+ import numpy
7
+ from amulet_nbt import (
8
+ CompoundTag,
9
+ ListTag,
10
+ StringTag,
11
+ LongArrayTag,
12
+ LongTag,
13
+ ShortTag,
14
+ )
15
+
16
+ import amulet
17
+ from amulet.api.data_types import AnyNDArray, BlockCoordinates
18
+ from amulet.api.block import Block
19
+ from amulet.api.chunk import StatusFormats
20
+ from .base_anvil_interface import ChunkDataType, ChunkPathType
21
+ from .anvil_0 import Anvil0Interface as ParentInterface
22
+ from amulet.utils.world_utils import (
23
+ decode_long_array,
24
+ encode_long_array,
25
+ )
26
+
27
+ if TYPE_CHECKING:
28
+ from amulet.api.chunk import Chunk
29
+
30
+ log = logging.getLogger(__name__)
31
+
32
+
33
+ class Anvil1444Interface(ParentInterface):
34
+ """
35
+ Moved TerrainPopulated and LightPopulated to Status
36
+ Made blocks paletted
37
+ Added more tick tags
38
+ Added structures tag
39
+ """
40
+
41
+ TerrainPopulated = None
42
+ LightPopulated = None
43
+ Status: ChunkPathType = (
44
+ "region",
45
+ [("Level", CompoundTag), ("Status", StringTag)],
46
+ StringTag("full"),
47
+ )
48
+
49
+ ToBeTicked: ChunkPathType = (
50
+ "region",
51
+ [("Level", CompoundTag), ("ToBeTicked", ListTag)],
52
+ ListTag,
53
+ )
54
+
55
+ LiquidTicks: ChunkPathType = (
56
+ "region",
57
+ [("Level", CompoundTag), ("LiquidTicks", ListTag)],
58
+ ListTag,
59
+ )
60
+ LiquidsToBeTicked: ChunkPathType = (
61
+ "region",
62
+ [("Level", CompoundTag), ("LiquidsToBeTicked", ListTag)],
63
+ ListTag,
64
+ )
65
+
66
+ PostProcessing: ChunkPathType = (
67
+ "region",
68
+ [("Level", CompoundTag), ("PostProcessing", ListTag)],
69
+ ListTag,
70
+ )
71
+
72
+ Structures: ChunkPathType = (
73
+ "region",
74
+ [("Level", CompoundTag), ("Structures", CompoundTag)],
75
+ CompoundTag,
76
+ )
77
+
78
+ LongArrayDense = True
79
+
80
+ def __init__(self):
81
+ super().__init__()
82
+ self._set_feature("status", StatusFormats.Java_13)
83
+
84
+ self._register_decoder(self._decode_fluid_ticks)
85
+ self._register_decoder(self._decode_post_processing)
86
+ self._register_decoder(self._decode_structures)
87
+
88
+ self._register_encoder(self._encode_fluid_ticks)
89
+ self._register_encoder(self._encode_post_processing)
90
+ self._register_encoder(self._encode_structures)
91
+
92
+ @staticmethod
93
+ def minor_is_valid(key: int):
94
+ return 1444 <= key < 1466
95
+
96
+ def _decode_status(
97
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
98
+ ):
99
+ chunk.status = self.get_layer_obj(data, self.Status, pop_last=True).py_str
100
+
101
+ def _decode_block_section(
102
+ self, section: CompoundTag
103
+ ) -> Optional[Tuple[numpy.ndarray, list]]:
104
+ if "Palette" not in section: # 1.14 makes block_palette/blocks optional.
105
+ return None
106
+ section_palette = self._decode_block_palette(section.pop("Palette"))
107
+ decoded = decode_long_array(
108
+ section.get_long_array("BlockStates").np_array,
109
+ 4096,
110
+ max(4, (len(section_palette) - 1).bit_length()),
111
+ dense=self.LongArrayDense,
112
+ ).astype(numpy.uint32)
113
+ arr = numpy.transpose(decoded.reshape((16, 16, 16)), (2, 0, 1))
114
+ return arr, section_palette
115
+
116
+ def _decode_blocks(
117
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
118
+ ):
119
+ blocks: Dict[int, numpy.ndarray] = {}
120
+ palette = [Block(namespace="minecraft", base_name="air")]
121
+
122
+ for cy, section in self._iter_sections(data):
123
+ data = self._decode_block_section(section)
124
+ if data is not None:
125
+ arr, section_palette = data
126
+ blocks[cy] = arr + len(palette)
127
+ palette += section_palette
128
+
129
+ np_palette, inverse = numpy.unique(palette, return_inverse=True)
130
+ np_palette: numpy.ndarray
131
+ inverse: numpy.ndarray
132
+ inverse = inverse.astype(numpy.uint32)
133
+ for cy in blocks:
134
+ blocks[cy] = inverse[blocks[cy]]
135
+ chunk.blocks = blocks
136
+ chunk.misc["block_palette"] = np_palette
137
+
138
+ @staticmethod
139
+ def _decode_block_palette(palette: ListTag) -> list:
140
+ blockstates = []
141
+ for entry in palette:
142
+ namespace, base_name = entry.get_string("Name").py_str.split(":", 1)
143
+ properties = entry.get_compound("Properties", CompoundTag({})).py_dict
144
+ block = Block(
145
+ namespace=namespace, base_name=base_name, properties=properties
146
+ )
147
+ blockstates.append(block)
148
+ return blockstates
149
+
150
+ @staticmethod
151
+ def _decode_to_be_ticked(ticks: ListTag, floor_cy: int) -> Set[BlockCoordinates]:
152
+ section_ticks: ListTag
153
+ pos: ShortTag
154
+ ticks_out = set()
155
+ for cy, section_ticks in enumerate(ticks):
156
+ block_cy = (floor_cy + cy) << 4
157
+ for pos in section_ticks:
158
+ pos_int = pos.py_int
159
+ # TODO: check if these are correct. The order may be wrong.
160
+ x = pos_int & 0xF
161
+ y = (pos_int >> 4) & 0xF
162
+ z = (pos_int >> 8) & 0xF
163
+ ticks_out.add((x, block_cy + y, z))
164
+ return ticks_out
165
+
166
+ def _decode_block_ticks(
167
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
168
+ ):
169
+ super()._decode_block_ticks(chunk, data, floor_cy, height_cy)
170
+ chunk.misc["to_be_ticked"] = self._decode_to_be_ticked(
171
+ self.get_layer_obj(data, self.ToBeTicked, pop_last=True), floor_cy
172
+ )
173
+
174
+ def _decode_fluid_ticks(self, chunk: Chunk, data: ChunkDataType, floor_cy: int, __):
175
+ chunk.misc.setdefault("fluid_ticks", {}).update(
176
+ self._decode_ticks(
177
+ self.get_layer_obj(data, self.LiquidTicks, pop_last=True)
178
+ )
179
+ )
180
+ chunk.misc["liquids_to_be_ticked"] = self._decode_to_be_ticked(
181
+ self.get_layer_obj(data, self.LiquidsToBeTicked, pop_last=True), floor_cy
182
+ )
183
+
184
+ def _decode_post_processing(
185
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
186
+ ):
187
+ chunk.misc["post_processing"] = self._decode_to_be_ticked(
188
+ self.get_layer_obj(data, self.PostProcessing, pop_last=True), floor_cy
189
+ )
190
+
191
+ def _decode_structures(
192
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
193
+ ):
194
+ chunk.misc["structures"] = self.get_layer_obj(
195
+ data, self.Structures, pop_last=True
196
+ )
197
+
198
+ def _encode_status(
199
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
200
+ ):
201
+ # Order the float value based on the order they would be run. Newer replacements for the same come just after
202
+ # to save back find the next lowest valid value.
203
+ status = chunk.status.as_type(self._features["status"])
204
+ self.set_layer_obj(data, self.Status, StringTag(status))
205
+
206
+ def _encode_inhabited_time(
207
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
208
+ ):
209
+ self.set_layer_obj(
210
+ data, self.InhabitedTime, LongTag(chunk.misc.get("inhabited_time", 0))
211
+ )
212
+
213
+ def _encode_block_section(
214
+ self,
215
+ chunk: Chunk,
216
+ sections: Dict[int, CompoundTag],
217
+ palette: AnyNDArray,
218
+ cy: int,
219
+ ) -> bool:
220
+ block_sub_array = numpy.transpose(
221
+ chunk.blocks.get_sub_chunk(cy), (1, 2, 0)
222
+ ).ravel()
223
+
224
+ sub_palette_, block_sub_array = numpy.unique(
225
+ block_sub_array, return_inverse=True
226
+ )
227
+ sub_palette = self._encode_block_palette(palette[sub_palette_])
228
+ if (
229
+ len(sub_palette) == 1
230
+ and sub_palette[0].get_string("Name").py_str == "minecraft:air"
231
+ ):
232
+ return False
233
+
234
+ section = sections.setdefault(cy, CompoundTag())
235
+ section["BlockStates"] = LongArrayTag(
236
+ encode_long_array(
237
+ block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4
238
+ )
239
+ )
240
+ section["Palette"] = sub_palette
241
+
242
+ @staticmethod
243
+ def _encode_block_palette(blockstates: Iterable[Block]) -> ListTag:
244
+ palette = ListTag()
245
+ for block in blockstates:
246
+ entry = CompoundTag()
247
+ entry["Name"] = StringTag(f"{block.namespace}:{block.base_name}")
248
+ if block.properties:
249
+ string_properties = {
250
+ k: v
251
+ for k, v in block.properties.items()
252
+ if isinstance(v, StringTag)
253
+ }
254
+ if string_properties:
255
+ entry["Properties"] = CompoundTag(string_properties)
256
+ palette.append(entry)
257
+ return palette
258
+
259
+ @staticmethod
260
+ def _encode_to_be_ticked(
261
+ ticks: Set[BlockCoordinates], floor_cy: int, height_cy: int
262
+ ) -> ListTag:
263
+ ceil_cy = floor_cy + height_cy
264
+ ticks_out = ListTag([ListTag([], 2) for _ in range(floor_cy, ceil_cy)])
265
+ if isinstance(ticks, set):
266
+ for k in ticks:
267
+ try:
268
+ (x, y, z) = k
269
+ cy = y >> 4
270
+ if floor_cy <= cy < ceil_cy:
271
+ x = x & 15
272
+ y = y & 15
273
+ z = z & 15
274
+ ticks_out[cy].append(ShortTag((z << 8) + (y << 4) + x))
275
+ except Exception:
276
+ log.error(f"Could not serialise tick data {k}")
277
+ return ticks_out
278
+
279
+ def _encode_block_ticks(
280
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
281
+ ):
282
+ super()._encode_block_ticks(chunk, data, floor_cy, height_cy)
283
+ self.set_layer_obj(
284
+ data,
285
+ self.ToBeTicked,
286
+ self._encode_to_be_ticked(
287
+ chunk.misc.get("to_be_ticked"), floor_cy, height_cy
288
+ ),
289
+ )
290
+
291
+ def _encode_fluid_ticks(
292
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
293
+ ):
294
+ self.set_layer_obj(
295
+ data,
296
+ self.LiquidTicks,
297
+ self._encode_ticks(chunk.misc.get("fluid_ticks", {})),
298
+ )
299
+ self.set_layer_obj(
300
+ data,
301
+ self.LiquidsToBeTicked,
302
+ self._encode_to_be_ticked(
303
+ chunk.misc.get("liquids_to_be_ticked"), floor_cy, height_cy
304
+ ),
305
+ )
306
+
307
+ def _encode_post_processing(
308
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
309
+ ):
310
+ self.set_layer_obj(
311
+ data,
312
+ self.PostProcessing,
313
+ self._encode_to_be_ticked(
314
+ chunk.misc.get("post_processing"), floor_cy, height_cy
315
+ ),
316
+ )
317
+
318
+ def _encode_structures(
319
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
320
+ ):
321
+ self.set_layer_obj(
322
+ data,
323
+ self.Structures,
324
+ chunk.misc.get(
325
+ "structures",
326
+ CompoundTag(
327
+ {
328
+ "References": CompoundTag(),
329
+ "Starts": CompoundTag(),
330
+ }
331
+ ),
332
+ ),
333
+ )
334
+
335
+
336
+ export = Anvil1444Interface