amulet-core 2.0a6__cp311-cp311-win_amd64.whl → 2.0a8__cp311-cp311-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.

Files changed (149) hide show
  1. amulet/__init__.cp311-win_amd64.pyd +0 -0
  2. amulet/__init__.py.cpp +6 -0
  3. amulet/__init__.pyi +2 -2
  4. amulet/_init.py +0 -2
  5. amulet/_version.py +3 -3
  6. amulet/biome.pyi +0 -2
  7. amulet/block.pyi +0 -2
  8. amulet/block_entity.pyi +0 -2
  9. amulet/chunk.hpp +2 -1
  10. amulet/chunk.pyi +0 -2
  11. amulet/chunk_components.pyi +20 -18
  12. amulet/collections/eq.py.hpp +1 -1
  13. amulet/collections/mapping.py.hpp +18 -11
  14. amulet/collections/mutable_mapping.py.hpp +17 -6
  15. amulet/collections/sequence.py.hpp +5 -6
  16. amulet/collections.pyi +8 -5
  17. amulet/entity.py +22 -20
  18. amulet/game/translate/_functions/_code_functions/_text.py +2 -2
  19. amulet/game/translate/_functions/abc.py +10 -3
  20. amulet/img/__init__.py +10 -0
  21. amulet/img/missing_no.png +0 -0
  22. amulet/img/missing_pack.png +0 -0
  23. amulet/level/__init__.pyi +2 -6
  24. amulet/level/abc/_chunk_handle.py +45 -22
  25. amulet/level/abc/_level/_creatable_level.py +1 -2
  26. amulet/level/abc/_level/_level.py +1 -5
  27. amulet/level/java/__init__.pyi +0 -5
  28. amulet/level/java/_raw/__init__.pyi +0 -4
  29. amulet/level/java/_raw/java_chunk_decode.cpp +2 -4
  30. amulet/level/java/long_array.pyi +2 -1
  31. amulet/mesh/block/__init__.pyi +301 -0
  32. amulet/mesh/block/_cube.py +198 -0
  33. amulet/mesh/block/_missing_block.py +20 -0
  34. amulet/mesh/block/block_mesh.cpp +107 -0
  35. amulet/mesh/block/block_mesh.hpp +207 -0
  36. amulet/mesh/util.py +17 -0
  37. amulet/player.py +4 -6
  38. amulet/pybind11/collections.hpp +80 -38
  39. amulet/pybind11/numpy.hpp +26 -0
  40. amulet/pybind11/py_module.hpp +16 -51
  41. amulet/pybind11/type_hints.hpp +51 -0
  42. amulet/pybind11/types.hpp +14 -6
  43. amulet/pybind11/typing.hpp +7 -0
  44. amulet/resource_pack/__init__.py +63 -0
  45. amulet/resource_pack/abc/__init__.py +2 -0
  46. amulet/resource_pack/abc/resource_pack.py +38 -0
  47. amulet/resource_pack/abc/resource_pack_manager.py +85 -0
  48. amulet/resource_pack/java/__init__.py +2 -0
  49. amulet/resource_pack/java/download_resources.py +212 -0
  50. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_black.png +0 -0
  51. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_blue.png +0 -0
  52. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_brown.png +0 -0
  53. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_cyan.png +0 -0
  54. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_gray.png +0 -0
  55. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_green.png +0 -0
  56. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_blue.png +0 -0
  57. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_gray.png +0 -0
  58. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_lime.png +0 -0
  59. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_magenta.png +0 -0
  60. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_orange.png +0 -0
  61. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_pink.png +0 -0
  62. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_purple.png +0 -0
  63. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_red.png +0 -0
  64. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_white.png +0 -0
  65. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_yellow.png +0 -0
  66. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/barrier.png +0 -0
  67. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/end_portal.png +0 -0
  68. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/grass.png +0 -0
  69. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/lava.png +0 -0
  70. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/structure_void.png +0 -0
  71. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png +0 -0
  72. amulet/resource_pack/java/java_vanilla_fix/pack.png +0 -0
  73. amulet/resource_pack/java/resource_pack.py +44 -0
  74. amulet/resource_pack/java/resource_pack_manager.py +563 -0
  75. amulet/resource_pack/unknown_resource_pack.py +10 -0
  76. amulet/utils/__init__.pyi +0 -5
  77. amulet/utils/call_spec/_call_spec.py +2 -7
  78. amulet/utils/cast.py +10 -0
  79. amulet/utils/comment_json.py +188 -0
  80. amulet/utils/matrix.py +3 -3
  81. amulet/utils/numpy_helpers.py +2 -2
  82. amulet/utils/shareable_lock.py +2 -2
  83. amulet/utils/world_utils.py +2 -2
  84. amulet/version.pyi +0 -8
  85. {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/METADATA +2 -2
  86. {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/RECORD +91 -103
  87. {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/WHEEL +1 -1
  88. amulet/chunk_/components/biome.py +0 -155
  89. amulet/chunk_/components/block_entity.py +0 -117
  90. amulet/chunk_/components/entity.py +0 -64
  91. amulet/chunk_/components/height_2d.py +0 -16
  92. amulet/level/bedrock/__init__.py +0 -2
  93. amulet/level/bedrock/_chunk_handle.py +0 -19
  94. amulet/level/bedrock/_dimension.py +0 -22
  95. amulet/level/bedrock/_level.py +0 -187
  96. amulet/level/bedrock/_raw/__init__.py +0 -5
  97. amulet/level/bedrock/_raw/_actor_counter.py +0 -53
  98. amulet/level/bedrock/_raw/_chunk.py +0 -54
  99. amulet/level/bedrock/_raw/_chunk_decode.py +0 -668
  100. amulet/level/bedrock/_raw/_chunk_encode.py +0 -602
  101. amulet/level/bedrock/_raw/_constant.py +0 -9
  102. amulet/level/bedrock/_raw/_dimension.py +0 -343
  103. amulet/level/bedrock/_raw/_level.py +0 -463
  104. amulet/level/bedrock/_raw/_level_dat.py +0 -90
  105. amulet/level/bedrock/_raw/_typing.py +0 -6
  106. amulet/level/bedrock/_raw/leveldb_chunk_versions.py +0 -83
  107. amulet/level/bedrock/chunk/__init__.py +0 -1
  108. amulet/level/bedrock/chunk/_chunk.py +0 -126
  109. amulet/level/bedrock/chunk/components/chunk_version.py +0 -12
  110. amulet/level/bedrock/chunk/components/finalised_state.py +0 -13
  111. amulet/level/bedrock/chunk/components/raw_chunk.py +0 -15
  112. amulet/level/construction/__init__.py +0 -0
  113. amulet/level/java/_chunk_handle.pyi +0 -15
  114. amulet/level/java/_dimension.pyi +0 -13
  115. amulet/level/java/_level.pyi +0 -120
  116. amulet/level/java/_raw/_chunk_decode.py +0 -561
  117. amulet/level/java/_raw/_chunk_encode.py +0 -463
  118. amulet/level/java/_raw/_constant.pyi +0 -20
  119. amulet/level/java/_raw/_data_pack/__init__.pyi +0 -8
  120. amulet/level/java/_raw/_data_pack/data_pack.pyi +0 -197
  121. amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +0 -75
  122. amulet/level/java/_raw/_dimension.pyi +0 -72
  123. amulet/level/java/_raw/_level.pyi +0 -238
  124. amulet/level/java/_raw/_typing.pyi +0 -5
  125. amulet/level/java/anvil/__init__.pyi +0 -11
  126. amulet/level/java/anvil/_dimension.pyi +0 -109
  127. amulet/level/java/anvil/_region.pyi +0 -197
  128. amulet/level/java/anvil/_sector_manager.pyi +0 -142
  129. amulet/level/java_forge/__init__.py +0 -0
  130. amulet/level/mcstructure/__init__.py +0 -0
  131. amulet/level/nbt/__init__.py +0 -0
  132. amulet/level/schematic/__init__.py +0 -0
  133. amulet/level/sponge_schematic/__init__.py +0 -0
  134. amulet/pybind11/python.hpp +0 -14
  135. amulet/utils/call_spec/__init__.pyi +0 -53
  136. amulet/utils/call_spec/_call_spec.pyi +0 -272
  137. amulet/utils/matrix.pyi +0 -177
  138. amulet/utils/shareable_lock.pyi +0 -190
  139. amulet/utils/signal/__init__.pyi +0 -25
  140. amulet/utils/signal/_signal.pyi +0 -84
  141. amulet/utils/task_manager.pyi +0 -168
  142. amulet/utils/typing.py +0 -4
  143. amulet/utils/typing.pyi +0 -6
  144. amulet/utils/weakref.pyi +0 -50
  145. amulet/utils/world_utils.pyi +0 -109
  146. /amulet/img/{missing_world_icon.png → missing_world.png} +0 -0
  147. /amulet/{level/bedrock/chunk/components → mesh}/__init__.py +0 -0
  148. {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/entry_points.txt +0 -0
  149. {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,563 @@
1
+ import os
2
+ import json
3
+ from typing import Union, Iterable, Iterator, Optional
4
+ from PIL import Image
5
+ import numpy
6
+ import glob
7
+ import itertools
8
+ import logging
9
+ import re
10
+
11
+ from amulet_nbt import StringTag
12
+
13
+ from amulet.utils.cast import dynamic_cast
14
+ from amulet.block import Block, PropertyValueType
15
+ from amulet.resource_pack import BaseResourcePackManager
16
+ from amulet.resource_pack.java import JavaResourcePack
17
+ from amulet.mesh.block import (
18
+ BlockMesh,
19
+ BlockMeshPart,
20
+ Triangle,
21
+ Vertex,
22
+ FloatVec3,
23
+ FloatVec2,
24
+ BlockMeshTransparency,
25
+ BlockMeshCullDirection,
26
+ merge_block_meshes,
27
+ FACE_KEYS,
28
+ CUBE_FACE_LUT,
29
+ UV_ROTATION_LUT,
30
+ TRI_FACE,
31
+ )
32
+ from amulet.mesh.util import rotate_3d
33
+
34
+ log = logging.getLogger(__name__)
35
+
36
+
37
+ UselessImageGroups = {
38
+ "colormap",
39
+ "effect",
40
+ "environment",
41
+ "font",
42
+ "gui",
43
+ "map",
44
+ "mob_effect",
45
+ "particle",
46
+ }
47
+
48
+ _PropertiesPattern = re.compile(r"(?P<name>[a-zA-Z0-9_]+)=(?P<value>[a-zA-Z0-9_]+),?")
49
+
50
+ CULL_DIRECTIONS = {
51
+ None: BlockMeshCullDirection.CullNone,
52
+ "down": BlockMeshCullDirection.CullDown,
53
+ "up": BlockMeshCullDirection.CullUp,
54
+ "north": BlockMeshCullDirection.CullNorth,
55
+ "east": BlockMeshCullDirection.CullEast,
56
+ "south": BlockMeshCullDirection.CullSouth,
57
+ "west": BlockMeshCullDirection.CullWest,
58
+ }
59
+
60
+
61
+ def get_py_data(obj: PropertyValueType) -> str | bytes | int:
62
+ if isinstance(obj, StringTag):
63
+ return obj.py_str_or_bytes
64
+ else:
65
+ return obj.py_int
66
+
67
+
68
+ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
69
+ """A class to load and handle the data from the packs.
70
+ Packs are given as a list with the later packs overwriting the earlier ones."""
71
+
72
+ def __init__(
73
+ self,
74
+ resource_packs: Union[JavaResourcePack, Iterable[JavaResourcePack]],
75
+ load: bool = True,
76
+ ) -> None:
77
+ super().__init__()
78
+ self._blockstate_files: dict[tuple[str, str], dict] = {}
79
+ self._textures: dict[tuple[str, str], str] = {}
80
+ self._texture_is_transparent: dict[str, tuple[float, bool]] = {}
81
+ self._model_files: dict[tuple[str, str], dict] = {}
82
+ if isinstance(resource_packs, Iterable):
83
+ self._packs = list(resource_packs)
84
+ elif isinstance(resource_packs, JavaResourcePack):
85
+ self._packs = [resource_packs]
86
+ else:
87
+ raise Exception(f"Invalid format {resource_packs}")
88
+ if load:
89
+ for _ in self.reload():
90
+ pass
91
+
92
+ def _unload(self) -> None:
93
+ """Clear all loaded resources."""
94
+ super()._unload()
95
+ self._blockstate_files.clear()
96
+ self._textures.clear()
97
+ self._texture_is_transparent.clear()
98
+ self._model_files.clear()
99
+
100
+ def _load_iter(self) -> Iterator[float]:
101
+ blockstate_file_paths: dict[tuple[str, str], str] = {}
102
+ model_file_paths: dict[tuple[str, str], str] = {}
103
+
104
+ transparency_cache_path = os.path.join(
105
+ os.environ["CACHE_DIR"], "resource_packs", "java", "transparency_cache.json"
106
+ )
107
+ self._load_transparency_cache(transparency_cache_path)
108
+
109
+ self._textures[("minecraft", "missing_no")] = self.missing_no
110
+
111
+ pack_count = len(self._packs)
112
+
113
+ for pack_index, pack in enumerate(self._packs):
114
+ # pack_format=2 textures/blocks, textures/items - case sensitive
115
+ # pack_format=3 textures/blocks, textures/items - lower case
116
+ # pack_format=4 textures/block, textures/item
117
+ # pack_format=5 model paths and texture paths are now optionally namespaced
118
+
119
+ pack_progress = pack_index / pack_count
120
+ yield pack_progress
121
+
122
+ if pack.valid_pack and pack.pack_format >= 2:
123
+ image_paths = glob.glob(
124
+ os.path.join(
125
+ glob.escape(pack.root_dir),
126
+ "assets",
127
+ "*", # namespace
128
+ "textures",
129
+ "**",
130
+ "*.png",
131
+ ),
132
+ recursive=True,
133
+ )
134
+ image_count = len(image_paths)
135
+ sub_progress = pack_progress
136
+ for image_index, texture_path in enumerate(image_paths):
137
+ _, namespace, _, *rel_path_list = os.path.normpath(
138
+ os.path.relpath(texture_path, pack.root_dir)
139
+ ).split(os.sep)
140
+ if rel_path_list[0] not in UselessImageGroups:
141
+ rel_path = "/".join(rel_path_list)[:-4]
142
+ self._textures[(namespace, rel_path)] = texture_path
143
+ if (
144
+ os.stat(texture_path).st_mtime
145
+ != self._texture_is_transparent.get(texture_path, [0])[0]
146
+ ):
147
+ im: Image.Image = Image.open(texture_path)
148
+ if im.mode == "RGBA":
149
+ alpha = numpy.array(im.getchannel("A").getdata())
150
+ texture_is_transparent = bool(numpy.any(alpha != 255))
151
+ else:
152
+ texture_is_transparent = False
153
+
154
+ self._texture_is_transparent[texture_path] = (
155
+ os.stat(texture_path).st_mtime,
156
+ texture_is_transparent,
157
+ )
158
+ yield sub_progress + image_index / (image_count * pack_count * 3)
159
+
160
+ blockstate_paths = glob.glob(
161
+ os.path.join(
162
+ glob.escape(pack.root_dir),
163
+ "assets",
164
+ "*", # namespace
165
+ "blockstates",
166
+ "*.json",
167
+ )
168
+ )
169
+ blockstate_count = len(blockstate_paths)
170
+ sub_progress = pack_progress + 1 / (pack_count * 3)
171
+ for blockstate_index, blockstate_path in enumerate(blockstate_paths):
172
+ _, namespace, _, blockstate_file = os.path.normpath(
173
+ os.path.relpath(blockstate_path, pack.root_dir)
174
+ ).split(os.sep)
175
+ blockstate_file_paths[(namespace, blockstate_file[:-5])] = (
176
+ blockstate_path
177
+ )
178
+ yield sub_progress + (blockstate_index) / (
179
+ blockstate_count * pack_count * 3
180
+ )
181
+
182
+ model_paths = glob.glob(
183
+ os.path.join(
184
+ glob.escape(pack.root_dir),
185
+ "assets",
186
+ "*", # namespace
187
+ "models",
188
+ "**",
189
+ "*.json",
190
+ ),
191
+ recursive=True,
192
+ )
193
+ model_count = len(model_paths)
194
+ sub_progress = pack_progress + 2 / (pack_count * 3)
195
+ for model_index, model_path in enumerate(model_paths):
196
+ _, namespace, _, *rel_path_list = os.path.normpath(
197
+ os.path.relpath(model_path, pack.root_dir)
198
+ ).split(os.sep)
199
+ rel_path = "/".join(rel_path_list)[:-5]
200
+ model_file_paths[(namespace, rel_path.replace(os.sep, "/"))] = (
201
+ model_path
202
+ )
203
+ yield sub_progress + (model_index) / (model_count * pack_count * 3)
204
+
205
+ os.makedirs(os.path.dirname(transparency_cache_path), exist_ok=True)
206
+ with open(transparency_cache_path, "w") as f:
207
+ json.dump(self._texture_is_transparent, f)
208
+
209
+ for key, path in blockstate_file_paths.items():
210
+ with open(path) as fi:
211
+ try:
212
+ self._blockstate_files[key] = json.load(fi)
213
+ except json.JSONDecodeError:
214
+ log.error(f"Failed to parse blockstate file {path}")
215
+
216
+ for key, path in model_file_paths.items():
217
+ with open(path) as fi:
218
+ try:
219
+ self._model_files[key] = json.load(fi)
220
+ except json.JSONDecodeError:
221
+ log.error(f"Failed to parse model file file {path}")
222
+
223
+ @property
224
+ def textures(self) -> tuple[str, ...]:
225
+ """Returns a tuple of all the texture paths in the resource pack."""
226
+ return tuple(self._textures.values())
227
+
228
+ def get_texture_path(self, namespace: Optional[str], relative_path: str) -> str:
229
+ """Get the absolute texture path from the namespace and relative path pair"""
230
+ if namespace is None:
231
+ return self.missing_no
232
+ key = (namespace, relative_path)
233
+ if key in self._textures:
234
+ return self._textures[key]
235
+ else:
236
+ return self.missing_no
237
+
238
+ @staticmethod
239
+ def parse_state_val(val: Union[str, bool]) -> list:
240
+ """Convert the json block state format into a consistent format."""
241
+ if isinstance(val, str):
242
+ return [StringTag(v) for v in val.split("|")]
243
+ elif isinstance(val, bool):
244
+ return [StringTag("true") if val else StringTag("false")]
245
+ else:
246
+ raise Exception(f"Could not parse state val {val}")
247
+
248
+ def _get_model(self, block: Block) -> BlockMesh:
249
+ """Find the model paths for a given block state and load them."""
250
+ if (block.namespace, block.base_name) in self._blockstate_files:
251
+ blockstate: dict = self._blockstate_files[
252
+ (block.namespace, block.base_name)
253
+ ]
254
+ if "variants" in blockstate:
255
+ for variant in blockstate["variants"]:
256
+ if variant == "":
257
+ try:
258
+ return self._load_blockstate_model(
259
+ blockstate["variants"][variant]
260
+ )
261
+ except Exception as e:
262
+ log.exception(
263
+ f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
264
+ )
265
+ else:
266
+ properties_match = _PropertiesPattern.finditer(f",{variant}")
267
+ if all(
268
+ get_py_data(
269
+ block.properties.get(
270
+ match.group("name"),
271
+ StringTag(match.group("value")),
272
+ )
273
+ )
274
+ == match.group("value")
275
+ for match in properties_match
276
+ ):
277
+ try:
278
+ return self._load_blockstate_model(
279
+ blockstate["variants"][variant]
280
+ )
281
+ except Exception as e:
282
+ log.exception(
283
+ f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
284
+ )
285
+
286
+ elif "multipart" in blockstate:
287
+ models = []
288
+
289
+ for case in blockstate["multipart"]:
290
+ try:
291
+ if "when" in case:
292
+ if "OR" in case["when"]:
293
+ if not any(
294
+ all(
295
+ block.properties.get(prop, None)
296
+ in self.parse_state_val(val)
297
+ for prop, val in prop_match.items()
298
+ )
299
+ for prop_match in case["when"]["OR"]
300
+ ):
301
+ continue
302
+ elif "AND" in case["when"]:
303
+ if not all(
304
+ all(
305
+ block.properties.get(prop, None)
306
+ in self.parse_state_val(val)
307
+ for prop, val in prop_match.items()
308
+ )
309
+ for prop_match in case["when"]["AND"]
310
+ ):
311
+ continue
312
+ elif not all(
313
+ block.properties.get(prop, None)
314
+ in self.parse_state_val(val)
315
+ for prop, val in case["when"].items()
316
+ ):
317
+ continue
318
+
319
+ if "apply" in case:
320
+ try:
321
+ models.append(
322
+ self._load_blockstate_model(case["apply"])
323
+ )
324
+
325
+ except Exception as e:
326
+ log.exception(
327
+ f"Failed to load block model {case['apply']}\n{e}"
328
+ )
329
+ except Exception as e:
330
+ log.exception(f"Failed to parse block state for {block}\n{e}")
331
+
332
+ return merge_block_meshes(models)
333
+
334
+ return self.missing_block
335
+
336
+ def _load_blockstate_model(
337
+ self, blockstate_value: Union[dict, list[dict]]
338
+ ) -> BlockMesh:
339
+ """Load the model(s) associated with a block state and apply rotations if needed."""
340
+ if isinstance(blockstate_value, list):
341
+ blockstate_value = blockstate_value[0]
342
+ if "model" not in blockstate_value:
343
+ return self.missing_block
344
+ model_path = blockstate_value["model"]
345
+ rotx = int(blockstate_value.get("x", 0) // 90)
346
+ roty = int(blockstate_value.get("y", 0) // 90)
347
+ uvlock = blockstate_value.get("uvlock", False)
348
+
349
+ model = self._load_block_model(model_path)
350
+
351
+ # TODO: rotate model based on uv_lock
352
+ return model.rotate(rotx, roty)
353
+
354
+ def _load_block_model(self, model_path: str) -> BlockMesh:
355
+ """Load the model file associated with the Block and convert to a BlockMesh."""
356
+ # recursively load model files into one dictionary
357
+ java_model = self._recursive_load_block_model(model_path)
358
+
359
+ if java_model.get("textures", {}) and not java_model.get("elements"):
360
+ return self.missing_block
361
+
362
+ # set up some variables
363
+ texture_paths: dict[str, int] = {}
364
+ mesh_parts: list[tuple[list[Vertex], list[Triangle]] | None] = [
365
+ None,
366
+ None,
367
+ None,
368
+ None,
369
+ None,
370
+ None,
371
+ None,
372
+ ]
373
+ transparency = BlockMeshTransparency.Partial
374
+
375
+ for element in dynamic_cast(java_model.get("elements", []), list):
376
+ # iterate through elements (one cube per element)
377
+ element_faces = dynamic_cast(element.get("faces", {}), dict)
378
+
379
+ opaque_face_count = 0
380
+ if (
381
+ transparency
382
+ and "rotation" not in element
383
+ and element.get("to", [16, 16, 16]) == [16, 16, 16]
384
+ and element.get("from", [0, 0, 0]) == [0, 0, 0]
385
+ and len(element_faces) >= 6
386
+ ):
387
+ # if the block is not yet defined as a solid block
388
+ # and this element is a full size element
389
+ # check if the texture is opaque
390
+ transparency = BlockMeshTransparency.FullTranslucent
391
+ check_faces = True
392
+ else:
393
+ check_faces = False
394
+
395
+ # lower and upper box coordinates
396
+ x1, y1, z1 = [v / 16.0 for v in element.get("from", [0, 0, 0])]
397
+ x2, y2, z2 = [v / 16.0 for v in element.get("to", [16, 16, 16])]
398
+
399
+ # vertex coordinates of the box
400
+ box_coordinates = numpy.array(
401
+ list(
402
+ itertools.product(
403
+ (min(x1, x2), max(x1, x2)),
404
+ (min(y1, y2), max(y1, y2)),
405
+ (min(z1, z2), max(z1, x2)),
406
+ )
407
+ )
408
+ )
409
+
410
+ for face_dir, face_data in element_faces.items():
411
+ if face_dir not in CUBE_FACE_LUT:
412
+ continue
413
+
414
+ # get the cull direction. If there is an opaque block in this direction then cull this face
415
+ cull_dir = face_data.get("cullface", None)
416
+ if cull_dir not in FACE_KEYS:
417
+ cull_dir = None
418
+
419
+ # get the relative texture path for the texture used
420
+ texture_relative_path = face_data.get("texture", None)
421
+ while isinstance(
422
+ texture_relative_path, str
423
+ ) and texture_relative_path.startswith("#"):
424
+ texture_relative_path = java_model["textures"].get(
425
+ texture_relative_path[1:], None
426
+ )
427
+ texture_path_list = texture_relative_path.split(":", 1)
428
+ if len(texture_path_list) == 2:
429
+ namespace, texture_relative_path = texture_path_list
430
+ else:
431
+ namespace = "minecraft"
432
+
433
+ texture_path = self.get_texture_path(namespace, texture_relative_path)
434
+
435
+ if check_faces:
436
+ if self._texture_is_transparent[texture_path][1]:
437
+ check_faces = False
438
+ else:
439
+ opaque_face_count += 1
440
+
441
+ # texture index for the face
442
+ texture_index = texture_paths.setdefault(
443
+ texture_path, len(texture_paths)
444
+ )
445
+
446
+ # get the uv values for each vertex
447
+ texture_uv: tuple[float, float, float, float]
448
+ if "uv" in face_data:
449
+ uv = face_data["uv"]
450
+ texture_uv = (
451
+ uv[0] / 16.0,
452
+ uv[1] / 16.0,
453
+ uv[2] / 16.0,
454
+ uv[3] / 16.0,
455
+ )
456
+ else:
457
+ # TODO: get the uv based on box location if not defined
458
+ texture_uv = (0.0, 0.0, 1.0, 1.0)
459
+
460
+ texture_rotation = face_data.get("rotation", 0)
461
+ uv_slice = (
462
+ UV_ROTATION_LUT[2 * int(texture_rotation / 90) :]
463
+ + UV_ROTATION_LUT[: 2 * int(texture_rotation / 90)]
464
+ )
465
+
466
+ # merge the vertex coordinates and texture coordinates
467
+ face_verts = box_coordinates[CUBE_FACE_LUT[face_dir]]
468
+ if "rotation" in element:
469
+ rotation = element["rotation"]
470
+ origin = [r / 16 for r in rotation.get("origin", [8, 8, 8])]
471
+ angle = rotation.get("angle", 0)
472
+ axis = rotation.get("axis", "x")
473
+ angles = [0, 0, 0]
474
+ if axis == "x":
475
+ angles[0] = -angle
476
+ elif axis == "y":
477
+ angles[1] = -angle
478
+ elif axis == "z":
479
+ angles[2] = -angle
480
+ face_verts = rotate_3d(face_verts, *angles, *origin)
481
+
482
+ cull_direction = CULL_DIRECTIONS[cull_dir]
483
+ part = mesh_parts[cull_direction]
484
+ if part is None:
485
+ mesh_parts[cull_direction] = part = ([], [])
486
+ verts, tris = part
487
+ vert_count = len(verts)
488
+
489
+ if "tintindex" in face_data:
490
+ # TODO: set this up for each supported block
491
+ tint_vec = FloatVec3(0, 1, 0)
492
+ else:
493
+ tint_vec = FloatVec3(1, 1, 1)
494
+
495
+ for i in range(4):
496
+ x, y, z = face_verts[i]
497
+ uvx = texture_uv[uv_slice[i * 2]]
498
+ uvy = texture_uv[uv_slice[i * 2 + 1]]
499
+ verts.append(
500
+ Vertex(
501
+ FloatVec3(x, y, z),
502
+ FloatVec2(uvx, uvy),
503
+ tint_vec,
504
+ )
505
+ )
506
+
507
+ for a, b, c in TRI_FACE.reshape((2, 3)):
508
+ tris.append(
509
+ Triangle(
510
+ a + vert_count,
511
+ b + vert_count,
512
+ c + vert_count,
513
+ texture_index,
514
+ )
515
+ )
516
+
517
+ if opaque_face_count == 6:
518
+ transparency = BlockMeshTransparency.FullOpaque
519
+
520
+ def create_part(
521
+ part: tuple[list[Vertex], list[Triangle]] | None
522
+ ) -> BlockMeshPart | None:
523
+ return None if part is None else BlockMeshPart(*part)
524
+
525
+ return BlockMesh(
526
+ transparency,
527
+ list(texture_paths),
528
+ (
529
+ create_part(mesh_parts[0]),
530
+ create_part(mesh_parts[1]),
531
+ create_part(mesh_parts[2]),
532
+ create_part(mesh_parts[3]),
533
+ create_part(mesh_parts[4]),
534
+ create_part(mesh_parts[5]),
535
+ create_part(mesh_parts[6]),
536
+ ),
537
+ )
538
+
539
+ def _recursive_load_block_model(self, model_path: str) -> dict:
540
+ """Load a model json file and recursively load and merge the parent entries into one json file."""
541
+ model_path_list = model_path.split(":", 1)
542
+ if len(model_path_list) == 2:
543
+ namespace, model_path = model_path_list
544
+ else:
545
+ namespace = "minecraft"
546
+ if (namespace, model_path) in self._model_files:
547
+ model = self._model_files[(namespace, model_path)]
548
+
549
+ if "parent" in model:
550
+ parent_model = self._recursive_load_block_model(model["parent"])
551
+ else:
552
+ parent_model = {}
553
+ if "textures" in model:
554
+ if "textures" not in parent_model:
555
+ parent_model["textures"] = {}
556
+ for key, val in model["textures"].items():
557
+ parent_model["textures"][key] = val
558
+ if "elements" in model:
559
+ parent_model["elements"] = model["elements"]
560
+
561
+ return parent_model
562
+
563
+ return {}
@@ -0,0 +1,10 @@
1
+ from amulet.resource_pack.abc.resource_pack import BaseResourcePack
2
+
3
+
4
+ class UnknownResourcePack(BaseResourcePack):
5
+ def __repr__(self) -> str:
6
+ return f"UnknownResourcePack({self._root_dir})"
7
+
8
+ @staticmethod
9
+ def is_valid(pack_path: str) -> bool:
10
+ return True
amulet/utils/__init__.pyi CHANGED
@@ -7,7 +7,6 @@ from . import (
7
7
  shareable_lock,
8
8
  signal,
9
9
  task_manager,
10
- typing,
11
10
  weakref,
12
11
  world_utils,
13
12
  )
@@ -19,10 +18,6 @@ __all__ = [
19
18
  "shareable_lock",
20
19
  "signal",
21
20
  "task_manager",
22
- "typing",
23
21
  "weakref",
24
22
  "world_utils",
25
23
  ]
26
-
27
- def __dir__() -> typing.Any: ...
28
- def __getattr__(arg0: typing.Any) -> typing.Any: ...
@@ -7,7 +7,6 @@ from __future__ import annotations
7
7
  from typing import (
8
8
  Any,
9
9
  Callable,
10
- Hashable,
11
10
  Protocol,
12
11
  TypeVar,
13
12
  cast,
@@ -16,17 +15,13 @@ from typing import (
16
15
  Concatenate,
17
16
  runtime_checkable,
18
17
  )
19
- from collections.abc import Sequence
20
- from abc import ABC, abstractmethod
18
+ from collections.abc import Sequence, Hashable
19
+ from abc import ABC
21
20
 
22
21
 
23
22
  class AbstractArg(ABC):
24
23
  """The base class for all arguments."""
25
24
 
26
- @abstractmethod
27
- def __init__(self, *args: Any, **kwargs: Any) -> None:
28
- raise NotImplementedError
29
-
30
25
 
31
26
  class AbstractHashableArg(AbstractArg, ABC):
32
27
  """A base class for all arguments that are hashable."""
amulet/utils/cast.py ADDED
@@ -0,0 +1,10 @@
1
+ from typing import TypeVar, Any
2
+
3
+ T = TypeVar("T")
4
+
5
+
6
+ def dynamic_cast(obj: Any, cls: type[T]) -> T:
7
+ """Like typing.cast but with runtime type checking."""
8
+ if isinstance(obj, cls):
9
+ return obj
10
+ raise TypeError(f"{obj} is not an instance of {cls}")