amulet-core 2.0a6__cp312-cp312-win_amd64.whl → 2.0a7__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.
- amulet/__init__.cp312-win_amd64.pyd +0 -0
- amulet/__init__.py.cpp +4 -0
- amulet/__init__.pyi +0 -2
- amulet/_init.py +0 -2
- amulet/_version.py +3 -3
- amulet/biome.pyi +0 -2
- amulet/block.pyi +0 -2
- amulet/block_entity.pyi +0 -2
- amulet/chunk.pyi +0 -2
- amulet/chunk_components.pyi +20 -18
- amulet/collections/eq.py.hpp +1 -1
- amulet/collections/mapping.py.hpp +18 -11
- amulet/collections/mutable_mapping.py.hpp +17 -6
- amulet/collections/sequence.py.hpp +5 -6
- amulet/collections.pyi +8 -5
- amulet/entity.py +22 -20
- amulet/game/translate/_functions/_code_functions/_text.py +2 -2
- amulet/game/translate/_functions/abc.py +10 -3
- amulet/img/__init__.py +10 -0
- amulet/img/missing_no.png +0 -0
- amulet/img/missing_pack.png +0 -0
- amulet/level/__init__.pyi +2 -6
- amulet/level/abc/_level/_creatable_level.py +1 -2
- amulet/level/abc/_level/_level.py +1 -5
- amulet/level/java/__init__.pyi +0 -5
- amulet/level/java/_raw/__init__.pyi +0 -4
- amulet/level/java/_raw/java_chunk_decode.cpp +2 -4
- amulet/level/java/long_array.pyi +2 -1
- amulet/mesh/block/__init__.py +1 -0
- amulet/mesh/block/block_mesh.py +369 -0
- amulet/mesh/block/cube.py +149 -0
- amulet/mesh/block/missing_block.py +20 -0
- amulet/mesh/util.py +17 -0
- amulet/player.py +4 -6
- amulet/pybind11/collections.hpp +80 -38
- amulet/pybind11/numpy.hpp +26 -0
- amulet/pybind11/py_module.hpp +16 -51
- amulet/pybind11/type_hints.hpp +51 -0
- amulet/pybind11/types.hpp +14 -6
- amulet/pybind11/typing.hpp +7 -0
- amulet/resource_pack/__init__.py +62 -0
- amulet/resource_pack/abc/__init__.py +2 -0
- amulet/resource_pack/abc/resource_pack.py +38 -0
- amulet/resource_pack/abc/resource_pack_manager.py +87 -0
- amulet/resource_pack/bedrock/__init__.py +2 -0
- amulet/resource_pack/bedrock/bedrock_vanilla_fix/pack_icon.png +0 -0
- amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_carried.png +0 -0
- amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_side_carried.png +0 -0
- amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/water.png +0 -0
- amulet/resource_pack/bedrock/blockshapes/__init__.py +31 -0
- amulet/resource_pack/bedrock/blockshapes/air.py +35 -0
- amulet/resource_pack/bedrock/blockshapes/base_blockshape.py +29 -0
- amulet/resource_pack/bedrock/blockshapes/bubble_column.py +29 -0
- amulet/resource_pack/bedrock/blockshapes/cake.py +46 -0
- amulet/resource_pack/bedrock/blockshapes/chest.py +54 -0
- amulet/resource_pack/bedrock/blockshapes/comparator.py +51 -0
- amulet/resource_pack/bedrock/blockshapes/cross_texture.py +186 -0
- amulet/resource_pack/bedrock/blockshapes/cross_texture0.py +17 -0
- amulet/resource_pack/bedrock/blockshapes/cross_texture_green.py +16 -0
- amulet/resource_pack/bedrock/blockshapes/cube.py +38 -0
- amulet/resource_pack/bedrock/blockshapes/default.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door.py +38 -0
- amulet/resource_pack/bedrock/blockshapes/door1.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door2.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door3.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door4.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door5.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/door6.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/double_plant.py +40 -0
- amulet/resource_pack/bedrock/blockshapes/enchanting_table.py +22 -0
- amulet/resource_pack/bedrock/blockshapes/farmland.py +22 -0
- amulet/resource_pack/bedrock/blockshapes/fence.py +22 -0
- amulet/resource_pack/bedrock/blockshapes/flat.py +55 -0
- amulet/resource_pack/bedrock/blockshapes/flat_wall.py +55 -0
- amulet/resource_pack/bedrock/blockshapes/furnace.py +44 -0
- amulet/resource_pack/bedrock/blockshapes/furnace_lit.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/green_cube.py +39 -0
- amulet/resource_pack/bedrock/blockshapes/ladder.py +36 -0
- amulet/resource_pack/bedrock/blockshapes/lilypad.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/partial_block.py +57 -0
- amulet/resource_pack/bedrock/blockshapes/piston.py +44 -0
- amulet/resource_pack/bedrock/blockshapes/piston_arm.py +72 -0
- amulet/resource_pack/bedrock/blockshapes/portal_frame.py +22 -0
- amulet/resource_pack/bedrock/blockshapes/pressure_plate.py +29 -0
- amulet/resource_pack/bedrock/blockshapes/pumpkin.py +36 -0
- amulet/resource_pack/bedrock/blockshapes/pumpkin_carved.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/pumpkin_lit.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/red_dust.py +14 -0
- amulet/resource_pack/bedrock/blockshapes/repeater.py +53 -0
- amulet/resource_pack/bedrock/blockshapes/slab.py +33 -0
- amulet/resource_pack/bedrock/blockshapes/slab_double.py +15 -0
- amulet/resource_pack/bedrock/blockshapes/tree.py +41 -0
- amulet/resource_pack/bedrock/blockshapes/turtle_egg.py +15 -0
- amulet/resource_pack/bedrock/blockshapes/vine.py +52 -0
- amulet/resource_pack/bedrock/blockshapes/wall.py +22 -0
- amulet/resource_pack/bedrock/blockshapes/water.py +38 -0
- amulet/resource_pack/bedrock/download_resources.py +147 -0
- amulet/resource_pack/bedrock/resource_pack.py +40 -0
- amulet/resource_pack/bedrock/resource_pack_manager.py +361 -0
- amulet/resource_pack/bedrock/sort_blockshapes.py +15 -0
- amulet/resource_pack/java/__init__.py +2 -0
- amulet/resource_pack/java/download_resources.py +212 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_black.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_blue.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_brown.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_cyan.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_gray.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_green.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_blue.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_gray.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_lime.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_magenta.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_orange.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_pink.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_purple.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_red.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_white.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_yellow.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/barrier.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/end_portal.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/grass.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/lava.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/structure_void.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png +0 -0
- amulet/resource_pack/java/java_vanilla_fix/pack.png +0 -0
- amulet/resource_pack/java/resource_pack.py +44 -0
- amulet/resource_pack/java/resource_pack_manager.py +551 -0
- amulet/resource_pack/unknown_resource_pack.py +10 -0
- amulet/utils/__init__.pyi +0 -5
- amulet/utils/call_spec/_call_spec.py +2 -7
- amulet/utils/comment_json.py +188 -0
- amulet/utils/matrix.py +3 -3
- amulet/utils/numpy_helpers.py +2 -2
- amulet/utils/world_utils.py +2 -2
- amulet/version.pyi +0 -8
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a7.dist-info}/METADATA +2 -2
- amulet_core-2.0a7.dist-info/RECORD +295 -0
- amulet/chunk_/components/biome.py +0 -155
- amulet/chunk_/components/block_entity.py +0 -117
- amulet/chunk_/components/entity.py +0 -64
- amulet/chunk_/components/height_2d.py +0 -16
- amulet/level/bedrock/__init__.py +0 -2
- amulet/level/bedrock/_chunk_handle.py +0 -19
- amulet/level/bedrock/_dimension.py +0 -22
- amulet/level/bedrock/_level.py +0 -187
- amulet/level/bedrock/_raw/__init__.py +0 -5
- amulet/level/bedrock/_raw/_actor_counter.py +0 -53
- amulet/level/bedrock/_raw/_chunk.py +0 -54
- amulet/level/bedrock/_raw/_chunk_decode.py +0 -668
- amulet/level/bedrock/_raw/_chunk_encode.py +0 -602
- amulet/level/bedrock/_raw/_constant.py +0 -9
- amulet/level/bedrock/_raw/_dimension.py +0 -343
- amulet/level/bedrock/_raw/_level.py +0 -463
- amulet/level/bedrock/_raw/_level_dat.py +0 -90
- amulet/level/bedrock/_raw/_typing.py +0 -6
- amulet/level/bedrock/_raw/leveldb_chunk_versions.py +0 -83
- amulet/level/bedrock/chunk/__init__.py +0 -1
- amulet/level/bedrock/chunk/_chunk.py +0 -126
- amulet/level/bedrock/chunk/components/chunk_version.py +0 -12
- amulet/level/bedrock/chunk/components/finalised_state.py +0 -13
- amulet/level/bedrock/chunk/components/raw_chunk.py +0 -15
- amulet/level/construction/__init__.py +0 -0
- amulet/level/java/_chunk_handle.pyi +0 -15
- amulet/level/java/_dimension.pyi +0 -13
- amulet/level/java/_level.pyi +0 -120
- amulet/level/java/_raw/_chunk_decode.py +0 -561
- amulet/level/java/_raw/_chunk_encode.py +0 -463
- amulet/level/java/_raw/_constant.pyi +0 -20
- amulet/level/java/_raw/_data_pack/__init__.pyi +0 -8
- amulet/level/java/_raw/_data_pack/data_pack.pyi +0 -197
- amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +0 -75
- amulet/level/java/_raw/_dimension.pyi +0 -72
- amulet/level/java/_raw/_level.pyi +0 -238
- amulet/level/java/_raw/_typing.pyi +0 -5
- amulet/level/java/anvil/__init__.pyi +0 -11
- amulet/level/java/anvil/_dimension.pyi +0 -109
- amulet/level/java/anvil/_region.pyi +0 -197
- amulet/level/java/anvil/_sector_manager.pyi +0 -142
- amulet/level/java_forge/__init__.py +0 -0
- amulet/level/mcstructure/__init__.py +0 -0
- amulet/level/nbt/__init__.py +0 -0
- amulet/level/schematic/__init__.py +0 -0
- amulet/level/sponge_schematic/__init__.py +0 -0
- amulet/pybind11/python.hpp +0 -14
- amulet/utils/call_spec/__init__.pyi +0 -53
- amulet/utils/call_spec/_call_spec.pyi +0 -272
- amulet/utils/matrix.pyi +0 -177
- amulet/utils/shareable_lock.pyi +0 -190
- amulet/utils/signal/__init__.pyi +0 -25
- amulet/utils/signal/_signal.pyi +0 -84
- amulet/utils/task_manager.pyi +0 -168
- amulet/utils/typing.py +0 -4
- amulet/utils/typing.pyi +0 -6
- amulet/utils/weakref.pyi +0 -50
- amulet/utils/world_utils.pyi +0 -109
- amulet_core-2.0a6.dist-info/RECORD +0 -253
- /amulet/img/{missing_world_icon.png → missing_world.png} +0 -0
- /amulet/{level/bedrock/chunk/components → mesh}/__init__.py +0 -0
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a7.dist-info}/WHEEL +0 -0
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a7.dist-info}/entry_points.txt +0 -0
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from amulet.resource_pack.abc import BaseResourcePack
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JavaResourcePack(BaseResourcePack):
|
|
8
|
+
"""A class to hold the bare bones information about the resource pack.
|
|
9
|
+
Holds the pack format, description and if the pack is valid.
|
|
10
|
+
This information can be used in a viewer to display the packs to the user."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, resource_pack_path: str):
|
|
13
|
+
super().__init__(resource_pack_path)
|
|
14
|
+
meta_path = os.path.join(resource_pack_path, "pack.mcmeta")
|
|
15
|
+
self._pack_format = 0
|
|
16
|
+
if os.path.isfile(meta_path):
|
|
17
|
+
try:
|
|
18
|
+
with open(meta_path) as f:
|
|
19
|
+
pack_mcmeta = json.load(f)
|
|
20
|
+
except json.JSONDecodeError:
|
|
21
|
+
pass
|
|
22
|
+
else:
|
|
23
|
+
if "pack" in pack_mcmeta:
|
|
24
|
+
if "description" in pack_mcmeta["pack"]:
|
|
25
|
+
self._pack_description = str(pack_mcmeta["pack"]["description"])
|
|
26
|
+
if "pack_format" in pack_mcmeta["pack"]:
|
|
27
|
+
self._pack_format = pack_mcmeta["pack"]["pack_format"]
|
|
28
|
+
self._valid_pack = True
|
|
29
|
+
|
|
30
|
+
pack_icon_path = os.path.join(resource_pack_path, "pack.png")
|
|
31
|
+
if os.path.isfile(pack_icon_path):
|
|
32
|
+
self._pack_icon = pack_icon_path
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def is_valid(pack_path: str) -> bool:
|
|
36
|
+
return os.path.isfile(os.path.join(pack_path, "pack.mcmeta"))
|
|
37
|
+
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return f"JavaResourcePack({self._root_dir})"
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def pack_format(self) -> int:
|
|
43
|
+
"""int - pack format number"""
|
|
44
|
+
return self._pack_format
|
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import copy
|
|
4
|
+
from typing import Union, Iterable, Iterator, Optional
|
|
5
|
+
from PIL import Image
|
|
6
|
+
import numpy
|
|
7
|
+
import glob
|
|
8
|
+
import itertools
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
import amulet_nbt
|
|
13
|
+
|
|
14
|
+
from amulet.block import Block
|
|
15
|
+
from amulet.resource_pack import BaseResourcePackManager
|
|
16
|
+
from amulet.resource_pack.java import JavaResourcePack
|
|
17
|
+
from amulet.mesh.block.block_mesh import (
|
|
18
|
+
BlockMesh,
|
|
19
|
+
FACE_KEYS,
|
|
20
|
+
Transparency,
|
|
21
|
+
)
|
|
22
|
+
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
|
+
|
|
29
|
+
log = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
UselessImageGroups = {
|
|
33
|
+
"colormap",
|
|
34
|
+
"effect",
|
|
35
|
+
"environment",
|
|
36
|
+
"font",
|
|
37
|
+
"gui",
|
|
38
|
+
"map",
|
|
39
|
+
"mob_effect",
|
|
40
|
+
"particle",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_PropertiesPattern = re.compile(r"(?P<name>[a-zA-Z0-9_]+)=(?P<value>[a-zA-Z0-9_]+),?")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class JavaResourcePackManager(BaseResourcePackManager[JavaResourcePack]):
|
|
47
|
+
"""A class to load and handle the data from the packs.
|
|
48
|
+
Packs are given as a list with the later packs overwriting the earlier ones."""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
resource_packs: Union[JavaResourcePack, Iterable[JavaResourcePack]],
|
|
53
|
+
load: bool = True,
|
|
54
|
+
) -> None:
|
|
55
|
+
super().__init__()
|
|
56
|
+
self._blockstate_files: dict[tuple[str, str], dict] = {}
|
|
57
|
+
self._textures: dict[tuple[str, str], str] = {}
|
|
58
|
+
self._texture_is_transparent: dict[str, tuple[float, bool]] = {}
|
|
59
|
+
self._model_files: dict[tuple[str, str], dict] = {}
|
|
60
|
+
if isinstance(resource_packs, Iterable):
|
|
61
|
+
self._packs = list(resource_packs)
|
|
62
|
+
elif isinstance(resource_packs, JavaResourcePack):
|
|
63
|
+
self._packs = [resource_packs]
|
|
64
|
+
else:
|
|
65
|
+
raise Exception(f"Invalid format {resource_packs}")
|
|
66
|
+
if load:
|
|
67
|
+
for _ in self.reload():
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def _unload(self) -> None:
|
|
71
|
+
"""Clear all loaded resources."""
|
|
72
|
+
super()._unload()
|
|
73
|
+
self._blockstate_files.clear()
|
|
74
|
+
self._textures.clear()
|
|
75
|
+
self._texture_is_transparent.clear()
|
|
76
|
+
self._model_files.clear()
|
|
77
|
+
|
|
78
|
+
def _load_iter(self) -> Iterator[float]:
|
|
79
|
+
blockstate_file_paths: dict[tuple[str, str], str] = {}
|
|
80
|
+
model_file_paths: dict[tuple[str, str], str] = {}
|
|
81
|
+
|
|
82
|
+
transparency_cache_path = os.path.join(
|
|
83
|
+
os.environ["CACHE_DIR"], "resource_packs", "java", "transparency_cache.json"
|
|
84
|
+
)
|
|
85
|
+
self._load_transparency_cache(transparency_cache_path)
|
|
86
|
+
|
|
87
|
+
self._textures[("minecraft", "missing_no")] = self.missing_no
|
|
88
|
+
|
|
89
|
+
pack_count = len(self._packs)
|
|
90
|
+
|
|
91
|
+
for pack_index, pack in enumerate(self._packs):
|
|
92
|
+
# pack_format=2 textures/blocks, textures/items - case sensitive
|
|
93
|
+
# pack_format=3 textures/blocks, textures/items - lower case
|
|
94
|
+
# pack_format=4 textures/block, textures/item
|
|
95
|
+
# pack_format=5 model paths and texture paths are now optionally namespaced
|
|
96
|
+
|
|
97
|
+
pack_progress = pack_index / pack_count
|
|
98
|
+
yield pack_progress
|
|
99
|
+
|
|
100
|
+
if pack.valid_pack and pack.pack_format >= 2:
|
|
101
|
+
image_paths = glob.glob(
|
|
102
|
+
os.path.join(
|
|
103
|
+
glob.escape(pack.root_dir),
|
|
104
|
+
"assets",
|
|
105
|
+
"*", # namespace
|
|
106
|
+
"textures",
|
|
107
|
+
"**",
|
|
108
|
+
"*.png",
|
|
109
|
+
),
|
|
110
|
+
recursive=True,
|
|
111
|
+
)
|
|
112
|
+
image_count = len(image_paths)
|
|
113
|
+
sub_progress = pack_progress
|
|
114
|
+
for image_index, texture_path in enumerate(image_paths):
|
|
115
|
+
_, namespace, _, *rel_path_list = os.path.normpath(
|
|
116
|
+
os.path.relpath(texture_path, pack.root_dir)
|
|
117
|
+
).split(os.sep)
|
|
118
|
+
if rel_path_list[0] not in UselessImageGroups:
|
|
119
|
+
rel_path = "/".join(rel_path_list)[:-4]
|
|
120
|
+
self._textures[(namespace, rel_path)] = texture_path
|
|
121
|
+
if (
|
|
122
|
+
os.stat(texture_path).st_mtime
|
|
123
|
+
!= self._texture_is_transparent.get(texture_path, [0])[0]
|
|
124
|
+
):
|
|
125
|
+
im: Image.Image = Image.open(texture_path)
|
|
126
|
+
if im.mode == "RGBA":
|
|
127
|
+
alpha = numpy.array(im.getchannel("A").getdata())
|
|
128
|
+
texture_is_transparent = bool(numpy.any(alpha != 255))
|
|
129
|
+
else:
|
|
130
|
+
texture_is_transparent = False
|
|
131
|
+
|
|
132
|
+
self._texture_is_transparent[texture_path] = (
|
|
133
|
+
os.stat(texture_path).st_mtime,
|
|
134
|
+
texture_is_transparent,
|
|
135
|
+
)
|
|
136
|
+
yield sub_progress + image_index / (image_count * pack_count * 3)
|
|
137
|
+
|
|
138
|
+
blockstate_paths = glob.glob(
|
|
139
|
+
os.path.join(
|
|
140
|
+
glob.escape(pack.root_dir),
|
|
141
|
+
"assets",
|
|
142
|
+
"*", # namespace
|
|
143
|
+
"blockstates",
|
|
144
|
+
"*.json",
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
blockstate_count = len(blockstate_paths)
|
|
148
|
+
sub_progress = pack_progress + 1 / (pack_count * 3)
|
|
149
|
+
for blockstate_index, blockstate_path in enumerate(blockstate_paths):
|
|
150
|
+
_, namespace, _, blockstate_file = os.path.normpath(
|
|
151
|
+
os.path.relpath(blockstate_path, pack.root_dir)
|
|
152
|
+
).split(os.sep)
|
|
153
|
+
blockstate_file_paths[(namespace, blockstate_file[:-5])] = (
|
|
154
|
+
blockstate_path
|
|
155
|
+
)
|
|
156
|
+
yield sub_progress + (blockstate_index) / (
|
|
157
|
+
blockstate_count * pack_count * 3
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
model_paths = glob.glob(
|
|
161
|
+
os.path.join(
|
|
162
|
+
glob.escape(pack.root_dir),
|
|
163
|
+
"assets",
|
|
164
|
+
"*", # namespace
|
|
165
|
+
"models",
|
|
166
|
+
"**",
|
|
167
|
+
"*.json",
|
|
168
|
+
),
|
|
169
|
+
recursive=True,
|
|
170
|
+
)
|
|
171
|
+
model_count = len(model_paths)
|
|
172
|
+
sub_progress = pack_progress + 2 / (pack_count * 3)
|
|
173
|
+
for model_index, model_path in enumerate(model_paths):
|
|
174
|
+
_, namespace, _, *rel_path_list = os.path.normpath(
|
|
175
|
+
os.path.relpath(model_path, pack.root_dir)
|
|
176
|
+
).split(os.sep)
|
|
177
|
+
rel_path = "/".join(rel_path_list)[:-5]
|
|
178
|
+
model_file_paths[(namespace, rel_path.replace(os.sep, "/"))] = (
|
|
179
|
+
model_path
|
|
180
|
+
)
|
|
181
|
+
yield sub_progress + (model_index) / (model_count * pack_count * 3)
|
|
182
|
+
|
|
183
|
+
os.makedirs(os.path.dirname(transparency_cache_path), exist_ok=True)
|
|
184
|
+
with open(transparency_cache_path, "w") as f:
|
|
185
|
+
json.dump(self._texture_is_transparent, f)
|
|
186
|
+
|
|
187
|
+
for key, path in blockstate_file_paths.items():
|
|
188
|
+
with open(path) as fi:
|
|
189
|
+
try:
|
|
190
|
+
self._blockstate_files[key] = json.load(fi)
|
|
191
|
+
except json.JSONDecodeError:
|
|
192
|
+
log.error(f"Failed to parse blockstate file {path}")
|
|
193
|
+
|
|
194
|
+
for key, path in model_file_paths.items():
|
|
195
|
+
with open(path) as fi:
|
|
196
|
+
try:
|
|
197
|
+
self._model_files[key] = json.load(fi)
|
|
198
|
+
except json.JSONDecodeError:
|
|
199
|
+
log.error(f"Failed to parse model file file {path}")
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def textures(self) -> tuple[str, ...]:
|
|
203
|
+
"""Returns a tuple of all the texture paths in the resource pack."""
|
|
204
|
+
return tuple(self._textures.values())
|
|
205
|
+
|
|
206
|
+
def get_texture_path(self, namespace: Optional[str], relative_path: str) -> str:
|
|
207
|
+
"""Get the absolute texture path from the namespace and relative path pair"""
|
|
208
|
+
if namespace is None:
|
|
209
|
+
return self.missing_no
|
|
210
|
+
key = (namespace, relative_path)
|
|
211
|
+
if key in self._textures:
|
|
212
|
+
return self._textures[key]
|
|
213
|
+
else:
|
|
214
|
+
return self.missing_no
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def parse_state_val(val: Union[str, bool]) -> list:
|
|
218
|
+
"""Convert the json block state format into a consistent format."""
|
|
219
|
+
if isinstance(val, str):
|
|
220
|
+
return [amulet_nbt.TAG_String(v) for v in val.split("|")]
|
|
221
|
+
elif isinstance(val, bool):
|
|
222
|
+
return [
|
|
223
|
+
amulet_nbt.TAG_String("true") if val else amulet_nbt.TAG_String("false")
|
|
224
|
+
]
|
|
225
|
+
else:
|
|
226
|
+
raise Exception(f"Could not parse state val {val}")
|
|
227
|
+
|
|
228
|
+
def _get_model(self, block: Block) -> BlockMesh:
|
|
229
|
+
"""Find the model paths for a given block state and load them."""
|
|
230
|
+
if (block.namespace, block.base_name) in self._blockstate_files:
|
|
231
|
+
blockstate: dict = self._blockstate_files[
|
|
232
|
+
(block.namespace, block.base_name)
|
|
233
|
+
]
|
|
234
|
+
if "variants" in blockstate:
|
|
235
|
+
for variant in blockstate["variants"]:
|
|
236
|
+
if variant == "":
|
|
237
|
+
try:
|
|
238
|
+
return self._load_blockstate_model(
|
|
239
|
+
blockstate["variants"][variant]
|
|
240
|
+
)
|
|
241
|
+
except Exception as e:
|
|
242
|
+
log.error(
|
|
243
|
+
f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
properties_match = _PropertiesPattern.finditer(f",{variant}")
|
|
247
|
+
if all(
|
|
248
|
+
block.properties.get(
|
|
249
|
+
match.group("name"),
|
|
250
|
+
amulet_nbt.TAG_String(match.group("value")),
|
|
251
|
+
).py_data
|
|
252
|
+
== match.group("value")
|
|
253
|
+
for match in properties_match
|
|
254
|
+
):
|
|
255
|
+
try:
|
|
256
|
+
return self._load_blockstate_model(
|
|
257
|
+
blockstate["variants"][variant]
|
|
258
|
+
)
|
|
259
|
+
except Exception as e:
|
|
260
|
+
log.error(
|
|
261
|
+
f"Failed to load block model {blockstate['variants'][variant]}\n{e}"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
elif "multipart" in blockstate:
|
|
265
|
+
models = []
|
|
266
|
+
|
|
267
|
+
for case in blockstate["multipart"]:
|
|
268
|
+
try:
|
|
269
|
+
if "when" in case:
|
|
270
|
+
if "OR" in case["when"]:
|
|
271
|
+
if not any(
|
|
272
|
+
all(
|
|
273
|
+
block.properties.get(prop, None)
|
|
274
|
+
in self.parse_state_val(val)
|
|
275
|
+
for prop, val in prop_match.items()
|
|
276
|
+
)
|
|
277
|
+
for prop_match in case["when"]["OR"]
|
|
278
|
+
):
|
|
279
|
+
continue
|
|
280
|
+
elif "AND" in case["when"]:
|
|
281
|
+
if not all(
|
|
282
|
+
all(
|
|
283
|
+
block.properties.get(prop, None)
|
|
284
|
+
in self.parse_state_val(val)
|
|
285
|
+
for prop, val in prop_match.items()
|
|
286
|
+
)
|
|
287
|
+
for prop_match in case["when"]["AND"]
|
|
288
|
+
):
|
|
289
|
+
continue
|
|
290
|
+
elif not all(
|
|
291
|
+
block.properties.get(prop, None)
|
|
292
|
+
in self.parse_state_val(val)
|
|
293
|
+
for prop, val in case["when"].items()
|
|
294
|
+
):
|
|
295
|
+
continue
|
|
296
|
+
|
|
297
|
+
if "apply" in case:
|
|
298
|
+
try:
|
|
299
|
+
models.append(
|
|
300
|
+
self._load_blockstate_model(case["apply"])
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
except Exception as e:
|
|
304
|
+
log.error(
|
|
305
|
+
f"Failed to load block model {case['apply']}\n{e}"
|
|
306
|
+
)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
log.error(f"Failed to parse block state for {block}\n{e}")
|
|
309
|
+
|
|
310
|
+
return BlockMesh.merge(models)
|
|
311
|
+
|
|
312
|
+
return self.missing_block
|
|
313
|
+
|
|
314
|
+
def _load_blockstate_model(
|
|
315
|
+
self, blockstate_value: Union[dict, list[dict]]
|
|
316
|
+
) -> BlockMesh:
|
|
317
|
+
"""Load the model(s) associated with a block state and apply rotations if needed."""
|
|
318
|
+
if isinstance(blockstate_value, list):
|
|
319
|
+
blockstate_value = blockstate_value[0]
|
|
320
|
+
if "model" not in blockstate_value:
|
|
321
|
+
return self.missing_block
|
|
322
|
+
model_path = blockstate_value["model"]
|
|
323
|
+
rotx = int(blockstate_value.get("x", 0) // 90)
|
|
324
|
+
roty = int(blockstate_value.get("y", 0) // 90)
|
|
325
|
+
uvlock = blockstate_value.get("uvlock", False)
|
|
326
|
+
|
|
327
|
+
model = copy.deepcopy(self._load_block_model(model_path))
|
|
328
|
+
|
|
329
|
+
# TODO: rotate model based on uv_lock
|
|
330
|
+
return model.rotate(rotx, roty)
|
|
331
|
+
|
|
332
|
+
def _load_block_model(self, model_path: str) -> BlockMesh:
|
|
333
|
+
"""Load the model file associated with the Block and convert to a BlockMesh."""
|
|
334
|
+
# recursively load model files into one dictionary
|
|
335
|
+
java_model = self._recursive_load_block_model(model_path)
|
|
336
|
+
|
|
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
|
+
if java_model.get("textures", {}) and not java_model.get("elements"):
|
|
361
|
+
return self.missing_block
|
|
362
|
+
|
|
363
|
+
for element in java_model.get("elements", {}):
|
|
364
|
+
# iterate through elements (one cube per element)
|
|
365
|
+
element_faces = element.get("faces", {})
|
|
366
|
+
|
|
367
|
+
opaque_face_count = 0
|
|
368
|
+
if (
|
|
369
|
+
transparent
|
|
370
|
+
and "rotation" not in element
|
|
371
|
+
and element.get("to", [16, 16, 16]) == [16, 16, 16]
|
|
372
|
+
and element.get("from", [0, 0, 0]) == [0, 0, 0]
|
|
373
|
+
and len(element_faces) >= 6
|
|
374
|
+
):
|
|
375
|
+
# if the block is not yet defined as a solid block
|
|
376
|
+
# and this element is a full size element
|
|
377
|
+
# check if the texture is opaque
|
|
378
|
+
transparent = Transparency.FullTranslucent
|
|
379
|
+
check_faces = True
|
|
380
|
+
else:
|
|
381
|
+
check_faces = False
|
|
382
|
+
|
|
383
|
+
# 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
|
+
)
|
|
392
|
+
|
|
393
|
+
# vertex coordinates of the box
|
|
394
|
+
box_coordinates = numpy.array(
|
|
395
|
+
list(itertools.product(corners[:, 0], corners[:, 1], corners[:, 2]))
|
|
396
|
+
)
|
|
397
|
+
|
|
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
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
if check_faces:
|
|
424
|
+
if self._texture_is_transparent[texture_path][1]:
|
|
425
|
+
check_faces = False
|
|
426
|
+
else:
|
|
427
|
+
opaque_face_count += 1
|
|
428
|
+
|
|
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
|
|
434
|
+
|
|
435
|
+
# texture index for the face
|
|
436
|
+
texture_index = texture_dict[texture_relative_path]
|
|
437
|
+
|
|
438
|
+
# get the uv values for each vertex
|
|
439
|
+
# TODO: get the uv based on box location if not defined
|
|
440
|
+
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
|
+
)
|
|
452
|
+
|
|
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]
|
|
488
|
+
|
|
489
|
+
# faces stored under cull direction because this is the criteria to render them or not
|
|
490
|
+
faces_src[cull_dir].append(face_table)
|
|
491
|
+
|
|
492
|
+
vert_count[cull_dir] += 4
|
|
493
|
+
|
|
494
|
+
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
|
+
)
|
|
515
|
+
|
|
516
|
+
return BlockMesh(
|
|
517
|
+
3,
|
|
518
|
+
verts,
|
|
519
|
+
tverts,
|
|
520
|
+
tint_verts,
|
|
521
|
+
faces,
|
|
522
|
+
texture_indexes,
|
|
523
|
+
tuple(textures),
|
|
524
|
+
transparent,
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
def _recursive_load_block_model(self, model_path: str) -> dict:
|
|
528
|
+
"""Load a model json file and recursively load and merge the parent entries into one json file."""
|
|
529
|
+
model_path_list = model_path.split(":", 1)
|
|
530
|
+
if len(model_path_list) == 2:
|
|
531
|
+
namespace, model_path = model_path_list
|
|
532
|
+
else:
|
|
533
|
+
namespace = "minecraft"
|
|
534
|
+
if (namespace, model_path) in self._model_files:
|
|
535
|
+
model = self._model_files[(namespace, model_path)]
|
|
536
|
+
|
|
537
|
+
if "parent" in model:
|
|
538
|
+
parent_model = self._recursive_load_block_model(model["parent"])
|
|
539
|
+
else:
|
|
540
|
+
parent_model = {}
|
|
541
|
+
if "textures" in model:
|
|
542
|
+
if "textures" not in parent_model:
|
|
543
|
+
parent_model["textures"] = {}
|
|
544
|
+
for key, val in model["textures"].items():
|
|
545
|
+
parent_model["textures"][key] = val
|
|
546
|
+
if "elements" in model:
|
|
547
|
+
parent_model["elements"] = model["elements"]
|
|
548
|
+
|
|
549
|
+
return parent_model
|
|
550
|
+
|
|
551
|
+
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
|
|
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."""
|