amulet-core 1.9.19__py3-none-any.whl → 1.9.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of amulet-core might be problematic. Click here for more details.
- amulet/__init__.py +27 -27
- amulet/__pyinstaller/__init__.py +2 -2
- amulet/__pyinstaller/hook-amulet.py +4 -4
- amulet/_version.py +21 -21
- amulet/api/__init__.py +2 -2
- amulet/api/abstract_base_entity.py +128 -128
- amulet/api/block.py +630 -630
- amulet/api/block_entity.py +71 -71
- amulet/api/cache.py +107 -107
- amulet/api/chunk/__init__.py +6 -6
- amulet/api/chunk/biomes.py +207 -207
- amulet/api/chunk/block_entity_dict.py +175 -175
- amulet/api/chunk/blocks.py +46 -46
- amulet/api/chunk/chunk.py +389 -389
- amulet/api/chunk/entity_list.py +75 -75
- amulet/api/chunk/status.py +167 -167
- amulet/api/data_types/__init__.py +4 -4
- amulet/api/data_types/generic_types.py +4 -4
- amulet/api/data_types/operation_types.py +16 -16
- amulet/api/data_types/world_types.py +49 -49
- amulet/api/data_types/wrapper_types.py +71 -71
- amulet/api/entity.py +74 -74
- amulet/api/errors.py +119 -119
- amulet/api/history/__init__.py +36 -36
- amulet/api/history/base/__init__.py +3 -3
- amulet/api/history/base/base_history.py +26 -26
- amulet/api/history/base/history_manager.py +63 -63
- amulet/api/history/base/revision_manager.py +73 -73
- amulet/api/history/changeable.py +15 -15
- amulet/api/history/data_types.py +7 -7
- amulet/api/history/history_manager/__init__.py +3 -3
- amulet/api/history/history_manager/container.py +102 -102
- amulet/api/history/history_manager/database.py +279 -279
- amulet/api/history/history_manager/meta.py +93 -93
- amulet/api/history/history_manager/object.py +116 -116
- amulet/api/history/revision_manager/__init__.py +2 -2
- amulet/api/history/revision_manager/disk.py +33 -33
- amulet/api/history/revision_manager/ram.py +12 -12
- amulet/api/item.py +75 -75
- amulet/api/level/__init__.py +4 -4
- amulet/api/level/base_level/__init__.py +1 -1
- amulet/api/level/base_level/base_level.py +1035 -1026
- amulet/api/level/base_level/chunk_manager.py +227 -227
- amulet/api/level/base_level/clone.py +389 -389
- amulet/api/level/base_level/player_manager.py +101 -101
- amulet/api/level/immutable_structure/__init__.py +1 -1
- amulet/api/level/immutable_structure/immutable_structure.py +94 -94
- amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
- amulet/api/level/structure.py +22 -22
- amulet/api/level/world.py +19 -19
- amulet/api/partial_3d_array/__init__.py +2 -2
- amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
- amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
- amulet/api/partial_3d_array/data_types.py +15 -15
- amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
- amulet/api/partial_3d_array/util.py +152 -152
- amulet/api/player.py +65 -65
- amulet/api/registry/__init__.py +2 -2
- amulet/api/registry/base_registry.py +34 -34
- amulet/api/registry/biome_manager.py +153 -153
- amulet/api/registry/block_manager.py +156 -156
- amulet/api/selection/__init__.py +2 -2
- amulet/api/selection/abstract_selection.py +315 -315
- amulet/api/selection/box.py +805 -805
- amulet/api/selection/group.py +488 -488
- amulet/api/structure.py +37 -37
- amulet/api/wrapper/__init__.py +8 -8
- amulet/api/wrapper/chunk/interface.py +441 -441
- amulet/api/wrapper/chunk/translator.py +567 -567
- amulet/api/wrapper/format_wrapper.py +772 -772
- amulet/api/wrapper/structure_format_wrapper.py +116 -116
- amulet/api/wrapper/world_format_wrapper.py +63 -63
- amulet/level/__init__.py +1 -1
- amulet/level/formats/anvil_forge_world.py +40 -40
- amulet/level/formats/anvil_world/__init__.py +3 -3
- amulet/level/formats/anvil_world/_sector_manager.py +291 -384
- amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
- amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
- amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
- amulet/level/formats/anvil_world/dimension.py +177 -177
- amulet/level/formats/anvil_world/format.py +769 -769
- amulet/level/formats/anvil_world/region.py +384 -384
- amulet/level/formats/construction/__init__.py +3 -3
- amulet/level/formats/construction/format_wrapper.py +515 -515
- amulet/level/formats/construction/interface.py +134 -134
- amulet/level/formats/construction/section.py +60 -60
- amulet/level/formats/construction/util.py +165 -165
- amulet/level/formats/leveldb_world/__init__.py +3 -3
- amulet/level/formats/leveldb_world/chunk.py +33 -33
- amulet/level/formats/leveldb_world/dimension.py +385 -419
- amulet/level/formats/leveldb_world/format.py +659 -641
- amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
- amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
- amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
- amulet/level/formats/mcstructure/__init__.py +3 -3
- amulet/level/formats/mcstructure/chunk.py +50 -50
- amulet/level/formats/mcstructure/format_wrapper.py +408 -408
- amulet/level/formats/mcstructure/interface.py +175 -175
- amulet/level/formats/schematic/__init__.py +3 -3
- amulet/level/formats/schematic/chunk.py +55 -55
- amulet/level/formats/schematic/data_types.py +4 -4
- amulet/level/formats/schematic/format_wrapper.py +373 -373
- amulet/level/formats/schematic/interface.py +142 -142
- amulet/level/formats/sponge_schem/__init__.py +4 -4
- amulet/level/formats/sponge_schem/chunk.py +62 -62
- amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
- amulet/level/formats/sponge_schem/interface.py +118 -118
- amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
- amulet/level/formats/sponge_schem/varint/varint.py +87 -87
- amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
- amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
- amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
- amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
- amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
- amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
- amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
- amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
- amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
- amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
- amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
- amulet/level/load.py +59 -59
- amulet/level/loader.py +95 -95
- amulet/level/translators/chunk/bedrock/__init__.py +267 -267
- amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
- amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
- amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
- amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
- amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
- amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
- amulet/libs/leveldb/__init__.py +7 -7
- amulet/operations/__init__.py +5 -5
- amulet/operations/clone.py +18 -18
- amulet/operations/delete_chunk.py +32 -32
- amulet/operations/fill.py +30 -30
- amulet/operations/paste.py +65 -65
- amulet/operations/replace.py +58 -58
- amulet/utils/__init__.py +14 -14
- amulet/utils/format_utils.py +41 -41
- amulet/utils/generator.py +15 -15
- amulet/utils/matrix.py +243 -243
- amulet/utils/numpy_helpers.py +46 -46
- amulet/utils/world_utils.py +349 -349
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
- amulet_core-1.9.20.dist-info/RECORD +208 -0
- amulet_core-1.9.19.dist-info/RECORD +0 -208
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
|
@@ -1,177 +1,177 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from typing import Dict, Iterable
|
|
5
|
-
import re
|
|
6
|
-
import threading
|
|
7
|
-
|
|
8
|
-
from amulet_nbt import NamedTag
|
|
9
|
-
|
|
10
|
-
from amulet.utils import world_utils
|
|
11
|
-
from amulet.api.errors import ChunkDoesNotExist
|
|
12
|
-
from amulet.api.data_types import (
|
|
13
|
-
ChunkCoordinates,
|
|
14
|
-
RegionCoordinates,
|
|
15
|
-
)
|
|
16
|
-
from .region import AnvilRegionInterface
|
|
17
|
-
|
|
18
|
-
InternalDimension = str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ChunkDataType = Dict[str, NamedTag]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class AnvilDimensionManager:
|
|
25
|
-
"""
|
|
26
|
-
A class to manage the data for a dimension.
|
|
27
|
-
This can consist of multiple layers. Eg the region layer which contains chunk data and the entities layer which contains entities.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
level_regex = re.compile(r"DIM(?P<level>-?\d+)")
|
|
31
|
-
|
|
32
|
-
def __init__(self, directory: str, *, mcc=False, layers=("region",)):
|
|
33
|
-
self._directory = directory
|
|
34
|
-
self._mcc = mcc
|
|
35
|
-
self.__layers: Dict[str, AnvilRegionManager] = {
|
|
36
|
-
layer: AnvilRegionManager(
|
|
37
|
-
os.path.join(self._directory, layer), mcc=self._mcc
|
|
38
|
-
)
|
|
39
|
-
for layer in layers
|
|
40
|
-
}
|
|
41
|
-
self.__default_layer = self.__layers[layers[0]]
|
|
42
|
-
|
|
43
|
-
def all_chunk_coords(self) -> Iterable[ChunkCoordinates]:
|
|
44
|
-
yield from self.__default_layer.all_chunk_coords()
|
|
45
|
-
|
|
46
|
-
def has_chunk(self, cx: int, cz: int) -> bool:
|
|
47
|
-
return self.__default_layer.has_chunk(cx, cz)
|
|
48
|
-
|
|
49
|
-
def unload(self):
|
|
50
|
-
for layer in self.__layers.values():
|
|
51
|
-
layer.unload()
|
|
52
|
-
|
|
53
|
-
def get_chunk_data(self, cx: int, cz: int) -> NamedTag:
|
|
54
|
-
"""
|
|
55
|
-
Get a NamedTag of a chunk from the database.
|
|
56
|
-
Will raise ChunkDoesNotExist if the region or chunk does not exist
|
|
57
|
-
"""
|
|
58
|
-
# get the region key
|
|
59
|
-
return self.__default_layer.get_chunk_data(cx, cz)
|
|
60
|
-
|
|
61
|
-
def get_chunk_data_layers(self, cx: int, cz: int) -> ChunkDataType:
|
|
62
|
-
"""Get the chunk data for each layer"""
|
|
63
|
-
chunk_data = {}
|
|
64
|
-
for layer_name, layer in self.__layers.items():
|
|
65
|
-
try:
|
|
66
|
-
chunk_data[layer_name] = layer.get_chunk_data(cx, cz)
|
|
67
|
-
except ChunkDoesNotExist:
|
|
68
|
-
pass
|
|
69
|
-
|
|
70
|
-
if chunk_data:
|
|
71
|
-
return chunk_data
|
|
72
|
-
else:
|
|
73
|
-
raise ChunkDoesNotExist
|
|
74
|
-
|
|
75
|
-
def put_chunk_data(self, cx: int, cz: int, data: NamedTag):
|
|
76
|
-
"""pass data to the region file class"""
|
|
77
|
-
self.__default_layer.put_chunk_data(cx, cz, data)
|
|
78
|
-
|
|
79
|
-
def put_chunk_data_layers(self, cx: int, cz: int, data_layers: ChunkDataType):
|
|
80
|
-
"""Put one or more layers of data"""
|
|
81
|
-
for layer_name, data in data_layers.items():
|
|
82
|
-
if (
|
|
83
|
-
layer_name not in self.__layers
|
|
84
|
-
and layer_name.isalpha()
|
|
85
|
-
and layer_name.islower()
|
|
86
|
-
):
|
|
87
|
-
self.__layers[layer_name] = AnvilRegionManager(
|
|
88
|
-
os.path.join(self._directory, layer_name), mcc=self._mcc
|
|
89
|
-
)
|
|
90
|
-
if layer_name in self.__layers:
|
|
91
|
-
self.__layers[layer_name].put_chunk_data(cx, cz, data)
|
|
92
|
-
|
|
93
|
-
def delete_chunk(self, cx: int, cz: int):
|
|
94
|
-
for layer in self.__layers.values():
|
|
95
|
-
layer.delete_chunk(cx, cz)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class AnvilRegionManager:
|
|
99
|
-
"""A class to manage a directory of region files."""
|
|
100
|
-
|
|
101
|
-
def __init__(self, directory: str, *, mcc=False):
|
|
102
|
-
self._directory = directory
|
|
103
|
-
self._regions: Dict[RegionCoordinates, AnvilRegionInterface] = {}
|
|
104
|
-
self._mcc = mcc
|
|
105
|
-
self._lock = threading.RLock()
|
|
106
|
-
|
|
107
|
-
def unload(self):
|
|
108
|
-
with self._lock:
|
|
109
|
-
self._regions.clear()
|
|
110
|
-
|
|
111
|
-
def _region_path(self, rx, rz) -> str:
|
|
112
|
-
"""Get the file path for a region file."""
|
|
113
|
-
return os.path.join(self._directory, f"r.{rx}.{rz}.mca")
|
|
114
|
-
|
|
115
|
-
def _has_region(self, rx: int, rz: int) -> bool:
|
|
116
|
-
"""Does a region file exist."""
|
|
117
|
-
return os.path.isfile(self._region_path(rx, rz))
|
|
118
|
-
|
|
119
|
-
def _get_region(self, rx: int, rz: int, create=False) -> AnvilRegionInterface:
|
|
120
|
-
with self._lock:
|
|
121
|
-
if (rx, rz) in self._regions:
|
|
122
|
-
return self._regions[(rx, rz)]
|
|
123
|
-
elif create or self._has_region(rx, rz):
|
|
124
|
-
region = self._regions[(rx, rz)] = AnvilRegionInterface(
|
|
125
|
-
self._region_path(rx, rz), mcc=self._mcc
|
|
126
|
-
)
|
|
127
|
-
return region
|
|
128
|
-
else:
|
|
129
|
-
raise ChunkDoesNotExist
|
|
130
|
-
|
|
131
|
-
def _iter_regions(self) -> Iterable[AnvilRegionInterface]:
|
|
132
|
-
if os.path.isdir(self._directory):
|
|
133
|
-
for region_file_name in os.listdir(self._directory):
|
|
134
|
-
rx, rz = AnvilRegionInterface.get_coords(region_file_name)
|
|
135
|
-
if rx is None:
|
|
136
|
-
continue
|
|
137
|
-
yield self._get_region(rx, rz)
|
|
138
|
-
|
|
139
|
-
def all_chunk_coords(self) -> Iterable[ChunkCoordinates]:
|
|
140
|
-
for region in self._iter_regions():
|
|
141
|
-
yield from region.all_chunk_coords()
|
|
142
|
-
|
|
143
|
-
def has_chunk(self, cx: int, cz: int) -> bool:
|
|
144
|
-
try:
|
|
145
|
-
region = self._get_region(
|
|
146
|
-
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
147
|
-
)
|
|
148
|
-
except ChunkDoesNotExist:
|
|
149
|
-
return False
|
|
150
|
-
else:
|
|
151
|
-
return region.has_chunk(cx & 0x1F, cz & 0x1F)
|
|
152
|
-
|
|
153
|
-
def get_chunk_data(self, cx: int, cz: int) -> NamedTag:
|
|
154
|
-
"""
|
|
155
|
-
Get a NamedTag of a chunk from the database.
|
|
156
|
-
Will raise ChunkDoesNotExist if the region or chunk does not exist
|
|
157
|
-
"""
|
|
158
|
-
# get the region key
|
|
159
|
-
return self._get_region(
|
|
160
|
-
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
161
|
-
).get_data(cx & 0x1F, cz & 0x1F)
|
|
162
|
-
|
|
163
|
-
def put_chunk_data(self, cx: int, cz: int, data: NamedTag):
|
|
164
|
-
"""pass data to the region file class"""
|
|
165
|
-
self._get_region(
|
|
166
|
-
*world_utils.chunk_coords_to_region_coords(cx, cz), create=True
|
|
167
|
-
).write_data(cx & 0x1F, cz & 0x1F, data)
|
|
168
|
-
|
|
169
|
-
def delete_chunk(self, cx: int, cz: int):
|
|
170
|
-
try:
|
|
171
|
-
region = self._get_region(
|
|
172
|
-
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
173
|
-
)
|
|
174
|
-
except ChunkDoesNotExist:
|
|
175
|
-
pass
|
|
176
|
-
else:
|
|
177
|
-
region.delete_data(cx & 0x1F, cz & 0x1F)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Dict, Iterable
|
|
5
|
+
import re
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
from amulet_nbt import NamedTag
|
|
9
|
+
|
|
10
|
+
from amulet.utils import world_utils
|
|
11
|
+
from amulet.api.errors import ChunkDoesNotExist
|
|
12
|
+
from amulet.api.data_types import (
|
|
13
|
+
ChunkCoordinates,
|
|
14
|
+
RegionCoordinates,
|
|
15
|
+
)
|
|
16
|
+
from .region import AnvilRegionInterface
|
|
17
|
+
|
|
18
|
+
InternalDimension = str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
ChunkDataType = Dict[str, NamedTag]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AnvilDimensionManager:
|
|
25
|
+
"""
|
|
26
|
+
A class to manage the data for a dimension.
|
|
27
|
+
This can consist of multiple layers. Eg the region layer which contains chunk data and the entities layer which contains entities.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
level_regex = re.compile(r"DIM(?P<level>-?\d+)")
|
|
31
|
+
|
|
32
|
+
def __init__(self, directory: str, *, mcc=False, layers=("region",)):
|
|
33
|
+
self._directory = directory
|
|
34
|
+
self._mcc = mcc
|
|
35
|
+
self.__layers: Dict[str, AnvilRegionManager] = {
|
|
36
|
+
layer: AnvilRegionManager(
|
|
37
|
+
os.path.join(self._directory, layer), mcc=self._mcc
|
|
38
|
+
)
|
|
39
|
+
for layer in layers
|
|
40
|
+
}
|
|
41
|
+
self.__default_layer = self.__layers[layers[0]]
|
|
42
|
+
|
|
43
|
+
def all_chunk_coords(self) -> Iterable[ChunkCoordinates]:
|
|
44
|
+
yield from self.__default_layer.all_chunk_coords()
|
|
45
|
+
|
|
46
|
+
def has_chunk(self, cx: int, cz: int) -> bool:
|
|
47
|
+
return self.__default_layer.has_chunk(cx, cz)
|
|
48
|
+
|
|
49
|
+
def unload(self):
|
|
50
|
+
for layer in self.__layers.values():
|
|
51
|
+
layer.unload()
|
|
52
|
+
|
|
53
|
+
def get_chunk_data(self, cx: int, cz: int) -> NamedTag:
|
|
54
|
+
"""
|
|
55
|
+
Get a NamedTag of a chunk from the database.
|
|
56
|
+
Will raise ChunkDoesNotExist if the region or chunk does not exist
|
|
57
|
+
"""
|
|
58
|
+
# get the region key
|
|
59
|
+
return self.__default_layer.get_chunk_data(cx, cz)
|
|
60
|
+
|
|
61
|
+
def get_chunk_data_layers(self, cx: int, cz: int) -> ChunkDataType:
|
|
62
|
+
"""Get the chunk data for each layer"""
|
|
63
|
+
chunk_data = {}
|
|
64
|
+
for layer_name, layer in self.__layers.items():
|
|
65
|
+
try:
|
|
66
|
+
chunk_data[layer_name] = layer.get_chunk_data(cx, cz)
|
|
67
|
+
except ChunkDoesNotExist:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
if chunk_data:
|
|
71
|
+
return chunk_data
|
|
72
|
+
else:
|
|
73
|
+
raise ChunkDoesNotExist
|
|
74
|
+
|
|
75
|
+
def put_chunk_data(self, cx: int, cz: int, data: NamedTag):
|
|
76
|
+
"""pass data to the region file class"""
|
|
77
|
+
self.__default_layer.put_chunk_data(cx, cz, data)
|
|
78
|
+
|
|
79
|
+
def put_chunk_data_layers(self, cx: int, cz: int, data_layers: ChunkDataType):
|
|
80
|
+
"""Put one or more layers of data"""
|
|
81
|
+
for layer_name, data in data_layers.items():
|
|
82
|
+
if (
|
|
83
|
+
layer_name not in self.__layers
|
|
84
|
+
and layer_name.isalpha()
|
|
85
|
+
and layer_name.islower()
|
|
86
|
+
):
|
|
87
|
+
self.__layers[layer_name] = AnvilRegionManager(
|
|
88
|
+
os.path.join(self._directory, layer_name), mcc=self._mcc
|
|
89
|
+
)
|
|
90
|
+
if layer_name in self.__layers:
|
|
91
|
+
self.__layers[layer_name].put_chunk_data(cx, cz, data)
|
|
92
|
+
|
|
93
|
+
def delete_chunk(self, cx: int, cz: int):
|
|
94
|
+
for layer in self.__layers.values():
|
|
95
|
+
layer.delete_chunk(cx, cz)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AnvilRegionManager:
|
|
99
|
+
"""A class to manage a directory of region files."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, directory: str, *, mcc=False):
|
|
102
|
+
self._directory = directory
|
|
103
|
+
self._regions: Dict[RegionCoordinates, AnvilRegionInterface] = {}
|
|
104
|
+
self._mcc = mcc
|
|
105
|
+
self._lock = threading.RLock()
|
|
106
|
+
|
|
107
|
+
def unload(self):
|
|
108
|
+
with self._lock:
|
|
109
|
+
self._regions.clear()
|
|
110
|
+
|
|
111
|
+
def _region_path(self, rx, rz) -> str:
|
|
112
|
+
"""Get the file path for a region file."""
|
|
113
|
+
return os.path.join(self._directory, f"r.{rx}.{rz}.mca")
|
|
114
|
+
|
|
115
|
+
def _has_region(self, rx: int, rz: int) -> bool:
|
|
116
|
+
"""Does a region file exist."""
|
|
117
|
+
return os.path.isfile(self._region_path(rx, rz))
|
|
118
|
+
|
|
119
|
+
def _get_region(self, rx: int, rz: int, create=False) -> AnvilRegionInterface:
|
|
120
|
+
with self._lock:
|
|
121
|
+
if (rx, rz) in self._regions:
|
|
122
|
+
return self._regions[(rx, rz)]
|
|
123
|
+
elif create or self._has_region(rx, rz):
|
|
124
|
+
region = self._regions[(rx, rz)] = AnvilRegionInterface(
|
|
125
|
+
self._region_path(rx, rz), mcc=self._mcc
|
|
126
|
+
)
|
|
127
|
+
return region
|
|
128
|
+
else:
|
|
129
|
+
raise ChunkDoesNotExist
|
|
130
|
+
|
|
131
|
+
def _iter_regions(self) -> Iterable[AnvilRegionInterface]:
|
|
132
|
+
if os.path.isdir(self._directory):
|
|
133
|
+
for region_file_name in os.listdir(self._directory):
|
|
134
|
+
rx, rz = AnvilRegionInterface.get_coords(region_file_name)
|
|
135
|
+
if rx is None:
|
|
136
|
+
continue
|
|
137
|
+
yield self._get_region(rx, rz)
|
|
138
|
+
|
|
139
|
+
def all_chunk_coords(self) -> Iterable[ChunkCoordinates]:
|
|
140
|
+
for region in self._iter_regions():
|
|
141
|
+
yield from region.all_chunk_coords()
|
|
142
|
+
|
|
143
|
+
def has_chunk(self, cx: int, cz: int) -> bool:
|
|
144
|
+
try:
|
|
145
|
+
region = self._get_region(
|
|
146
|
+
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
147
|
+
)
|
|
148
|
+
except ChunkDoesNotExist:
|
|
149
|
+
return False
|
|
150
|
+
else:
|
|
151
|
+
return region.has_chunk(cx & 0x1F, cz & 0x1F)
|
|
152
|
+
|
|
153
|
+
def get_chunk_data(self, cx: int, cz: int) -> NamedTag:
|
|
154
|
+
"""
|
|
155
|
+
Get a NamedTag of a chunk from the database.
|
|
156
|
+
Will raise ChunkDoesNotExist if the region or chunk does not exist
|
|
157
|
+
"""
|
|
158
|
+
# get the region key
|
|
159
|
+
return self._get_region(
|
|
160
|
+
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
161
|
+
).get_data(cx & 0x1F, cz & 0x1F)
|
|
162
|
+
|
|
163
|
+
def put_chunk_data(self, cx: int, cz: int, data: NamedTag):
|
|
164
|
+
"""pass data to the region file class"""
|
|
165
|
+
self._get_region(
|
|
166
|
+
*world_utils.chunk_coords_to_region_coords(cx, cz), create=True
|
|
167
|
+
).write_data(cx & 0x1F, cz & 0x1F, data)
|
|
168
|
+
|
|
169
|
+
def delete_chunk(self, cx: int, cz: int):
|
|
170
|
+
try:
|
|
171
|
+
region = self._get_region(
|
|
172
|
+
*world_utils.chunk_coords_to_region_coords(cx, cz)
|
|
173
|
+
)
|
|
174
|
+
except ChunkDoesNotExist:
|
|
175
|
+
pass
|
|
176
|
+
else:
|
|
177
|
+
region.delete_data(cx & 0x1F, cz & 0x1F)
|