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.

Files changed (198) hide show
  1. amulet/__init__.py +27 -27
  2. amulet/__pyinstaller/__init__.py +2 -2
  3. amulet/__pyinstaller/hook-amulet.py +4 -4
  4. amulet/_version.py +21 -21
  5. amulet/api/__init__.py +2 -2
  6. amulet/api/abstract_base_entity.py +128 -128
  7. amulet/api/block.py +630 -630
  8. amulet/api/block_entity.py +71 -71
  9. amulet/api/cache.py +107 -107
  10. amulet/api/chunk/__init__.py +6 -6
  11. amulet/api/chunk/biomes.py +207 -207
  12. amulet/api/chunk/block_entity_dict.py +175 -175
  13. amulet/api/chunk/blocks.py +46 -46
  14. amulet/api/chunk/chunk.py +389 -389
  15. amulet/api/chunk/entity_list.py +75 -75
  16. amulet/api/chunk/status.py +167 -167
  17. amulet/api/data_types/__init__.py +4 -4
  18. amulet/api/data_types/generic_types.py +4 -4
  19. amulet/api/data_types/operation_types.py +16 -16
  20. amulet/api/data_types/world_types.py +49 -49
  21. amulet/api/data_types/wrapper_types.py +71 -71
  22. amulet/api/entity.py +74 -74
  23. amulet/api/errors.py +119 -119
  24. amulet/api/history/__init__.py +36 -36
  25. amulet/api/history/base/__init__.py +3 -3
  26. amulet/api/history/base/base_history.py +26 -26
  27. amulet/api/history/base/history_manager.py +63 -63
  28. amulet/api/history/base/revision_manager.py +73 -73
  29. amulet/api/history/changeable.py +15 -15
  30. amulet/api/history/data_types.py +7 -7
  31. amulet/api/history/history_manager/__init__.py +3 -3
  32. amulet/api/history/history_manager/container.py +102 -102
  33. amulet/api/history/history_manager/database.py +279 -279
  34. amulet/api/history/history_manager/meta.py +93 -93
  35. amulet/api/history/history_manager/object.py +116 -116
  36. amulet/api/history/revision_manager/__init__.py +2 -2
  37. amulet/api/history/revision_manager/disk.py +33 -33
  38. amulet/api/history/revision_manager/ram.py +12 -12
  39. amulet/api/item.py +75 -75
  40. amulet/api/level/__init__.py +4 -4
  41. amulet/api/level/base_level/__init__.py +1 -1
  42. amulet/api/level/base_level/base_level.py +1035 -1026
  43. amulet/api/level/base_level/chunk_manager.py +227 -227
  44. amulet/api/level/base_level/clone.py +389 -389
  45. amulet/api/level/base_level/player_manager.py +101 -101
  46. amulet/api/level/immutable_structure/__init__.py +1 -1
  47. amulet/api/level/immutable_structure/immutable_structure.py +94 -94
  48. amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
  49. amulet/api/level/structure.py +22 -22
  50. amulet/api/level/world.py +19 -19
  51. amulet/api/partial_3d_array/__init__.py +2 -2
  52. amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
  53. amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
  54. amulet/api/partial_3d_array/data_types.py +15 -15
  55. amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
  56. amulet/api/partial_3d_array/util.py +152 -152
  57. amulet/api/player.py +65 -65
  58. amulet/api/registry/__init__.py +2 -2
  59. amulet/api/registry/base_registry.py +34 -34
  60. amulet/api/registry/biome_manager.py +153 -153
  61. amulet/api/registry/block_manager.py +156 -156
  62. amulet/api/selection/__init__.py +2 -2
  63. amulet/api/selection/abstract_selection.py +315 -315
  64. amulet/api/selection/box.py +805 -805
  65. amulet/api/selection/group.py +488 -488
  66. amulet/api/structure.py +37 -37
  67. amulet/api/wrapper/__init__.py +8 -8
  68. amulet/api/wrapper/chunk/interface.py +441 -441
  69. amulet/api/wrapper/chunk/translator.py +567 -567
  70. amulet/api/wrapper/format_wrapper.py +772 -772
  71. amulet/api/wrapper/structure_format_wrapper.py +116 -116
  72. amulet/api/wrapper/world_format_wrapper.py +63 -63
  73. amulet/level/__init__.py +1 -1
  74. amulet/level/formats/anvil_forge_world.py +40 -40
  75. amulet/level/formats/anvil_world/__init__.py +3 -3
  76. amulet/level/formats/anvil_world/_sector_manager.py +291 -384
  77. amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
  78. amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
  79. amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
  80. amulet/level/formats/anvil_world/dimension.py +177 -177
  81. amulet/level/formats/anvil_world/format.py +769 -769
  82. amulet/level/formats/anvil_world/region.py +384 -384
  83. amulet/level/formats/construction/__init__.py +3 -3
  84. amulet/level/formats/construction/format_wrapper.py +515 -515
  85. amulet/level/formats/construction/interface.py +134 -134
  86. amulet/level/formats/construction/section.py +60 -60
  87. amulet/level/formats/construction/util.py +165 -165
  88. amulet/level/formats/leveldb_world/__init__.py +3 -3
  89. amulet/level/formats/leveldb_world/chunk.py +33 -33
  90. amulet/level/formats/leveldb_world/dimension.py +385 -419
  91. amulet/level/formats/leveldb_world/format.py +659 -641
  92. amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
  93. amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
  94. amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
  95. amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
  96. amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
  97. amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
  98. amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
  99. amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
  100. amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
  101. amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
  102. amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
  103. amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
  104. amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
  105. amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
  106. amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
  107. amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
  108. amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
  109. amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
  110. amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
  111. amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
  112. amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
  113. amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
  114. amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
  115. amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
  116. amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
  117. amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
  118. amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
  119. amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
  120. amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
  121. amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
  122. amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
  123. amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
  124. amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
  125. amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
  126. amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
  127. amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
  128. amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
  129. amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
  130. amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
  131. amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
  132. amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
  133. amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
  134. amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
  135. amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
  136. amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
  137. amulet/level/formats/mcstructure/__init__.py +3 -3
  138. amulet/level/formats/mcstructure/chunk.py +50 -50
  139. amulet/level/formats/mcstructure/format_wrapper.py +408 -408
  140. amulet/level/formats/mcstructure/interface.py +175 -175
  141. amulet/level/formats/schematic/__init__.py +3 -3
  142. amulet/level/formats/schematic/chunk.py +55 -55
  143. amulet/level/formats/schematic/data_types.py +4 -4
  144. amulet/level/formats/schematic/format_wrapper.py +373 -373
  145. amulet/level/formats/schematic/interface.py +142 -142
  146. amulet/level/formats/sponge_schem/__init__.py +4 -4
  147. amulet/level/formats/sponge_schem/chunk.py +62 -62
  148. amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
  149. amulet/level/formats/sponge_schem/interface.py +118 -118
  150. amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
  151. amulet/level/formats/sponge_schem/varint/varint.py +87 -87
  152. amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
  153. amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
  154. amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
  155. amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
  156. amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
  157. amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
  158. amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
  159. amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
  160. amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
  161. amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
  162. amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
  163. amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
  164. amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
  165. amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
  166. amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
  167. amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
  168. amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
  169. amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
  170. amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
  171. amulet/level/load.py +59 -59
  172. amulet/level/loader.py +95 -95
  173. amulet/level/translators/chunk/bedrock/__init__.py +267 -267
  174. amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
  175. amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
  176. amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
  177. amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
  178. amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
  179. amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
  180. amulet/libs/leveldb/__init__.py +7 -7
  181. amulet/operations/__init__.py +5 -5
  182. amulet/operations/clone.py +18 -18
  183. amulet/operations/delete_chunk.py +32 -32
  184. amulet/operations/fill.py +30 -30
  185. amulet/operations/paste.py +65 -65
  186. amulet/operations/replace.py +58 -58
  187. amulet/utils/__init__.py +14 -14
  188. amulet/utils/format_utils.py +41 -41
  189. amulet/utils/generator.py +15 -15
  190. amulet/utils/matrix.py +243 -243
  191. amulet/utils/numpy_helpers.py +46 -46
  192. amulet/utils/world_utils.py +349 -349
  193. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
  194. amulet_core-1.9.20.dist-info/RECORD +208 -0
  195. amulet_core-1.9.19.dist-info/RECORD +0 -208
  196. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
  197. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
  198. {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)