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,326 +1,326 @@
1
- from __future__ import annotations
2
- from abc import abstractmethod, ABC
3
-
4
- from typing import (
5
- List,
6
- Tuple,
7
- Iterable,
8
- TYPE_CHECKING,
9
- Any,
10
- Dict,
11
- Callable,
12
- Sequence,
13
- Union,
14
- Type,
15
- )
16
- import numpy
17
-
18
-
19
- from amulet_nbt import (
20
- AbstractBaseTag,
21
- IntTag,
22
- ListTag,
23
- CompoundTag,
24
- NamedTag,
25
- AnyNBT,
26
- )
27
-
28
- from amulet.api.chunk import Chunk, StatusFormats
29
- from amulet.api.wrapper import Interface
30
- from amulet.level import loader
31
- from amulet.api.data_types import AnyNDArray, VersionIdentifierType
32
- from amulet.api.wrapper import EntityIDType, EntityCoordType
33
-
34
- if TYPE_CHECKING:
35
- from amulet.api.wrapper import Translator
36
- from amulet.api.block_entity import BlockEntity
37
- from amulet.api.entity import Entity
38
-
39
-
40
- ChunkDataType = Dict[str, NamedTag]
41
-
42
- ChunkPathType = Tuple[
43
- str, # The layer name
44
- Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
45
- Type[AbstractBaseTag],
46
- ]
47
-
48
-
49
- class BaseDecoderEncoder(ABC):
50
- def __init__(self):
51
- self.__decoders = {}
52
- self.__post_decoders = {}
53
- self.__encoders = {}
54
- self.__post_encoders = {}
55
-
56
- def _register_decoder(self, decoder: Callable):
57
- """Register a function that does the decoding"""
58
- self.__decoders[decoder] = None
59
-
60
- def _register_post_decoder(self, post_decoder: Callable):
61
- """Register a function that runs after the decoding"""
62
- self.__post_decoders[post_decoder] = None
63
-
64
- def _unregister_decoder(self, decoder: Callable):
65
- """Unregister a function that does the decoding"""
66
- del self.__decoders[decoder]
67
-
68
- def _unregister_post_decoder(self, post_decoder: Callable):
69
- """Unregister a function that runs after the decoding"""
70
- del self.__post_decoders[post_decoder]
71
-
72
- def _do_decode(self, *args, **kwargs):
73
- for decoder in self.__decoders:
74
- decoder(*args, **kwargs)
75
- for post_decoder in self.__post_decoders:
76
- post_decoder(*args, **kwargs)
77
-
78
- def _register_encoder(self, encoder: Callable):
79
- """Register a function that does the encoding"""
80
- self.__encoders[encoder] = None
81
-
82
- def _register_post_encoder(self, post_encoder: Callable):
83
- """Register a function that runs after the encoding"""
84
- self.__post_encoders[post_encoder] = None
85
-
86
- def _unregister_encoder(self, encoder: Callable):
87
- """Unregister a function that does the encoding"""
88
- del self.__encoders[encoder]
89
-
90
- def _unregister_post_encoder(self, post_encoder: Callable):
91
- """Unregister a function that runs after the encoding"""
92
- del self.__post_encoders[post_encoder]
93
-
94
- def _do_encode(self, *args, **kwargs):
95
- for encoder in self.__encoders:
96
- encoder(*args, **kwargs)
97
- for post_encoder in self.__post_encoders:
98
- post_encoder(*args, **kwargs)
99
-
100
-
101
- class BaseAnvilInterface(Interface, BaseDecoderEncoder):
102
- # The chunk object, the chunk data, the floor chunk coord, the chunk height (in sub-chunks)
103
- DecoderType = Callable[[Chunk, ChunkDataType, int, int], None]
104
- EncoderType = Callable[[Chunk, ChunkDataType, int, int], None]
105
- _register_decoder: Callable[[DecoderType], None]
106
- _register_post_decoder: Callable[[DecoderType], None]
107
- _unregister_decoder: Callable[[DecoderType], None]
108
- _unregister_post_decoder: Callable[[DecoderType], None]
109
- _do_decode: DecoderType
110
- _register_encoder: Callable[[EncoderType], None]
111
- _register_post_encoder: Callable[[EncoderType], None]
112
- _unregister_encoder: Callable[[EncoderType], None]
113
- _unregister_post_encoder: Callable[[EncoderType], None]
114
- _do_encode: EncoderType
115
-
116
- def __init__(self):
117
- BaseDecoderEncoder.__init__(self)
118
- self._feature_options = {
119
- "status": StatusFormats,
120
- "height_map": [
121
- "256IARequired", # A 256 element Int Array in HeightMap
122
- "256IA", # A 256 element Int Array in HeightMap
123
- "C|V1", # A Compound of Long Arrays with these keys "LIQUID", "SOLID", "LIGHT", "RAIN"
124
- "C|V2", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "LIGHT_BLOCKING"
125
- "C|V3", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "LIGHT_BLOCKING", "WORLD_SURFACE"
126
- "C|V4", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "WORLD_SURFACE"
127
- ],
128
- # 'carving_masks': ['C|?BA'],
129
- "light_optional": ["false", "true"],
130
- "block_entity_format": [EntityIDType.namespace_str_id],
131
- "block_entity_coord_format": [EntityCoordType.xyz_int],
132
- "entity_format": [EntityIDType.namespace_str_id],
133
- "entity_coord_format": [EntityCoordType.Pos_list_double],
134
- # 'lights': [],
135
- }
136
- self._features = {key: None for key in self._feature_options.keys()}
137
-
138
- def _set_feature(self, feature: str, option: Any):
139
- assert feature in self._feature_options, f"{feature} is not a valid feature."
140
- assert (
141
- option is None or option in self._feature_options[feature]
142
- ), f'Invalid option {option} for feature "{feature}"'
143
- self._features[feature] = option
144
-
145
- def is_valid(self, key: Tuple) -> bool:
146
- return key[0] == "java" and self.minor_is_valid(key[1])
147
-
148
- @staticmethod
149
- @abstractmethod
150
- def minor_is_valid(key: int):
151
- raise NotImplementedError
152
-
153
- def get_translator(
154
- self,
155
- max_world_version: VersionIdentifierType,
156
- data: ChunkDataType = None,
157
- ) -> Tuple["Translator", int]:
158
- if data is None:
159
- key = max_world_version
160
- version = max_world_version[1]
161
- else:
162
- data_version = (
163
- data.get("region", NamedTag(CompoundTag()))
164
- .compound.get("DataVersion", IntTag(-1))
165
- .py_int
166
- )
167
- key, version = (("java", data_version), data_version)
168
-
169
- return loader.Translators.get(key), version
170
-
171
- def get_layer_obj(
172
- self,
173
- obj: ChunkDataType,
174
- data: Tuple[
175
- str,
176
- Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
177
- Union[None, AnyNBT, Type[AbstractBaseTag]],
178
- ],
179
- *,
180
- pop_last=False,
181
- ) -> Any:
182
- """
183
- Get an object from a nested NBT structure layer
184
-
185
- :param obj: The chunk data object
186
- :param data: The data layer name, the nbt path and the default
187
- :param pop_last: If true the last key will be popped
188
- :return: The found data or the default
189
- """
190
- layer_key, path, default = data
191
- if layer_key in obj:
192
- return self.get_nested_obj(
193
- obj[layer_key].compound, path, default, pop_last=pop_last
194
- )
195
- elif default is None or isinstance(default, AbstractBaseTag):
196
- return default
197
- elif issubclass(default, AbstractBaseTag):
198
- return default()
199
- else:
200
- raise TypeError("default must be None, an NBT instance or an NBT class.")
201
-
202
- def set_layer_obj(
203
- self,
204
- obj: ChunkDataType,
205
- data: Tuple[
206
- str,
207
- Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
208
- Union[None, AnyNBT, Type[AbstractBaseTag]],
209
- ],
210
- default_tag: AnyNBT = None,
211
- *,
212
- setdefault=False,
213
- ) -> AnyNBT:
214
- """
215
- Setdefault on a ChunkDataType object
216
-
217
- :param obj: The ChunkDataType object to use
218
- :param data: The data to set
219
- :param setdefault: If True will behave like setdefault. If False will replace existing data.
220
- :return: The existing data found or the default that was set
221
- """
222
- layer_key, path, default = data
223
- default = default if default_tag is None else default_tag
224
- if not path:
225
- raise ValueError("was not given a path to set")
226
- tag = obj.setdefault(layer_key, NamedTag()).compound
227
- *path, (key, dtype) = path
228
- if path:
229
- key_path = next(zip(*path))
230
- else:
231
- key_path = ()
232
- return self.set_obj(
233
- tag, key, dtype, default, path=key_path, setdefault=setdefault
234
- )
235
-
236
- @abstractmethod
237
- def decode(
238
- self, cx: int, cz: int, data: ChunkDataType, bounds: Tuple[int, int]
239
- ) -> Tuple["Chunk", AnyNDArray]:
240
- """
241
- Create an amulet.api.chunk.Chunk object from raw data.
242
- :param cx: chunk x coordinate
243
- :param cz: chunk z coordinate
244
- :param data: The chunk data
245
- :param bounds: The minimum and maximum bounds of the chunk. In 1.17 this is required to define where the biome array sits.
246
- :return: Chunk object in version-specific format, along with the block_palette for that chunk.
247
- """
248
- raise NotImplementedError
249
-
250
- def _decode_entity_list(self, entities: ListTag) -> List["Entity"]:
251
- entities_out = []
252
- if entities.list_data_type == CompoundTag.tag_id:
253
- for nbt in entities:
254
- entity = self._decode_entity(
255
- NamedTag(nbt),
256
- self._features["entity_format"],
257
- self._features["entity_coord_format"],
258
- )
259
- if entity is not None:
260
- entities_out.append(entity)
261
-
262
- return entities_out
263
-
264
- def _decode_block_entity_list(self, block_entities: ListTag) -> List["BlockEntity"]:
265
- entities_out = []
266
- if block_entities.list_data_type == CompoundTag.tag_id:
267
- for nbt in block_entities:
268
- if not isinstance(nbt, CompoundTag):
269
- continue
270
- entity = self._decode_block_entity(
271
- NamedTag(nbt),
272
- self._features["block_entity_format"],
273
- self._features["block_entity_coord_format"],
274
- )
275
- if entity is not None:
276
- entities_out.append(entity)
277
-
278
- return entities_out
279
-
280
- @abstractmethod
281
- def encode(
282
- self,
283
- chunk: "Chunk",
284
- palette: AnyNDArray,
285
- max_world_version: Tuple[str, int],
286
- bounds: Tuple[int, int],
287
- ) -> ChunkDataType:
288
- """
289
- Encode a version-specific chunk to raw data for the format to store.
290
-
291
- :param chunk: The already translated version-specific chunk to encode.
292
- :param palette: The block_palette the ids in the chunk correspond to.
293
- :type palette: numpy.ndarray[Block]
294
- :param max_world_version: The key to use to find the encoder.
295
- :param bounds: The minimum and maximum bounds of the chunk. In 1.17 this is required to define where the biome array sits.
296
- :return: Raw data to be stored by the Format.
297
- """
298
- raise NotImplementedError
299
-
300
- def _encode_entity_list(self, entities: Iterable["Entity"]) -> ListTag:
301
- entities_out = []
302
- for entity in entities:
303
- nbt = self._encode_entity(
304
- entity,
305
- self._features["entity_format"],
306
- self._features["entity_coord_format"],
307
- )
308
- if nbt is not None:
309
- entities_out.append(nbt.compound)
310
-
311
- return ListTag(entities_out)
312
-
313
- def _encode_block_entity_list(
314
- self, block_entities: Iterable["BlockEntity"]
315
- ) -> ListTag:
316
- entities_out = []
317
- for entity in block_entities:
318
- nbt = self._encode_block_entity(
319
- entity,
320
- self._features["block_entity_format"],
321
- self._features["block_entity_coord_format"],
322
- )
323
- if nbt is not None:
324
- entities_out.append(nbt.compound)
325
-
326
- return ListTag(entities_out)
1
+ from __future__ import annotations
2
+ from abc import abstractmethod, ABC
3
+
4
+ from typing import (
5
+ List,
6
+ Tuple,
7
+ Iterable,
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Dict,
11
+ Callable,
12
+ Sequence,
13
+ Union,
14
+ Type,
15
+ )
16
+ import numpy
17
+
18
+
19
+ from amulet_nbt import (
20
+ AbstractBaseTag,
21
+ IntTag,
22
+ ListTag,
23
+ CompoundTag,
24
+ NamedTag,
25
+ AnyNBT,
26
+ )
27
+
28
+ from amulet.api.chunk import Chunk, StatusFormats
29
+ from amulet.api.wrapper import Interface
30
+ from amulet.level import loader
31
+ from amulet.api.data_types import AnyNDArray, VersionIdentifierType
32
+ from amulet.api.wrapper import EntityIDType, EntityCoordType
33
+
34
+ if TYPE_CHECKING:
35
+ from amulet.api.wrapper import Translator
36
+ from amulet.api.block_entity import BlockEntity
37
+ from amulet.api.entity import Entity
38
+
39
+
40
+ ChunkDataType = Dict[str, NamedTag]
41
+
42
+ ChunkPathType = Tuple[
43
+ str, # The layer name
44
+ Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
45
+ Type[AbstractBaseTag],
46
+ ]
47
+
48
+
49
+ class BaseDecoderEncoder(ABC):
50
+ def __init__(self):
51
+ self.__decoders = {}
52
+ self.__post_decoders = {}
53
+ self.__encoders = {}
54
+ self.__post_encoders = {}
55
+
56
+ def _register_decoder(self, decoder: Callable):
57
+ """Register a function that does the decoding"""
58
+ self.__decoders[decoder] = None
59
+
60
+ def _register_post_decoder(self, post_decoder: Callable):
61
+ """Register a function that runs after the decoding"""
62
+ self.__post_decoders[post_decoder] = None
63
+
64
+ def _unregister_decoder(self, decoder: Callable):
65
+ """Unregister a function that does the decoding"""
66
+ del self.__decoders[decoder]
67
+
68
+ def _unregister_post_decoder(self, post_decoder: Callable):
69
+ """Unregister a function that runs after the decoding"""
70
+ del self.__post_decoders[post_decoder]
71
+
72
+ def _do_decode(self, *args, **kwargs):
73
+ for decoder in self.__decoders:
74
+ decoder(*args, **kwargs)
75
+ for post_decoder in self.__post_decoders:
76
+ post_decoder(*args, **kwargs)
77
+
78
+ def _register_encoder(self, encoder: Callable):
79
+ """Register a function that does the encoding"""
80
+ self.__encoders[encoder] = None
81
+
82
+ def _register_post_encoder(self, post_encoder: Callable):
83
+ """Register a function that runs after the encoding"""
84
+ self.__post_encoders[post_encoder] = None
85
+
86
+ def _unregister_encoder(self, encoder: Callable):
87
+ """Unregister a function that does the encoding"""
88
+ del self.__encoders[encoder]
89
+
90
+ def _unregister_post_encoder(self, post_encoder: Callable):
91
+ """Unregister a function that runs after the encoding"""
92
+ del self.__post_encoders[post_encoder]
93
+
94
+ def _do_encode(self, *args, **kwargs):
95
+ for encoder in self.__encoders:
96
+ encoder(*args, **kwargs)
97
+ for post_encoder in self.__post_encoders:
98
+ post_encoder(*args, **kwargs)
99
+
100
+
101
+ class BaseAnvilInterface(Interface, BaseDecoderEncoder):
102
+ # The chunk object, the chunk data, the floor chunk coord, the chunk height (in sub-chunks)
103
+ DecoderType = Callable[[Chunk, ChunkDataType, int, int], None]
104
+ EncoderType = Callable[[Chunk, ChunkDataType, int, int], None]
105
+ _register_decoder: Callable[[DecoderType], None]
106
+ _register_post_decoder: Callable[[DecoderType], None]
107
+ _unregister_decoder: Callable[[DecoderType], None]
108
+ _unregister_post_decoder: Callable[[DecoderType], None]
109
+ _do_decode: DecoderType
110
+ _register_encoder: Callable[[EncoderType], None]
111
+ _register_post_encoder: Callable[[EncoderType], None]
112
+ _unregister_encoder: Callable[[EncoderType], None]
113
+ _unregister_post_encoder: Callable[[EncoderType], None]
114
+ _do_encode: EncoderType
115
+
116
+ def __init__(self):
117
+ BaseDecoderEncoder.__init__(self)
118
+ self._feature_options = {
119
+ "status": StatusFormats,
120
+ "height_map": [
121
+ "256IARequired", # A 256 element Int Array in HeightMap
122
+ "256IA", # A 256 element Int Array in HeightMap
123
+ "C|V1", # A Compound of Long Arrays with these keys "LIQUID", "SOLID", "LIGHT", "RAIN"
124
+ "C|V2", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "LIGHT_BLOCKING"
125
+ "C|V3", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "LIGHT_BLOCKING", "WORLD_SURFACE"
126
+ "C|V4", # A Compound of Long Arrays with these keys "WORLD_SURFACE_WG", "OCEAN_FLOOR_WG", "MOTION_BLOCKING", "MOTION_BLOCKING_NO_LEAVES", "OCEAN_FLOOR", "WORLD_SURFACE"
127
+ ],
128
+ # 'carving_masks': ['C|?BA'],
129
+ "light_optional": ["false", "true"],
130
+ "block_entity_format": [EntityIDType.namespace_str_id],
131
+ "block_entity_coord_format": [EntityCoordType.xyz_int],
132
+ "entity_format": [EntityIDType.namespace_str_id],
133
+ "entity_coord_format": [EntityCoordType.Pos_list_double],
134
+ # 'lights': [],
135
+ }
136
+ self._features = {key: None for key in self._feature_options.keys()}
137
+
138
+ def _set_feature(self, feature: str, option: Any):
139
+ assert feature in self._feature_options, f"{feature} is not a valid feature."
140
+ assert (
141
+ option is None or option in self._feature_options[feature]
142
+ ), f'Invalid option {option} for feature "{feature}"'
143
+ self._features[feature] = option
144
+
145
+ def is_valid(self, key: Tuple) -> bool:
146
+ return key[0] == "java" and self.minor_is_valid(key[1])
147
+
148
+ @staticmethod
149
+ @abstractmethod
150
+ def minor_is_valid(key: int):
151
+ raise NotImplementedError
152
+
153
+ def get_translator(
154
+ self,
155
+ max_world_version: VersionIdentifierType,
156
+ data: ChunkDataType = None,
157
+ ) -> Tuple["Translator", int]:
158
+ if data is None:
159
+ key = max_world_version
160
+ version = max_world_version[1]
161
+ else:
162
+ data_version = (
163
+ data.get("region", NamedTag(CompoundTag()))
164
+ .compound.get("DataVersion", IntTag(-1))
165
+ .py_int
166
+ )
167
+ key, version = (("java", data_version), data_version)
168
+
169
+ return loader.Translators.get(key), version
170
+
171
+ def get_layer_obj(
172
+ self,
173
+ obj: ChunkDataType,
174
+ data: Tuple[
175
+ str,
176
+ Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
177
+ Union[None, AnyNBT, Type[AbstractBaseTag]],
178
+ ],
179
+ *,
180
+ pop_last=False,
181
+ ) -> Any:
182
+ """
183
+ Get an object from a nested NBT structure layer
184
+
185
+ :param obj: The chunk data object
186
+ :param data: The data layer name, the nbt path and the default
187
+ :param pop_last: If true the last key will be popped
188
+ :return: The found data or the default
189
+ """
190
+ layer_key, path, default = data
191
+ if layer_key in obj:
192
+ return self.get_nested_obj(
193
+ obj[layer_key].compound, path, default, pop_last=pop_last
194
+ )
195
+ elif default is None or isinstance(default, AbstractBaseTag):
196
+ return default
197
+ elif issubclass(default, AbstractBaseTag):
198
+ return default()
199
+ else:
200
+ raise TypeError("default must be None, an NBT instance or an NBT class.")
201
+
202
+ def set_layer_obj(
203
+ self,
204
+ obj: ChunkDataType,
205
+ data: Tuple[
206
+ str,
207
+ Sequence[Tuple[Union[str, int], Type[AbstractBaseTag]]],
208
+ Union[None, AnyNBT, Type[AbstractBaseTag]],
209
+ ],
210
+ default_tag: AnyNBT = None,
211
+ *,
212
+ setdefault=False,
213
+ ) -> AnyNBT:
214
+ """
215
+ Setdefault on a ChunkDataType object
216
+
217
+ :param obj: The ChunkDataType object to use
218
+ :param data: The data to set
219
+ :param setdefault: If True will behave like setdefault. If False will replace existing data.
220
+ :return: The existing data found or the default that was set
221
+ """
222
+ layer_key, path, default = data
223
+ default = default if default_tag is None else default_tag
224
+ if not path:
225
+ raise ValueError("was not given a path to set")
226
+ tag = obj.setdefault(layer_key, NamedTag()).compound
227
+ *path, (key, dtype) = path
228
+ if path:
229
+ key_path = next(zip(*path))
230
+ else:
231
+ key_path = ()
232
+ return self.set_obj(
233
+ tag, key, dtype, default, path=key_path, setdefault=setdefault
234
+ )
235
+
236
+ @abstractmethod
237
+ def decode(
238
+ self, cx: int, cz: int, data: ChunkDataType, bounds: Tuple[int, int]
239
+ ) -> Tuple["Chunk", AnyNDArray]:
240
+ """
241
+ Create an amulet.api.chunk.Chunk object from raw data.
242
+ :param cx: chunk x coordinate
243
+ :param cz: chunk z coordinate
244
+ :param data: The chunk data
245
+ :param bounds: The minimum and maximum bounds of the chunk. In 1.17 this is required to define where the biome array sits.
246
+ :return: Chunk object in version-specific format, along with the block_palette for that chunk.
247
+ """
248
+ raise NotImplementedError
249
+
250
+ def _decode_entity_list(self, entities: ListTag) -> List["Entity"]:
251
+ entities_out = []
252
+ if entities.list_data_type == CompoundTag.tag_id:
253
+ for nbt in entities:
254
+ entity = self._decode_entity(
255
+ NamedTag(nbt),
256
+ self._features["entity_format"],
257
+ self._features["entity_coord_format"],
258
+ )
259
+ if entity is not None:
260
+ entities_out.append(entity)
261
+
262
+ return entities_out
263
+
264
+ def _decode_block_entity_list(self, block_entities: ListTag) -> List["BlockEntity"]:
265
+ entities_out = []
266
+ if block_entities.list_data_type == CompoundTag.tag_id:
267
+ for nbt in block_entities:
268
+ if not isinstance(nbt, CompoundTag):
269
+ continue
270
+ entity = self._decode_block_entity(
271
+ NamedTag(nbt),
272
+ self._features["block_entity_format"],
273
+ self._features["block_entity_coord_format"],
274
+ )
275
+ if entity is not None:
276
+ entities_out.append(entity)
277
+
278
+ return entities_out
279
+
280
+ @abstractmethod
281
+ def encode(
282
+ self,
283
+ chunk: "Chunk",
284
+ palette: AnyNDArray,
285
+ max_world_version: Tuple[str, int],
286
+ bounds: Tuple[int, int],
287
+ ) -> ChunkDataType:
288
+ """
289
+ Encode a version-specific chunk to raw data for the format to store.
290
+
291
+ :param chunk: The already translated version-specific chunk to encode.
292
+ :param palette: The block_palette the ids in the chunk correspond to.
293
+ :type palette: numpy.ndarray[Block]
294
+ :param max_world_version: The key to use to find the encoder.
295
+ :param bounds: The minimum and maximum bounds of the chunk. In 1.17 this is required to define where the biome array sits.
296
+ :return: Raw data to be stored by the Format.
297
+ """
298
+ raise NotImplementedError
299
+
300
+ def _encode_entity_list(self, entities: Iterable["Entity"]) -> ListTag:
301
+ entities_out = []
302
+ for entity in entities:
303
+ nbt = self._encode_entity(
304
+ entity,
305
+ self._features["entity_format"],
306
+ self._features["entity_coord_format"],
307
+ )
308
+ if nbt is not None:
309
+ entities_out.append(nbt.compound)
310
+
311
+ return ListTag(entities_out)
312
+
313
+ def _encode_block_entity_list(
314
+ self, block_entities: Iterable["BlockEntity"]
315
+ ) -> ListTag:
316
+ entities_out = []
317
+ for entity in block_entities:
318
+ nbt = self._encode_block_entity(
319
+ entity,
320
+ self._features["block_entity_format"],
321
+ self._features["block_entity_coord_format"],
322
+ )
323
+ if nbt is not None:
324
+ entities_out.append(nbt.compound)
325
+
326
+ return ListTag(entities_out)