amulet-core 2.0a3__cp312-cp312-win_amd64.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.
- amulet/__init__.cp312-win_amd64.pyd +0 -0
- amulet/__init__.pyi +30 -0
- amulet/__pyinstaller/__init__.py +2 -0
- amulet/__pyinstaller/hook-amulet.py +4 -0
- amulet/_init.py +28 -0
- amulet/_version.py +21 -0
- amulet/biome.cpp +36 -0
- amulet/biome.hpp +43 -0
- amulet/biome.pyi +77 -0
- amulet/block.cpp +435 -0
- amulet/block.hpp +119 -0
- amulet/block.pyi +273 -0
- amulet/block_entity.cpp +12 -0
- amulet/block_entity.hpp +56 -0
- amulet/block_entity.pyi +80 -0
- amulet/chunk.cpp +16 -0
- amulet/chunk.hpp +99 -0
- amulet/chunk.pyi +30 -0
- amulet/chunk_/components/biome.py +155 -0
- amulet/chunk_/components/block_entity.py +117 -0
- amulet/chunk_/components/entity.py +64 -0
- amulet/chunk_/components/height_2d.py +16 -0
- amulet/chunk_components.pyi +95 -0
- amulet/collections.pyi +37 -0
- amulet/data_types.py +29 -0
- amulet/entity.py +180 -0
- amulet/errors.py +63 -0
- amulet/game/__init__.py +7 -0
- amulet/game/_game.py +152 -0
- amulet/game/_universal/__init__.py +1 -0
- amulet/game/_universal/_biome.py +17 -0
- amulet/game/_universal/_block.py +47 -0
- amulet/game/_universal/_version.py +68 -0
- amulet/game/abc/__init__.py +22 -0
- amulet/game/abc/_block_specification.py +150 -0
- amulet/game/abc/biome.py +213 -0
- amulet/game/abc/block.py +331 -0
- amulet/game/abc/game_version_container.py +25 -0
- amulet/game/abc/json_interface.py +27 -0
- amulet/game/abc/version.py +44 -0
- amulet/game/bedrock/__init__.py +1 -0
- amulet/game/bedrock/_biome.py +35 -0
- amulet/game/bedrock/_block.py +42 -0
- amulet/game/bedrock/_version.py +165 -0
- amulet/game/java/__init__.py +2 -0
- amulet/game/java/_biome.py +35 -0
- amulet/game/java/_block.py +60 -0
- amulet/game/java/_version.py +176 -0
- amulet/game/translate/__init__.py +12 -0
- amulet/game/translate/_functions/__init__.py +15 -0
- amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
- amulet/game/translate/_functions/_code_functions/_text.py +553 -0
- amulet/game/translate/_functions/_code_functions/banner_pattern.py +67 -0
- amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +88 -0
- amulet/game/translate/_functions/_code_functions/bedrock_sign.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +16 -0
- amulet/game/translate/_functions/_code_functions/custom_name.py +146 -0
- amulet/game/translate/_functions/_frozen.py +66 -0
- amulet/game/translate/_functions/_state.py +54 -0
- amulet/game/translate/_functions/_typing.py +98 -0
- amulet/game/translate/_functions/abc.py +116 -0
- amulet/game/translate/_functions/carry_nbt.py +160 -0
- amulet/game/translate/_functions/carry_properties.py +80 -0
- amulet/game/translate/_functions/code.py +143 -0
- amulet/game/translate/_functions/map_block_name.py +66 -0
- amulet/game/translate/_functions/map_nbt.py +111 -0
- amulet/game/translate/_functions/map_properties.py +93 -0
- amulet/game/translate/_functions/multiblock.py +112 -0
- amulet/game/translate/_functions/new_block.py +42 -0
- amulet/game/translate/_functions/new_entity.py +43 -0
- amulet/game/translate/_functions/new_nbt.py +206 -0
- amulet/game/translate/_functions/new_properties.py +64 -0
- amulet/game/translate/_functions/sequence.py +51 -0
- amulet/game/translate/_functions/walk_input_nbt.py +331 -0
- amulet/game/translate/_translator.py +433 -0
- amulet/item.py +75 -0
- amulet/level/__init__.pyi +27 -0
- amulet/level/_load.py +100 -0
- amulet/level/abc/__init__.py +12 -0
- amulet/level/abc/_chunk_handle.py +335 -0
- amulet/level/abc/_dimension.py +86 -0
- amulet/level/abc/_history/__init__.py +1 -0
- amulet/level/abc/_history/_cache.py +224 -0
- amulet/level/abc/_history/_history_manager.py +291 -0
- amulet/level/abc/_level/__init__.py +5 -0
- amulet/level/abc/_level/_compactable_level.py +10 -0
- amulet/level/abc/_level/_creatable_level.py +29 -0
- amulet/level/abc/_level/_disk_level.py +17 -0
- amulet/level/abc/_level/_level.py +453 -0
- amulet/level/abc/_level/_loadable_level.py +42 -0
- amulet/level/abc/_player_storage.py +7 -0
- amulet/level/abc/_raw_level.py +187 -0
- amulet/level/abc/_registry.py +40 -0
- amulet/level/bedrock/__init__.py +2 -0
- amulet/level/bedrock/_chunk_handle.py +19 -0
- amulet/level/bedrock/_dimension.py +22 -0
- amulet/level/bedrock/_level.py +187 -0
- amulet/level/bedrock/_raw/__init__.py +5 -0
- amulet/level/bedrock/_raw/_actor_counter.py +53 -0
- amulet/level/bedrock/_raw/_chunk.py +54 -0
- amulet/level/bedrock/_raw/_chunk_decode.py +668 -0
- amulet/level/bedrock/_raw/_chunk_encode.py +602 -0
- amulet/level/bedrock/_raw/_constant.py +9 -0
- amulet/level/bedrock/_raw/_dimension.py +343 -0
- amulet/level/bedrock/_raw/_level.py +463 -0
- amulet/level/bedrock/_raw/_level_dat.py +90 -0
- amulet/level/bedrock/_raw/_typing.py +6 -0
- amulet/level/bedrock/_raw/leveldb_chunk_versions.py +83 -0
- amulet/level/bedrock/chunk/__init__.py +1 -0
- amulet/level/bedrock/chunk/_chunk.py +126 -0
- amulet/level/bedrock/chunk/components/__init__.py +0 -0
- amulet/level/bedrock/chunk/components/chunk_version.py +12 -0
- amulet/level/bedrock/chunk/components/finalised_state.py +13 -0
- amulet/level/bedrock/chunk/components/raw_chunk.py +15 -0
- amulet/level/construction/__init__.py +0 -0
- amulet/level/java/__init__.pyi +21 -0
- amulet/level/java/_chunk_handle.py +17 -0
- amulet/level/java/_chunk_handle.pyi +15 -0
- amulet/level/java/_dimension.py +20 -0
- amulet/level/java/_dimension.pyi +13 -0
- amulet/level/java/_level.py +184 -0
- amulet/level/java/_level.pyi +120 -0
- amulet/level/java/_raw/__init__.pyi +19 -0
- amulet/level/java/_raw/_chunk.pyi +23 -0
- amulet/level/java/_raw/_chunk_decode.py +561 -0
- amulet/level/java/_raw/_chunk_encode.py +463 -0
- amulet/level/java/_raw/_constant.py +9 -0
- amulet/level/java/_raw/_constant.pyi +20 -0
- amulet/level/java/_raw/_data_pack/__init__.py +2 -0
- amulet/level/java/_raw/_data_pack/__init__.pyi +8 -0
- amulet/level/java/_raw/_data_pack/data_pack.py +241 -0
- amulet/level/java/_raw/_data_pack/data_pack.pyi +197 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.py +77 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +75 -0
- amulet/level/java/_raw/_dimension.py +86 -0
- amulet/level/java/_raw/_dimension.pyi +72 -0
- amulet/level/java/_raw/_level.py +507 -0
- amulet/level/java/_raw/_level.pyi +238 -0
- amulet/level/java/_raw/_typing.py +3 -0
- amulet/level/java/_raw/_typing.pyi +5 -0
- amulet/level/java/anvil/__init__.py +2 -0
- amulet/level/java/anvil/__init__.pyi +11 -0
- amulet/level/java/anvil/_dimension.py +170 -0
- amulet/level/java/anvil/_dimension.pyi +109 -0
- amulet/level/java/anvil/_region.py +421 -0
- amulet/level/java/anvil/_region.pyi +197 -0
- amulet/level/java/anvil/_sector_manager.py +223 -0
- amulet/level/java/anvil/_sector_manager.pyi +142 -0
- amulet/level/java/chunk.pyi +81 -0
- amulet/level/java/chunk_/_chunk.py +260 -0
- amulet/level/java/chunk_/components/inhabited_time.py +12 -0
- amulet/level/java/chunk_/components/last_update.py +12 -0
- amulet/level/java/chunk_/components/legacy_version.py +12 -0
- amulet/level/java/chunk_/components/light_populated.py +12 -0
- amulet/level/java/chunk_/components/named_height_2d.py +37 -0
- amulet/level/java/chunk_/components/status.py +11 -0
- amulet/level/java/chunk_/components/terrain_populated.py +12 -0
- amulet/level/java/chunk_components.pyi +22 -0
- amulet/level/java/long_array.pyi +38 -0
- amulet/level/java_forge/__init__.py +0 -0
- amulet/level/mcstructure/__init__.py +0 -0
- amulet/level/nbt/__init__.py +0 -0
- amulet/level/schematic/__init__.py +0 -0
- amulet/level/sponge_schematic/__init__.py +0 -0
- amulet/level/temporary_level/__init__.py +1 -0
- amulet/level/temporary_level/_level.py +16 -0
- amulet/palette/__init__.pyi +8 -0
- amulet/palette/biome_palette.pyi +45 -0
- amulet/palette/block_palette.pyi +45 -0
- amulet/player.py +64 -0
- amulet/py.typed +0 -0
- amulet/selection/__init__.py +2 -0
- amulet/selection/abstract_selection.py +342 -0
- amulet/selection/box.py +852 -0
- amulet/selection/group.py +481 -0
- amulet/utils/__init__.pyi +28 -0
- amulet/utils/call_spec/__init__.py +24 -0
- amulet/utils/call_spec/__init__.pyi +53 -0
- amulet/utils/call_spec/_call_spec.py +262 -0
- amulet/utils/call_spec/_call_spec.pyi +272 -0
- amulet/utils/format_utils.py +41 -0
- amulet/utils/generator.py +18 -0
- amulet/utils/matrix.py +243 -0
- amulet/utils/matrix.pyi +177 -0
- amulet/utils/numpy.pyi +11 -0
- amulet/utils/numpy_helpers.py +19 -0
- amulet/utils/shareable_lock.py +335 -0
- amulet/utils/shareable_lock.pyi +190 -0
- amulet/utils/signal/__init__.py +10 -0
- amulet/utils/signal/__init__.pyi +25 -0
- amulet/utils/signal/_signal.py +228 -0
- amulet/utils/signal/_signal.pyi +84 -0
- amulet/utils/task_manager.py +235 -0
- amulet/utils/task_manager.pyi +168 -0
- amulet/utils/typed_property.py +111 -0
- amulet/utils/typing.py +4 -0
- amulet/utils/typing.pyi +6 -0
- amulet/utils/weakref.py +70 -0
- amulet/utils/weakref.pyi +50 -0
- amulet/utils/world_utils.py +102 -0
- amulet/utils/world_utils.pyi +109 -0
- amulet/version.cpp +136 -0
- amulet/version.hpp +142 -0
- amulet/version.pyi +94 -0
- amulet_core-2.0a3.dist-info/METADATA +103 -0
- amulet_core-2.0a3.dist-info/RECORD +210 -0
- amulet_core-2.0a3.dist-info/WHEEL +5 -0
- amulet_core-2.0a3.dist-info/entry_points.txt +2 -0
- amulet_core-2.0a3.dist-info/top_level.txt +1 -0
amulet/game/abc/biome.py
ADDED
|
@@ -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
|
amulet/game/abc/block.py
ADDED
|
@@ -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)]
|