amulet-core 2.0a5__cp311-cp311-macosx_10_9_universal2.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 (210) hide show
  1. amulet/__init__.cpython-311-darwin.so +0 -0
  2. amulet/__init__.pyi +30 -0
  3. amulet/__pyinstaller/__init__.py +2 -0
  4. amulet/__pyinstaller/hook-amulet.py +4 -0
  5. amulet/_init.py +28 -0
  6. amulet/_version.py +21 -0
  7. amulet/biome.cpp +36 -0
  8. amulet/biome.hpp +43 -0
  9. amulet/biome.pyi +77 -0
  10. amulet/block.cpp +435 -0
  11. amulet/block.hpp +119 -0
  12. amulet/block.pyi +273 -0
  13. amulet/block_entity.cpp +12 -0
  14. amulet/block_entity.hpp +56 -0
  15. amulet/block_entity.pyi +80 -0
  16. amulet/chunk.cpp +16 -0
  17. amulet/chunk.hpp +99 -0
  18. amulet/chunk.pyi +30 -0
  19. amulet/chunk_/components/biome.py +155 -0
  20. amulet/chunk_/components/block_entity.py +117 -0
  21. amulet/chunk_/components/entity.py +64 -0
  22. amulet/chunk_/components/height_2d.py +16 -0
  23. amulet/chunk_components.pyi +95 -0
  24. amulet/collections.pyi +37 -0
  25. amulet/data_types.py +29 -0
  26. amulet/entity.py +180 -0
  27. amulet/errors.py +63 -0
  28. amulet/game/__init__.py +7 -0
  29. amulet/game/_game.py +152 -0
  30. amulet/game/_universal/__init__.py +1 -0
  31. amulet/game/_universal/_biome.py +17 -0
  32. amulet/game/_universal/_block.py +47 -0
  33. amulet/game/_universal/_version.py +68 -0
  34. amulet/game/abc/__init__.py +22 -0
  35. amulet/game/abc/_block_specification.py +150 -0
  36. amulet/game/abc/biome.py +213 -0
  37. amulet/game/abc/block.py +331 -0
  38. amulet/game/abc/game_version_container.py +25 -0
  39. amulet/game/abc/json_interface.py +27 -0
  40. amulet/game/abc/version.py +44 -0
  41. amulet/game/bedrock/__init__.py +1 -0
  42. amulet/game/bedrock/_biome.py +35 -0
  43. amulet/game/bedrock/_block.py +42 -0
  44. amulet/game/bedrock/_version.py +165 -0
  45. amulet/game/java/__init__.py +2 -0
  46. amulet/game/java/_biome.py +35 -0
  47. amulet/game/java/_block.py +60 -0
  48. amulet/game/java/_version.py +176 -0
  49. amulet/game/translate/__init__.py +12 -0
  50. amulet/game/translate/_functions/__init__.py +15 -0
  51. amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
  52. amulet/game/translate/_functions/_code_functions/_text.py +553 -0
  53. amulet/game/translate/_functions/_code_functions/banner_pattern.py +67 -0
  54. amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +152 -0
  55. amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +88 -0
  56. amulet/game/translate/_functions/_code_functions/bedrock_sign.py +152 -0
  57. amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +16 -0
  58. amulet/game/translate/_functions/_code_functions/custom_name.py +146 -0
  59. amulet/game/translate/_functions/_frozen.py +66 -0
  60. amulet/game/translate/_functions/_state.py +54 -0
  61. amulet/game/translate/_functions/_typing.py +98 -0
  62. amulet/game/translate/_functions/abc.py +116 -0
  63. amulet/game/translate/_functions/carry_nbt.py +160 -0
  64. amulet/game/translate/_functions/carry_properties.py +80 -0
  65. amulet/game/translate/_functions/code.py +143 -0
  66. amulet/game/translate/_functions/map_block_name.py +66 -0
  67. amulet/game/translate/_functions/map_nbt.py +111 -0
  68. amulet/game/translate/_functions/map_properties.py +93 -0
  69. amulet/game/translate/_functions/multiblock.py +112 -0
  70. amulet/game/translate/_functions/new_block.py +42 -0
  71. amulet/game/translate/_functions/new_entity.py +43 -0
  72. amulet/game/translate/_functions/new_nbt.py +206 -0
  73. amulet/game/translate/_functions/new_properties.py +64 -0
  74. amulet/game/translate/_functions/sequence.py +51 -0
  75. amulet/game/translate/_functions/walk_input_nbt.py +331 -0
  76. amulet/game/translate/_translator.py +433 -0
  77. amulet/item.py +75 -0
  78. amulet/level/__init__.pyi +27 -0
  79. amulet/level/_load.py +100 -0
  80. amulet/level/abc/__init__.py +12 -0
  81. amulet/level/abc/_chunk_handle.py +335 -0
  82. amulet/level/abc/_dimension.py +86 -0
  83. amulet/level/abc/_history/__init__.py +1 -0
  84. amulet/level/abc/_history/_cache.py +224 -0
  85. amulet/level/abc/_history/_history_manager.py +291 -0
  86. amulet/level/abc/_level/__init__.py +5 -0
  87. amulet/level/abc/_level/_compactable_level.py +10 -0
  88. amulet/level/abc/_level/_creatable_level.py +29 -0
  89. amulet/level/abc/_level/_disk_level.py +17 -0
  90. amulet/level/abc/_level/_level.py +453 -0
  91. amulet/level/abc/_level/_loadable_level.py +42 -0
  92. amulet/level/abc/_player_storage.py +7 -0
  93. amulet/level/abc/_raw_level.py +187 -0
  94. amulet/level/abc/_registry.py +40 -0
  95. amulet/level/bedrock/__init__.py +2 -0
  96. amulet/level/bedrock/_chunk_handle.py +19 -0
  97. amulet/level/bedrock/_dimension.py +22 -0
  98. amulet/level/bedrock/_level.py +187 -0
  99. amulet/level/bedrock/_raw/__init__.py +5 -0
  100. amulet/level/bedrock/_raw/_actor_counter.py +53 -0
  101. amulet/level/bedrock/_raw/_chunk.py +54 -0
  102. amulet/level/bedrock/_raw/_chunk_decode.py +668 -0
  103. amulet/level/bedrock/_raw/_chunk_encode.py +602 -0
  104. amulet/level/bedrock/_raw/_constant.py +9 -0
  105. amulet/level/bedrock/_raw/_dimension.py +343 -0
  106. amulet/level/bedrock/_raw/_level.py +463 -0
  107. amulet/level/bedrock/_raw/_level_dat.py +90 -0
  108. amulet/level/bedrock/_raw/_typing.py +6 -0
  109. amulet/level/bedrock/_raw/leveldb_chunk_versions.py +83 -0
  110. amulet/level/bedrock/chunk/__init__.py +1 -0
  111. amulet/level/bedrock/chunk/_chunk.py +126 -0
  112. amulet/level/bedrock/chunk/components/__init__.py +0 -0
  113. amulet/level/bedrock/chunk/components/chunk_version.py +12 -0
  114. amulet/level/bedrock/chunk/components/finalised_state.py +13 -0
  115. amulet/level/bedrock/chunk/components/raw_chunk.py +15 -0
  116. amulet/level/construction/__init__.py +0 -0
  117. amulet/level/java/__init__.pyi +21 -0
  118. amulet/level/java/_chunk_handle.py +17 -0
  119. amulet/level/java/_chunk_handle.pyi +15 -0
  120. amulet/level/java/_dimension.py +20 -0
  121. amulet/level/java/_dimension.pyi +13 -0
  122. amulet/level/java/_level.py +184 -0
  123. amulet/level/java/_level.pyi +120 -0
  124. amulet/level/java/_raw/__init__.pyi +19 -0
  125. amulet/level/java/_raw/_chunk.pyi +23 -0
  126. amulet/level/java/_raw/_chunk_decode.py +561 -0
  127. amulet/level/java/_raw/_chunk_encode.py +463 -0
  128. amulet/level/java/_raw/_constant.py +9 -0
  129. amulet/level/java/_raw/_constant.pyi +20 -0
  130. amulet/level/java/_raw/_data_pack/__init__.py +2 -0
  131. amulet/level/java/_raw/_data_pack/__init__.pyi +8 -0
  132. amulet/level/java/_raw/_data_pack/data_pack.py +241 -0
  133. amulet/level/java/_raw/_data_pack/data_pack.pyi +197 -0
  134. amulet/level/java/_raw/_data_pack/data_pack_manager.py +77 -0
  135. amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +75 -0
  136. amulet/level/java/_raw/_dimension.py +86 -0
  137. amulet/level/java/_raw/_dimension.pyi +72 -0
  138. amulet/level/java/_raw/_level.py +507 -0
  139. amulet/level/java/_raw/_level.pyi +238 -0
  140. amulet/level/java/_raw/_typing.py +3 -0
  141. amulet/level/java/_raw/_typing.pyi +5 -0
  142. amulet/level/java/anvil/__init__.py +2 -0
  143. amulet/level/java/anvil/__init__.pyi +11 -0
  144. amulet/level/java/anvil/_dimension.py +170 -0
  145. amulet/level/java/anvil/_dimension.pyi +109 -0
  146. amulet/level/java/anvil/_region.py +421 -0
  147. amulet/level/java/anvil/_region.pyi +197 -0
  148. amulet/level/java/anvil/_sector_manager.py +223 -0
  149. amulet/level/java/anvil/_sector_manager.pyi +142 -0
  150. amulet/level/java/chunk.pyi +81 -0
  151. amulet/level/java/chunk_/_chunk.py +260 -0
  152. amulet/level/java/chunk_/components/inhabited_time.py +12 -0
  153. amulet/level/java/chunk_/components/last_update.py +12 -0
  154. amulet/level/java/chunk_/components/legacy_version.py +12 -0
  155. amulet/level/java/chunk_/components/light_populated.py +12 -0
  156. amulet/level/java/chunk_/components/named_height_2d.py +37 -0
  157. amulet/level/java/chunk_/components/status.py +11 -0
  158. amulet/level/java/chunk_/components/terrain_populated.py +12 -0
  159. amulet/level/java/chunk_components.pyi +22 -0
  160. amulet/level/java/long_array.pyi +38 -0
  161. amulet/level/java_forge/__init__.py +0 -0
  162. amulet/level/mcstructure/__init__.py +0 -0
  163. amulet/level/nbt/__init__.py +0 -0
  164. amulet/level/schematic/__init__.py +0 -0
  165. amulet/level/sponge_schematic/__init__.py +0 -0
  166. amulet/level/temporary_level/__init__.py +1 -0
  167. amulet/level/temporary_level/_level.py +16 -0
  168. amulet/palette/__init__.pyi +8 -0
  169. amulet/palette/biome_palette.pyi +45 -0
  170. amulet/palette/block_palette.pyi +45 -0
  171. amulet/player.py +64 -0
  172. amulet/py.typed +0 -0
  173. amulet/selection/__init__.py +2 -0
  174. amulet/selection/abstract_selection.py +342 -0
  175. amulet/selection/box.py +852 -0
  176. amulet/selection/group.py +481 -0
  177. amulet/utils/__init__.pyi +28 -0
  178. amulet/utils/call_spec/__init__.py +24 -0
  179. amulet/utils/call_spec/__init__.pyi +53 -0
  180. amulet/utils/call_spec/_call_spec.py +262 -0
  181. amulet/utils/call_spec/_call_spec.pyi +272 -0
  182. amulet/utils/format_utils.py +41 -0
  183. amulet/utils/generator.py +18 -0
  184. amulet/utils/matrix.py +243 -0
  185. amulet/utils/matrix.pyi +177 -0
  186. amulet/utils/numpy.pyi +11 -0
  187. amulet/utils/numpy_helpers.py +19 -0
  188. amulet/utils/shareable_lock.py +335 -0
  189. amulet/utils/shareable_lock.pyi +190 -0
  190. amulet/utils/signal/__init__.py +10 -0
  191. amulet/utils/signal/__init__.pyi +25 -0
  192. amulet/utils/signal/_signal.py +228 -0
  193. amulet/utils/signal/_signal.pyi +84 -0
  194. amulet/utils/task_manager.py +235 -0
  195. amulet/utils/task_manager.pyi +168 -0
  196. amulet/utils/typed_property.py +111 -0
  197. amulet/utils/typing.py +4 -0
  198. amulet/utils/typing.pyi +6 -0
  199. amulet/utils/weakref.py +70 -0
  200. amulet/utils/weakref.pyi +50 -0
  201. amulet/utils/world_utils.py +102 -0
  202. amulet/utils/world_utils.pyi +109 -0
  203. amulet/version.cpp +136 -0
  204. amulet/version.hpp +142 -0
  205. amulet/version.pyi +94 -0
  206. amulet_core-2.0a5.dist-info/METADATA +103 -0
  207. amulet_core-2.0a5.dist-info/RECORD +210 -0
  208. amulet_core-2.0a5.dist-info/WHEEL +5 -0
  209. amulet_core-2.0a5.dist-info/entry_points.txt +2 -0
  210. amulet_core-2.0a5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,213 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Mapping, Collection
