amulet-core 2.0a5__cp311-cp311-win_amd64.whl → 2.0a7__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 (232) hide show
  1. amulet/__init__.cp311-win_amd64.pyd +0 -0
  2. amulet/__init__.py.cpp +43 -0
  3. amulet/__init__.pyi +0 -2
  4. amulet/_init.py +0 -2
  5. amulet/_version.py +3 -3
  6. amulet/biome.py.cpp +122 -0
  7. amulet/biome.pyi +0 -2
  8. amulet/block.py.cpp +377 -0
  9. amulet/block.pyi +0 -2
  10. amulet/block_entity.py.cpp +115 -0
  11. amulet/block_entity.pyi +0 -2
  12. amulet/chunk.py.cpp +80 -0
  13. amulet/chunk.pyi +0 -2
  14. amulet/chunk_components/biome_3d_component.cpp +5 -0
  15. amulet/chunk_components/biome_3d_component.hpp +79 -0
  16. amulet/chunk_components/block_component.cpp +41 -0
  17. amulet/chunk_components/block_component.hpp +88 -0
  18. amulet/chunk_components/block_entity_component.cpp +5 -0
  19. amulet/chunk_components/block_entity_component.hpp +147 -0
  20. amulet/chunk_components/section_array_map.cpp +129 -0
  21. amulet/chunk_components/section_array_map.hpp +147 -0
  22. amulet/chunk_components.pyi +20 -18
  23. amulet/collections/eq.py.hpp +37 -0
  24. amulet/collections/hash.py.hpp +27 -0
  25. amulet/collections/holder.py.hpp +37 -0
  26. amulet/collections/iterator.py.hpp +80 -0
  27. amulet/collections/mapping.py.hpp +199 -0
  28. amulet/collections/mutable_mapping.py.hpp +226 -0
  29. amulet/collections/sequence.py.hpp +163 -0
  30. amulet/collections.pyi +8 -5
  31. amulet/entity.py +22 -20
  32. amulet/game/translate/_functions/_code_functions/_text.py +2 -2
  33. amulet/game/translate/_functions/abc.py +10 -3
  34. amulet/img/__init__.py +10 -0
  35. amulet/img/missing_no.png +0 -0
  36. amulet/img/missing_pack.png +0 -0
  37. amulet/img/missing_world.png +0 -0
  38. amulet/io/binary_reader.hpp +45 -0
  39. amulet/io/binary_writer.hpp +30 -0
  40. amulet/level/__init__.pyi +2 -6
  41. amulet/level/abc/_level/_creatable_level.py +1 -2
  42. amulet/level/abc/_level/_level.py +1 -5
  43. amulet/level/java/__init__.pyi +0 -5
  44. amulet/level/java/_raw/__init__.pyi +0 -4
  45. amulet/level/java/_raw/java_chunk_decode.cpp +531 -0
  46. amulet/level/java/_raw/java_chunk_decode.hpp +23 -0
  47. amulet/level/java/_raw/java_chunk_encode.cpp +25 -0
  48. amulet/level/java/_raw/java_chunk_encode.hpp +23 -0
  49. amulet/level/java/chunk_components/data_version_component.cpp +32 -0
  50. amulet/level/java/chunk_components/data_version_component.hpp +31 -0
  51. amulet/level/java/chunk_components/java_raw_chunk_component.cpp +56 -0
  52. amulet/level/java/chunk_components/java_raw_chunk_component.hpp +45 -0
  53. amulet/level/java/java_chunk.cpp +170 -0
  54. amulet/level/java/java_chunk.hpp +141 -0
  55. amulet/level/java/long_array.hpp +175 -0
  56. amulet/level/java/long_array.pyi +2 -1
  57. amulet/mesh/block/__init__.py +1 -0
  58. amulet/mesh/block/block_mesh.py +369 -0
  59. amulet/mesh/block/cube.py +149 -0
  60. amulet/mesh/block/missing_block.py +20 -0
  61. amulet/mesh/util.py +17 -0
  62. amulet/palette/biome_palette.hpp +85 -0
  63. amulet/palette/block_palette.cpp +32 -0
  64. amulet/palette/block_palette.hpp +93 -0
  65. amulet/player.py +4 -6
  66. amulet/pybind11/collections.hpp +118 -0
  67. amulet/pybind11/numpy.hpp +26 -0
  68. amulet/pybind11/py_module.hpp +34 -0
  69. amulet/pybind11/type_hints.hpp +51 -0
  70. amulet/pybind11/types.hpp +25 -0
  71. amulet/pybind11/typing.hpp +7 -0
  72. amulet/resource_pack/__init__.py +62 -0
  73. amulet/resource_pack/abc/__init__.py +2 -0
  74. amulet/resource_pack/abc/resource_pack.py +38 -0
  75. amulet/resource_pack/abc/resource_pack_manager.py +87 -0
  76. amulet/resource_pack/bedrock/__init__.py +2 -0
  77. amulet/resource_pack/bedrock/bedrock_vanilla_fix/pack_icon.png +0 -0
  78. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_carried.png +0 -0
  79. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/grass_side_carried.png +0 -0
  80. amulet/resource_pack/bedrock/bedrock_vanilla_fix/textures/blocks/water.png +0 -0
  81. amulet/resource_pack/bedrock/blockshapes/__init__.py +31 -0
  82. amulet/resource_pack/bedrock/blockshapes/air.py +35 -0
  83. amulet/resource_pack/bedrock/blockshapes/base_blockshape.py +29 -0
  84. amulet/resource_pack/bedrock/blockshapes/bubble_column.py +29 -0
  85. amulet/resource_pack/bedrock/blockshapes/cake.py +46 -0
  86. amulet/resource_pack/bedrock/blockshapes/chest.py +54 -0
  87. amulet/resource_pack/bedrock/blockshapes/comparator.py +51 -0
  88. amulet/resource_pack/bedrock/blockshapes/cross_texture.py +186 -0
  89. amulet/resource_pack/bedrock/blockshapes/cross_texture0.py +17 -0
  90. amulet/resource_pack/bedrock/blockshapes/cross_texture_green.py +16 -0
  91. amulet/resource_pack/bedrock/blockshapes/cube.py +38 -0
  92. amulet/resource_pack/bedrock/blockshapes/default.py +14 -0
  93. amulet/resource_pack/bedrock/blockshapes/door.py +38 -0
  94. amulet/resource_pack/bedrock/blockshapes/door1.py +14 -0
  95. amulet/resource_pack/bedrock/blockshapes/door2.py +14 -0
  96. amulet/resource_pack/bedrock/blockshapes/door3.py +14 -0
  97. amulet/resource_pack/bedrock/blockshapes/door4.py +14 -0
  98. amulet/resource_pack/bedrock/blockshapes/door5.py +14 -0
  99. amulet/resource_pack/bedrock/blockshapes/door6.py +14 -0
  100. amulet/resource_pack/bedrock/blockshapes/double_plant.py +40 -0
  101. amulet/resource_pack/bedrock/blockshapes/enchanting_table.py +22 -0
  102. amulet/resource_pack/bedrock/blockshapes/farmland.py +22 -0
  103. amulet/resource_pack/bedrock/blockshapes/fence.py +22 -0
  104. amulet/resource_pack/bedrock/blockshapes/flat.py +55 -0
  105. amulet/resource_pack/bedrock/blockshapes/flat_wall.py +55 -0
  106. amulet/resource_pack/bedrock/blockshapes/furnace.py +44 -0
  107. amulet/resource_pack/bedrock/blockshapes/furnace_lit.py +14 -0
  108. amulet/resource_pack/bedrock/blockshapes/green_cube.py +39 -0
  109. amulet/resource_pack/bedrock/blockshapes/ladder.py +36 -0
  110. amulet/resource_pack/bedrock/blockshapes/lilypad.py +14 -0
  111. amulet/resource_pack/bedrock/blockshapes/partial_block.py +57 -0
  112. amulet/resource_pack/bedrock/blockshapes/piston.py +44 -0
  113. amulet/resource_pack/bedrock/blockshapes/piston_arm.py +72 -0
  114. amulet/resource_pack/bedrock/blockshapes/portal_frame.py +22 -0
  115. amulet/resource_pack/bedrock/blockshapes/pressure_plate.py +29 -0
  116. amulet/resource_pack/bedrock/blockshapes/pumpkin.py +36 -0
  117. amulet/resource_pack/bedrock/blockshapes/pumpkin_carved.py +14 -0
  118. amulet/resource_pack/bedrock/blockshapes/pumpkin_lit.py +14 -0
  119. amulet/resource_pack/bedrock/blockshapes/red_dust.py +14 -0
  120. amulet/resource_pack/bedrock/blockshapes/repeater.py +53 -0
  121. amulet/resource_pack/bedrock/blockshapes/slab.py +33 -0
  122. amulet/resource_pack/bedrock/blockshapes/slab_double.py +15 -0
  123. amulet/resource_pack/bedrock/blockshapes/tree.py +41 -0
  124. amulet/resource_pack/bedrock/blockshapes/turtle_egg.py +15 -0
  125. amulet/resource_pack/bedrock/blockshapes/vine.py +52 -0
  126. amulet/resource_pack/bedrock/blockshapes/wall.py +22 -0
  127. amulet/resource_pack/bedrock/blockshapes/water.py +38 -0
  128. amulet/resource_pack/bedrock/download_resources.py +147 -0
  129. amulet/resource_pack/bedrock/resource_pack.py +40 -0
  130. amulet/resource_pack/bedrock/resource_pack_manager.py +361 -0
  131. amulet/resource_pack/bedrock/sort_blockshapes.py +15 -0
  132. amulet/resource_pack/java/__init__.py +2 -0
  133. amulet/resource_pack/java/download_resources.py +212 -0
  134. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_black.png +0 -0
  135. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_blue.png +0 -0
  136. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_brown.png +0 -0
  137. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_cyan.png +0 -0
  138. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_gray.png +0 -0
  139. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_green.png +0 -0
  140. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_blue.png +0 -0
  141. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_gray.png +0 -0
  142. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_lime.png +0 -0
  143. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_magenta.png +0 -0
  144. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_orange.png +0 -0
  145. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_pink.png +0 -0
  146. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_purple.png +0 -0
  147. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_red.png +0 -0
  148. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_white.png +0 -0
  149. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_yellow.png +0 -0
  150. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/barrier.png +0 -0
  151. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/end_portal.png +0 -0
  152. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/grass.png +0 -0
  153. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/lava.png +0 -0
  154. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/structure_void.png +0 -0
  155. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png +0 -0
  156. amulet/resource_pack/java/java_vanilla_fix/pack.png +0 -0
  157. amulet/resource_pack/java/resource_pack.py +44 -0
  158. amulet/resource_pack/java/resource_pack_manager.py +551 -0
  159. amulet/resource_pack/unknown_resource_pack.py +10 -0
  160. amulet/utils/__init__.pyi +0 -5
  161. amulet/utils/call_spec/_call_spec.py +2 -7
  162. amulet/utils/comment_json.py +188 -0
  163. amulet/utils/matrix.py +3 -3
  164. amulet/utils/numpy.hpp +36 -0
  165. amulet/utils/numpy_helpers.py +2 -2
  166. amulet/utils/world_utils.py +2 -2
  167. amulet/version.py.cpp +281 -0
  168. amulet/version.pyi +0 -8
  169. {amulet_core-2.0a5.dist-info → amulet_core-2.0a7.dist-info}/METADATA +3 -3
  170. amulet_core-2.0a7.dist-info/RECORD +295 -0
  171. amulet/chunk_/components/biome.py +0 -155
  172. amulet/chunk_/components/block_entity.py +0 -117
  173. amulet/chunk_/components/entity.py +0 -64
  174. amulet/chunk_/components/height_2d.py +0 -16
  175. amulet/level/bedrock/__init__.py +0 -2
  176. amulet/level/bedrock/_chunk_handle.py +0 -19
  177. amulet/level/bedrock/_dimension.py +0 -22
  178. amulet/level/bedrock/_level.py +0 -187
  179. amulet/level/bedrock/_raw/__init__.py +0 -5
  180. amulet/level/bedrock/_raw/_actor_counter.py +0 -53
  181. amulet/level/bedrock/_raw/_chunk.py +0 -54
  182. amulet/level/bedrock/_raw/_chunk_decode.py +0 -668
  183. amulet/level/bedrock/_raw/_chunk_encode.py +0 -602
  184. amulet/level/bedrock/_raw/_constant.py +0 -9
  185. amulet/level/bedrock/_raw/_dimension.py +0 -343
  186. amulet/level/bedrock/_raw/_level.py +0 -463
  187. amulet/level/bedrock/_raw/_level_dat.py +0 -90
  188. amulet/level/bedrock/_raw/_typing.py +0 -6
  189. amulet/level/bedrock/_raw/leveldb_chunk_versions.py +0 -83
  190. amulet/level/bedrock/chunk/__init__.py +0 -1
  191. amulet/level/bedrock/chunk/_chunk.py +0 -126
  192. amulet/level/bedrock/chunk/components/chunk_version.py +0 -12
  193. amulet/level/bedrock/chunk/components/finalised_state.py +0 -13
  194. amulet/level/bedrock/chunk/components/raw_chunk.py +0 -15
  195. amulet/level/construction/__init__.py +0 -0
  196. amulet/level/java/_chunk_handle.pyi +0 -15
  197. amulet/level/java/_dimension.pyi +0 -13
  198. amulet/level/java/_level.pyi +0 -120
  199. amulet/level/java/_raw/_chunk_decode.py +0 -561
  200. amulet/level/java/_raw/_chunk_encode.py +0 -463
  201. amulet/level/java/_raw/_constant.pyi +0 -20
  202. amulet/level/java/_raw/_data_pack/__init__.pyi +0 -8
  203. amulet/level/java/_raw/_data_pack/data_pack.pyi +0 -197
  204. amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +0 -75
  205. amulet/level/java/_raw/_dimension.pyi +0 -72
  206. amulet/level/java/_raw/_level.pyi +0 -238
  207. amulet/level/java/_raw/_typing.pyi +0 -5
  208. amulet/level/java/anvil/__init__.pyi +0 -11
  209. amulet/level/java/anvil/_dimension.pyi +0 -109
  210. amulet/level/java/anvil/_region.pyi +0 -197
  211. amulet/level/java/anvil/_sector_manager.pyi +0 -142
  212. amulet/level/java_forge/__init__.py +0 -0
  213. amulet/level/mcstructure/__init__.py +0 -0
  214. amulet/level/nbt/__init__.py +0 -0
  215. amulet/level/schematic/__init__.py +0 -0
  216. amulet/level/sponge_schematic/__init__.py +0 -0
  217. amulet/utils/call_spec/__init__.pyi +0 -53
  218. amulet/utils/call_spec/_call_spec.pyi +0 -272
  219. amulet/utils/matrix.pyi +0 -177
  220. amulet/utils/shareable_lock.pyi +0 -190
  221. amulet/utils/signal/__init__.pyi +0 -25
  222. amulet/utils/signal/_signal.pyi +0 -84
  223. amulet/utils/task_manager.pyi +0 -168
  224. amulet/utils/typing.py +0 -4
  225. amulet/utils/typing.pyi +0 -6
  226. amulet/utils/weakref.pyi +0 -50
  227. amulet/utils/world_utils.pyi +0 -109
  228. amulet_core-2.0a5.dist-info/RECORD +0 -210
  229. /amulet/{level/bedrock/chunk/components → mesh}/__init__.py +0 -0
  230. {amulet_core-2.0a5.dist-info → amulet_core-2.0a7.dist-info}/WHEEL +0 -0
  231. {amulet_core-2.0a5.dist-info → amulet_core-2.0a7.dist-info}/entry_points.txt +0 -0
  232. {amulet_core-2.0a5.dist-info → amulet_core-2.0a7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,361 @@
1
+ import os
2
+ import json
3
+ from typing import Union, Iterable, Generator, Optional, TypedDict, Literal, Any
4
+ from PIL import Image
5
+ import numpy
6
+
7
+ from amulet.block import Block
8
+ from amulet.utils import comment_json
9
+ from amulet.resource_pack import BaseResourcePackManager
10
+ from amulet.resource_pack.bedrock import BedrockResourcePack
11
+ from amulet.mesh.block.block_mesh import BlockMesh
12
+ from .blockshapes import BlockShapeClasses
13
+
14
+
15
+ class NumericalProperty(TypedDict):
16
+ name: str
17
+ type: Union[Literal["byte"], Literal["int"]]
18
+ value: int
19
+
20
+
21
+ class StrProperty(TypedDict):
22
+ name: str
23
+ type: Literal["string"]
24
+ value: str
25
+
26
+
27
+ class StateDict(TypedDict):
28
+ name: str
29
+ data: int
30
+ states: list[Union[NumericalProperty, StrProperty]]
31
+
32
+
33
+ class BlockPaletteDict(TypedDict):
34
+ blocks: list[StateDict]
35
+
36
+
37
+ def _load_data() -> tuple[
38
+ dict[str, str],
39
+ dict[
40
+ str,
41
+ tuple[
42
+ tuple[tuple[str, Union[int, str]], ...],
43
+ dict[tuple[Union[str, int], ...], int],
44
+ ],
45
+ ],
46
+ ]:
47
+ with open(os.path.join(os.path.dirname(__file__), "blockshapes.json")) as f:
48
+ _block_shapes: dict[str, str] = comment_json.load(f) # type: ignore
49
+
50
+ _aux_values: dict[
51
+ str,
52
+ tuple[
53
+ tuple[tuple[str, Union[int, str]], ...],
54
+ dict[tuple[Union[str, int], ...], int],
55
+ ],
56
+ ] = {}
57
+ with open(os.path.join(os.path.dirname(__file__), "block_palette.json")) as f:
58
+ _block_palette: BlockPaletteDict = comment_json.load(f) # type: ignore
59
+ for block in _block_palette["blocks"]:
60
+ data = block["data"]
61
+ name = block["name"]
62
+ if name not in _aux_values:
63
+ _aux_values[name] = (
64
+ tuple((state["name"], state["value"]) for state in block["states"]),
65
+ {},
66
+ )
67
+ _aux_values[name][1][tuple(state["value"] for state in block["states"])] = data
68
+
69
+ return _block_shapes, _aux_values
70
+
71
+
72
+ BlockShapes, AuxValues = _load_data()
73
+
74
+
75
+ def get_aux_value(block: Block) -> int:
76
+ name = block.namespaced_name
77
+ if name in AuxValues:
78
+ property_names, aux_map = AuxValues[name]
79
+ properties = block.properties
80
+ key = tuple(
81
+ (
82
+ properties[property_name].py_data
83
+ if property_name in properties
84
+ else default
85
+ )
86
+ for property_name, default in property_names
87
+ )
88
+ return aux_map.get(key, 0)
89
+ else:
90
+ return 0
91
+
92
+
93
+ class BedrockResourcePackManager(BaseResourcePackManager):
94
+ """A class to load and handle the data from the packs.
95
+ Packs are given as a list with the later packs overwriting the earlier ones."""
96
+
97
+ def __init__(
98
+ self,
99
+ resource_packs: Union[BedrockResourcePack, Iterable[BedrockResourcePack]],
100
+ load: bool = True,
101
+ ) -> None:
102
+ super().__init__()
103
+ self._block_shapes: dict[str, str] = {} # block string to block shape
104
+ self._blocks: dict[str, Union[dict[str, str], str, None]] = (
105
+ {}
106
+ ) # block string to short texture ids
107
+ self._terrain_texture: dict[str, tuple[str, ...]] = (
108
+ {}
109
+ ) # texture ids to list of relative paths. Each relates to a different data value.
110
+ self._textures: dict[str, str] = {} # relative path to texture path
111
+ self._all_textures = None
112
+
113
+ self._texture_is_transparent: dict[str, tuple[float, bool]] = {}
114
+
115
+ if isinstance(resource_packs, (list, tuple)):
116
+ self._packs = [
117
+ rp for rp in resource_packs if isinstance(rp, BedrockResourcePack)
118
+ ]
119
+ elif isinstance(resource_packs, BedrockResourcePack):
120
+ self._packs = [resource_packs]
121
+ else:
122
+ raise Exception(f"Invalid format {resource_packs}")
123
+ if load:
124
+ for _ in self.reload():
125
+ pass
126
+
127
+ def _unload(self) -> None:
128
+ """Clear all loaded resources."""
129
+ super()._unload()
130
+ self._block_shapes.clear()
131
+ self._blocks.clear()
132
+ self._terrain_texture.clear()
133
+ self._textures.clear()
134
+ self._all_textures = None
135
+
136
+ def _check_texture(self, texture_path: str) -> str:
137
+ if os.path.isfile(texture_path + ".png"):
138
+ texture_path += ".png"
139
+ elif os.path.isfile(texture_path + ".tga"):
140
+ texture_path += ".tga"
141
+ else:
142
+ texture_path = self.missing_no
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
+ bool(texture_is_transparent),
157
+ )
158
+ return texture_path
159
+
160
+ def _load_iter(self) -> Generator[float, None, None]:
161
+ self._block_shapes.update(BlockShapes) # add the default block shapes
162
+
163
+ transparency_cache_path = os.path.join(
164
+ os.environ["CACHE_DIR"],
165
+ "resource_packs",
166
+ "bedrock",
167
+ "transparency_cache.json",
168
+ )
169
+ self._load_transparency_cache(transparency_cache_path)
170
+
171
+ self._textures["missing_no"] = self._check_texture("missing")
172
+
173
+ pack_count = len(self._packs)
174
+
175
+ for pack_index, pack in enumerate(self._packs):
176
+ pack_progress = pack_index / pack_count
177
+ yield pack_progress
178
+
179
+ if pack.valid_pack:
180
+ terrain_texture_path = os.path.join(
181
+ pack.root_dir, "textures", "terrain_texture.json"
182
+ )
183
+ if os.path.isfile(terrain_texture_path):
184
+ try:
185
+ with open(terrain_texture_path) as f:
186
+ terrain_texture = comment_json.load(f)
187
+ except json.JSONDecodeError:
188
+ pass
189
+ else:
190
+ if (
191
+ isinstance(terrain_texture, dict)
192
+ and "texture_data" in terrain_texture
193
+ and isinstance(terrain_texture["texture_data"], dict)
194
+ ):
195
+ sub_progress = pack_progress
196
+ image_count = len(terrain_texture["texture_data"])
197
+
198
+ def get_texture(_relative_path: Any) -> str:
199
+ if isinstance(_relative_path, dict):
200
+ _relative_path = _relative_path.get(
201
+ "path", "misssingno"
202
+ )
203
+ assert isinstance(_relative_path, str)
204
+ if isinstance(_relative_path, str):
205
+ full_path = self._check_texture(
206
+ os.path.join(pack.root_dir, _relative_path)
207
+ )
208
+ if _relative_path in self._textures:
209
+ if full_path != self.missing_no:
210
+ self._textures[_relative_path] = full_path
211
+ else:
212
+ self._textures[_relative_path] = full_path
213
+ else:
214
+ raise TypeError
215
+ return _relative_path
216
+
217
+ for image_index, (texture_id, data) in enumerate(
218
+ terrain_texture["texture_data"].items()
219
+ ):
220
+ if (
221
+ isinstance(texture_id, str)
222
+ and isinstance(data, dict)
223
+ and "textures" in data
224
+ ):
225
+ texture_data = data["textures"]
226
+ if isinstance(texture_data, list):
227
+ self._terrain_texture[texture_id] = tuple(
228
+ get_texture(relative_path)
229
+ for relative_path in texture_data
230
+ )
231
+ else:
232
+ self._terrain_texture[texture_id] = (
233
+ get_texture(texture_data),
234
+ ) * 16
235
+ yield sub_progress + image_index / (
236
+ image_count * pack_count * 2
237
+ )
238
+ sub_progress = pack_progress + 1 / (pack_count * 2)
239
+ yield sub_progress
240
+ blocks_path = os.path.join(pack.root_dir, "blocks.json")
241
+ if os.path.isfile(blocks_path):
242
+ try:
243
+ with open(blocks_path) as f:
244
+ blocks = comment_json.load(f)
245
+ except json.JSONDecodeError:
246
+ pass
247
+ else:
248
+ if isinstance(blocks, dict):
249
+ model_count = len(blocks)
250
+ for model_index, (block_id, data) in enumerate(
251
+ blocks.items()
252
+ ):
253
+ if isinstance(block_id, str) and isinstance(data, dict):
254
+ if ":" not in block_id:
255
+ block_id = "minecraft:" + block_id
256
+ textures = data.get("textures")
257
+ if textures is None or isinstance(textures, str):
258
+ self._blocks[block_id] = textures
259
+ elif isinstance(textures, dict) and all(
260
+ isinstance(v, str) for v in textures.values()
261
+ ):
262
+ self._blocks[block_id] = textures # type: ignore # TODO: improve this with TypeGuard
263
+ else:
264
+ raise TypeError
265
+
266
+ yield sub_progress + (model_index) / (
267
+ model_count * pack_count * 2
268
+ )
269
+ yield pack_progress + 1
270
+
271
+ os.makedirs(os.path.dirname(transparency_cache_path), exist_ok=True)
272
+ with open(transparency_cache_path, "w") as f:
273
+ json.dump(self._texture_is_transparent, f)
274
+
275
+ @property
276
+ def textures(self) -> tuple[str, ...]:
277
+ """Returns a tuple of all the texture paths in the resource pack."""
278
+ return tuple(self._textures.values())
279
+
280
+ def get_texture_path(self, namespace: Optional[str], relative_path: str) -> str:
281
+ """Get the absolute texture path from the namespace and relative path pair"""
282
+ if relative_path in self._textures:
283
+ return self._textures[relative_path]
284
+ else:
285
+ return self.missing_no
286
+
287
+ def _get_model(self, block: Block) -> BlockMesh:
288
+ block_shape = self._block_shapes.get(block.namespaced_name, "cube")
289
+
290
+ if block_shape in BlockShapeClasses:
291
+ block_shape_class = BlockShapeClasses[block_shape]
292
+ else:
293
+ block_shape_class = BlockShapeClasses["cube"]
294
+
295
+ if not block_shape_class.is_valid(block):
296
+ block_shape_class = BlockShapeClasses["cube"]
297
+
298
+ texture_index = block_shape_class.texture_index(block, get_aux_value(block))
299
+
300
+ if block.namespaced_name in self._blocks:
301
+ texture_id = self._blocks[block.namespaced_name]
302
+ if isinstance(texture_id, str):
303
+ up = down = north = east = south = west = self._get_texture(
304
+ texture_id, texture_index
305
+ )
306
+ transparent = (self._texture_is_transparent[up][1],) * 6
307
+
308
+ elif isinstance(texture_id, dict):
309
+ down = self._get_texture(
310
+ texture_id.get("down", "missing"), texture_index
311
+ )
312
+ up = self._get_texture(texture_id.get("up", "missing"), texture_index)
313
+
314
+ if "side" in texture_id:
315
+ north = east = south = west = self._get_texture(
316
+ texture_id.get("side", "missing"), texture_index
317
+ )
318
+ transparent = (
319
+ self._texture_is_transparent[down][1],
320
+ self._texture_is_transparent[up][1],
321
+ ) + (self._texture_is_transparent[north][1],) * 4
322
+ else:
323
+ north = self._get_texture(
324
+ texture_id.get("north", "missing"), texture_index
325
+ )
326
+ east = self._get_texture(
327
+ texture_id.get("east", "missing"), texture_index
328
+ )
329
+ south = self._get_texture(
330
+ texture_id.get("south", "missing"), texture_index
331
+ )
332
+ west = self._get_texture(
333
+ texture_id.get("west", "missing"), texture_index
334
+ )
335
+ transparent = (
336
+ self._texture_is_transparent[down][1],
337
+ self._texture_is_transparent[up][1],
338
+ self._texture_is_transparent[north][1],
339
+ self._texture_is_transparent[east][1],
340
+ self._texture_is_transparent[south][1],
341
+ self._texture_is_transparent[west][1],
342
+ )
343
+ else:
344
+ up = down = north = east = south = west = self._get_texture(
345
+ "missing", texture_index
346
+ )
347
+ transparent = (self._texture_is_transparent[up][1],) * 6
348
+
349
+ return block_shape_class.get_block_model(
350
+ block, down, up, north, east, south, west, transparent
351
+ )
352
+
353
+ return self.missing_block
354
+
355
+ def _get_texture(self, texture_id: str, index: int) -> str:
356
+ texture = self.missing_no
357
+ if texture_id in self._terrain_texture:
358
+ texture_list = self._terrain_texture[texture_id]
359
+ if len(texture_list) > index:
360
+ texture = self.get_texture_path(None, texture_list[index])
361
+ return texture
@@ -0,0 +1,15 @@
1
+ import os
2
+ import json
3
+
4
+
5
+ def main() -> None:
6
+ with open(os.path.join(os.path.dirname(__file__), "blockshapes.json")) as f:
7
+ shapes = json.load(f)
8
+ shapes_tuple = tuple(sorted(shapes.items(), key=lambda i: (i[1], i[0])))
9
+ shapes2 = dict(shapes_tuple)
10
+ with open(os.path.join(os.path.dirname(__file__), "blockshapes.json"), "w") as f:
11
+ json.dump(shapes2, f, indent=4)
12
+
13
+
14
+ if __name__ == "__main__":
15
+ main()
@@ -0,0 +1,2 @@
1
+ from .resource_pack import JavaResourcePack
2
+ from .resource_pack_manager import JavaResourcePackManager
@@ -0,0 +1,212 @@
1
+ import os
2
+ import shutil
3
+ import zipfile
4
+ import json
5
+ from urllib.request import urlopen, Request
6
+ import io
7
+ from typing import Generator, TypeVar, Any, Optional
8
+ import logging
9
+
10
+ from amulet.resource_pack import JavaResourcePack
11
+
12
+ T = TypeVar("T")
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+ launcher_manifest: Optional[dict] = None
17
+ INCLUDE_SNAPSHOT = False
18
+
19
+
20
+ def get_launcher_manifest() -> dict:
21
+ global launcher_manifest
22
+ if launcher_manifest is None:
23
+ log.info("Downloading java launcher manifest file.")
24
+ with urlopen(
25
+ "https://launchermeta.mojang.com/mc/game/version_manifest.json", timeout=20
26
+ ) as manifest:
27
+ launcher_manifest = json.load(manifest)
28
+ log.info("Finished downloading java launcher manifest file.")
29
+ return launcher_manifest
30
+
31
+
32
+ def generator_unpacker(gen: Generator[Any, Any, T]) -> T:
33
+ try:
34
+ while True:
35
+ next(gen)
36
+ except StopIteration as e:
37
+ return e.value # type: ignore
38
+
39
+
40
+ def get_latest() -> JavaResourcePack:
41
+ return generator_unpacker(get_latest_iter())
42
+
43
+
44
+ def get_latest_iter() -> Generator[float, None, JavaResourcePack]:
45
+ """Download the latest resource pack if required.
46
+
47
+ :return: The loaded Java resource pack.
48
+ :raises:
49
+ Exception: If the
50
+ """
51
+ vanilla_rp_path = os.path.join(
52
+ os.environ["CACHE_DIR"], "resource_packs", "java", "vanilla"
53
+ )
54
+ try:
55
+ if INCLUDE_SNAPSHOT:
56
+ new_version = get_launcher_manifest()["latest"]["snapshot"]
57
+ else:
58
+ new_version = get_launcher_manifest()["latest"]["release"]
59
+ except Exception as e:
60
+ if os.path.isdir(vanilla_rp_path):
61
+ log.error(
62
+ "Could not download the launcher manifest. The resource pack seems to be present so using that."
63
+ )
64
+ else:
65
+ raise e
66
+ else:
67
+ has_new_pack = False
68
+ if os.path.isfile(os.path.join(vanilla_rp_path, "version")):
69
+ with open(os.path.join(vanilla_rp_path, "version")) as f:
70
+ old_version = f.read()
71
+ has_new_pack = old_version == new_version
72
+
73
+ if not has_new_pack:
74
+ yield from _remove_and_download_iter(vanilla_rp_path, new_version)
75
+ return JavaResourcePack(vanilla_rp_path)
76
+
77
+
78
+ _java_vanilla_fix: Optional[JavaResourcePack] = None
79
+ _java_vanilla_latest: Optional[JavaResourcePack] = None
80
+
81
+
82
+ def get_java_vanilla_fix() -> JavaResourcePack:
83
+ global _java_vanilla_fix
84
+ if _java_vanilla_fix is None:
85
+ _java_vanilla_fix = JavaResourcePack(
86
+ os.path.join(os.path.dirname(__file__), "java_vanilla_fix")
87
+ )
88
+ return _java_vanilla_fix
89
+
90
+
91
+ def get_java_vanilla_latest() -> JavaResourcePack:
92
+ global _java_vanilla_latest
93
+ if _java_vanilla_latest is None:
94
+ _java_vanilla_latest = get_latest()
95
+ return _java_vanilla_latest
96
+
97
+
98
+ def get_java_vanilla_latest_iter() -> Generator[float, None, JavaResourcePack]:
99
+ global _java_vanilla_latest
100
+ if _java_vanilla_latest is None:
101
+ _java_vanilla_latest = yield from get_latest_iter()
102
+ return _java_vanilla_latest
103
+
104
+
105
+ def _remove_and_download(path: str, version: str) -> None:
106
+ for _ in _remove_and_download_iter(path, version):
107
+ pass
108
+
109
+
110
+ def _remove_and_download_iter(path: str, version: str) -> Generator[float, None, None]:
111
+ # try downloading the new resources to a temporary location
112
+ temp_path = os.path.join(os.path.dirname(path), "_temp_")
113
+ # clear the temporary location
114
+ if os.path.isfile(temp_path):
115
+ os.remove(temp_path)
116
+ elif os.path.isdir(temp_path):
117
+ shutil.rmtree(temp_path, ignore_errors=True)
118
+
119
+ yield from download_resources_iter(temp_path, version)
120
+ if os.path.isdir(path):
121
+ shutil.rmtree(path, ignore_errors=True)
122
+
123
+ shutil.move(temp_path, path)
124
+
125
+ with open(os.path.join(path, "version"), "w") as f:
126
+ f.write(version)
127
+
128
+
129
+ def download_with_retry(
130
+ url: str, chunk_size: int = 4096, attempts: int = 5
131
+ ) -> Generator[float, None, bytes]:
132
+ content_length_found = 0
133
+ content = []
134
+
135
+ for _ in range(attempts):
136
+ request = Request(url, headers={"Range": f"bytes={content_length_found}-"})
137
+ with urlopen(request, timeout=20) as response:
138
+ content_length = int(response.headers["content-length"].strip())
139
+ while content_length_found < content_length:
140
+ chunk = response.read(chunk_size)
141
+ if not chunk:
142
+ break
143
+ content.append(chunk)
144
+ content_length_found += len(chunk)
145
+ yield min(1.0, content_length_found / content_length)
146
+ if content_length == content_length_found:
147
+ break
148
+ else:
149
+ raise RuntimeError(f"Failed to download")
150
+ return b"".join(content)
151
+
152
+
153
+ def download_resources(path: str, version: str) -> None:
154
+ generator_unpacker(download_resources_iter(path, version))
155
+
156
+
157
+ def download_resources_iter(
158
+ path: str, version: str, chunk_size: int = 4096
159
+ ) -> Generator[float, None, None]:
160
+ log.info(f"Downloading Java resource pack for version {version}")
161
+ version_url = next(
162
+ (v["url"] for v in get_launcher_manifest()["versions"] if v["id"] == version),
163
+ None,
164
+ )
165
+ if version_url is None:
166
+ raise Exception(f"Could not find Java resource pack for version {version}.")
167
+
168
+ try:
169
+ with urlopen(version_url, timeout=20) as vm:
170
+ version_manifest = json.load(vm)
171
+ version_client_url = version_manifest["downloads"]["client"]["url"]
172
+
173
+ downloader = download_with_retry(version_client_url)
174
+ try:
175
+ while True:
176
+ yield next(downloader) / 2
177
+ except StopIteration as e:
178
+ data = e.value
179
+
180
+ client = zipfile.ZipFile(io.BytesIO(data))
181
+ paths: list[str] = [
182
+ fpath for fpath in client.namelist() if fpath.startswith("assets/")
183
+ ]
184
+ path_count = len(paths)
185
+ for path_index, fpath in enumerate(paths):
186
+ if not path_index % 30:
187
+ yield path_index / (path_count * 2) + 0.5
188
+ if fpath.endswith("/"):
189
+ continue
190
+ os.makedirs(
191
+ os.path.dirname(os.path.abspath(os.path.join(path, fpath))),
192
+ exist_ok=True,
193
+ )
194
+ client.extract(fpath, path)
195
+ if "pack.mcmeta" in client.namelist():
196
+ client.extract("pack.mcmeta", path)
197
+ else:
198
+ # TODO: work out proper version support for this
199
+ with open(os.path.join(path, "pack.mcmeta"), "w") as f:
200
+ f.write(
201
+ '{"pack": {"description": "The default data for Minecraft","pack_format": 7}}'
202
+ )
203
+ if "pack.png" in client.namelist():
204
+ client.extract("pack.png", path)
205
+
206
+ except Exception as e:
207
+ log.error(
208
+ f"Failed to download and extract the Java resource pack for version {version}.",
209
+ exc_info=True,
210
+ )
211
+ raise e
212
+ log.info(f"Finished downloading Java resource pack for version {version}")