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,441 +1,441 @@
1
- from __future__ import annotations
2
-
3
- from abc import ABC, abstractmethod
4
- from typing import (
5
- Tuple,
6
- Any,
7
- Union,
8
- TYPE_CHECKING,
9
- Optional,
10
- overload,
11
- Type,
12
- Sequence,
13
- Callable,
14
- )
15
- from enum import Enum
16
-
17
- from amulet.api.block_entity import BlockEntity
18
- from amulet.api.entity import Entity
19
- from amulet.api.data_types import AnyNDArray, VersionNumberAny, VersionIdentifierType
20
- from amulet_nbt import (
21
- AbstractBaseTag,
22
- ListTag,
23
- CompoundTag,
24
- AnyNBT,
25
- NamedTag,
26
- StringTag,
27
- IntTag,
28
- IntArrayTag,
29
- DoubleTag,
30
- FloatTag,
31
- )
32
-
33
- if TYPE_CHECKING:
34
- from amulet.api.wrapper import Translator
35
- from amulet.api.chunk import Chunk
36
-
37
-
38
- class EntityIDType(Enum):
39
- int_id = 0
40
- str_id = 1
41
- namespace_str_id = 2
42
- namespace_str_Id = 3
43
- namespace_str_identifier = 4
44
-
45
-
46
- class EntityCoordType(Enum):
47
- xyz_int = 0
48
- Pos_list_float = 1
49
- Pos_list_double = 2
50
- Pos_list_int = 3
51
- Pos_array_int = 4
52
-
53
-
54
- PosTypeMap = {
55
- 3: EntityCoordType.Pos_list_int,
56
- 5: EntityCoordType.Pos_list_float,
57
- 6: EntityCoordType.Pos_list_double,
58
- }
59
-
60
-
61
- class Interface(ABC):
62
- @abstractmethod
63
- def decode(self, *args, **kwargs) -> Tuple["Chunk", AnyNDArray]:
64
- raise NotImplementedError
65
-
66
- def _decode_entity(
67
- self,
68
- nbt: NamedTag,
69
- id_type: EntityIDType,
70
- coord_type: EntityCoordType,
71
- ) -> Optional[Entity]:
72
- entity = self._decode_base_entity(nbt, id_type, coord_type)
73
- if entity is not None:
74
- namespace, base_name, x, y, z, nbt = entity
75
- return Entity(
76
- namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
77
- )
78
-
79
- def _decode_block_entity(
80
- self,
81
- nbt: NamedTag,
82
- id_type: EntityIDType,
83
- coord_type: EntityCoordType,
84
- ) -> Optional[BlockEntity]:
85
- entity = self._decode_base_entity(nbt, id_type, coord_type)
86
- if entity is not None:
87
- namespace, base_name, x, y, z, nbt = entity
88
- return BlockEntity(
89
- namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
90
- )
91
-
92
- @staticmethod
93
- def _decode_base_entity(
94
- named_tag: NamedTag, id_type: EntityIDType, coord_type: EntityCoordType
95
- ) -> Optional[
96
- Tuple[
97
- str,
98
- str,
99
- Union[int, float],
100
- Union[int, float],
101
- Union[int, float],
102
- NamedTag,
103
- ]
104
- ]:
105
- if not (
106
- isinstance(named_tag, NamedTag) and isinstance(named_tag.tag, CompoundTag)
107
- ):
108
- return
109
-
110
- nbt_tag = named_tag.tag
111
-
112
- if id_type == EntityIDType.namespace_str_id:
113
- entity_id = nbt_tag.pop("id", StringTag(""))
114
- if (
115
- not isinstance(entity_id, StringTag)
116
- or entity_id.py_str == ""
117
- or ":" not in entity_id.py_str
118
- ):
119
- return
120
- namespace, base_name = entity_id.py_str.split(":", 1)
121
-
122
- elif id_type == EntityIDType.namespace_str_Id:
123
- entity_id = nbt_tag.pop("Id", StringTag(""))
124
- if (
125
- not isinstance(entity_id, StringTag)
126
- or entity_id.py_str == ""
127
- or ":" not in entity_id.py_str
128
- ):
129
- return
130
- namespace, base_name = entity_id.py_str.split(":", 1)
131
-
132
- elif id_type == EntityIDType.str_id:
133
- entity_id = nbt_tag.pop("id", StringTag(""))
134
- if not isinstance(entity_id, StringTag) or entity_id.py_str == "":
135
- return
136
- namespace = ""
137
- base_name = entity_id.py_str
138
-
139
- elif id_type in [EntityIDType.namespace_str_identifier, EntityIDType.int_id]:
140
- if "identifier" in nbt_tag:
141
- entity_id = nbt_tag.pop("identifier")
142
- if (
143
- not isinstance(entity_id, StringTag)
144
- or entity_id.py_str == ""
145
- or ":" not in entity_id.py_str
146
- ):
147
- return
148
- namespace, base_name = entity_id.py_str.split(":", 1)
149
- elif "id" in nbt_tag:
150
- entity_id = nbt_tag.pop("id")
151
- if not isinstance(entity_id, IntTag):
152
- return
153
- namespace = ""
154
- base_name = str(entity_id.py_int)
155
- else:
156
- return
157
- else:
158
- raise NotImplementedError(f"Entity id type {id_type}")
159
-
160
- if coord_type in [
161
- EntityCoordType.Pos_list_double,
162
- EntityCoordType.Pos_list_float,
163
- EntityCoordType.Pos_list_int,
164
- ]:
165
- if "Pos" not in nbt_tag:
166
- return
167
- pos = nbt_tag.pop("Pos")
168
- pos: ListTag
169
-
170
- if (
171
- not isinstance(pos, ListTag)
172
- or len(pos) != 3
173
- or PosTypeMap.get(pos.list_data_type) != coord_type
174
- ):
175
- return
176
- x, y, z = [c.py_data for c in pos]
177
- elif coord_type == EntityCoordType.Pos_array_int:
178
- if "Pos" not in nbt_tag:
179
- return
180
- pos = nbt_tag.pop("Pos")
181
- pos: IntArrayTag
182
-
183
- if not isinstance(pos, IntArrayTag) or len(pos) != 3:
184
- return
185
- x, y, z = pos
186
- elif coord_type == EntityCoordType.xyz_int:
187
- if not all(
188
- c in nbt_tag and isinstance(nbt_tag[c], IntTag) for c in ("x", "y", "z")
189
- ):
190
- return
191
- x, y, z = [nbt_tag.pop(c).py_int for c in ("x", "y", "z")]
192
- else:
193
- raise NotImplementedError(f"Entity coord type {coord_type}")
194
-
195
- return namespace, base_name, x, y, z, named_tag
196
-
197
- @abstractmethod
198
- def encode(self, *args, **kwargs) -> Any:
199
- """
200
- Take a version-specific chunk and encode it to raw data for the format to store.
201
- """
202
- raise NotImplementedError
203
-
204
- def _encode_entity(
205
- self, entity: Entity, id_type: EntityIDType, coord_type: EntityCoordType
206
- ) -> Optional[NamedTag]:
207
- return self._encode_base_entity(entity, id_type, coord_type)
208
-
209
- def _encode_block_entity(
210
- self, entity: BlockEntity, id_type: EntityIDType, coord_type: EntityCoordType
211
- ) -> Optional[NamedTag]:
212
- return self._encode_base_entity(entity, id_type, coord_type)
213
-
214
- @staticmethod
215
- def _encode_base_entity(
216
- entity: Union[Entity, BlockEntity],
217
- id_type: EntityIDType,
218
- coord_type: EntityCoordType,
219
- ) -> Optional[NamedTag]:
220
- if not isinstance(entity.nbt, NamedTag) and isinstance(
221
- entity.nbt.tag, CompoundTag
222
- ):
223
- return
224
- named_tag = entity.nbt
225
- tag = named_tag.compound
226
-
227
- if id_type == EntityIDType.namespace_str_id:
228
- tag["id"] = StringTag(entity.namespaced_name)
229
- elif id_type == EntityIDType.namespace_str_Id:
230
- tag["Id"] = StringTag(entity.namespaced_name)
231
- elif id_type == EntityIDType.namespace_str_identifier:
232
- tag["identifier"] = StringTag(entity.namespaced_name)
233
- elif id_type == EntityIDType.str_id:
234
- tag["id"] = StringTag(entity.base_name)
235
- elif id_type == EntityIDType.int_id:
236
- if not entity.base_name.isnumeric():
237
- return
238
- tag["id"] = IntTag(int(entity.base_name))
239
- else:
240
- raise NotImplementedError(f"Entity id type {id_type}")
241
-
242
- if coord_type == EntityCoordType.Pos_list_double:
243
- tag["Pos"] = ListTag(
244
- [
245
- DoubleTag(float(entity.x)),
246
- DoubleTag(float(entity.y)),
247
- DoubleTag(float(entity.z)),
248
- ]
249
- )
250
- elif coord_type == EntityCoordType.Pos_list_float:
251
- tag["Pos"] = ListTag(
252
- [
253
- FloatTag(float(entity.x)),
254
- FloatTag(float(entity.y)),
255
- FloatTag(float(entity.z)),
256
- ]
257
- )
258
- elif coord_type == EntityCoordType.Pos_list_int:
259
- tag["Pos"] = ListTag(
260
- [
261
- IntTag(int(entity.x)),
262
- IntTag(int(entity.y)),
263
- IntTag(int(entity.z)),
264
- ]
265
- )
266
- elif coord_type == EntityCoordType.Pos_array_int:
267
- tag["Pos"] = IntArrayTag([int(entity.x), int(entity.y), int(entity.z)])
268
- elif coord_type == EntityCoordType.xyz_int:
269
- tag["x"] = IntTag(int(entity.x))
270
- tag["y"] = IntTag(int(entity.y))
271
- tag["z"] = IntTag(int(entity.z))
272
- else:
273
- raise NotImplementedError(f"Entity coord type {coord_type}")
274
-
275
- return named_tag
276
-
277
- @overload
278
- @staticmethod
279
- def check_type(obj: CompoundTag, key: str, dtype: Type[AnyNBT]) -> bool:
280
- ...
281
-
282
- @overload
283
- @staticmethod
284
- def check_type(obj: ListTag, key: int, dtype: Type[AnyNBT]) -> bool:
285
- ...
286
-
287
- @staticmethod
288
- def check_type(
289
- obj: Union[CompoundTag, ListTag], key: Union[str, int], dtype: Type[AnyNBT]
290
- ) -> bool:
291
- """Check the key exists and the type is correct."""
292
- return key in obj and isinstance(obj[key], dtype)
293
-
294
- @overload
295
- def get_obj(
296
- self,
297
- obj: CompoundTag,
298
- key: str,
299
- dtype: Type[AnyNBT],
300
- default: Optional[AnyNBT] = None,
301
- ) -> Optional[AnyNBT]:
302
- ...
303
-
304
- @overload
305
- def get_obj(
306
- self,
307
- obj: ListTag,
308
- key: int,
309
- dtype: Type[AnyNBT],
310
- default: Optional[AnyNBT] = None,
311
- ) -> Optional[AnyNBT]:
312
- ...
313
-
314
- def get_obj(
315
- self, obj, key, dtype: Type[AnyNBT], default: Optional[AnyNBT] = None
316
- ) -> Optional[AnyNBT]:
317
- """Pop a key from a container object if it exists and the type is correct. Otherwise return default.
318
- This works in much the same way as dict.get but uses default if the data type does not match.
319
-
320
- :param obj: The CompoundTag to read from.
321
- :param key: The key to use.
322
- :param dtype: The expected data type.
323
- :param default: The default value to use if the existing is not valid. If None will use dtype()
324
- :return: The final value in the key.
325
- """
326
- if key in obj:
327
- if isinstance(obj[key], dtype):
328
- # if it exists and is correct
329
- return obj.pop(key)
330
- if default is None:
331
- return dtype()
332
- return default
333
-
334
- def get_nested_obj(
335
- self,
336
- obj: Union[CompoundTag, ListTag],
337
- path: Sequence[ # The path to the object.
338
- Tuple[Union[str, int], Type[AbstractBaseTag]],
339
- ],
340
- default: Union[None, AnyNBT, Callable[[], Any]] = None,
341
- *,
342
- pop_last=False,
343
- ):
344
- """
345
- Get an object from a nested NBT structure
346
-
347
- :param obj: The root NBT object
348
- :param path: The path to the desired object (key, dtype)
349
- :param default: The default value to use if the existing is not valid. If default is callable then return the called result.
350
- :param pop_last: If true the last key will be popped
351
- :return:
352
- """
353
- try:
354
- last_index = len(path) - 1
355
- for i, (key, dtype) in enumerate(path):
356
- if i == last_index and pop_last:
357
- obj = obj.pop(key)
358
- else:
359
- obj = obj[key]
360
- if dtype is not obj.__class__:
361
- raise TypeError
362
- except (KeyError, IndexError, TypeError):
363
- if default is None or isinstance(default, AbstractBaseTag):
364
- return default
365
- elif callable(default):
366
- return default()
367
- else:
368
- raise TypeError(
369
- "default must be None, an NBT instance or an NBT class."
370
- )
371
- else:
372
- return obj
373
-
374
- @staticmethod
375
- def set_obj(
376
- obj: CompoundTag,
377
- key: str,
378
- dtype: Type[AnyNBT],
379
- default: Union[None, AnyNBT, Callable[[], Any]] = None,
380
- path: Sequence[str] = (), # The path to the object.
381
- *,
382
- setdefault=False,
383
- ) -> AnyNBT:
384
- """
385
- Works like setdefualt on a dictionary but works with an optional nested path.
386
-
387
- :param obj: The compound tag to get the data from
388
- :param key: The key to setdefault
389
- :param dtype: The dtype that the key must be
390
- :param default: The default value to set if it does not exist or the type is wrong
391
- :param path: Optional path to the nested compound.
392
- :param setdefault: If True will behave like setdefault. If False will replace existing data.
393
- :return: The data at the path
394
- """
395
- for path_key in path:
396
- obj_ = obj.get(path_key, None)
397
- if not isinstance(obj_, CompoundTag):
398
- # if it does not exist or the type is wrong then create it
399
- obj_ = obj[path_key] = CompoundTag()
400
- obj = obj_
401
- obj_ = obj.get(key, None)
402
- if not setdefault or not isinstance(obj_, dtype):
403
- # if it does not exist or the type is wrong then create it
404
- if default is None:
405
- obj_ = dtype()
406
- elif isinstance(default, AbstractBaseTag):
407
- obj_ = default
408
- elif callable(default):
409
- obj_ = default()
410
- else:
411
- raise TypeError(
412
- "default must be None, an NBT instance or an NBT class."
413
- )
414
- obj[key] = obj_
415
- return obj_
416
-
417
- @abstractmethod
418
- def get_translator(
419
- self,
420
- max_world_version: VersionIdentifierType,
421
- data: Any = None,
422
- ) -> Tuple["Translator", VersionNumberAny]:
423
- """
424
- Get the Translator class for the requested version.
425
- :param max_world_version: The game version the world was last opened in. Version number tuple or data version number.
426
- :param data: Optional data to get translator based on chunk version rather than world version
427
- :return: Tuple[Translator, version number for PyMCTranslate to use]
428
- """
429
- raise NotImplementedError
430
-
431
- @staticmethod
432
- @abstractmethod
433
- def is_valid(key: Tuple) -> bool:
434
- """
435
- Returns whether this Interface is able to interface with the chunk type with a given identifier key,
436
- generated by the format.
437
-
438
- :param key: The key who's decodability needs to be checked.
439
- :return: True if this interface can interface with the chunk version associated with the key, False otherwise.
440
- """
441
- raise NotImplementedError
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import (
5
+ Tuple,
6
+ Any,
7
+ Union,
8
+ TYPE_CHECKING,
9
+ Optional,
10
+ overload,
11
+ Type,
12
+ Sequence,
13
+ Callable,
14
+ )
15
+ from enum import Enum
16
+
17
+ from amulet.api.block_entity import BlockEntity
18
+ from amulet.api.entity import Entity
19
+ from amulet.api.data_types import AnyNDArray, VersionNumberAny, VersionIdentifierType
20
+ from amulet_nbt import (
21
+ AbstractBaseTag,
22
+ ListTag,
23
+ CompoundTag,
24
+ AnyNBT,
25
+ NamedTag,
26
+ StringTag,
27
+ IntTag,
28
+ IntArrayTag,
29
+ DoubleTag,
30
+ FloatTag,
31
+ )
32
+
33
+ if TYPE_CHECKING:
34
+ from amulet.api.wrapper import Translator
35
+ from amulet.api.chunk import Chunk
36
+
37
+
38
+ class EntityIDType(Enum):
39
+ int_id = 0
40
+ str_id = 1
41
+ namespace_str_id = 2
42
+ namespace_str_Id = 3
43
+ namespace_str_identifier = 4
44
+
45
+
46
+ class EntityCoordType(Enum):
47
+ xyz_int = 0
48
+ Pos_list_float = 1
49
+ Pos_list_double = 2
50
+ Pos_list_int = 3
51
+ Pos_array_int = 4
52
+
53
+
54
+ PosTypeMap = {
55
+ 3: EntityCoordType.Pos_list_int,
56
+ 5: EntityCoordType.Pos_list_float,
57
+ 6: EntityCoordType.Pos_list_double,
58
+ }
59
+
60
+
61
+ class Interface(ABC):
62
+ @abstractmethod
63
+ def decode(self, *args, **kwargs) -> Tuple["Chunk", AnyNDArray]:
64
+ raise NotImplementedError
65
+
66
+ def _decode_entity(
67
+ self,
68
+ nbt: NamedTag,
69
+ id_type: EntityIDType,
70
+ coord_type: EntityCoordType,
71
+ ) -> Optional[Entity]:
72
+ entity = self._decode_base_entity(nbt, id_type, coord_type)
73
+ if entity is not None:
74
+ namespace, base_name, x, y, z, nbt = entity
75
+ return Entity(
76
+ namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
77
+ )
78
+
79
+ def _decode_block_entity(
80
+ self,
81
+ nbt: NamedTag,
82
+ id_type: EntityIDType,
83
+ coord_type: EntityCoordType,
84
+ ) -> Optional[BlockEntity]:
85
+ entity = self._decode_base_entity(nbt, id_type, coord_type)
86
+ if entity is not None:
87
+ namespace, base_name, x, y, z, nbt = entity
88
+ return BlockEntity(
89
+ namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
90
+ )
91
+
92
+ @staticmethod
93
+ def _decode_base_entity(
94
+ named_tag: NamedTag, id_type: EntityIDType, coord_type: EntityCoordType
95
+ ) -> Optional[
96
+ Tuple[
97
+ str,
98
+ str,
99
+ Union[int, float],
100
+ Union[int, float],
101
+ Union[int, float],
102
+ NamedTag,
103
+ ]
104
+ ]:
105
+ if not (
106
+ isinstance(named_tag, NamedTag) and isinstance(named_tag.tag, CompoundTag)
107
+ ):
108
+ return
109
+
110
+ nbt_tag = named_tag.tag
111
+
112
+ if id_type == EntityIDType.namespace_str_id:
113
+ entity_id = nbt_tag.pop("id", StringTag(""))
114
+ if (
115
+ not isinstance(entity_id, StringTag)
116
+ or entity_id.py_str == ""
117
+ or ":" not in entity_id.py_str
118
+ ):
119
+ return
120
+ namespace, base_name = entity_id.py_str.split(":", 1)
121
+
122
+ elif id_type == EntityIDType.namespace_str_Id:
123
+ entity_id = nbt_tag.pop("Id", StringTag(""))
124
+ if (
125
+ not isinstance(entity_id, StringTag)
126
+ or entity_id.py_str == ""
127
+ or ":" not in entity_id.py_str
128
+ ):
129
+ return
130
+ namespace, base_name = entity_id.py_str.split(":", 1)
131
+
132
+ elif id_type == EntityIDType.str_id:
133
+ entity_id = nbt_tag.pop("id", StringTag(""))
134
+ if not isinstance(entity_id, StringTag) or entity_id.py_str == "":
135
+ return
136
+ namespace = ""
137
+ base_name = entity_id.py_str
138
+
139
+ elif id_type in [EntityIDType.namespace_str_identifier, EntityIDType.int_id]:
140
+ if "identifier" in nbt_tag:
141
+ entity_id = nbt_tag.pop("identifier")
142
+ if (
143
+ not isinstance(entity_id, StringTag)
144
+ or entity_id.py_str == ""
145
+ or ":" not in entity_id.py_str
146
+ ):
147
+ return
148
+ namespace, base_name = entity_id.py_str.split(":", 1)
149
+ elif "id" in nbt_tag:
150
+ entity_id = nbt_tag.pop("id")
151
+ if not isinstance(entity_id, IntTag):
152
+ return
153
+ namespace = ""
154
+ base_name = str(entity_id.py_int)
155
+ else:
156
+ return
157
+ else:
158
+ raise NotImplementedError(f"Entity id type {id_type}")
159
+
160
+ if coord_type in [
161
+ EntityCoordType.Pos_list_double,
162
+ EntityCoordType.Pos_list_float,
163
+ EntityCoordType.Pos_list_int,
164
+ ]:
165
+ if "Pos" not in nbt_tag:
166
+ return
167
+ pos = nbt_tag.pop("Pos")
168
+ pos: ListTag
169
+
170
+ if (
171
+ not isinstance(pos, ListTag)
172
+ or len(pos) != 3
173
+ or PosTypeMap.get(pos.list_data_type) != coord_type
174
+ ):
175
+ return
176
+ x, y, z = [c.py_data for c in pos]
177
+ elif coord_type == EntityCoordType.Pos_array_int:
178
+ if "Pos" not in nbt_tag:
179
+ return
180
+ pos = nbt_tag.pop("Pos")
181
+ pos: IntArrayTag
182
+
183
+ if not isinstance(pos, IntArrayTag) or len(pos) != 3:
184
+ return
185
+ x, y, z = pos
186
+ elif coord_type == EntityCoordType.xyz_int:
187
+ if not all(
188
+ c in nbt_tag and isinstance(nbt_tag[c], IntTag) for c in ("x", "y", "z")
189
+ ):
190
+ return
191
+ x, y, z = [nbt_tag.pop(c).py_int for c in ("x", "y", "z")]
192
+ else:
193
+ raise NotImplementedError(f"Entity coord type {coord_type}")
194
+
195
+ return namespace, base_name, x, y, z, named_tag
196
+
197
+ @abstractmethod
198
+ def encode(self, *args, **kwargs) -> Any:
199
+ """
200
+ Take a version-specific chunk and encode it to raw data for the format to store.
201
+ """
202
+ raise NotImplementedError
203
+
204
+ def _encode_entity(
205
+ self, entity: Entity, id_type: EntityIDType, coord_type: EntityCoordType
206
+ ) -> Optional[NamedTag]:
207
+ return self._encode_base_entity(entity, id_type, coord_type)
208
+
209
+ def _encode_block_entity(
210
+ self, entity: BlockEntity, id_type: EntityIDType, coord_type: EntityCoordType
211
+ ) -> Optional[NamedTag]:
212
+ return self._encode_base_entity(entity, id_type, coord_type)
213
+
214
+ @staticmethod
215
+ def _encode_base_entity(
216
+ entity: Union[Entity, BlockEntity],
217
+ id_type: EntityIDType,
218
+ coord_type: EntityCoordType,
219
+ ) -> Optional[NamedTag]:
220
+ if not isinstance(entity.nbt, NamedTag) and isinstance(
221
+ entity.nbt.tag, CompoundTag
222
+ ):
223
+ return
224
+ named_tag = entity.nbt
225
+ tag = named_tag.compound
226
+
227
+ if id_type == EntityIDType.namespace_str_id:
228
+ tag["id"] = StringTag(entity.namespaced_name)
229
+ elif id_type == EntityIDType.namespace_str_Id:
230
+ tag["Id"] = StringTag(entity.namespaced_name)
231
+ elif id_type == EntityIDType.namespace_str_identifier:
232
+ tag["identifier"] = StringTag(entity.namespaced_name)
233
+ elif id_type == EntityIDType.str_id:
234
+ tag["id"] = StringTag(entity.base_name)
235
+ elif id_type == EntityIDType.int_id:
236
+ if not entity.base_name.isnumeric():
237
+ return
238
+ tag["id"] = IntTag(int(entity.base_name))
239
+ else:
240
+ raise NotImplementedError(f"Entity id type {id_type}")
241
+
242
+ if coord_type == EntityCoordType.Pos_list_double:
243
+ tag["Pos"] = ListTag(
244
+ [
245
+ DoubleTag(float(entity.x)),
246
+ DoubleTag(float(entity.y)),
247
+ DoubleTag(float(entity.z)),
248
+ ]
249
+ )
250
+ elif coord_type == EntityCoordType.Pos_list_float:
251
+ tag["Pos"] = ListTag(
252
+ [
253
+ FloatTag(float(entity.x)),
254
+ FloatTag(float(entity.y)),
255
+ FloatTag(float(entity.z)),
256
+ ]
257
+ )
258
+ elif coord_type == EntityCoordType.Pos_list_int:
259
+ tag["Pos"] = ListTag(
260
+ [
261
+ IntTag(int(entity.x)),
262
+ IntTag(int(entity.y)),
263
+ IntTag(int(entity.z)),
264
+ ]
265
+ )
266
+ elif coord_type == EntityCoordType.Pos_array_int:
267
+ tag["Pos"] = IntArrayTag([int(entity.x), int(entity.y), int(entity.z)])
268
+ elif coord_type == EntityCoordType.xyz_int:
269
+ tag["x"] = IntTag(int(entity.x))
270
+ tag["y"] = IntTag(int(entity.y))
271
+ tag["z"] = IntTag(int(entity.z))
272
+ else:
273
+ raise NotImplementedError(f"Entity coord type {coord_type}")
274
+
275
+ return named_tag
276
+
277
+ @overload
278
+ @staticmethod
279
+ def check_type(obj: CompoundTag, key: str, dtype: Type[AnyNBT]) -> bool:
280
+ ...
281
+
282
+ @overload
283
+ @staticmethod
284
+ def check_type(obj: ListTag, key: int, dtype: Type[AnyNBT]) -> bool:
285
+ ...
286
+
287
+ @staticmethod
288
+ def check_type(
289
+ obj: Union[CompoundTag, ListTag], key: Union[str, int], dtype: Type[AnyNBT]
290
+ ) -> bool:
291
+ """Check the key exists and the type is correct."""
292
+ return key in obj and isinstance(obj[key], dtype)
293
+
294
+ @overload
295
+ def get_obj(
296
+ self,
297
+ obj: CompoundTag,
298
+ key: str,
299
+ dtype: Type[AnyNBT],
300
+ default: Optional[AnyNBT] = None,
301
+ ) -> Optional[AnyNBT]:
302
+ ...
303
+
304
+ @overload
305
+ def get_obj(
306
+ self,
307
+ obj: ListTag,
308
+ key: int,
309
+ dtype: Type[AnyNBT],
310
+ default: Optional[AnyNBT] = None,
311
+ ) -> Optional[AnyNBT]:
312
+ ...
313
+
314
+ def get_obj(
315
+ self, obj, key, dtype: Type[AnyNBT], default: Optional[AnyNBT] = None
316
+ ) -> Optional[AnyNBT]:
317
+ """Pop a key from a container object if it exists and the type is correct. Otherwise return default.
318
+ This works in much the same way as dict.get but uses default if the data type does not match.
319
+
320
+ :param obj: The CompoundTag to read from.
321
+ :param key: The key to use.
322
+ :param dtype: The expected data type.
323
+ :param default: The default value to use if the existing is not valid. If None will use dtype()
324
+ :return: The final value in the key.
325
+ """
326
+ if key in obj:
327
+ if isinstance(obj[key], dtype):
328
+ # if it exists and is correct
329
+ return obj.pop(key)
330
+ if default is None:
331
+ return dtype()
332
+ return default
333
+
334
+ def get_nested_obj(
335
+ self,
336
+ obj: Union[CompoundTag, ListTag],
337
+ path: Sequence[ # The path to the object.
338
+ Tuple[Union[str, int], Type[AbstractBaseTag]],
339
+ ],
340
+ default: Union[None, AnyNBT, Callable[[], Any]] = None,
341
+ *,
342
+ pop_last=False,
343
+ ):
344
+ """
345
+ Get an object from a nested NBT structure
346
+
347
+ :param obj: The root NBT object
348
+ :param path: The path to the desired object (key, dtype)
349
+ :param default: The default value to use if the existing is not valid. If default is callable then return the called result.
350
+ :param pop_last: If true the last key will be popped
351
+ :return:
352
+ """
353
+ try:
354
+ last_index = len(path) - 1
355
+ for i, (key, dtype) in enumerate(path):
356
+ if i == last_index and pop_last:
357
+ obj = obj.pop(key)
358
+ else:
359
+ obj = obj[key]
360
+ if dtype is not obj.__class__:
361
+ raise TypeError
362
+ except (KeyError, IndexError, TypeError):
363
+ if default is None or isinstance(default, AbstractBaseTag):
364
+ return default
365
+ elif callable(default):
366
+ return default()
367
+ else:
368
+ raise TypeError(
369
+ "default must be None, an NBT instance or an NBT class."
370
+ )
371
+ else:
372
+ return obj
373
+
374
+ @staticmethod
375
+ def set_obj(
376
+ obj: CompoundTag,
377
+ key: str,
378
+ dtype: Type[AnyNBT],
379
+ default: Union[None, AnyNBT, Callable[[], Any]] = None,
380
+ path: Sequence[str] = (), # The path to the object.
381
+ *,
382
+ setdefault=False,
383
+ ) -> AnyNBT:
384
+ """
385
+ Works like setdefualt on a dictionary but works with an optional nested path.
386
+
387
+ :param obj: The compound tag to get the data from
388
+ :param key: The key to setdefault
389
+ :param dtype: The dtype that the key must be
390
+ :param default: The default value to set if it does not exist or the type is wrong
391
+ :param path: Optional path to the nested compound.
392
+ :param setdefault: If True will behave like setdefault. If False will replace existing data.
393
+ :return: The data at the path
394
+ """
395
+ for path_key in path:
396
+ obj_ = obj.get(path_key, None)
397
+ if not isinstance(obj_, CompoundTag):
398
+ # if it does not exist or the type is wrong then create it
399
+ obj_ = obj[path_key] = CompoundTag()
400
+ obj = obj_
401
+ obj_ = obj.get(key, None)
402
+ if not setdefault or not isinstance(obj_, dtype):
403
+ # if it does not exist or the type is wrong then create it
404
+ if default is None:
405
+ obj_ = dtype()
406
+ elif isinstance(default, AbstractBaseTag):
407
+ obj_ = default
408
+ elif callable(default):
409
+ obj_ = default()
410
+ else:
411
+ raise TypeError(
412
+ "default must be None, an NBT instance or an NBT class."
413
+ )
414
+ obj[key] = obj_
415
+ return obj_
416
+
417
+ @abstractmethod
418
+ def get_translator(
419
+ self,
420
+ max_world_version: VersionIdentifierType,
421
+ data: Any = None,
422
+ ) -> Tuple["Translator", VersionNumberAny]:
423
+ """
424
+ Get the Translator class for the requested version.
425
+ :param max_world_version: The game version the world was last opened in. Version number tuple or data version number.
426
+ :param data: Optional data to get translator based on chunk version rather than world version
427
+ :return: Tuple[Translator, version number for PyMCTranslate to use]
428
+ """
429
+ raise NotImplementedError
430
+
431
+ @staticmethod
432
+ @abstractmethod
433
+ def is_valid(key: Tuple) -> bool:
434
+ """
435
+ Returns whether this Interface is able to interface with the chunk type with a given identifier key,
436
+ generated by the format.
437
+
438
+ :param key: The key who's decodability needs to be checked.
439
+ :return: True if this interface can interface with the chunk version associated with the key, False otherwise.
440
+ """
441
+ raise NotImplementedError