6
+ import os
7
+ import json
8
+
9
+ from amulet.biome import Biome
10
+ from amulet.version import VersionNumber
11
+ from amulet.game import get_game_version
12
+
13
+ from .game_version_container import GameVersionContainer
14
+
15
+
16
+ if TYPE_CHECKING:
17
+ from .version import GameVersion
18
+
19
+
20
+ class BiomeTranslationError(Exception):
21
+ """An exception raised if the biome could not be translated."""
22
+
23
+
24
+ class BiomeData(GameVersionContainer, ABC):
25
+ _biomes: Mapping[str, Collection[str]]
26
+
27
+ def __init__(
28
+ self, game_version: GameVersion, biomes: Mapping[str, Collection[str]]
29
+ ):
30
+ super().__init__(game_version)
31
+ self._biomes = {
32
+ namespace: tuple(base_names) for namespace, base_names in biomes.items()
33
+ }
34
+
35
+ def __getstate__(self) -> dict:
36
+ state = super().__getstate__()
37
+ state["_biomes"] = self._biomes
38
+ return state
39
+
40
+ def __setstate__(self, state: dict) -> None:
41
+ super().__setstate__(state)
42
+ self._biomes = state["_biomes"]
43
+
44
+ def namespaces(self) -> Collection[str]:
45
+ """An iterable of all the valid biome namespaces."""
46
+ return self._biomes.keys()
47
+
48
+ def base_names(self, namespace: str) -> Collection[str]:
49
+ """An iterable of all valid base names for the given namespace."""
50
+ return self._biomes[namespace]
51
+
52
+ def translate(
53
+ self, target_platform: str, target_version: VersionNumber, biome: Biome
54
+ ) -> Biome:
55
+ """Translate a biome from this version to the target version specified.
56
+
57
+ :param target_platform: The game platform to convert to.
58
+ :param target_version: The game version number to convert to.
59
+ :param biome: The biome to translate.
60
+ :return: The biome converted to the output version.
61
+ :raises:
62
+ ValueError: The arguments are incorrect. You did something wrong.
63
+ BlockTranslationError: The translator is not aware of the biome. You should handle a sensible default.
64
+ """
65
+ target_game_version = get_game_version(target_platform, target_version)
66
+ universal_biome = self.to_universal(biome)
67
+ return target_game_version.biome.from_universal(
68
+ target_platform, target_version, universal_biome
69
+ )
70
+
71
+ @abstractmethod
72
+ def to_universal(self, biome: Biome) -> Biome:
73
+ """Convert a biome to the universal format.
74
+
75
+ This method should be considered private.
76
+
77
+ :meta private:
78
+ :param biome: The biome to translate.
79
+ :return: The biome converted to the universal version.
80
+ :raises:
81
+ ValueError: The arguments are incorrect. You did something wrong.
82
+ BlockTranslationError: The translator is not aware of the biome. You should handle a sensible default.
83
+ """
84
+ raise NotImplementedError
85
+
86
+ @abstractmethod
87
+ def from_universal(
88
+ self, target_platform: str, target_version: VersionNumber, biome: Biome
89
+ ) -> Biome:
90
+ """Convert a biome from the universal format.
91
+
92
+ This method should be considered private.
93
+
94
+ :meta private:
95
+ :param target_platform: The game platform to convert to.
96
+ :param target_version: The game version number to convert to.
97
+ :param biome: The biome to translate.
98
+ :return: The biome converted to this version.
99
+ :raises:
100
+ ValueError: The arguments are incorrect. You did something wrong.
101
+ BlockTranslationError: The translator is not aware of the biome. You should handle a sensible default.
102
+ """
103
+ raise NotImplementedError
104
+
105
+
106
+ class DatabaseBiomeData(BiomeData):
107
+ _to_universal: Mapping[tuple[str, str], Biome]
108
+ _from_universal: Mapping[Biome, tuple[str, str]]
109
+
110
+ def __init__(
111
+ self,
112
+ game_version: GameVersion,
113
+ biomes: Mapping[str, Collection[str]],
114
+ to_universal: Mapping[tuple[str, str], Biome],
115
+ from_universal: Mapping[Biome, tuple[str, str]],
116
+ ):
117
+ super().__init__(game_version, biomes)
118
+ self._to_universal = to_universal
119
+ self._from_universal = from_universal
120
+
121
+ def __getstate__(self) -> dict:
122
+ state = super().__getstate__()
123
+ state["_to_universal"] = self._to_universal
124
+ state["_from_universal"] = self._from_universal
125
+ return state
126
+
127
+ def __setstate__(self, state: dict) -> None:
128
+ super().__setstate__(state)
129
+ self._to_universal = state["_to_universal"]
130
+ self._from_universal = state["_from_universal"]
131
+
132
+ def to_universal(self, biome: Biome) -> Biome:
133
+ if not self._game_version.supports_version(biome.platform, biome.version):
134
+ raise ValueError("The biome is not compatible with this version")
135
+
136
+ try:
137
+ return self._to_universal[(biome.namespace, biome.base_name)]
138
+ except KeyError:
139
+ raise BiomeTranslationError(
140
+ f"Biome {biome} does not exist in version {self._game_version.platform} {self._game_version.min_version}"
141
+ )
142
+
143
+ def from_universal(
144
+ self, target_platform: str, target_version: VersionNumber, biome: Biome
145
+ ) -> Biome:
146
+ if not self._game_version.supports_version(target_platform, target_version):
147
+ raise ValueError("The target version is not compatible with this version")
148
+
149
+ if biome.platform != "universal":
150
+ raise ValueError("The source biome is not in the universal format")
151
+
152
+ try:
153
+ namespace, base_name = self._from_universal[biome]
154
+ except KeyError:
155
+ raise BiomeTranslationError(
156
+ f"Biome {biome} does not exist in version {self._game_version.platform} {self._game_version.min_version}"
157
+ )
158
+ else:
159
+ return Biome(target_platform, target_version, namespace, base_name)
160
+
161
+
162
+ class BiomeDataNumericalComponent(ABC):
163
+ @abstractmethod
164
+ def numerical_id_to_namespace_id(self, numerical_id: int) -> tuple[str, str]:
165
+ """Convert the numerical id to its namespace id"""
166
+ raise NotImplementedError
167
+
168
+ @abstractmethod
169
+ def namespace_id_to_numerical_id(self, namespace: str, base_name: str) -> int:
170
+ raise NotImplementedError
171
+
172
+
173
+ def load_json_biome_data(
174
+ version_path: str,
175
+ ) -> tuple[
176
+ dict[tuple[str, str], int | None],
177
+ dict[tuple[str, str], tuple[str, str]],
178
+ dict[tuple[str, str], tuple[str, str]],
179
+ ]:
180
+ with open(os.path.join(version_path, "__biome_data__.json")) as f:
181
+ data = json.load(f)
182
+
183
+ biomes = dict[tuple[str, str], int | None]()
184
+ to_universal = dict[tuple[str, str], tuple[str, str]]()
185
+ from_universal = dict[tuple[str, str], tuple[str, str]]()
186
+
187
+ for biome_str, biome_int in data["int_map"].items():
188
+ assert isinstance(biome_str, str)
189
+ assert isinstance(biome_int, int) or biome_int is None
190
+ namespace, base_name = biome_str.split(":", 1)
191
+ biomes[(namespace, base_name)] = biome_int
192
+
193
+ for biome_str, universal_biome_str in data["version2universal"].items():
194
+ assert isinstance(biome_str, str)
195
+ assert isinstance(universal_biome_str, str)
196
+ namespace, base_name = biome_str.split(":", 1)
197
+ universal_namespace, universal_base_name = biome_str.split(":", 1)
198
+ to_universal[(namespace, base_name)] = (
199
+ universal_namespace,
200
+ universal_base_name,
201
+ )
202
+
203
+ for universal_biome_str, biome_str in data["universal2version"].items():
204
+ assert isinstance(biome_str, str)
205
+ assert isinstance(universal_biome_str, str)
206
+ namespace, base_name = biome_str.split(":", 1)
207
+ universal_namespace, universal_base_name = biome_str.split(":", 1)
208
+ from_universal[(universal_namespace, universal_base_name)] = (
209
+ namespace,
210
+ base_name,
211
+ )
212
+
213
+ return biomes, to_universal, from_universal
@@ -0,0 +1,331 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Callable, TYPE_CHECKING, TypeVar
5
+ from collections.abc import Mapping, Collection
6
+ from copy import deepcopy
7
+
8
+ from amulet.block import Block
9
+ from amulet.block_entity import BlockEntity
10
+ from amulet.entity import Entity
11
+ from amulet.data_types import BlockCoordinates
12
+ from amulet.version import VersionNumber
13
+ from amulet.game import get_game_version
14
+
15
+ from ._block_specification import BlockSpec
16
+ from .game_version_container import GameVersionContainer
17
+
18
+ T = TypeVar("T")
19
+
20
+ if TYPE_CHECKING:
21
+ from .version import GameVersion
22
+ from amulet.game.translate import (
23
+ BlockToUniversalTranslator,
24
+ BlockFromUniversalTranslator,
25
+ )
26
+
27
+
28
+ class BlockTranslationError(Exception):
29
+ """An exception raised if the block could not be translated."""
30
+
31
+
32
+ class BlockData(GameVersionContainer, ABC):
33
+ def __init__(
34
+ self,
35
+ game_version: GameVersion,
36
+ specification: Mapping[str, Mapping[str, BlockSpec]],
37
+ ) -> None:
38
+ super().__init__(game_version)
39
+ self._spec = specification
40
+
41
+ def __getstate__(self) -> dict:
42
+ state = super().__getstate__()
43
+ state["_spec"] = self._spec
44
+ return state
45
+
46
+ def __setstate__(self, state: dict) -> None:
47
+ super().__setstate__(state)
48
+ self._spec = state["_spec"]
49
+
50
+ def namespaces(self) -> Collection[str]:
51
+ """An iterable of all the valid block namespaces."""
52
+ return self._spec.keys()
53
+
54
+ def base_names(self, namespace: str) -> Collection[str]:
55
+ """An iterable of all valid base names for the given namespace."""
56
+ return self._spec[namespace].keys()
57
+
58
+ def get_specification(self, namespace: str, base_name: str) -> BlockSpec:
59
+ return self._spec[namespace][base_name]
60
+
61
+ def translate(
62
+ self,
63
+ target_platform: str,
64
+ target_version: VersionNumber,
65
+ block: Block,
66
+ block_entity: BlockEntity | None = None,
67
+ extra: (
68
+ tuple[
69
+ BlockCoordinates,
70
+ Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
71
+ ]
72
+ | None
73
+ ) = None,
74
+ ) -> tuple[Block, BlockEntity | None, bool] | tuple[Entity, None, bool]:
75
+ """Translate a block from this version to the target version specified.
76
+
77
+ :param target_platform: The game platform to convert to.
78
+ :param target_version: The game version number to convert to.
79
+ :param block: The block to translate
80
+ :param block_entity: An optional block entity related to the block input
81
+ :param extra: An optional tuple containing the absolute coordinate of the block in the world and a callback
82
+ function taking a relative coordinate and returning the block and block entity at that coordinate.
83
+ This is required for cases where the neighbour block is required to fully define the state.
84
+ If the bool in the output is True this is required to fully define the translation.
85
+ :return: There are two formats that can be returned.
86
+ The first is a Block, optional BlockEntity and a bool.
87
+ The second is an Entity, None and a bool.
88
+ The bool specifies if block_location and get_block_callback are required to fully define the output data.
89
+ :raises:
90
+ ValueError: The arguments are incorrect. You did something wrong.
91
+ BlockTranslationError: The translator is not aware of the block. You should handle a sensible default.
92
+ """
93
+ target_game_version = get_game_version(target_platform, target_version)
94
+ universal_block, universal_block_entity, extra_needed = self.to_universal(
95
+ block, block_entity, extra
96
+ )
97
+ (
98
+ target_obj,
99
+ target_block_entity,
100
+ extra_needed2,
101
+ ) = target_game_version.block.from_universal(
102
+ target_platform,
103
+ target_version,
104
+ universal_block,
105
+ universal_block_entity,
106
+ extra,
107
+ )
108
+ if isinstance(target_obj, Block):
109
+ return target_obj, target_block_entity, extra_needed or extra_needed2
110
+ elif isinstance(target_obj, Entity):
111
+ return target_obj, None, extra_needed or extra_needed2
112
+ else:
113
+ raise RuntimeError
114
+
115
+ @abstractmethod
116
+ def to_universal(
117
+ self,
118
+ block: Block,
119
+ block_entity: BlockEntity | None,
120
+ extra: (
121
+ tuple[
122
+ BlockCoordinates,
123
+ Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
124
+ ]
125
+ | None
126
+ ),
127
+ ) -> tuple[Block, BlockEntity | None, bool]:
128
+ """Convert a block to the universal format.
129
+
130
+ This method should be considered private.
131
+
132
+ :meta private:
133
+ :param block: The block to translate
134
+ :param block_entity: An optional block entity related to the block input
135
+ :param extra: An optional tuple containing the absolute coordinate of the block in the world and a callback
136
+ function taking a relative coordinate and returning the block and block entity at that coordinate.
137
+ This is required for cases where the neighbour block is required to fully define the state.
138
+ If the bool in the output is True this is required to fully define the translation.
139
+ :return: A Block, optional BlockEntity and a bool.
140
+ If the bool is True, the extra parameter is required to fully define the output data.
141
+ :raises:
142
+ ValueError: The arguments are incorrect. You did something wrong.
143
+ BlockTranslationError: The translator is not aware of the block. You should handle a sensible default.
144
+ """
145
+ raise NotImplementedError
146
+
147
+ @abstractmethod
148
+ def from_universal(
149
+ self,
150
+ target_platform: str,
151
+ target_version: VersionNumber,
152
+ block: Block,
153
+ block_entity: BlockEntity | None,
154
+ extra: (
155
+ tuple[
156
+ BlockCoordinates,
157
+ Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
158
+ ]
159
+ | None
160
+ ),
161
+ ) -> tuple[Block, BlockEntity | None, bool] | tuple[Entity, None, bool]:
162
+ """Convert a block from the universal format.
163
+
164
+ This method should be considered private.
165
+
166
+ :meta private:
167
+ :param target_platform: The game platform to convert to.
168
+ :param target_version: The game version number to convert to.
169
+ :param block: The block to translate
170
+ :param block_entity: An optional block entity related to the block input
171
+ :param extra: An optional tuple containing the absolute coordinate of the block in the world and a callback
172
+ function taking a relative coordinate and returning the block and block entity at that coordinate.
173
+ This is required for cases where the neighbour block is required to fully define the state.
174
+ If the bool in the output is True this is required to fully define the translation.
175
+ :return: There are two formats that can be returned.
176
+ Block, optional BlockEntity and a bool.
177
+ Entity, None and a bool.
178
+ If the bool is True, the extra parameter is required to fully define the output data.
179
+ :raises:
180
+ ValueError: The arguments are incorrect. You did something wrong.
181
+ BlockTranslationError: The translator is not aware of the block. You should handle a sensible default.
182
+ """
183
+ raise NotImplementedError
184
+
185
+
186
+ class DatabaseBlockData(BlockData, ABC):
187
+ _to_universal: Mapping[tuple[str, str], BlockToUniversalTranslator]
188
+ _from_universal: Mapping[tuple[str, str], BlockFromUniversalTranslator]
189
+ _to_universal_cache: dict[Block, tuple[Block, BlockEntity | None, bool]]
190
+ _from_universal_cache: dict[
191
+ tuple[Block, VersionNumber],
192
+ tuple[Block, BlockEntity | None, bool] | tuple[Entity, None, bool],
193
+ ]
194
+
195
+ def __init__(
196
+ self,
197
+ game_version: GameVersion,
198
+ specification: Mapping[str, Mapping[str, BlockSpec]],
199
+ to_universal: Mapping[tuple[str, str], BlockToUniversalTranslator],
200
+ from_universal: Mapping[tuple[str, str], BlockFromUniversalTranslator],
201
+ ) -> None:
202
+ super().__init__(game_version, specification)
203
+ self._to_universal = to_universal
204
+ self._from_universal = from_universal
205
+ # Cache computed results so we don't need to recompute unnecessarily.
206
+ self._to_universal_cache = {}
207
+ self._from_universal_cache = {}
208
+
209
+ def __getstate__(self) -> dict:
210
+ state = super().__getstate__()
211
+ state["_to_universal"] = self._to_universal
212
+ state["_from_universal"] = self._from_universal
213
+ return state
214
+
215
+ def __setstate__(self, state: dict) -> None:
216
+ super().__setstate__(state)
217
+ self._to_universal = state["_to_universal"]
218
+ self._from_universal = state["_from_universal"]
219
+ self._to_universal_cache = {}
220
+ self._from_universal_cache = {}
221
+
222
+ def to_universal(
223
+ self,
224
+ block: Block,
225
+ block_entity: BlockEntity | None,
226
+ extra: (
227
+ tuple[
228
+ BlockCoordinates,
229
+ Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
230
+ ]
231
+ | None
232
+ ),
233
+ ) -> tuple[Block, BlockEntity | None, bool]:
234
+ if not self._game_version.supports_version(block.platform, block.version):
235
+ raise ValueError("The block is not compatible with this version")
236
+
237
+ if block_entity is None:
238
+ if block in self._to_universal_cache:
239
+ output, extra_output, extra_needed = self._to_universal_cache[block]
240
+ return output, deepcopy(extra_output), extra_needed
241
+ else:
242
+ block_entity = deepcopy(block_entity)
243
+
244
+ try:
245
+ translator = self._to_universal[(block.namespace, block.base_name)]
246
+ except KeyError:
247
+ raise BlockTranslationError(
248
+ f"Block {block} does not exist in version {self._game_version.platform} {self._game_version.min_version}"
249
+ )
250
+
251
+ output, extra_output, extra_needed, cacheable = translator.run(
252
+ block, block_entity, extra
253
+ )
254
+
255
+ if cacheable:
256
+ self._to_universal_cache[block] = output, extra_output, extra_needed
257
+
258
+ return output, deepcopy(extra_output), extra_needed
259
+
260
+ def from_universal(
261
+ self,
262
+ target_platform: str,
263
+ target_version: VersionNumber,
264
+ block: Block,
265
+ block_entity: BlockEntity | None,
266
+ extra: (
267
+ tuple[
268
+ BlockCoordinates,
269
+ Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
270
+ ]
271
+ | None
272
+ ),
273
+ ) -> tuple[Block, BlockEntity | None, bool] | tuple[Entity, None, bool]:
274
+ if not self._game_version.supports_version(target_platform, target_version):
275
+ raise ValueError("The target version is not compatible with this version")
276
+
277
+ if block.platform != "universal":
278
+ raise ValueError("The source block is not in the universal format")
279
+
280
+ cache_token = (block, target_version)
281
+
282
+ if block_entity is None:
283
+ if cache_token in self._from_universal_cache:
284
+ output, extra_output, extra_needed = self._from_universal_cache[
285
+ cache_token
286
+ ]
287
+ if isinstance(output, Block):
288
+ return output, deepcopy(extra_output), extra_needed
289
+ elif isinstance(output, Entity):
290
+ return deepcopy(output), None, extra_needed
291
+ else:
292
+ block_entity = deepcopy(block_entity)
293
+
294
+ try:
295
+ translator = self._from_universal[(block.namespace, block.base_name)]
296
+ except KeyError:
297
+ raise BlockTranslationError(
298
+ f"Block {block} does not exist in version {self._game_version.platform} {self._game_version.min_version}"
299
+ )
300
+
301
+ output, extra_output, extra_needed, cacheable = translator.run(
302
+ target_platform, target_version, block, block_entity, extra
303
+ )
304
+
305
+ if isinstance(output, Block):
306
+ if cacheable:
307
+ self._from_universal_cache[cache_token] = (
308
+ output,
309
+ deepcopy(extra_output),
310
+ extra_needed,
311
+ )
312
+ return output, deepcopy(extra_output), extra_needed
313
+ elif isinstance(output, Entity):
314
+ if cacheable:
315
+ self._from_universal_cache[cache_token] = (
316
+ deepcopy(output),
317
+ None,
318
+ extra_needed,
319
+ )
320
+ return deepcopy(output), None, extra_needed
321
+
322
+
323
+ class BlockDataNumericalComponent(ABC):
324
+ @abstractmethod
325
+ def numerical_id_to_namespace_id(self, numerical_id: int) -> tuple[str, str]:
326
+ """Convert the numerical id to its namespace id"""
327
+ raise NotImplementedError
328
+
329
+ @abstractmethod
330
+ def namespace_id_to_numerical_id(self, namespace: str, base_name: str) -> int:
331
+ raise NotImplementedError
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+ from weakref import ref
5
+
6
+ if TYPE_CHECKING:
7
+ from .version import GameVersion
8
+
9
+
10
+ class GameVersionContainer:
11
+ def __init__(self, game_version: GameVersion):
12
+ self.__game_version_ref = ref(game_version)
13
+
14
+ @property
15
+ def _game_version(self) -> GameVersion:
16
+ game = self.__game_version_ref()
17
+ if game is None:
18
+ raise ReferenceError("Referenced GameVersion no longer exists.")
19
+ return game
20
+
21
+ def __getstate__(self) -> dict:
22
+ return {"_game_version": self._game_version}
23
+
24
+ def __setstate__(self, state: dict) -> None:
25
+ self.__game_version_ref = ref(state["_game_version"])
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Self, Union, TypeAlias
5
+
6
+ JSONCompatible: TypeAlias = Union[
7
+ str,
8
+ int,
9
+ float,
10
+ bool,
11
+ None,
12
+ "JSONList",
13
+ "JSONDict",
14
+ ]
15
+ JSONDict: TypeAlias = dict[str, "JSONCompatible"]
16
+ JSONList: TypeAlias = list["JSONCompatible"]
17
+
18
+
19
+ class JSONInterface(ABC):
20
+ @classmethod
21
+ @abstractmethod
22
+ def from_json(cls, obj: JSONCompatible) -> Self:
23
+ raise NotImplementedError
24
+
25
+ @abstractmethod
26
+ def to_json(self) -> JSONCompatible:
27
+ raise NotImplementedError
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+ from abc import ABC, abstractmethod
5
+
6
+ from amulet.version import VersionNumber
7
+
8
+ if TYPE_CHECKING:
9
+ from .block import BlockData
10
+ from .biome import BiomeData
11
+
12
+
13
+ class GameVersion(ABC):
14
+ @abstractmethod
15
+ def supports_version(self, platform: str, version: VersionNumber) -> bool:
16
+ raise NotImplementedError
17
+
18
+ @property
19
+ @abstractmethod
20
+ def platform(self) -> str:
21
+ """The platform string this instance is part of."""
22
+ raise NotImplementedError
23
+
24
+ @property
25
+ @abstractmethod
26
+ def min_version(self) -> VersionNumber:
27
+ """The minimum game version this instance can be used with."""
28
+ raise NotImplementedError
29
+
30
+ @property
31
+ @abstractmethod
32
+ def max_version(self) -> VersionNumber:
33
+ """The maximum game version this instance can be used with."""
34
+ raise NotImplementedError
35
+
36
+ @property
37
+ @abstractmethod
38
+ def block(self) -> BlockData:
39
+ raise NotImplementedError
40
+
41
+ @property
42
+ @abstractmethod
43
+ def biome(self) -> BiomeData:
44
+ raise NotImplementedError
@@ -0,0 +1 @@
1
+ from ._version import BedrockGameVersion
@@ -0,0 +1,35 @@
1
+ from collections.abc import Mapping, Collection
2
+
3
+ from amulet.biome import Biome
4
+ from amulet.game.abc import DatabaseBiomeData, BiomeDataNumericalComponent
5
+ from amulet.game.abc import GameVersion
6
+
7
+
8
+ class BedrockBiomeData(DatabaseBiomeData, BiomeDataNumericalComponent):
9
+ def __init__(
10
+ self,
11
+ game_version: GameVersion,
12
+ biomes: Mapping[str, Collection[str]],
13
+ to_universal: Mapping[tuple[str, str], Biome],
14
+ from_universal: Mapping[Biome, tuple[str, str]],
15
+ numerical_map: Mapping[int, tuple[str, str]],
16
+ ):
17
+ super().__init__(game_version, biomes, to_universal, from_universal)
18
+ self._num_to_str = numerical_map
19
+ self._str_to_num = {v: k for k, v in self._num_to_str.items()}
20
+
21
+ def __getstate__(self) -> dict:
22
+ state = super().__getstate__()
23
+ state["_num_to_str"] = self._num_to_str
24
+ return state
25
+
26
+ def __setstate__(self, state: dict) -> None:
27
+ super().__setstate__(state)
28
+ self._num_to_str = state["_num_to_str"]
29
+ self._str_to_num = {v: k for k, v in self._num_to_str.items()}
30
+
31
+ def numerical_id_to_namespace_id(self, numerical_id: int) -> tuple[str, str]:
32
+ return self._num_to_str[numerical_id]
33
+
34
+ def namespace_id_to_numerical_id(self, namespace: str, base_name: str) -> int:
35
+ return self._str_to_num[(namespace, base_name)]