amulet-core 2.0a8__cp312-cp312-win_amd64.whl → 2.0.1a2.post250529084747__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/core/__init__.py +36 -0
- amulet/core/__pyinstaller/hook-amulet.core.py +4 -0
- amulet/core/_amulet_core.cp312-win_amd64.pyd +0 -0
- amulet/core/_amulet_core.pyi +7 -0
- amulet/{_version.py → core/_version.py} +3 -3
- amulet/core/amulet_core.dll +0 -0
- amulet/core/amulet_core.lib +0 -0
- amulet/core/amulet_coreConfig.cmake +18 -0
- amulet/{biome.pyi → core/biome/__init__.pyi} +3 -3
- amulet/core/biome/biome.hpp +53 -0
- amulet/{block.pyi → core/block/__init__.pyi} +25 -26
- amulet/core/block/block.hpp +156 -0
- amulet/{block_entity.pyi → core/block_entity/__init__.pyi} +7 -7
- amulet/core/block_entity/block_entity.hpp +84 -0
- amulet/{errors.py → core/chunk/__init__.pyi} +37 -33
- amulet/core/chunk/chunk.hpp +126 -0
- amulet/core/chunk/component/__init__.pyi +18 -0
- amulet/core/chunk/component/biome_3d_component.hpp +96 -0
- amulet/core/chunk/component/block_component.hpp +101 -0
- amulet/core/chunk/component/block_component.pyi +28 -0
- amulet/core/chunk/component/block_entity_component.hpp +119 -0
- amulet/core/chunk/component/section_array_map.hpp +129 -0
- amulet/{chunk_components.pyi → core/chunk/component/section_array_map.pyi} +4 -24
- amulet/core/dll.hpp +21 -0
- amulet/core/entity/__init__.pyi +105 -0
- amulet/core/entity/entity.hpp +100 -0
- amulet/{palette → core/palette}/__init__.pyi +2 -2
- amulet/core/palette/biome_palette.hpp +65 -0
- amulet/{palette → core/palette}/biome_palette.pyi +8 -8
- amulet/core/palette/block_palette.hpp +71 -0
- amulet/{palette → core/palette}/block_palette.pyi +12 -10
- amulet/core/selection/__init__.pyi +8 -0
- amulet/core/selection/box.hpp +86 -0
- amulet/core/selection/box.pyi +215 -0
- amulet/core/selection/group.hpp +80 -0
- amulet/core/selection/group.pyi +213 -0
- amulet/{version.pyi → core/version/__init__.pyi} +58 -10
- amulet/core/version/version.hpp +204 -0
- {amulet_core-2.0a8.dist-info → amulet_core-2.0.1a2.post250529084747.dist-info}/METADATA +25 -20
- amulet_core-2.0.1a2.post250529084747.dist-info/RECORD +45 -0
- {amulet_core-2.0a8.dist-info → amulet_core-2.0.1a2.post250529084747.dist-info}/WHEEL +1 -1
- amulet_core-2.0.1a2.post250529084747.dist-info/entry_points.txt +2 -0
- amulet/__init__.cp312-win_amd64.pyd +0 -0
- amulet/__init__.py.cpp +0 -45
- amulet/__init__.pyi +0 -30
- amulet/__pyinstaller/hook-amulet.py +0 -4
- amulet/_init.py +0 -26
- amulet/biome.cpp +0 -36
- amulet/biome.hpp +0 -43
- amulet/biome.py.cpp +0 -122
- amulet/block.cpp +0 -435
- amulet/block.hpp +0 -119
- amulet/block.py.cpp +0 -377
- amulet/block_entity.cpp +0 -12
- amulet/block_entity.hpp +0 -56
- amulet/block_entity.py.cpp +0 -115
- amulet/chunk.cpp +0 -16
- amulet/chunk.hpp +0 -100
- amulet/chunk.py.cpp +0 -80
- amulet/chunk.pyi +0 -28
- amulet/chunk_components/biome_3d_component.cpp +0 -5
- amulet/chunk_components/biome_3d_component.hpp +0 -79
- amulet/chunk_components/block_component.cpp +0 -41
- amulet/chunk_components/block_component.hpp +0 -88
- amulet/chunk_components/block_entity_component.cpp +0 -5
- amulet/chunk_components/block_entity_component.hpp +0 -147
- amulet/chunk_components/section_array_map.cpp +0 -129
- amulet/chunk_components/section_array_map.hpp +0 -147
- amulet/collections/eq.py.hpp +0 -37
- amulet/collections/hash.py.hpp +0 -27
- amulet/collections/holder.py.hpp +0 -37
- amulet/collections/iterator.py.hpp +0 -80
- amulet/collections/mapping.py.hpp +0 -199
- amulet/collections/mutable_mapping.py.hpp +0 -226
- amulet/collections/sequence.py.hpp +0 -163
- amulet/collections.pyi +0 -40
- amulet/data_types.py +0 -29
- amulet/entity.py +0 -182
- amulet/game/__init__.py +0 -7
- amulet/game/_game.py +0 -152
- amulet/game/_universal/__init__.py +0 -1
- amulet/game/_universal/_biome.py +0 -17
- amulet/game/_universal/_block.py +0 -47
- amulet/game/_universal/_version.py +0 -68
- amulet/game/abc/__init__.py +0 -22
- amulet/game/abc/_block_specification.py +0 -150
- amulet/game/abc/biome.py +0 -213
- amulet/game/abc/block.py +0 -331
- amulet/game/abc/game_version_container.py +0 -25
- amulet/game/abc/json_interface.py +0 -27
- amulet/game/abc/version.py +0 -44
- amulet/game/bedrock/__init__.py +0 -1
- amulet/game/bedrock/_biome.py +0 -35
- amulet/game/bedrock/_block.py +0 -42
- amulet/game/bedrock/_version.py +0 -165
- amulet/game/java/__init__.py +0 -2
- amulet/game/java/_biome.py +0 -35
- amulet/game/java/_block.py +0 -60
- amulet/game/java/_version.py +0 -176
- amulet/game/translate/__init__.py +0 -12
- amulet/game/translate/_functions/__init__.py +0 -15
- amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
- amulet/game/translate/_functions/_code_functions/_text.py +0 -553
- amulet/game/translate/_functions/_code_functions/banner_pattern.py +0 -67
- amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +0 -152
- amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +0 -88
- amulet/game/translate/_functions/_code_functions/bedrock_sign.py +0 -152
- amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +0 -16
- amulet/game/translate/_functions/_code_functions/custom_name.py +0 -146
- amulet/game/translate/_functions/_frozen.py +0 -66
- amulet/game/translate/_functions/_state.py +0 -54
- amulet/game/translate/_functions/_typing.py +0 -98
- amulet/game/translate/_functions/abc.py +0 -123
- amulet/game/translate/_functions/carry_nbt.py +0 -160
- amulet/game/translate/_functions/carry_properties.py +0 -80
- amulet/game/translate/_functions/code.py +0 -143
- amulet/game/translate/_functions/map_block_name.py +0 -66
- amulet/game/translate/_functions/map_nbt.py +0 -111
- amulet/game/translate/_functions/map_properties.py +0 -93
- amulet/game/translate/_functions/multiblock.py +0 -112
- amulet/game/translate/_functions/new_block.py +0 -42
- amulet/game/translate/_functions/new_entity.py +0 -43
- amulet/game/translate/_functions/new_nbt.py +0 -206
- amulet/game/translate/_functions/new_properties.py +0 -64
- amulet/game/translate/_functions/sequence.py +0 -51
- amulet/game/translate/_functions/walk_input_nbt.py +0 -331
- amulet/game/translate/_translator.py +0 -433
- amulet/img/__init__.py +0 -10
- amulet/img/missing_no.png +0 -0
- amulet/img/missing_pack.png +0 -0
- amulet/img/missing_world.png +0 -0
- amulet/io/binary_reader.hpp +0 -45
- amulet/io/binary_writer.hpp +0 -30
- amulet/item.py +0 -75
- amulet/level/__init__.pyi +0 -23
- amulet/level/_load.py +0 -100
- amulet/level/abc/__init__.py +0 -12
- amulet/level/abc/_chunk_handle.py +0 -358
- amulet/level/abc/_dimension.py +0 -86
- amulet/level/abc/_history/__init__.py +0 -1
- amulet/level/abc/_history/_cache.py +0 -224
- amulet/level/abc/_history/_history_manager.py +0 -291
- amulet/level/abc/_level/__init__.py +0 -5
- amulet/level/abc/_level/_compactable_level.py +0 -10
- amulet/level/abc/_level/_creatable_level.py +0 -28
- amulet/level/abc/_level/_disk_level.py +0 -17
- amulet/level/abc/_level/_level.py +0 -449
- amulet/level/abc/_level/_loadable_level.py +0 -42
- amulet/level/abc/_player_storage.py +0 -7
- amulet/level/abc/_raw_level.py +0 -187
- amulet/level/abc/_registry.py +0 -40
- amulet/level/java/__init__.pyi +0 -16
- amulet/level/java/_chunk_handle.py +0 -17
- amulet/level/java/_dimension.py +0 -20
- amulet/level/java/_level.py +0 -184
- amulet/level/java/_raw/__init__.pyi +0 -15
- amulet/level/java/_raw/_chunk.pyi +0 -23
- amulet/level/java/_raw/_constant.py +0 -9
- amulet/level/java/_raw/_data_pack/__init__.py +0 -2
- amulet/level/java/_raw/_data_pack/data_pack.py +0 -241
- amulet/level/java/_raw/_data_pack/data_pack_manager.py +0 -77
- amulet/level/java/_raw/_dimension.py +0 -86
- amulet/level/java/_raw/_level.py +0 -507
- amulet/level/java/_raw/_typing.py +0 -3
- amulet/level/java/_raw/java_chunk_decode.cpp +0 -531
- amulet/level/java/_raw/java_chunk_decode.hpp +0 -23
- amulet/level/java/_raw/java_chunk_encode.cpp +0 -25
- amulet/level/java/_raw/java_chunk_encode.hpp +0 -23
- amulet/level/java/anvil/__init__.py +0 -2
- amulet/level/java/anvil/_dimension.py +0 -170
- amulet/level/java/anvil/_region.py +0 -421
- amulet/level/java/anvil/_sector_manager.py +0 -223
- amulet/level/java/chunk.pyi +0 -81
- amulet/level/java/chunk_/_chunk.py +0 -260
- amulet/level/java/chunk_/components/inhabited_time.py +0 -12
- amulet/level/java/chunk_/components/last_update.py +0 -12
- amulet/level/java/chunk_/components/legacy_version.py +0 -12
- amulet/level/java/chunk_/components/light_populated.py +0 -12
- amulet/level/java/chunk_/components/named_height_2d.py +0 -37
- amulet/level/java/chunk_/components/status.py +0 -11
- amulet/level/java/chunk_/components/terrain_populated.py +0 -12
- amulet/level/java/chunk_components/data_version_component.cpp +0 -32
- amulet/level/java/chunk_components/data_version_component.hpp +0 -31
- amulet/level/java/chunk_components/java_raw_chunk_component.cpp +0 -56
- amulet/level/java/chunk_components/java_raw_chunk_component.hpp +0 -45
- amulet/level/java/chunk_components.pyi +0 -22
- amulet/level/java/java_chunk.cpp +0 -170
- amulet/level/java/java_chunk.hpp +0 -141
- amulet/level/java/long_array.hpp +0 -175
- amulet/level/java/long_array.pyi +0 -39
- amulet/level/temporary_level/__init__.py +0 -1
- amulet/level/temporary_level/_level.py +0 -16
- amulet/mesh/__init__.py +0 -0
- amulet/mesh/block/__init__.pyi +0 -301
- amulet/mesh/block/_cube.py +0 -198
- amulet/mesh/block/_missing_block.py +0 -20
- amulet/mesh/block/block_mesh.cpp +0 -107
- amulet/mesh/block/block_mesh.hpp +0 -207
- amulet/mesh/util.py +0 -17
- amulet/palette/biome_palette.hpp +0 -85
- amulet/palette/block_palette.cpp +0 -32
- amulet/palette/block_palette.hpp +0 -93
- amulet/player.py +0 -62
- amulet/pybind11/collections.hpp +0 -118
- amulet/pybind11/numpy.hpp +0 -26
- amulet/pybind11/py_module.hpp +0 -34
- amulet/pybind11/type_hints.hpp +0 -51
- amulet/pybind11/types.hpp +0 -25
- amulet/pybind11/typing.hpp +0 -7
- amulet/resource_pack/__init__.py +0 -63
- amulet/resource_pack/abc/__init__.py +0 -2
- amulet/resource_pack/abc/resource_pack.py +0 -38
- amulet/resource_pack/abc/resource_pack_manager.py +0 -85
- amulet/resource_pack/java/__init__.py +0 -2
- amulet/resource_pack/java/download_resources.py +0 -212
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_black.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_blue.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_brown.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_cyan.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_gray.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_green.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_blue.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_gray.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_lime.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_magenta.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_orange.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_pink.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_purple.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_red.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_white.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_yellow.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/barrier.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/end_portal.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/grass.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/lava.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/structure_void.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/pack.png +0 -0
- amulet/resource_pack/java/resource_pack.py +0 -44
- amulet/resource_pack/java/resource_pack_manager.py +0 -563
- amulet/resource_pack/unknown_resource_pack.py +0 -10
- amulet/selection/__init__.py +0 -2
- amulet/selection/abstract_selection.py +0 -342
- amulet/selection/box.py +0 -852
- amulet/selection/group.py +0 -481
- amulet/utils/__init__.pyi +0 -23
- amulet/utils/call_spec/__init__.py +0 -24
- amulet/utils/call_spec/_call_spec.py +0 -257
- amulet/utils/cast.py +0 -10
- amulet/utils/comment_json.py +0 -188
- amulet/utils/format_utils.py +0 -41
- amulet/utils/generator.py +0 -18
- amulet/utils/matrix.py +0 -243
- amulet/utils/numpy.hpp +0 -36
- amulet/utils/numpy.pyi +0 -11
- amulet/utils/numpy_helpers.py +0 -19
- amulet/utils/shareable_lock.py +0 -335
- amulet/utils/signal/__init__.py +0 -10
- amulet/utils/signal/_signal.py +0 -228
- amulet/utils/task_manager.py +0 -235
- amulet/utils/typed_property.py +0 -111
- amulet/utils/weakref.py +0 -70
- amulet/utils/world_utils.py +0 -102
- amulet/version.cpp +0 -136
- amulet/version.hpp +0 -142
- amulet/version.py.cpp +0 -281
- amulet_core-2.0a8.dist-info/RECORD +0 -241
- amulet_core-2.0a8.dist-info/entry_points.txt +0 -2
- /amulet/{__pyinstaller → core/__pyinstaller}/__init__.py +0 -0
- /amulet/{py.typed → core/py.typed} +0 -0
- {amulet_core-2.0a8.dist-info → amulet_core-2.0.1a2.post250529084747.dist-info}/top_level.txt +0 -0
amulet/level/_load.py
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
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
|
amulet/level/abc/__init__.py
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
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
|
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import pickle
|
|
4
|
-
from typing import Optional, TYPE_CHECKING, Generic, TypeVar, Callable, Self
|
|
5
|
-
from collections.abc import Iterator, Iterable
|
|
6
|
-
from contextlib import contextmanager
|
|
7
|
-
from threading import RLock
|
|
8
|
-
from abc import ABC, abstractmethod
|
|
9
|
-
|
|
10
|
-
from amulet.utils.shareable_lock import LockNotAcquired
|
|
11
|
-
from amulet.chunk import Chunk, get_null_chunk
|
|
12
|
-
from amulet.data_types import DimensionId
|
|
13
|
-
from amulet.errors import ChunkDoesNotExist, ChunkLoadError
|
|
14
|
-
from amulet.utils.signal import Signal
|
|
15
|
-
from amulet.utils.shareable_lock import ShareableRLock
|
|
16
|
-
|
|
17
|
-
from ._level import LevelFriend, LevelT
|
|
18
|
-
from ._history import HistoryManagerLayer
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if TYPE_CHECKING:
|
|
22
|
-
from ._level import Level
|
|
23
|
-
from ._raw_level import RawDimension
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ChunkT = TypeVar("ChunkT", bound=Chunk)
|
|
27
|
-
RawDimensionT = TypeVar("RawDimensionT", bound="RawDimension")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class ChunkKey(tuple[int, int]):
|
|
31
|
-
def __new__(cls, cx: int, cz: int) -> Self:
|
|
32
|
-
return tuple.__new__(cls, (cx, cz))
|
|
33
|
-
|
|
34
|
-
def __init__(self, cx: int, cz: int) -> None:
|
|
35
|
-
self._bytes: Optional[bytes] = None
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def cx(self) -> int:
|
|
39
|
-
return self[0]
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def cz(self) -> int:
|
|
43
|
-
return self[1]
|
|
44
|
-
|
|
45
|
-
def __bytes__(self) -> bytes:
|
|
46
|
-
if self._bytes is None:
|
|
47
|
-
self._bytes = b"/".join((str(self[0]).encode(), str(self[1]).encode()))
|
|
48
|
-
return self._bytes
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class ChunkHandle(
|
|
52
|
-
LevelFriend[LevelT],
|
|
53
|
-
ABC,
|
|
54
|
-
Generic[LevelT, RawDimensionT, ChunkT],
|
|
55
|
-
):
|
|
56
|
-
"""
|
|
57
|
-
A class which manages chunk data.
|
|
58
|
-
You must acquire the lock for the chunk before reading or writing data.
|
|
59
|
-
Some internal synchronisation is done to catch some threading issues.
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
_lock: ShareableRLock
|
|
63
|
-
_dimension: DimensionId
|
|
64
|
-
_key: ChunkKey
|
|
65
|
-
_chunk_history: HistoryManagerLayer[ChunkKey]
|
|
66
|
-
_chunk_data_history: HistoryManagerLayer[bytes]
|
|
67
|
-
_raw_dimension: Optional[RawDimensionT]
|
|
68
|
-
|
|
69
|
-
__slots__ = (
|
|
70
|
-
"_lock",
|
|
71
|
-
"_dimension_id",
|
|
72
|
-
"_key",
|
|
73
|
-
"_chunk_history",
|
|
74
|
-
"_chunk_data_history",
|
|
75
|
-
"_raw_dimension",
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
def __init__(
|
|
79
|
-
self,
|
|
80
|
-
level_ref: Callable[[], LevelT | None],
|
|
81
|
-
chunk_history: HistoryManagerLayer[ChunkKey],
|
|
82
|
-
chunk_data_history: HistoryManagerLayer[bytes],
|
|
83
|
-
dimension_id: DimensionId,
|
|
84
|
-
cx: int,
|
|
85
|
-
cz: int,
|
|
86
|
-
) -> None:
|
|
87
|
-
super().__init__(level_ref)
|
|
88
|
-
self._lock = ShareableRLock()
|
|
89
|
-
self._dimension_id = dimension_id
|
|
90
|
-
self._key = ChunkKey(cx, cz)
|
|
91
|
-
self._chunk_history = chunk_history
|
|
92
|
-
self._chunk_data_history = chunk_data_history
|
|
93
|
-
self._raw_dimension = None
|
|
94
|
-
|
|
95
|
-
changed = Signal[()]()
|
|
96
|
-
|
|
97
|
-
@property
|
|
98
|
-
def dimension_id(self) -> DimensionId:
|
|
99
|
-
return self._dimension_id
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
def cx(self) -> int:
|
|
103
|
-
return self._key.cx
|
|
104
|
-
|
|
105
|
-
@property
|
|
106
|
-
def cz(self) -> int:
|
|
107
|
-
return self._key.cz
|
|
108
|
-
|
|
109
|
-
def _get_raw_dimension(self) -> RawDimensionT:
|
|
110
|
-
if self._raw_dimension is None:
|
|
111
|
-
self._raw_dimension = self._l.raw.get_dimension(self.dimension_id)
|
|
112
|
-
return self._raw_dimension
|
|
113
|
-
|
|
114
|
-
@contextmanager
|
|
115
|
-
def lock(
|
|
116
|
-
self,
|
|
117
|
-
*,
|
|
118
|
-
blocking: bool = True,
|
|
119
|
-
timeout: float = -1,
|
|
120
|
-
) -> Iterator[None]:
|
|
121
|
-
"""
|
|
122
|
-
Lock access to the chunk.
|
|
123
|
-
|
|
124
|
-
>>> level: Level
|
|
125
|
-
>>> dimension_name: str
|
|
126
|
-
>>> cx: int
|
|
127
|
-
>>> cz: int
|
|
128
|
-
>>> with level.get_dimension(dimension_name).get_chunk_handle(cx, cz).lock():
|
|
129
|
-
>>> # Do what you need to with the chunk
|
|
130
|
-
>>> # No other threads are able to edit or set the chunk while in this with block.
|
|
131
|
-
|
|
132
|
-
If you want to lock, get and set the chunk data :meth:`edit` is probably a better fit.
|
|
133
|
-
|
|
134
|
-
:param blocking: Should this block until the lock is acquired.
|
|
135
|
-
:param timeout: The amount of time to wait for the lock.
|
|
136
|
-
:raises:
|
|
137
|
-
LockNotAcquired: If the lock could not be acquired.
|
|
138
|
-
"""
|
|
139
|
-
return self._lock.unique(blocking, timeout)
|
|
140
|
-
|
|
141
|
-
@contextmanager
|
|
142
|
-
def edit(
|
|
143
|
-
self,
|
|
144
|
-
*,
|
|
145
|
-
components: Iterable[str] | None = None,
|
|
146
|
-
blocking: bool = True,
|
|
147
|
-
timeout: float = -1,
|
|
148
|
-
) -> Iterator[ChunkT | None]:
|
|
149
|
-
"""Lock and edit a chunk.
|
|
150
|
-
|
|
151
|
-
If you only want to access/modify parts of the chunk data you can specify the components you want to load.
|
|
152
|
-
This makes it faster because you don't need to load unneeded parts.
|
|
153
|
-
|
|
154
|
-
>>> level: Level
|
|
155
|
-
>>> dimension_name: str
|
|
156
|
-
>>> cx: int
|
|
157
|
-
>>> cz: int
|
|
158
|
-
>>> with level.get_dimension(dimension_name).get_chunk_handle(cx, cz).edit() as chunk:
|
|
159
|
-
>>> # Edit the chunk data
|
|
160
|
-
>>> # No other threads are able to edit the chunk while in this with block.
|
|
161
|
-
>>> # When the with block exits the edited chunk will be automatically set if no exception occurred.
|
|
162
|
-
|
|
163
|
-
:param components: None to load all components or an iterable of component strings to load.
|
|
164
|
-
:param blocking: Should this block until the lock is acquired.
|
|
165
|
-
:param timeout: The amount of time to wait for the lock.
|
|
166
|
-
:raises:
|
|
167
|
-
LockNotAcquired: If the lock could not be acquired.
|
|
168
|
-
"""
|
|
169
|
-
with self._lock.unique(blocking=blocking, timeout=timeout):
|
|
170
|
-
chunk = self.get(components)
|
|
171
|
-
yield chunk
|
|
172
|
-
# If an exception occurs in user code, this line won't be run.
|
|
173
|
-
self._set(chunk)
|
|
174
|
-
self.changed.emit()
|
|
175
|
-
self._l.changed.emit()
|
|
176
|
-
|
|
177
|
-
def exists(self) -> bool:
|
|
178
|
-
"""
|
|
179
|
-
Does the chunk exist. This is a quick way to check if the chunk exists without loading it.
|
|
180
|
-
|
|
181
|
-
This state may change if the lock is not acquired.
|
|
182
|
-
|
|
183
|
-
:return: True if the chunk exists. Calling get on this chunk handle may still throw ChunkLoadError
|
|
184
|
-
"""
|
|
185
|
-
with self._lock.shared():
|
|
186
|
-
if self._chunk_history.has_resource(self._key):
|
|
187
|
-
return self._chunk_history.resource_exists(self._key)
|
|
188
|
-
else:
|
|
189
|
-
# The history system is not aware of the chunk. Look in the level data
|
|
190
|
-
return self._get_raw_dimension().has_chunk(self.cx, self.cz)
|
|
191
|
-
|
|
192
|
-
def _preload(self) -> None:
|
|
193
|
-
"""
|
|
194
|
-
Load the chunk data if it has not already been loaded.
|
|
195
|
-
The lock must be acquired in unique mode before calling this.
|
|
196
|
-
"""
|
|
197
|
-
if not self._chunk_history.has_resource(self._key):
|
|
198
|
-
# The history system is not aware of the chunk. Load from the level data
|
|
199
|
-
chunk: Chunk
|
|
200
|
-
try:
|
|
201
|
-
raw_chunk = self._get_raw_dimension().get_raw_chunk(self.cx, self.cz)
|
|
202
|
-
chunk = self._get_raw_dimension().raw_chunk_to_native_chunk(
|
|
203
|
-
raw_chunk,
|
|
204
|
-
self.cx,
|
|
205
|
-
self.cz,
|
|
206
|
-
)
|
|
207
|
-
except ChunkDoesNotExist:
|
|
208
|
-
self._chunk_history.set_initial_resource(self._key, b"")
|
|
209
|
-
except ChunkLoadError as e:
|
|
210
|
-
self._chunk_history.set_initial_resource(self._key, pickle.dumps(e))
|
|
211
|
-
else:
|
|
212
|
-
self._chunk_history.set_initial_resource(
|
|
213
|
-
self._key, pickle.dumps(chunk.chunk_id)
|
|
214
|
-
)
|
|
215
|
-
for component_id, component_data in chunk.serialise_chunk().items():
|
|
216
|
-
if component_data is None:
|
|
217
|
-
raise RuntimeError(
|
|
218
|
-
"Component must not be None when initialising chunk"
|
|
219
|
-
)
|
|
220
|
-
self._chunk_data_history.set_initial_resource(
|
|
221
|
-
b"/".join((bytes(self._key), component_id.encode())),
|
|
222
|
-
component_data,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
def _get_null_chunk(self) -> ChunkT:
|
|
226
|
-
"""Get a null chunk instance used for this chunk.
|
|
227
|
-
|
|
228
|
-
:raises:
|
|
229
|
-
ChunkDoesNotExist if the chunk does not exist.
|
|
230
|
-
"""
|
|
231
|
-
data = self._chunk_history.get_resource(self._key)
|
|
232
|
-
if data:
|
|
233
|
-
obj: ChunkLoadError | str = pickle.loads(data)
|
|
234
|
-
if isinstance(obj, ChunkLoadError):
|
|
235
|
-
raise obj
|
|
236
|
-
elif isinstance(obj, str):
|
|
237
|
-
return get_null_chunk(obj) # type: ignore
|
|
238
|
-
else:
|
|
239
|
-
raise RuntimeError
|
|
240
|
-
else:
|
|
241
|
-
raise ChunkDoesNotExist
|
|
242
|
-
|
|
243
|
-
def get_class(self) -> type[ChunkT]:
|
|
244
|
-
"""Get the chunk class used for this chunk.
|
|
245
|
-
|
|
246
|
-
:raises:
|
|
247
|
-
ChunkDoesNotExist if the chunk does not exist.
|
|
248
|
-
"""
|
|
249
|
-
return type(self._get_null_chunk())
|
|
250
|
-
|
|
251
|
-
def get(self, components: Iterable[str] | None = None) -> ChunkT:
|
|
252
|
-
"""Get a unique copy of the chunk data.
|
|
253
|
-
|
|
254
|
-
If you want to edit the chunk, use :meth:`edit` instead.
|
|
255
|
-
|
|
256
|
-
If you only want to access/modify parts of the chunk data you can specify the components you want to load.
|
|
257
|
-
This makes it faster because you don't need to load unneeded parts.
|
|
258
|
-
|
|
259
|
-
:param components: None to load all components or an iterable of component strings to load.
|
|
260
|
-
:return: A unique copy of the chunk data.
|
|
261
|
-
"""
|
|
262
|
-
|
|
263
|
-
def get_chunk() -> ChunkT:
|
|
264
|
-
nonlocal components
|
|
265
|
-
chunk = self._get_null_chunk()
|
|
266
|
-
if components is None:
|
|
267
|
-
components = chunk.component_ids
|
|
268
|
-
else:
|
|
269
|
-
# Ensure all component ids are valid for this class.
|
|
270
|
-
components = set(components).intersection(chunk.component_ids)
|
|
271
|
-
chunk_components = dict[str, bytes | None]()
|
|
272
|
-
for component_id in components:
|
|
273
|
-
chunk_components[component_id] = self._chunk_data_history.get_resource(
|
|
274
|
-
b"/".join((bytes(self._key), component_id.encode()))
|
|
275
|
-
)
|
|
276
|
-
chunk.reconstruct_chunk(chunk_components)
|
|
277
|
-
return chunk
|
|
278
|
-
|
|
279
|
-
# Block if the chunk is locked in unique mode.
|
|
280
|
-
with self._lock.shared():
|
|
281
|
-
if self._chunk_history.has_resource(self._key):
|
|
282
|
-
# Does not need loading from disk.
|
|
283
|
-
return get_chunk()
|
|
284
|
-
|
|
285
|
-
# Acquire the lock in unique mode.
|
|
286
|
-
with self._lock.unique():
|
|
287
|
-
# If it wasn't already loaded by another thread.
|
|
288
|
-
if not self._chunk_history.has_resource(self._key):
|
|
289
|
-
# Load it from disk.
|
|
290
|
-
self._preload()
|
|
291
|
-
|
|
292
|
-
with self._lock.shared():
|
|
293
|
-
# If it was loaded in another thread just read it from the cache.
|
|
294
|
-
return get_chunk()
|
|
295
|
-
|
|
296
|
-
def _set(self, chunk: ChunkT | None) -> None:
|
|
297
|
-
"""lock must be acquired in unique mode before calling this."""
|
|
298
|
-
history = self._chunk_history
|
|
299
|
-
if not history.has_resource(self._key):
|
|
300
|
-
if self._l.history_enabled:
|
|
301
|
-
self._preload()
|
|
302
|
-
else:
|
|
303
|
-
history.set_initial_resource(self._key, b"")
|
|
304
|
-
if chunk is None:
|
|
305
|
-
history.set_resource(self._key, b"")
|
|
306
|
-
else:
|
|
307
|
-
self._validate_chunk(chunk)
|
|
308
|
-
try:
|
|
309
|
-
old_chunk_class = self.get_class()
|
|
310
|
-
except ChunkLoadError:
|
|
311
|
-
old_chunk_class = None
|
|
312
|
-
new_chunk_class = type(chunk)
|
|
313
|
-
component_data = chunk.serialise_chunk()
|
|
314
|
-
if old_chunk_class != new_chunk_class and None in component_data.values():
|
|
315
|
-
raise RuntimeError(
|
|
316
|
-
"When changing chunk class all the data must be present."
|
|
317
|
-
)
|
|
318
|
-
history.set_resource(self._key, pickle.dumps(new_chunk_class))
|
|
319
|
-
for component_id, data in component_data.items():
|
|
320
|
-
if data is None:
|
|
321
|
-
continue
|
|
322
|
-
self._chunk_data_history.set_resource(
|
|
323
|
-
b"/".join((bytes(self._key), component_id.encode())),
|
|
324
|
-
data,
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
@staticmethod
|
|
328
|
-
@abstractmethod
|
|
329
|
-
def _validate_chunk(chunk: ChunkT) -> None:
|
|
330
|
-
raise NotImplementedError
|
|
331
|
-
|
|
332
|
-
def set(self, chunk: ChunkT) -> None:
|
|
333
|
-
"""
|
|
334
|
-
Overwrite the chunk data.
|
|
335
|
-
You must acquire the chunk lock before setting.
|
|
336
|
-
If you want to edit the chunk, use :meth:`edit` instead.
|
|
337
|
-
|
|
338
|
-
:param chunk: The chunk data to set.
|
|
339
|
-
:raises:
|
|
340
|
-
LockNotAcquired: If the chunk is already locked by another thread.
|
|
341
|
-
"""
|
|
342
|
-
with self._lock.unique(blocking=False):
|
|
343
|
-
self._set(chunk)
|
|
344
|
-
self.changed.emit()
|
|
345
|
-
self._l.changed.emit()
|
|
346
|
-
|
|
347
|
-
def delete(self) -> None:
|
|
348
|
-
"""
|
|
349
|
-
Delete the chunk from the level.
|
|
350
|
-
You must acquire the chunk lock before deleting.
|
|
351
|
-
|
|
352
|
-
:raises:
|
|
353
|
-
LockNotAcquired: If the chunk is already locked by another thread.
|
|
354
|
-
"""
|
|
355
|
-
with self._lock.unique(blocking=False):
|
|
356
|
-
self._set(None)
|
|
357
|
-
self.changed.emit()
|
|
358
|
-
self._l.changed.emit()
|
amulet/level/abc/_dimension.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Generic, TypeVar, Callable
|
|
3
|
-
from weakref import WeakValueDictionary
|
|
4
|
-
from threading import Lock
|
|
5
|
-
|
|
6
|
-
from amulet.data_types import DimensionId
|
|
7
|
-
from amulet.block import BlockStack
|
|
8
|
-
from amulet.biome import Biome
|
|
9
|
-
from amulet.selection import SelectionGroup
|
|
10
|
-
|
|
11
|
-
from ._level import LevelFriend, LevelT
|
|
12
|
-
from ._history import HistoryManagerLayer
|
|
13
|
-
from ._chunk_handle import ChunkKey, ChunkHandle
|
|
14
|
-
from ._raw_level import RawDimension
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
ChunkHandleT = TypeVar("ChunkHandleT", bound=ChunkHandle)
|
|
18
|
-
RawDimensionT = TypeVar("RawDimensionT", bound=RawDimension)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Dimension(LevelFriend[LevelT], ABC, Generic[LevelT, RawDimensionT, ChunkHandleT]):
|
|
22
|
-
_dimension_id: DimensionId
|
|
23
|
-
_chunk_handles: WeakValueDictionary[tuple[int, int], ChunkHandleT]
|
|
24
|
-
_chunk_handle_lock: Lock
|
|
25
|
-
_chunk_history: HistoryManagerLayer[ChunkKey]
|
|
26
|
-
_chunk_data_history: HistoryManagerLayer[bytes]
|
|
27
|
-
_raw: RawDimensionT
|
|
28
|
-
|
|
29
|
-
def __init__(
|
|
30
|
-
self, level_ref: Callable[[], LevelT | None], dimension_id: DimensionId
|
|
31
|
-
) -> None:
|
|
32
|
-
super().__init__(level_ref)
|
|
33
|
-
self._dimension_id = dimension_id
|
|
34
|
-
self._chunk_handles = WeakValueDictionary()
|
|
35
|
-
self._chunk_handle_lock = Lock()
|
|
36
|
-
self._chunk_history = self._l._o.history_manager.new_layer()
|
|
37
|
-
self._chunk_data_history = self._l._o.history_manager.new_layer()
|
|
38
|
-
self._raw = self._l.raw.get_dimension(self._dimension_id)
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def dimension_id(self) -> DimensionId:
|
|
42
|
-
return self._dimension_id
|
|
43
|
-
|
|
44
|
-
def bounds(self) -> SelectionGroup:
|
|
45
|
-
"""The editable region of the dimension."""
|
|
46
|
-
return self._raw.bounds()
|
|
47
|
-
|
|
48
|
-
def default_block(self) -> BlockStack:
|
|
49
|
-
"""The default block for this dimension"""
|
|
50
|
-
return self._raw.default_block()
|
|
51
|
-
|
|
52
|
-
def default_biome(self) -> Biome:
|
|
53
|
-
"""The default biome for this dimension"""
|
|
54
|
-
return self._raw.default_biome()
|
|
55
|
-
|
|
56
|
-
def chunk_coords(self) -> set[tuple[int, int]]:
|
|
57
|
-
"""
|
|
58
|
-
The coordinates of every chunk that exists in this dimension.
|
|
59
|
-
|
|
60
|
-
This is the combination of chunks saved to the level and chunks yet to be saved.
|
|
61
|
-
"""
|
|
62
|
-
chunks: set[tuple[int, int]] = set(self._raw.all_chunk_coords())
|
|
63
|
-
for key, state in self._chunk_history.resources_exist_map().items():
|
|
64
|
-
if state:
|
|
65
|
-
chunks.add((key.cx, key.cz))
|
|
66
|
-
else:
|
|
67
|
-
chunks.discard((key.cx, key.cz))
|
|
68
|
-
return chunks
|
|
69
|
-
|
|
70
|
-
def changed_chunk_coords(self) -> set[tuple[int, int]]:
|
|
71
|
-
"""The coordinates of every chunk in this dimension that have been changed since the last save."""
|
|
72
|
-
return {(key.cx, key.cz) for key in self._chunk_history.changed_resources()}
|
|
73
|
-
|
|
74
|
-
@abstractmethod
|
|
75
|
-
def _create_chunk_handle(self, cx: int, cz: int) -> ChunkHandleT:
|
|
76
|
-
raise NotImplementedError
|
|
77
|
-
|
|
78
|
-
def get_chunk_handle(self, cx: int, cz: int) -> ChunkHandleT:
|
|
79
|
-
key = cx, cz
|
|
80
|
-
with self._chunk_handle_lock:
|
|
81
|
-
chunk_handle = self._chunk_handles.get(key)
|
|
82
|
-
if chunk_handle is None:
|
|
83
|
-
chunk_handle = self._chunk_handles[key] = self._create_chunk_handle(
|
|
84
|
-
cx, cz
|
|
85
|
-
)
|
|
86
|
-
return chunk_handle
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ._history_manager import HistoryManager, HistoryManagerLayer
|