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,267 +1,267 @@
1
- from __future__ import annotations
2
- from typing import Tuple, Dict, Optional
3
- import numpy
4
-
5
- from amulet_nbt import (
6
- CompoundTag,
7
- ListTag,
8
- ByteTag,
9
- IntTag,
10
- LongTag,
11
- LongArrayTag,
12
- StringTag,
13
- )
14
-
15
- from amulet.api.chunk import Chunk
16
- from amulet.api.registry import BiomeManager
17
- from amulet.api.data_types import AnyNDArray, BiomeType
18
- from amulet.utils.world_utils import (
19
- decode_long_array,
20
- encode_long_array,
21
- )
22
-
23
- from .base_anvil_interface import (
24
- ChunkDataType,
25
- ChunkPathType,
26
- )
27
- from .anvil_2709 import (
28
- Anvil2709Interface as ParentInterface,
29
- )
30
-
31
-
32
- class Anvil2844Interface(ParentInterface):
33
- """
34
- Note that some of these changes happened in earlier snapshots
35
- Chunk restructuring
36
- Contents of Level tag moved into root
37
- Some tags renamed from PascalCase to snake_case
38
- 2844
39
- Level.Entities -> entities.
40
- Level.TileEntities -> block_entities.
41
- Level.TileTicks and Level.ToBeTicked have moved to block_ticks.
42
- Level.LiquidTicks and Level.LiquidsToBeTicked have moved to fluid_ticks.
43
- Level.Sections -> sections.
44
- Level.Structures -> structures.
45
- Level.Structures.Starts -> structures.starts.
46
- Level.Sections[].block_states -> sections[].block_states.
47
- Level.Sections[].biomes -> sections[].biomes
48
- Added yPos the minimum section y position in the chunk.
49
- Added below_zero_retrogen containing data to support below zero generation.
50
- Added blending_data containing data to support blending new world generation with existing chunks.
51
- 2836
52
- Level.Sections[].BlockStates & Level.Sections[].Palette -> Level.Sections[].block_states.
53
- Level.Biomes -> Level.Sections[].biomes.
54
- Level.CarvingMasks[] is now long[] instead of byte[].
55
- """
56
-
57
- OldLevel: ChunkPathType = ("region", [("Level", CompoundTag)], CompoundTag)
58
- Level: ChunkPathType = ("region", [], CompoundTag)
59
- Sections: ChunkPathType = ("region", [("sections", ListTag)], ListTag)
60
-
61
- Entities: ChunkPathType = ("region", [("entities", ListTag)], ListTag)
62
- BlockEntities: ChunkPathType = ("region", [("block_entities", ListTag)], ListTag)
63
- BlockTicks: ChunkPathType = ("region", [("block_ticks", ListTag)], ListTag)
64
- ToBeTicked = None
65
- LiquidTicks: ChunkPathType = ("region", [("fluid_ticks", ListTag)], ListTag)
66
- LiquidsToBeTicked = None
67
- Structures: ChunkPathType = ("region", [("structures", CompoundTag)], CompoundTag)
68
- yPos: ChunkPathType = ("region", [("yPos", IntTag)], IntTag)
69
- Biomes = None
70
-
71
- # Changed attributes not listed on the wiki
72
- xPos: ChunkPathType = ("region", [("xPos", IntTag)], IntTag)
73
- zPos: ChunkPathType = ("region", [("zPos", IntTag)], IntTag)
74
- LastUpdate: ChunkPathType = ("region", [("LastUpdate", LongTag)], LongTag)
75
- InhabitedTime: ChunkPathType = ("region", [("InhabitedTime", LongTag)], LongTag)
76
- Status: ChunkPathType = ("region", [("Status", StringTag)], StringTag("full"))
77
- PostProcessing: ChunkPathType = ("region", [("PostProcessing", ListTag)], ListTag)
78
- Heightmaps: ChunkPathType = ("region", [("Heightmaps", CompoundTag)], CompoundTag)
79
-
80
- def __init__(self):
81
- super().__init__()
82
- self._register_post_encoder(self._post_encode_remove_old_level)
83
-
84
- @staticmethod
85
- def minor_is_valid(key: int):
86
- return 2844 <= key <= 3337
87
-
88
- def _get_floor_cy(self, data: ChunkDataType):
89
- return self.get_layer_obj(data, self.yPos, pop_last=True).py_int
90
-
91
- def _decode_block_section(
92
- self, section: CompoundTag
93
- ) -> Optional[Tuple[numpy.ndarray, list]]:
94
- block_states = self.get_obj(section, "block_states", CompoundTag)
95
- if (
96
- isinstance(block_states, CompoundTag) and "palette" in block_states
97
- ): # 1.14 makes block_palette/blocks optional.
98
- section_palette = self._decode_block_palette(block_states.pop("palette"))
99
- data = block_states.pop("data", None)
100
- if data is None:
101
- arr = numpy.zeros((16, 16, 16), numpy.uint32)
102
- else:
103
- decoded = decode_long_array(
104
- data.np_array,
105
- 16**3,
106
- max(4, (len(section_palette) - 1).bit_length()),
107
- dense=self.LongArrayDense,
108
- ).astype(numpy.uint32)
109
- arr = numpy.transpose(decoded.reshape((16, 16, 16)), (2, 0, 1))
110
- return arr, section_palette
111
- else:
112
- return None
113
-
114
- @staticmethod
115
- def _decode_biome_palette(palette: ListTag) -> list[BiomeType]:
116
- return [entry.py_data for entry in palette]
117
-
118
- def _decode_biome_section(
119
- self, section: CompoundTag
120
- ) -> Optional[Tuple[numpy.ndarray, list]]:
121
- biomes = self.get_obj(section, "biomes", CompoundTag)
122
- if isinstance(biomes, CompoundTag) and "palette" in biomes:
123
- section_palette = self._decode_biome_palette(biomes.pop("palette"))
124
- assert section_palette, "Biome palette cannot be empty"
125
- data = biomes.pop("data", None)
126
- if data is None:
127
- # case 1: palette contains one value and data does not exist (undefined zero array)
128
- # TODO: in the new biome system just leave this as the number
129
- arr = numpy.zeros((4, 4, 4), numpy.uint32)
130
- else:
131
- # case 2: palette contains values and data is an index array
132
- arr = numpy.transpose(
133
- decode_long_array(
134
- data.np_array,
135
- 4**3,
136
- max(1, (len(section_palette) - 1).bit_length()),
137
- dense=self.LongArrayDense,
138
- )
139
- .astype(numpy.uint32)
140
- .reshape((4, 4, 4)),
141
- (2, 0, 1),
142
- )
143
- return arr, section_palette
144
- else:
145
- return None
146
-
147
- def _decode_biomes(
148
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
149
- ):
150
- biomes: Dict[int, numpy.ndarray] = {}
151
- palette = BiomeManager()
152
-
153
- for cy, section in self._iter_sections(data):
154
- data = self._decode_biome_section(section)
155
- if data is not None:
156
- arr, section_palette = data
157
- lut = numpy.array(
158
- [palette.get_add_biome(biome) for biome in section_palette]
159
- )
160
- biomes[cy] = lut[arr].astype(numpy.uint32)
161
-
162
- chunk.biomes = biomes
163
- chunk.biome_palette = palette
164
-
165
- def _decode_block_ticks(
166
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
167
- ):
168
- chunk.misc.setdefault("block_ticks", {}).update(
169
- self._decode_ticks(self.get_layer_obj(data, self.BlockTicks, pop_last=True))
170
- )
171
-
172
- def _decode_fluid_ticks(
173
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
174
- ):
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
-
181
- def _encode_coords(
182
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
183
- ):
184
- super()._encode_coords(chunk, data, floor_cy, height_cy)
185
- self.set_layer_obj(data, self.yPos, IntTag(floor_cy))
186
-
187
- def _encode_block_section(
188
- self,
189
- chunk: Chunk,
190
- sections: Dict[int, CompoundTag],
191
- palette: AnyNDArray,
192
- cy: int,
193
- ):
194
- block_sub_array = numpy.transpose(
195
- chunk.blocks.get_sub_chunk(cy), (1, 2, 0)
196
- ).ravel()
197
-
198
- sub_palette_, block_sub_array = numpy.unique(
199
- block_sub_array, return_inverse=True
200
- )
201
- sub_palette = self._encode_block_palette(palette[sub_palette_])
202
- section = sections.setdefault(cy, CompoundTag())
203
- block_states = section["block_states"] = CompoundTag({"palette": sub_palette})
204
- if len(sub_palette) != 1:
205
- block_states["data"] = LongArrayTag(
206
- encode_long_array(
207
- block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4
208
- )
209
- )
210
-
211
- @staticmethod
212
- def _encode_biome_palette(palette: list[BiomeType]) -> ListTag:
213
- return ListTag([StringTag(entry) for entry in palette])
214
-
215
- def _encode_biome_section(
216
- self,
217
- chunk: Chunk,
218
- sections: Dict[int, CompoundTag],
219
- cy: int,
220
- ):
221
- biome_sub_array = numpy.transpose(
222
- chunk.biomes.get_section(cy), (1, 2, 0)
223
- ).ravel()
224
-
225
- sub_palette_, biome_sub_array = numpy.unique(
226
- biome_sub_array, return_inverse=True
227
- )
228
- sub_palette = self._encode_biome_palette(chunk.biome_palette[sub_palette_])
229
- biomes = sections[cy]["biomes"] = CompoundTag({"palette": sub_palette})
230
- if len(sub_palette) != 1:
231
- biomes["data"] = LongArrayTag(
232
- encode_long_array(biome_sub_array, dense=self.LongArrayDense)
233
- )
234
-
235
- def _encode_biomes(
236
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
237
- ):
238
- sections = self._get_encode_sections(data, floor_cy, height_cy)
239
- ceil_cy = floor_cy + height_cy
240
- chunk.biomes.convert_to_3d()
241
- for cy in chunk.biomes.sections:
242
- if floor_cy <= cy < ceil_cy:
243
- self._encode_biome_section(chunk, sections, cy)
244
-
245
- def _encode_block_ticks(
246
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
247
- ):
248
- self.set_layer_obj(
249
- data, self.BlockTicks, self._encode_ticks(chunk.misc.get("block_ticks", {}))
250
- )
251
-
252
- def _encode_fluid_ticks(
253
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
254
- ):
255
- self.set_layer_obj(
256
- data,
257
- self.LiquidTicks,
258
- self._encode_ticks(chunk.misc.get("fluid_ticks", {})),
259
- )
260
-
261
- def _post_encode_remove_old_level(
262
- self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
263
- ):
264
- self.get_layer_obj(data, self.OldLevel, pop_last=True)
265
-
266
-
267
- export = Anvil2844Interface
1
+ from __future__ import annotations
2
+ from typing import Tuple, Dict, Optional
3
+ import numpy
4
+
5
+ from amulet_nbt import (
6
+ CompoundTag,
7
+ ListTag,
8
+ ByteTag,
9
+ IntTag,
10
+ LongTag,
11
+ LongArrayTag,
12
+ StringTag,
13
+ )
14
+
15
+ from amulet.api.chunk import Chunk
16
+ from amulet.api.registry import BiomeManager
17
+ from amulet.api.data_types import AnyNDArray, BiomeType
18
+ from amulet.utils.world_utils import (
19
+ decode_long_array,
20
+ encode_long_array,
21
+ )
22
+
23
+ from .base_anvil_interface import (
24
+ ChunkDataType,
25
+ ChunkPathType,
26
+ )
27
+ from .anvil_2709 import (
28
+ Anvil2709Interface as ParentInterface,
29
+ )
30
+
31
+
32
+ class Anvil2844Interface(ParentInterface):
33
+ """
34
+ Note that some of these changes happened in earlier snapshots
35
+ Chunk restructuring
36
+ Contents of Level tag moved into root
37
+ Some tags renamed from PascalCase to snake_case
38
+ 2844
39
+ Level.Entities -> entities.
40
+ Level.TileEntities -> block_entities.
41
+ Level.TileTicks and Level.ToBeTicked have moved to block_ticks.
42
+ Level.LiquidTicks and Level.LiquidsToBeTicked have moved to fluid_ticks.
43
+ Level.Sections -> sections.
44
+ Level.Structures -> structures.
45
+ Level.Structures.Starts -> structures.starts.
46
+ Level.Sections[].block_states -> sections[].block_states.
47
+ Level.Sections[].biomes -> sections[].biomes
48
+ Added yPos the minimum section y position in the chunk.
49
+ Added below_zero_retrogen containing data to support below zero generation.
50
+ Added blending_data containing data to support blending new world generation with existing chunks.
51
+ 2836
52
+ Level.Sections[].BlockStates & Level.Sections[].Palette -> Level.Sections[].block_states.
53
+ Level.Biomes -> Level.Sections[].biomes.
54
+ Level.CarvingMasks[] is now long[] instead of byte[].
55
+ """
56
+
57
+ OldLevel: ChunkPathType = ("region", [("Level", CompoundTag)], CompoundTag)
58
+ Level: ChunkPathType = ("region", [], CompoundTag)
59
+ Sections: ChunkPathType = ("region", [("sections", ListTag)], ListTag)
60
+
61
+ Entities: ChunkPathType = ("region", [("entities", ListTag)], ListTag)
62
+ BlockEntities: ChunkPathType = ("region", [("block_entities", ListTag)], ListTag)
63
+ BlockTicks: ChunkPathType = ("region", [("block_ticks", ListTag)], ListTag)
64
+ ToBeTicked = None
65
+ LiquidTicks: ChunkPathType = ("region", [("fluid_ticks", ListTag)], ListTag)
66
+ LiquidsToBeTicked = None
67
+ Structures: ChunkPathType = ("region", [("structures", CompoundTag)], CompoundTag)
68
+ yPos: ChunkPathType = ("region", [("yPos", IntTag)], IntTag)
69
+ Biomes = None
70
+
71
+ # Changed attributes not listed on the wiki
72
+ xPos: ChunkPathType = ("region", [("xPos", IntTag)], IntTag)
73
+ zPos: ChunkPathType = ("region", [("zPos", IntTag)], IntTag)
74
+ LastUpdate: ChunkPathType = ("region", [("LastUpdate", LongTag)], LongTag)
75
+ InhabitedTime: ChunkPathType = ("region", [("InhabitedTime", LongTag)], LongTag)
76
+ Status: ChunkPathType = ("region", [("Status", StringTag)], StringTag("full"))
77
+ PostProcessing: ChunkPathType = ("region", [("PostProcessing", ListTag)], ListTag)
78
+ Heightmaps: ChunkPathType = ("region", [("Heightmaps", CompoundTag)], CompoundTag)
79
+
80
+ def __init__(self):
81
+ super().__init__()
82
+ self._register_post_encoder(self._post_encode_remove_old_level)
83
+
84
+ @staticmethod
85
+ def minor_is_valid(key: int):
86
+ return 2844 <= key <= 3337
87
+
88
+ def _get_floor_cy(self, data: ChunkDataType):
89
+ return self.get_layer_obj(data, self.yPos, pop_last=True).py_int
90
+
91
+ def _decode_block_section(
92
+ self, section: CompoundTag
93
+ ) -> Optional[Tuple[numpy.ndarray, list]]:
94
+ block_states = self.get_obj(section, "block_states", CompoundTag)
95
+ if (
96
+ isinstance(block_states, CompoundTag) and "palette" in block_states
97
+ ): # 1.14 makes block_palette/blocks optional.
98
+ section_palette = self._decode_block_palette(block_states.pop("palette"))
99
+ data = block_states.pop("data", None)
100
+ if data is None:
101
+ arr = numpy.zeros((16, 16, 16), numpy.uint32)
102
+ else:
103
+ decoded = decode_long_array(
104
+ data.np_array,
105
+ 16**3,
106
+ max(4, (len(section_palette) - 1).bit_length()),
107
+ dense=self.LongArrayDense,
108
+ ).astype(numpy.uint32)
109
+ arr = numpy.transpose(decoded.reshape((16, 16, 16)), (2, 0, 1))
110
+ return arr, section_palette
111
+ else:
112
+ return None
113
+
114
+ @staticmethod
115
+ def _decode_biome_palette(palette: ListTag) -> list[BiomeType]:
116
+ return [entry.py_data for entry in palette]
117
+
118
+ def _decode_biome_section(
119
+ self, section: CompoundTag
120
+ ) -> Optional[Tuple[numpy.ndarray, list]]:
121
+ biomes = self.get_obj(section, "biomes", CompoundTag)
122
+ if isinstance(biomes, CompoundTag) and "palette" in biomes:
123
+ section_palette = self._decode_biome_palette(biomes.pop("palette"))
124
+ assert section_palette, "Biome palette cannot be empty"
125
+ data = biomes.pop("data", None)
126
+ if data is None:
127
+ # case 1: palette contains one value and data does not exist (undefined zero array)
128
+ # TODO: in the new biome system just leave this as the number
129
+ arr = numpy.zeros((4, 4, 4), numpy.uint32)
130
+ else:
131
+ # case 2: palette contains values and data is an index array
132
+ arr = numpy.transpose(
133
+ decode_long_array(
134
+ data.np_array,
135
+ 4**3,
136
+ max(1, (len(section_palette) - 1).bit_length()),
137
+ dense=self.LongArrayDense,
138
+ )
139
+ .astype(numpy.uint32)
140
+ .reshape((4, 4, 4)),
141
+ (2, 0, 1),
142
+ )
143
+ return arr, section_palette
144
+ else:
145
+ return None
146
+
147
+ def _decode_biomes(
148
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
149
+ ):
150
+ biomes: Dict[int, numpy.ndarray] = {}
151
+ palette = BiomeManager()
152
+
153
+ for cy, section in self._iter_sections(data):
154
+ data = self._decode_biome_section(section)
155
+ if data is not None:
156
+ arr, section_palette = data
157
+ lut = numpy.array(
158
+ [palette.get_add_biome(biome) for biome in section_palette]
159
+ )
160
+ biomes[cy] = lut[arr].astype(numpy.uint32)
161
+
162
+ chunk.biomes = biomes
163
+ chunk.biome_palette = palette
164
+
165
+ def _decode_block_ticks(
166
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
167
+ ):
168
+ chunk.misc.setdefault("block_ticks", {}).update(
169
+ self._decode_ticks(self.get_layer_obj(data, self.BlockTicks, pop_last=True))
170
+ )
171
+
172
+ def _decode_fluid_ticks(
173
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
174
+ ):
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
+
181
+ def _encode_coords(
182
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
183
+ ):
184
+ super()._encode_coords(chunk, data, floor_cy, height_cy)
185
+ self.set_layer_obj(data, self.yPos, IntTag(floor_cy))
186
+
187
+ def _encode_block_section(
188
+ self,
189
+ chunk: Chunk,
190
+ sections: Dict[int, CompoundTag],
191
+ palette: AnyNDArray,
192
+ cy: int,
193
+ ):
194
+ block_sub_array = numpy.transpose(
195
+ chunk.blocks.get_sub_chunk(cy), (1, 2, 0)
196
+ ).ravel()
197
+
198
+ sub_palette_, block_sub_array = numpy.unique(
199
+ block_sub_array, return_inverse=True
200
+ )
201
+ sub_palette = self._encode_block_palette(palette[sub_palette_])
202
+ section = sections.setdefault(cy, CompoundTag())
203
+ block_states = section["block_states"] = CompoundTag({"palette": sub_palette})
204
+ if len(sub_palette) != 1:
205
+ block_states["data"] = LongArrayTag(
206
+ encode_long_array(
207
+ block_sub_array, dense=self.LongArrayDense, min_bits_per_entry=4
208
+ )
209
+ )
210
+
211
+ @staticmethod
212
+ def _encode_biome_palette(palette: list[BiomeType]) -> ListTag:
213
+ return ListTag([StringTag(entry) for entry in palette])
214
+
215
+ def _encode_biome_section(
216
+ self,
217
+ chunk: Chunk,
218
+ sections: Dict[int, CompoundTag],
219
+ cy: int,
220
+ ):
221
+ biome_sub_array = numpy.transpose(
222
+ chunk.biomes.get_section(cy), (1, 2, 0)
223
+ ).ravel()
224
+
225
+ sub_palette_, biome_sub_array = numpy.unique(
226
+ biome_sub_array, return_inverse=True
227
+ )
228
+ sub_palette = self._encode_biome_palette(chunk.biome_palette[sub_palette_])
229
+ biomes = sections[cy]["biomes"] = CompoundTag({"palette": sub_palette})
230
+ if len(sub_palette) != 1:
231
+ biomes["data"] = LongArrayTag(
232
+ encode_long_array(biome_sub_array, dense=self.LongArrayDense)
233
+ )
234
+
235
+ def _encode_biomes(
236
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
237
+ ):
238
+ sections = self._get_encode_sections(data, floor_cy, height_cy)
239
+ ceil_cy = floor_cy + height_cy
240
+ chunk.biomes.convert_to_3d()
241
+ for cy in chunk.biomes.sections:
242
+ if floor_cy <= cy < ceil_cy:
243
+ self._encode_biome_section(chunk, sections, cy)
244
+
245
+ def _encode_block_ticks(
246
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
247
+ ):
248
+ self.set_layer_obj(
249
+ data, self.BlockTicks, self._encode_ticks(chunk.misc.get("block_ticks", {}))
250
+ )
251
+
252
+ def _encode_fluid_ticks(
253
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
254
+ ):
255
+ self.set_layer_obj(
256
+ data,
257
+ self.LiquidTicks,
258
+ self._encode_ticks(chunk.misc.get("fluid_ticks", {})),
259
+ )
260
+
261
+ def _post_encode_remove_old_level(
262
+ self, chunk: Chunk, data: ChunkDataType, floor_cy: int, height_cy: int
263
+ ):
264
+ self.get_layer_obj(data, self.OldLevel, pop_last=True)
265
+
266
+
267
+ export = Anvil2844Interface
@@ -1,19 +1,19 @@
1
- from __future__ import annotations
2
-
3
- from .anvil_2844 import (
4
- Anvil2844Interface as ParentInterface,
5
- )
6
- from amulet.api.chunk import StatusFormats
7
-
8
-
9
- class Anvil3463Interface(ParentInterface):
10
- def __init__(self):
11
- super().__init__()
12
- self._set_feature("status", StatusFormats.Java_20)
13
-
14
- @staticmethod
15
- def minor_is_valid(key: int):
16
- return 3454 <= key < 3580
17
-
18
-
19
- export = Anvil3463Interface
1
+ from __future__ import annotations
2
+
3
+ from .anvil_2844 import (
4
+ Anvil2844Interface as ParentInterface,
5
+ )
6
+ from amulet.api.chunk import StatusFormats
7
+
8
+
9
+ class Anvil3463Interface(ParentInterface):
10
+ def __init__(self):
11
+ super().__init__()
12
+ self._set_feature("status", StatusFormats.Java_20)
13
+
14
+ @staticmethod
15
+ def minor_is_valid(key: int):
16
+ return 3454 <= key < 3580
17
+
18
+
19
+ export = Anvil3463Interface