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,2 +1,2 @@
1
- from .data_pack import DataPack
2
- from .data_pack_manager import DataPackManager
1
+ from .data_pack import DataPack
2
+ from .data_pack_manager import DataPackManager
@@ -1,224 +1,224 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Iterable, BinaryIO
3
- import os
4
- import json
5
- from zipfile import ZipFile
6
- import re
7
-
8
-
9
- class BaseWrapper(ABC):
10
- def __init__(self, path: str):
11
- self._path = path
12
-
13
- @staticmethod
14
- @abstractmethod
15
- def is_valid(path: str) -> bool:
16
- """
17
- Is the given path valid for this class.
18
-
19
- :param path: The path to test.
20
- """
21
-
22
- @property
23
- def path(self) -> str:
24
- """The path to the data."""
25
- return self._path
26
-
27
- @property
28
- @abstractmethod
29
- def all_files(self) -> Iterable[str]:
30
- """
31
- The relative paths of all files contained within.
32
-
33
- :return: An iterable of paths.
34
- """
35
- raise NotImplementedError
36
-
37
- def all_files_match(self, match: str) -> Iterable[str]:
38
- """
39
- The relative paths of all files contained within that match the given regex.
40
-
41
- :param match: The regex string to match again.
42
- :return: An iterable of file paths that match the given regex.
43
- """
44
- re_match = re.compile(match)
45
- return tuple(path for path in self.all_files if re_match.fullmatch(path))
46
-
47
- @abstractmethod
48
- def has_file(self, relative_path: str) -> bool:
49
- """
50
- Does the requested file exist.
51
-
52
- :param relative_path:
53
- :return:
54
- """
55
- raise NotImplementedError
56
-
57
- @abstractmethod
58
- def open(self, relative_path: str, **kwargs) -> BinaryIO:
59
- """
60
- Get the contents of the file.
61
-
62
- :param relative_path:
63
- :return:
64
- """
65
- raise NotImplementedError
66
-
67
- @abstractmethod
68
- def close(self):
69
- """Close the contents."""
70
- raise NotImplementedError
71
-
72
-
73
- class ZipWrapper(BaseWrapper, ZipFile):
74
- def __init__(self, path: str):
75
- BaseWrapper.__init__(self, path)
76
- ZipFile.__init__(self, path)
77
-
78
- @staticmethod
79
- def is_valid(path: str) -> bool:
80
- return os.path.isfile(path) and path.endswith(".zip")
81
-
82
- @property
83
- def all_files(self) -> Iterable[str]:
84
- for path in self.NameToInfo:
85
- if not path.endswith("/"):
86
- yield path
87
-
88
- def has_file(self, relative_path: str) -> bool:
89
- if relative_path.endswith("/"):
90
- # is a directory
91
- return False
92
- else:
93
- return relative_path in self.NameToInfo
94
-
95
- open = ZipFile.open
96
- close = ZipFile.close
97
-
98
-
99
- class DirWrapper(BaseWrapper):
100
- def __init__(self, path: str):
101
- BaseWrapper.__init__(self, path)
102
-
103
- @staticmethod
104
- def is_valid(path: str) -> bool:
105
- return os.path.isdir(path)
106
-
107
- @property
108
- def all_files(self) -> Iterable[str]:
109
- for abs_path, _, files in os.walk(self.path):
110
- rel_path = os.path.normpath(os.path.relpath(abs_path, self.path)).replace(
111
- os.sep, "/"
112
- )
113
- if rel_path == ".":
114
- yield from files
115
- else:
116
- for f in files:
117
- yield f"{rel_path}/{f}"
118
-
119
- def has_file(self, relative_path: str) -> bool:
120
- return os.path.isfile(os.path.join(self.path, relative_path))
121
-
122
- def open(self, relative_path: str, **kwargs) -> BinaryIO:
123
- return open(os.path.join(self.path, relative_path), "rb", **kwargs)
124
-
125
- def close(self):
126
- pass
127
-
128
-
129
- def _open_wrapper(path: str) -> BaseWrapper:
130
- for cls in (ZipWrapper, DirWrapper):
131
- if cls.is_valid(path):
132
- return cls(path)
133
- raise FileNotFoundError(f"The given path {path} is not valid.")
134
-
135
-
136
- class DataPack:
137
- """The DataPack class wraps a single data pack."""
138
-
139
- def __init__(self, path: str):
140
- self._path = path
141
- self._wrapper = _open_wrapper(path)
142
- self._is_valid = self.is_wrapper_valid(self._wrapper)
143
-
144
- @property
145
- def path(self) -> str:
146
- """The path to the data."""
147
- return self._path
148
-
149
- @staticmethod
150
- def is_path_valid(path: str) -> bool:
151
- """
152
- Check if the given path is a valid data pack.
153
-
154
- :param path: The path to the data pack. Can be a zip file or directory.
155
- :return: True if the path is a valid data pack, False otherwise.
156
- """
157
- try:
158
- wrapper = _open_wrapper(path)
159
- except FileNotFoundError:
160
- return False
161
- is_valid = DataPack.is_wrapper_valid(wrapper)
162
- wrapper.close()
163
- return is_valid
164
-
165
- @property
166
- def is_valid(self) -> bool:
167
- return self._is_valid
168
-
169
- @staticmethod
170
- def is_wrapper_valid(wrapper: BaseWrapper):
171
- if wrapper.has_file("pack.mcmeta"):
172
- with wrapper.open("pack.mcmeta") as m:
173
- try:
174
- meta_file = json.load(m)
175
- except json.JSONDecodeError:
176
- pass
177
- else:
178
- if isinstance(meta_file, dict) and isinstance(
179
- meta_file.get("pack", {}).get("pack_format", None), int
180
- ):
181
- # TODO: check the actual value
182
- return True
183
- return False
184
-
185
- @property
186
- def all_files(self) -> Iterable[str]:
187
- """
188
- The relative paths of all files contained within.
189
-
190
- :return: An iterable of paths.
191
- """
192
- return self._wrapper.all_files
193
-
194
- def all_files_match(self, match: str) -> Iterable[str]:
195
- """
196
- The relative paths of all files contained within that match the given regex.
197
-
198
- :param match: The regex string to match again.
199
- :return: An iterable of file paths that match the given regex.
200
- """
201
- return self._wrapper.all_files_match(match)
202
-
203
- def has_file(self, relative_path: str) -> bool:
204
- """
205
- Does the requested file exist.
206
-
207
- :param relative_path:
208
- :return:
209
- """
210
- return self._wrapper.has_file(relative_path)
211
-
212
- def open(self, relative_path: str) -> BinaryIO:
213
- """
214
- Get the contents of the file.
215
-
216
- :param relative_path:
217
- :return:
218
- """
219
- return self._wrapper.open(relative_path)
220
-
221
- @abstractmethod
222
- def close(self):
223
- """Close the contents."""
224
- self._wrapper.close()
1
+ from abc import ABC, abstractmethod
2
+ from typing import Iterable, BinaryIO
3
+ import os
4
+ import json
5
+ from zipfile import ZipFile
6
+ import re
7
+
8
+
9
+ class BaseWrapper(ABC):
10
+ def __init__(self, path: str):
11
+ self._path = path
12
+
13
+ @staticmethod
14
+ @abstractmethod
15
+ def is_valid(path: str) -> bool:
16
+ """
17
+ Is the given path valid for this class.
18
+
19
+ :param path: The path to test.
20
+ """
21
+
22
+ @property
23
+ def path(self) -> str:
24
+ """The path to the data."""
25
+ return self._path
26
+
27
+ @property
28
+ @abstractmethod
29
+ def all_files(self) -> Iterable[str]:
30
+ """
31
+ The relative paths of all files contained within.
32
+
33
+ :return: An iterable of paths.
34
+ """
35
+ raise NotImplementedError
36
+
37
+ def all_files_match(self, match: str) -> Iterable[str]:
38
+ """
39
+ The relative paths of all files contained within that match the given regex.
40
+
41
+ :param match: The regex string to match again.
42
+ :return: An iterable of file paths that match the given regex.
43
+ """
44
+ re_match = re.compile(match)
45
+ return tuple(path for path in self.all_files if re_match.fullmatch(path))
46
+
47
+ @abstractmethod
48
+ def has_file(self, relative_path: str) -> bool:
49
+ """
50
+ Does the requested file exist.
51
+
52
+ :param relative_path:
53
+ :return:
54
+ """
55
+ raise NotImplementedError
56
+
57
+ @abstractmethod
58
+ def open(self, relative_path: str, **kwargs) -> BinaryIO:
59
+ """
60
+ Get the contents of the file.
61
+
62
+ :param relative_path:
63
+ :return:
64
+ """
65
+ raise NotImplementedError
66
+
67
+ @abstractmethod
68
+ def close(self):
69
+ """Close the contents."""
70
+ raise NotImplementedError
71
+
72
+
73
+ class ZipWrapper(BaseWrapper, ZipFile):
74
+ def __init__(self, path: str):
75
+ BaseWrapper.__init__(self, path)
76
+ ZipFile.__init__(self, path)
77
+
78
+ @staticmethod
79
+ def is_valid(path: str) -> bool:
80
+ return os.path.isfile(path) and path.endswith(".zip")
81
+
82
+ @property
83
+ def all_files(self) -> Iterable[str]:
84
+ for path in self.NameToInfo:
85
+ if not path.endswith("/"):
86
+ yield path
87
+
88
+ def has_file(self, relative_path: str) -> bool:
89
+ if relative_path.endswith("/"):
90
+ # is a directory
91
+ return False
92
+ else:
93
+ return relative_path in self.NameToInfo
94
+
95
+ open = ZipFile.open
96
+ close = ZipFile.close
97
+
98
+
99
+ class DirWrapper(BaseWrapper):
100
+ def __init__(self, path: str):
101
+ BaseWrapper.__init__(self, path)
102
+
103
+ @staticmethod
104
+ def is_valid(path: str) -> bool:
105
+ return os.path.isdir(path)
106
+
107
+ @property
108
+ def all_files(self) -> Iterable[str]:
109
+ for abs_path, _, files in os.walk(self.path):
110
+ rel_path = os.path.normpath(os.path.relpath(abs_path, self.path)).replace(
111
+ os.sep, "/"
112
+ )
113
+ if rel_path == ".":
114
+ yield from files
115
+ else:
116
+ for f in files:
117
+ yield f"{rel_path}/{f}"
118
+
119
+ def has_file(self, relative_path: str) -> bool:
120
+ return os.path.isfile(os.path.join(self.path, relative_path))
121
+
122
+ def open(self, relative_path: str, **kwargs) -> BinaryIO:
123
+ return open(os.path.join(self.path, relative_path), "rb", **kwargs)
124
+
125
+ def close(self):
126
+ pass
127
+
128
+
129
+ def _open_wrapper(path: str) -> BaseWrapper:
130
+ for cls in (ZipWrapper, DirWrapper):
131
+ if cls.is_valid(path):
132
+ return cls(path)
133
+ raise FileNotFoundError(f"The given path {path} is not valid.")
134
+
135
+
136
+ class DataPack:
137
+ """The DataPack class wraps a single data pack."""
138
+
139
+ def __init__(self, path: str):
140
+ self._path = path
141
+ self._wrapper = _open_wrapper(path)
142
+ self._is_valid = self.is_wrapper_valid(self._wrapper)
143
+
144
+ @property
145
+ def path(self) -> str:
146
+ """The path to the data."""
147
+ return self._path
148
+
149
+ @staticmethod
150
+ def is_path_valid(path: str) -> bool:
151
+ """
152
+ Check if the given path is a valid data pack.
153
+
154
+ :param path: The path to the data pack. Can be a zip file or directory.
155
+ :return: True if the path is a valid data pack, False otherwise.
156
+ """
157
+ try:
158
+ wrapper = _open_wrapper(path)
159
+ except FileNotFoundError:
160
+ return False
161
+ is_valid = DataPack.is_wrapper_valid(wrapper)
162
+ wrapper.close()
163
+ return is_valid
164
+
165
+ @property
166
+ def is_valid(self) -> bool:
167
+ return self._is_valid
168
+
169
+ @staticmethod
170
+ def is_wrapper_valid(wrapper: BaseWrapper):
171
+ if wrapper.has_file("pack.mcmeta"):
172
+ with wrapper.open("pack.mcmeta") as m:
173
+ try:
174
+ meta_file = json.load(m)
175
+ except json.JSONDecodeError:
176
+ pass
177
+ else:
178
+ if isinstance(meta_file, dict) and isinstance(
179
+ meta_file.get("pack", {}).get("pack_format", None), int
180
+ ):
181
+ # TODO: check the actual value
182
+ return True
183
+ return False
184
+
185
+ @property
186
+ def all_files(self) -> Iterable[str]:
187
+ """
188
+ The relative paths of all files contained within.
189
+
190
+ :return: An iterable of paths.
191
+ """
192
+ return self._wrapper.all_files
193
+
194
+ def all_files_match(self, match: str) -> Iterable[str]:
195
+ """
196
+ The relative paths of all files contained within that match the given regex.
197
+
198
+ :param match: The regex string to match again.
199
+ :return: An iterable of file paths that match the given regex.
200
+ """
201
+ return self._wrapper.all_files_match(match)
202
+
203
+ def has_file(self, relative_path: str) -> bool:
204
+ """
205
+ Does the requested file exist.
206
+
207
+ :param relative_path:
208
+ :return:
209
+ """
210
+ return self._wrapper.has_file(relative_path)
211
+
212
+ def open(self, relative_path: str) -> BinaryIO:
213
+ """
214
+ Get the contents of the file.
215
+
216
+ :param relative_path:
217
+ :return:
218
+ """
219
+ return self._wrapper.open(relative_path)
220
+
221
+ @abstractmethod
222
+ def close(self):
223
+ """Close the contents."""
224
+ self._wrapper.close()
@@ -1,77 +1,77 @@
1
- from typing import Iterable, BinaryIO
2
- from .data_pack import DataPack
3
-
4
-
5
- class DataPackManager:
6
- """
7
- The DataPackManager class contains one or more data packs.
8
- It manages loading them so that the stacking order is maintained.
9
- """
10
-
11
- def __init__(self, data_packs: Iterable[DataPack]):
12
- """
13
- Construct a new DataPackManager class.
14
-
15
- :param data_packs: The data packs to load from. Later in the list get higher priority.
16
- """
17
- self._data_packs = tuple(
18
- reversed(
19
- tuple(
20
- pack
21
- for pack in data_packs
22
- if isinstance(pack, DataPack) and pack.is_valid
23
- )
24
- )
25
- )
26
-
27
- @property
28
- def all_files(self) -> Iterable[str]:
29
- """
30
- The relative paths of all files contained within.
31
-
32
- :return: An iterable of paths.
33
- """
34
- all_files = set()
35
- for pack in self._data_packs:
36
- all_files.update(pack.all_files)
37
- return all_files
38
-
39
- def all_files_match(self, match: str) -> Iterable[str]:
40
- """
41
- The relative paths of all files contained within that match the given regex.
42
-
43
- :param match: The regex string to match again.
44
- :return: An iterable of file paths that match the given regex.
45
- """
46
- all_files = set()
47
- for pack in self._data_packs:
48
- all_files.update(pack.all_files_match(match))
49
- return all_files
50
-
51
- def has_file(self, relative_path: str) -> bool:
52
- """
53
- Does the requested file exist.
54
-
55
- :param relative_path:
56
- :return:
57
- """
58
- return any(pack.has_file(relative_path) for pack in self._data_packs)
59
-
60
- def open(self, relative_path: str) -> BinaryIO:
61
- """
62
- Get the contents of the file.
63
-
64
- :param relative_path:
65
- :return:
66
- """
67
- for pack in self._data_packs:
68
- if pack.has_file(relative_path):
69
- return pack.open(relative_path)
70
- raise FileNotFoundError(
71
- f"The requested path {relative_path} could not be found."
72
- )
73
-
74
- def close(self):
75
- """Close the contents."""
76
- for pack in self._data_packs:
77
- pack.close()
1
+ from typing import Iterable, BinaryIO
2
+ from .data_pack import DataPack
3
+
4
+
5
+ class DataPackManager:
6
+ """
7
+ The DataPackManager class contains one or more data packs.
8
+ It manages loading them so that the stacking order is maintained.
9
+ """
10
+
11
+ def __init__(self, data_packs: Iterable[DataPack]):
12
+ """
13
+ Construct a new DataPackManager class.
14
+
15
+ :param data_packs: The data packs to load from. Later in the list get higher priority.
16
+ """
17
+ self._data_packs = tuple(
18
+ reversed(
19
+ tuple(
20
+ pack
21
+ for pack in data_packs
22
+ if isinstance(pack, DataPack) and pack.is_valid
23
+ )
24
+ )
25
+ )
26
+
27
+ @property
28
+ def all_files(self) -> Iterable[str]:
29
+ """
30
+ The relative paths of all files contained within.
31
+
32
+ :return: An iterable of paths.
33
+ """
34
+ all_files = set()
35
+ for pack in self._data_packs:
36
+ all_files.update(pack.all_files)
37
+ return all_files
38
+
39
+ def all_files_match(self, match: str) -> Iterable[str]:
40
+ """
41
+ The relative paths of all files contained within that match the given regex.
42
+
43
+ :param match: The regex string to match again.
44
+ :return: An iterable of file paths that match the given regex.
45
+ """
46
+ all_files = set()
47
+ for pack in self._data_packs:
48
+ all_files.update(pack.all_files_match(match))
49
+ return all_files
50
+
51
+ def has_file(self, relative_path: str) -> bool:
52
+ """
53
+ Does the requested file exist.
54
+
55
+ :param relative_path:
56
+ :return:
57
+ """
58
+ return any(pack.has_file(relative_path) for pack in self._data_packs)
59
+
60
+ def open(self, relative_path: str) -> BinaryIO:
61
+ """
62
+ Get the contents of the file.
63
+
64
+ :param relative_path:
65
+ :return:
66
+ """
67
+ for pack in self._data_packs:
68
+ if pack.has_file(relative_path):
69
+ return pack.open(relative_path)
70
+ raise FileNotFoundError(
71
+ f"The requested path {relative_path} could not be found."
72
+ )
73
+
74
+ def close(self):
75
+ """Close the contents."""
76
+ for pack in self._data_packs:
77
+ pack.close()