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
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Callable, Union, Sequence, TypeVar, Type, Any, TYPE_CHECKING
|
|
3
|
+
import json
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
7
|
+
|
|
8
|
+
from amulet_nbt import (
|
|
9
|
+
read_nbt,
|
|
10
|
+
read_snbt,
|
|
11
|
+
NamedTag,
|
|
12
|
+
ListTag,
|
|
13
|
+
CompoundTag,
|
|
14
|
+
AnyNBT,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from amulet.block import Block
|
|
18
|
+
from amulet.block_entity import BlockEntity
|
|
19
|
+
from amulet.entity import Entity
|
|
20
|
+
from amulet.data_types import BlockCoordinates
|
|
21
|
+
from amulet.version import VersionNumber
|
|
22
|
+
|
|
23
|
+
from ._functions import (
|
|
24
|
+
AbstractBaseTranslationFunction,
|
|
25
|
+
SrcData,
|
|
26
|
+
SrcDataExtra,
|
|
27
|
+
StateData,
|
|
28
|
+
DstData,
|
|
29
|
+
)
|
|
30
|
+
from ._functions.abc import translation_function_from_json
|
|
31
|
+
from ._functions._typing import NBTPath
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from amulet.game.abc import GameVersion, BlockSpec
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# These classes exist to do some pre-translation and post-translation processing.
|
|
38
|
+
# They also store the constant data so that it doesn't need to be passed in each time.
|
|
39
|
+
# They don't do any caching
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def create_nbt(
|
|
43
|
+
outer_name: str,
|
|
44
|
+
outer_type: type[ListTag] | type[CompoundTag],
|
|
45
|
+
nbt_list: Sequence[
|
|
46
|
+
tuple[
|
|
47
|
+
str,
|
|
48
|
+
type[ListTag] | type[CompoundTag],
|
|
49
|
+
NBTPath,
|
|
50
|
+
Union[str, int],
|
|
51
|
+
AnyNBT,
|
|
52
|
+
]
|
|
53
|
+
],
|
|
54
|
+
default_template: str | None = None,
|
|
55
|
+
) -> NamedTag:
|
|
56
|
+
nbt_object: AnyNBT
|
|
57
|
+
if default_template is None:
|
|
58
|
+
nbt_object = outer_type()
|
|
59
|
+
else:
|
|
60
|
+
nbt_object = read_snbt(default_template)
|
|
61
|
+
|
|
62
|
+
for nbt_entry in nbt_list:
|
|
63
|
+
(
|
|
64
|
+
element_outer_name,
|
|
65
|
+
element_outer_type,
|
|
66
|
+
element_nbt_path,
|
|
67
|
+
element_data_path,
|
|
68
|
+
element_data,
|
|
69
|
+
) = nbt_entry
|
|
70
|
+
if outer_name == element_outer_name and issubclass(
|
|
71
|
+
outer_type, element_outer_type
|
|
72
|
+
):
|
|
73
|
+
nbt_temp: AnyNBT = nbt_object
|
|
74
|
+
for path, nbt_type in element_nbt_path:
|
|
75
|
+
# if the nested NBT object does not exist then create it
|
|
76
|
+
if isinstance(nbt_temp, CompoundTag):
|
|
77
|
+
assert isinstance(path, str)
|
|
78
|
+
if path not in nbt_temp or not isinstance(nbt_temp[path], nbt_type):
|
|
79
|
+
nbt_temp[path] = nbt_type()
|
|
80
|
+
nbt_temp = nbt_temp[path]
|
|
81
|
+
elif isinstance(nbt_temp, ListTag):
|
|
82
|
+
assert isinstance(path, int)
|
|
83
|
+
# if the list is a different type to nbt_type replace it with nbt_type
|
|
84
|
+
if len(nbt_temp) > 0 and not isinstance(nbt_temp[0], nbt_type):
|
|
85
|
+
raise RuntimeError("ListTag elements are the wrong type")
|
|
86
|
+
|
|
87
|
+
for _ in range(path + 1 - len(nbt_temp)):
|
|
88
|
+
# pad out the list to the length of the index
|
|
89
|
+
nbt_temp.append(nbt_type())
|
|
90
|
+
# we now should have a ListTag of the same type as nbt_type and length as path
|
|
91
|
+
nbt_temp = nbt_temp[path]
|
|
92
|
+
else:
|
|
93
|
+
raise RuntimeError
|
|
94
|
+
|
|
95
|
+
if isinstance(nbt_temp, CompoundTag):
|
|
96
|
+
assert isinstance(element_data_path, str)
|
|
97
|
+
nbt_temp[element_data_path] = element_data
|
|
98
|
+
|
|
99
|
+
elif isinstance(nbt_temp, ListTag):
|
|
100
|
+
assert isinstance(element_data_path, int)
|
|
101
|
+
# if the list is a different type to data replace it with type(data)
|
|
102
|
+
if (
|
|
103
|
+
len(nbt_temp) > 0
|
|
104
|
+
and nbt_temp[0].list_data_type != element_data.tag_id
|
|
105
|
+
):
|
|
106
|
+
raise RuntimeError("ListTag elements are the wrong type")
|
|
107
|
+
|
|
108
|
+
for _ in range(element_data_path + 1 - len(nbt_temp)):
|
|
109
|
+
# pad out the list to the length of the index
|
|
110
|
+
nbt_temp.append(element_data.__class__())
|
|
111
|
+
# we now should have a ListTag of the same type as nbt_type and length as data_path
|
|
112
|
+
nbt_temp[element_data_path] = element_data
|
|
113
|
+
|
|
114
|
+
# TODO:
|
|
115
|
+
# elif isinstance(nbt_temp, ByteArrayTag) and isinstance(data, ByteTag):
|
|
116
|
+
# # pad to the length of data_path if less than the current length
|
|
117
|
+
# # nbt_temp[data_path] = data.value
|
|
118
|
+
# elif isinstance(nbt_temp, IntArrayTag) and isinstance(data, IntTag):
|
|
119
|
+
# elif isinstance(nbt_temp, LongArrayTag) and isinstance(data, LongTag):
|
|
120
|
+
|
|
121
|
+
return NamedTag(nbt_object, outer_name)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class BlockToUniversalTranslator:
|
|
125
|
+
# Class variables
|
|
126
|
+
_instances: dict[BlockToUniversalTranslator, BlockToUniversalTranslator] = {}
|
|
127
|
+
|
|
128
|
+
# Instance variables
|
|
129
|
+
_src_spec: BlockSpec
|
|
130
|
+
_translation: AbstractBaseTranslationFunction
|
|
131
|
+
_universal_version: GameVersion
|
|
132
|
+
_hash: int | None
|
|
133
|
+
|
|
134
|
+
def __new__(
|
|
135
|
+
cls,
|
|
136
|
+
src_spec: BlockSpec,
|
|
137
|
+
translation: AbstractBaseTranslationFunction,
|
|
138
|
+
universal_version: GameVersion,
|
|
139
|
+
) -> BlockToUniversalTranslator:
|
|
140
|
+
self = super().__new__(cls)
|
|
141
|
+
self._src_spec = src_spec
|
|
142
|
+
self._translation = translation
|
|
143
|
+
self._universal_version = universal_version
|
|
144
|
+
self._hash = None
|
|
145
|
+
return cls._instances.setdefault(self, self)
|
|
146
|
+
|
|
147
|
+
def __reduce__(self) -> Any:
|
|
148
|
+
return BlockToUniversalTranslator, (
|
|
149
|
+
self._src_spec,
|
|
150
|
+
self._translation,
|
|
151
|
+
self._universal_version,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def __hash__(self) -> int:
|
|
155
|
+
if self._hash is None:
|
|
156
|
+
self._hash = hash(
|
|
157
|
+
(self._src_spec, self._translation, self._universal_version)
|
|
158
|
+
)
|
|
159
|
+
return self._hash
|
|
160
|
+
|
|
161
|
+
def run(
|
|
162
|
+
self,
|
|
163
|
+
block: Block,
|
|
164
|
+
block_entity: BlockEntity | None,
|
|
165
|
+
extra: (
|
|
166
|
+
tuple[
|
|
167
|
+
BlockCoordinates,
|
|
168
|
+
Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
|
|
169
|
+
]
|
|
170
|
+
| None
|
|
171
|
+
),
|
|
172
|
+
) -> tuple[Block, BlockEntity | None, bool, bool]:
|
|
173
|
+
if extra is None:
|
|
174
|
+
src_extra = None
|
|
175
|
+
else:
|
|
176
|
+
coords, get_block = extra
|
|
177
|
+
src_extra = SrcDataExtra(coords, get_block)
|
|
178
|
+
|
|
179
|
+
block_entity_nbt = None if block_entity is None else block_entity.nbt
|
|
180
|
+
|
|
181
|
+
if self._src_spec.nbt is not None and block_entity_nbt is None:
|
|
182
|
+
# There should be a block entity
|
|
183
|
+
if src_extra is not None:
|
|
184
|
+
# Load it from the level
|
|
185
|
+
block_entity = src_extra.get_block_callback((0, 0, 0))[1]
|
|
186
|
+
if block_entity is not None:
|
|
187
|
+
block_entity_nbt = block_entity.nbt
|
|
188
|
+
if block_entity_nbt is None:
|
|
189
|
+
# If it is still None then load it from the specification
|
|
190
|
+
block_entity_nbt = read_nbt(self._src_spec.nbt.snbt)
|
|
191
|
+
|
|
192
|
+
src = SrcData(block, block_entity_nbt, src_extra)
|
|
193
|
+
state = StateData()
|
|
194
|
+
dst = DstData()
|
|
195
|
+
self._translation.run(src, state, dst)
|
|
196
|
+
|
|
197
|
+
assert dst.cls is Block
|
|
198
|
+
assert dst.resource_id is not None
|
|
199
|
+
namespace, base_name = dst.resource_id
|
|
200
|
+
|
|
201
|
+
dst_spec = self._universal_version.block.get_specification(namespace, base_name)
|
|
202
|
+
properties = {k: v.default for k, v in dst_spec.properties.items()}
|
|
203
|
+
properties.update(dst.properties)
|
|
204
|
+
|
|
205
|
+
dst_block = Block(
|
|
206
|
+
"universal", VersionNumber(1), namespace, base_name, properties
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if dst_spec.nbt is None:
|
|
210
|
+
dst_block_entity = None
|
|
211
|
+
else:
|
|
212
|
+
dst_block_entity = BlockEntity(
|
|
213
|
+
"universal",
|
|
214
|
+
VersionNumber(1),
|
|
215
|
+
dst_spec.nbt.namespace,
|
|
216
|
+
dst_spec.nbt.base_name,
|
|
217
|
+
create_nbt(
|
|
218
|
+
"",
|
|
219
|
+
CompoundTag,
|
|
220
|
+
dst.nbt,
|
|
221
|
+
dst_spec.nbt.snbt,
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return dst_block, dst_block_entity, dst.extra_needed, dst.cacheable
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class BlockFromUniversalTranslator:
|
|
229
|
+
# Class variables
|
|
230
|
+
_instances: dict[BlockFromUniversalTranslator, BlockFromUniversalTranslator] = {}
|
|
231
|
+
|
|
232
|
+
# Instance variables
|
|
233
|
+
_src_spec: BlockSpec
|
|
234
|
+
_translation: AbstractBaseTranslationFunction
|
|
235
|
+
_target_version: GameVersion
|
|
236
|
+
_hash: int | None
|
|
237
|
+
|
|
238
|
+
def __new__(
|
|
239
|
+
cls,
|
|
240
|
+
src_spec: BlockSpec,
|
|
241
|
+
translation: AbstractBaseTranslationFunction,
|
|
242
|
+
target_version: GameVersion,
|
|
243
|
+
) -> BlockFromUniversalTranslator:
|
|
244
|
+
self = super().__new__(cls)
|
|
245
|
+
self._src_spec = src_spec
|
|
246
|
+
self._translation = translation
|
|
247
|
+
self._target_version = target_version
|
|
248
|
+
self._hash = None
|
|
249
|
+
return cls._instances.setdefault(self, self)
|
|
250
|
+
|
|
251
|
+
def __reduce__(self) -> Any:
|
|
252
|
+
return BlockFromUniversalTranslator, (
|
|
253
|
+
self._src_spec,
|
|
254
|
+
self._translation,
|
|
255
|
+
self._target_version,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def __hash__(self) -> int:
|
|
259
|
+
if self._hash is None:
|
|
260
|
+
self._hash = hash((self._src_spec, self._translation, self._target_version))
|
|
261
|
+
return self._hash
|
|
262
|
+
|
|
263
|
+
def run(
|
|
264
|
+
self,
|
|
265
|
+
target_platform: str,
|
|
266
|
+
target_version: VersionNumber,
|
|
267
|
+
block: Block,
|
|
268
|
+
block_entity: BlockEntity | None,
|
|
269
|
+
extra: (
|
|
270
|
+
tuple[
|
|
271
|
+
BlockCoordinates,
|
|
272
|
+
Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
|
|
273
|
+
]
|
|
274
|
+
| None
|
|
275
|
+
),
|
|
276
|
+
) -> tuple[Block, BlockEntity | None, bool, bool] | tuple[Entity, None, bool, bool]:
|
|
277
|
+
if extra is None:
|
|
278
|
+
src_extra = None
|
|
279
|
+
else:
|
|
280
|
+
coords, get_block = extra
|
|
281
|
+
src_extra = SrcDataExtra(coords, get_block)
|
|
282
|
+
|
|
283
|
+
block_entity_nbt = None if block_entity is None else block_entity.nbt
|
|
284
|
+
|
|
285
|
+
if self._src_spec.nbt is not None and block_entity_nbt is None:
|
|
286
|
+
# There should be a block entity
|
|
287
|
+
if src_extra is not None:
|
|
288
|
+
# Load it from the level
|
|
289
|
+
block_entity = src_extra.get_block_callback((0, 0, 0))[1]
|
|
290
|
+
if block_entity is not None:
|
|
291
|
+
block_entity_nbt = block_entity.nbt
|
|
292
|
+
if block_entity_nbt is None:
|
|
293
|
+
# If it is still None then load it from the specification
|
|
294
|
+
block_entity_nbt = read_nbt(self._src_spec.nbt.snbt)
|
|
295
|
+
|
|
296
|
+
src = SrcData(block, block_entity_nbt, src_extra)
|
|
297
|
+
state = StateData()
|
|
298
|
+
dst = DstData()
|
|
299
|
+
self._translation.run(src, state, dst)
|
|
300
|
+
|
|
301
|
+
if dst.cls is Block:
|
|
302
|
+
assert dst.resource_id is not None
|
|
303
|
+
namespace, base_name = dst.resource_id
|
|
304
|
+
|
|
305
|
+
dst_spec = self._target_version.block.get_specification(
|
|
306
|
+
namespace, base_name
|
|
307
|
+
)
|
|
308
|
+
properties = {k: v.default for k, v in dst_spec.properties.items()}
|
|
309
|
+
properties.update(dst.properties)
|
|
310
|
+
|
|
311
|
+
dst_block = Block(
|
|
312
|
+
target_platform, target_version, namespace, base_name, properties
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if dst_spec.nbt is None:
|
|
316
|
+
dst_block_entity = None
|
|
317
|
+
else:
|
|
318
|
+
dst_block_entity = BlockEntity(
|
|
319
|
+
target_platform,
|
|
320
|
+
target_version,
|
|
321
|
+
dst_spec.nbt.namespace,
|
|
322
|
+
dst_spec.nbt.base_name,
|
|
323
|
+
create_nbt(
|
|
324
|
+
"",
|
|
325
|
+
CompoundTag,
|
|
326
|
+
dst.nbt,
|
|
327
|
+
dst_spec.nbt.snbt,
|
|
328
|
+
),
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return dst_block, dst_block_entity, dst.extra_needed, dst.cacheable
|
|
332
|
+
|
|
333
|
+
elif dst.cls is Entity:
|
|
334
|
+
assert dst.resource_id is not None
|
|
335
|
+
namespace, base_name = dst.resource_id
|
|
336
|
+
|
|
337
|
+
# dst_spec = self._target_version.block.get_specification(
|
|
338
|
+
# namespace, base_name
|
|
339
|
+
# )
|
|
340
|
+
|
|
341
|
+
dst_entity = Entity(
|
|
342
|
+
target_platform,
|
|
343
|
+
target_version,
|
|
344
|
+
namespace,
|
|
345
|
+
base_name,
|
|
346
|
+
0.0,
|
|
347
|
+
0.0,
|
|
348
|
+
0.0,
|
|
349
|
+
create_nbt(
|
|
350
|
+
"",
|
|
351
|
+
CompoundTag,
|
|
352
|
+
dst.nbt,
|
|
353
|
+
# dst_spec.nbt.snbt,
|
|
354
|
+
),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
return dst_entity, None, dst.extra_needed, dst.cacheable
|
|
358
|
+
|
|
359
|
+
else:
|
|
360
|
+
raise RuntimeError
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
TranslationClsT = TypeVar(
|
|
364
|
+
"TranslationClsT", BlockToUniversalTranslator, BlockFromUniversalTranslator
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _read_file(path: str) -> str:
|
|
369
|
+
with open(path) as f:
|
|
370
|
+
return f.read()
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def load_json_block_translations(
|
|
374
|
+
version_path: str,
|
|
375
|
+
block_format: str,
|
|
376
|
+
direction: str,
|
|
377
|
+
translation_cls: Type[TranslationClsT],
|
|
378
|
+
get_src_spec: Callable[[str, str], BlockSpec],
|
|
379
|
+
target_version: GameVersion,
|
|
380
|
+
) -> dict[tuple[str, str], TranslationClsT]:
|
|
381
|
+
translations = dict[tuple[str, str], TranslationClsT]()
|
|
382
|
+
paths = glob.glob(
|
|
383
|
+
os.path.join(
|
|
384
|
+
glob.escape(version_path),
|
|
385
|
+
"block",
|
|
386
|
+
block_format,
|
|
387
|
+
direction,
|
|
388
|
+
"*",
|
|
389
|
+
"*",
|
|
390
|
+
"*.json",
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
with ThreadPoolExecutor() as e:
|
|
394
|
+
for file_path, data in zip(paths, e.map(_read_file, paths)):
|
|
395
|
+
*_, namespace, _, base_name = os.path.splitext(os.path.normpath(file_path))[
|
|
396
|
+
0
|
|
397
|
+
].split(os.sep)
|
|
398
|
+
translations[(namespace, base_name)] = translation_cls(
|
|
399
|
+
get_src_spec(namespace, base_name),
|
|
400
|
+
translation_function_from_json(json.loads(data)),
|
|
401
|
+
target_version,
|
|
402
|
+
)
|
|
403
|
+
return translations
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class EntityToUniversalTranslator:
|
|
407
|
+
def run(
|
|
408
|
+
self,
|
|
409
|
+
entity: Entity,
|
|
410
|
+
extra: (
|
|
411
|
+
tuple[
|
|
412
|
+
BlockCoordinates,
|
|
413
|
+
Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
|
|
414
|
+
]
|
|
415
|
+
| None
|
|
416
|
+
),
|
|
417
|
+
) -> tuple[Block, BlockEntity | None, bool, bool]:
|
|
418
|
+
raise NotImplementedError
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class EntityFromUniversalTranslator:
|
|
422
|
+
def run(
|
|
423
|
+
self,
|
|
424
|
+
entity: Entity | None,
|
|
425
|
+
extra: (
|
|
426
|
+
tuple[
|
|
427
|
+
BlockCoordinates,
|
|
428
|
+
Callable[[BlockCoordinates], tuple[Block, BlockEntity | None]],
|
|
429
|
+
]
|
|
430
|
+
| None
|
|
431
|
+
),
|
|
432
|
+
) -> tuple[Block, BlockEntity | None, bool] | tuple[Entity, None, bool]:
|
|
433
|
+
raise NotImplementedError
|
amulet/item.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# from amulet_nbt import CompoundTag, StringTag
|
|
2
|
+
#
|
|
3
|
+
#
|
|
4
|
+
# class Item(CompoundTag):
|
|
5
|
+
# """
|
|
6
|
+
# {
|
|
7
|
+
# "namespace": StringTag,
|
|
8
|
+
# "base_name": StringTag,
|
|
9
|
+
# "metadata": CompoundTag
|
|
10
|
+
# }
|
|
11
|
+
# """
|
|
12
|
+
#
|
|
13
|
+
# def __init__(self, namespace: str, base_name: str, metadata: dict = None) -> None:
|
|
14
|
+
# super().__init__(
|
|
15
|
+
# {
|
|
16
|
+
# "namespace": StringTag(namespace),
|
|
17
|
+
# "base_name": StringTag(base_name),
|
|
18
|
+
# "metadata": CompoundTag(metadata or {}),
|
|
19
|
+
# }
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# @property
|
|
23
|
+
# def namespace(self) -> str:
|
|
24
|
+
# return self.get_string("namespace").py_str
|
|
25
|
+
#
|
|
26
|
+
# @property
|
|
27
|
+
# def base_name(self) -> str:
|
|
28
|
+
# return self.get_string("base_name").py_str
|
|
29
|
+
#
|
|
30
|
+
# @property
|
|
31
|
+
# def metadata(self) -> CompoundTag:
|
|
32
|
+
# return self.get_compound("metadata")
|
|
33
|
+
#
|
|
34
|
+
#
|
|
35
|
+
# class BlockItem(CompoundTag):
|
|
36
|
+
# """
|
|
37
|
+
# {
|
|
38
|
+
# "namespace": StringTag,
|
|
39
|
+
# "base_name": StringTag,
|
|
40
|
+
# "properties": CompoundTag
|
|
41
|
+
# "metadata": CompoundTag
|
|
42
|
+
# }
|
|
43
|
+
# """
|
|
44
|
+
#
|
|
45
|
+
# def __init__(
|
|
46
|
+
# self,
|
|
47
|
+
# namespace: str,
|
|
48
|
+
# base_name: str,
|
|
49
|
+
# properties: dict = None,
|
|
50
|
+
# metadata: dict = None,
|
|
51
|
+
# ):
|
|
52
|
+
# super().__init__(
|
|
53
|
+
# {
|
|
54
|
+
# "namespace": StringTag(namespace),
|
|
55
|
+
# "base_name": StringTag(base_name),
|
|
56
|
+
# "properties": CompoundTag(properties or {}),
|
|
57
|
+
# "metadata": CompoundTag(metadata or {}),
|
|
58
|
+
# }
|
|
59
|
+
# )
|
|
60
|
+
#
|
|
61
|
+
# @property
|
|
62
|
+
# def namespace(self) -> str:
|
|
63
|
+
# return self.get_string("namespace").py_str
|
|
64
|
+
#
|
|
65
|
+
# @property
|
|
66
|
+
# def base_name(self) -> str:
|
|
67
|
+
# return self.get_string("base_name").py_str
|
|
68
|
+
#
|
|
69
|
+
# @property
|
|
70
|
+
# def properties(self) -> CompoundTag:
|
|
71
|
+
# return self.get_compound("properties")
|
|
72
|
+
#
|
|
73
|
+
# @property
|
|
74
|
+
# def metadata(self) -> CompoundTag:
|
|
75
|
+
# return self.get_compound("metadata")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from amulet.level._load import (
|
|
6
|
+
NoValidLevel,
|
|
7
|
+
get_level,
|
|
8
|
+
register_level_class,
|
|
9
|
+
unregister_level_class,
|
|
10
|
+
)
|
|
11
|
+
from amulet.level.abc._level._level import Level
|
|
12
|
+
from amulet.level.java._level import JavaLevel
|
|
13
|
+
|
|
14
|
+
from . import java
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"JavaLevel",
|
|
18
|
+
"Level",
|
|
19
|
+
"NoValidLevel",
|
|
20
|
+
"get_level",
|
|
21
|
+
"java",
|
|
22
|
+
"register_level_class",
|
|
23
|
+
"unregister_level_class",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
def __dir__() -> typing.Any: ...
|
|
27
|
+
def __getattr__(arg0: typing.Any) -> typing.Any: ...
|
amulet/level/_load.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Type, Any
|
|
4
|
+
import logging
|
|
5
|
+
from inspect import isclass
|
|
6
|
+
from threading import Lock
|
|
7
|
+
from weakref import WeakValueDictionary
|
|
8
|
+
|
|
9
|
+
from amulet.level.abc import LoadableLevel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_level_classes = set[Type[LoadableLevel]]()
|
|
16
|
+
_levels = WeakValueDictionary[Any, LoadableLevel]()
|
|
17
|
+
_lock = Lock()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _check_loadable_level(cls: Any) -> None:
|
|
21
|
+
if not (isclass(cls) and issubclass(cls, LoadableLevel)):
|
|
22
|
+
raise TypeError(
|
|
23
|
+
"cls must be a subclass of amulet.level.abc.Level and amulet.level.abc.LoadableLevel"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def register_level_class(cls: Type[LoadableLevel]) -> None:
|
|
28
|
+
"""Add a level class to be considered when getting a level.
|
|
29
|
+
|
|
30
|
+
:param cls: The Level subclass to register.
|
|
31
|
+
:return:
|
|
32
|
+
"""
|
|
33
|
+
_check_loadable_level(cls)
|
|
34
|
+
with _lock:
|
|
35
|
+
_level_classes.add(cls)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def unregister_level_class(cls: Type[LoadableLevel]) -> None:
|
|
39
|
+
"""Remove a level class from consideration when getting a level.
|
|
40
|
+
|
|
41
|
+
Note that any instances of the class will remain.
|
|
42
|
+
|
|
43
|
+
:param cls: The Level subclass to unregister.
|
|
44
|
+
"""
|
|
45
|
+
_check_loadable_level(cls)
|
|
46
|
+
with _lock:
|
|
47
|
+
_level_classes.discard(cls)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class NoValidLevel(Exception):
|
|
51
|
+
"""An error thrown if no level could load the token."""
|
|
52
|
+
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_level(token: Any) -> LoadableLevel:
|
|
57
|
+
"""Get the level for the given token.
|
|
58
|
+
|
|
59
|
+
If a level object already exists for this token then that will be returned.
|
|
60
|
+
This will return a subclass of Level specialised for that level type.
|
|
61
|
+
Note that the returned level may or may not be open.
|
|
62
|
+
The level will automatically close itself when the last reference is lost so you must hold a strong reference to it.
|
|
63
|
+
|
|
64
|
+
:param token: The token to load. This may be a file/directory path or some other token.
|
|
65
|
+
:return: The level instance.
|
|
66
|
+
:raises:
|
|
67
|
+
NoValidLevel: If no level could load the token.
|
|
68
|
+
|
|
69
|
+
Exception: Other errors.
|
|
70
|
+
"""
|
|
71
|
+
level: None | LoadableLevel
|
|
72
|
+
with _lock:
|
|
73
|
+
# Find the level to load the token
|
|
74
|
+
cls = next((cls for cls in _level_classes if cls.can_load(token)), None)
|
|
75
|
+
|
|
76
|
+
if cls is None:
|
|
77
|
+
# If no level could load the token then raise
|
|
78
|
+
raise NoValidLevel(f"Could not load {token}")
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
# Try and get a cached instance of the level
|
|
82
|
+
level = _levels.get(token)
|
|
83
|
+
if level is not None:
|
|
84
|
+
# If there is a cached instance then return it
|
|
85
|
+
return level
|
|
86
|
+
except TypeError:
|
|
87
|
+
# If the token is not hashable
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# Load the level
|
|
91
|
+
level = cls.load(token)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
# Cache the level
|
|
95
|
+
_levels[token] = level
|
|
96
|
+
except TypeError:
|
|
97
|
+
# Token may not be hashable
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
return level
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ._level import *
|
|
2
|
+
from ._chunk_handle import ChunkHandle
|
|
3
|
+
from ._player_storage import PlayerStorage
|
|
4
|
+
from ._raw_level import (
|
|
5
|
+
RawLevel,
|
|
6
|
+
RawLevelFriend,
|
|
7
|
+
RawLevelPlayerComponent,
|
|
8
|
+
RawLevelBufferedComponent,
|
|
9
|
+
RawDimension,
|
|
10
|
+
)
|
|
11
|
+
from ._dimension import Dimension
|
|
12
|
+
from ._registry import IdRegistry
|