amulet-core 2.0a7__cp312-cp312-win_amd64.whl → 2.0a8__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of amulet-core might be problematic. Click here for more details.

Files changed (80) hide show
  1. amulet/__init__.cp312-win_amd64.pyd +0 -0
  2. amulet/__init__.py.cpp +2 -0
  3. amulet/__init__.pyi +2 -0
  4. amulet/_version.py +3 -3
  5. amulet/chunk.hpp +2 -1
  6. amulet/level/abc/_chunk_handle.py +45 -22
  7. amulet/mesh/block/__init__.pyi +301 -0
  8. amulet/mesh/block/_cube.py +198 -0
  9. amulet/mesh/block/{missing_block.py → _missing_block.py} +2 -2
  10. amulet/mesh/block/block_mesh.cpp +107 -0
  11. amulet/mesh/block/block_mesh.hpp +207 -0
  12. amulet/resource_pack/__init__.py +16 -15
  13. amulet/resource_pack/abc/resource_pack_manager.py +3 -5
  14. amulet/resource_pack/java/resource_pack_manager.py +185 -173
  15. amulet/utils/cast.py +10 -0
  16. amulet/utils/shareable_lock.py +2 -2
  17. {amulet_core-2.0a7.dist-info → amulet_core-2.0a8.dist-info}/METADATA +2 -2
  18. {amulet_core-2.0a7.dist-info → amulet_core-2.0a8.dist-info}/RECORD +21 -75
  19. {amulet_core-2.0a7.dist-info → amulet_core-2.0a8.dist-info}/WHEEL +1 -1
  20. amulet/mesh/block/__init__.py +0 -1
  21. amulet/mesh/block/block_mesh.py +0 -369
  22. amulet/mesh/block/cube.py +0 -149
  23. amulet/resource_pack/bedrock/__init__.py +0 -2
  24. amulet/resource_pack/bedrock/bedrock_vanilla_fix/pack_icon.png +0 -0
  25. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_carried.png +0 -0
  26. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_side_carried.png +0 -0
  27. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/water.png +0 -0
  28. amulet/resource_pack/bedrock/blockshapes/__init__.py +0 -31
  29. amulet/resource_pack/bedrock/blockshapes/air.py +0 -35
  30. amulet/resource_pack/bedrock/blockshapes/base_blockshape.py +0 -29
  31. amulet/resource_pack/bedrock/blockshapes/bubble_column.py +0 -29
  32. amulet/resource_pack/bedrock/blockshapes/cake.py +0 -46
  33. amulet/resource_pack/bedrock/blockshapes/chest.py +0 -54
  34. amulet/resource_pack/bedrock/blockshapes/comparator.py +0 -51
  35. amulet/resource_pack/bedrock/blockshapes/cross_texture.py +0 -186
  36. amulet/resource_pack/bedrock/blockshapes/cross_texture0.py +0 -17
  37. amulet/resource_pack/bedrock/blockshapes/cross_texture_green.py +0 -16
  38. amulet/resource_pack/bedrock/blockshapes/cube.py +0 -38
  39. amulet/resource_pack/bedrock/blockshapes/default.py +0 -14
  40. amulet/resource_pack/bedrock/blockshapes/door.py +0 -38
  41. amulet/resource_pack/bedrock/blockshapes/door1.py +0 -14
  42. amulet/resource_pack/bedrock/blockshapes/door2.py +0 -14
  43. amulet/resource_pack/bedrock/blockshapes/door3.py +0 -14
  44. amulet/resource_pack/bedrock/blockshapes/door4.py +0 -14
  45. amulet/resource_pack/bedrock/blockshapes/door5.py +0 -14
  46. amulet/resource_pack/bedrock/blockshapes/door6.py +0 -14
  47. amulet/resource_pack/bedrock/blockshapes/double_plant.py +0 -40
  48. amulet/resource_pack/bedrock/blockshapes/enchanting_table.py +0 -22
  49. amulet/resource_pack/bedrock/blockshapes/farmland.py +0 -22
  50. amulet/resource_pack/bedrock/blockshapes/fence.py +0 -22
  51. amulet/resource_pack/bedrock/blockshapes/flat.py +0 -55
  52. amulet/resource_pack/bedrock/blockshapes/flat_wall.py +0 -55
  53. amulet/resource_pack/bedrock/blockshapes/furnace.py +0 -44
  54. amulet/resource_pack/bedrock/blockshapes/furnace_lit.py +0 -14
  55. amulet/resource_pack/bedrock/blockshapes/green_cube.py +0 -39
  56. amulet/resource_pack/bedrock/blockshapes/ladder.py +0 -36
  57. amulet/resource_pack/bedrock/blockshapes/lilypad.py +0 -14
  58. amulet/resource_pack/bedrock/blockshapes/partial_block.py +0 -57
  59. amulet/resource_pack/bedrock/blockshapes/piston.py +0 -44
  60. amulet/resource_pack/bedrock/blockshapes/piston_arm.py +0 -72
  61. amulet/resource_pack/bedrock/blockshapes/portal_frame.py +0 -22
  62. amulet/resource_pack/bedrock/blockshapes/pressure_plate.py +0 -29
  63. amulet/resource_pack/bedrock/blockshapes/pumpkin.py +0 -36
  64. amulet/resource_pack/bedrock/blockshapes/pumpkin_carved.py +0 -14
  65. amulet/resource_pack/bedrock/blockshapes/pumpkin_lit.py +0 -14
  66. amulet/resource_pack/bedrock/blockshapes/red_dust.py +0 -14
  67. amulet/resource_pack/bedrock/blockshapes/repeater.py +0 -53
  68. amulet/resource_pack/bedrock/blockshapes/slab.py +0 -33
  69. amulet/resource_pack/bedrock/blockshapes/slab_double.py +0 -15
  70. amulet/resource_pack/bedrock/blockshapes/tree.py +0 -41
  71. amulet/resource_pack/bedrock/blockshapes/turtle_egg.py +0 -15
  72. amulet/resource_pack/bedrock/blockshapes/vine.py +0 -52
  73. amulet/resource_pack/bedrock/blockshapes/wall.py +0 -22
  74. amulet/resource_pack/bedrock/blockshapes/water.py +0 -38
  75. amulet/resource_pack/bedrock/download_resources.py +0 -147
  76. amulet/resource_pack/bedrock/resource_pack.py +0 -40
  77. amulet/resource_pack/bedrock/resource_pack_manager.py +0 -361
  78. amulet/resource_pack/bedrock/sort_blockshapes.py +0 -15
  79. {amulet_core-2.0a7.dist-info → amulet_core-2.0a8.dist-info}/entry_points.txt +0 -0
  80. {amulet_core-2.0a7.dist-info → amulet_core-2.0a8.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  import os
2
2
  import json
3
- import copy
4
3
  from typing import Union, Iterable, Iterator, Optional
5
4
  from PIL import Image
6
5
  import numpy
@@ -9,22 +8,28 @@ import itertools
9
8
  import logging
10
9
  import re
11
10
 
12
- import amulet_nbt
11
+ from amulet_nbt import StringTag
13
12
 
14
- from amulet.block import Block
13
+ from amulet.utils.cast import dynamic_cast
14
+ from amulet.block import Block, PropertyValueType
15
15
  from amulet.resource_pack import BaseResourcePackManager
16
16
  from amulet.resource_pack.java import JavaResourcePack
17
- from amulet.mesh.block.block_mesh import (
17
+ from amulet.mesh.block import (
18
18
  BlockMesh,
19
+ BlockMeshPart,
20
+ Triangle,
21
+ Vertex,
22
+ FloatVec3,
23
+ FloatVec2,
24
+ BlockMeshTransparency,
25
+ BlockMeshCullDirection,
26
+ merge_block_meshes,
19
27
  FACE_KEYS,
20
- Transparency,
28
+ CUBE_FACE_LUT,
29
+ UV_ROTATION_LUT,
30
+ TRI_FACE,
21
31
  )
22
32
  from amulet.mesh.util import rotate_3d
23
- from amulet.mesh.block.cube import (
24
- cube_face_lut,
25
- uv_rotation_lut,
26
- tri_face,
27
- )
28
33
 
29
34
  log = logging.getLogger(__name__)
30
35
 
@@ -42,6 +47,23 @@ UselessImageGroups = {
42
47
 
43
48
  _PropertiesPattern = re.compile(r"(?P<name>[a-zA-Z0-9_]+)=(?P<value>[a-zA-Z0-9_]+),?")
44
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
+
45
67
 
46
68
  class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
47
69
  """A class to load and handle the data from the packs.
@@ -217,11 +239,9 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
217
239
  def parse_state_val(val: Union[str, bool]) -> list:
218
240
  """Convert the json block state format into a consistent format."""
219
241
  if isinstance(val, str):
220
- return [amulet_nbt.TAG_String(v) for v in val.split("|")]
242
+ return [StringTag(v) for v in val.split("|")]
221
243
  elif isinstance(val, bool):
222
- return [
223
- amulet_nbt.TAG_String("true") if val else amulet_nbt.TAG_String("false")
224
- ]
244
+ return [StringTag("true") if val else StringTag("false")]
225
245
  else:
226
246
  raise Exception(f"Could not parse state val {val}")
227
247
 
@@ -239,16 +259,18 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
239
259
  blockstate["variants"][variant]
240
260
  )
241
261
  except Exception as e:
242
- log.error(
262
+ log.exception(
243
263
  f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
244
264
  )
245
265
  else:
246
266
  properties_match = _PropertiesPattern.finditer(f",{variant}")
247
267
  if all(
248
- block.properties.get(
249
- match.group("name"),
250
- amulet_nbt.TAG_String(match.group("value")),
251
- ).py_data
268
+ get_py_data(
269
+ block.properties.get(
270
+ match.group("name"),
271
+ StringTag(match.group("value")),
272
+ )
273
+ )
252
274
  == match.group("value")
253
275
  for match in properties_match
254
276
  ):
@@ -257,7 +279,7 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
257
279
  blockstate["variants"][variant]
258
280
  )
259
281
  except Exception as e:
260
- log.error(
282
+ log.exception(
261
283
  f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
262
284
  )
263
285
 
@@ -301,13 +323,13 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
301
323
  )
302
324
 
303
325
  except Exception as e:
304
- log.error(
326
+ log.exception(
305
327
  f"Failed to load block model {case['apply']}\n{e}"
306
328
  )
307
329
  except Exception as e:
308
- log.error(f"Failed to parse block state for {block}\n{e}")
330
+ log.exception(f"Failed to parse block state for {block}\n{e}")
309
331
 
310
- return BlockMesh.merge(models)
332
+ return merge_block_meshes(models)
311
333
 
312
334
  return self.missing_block
313
335
 
@@ -324,7 +346,7 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
324
346
  roty = int(blockstate_value.get("y", 0) // 90)
325
347
  uvlock = blockstate_value.get("uvlock", False)
326
348
 
327
- model = copy.deepcopy(self._load_block_model(model_path))
349
+ model = self._load_block_model(model_path)
328
350
 
329
351
  # TODO: rotate model based on uv_lock
330
352
  return model.rotate(rotx, roty)
@@ -334,39 +356,29 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
334
356
  # recursively load model files into one dictionary
335
357
  java_model = self._recursive_load_block_model(model_path)
336
358
 
337
- # set up some variables
338
- texture_dict = {}
339
- textures = []
340
- texture_count = 0
341
- vert_count = {side: 0 for side in FACE_KEYS}
342
- verts_src: dict[Optional[str], list[numpy.ndarray]] = {
343
- side: [] for side in FACE_KEYS
344
- }
345
- tverts_src: dict[Optional[str], list[numpy.ndarray]] = {
346
- side: [] for side in FACE_KEYS
347
- }
348
- tint_verts_src: dict[Optional[str], list[float]] = {
349
- side: [] for side in FACE_KEYS
350
- }
351
- faces_src: dict[Optional[str], list[numpy.ndarray]] = {
352
- side: [] for side in FACE_KEYS
353
- }
354
-
355
- texture_indexes_src: dict[Optional[str], list[int]] = {
356
- side: [] for side in FACE_KEYS
357
- }
358
- transparent = Transparency.Partial
359
-
360
359
  if java_model.get("textures", {}) and not java_model.get("elements"):
361
360
  return self.missing_block
362
361
 
363
- for element in java_model.get("elements", {}):
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):
364
376
  # iterate through elements (one cube per element)
365
- element_faces = element.get("faces", {})
377
+ element_faces = dynamic_cast(element.get("faces", {}), dict)
366
378
 
367
379
  opaque_face_count = 0
368
380
  if (
369
- transparent
381
+ transparency
370
382
  and "rotation" not in element
371
383
  and element.get("to", [16, 16, 16]) == [16, 16, 16]
372
384
  and element.get("from", [0, 0, 0]) == [0, 0, 0]
@@ -375,153 +387,153 @@ class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
375
387
  # if the block is not yet defined as a solid block
376
388
  # and this element is a full size element
377
389
  # check if the texture is opaque
378
- transparent = Transparency.FullTranslucent
390
+ transparency = BlockMeshTransparency.FullTranslucent
379
391
  check_faces = True
380
392
  else:
381
393
  check_faces = False
382
394
 
383
395
  # lower and upper box coordinates
384
- corners = numpy.sort(
385
- numpy.array(
386
- [element.get("to", [16, 16, 16]), element.get("from", [0, 0, 0])],
387
- float,
388
- )
389
- / 16,
390
- 0,
391
- )
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])]
392
398
 
393
399
  # vertex coordinates of the box
394
400
  box_coordinates = numpy.array(
395
- list(itertools.product(corners[:, 0], corners[:, 1], corners[:, 2]))
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
+ )
396
408
  )
397
409
 
398
- for face_dir in element_faces:
399
- if face_dir in cube_face_lut:
400
- # get the cull direction. If there is an opaque block in this direction then cull this face
401
- cull_dir = element_faces[face_dir].get("cullface", None)
402
- if cull_dir not in FACE_KEYS:
403
- cull_dir = None
404
-
405
- # get the relative texture path for the texture used
406
- texture_relative_path = element_faces[face_dir].get("texture", None)
407
- while isinstance(
408
- texture_relative_path, str
409
- ) and texture_relative_path.startswith("#"):
410
- texture_relative_path = java_model["textures"].get(
411
- texture_relative_path[1:], None
412
- )
413
- texture_path_list = texture_relative_path.split(":", 1)
414
- if len(texture_path_list) == 2:
415
- namespace, texture_relative_path = texture_path_list
416
- else:
417
- namespace = "minecraft"
418
-
419
- texture_path = self.get_texture_path(
420
- namespace, texture_relative_path
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
421
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"
422
432
 
423
- if check_faces:
424
- if self._texture_is_transparent[texture_path][1]:
425
- check_faces = False
426
- else:
427
- opaque_face_count += 1
433
+ texture_path = self.get_texture_path(namespace, texture_relative_path)
428
434
 
429
- # get the texture
430
- if texture_relative_path not in texture_dict:
431
- texture_dict[texture_relative_path] = texture_count
432
- textures.append(texture_path)
433
- texture_count += 1
435
+ if check_faces:
436
+ if self._texture_is_transparent[texture_path][1]:
437
+ check_faces = False
438
+ else:
439
+ opaque_face_count += 1
434
440
 
435
- # texture index for the face
436
- texture_index = texture_dict[texture_relative_path]
441
+ # texture index for the face
442
+ texture_index = texture_paths.setdefault(
443
+ texture_path, len(texture_paths)
444
+ )
437
445
 
438
- # get the uv values for each vertex
439
- # TODO: get the uv based on box location if not defined
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"]
440
450
  texture_uv = (
441
- numpy.array(
442
- element_faces[face_dir].get("uv", [0, 0, 16, 16]),
443
- float,
444
- )
445
- / 16
446
- )
447
- texture_rotation = element_faces[face_dir].get("rotation", 0)
448
- uv_slice = (
449
- uv_rotation_lut[2 * int(texture_rotation / 90) :]
450
- + uv_rotation_lut[: 2 * int(texture_rotation / 90)]
451
+ uv[0] / 16.0,
452
+ uv[1] / 16.0,
453
+ uv[2] / 16.0,
454
+ uv[3] / 16.0,
451
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)
452
459
 
453
- # merge the vertex coordinates and texture coordinates
454
- face_verts = box_coordinates[cube_face_lut[face_dir]]
455
- if "rotation" in element:
456
- rotation = element["rotation"]
457
- origin = [r / 16 for r in rotation.get("origin", [8, 8, 8])]
458
- angle = rotation.get("angle", 0)
459
- axis = rotation.get("axis", "x")
460
- angles = [0, 0, 0]
461
- if axis == "x":
462
- angles[0] = -angle
463
- elif axis == "y":
464
- angles[1] = -angle
465
- elif axis == "z":
466
- angles[2] = -angle
467
- face_verts = rotate_3d(face_verts, *angles, *origin)
468
-
469
- verts_src[cull_dir].append(
470
- face_verts
471
- ) # vertex coordinates for this face
472
-
473
- tverts_src[cull_dir].append(
474
- texture_uv[uv_slice].reshape((-1, 2)) # texture vertices
475
- )
476
- if "tintindex" in element_faces[face_dir]:
477
- tint_verts_src[cull_dir] += [
478
- 0,
479
- 1,
480
- 0,
481
- ] * 4 # TODO: set this up for each supported block
482
- else:
483
- tint_verts_src[cull_dir] += [1, 1, 1] * 4
484
-
485
- # merge the face indexes and texture index
486
- face_table = tri_face + vert_count[cull_dir]
487
- texture_indexes_src[cull_dir] += [texture_index, texture_index]
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
+ )
488
465
 
489
- # faces stored under cull direction because this is the criteria to render them or not
490
- faces_src[cull_dir].append(face_table)
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
+ )
491
506
 
492
- vert_count[cull_dir] += 4
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
+ )
493
516
 
494
517
  if opaque_face_count == 6:
495
- transparent = Transparency.FullOpaque
496
-
497
- verts: dict[Optional[str], numpy.ndarray] = {}
498
- tverts: dict[Optional[str], numpy.ndarray] = {}
499
- tint_verts: dict[Optional[str], numpy.ndarray] = {}
500
- faces: dict[Optional[str], numpy.ndarray] = {}
501
- texture_indexes: dict[Optional[str], numpy.ndarray] = {}
502
-
503
- for cull_dir in FACE_KEYS:
504
- face_array = faces_src[cull_dir]
505
- if len(face_array) > 0:
506
- faces[cull_dir] = numpy.concatenate(face_array, axis=None)
507
- tint_verts[cull_dir] = numpy.concatenate(
508
- tint_verts_src[cull_dir], axis=None
509
- )
510
- verts[cull_dir] = numpy.concatenate(verts_src[cull_dir], axis=None)
511
- tverts[cull_dir] = numpy.concatenate(tverts_src[cull_dir], axis=None)
512
- texture_indexes[cull_dir] = numpy.array(
513
- texture_indexes_src[cull_dir], dtype=numpy.uint32
514
- )
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)
515
524
 
516
525
  return BlockMesh(
517
- 3,
518
- verts,
519
- tverts,
520
- tint_verts,
521
- faces,
522
- texture_indexes,
523
- tuple(textures),
524
- transparent,
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
+ ),
525
537
  )
526
538
 
527
539
  def _recursive_load_block_model(self, model_path: str) -> dict:
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}")
@@ -289,7 +289,7 @@ class ShareableRLock:
289
289
  :raises: LockNotAcquired if the lock could not be acquired.
290
290
  """
291
291
  if not self.acquire_unique(blocking, timeout, task_manager):
292
- raise LockNotAcquired
292
+ raise LockNotAcquired("Lock was not acquired.")
293
293
  try:
294
294
  yield
295
295
  finally:
@@ -328,7 +328,7 @@ class ShareableRLock:
328
328
  :raises: LockNotAcquired if the lock could not be acquired.
329
329
  """
330
330
  if not self.acquire_shared(blocking, timeout, task_manager):
331
- raise LockNotAcquired
331
+ raise LockNotAcquired("Lock was not acquired.")
332
332
  try:
333
333
  yield
334
334
  finally:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amulet-core
3
- Version: 2.0a7
3
+ Version: 2.0a8
4
4
  Summary: A Python library for reading/writing Minecraft's various save formats.
5
5
  Home-page: https://www.amuletmc.com
6
6
  Author: James Clare, Ben Gothard et al.
@@ -11,7 +11,7 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.11
12
12
  Description-Content-Type: text/markdown
13
13
  Requires-Dist: numpy~=2.0
14
- Requires-Dist: amulet-nbt~=4.0a3
14
+ Requires-Dist: amulet-nbt~=4.0a6
15
15
  Requires-Dist: portalocker~=2.4
16
16
  Requires-Dist: amulet-leveldb~=1.0b0
17
17
  Requires-Dist: platformdirs~=3.1