amulet-core 2.0a6__cp311-cp311-win_amd64.whl → 2.0a8__cp311-cp311-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of amulet-core might be problematic. Click here for more details.
- amulet/__init__.cp311-win_amd64.pyd +0 -0
- amulet/__init__.py.cpp +6 -0
- amulet/__init__.pyi +2 -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.hpp +2 -1
- 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/_chunk_handle.py +45 -22
- 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__.pyi +301 -0
- amulet/mesh/block/_cube.py +198 -0
- amulet/mesh/block/_missing_block.py +20 -0
- amulet/mesh/block/block_mesh.cpp +107 -0
- amulet/mesh/block/block_mesh.hpp +207 -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 +63 -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 +85 -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 +563 -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/cast.py +10 -0
- amulet/utils/comment_json.py +188 -0
- amulet/utils/matrix.py +3 -3
- amulet/utils/numpy_helpers.py +2 -2
- amulet/utils/shareable_lock.py +2 -2
- amulet/utils/world_utils.py +2 -2
- amulet/version.pyi +0 -8
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/METADATA +2 -2
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/RECORD +91 -103
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/WHEEL +1 -1
- 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/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.0a8.dist-info}/entry_points.txt +0 -0
- {amulet_core-2.0a6.dist-info → amulet_core-2.0a8.dist-info}/top_level.txt +0 -0
|
@@ -1,602 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import struct
|
|
3
|
-
from typing import TYPE_CHECKING, Callable, Iterable
|
|
4
|
-
from functools import cache
|
|
5
|
-
from logging import getLogger
|
|
6
|
-
|
|
7
|
-
import numpy
|
|
8
|
-
|
|
9
|
-
from amulet_nbt import (
|
|
10
|
-
CompoundTag,
|
|
11
|
-
AbstractBaseIntTag,
|
|
12
|
-
ShortTag,
|
|
13
|
-
IntTag,
|
|
14
|
-
FloatTag,
|
|
15
|
-
StringTag,
|
|
16
|
-
ListTag,
|
|
17
|
-
NamedTag,
|
|
18
|
-
utf8_escape_encoding,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
from amulet.block import Block, PropertyValueType
|
|
22
|
-
from amulet.block_entity import BlockEntity
|
|
23
|
-
from amulet.entity import Entity
|
|
24
|
-
from amulet.chunk.components.sub_chunk_array import SubChunkArrayContainer
|
|
25
|
-
from amulet.chunk.components.block import BlockComponent, BlockComponentData
|
|
26
|
-
from amulet.chunk.components.block_entity import (
|
|
27
|
-
BlockEntityComponent,
|
|
28
|
-
BlockEntityComponentData,
|
|
29
|
-
)
|
|
30
|
-
from amulet.chunk.components.entity import EntityComponent, EntityComponentData
|
|
31
|
-
from amulet.chunk.components.height_2d import Height2DComponent
|
|
32
|
-
from amulet.chunk.components.biome import Biome2DComponent, Biome3DComponent
|
|
33
|
-
from amulet.game import get_game_version
|
|
34
|
-
from amulet.version import VersionNumber
|
|
35
|
-
from amulet.palette import BlockPalette, BiomePalette
|
|
36
|
-
from amulet.utils.world_utils import to_nibble_array
|
|
37
|
-
from amulet.utils.numpy import unique_inverse
|
|
38
|
-
|
|
39
|
-
from amulet.level.bedrock._raw import BedrockRawChunk
|
|
40
|
-
from amulet.level.bedrock.chunk import BedrockChunk
|
|
41
|
-
from amulet.level.bedrock.chunk.components.finalised_state import (
|
|
42
|
-
FinalisedStateComponent,
|
|
43
|
-
)
|
|
44
|
-
from amulet.level.bedrock.chunk.components.raw_chunk import RawChunkComponent
|
|
45
|
-
from amulet.level.bedrock.chunk.components.chunk_version import ChunkVersionComponent
|
|
46
|
-
|
|
47
|
-
if TYPE_CHECKING:
|
|
48
|
-
from ._level import BedrockRawLevel
|
|
49
|
-
from ._dimension import BedrockRawDimension
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
log = getLogger(__name__)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@cache
|
|
56
|
-
def pack_block_version(version: VersionNumber) -> int:
|
|
57
|
-
return struct.unpack(">i", bytes([*version.padded_version(4)]))[0] # type: ignore
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def native_to_raw(
|
|
61
|
-
raw_level: BedrockRawLevel, dimension: BedrockRawDimension, chunk: BedrockChunk
|
|
62
|
-
) -> BedrockRawChunk:
|
|
63
|
-
game_version = get_game_version("bedrock", raw_level.version)
|
|
64
|
-
max_version = game_version.max_version
|
|
65
|
-
|
|
66
|
-
floor_cy = dimension.bounds().min_y >> 4
|
|
67
|
-
ceil_cy = dimension.bounds().max_y >> 4
|
|
68
|
-
|
|
69
|
-
if chunk.has_component(RawChunkComponent):
|
|
70
|
-
raw_chunk_component = chunk.get_component(RawChunkComponent)
|
|
71
|
-
if raw_chunk_component is None:
|
|
72
|
-
raw_chunk = BedrockRawChunk()
|
|
73
|
-
else:
|
|
74
|
-
raw_chunk = raw_chunk_component
|
|
75
|
-
else:
|
|
76
|
-
raw_chunk = BedrockRawChunk()
|
|
77
|
-
|
|
78
|
-
chunk_data = raw_chunk.chunk_data
|
|
79
|
-
|
|
80
|
-
# Chunk version
|
|
81
|
-
chunk_version = chunk.get_component(ChunkVersionComponent)
|
|
82
|
-
chunk_data[b"v" if chunk_version <= 20 else b","] = bytes([chunk_version])
|
|
83
|
-
|
|
84
|
-
# Finalised state
|
|
85
|
-
finalised_state = chunk.get_component(FinalisedStateComponent)
|
|
86
|
-
if max_version >= VersionNumber(1):
|
|
87
|
-
# TODO: when did this become an int?
|
|
88
|
-
chunk_data[b"\x36"] = struct.pack("<i", finalised_state)
|
|
89
|
-
else:
|
|
90
|
-
chunk_data[b"\x36"] = struct.pack("b", finalised_state)
|
|
91
|
-
|
|
92
|
-
# blocks
|
|
93
|
-
block_component = chunk.get_component(BlockComponent)
|
|
94
|
-
terrain: dict[int, bytes]
|
|
95
|
-
if max_version >= VersionNumber(1, 17, 30):
|
|
96
|
-
terrain = _encode_blocks_v9(
|
|
97
|
-
block_component, floor_cy, ceil_cy, dimension.default_block()[0]
|
|
98
|
-
)
|
|
99
|
-
elif max_version >= VersionNumber(1, 3):
|
|
100
|
-
terrain = _encode_blocks_v8(
|
|
101
|
-
block_component, floor_cy, ceil_cy, dimension.default_block()[0]
|
|
102
|
-
)
|
|
103
|
-
elif max_version >= VersionNumber(0, 17):
|
|
104
|
-
terrain = _encode_blocks_v1(block_component, dimension.default_block()[0])
|
|
105
|
-
else:
|
|
106
|
-
get_block_id_override = raw_level.block_id_override.namespace_id_to_numerical_id
|
|
107
|
-
get_block_id_game = game_version.block.namespace_id_to_numerical_id
|
|
108
|
-
|
|
109
|
-
@cache
|
|
110
|
-
def encode_block(block: Block) -> tuple[int, int]:
|
|
111
|
-
namespace = block.namespace
|
|
112
|
-
base_name = block.base_name
|
|
113
|
-
try:
|
|
114
|
-
block_id = get_block_id_override(namespace, base_name)
|
|
115
|
-
except KeyError:
|
|
116
|
-
try:
|
|
117
|
-
block_id = get_block_id_game(namespace, base_name)
|
|
118
|
-
except KeyError:
|
|
119
|
-
if namespace == "numerical" and base_name.isnumeric():
|
|
120
|
-
block_id = int(base_name) & 255
|
|
121
|
-
else:
|
|
122
|
-
return 0, 0
|
|
123
|
-
block_data_tag = block.properties.get("block_data")
|
|
124
|
-
if isinstance(block_data_tag, AbstractBaseIntTag):
|
|
125
|
-
block_data = block_data_tag.py_int & 15
|
|
126
|
-
else:
|
|
127
|
-
block_data = 0
|
|
128
|
-
return block_id, block_data
|
|
129
|
-
|
|
130
|
-
terrain = _encode_blocks_v0(block_component, encode_block)
|
|
131
|
-
|
|
132
|
-
# TODO: is this right?
|
|
133
|
-
for cy, sub_chunk in terrain.items():
|
|
134
|
-
if 25 <= chunk_version <= 28:
|
|
135
|
-
# The chunk db keys all start at 0 regardless of chunk floor position.
|
|
136
|
-
# This is the floor position of when the world was created.
|
|
137
|
-
# If the floor position changes in the future this will break.
|
|
138
|
-
chunk_key = struct.pack("b", cy - floor_cy)
|
|
139
|
-
else:
|
|
140
|
-
chunk_key = struct.pack("b", cy)
|
|
141
|
-
|
|
142
|
-
chunk_data[b"\x2F" + chunk_key] = sub_chunk
|
|
143
|
-
|
|
144
|
-
block_entity_component = chunk.get_component(BlockEntityComponent)
|
|
145
|
-
block_entities_out = _encode_block_entities(block_entity_component)
|
|
146
|
-
if block_entities_out:
|
|
147
|
-
chunk_data[b"\x31"] = _pack_nbt_list(block_entities_out)
|
|
148
|
-
|
|
149
|
-
entity_component = chunk.get_component(EntityComponent)
|
|
150
|
-
entities_out = _encode_entities(
|
|
151
|
-
entity_component, max_version >= VersionNumber(1, 8)
|
|
152
|
-
)
|
|
153
|
-
if entities_out:
|
|
154
|
-
if max_version >= VersionNumber(1, 18, 30):
|
|
155
|
-
raw_chunk.entity_actor.extend(entities_out)
|
|
156
|
-
else:
|
|
157
|
-
chunk_data[b"\x32"] = _pack_nbt_list(entities_out)
|
|
158
|
-
|
|
159
|
-
height_component = chunk.get_component(Height2DComponent)
|
|
160
|
-
if height_component is None:
|
|
161
|
-
height = bytes(512)
|
|
162
|
-
else:
|
|
163
|
-
height = height_component.ravel().astype("<i2").tobytes()
|
|
164
|
-
|
|
165
|
-
get_biome_id_override = raw_level.biome_id_override.namespace_id_to_numerical_id
|
|
166
|
-
get_biome_id_game = game_version.biome.namespace_id_to_numerical_id
|
|
167
|
-
|
|
168
|
-
@cache
|
|
169
|
-
def encode_biome(namespace: str, base_name: str) -> int:
|
|
170
|
-
try:
|
|
171
|
-
# First try overrides
|
|
172
|
-
return get_biome_id_override(namespace, base_name)
|
|
173
|
-
except KeyError:
|
|
174
|
-
try:
|
|
175
|
-
# Then fall back to the game implementation.
|
|
176
|
-
return get_biome_id_game(namespace, base_name)
|
|
177
|
-
except KeyError:
|
|
178
|
-
log.error(
|
|
179
|
-
f"Unknown biome {namespace}:{base_name}. Falling back to default."
|
|
180
|
-
)
|
|
181
|
-
default_biome = dimension.default_biome()
|
|
182
|
-
if (
|
|
183
|
-
namespace == default_biome.namespace
|
|
184
|
-
and base_name == default_biome.base_name
|
|
185
|
-
):
|
|
186
|
-
return 0
|
|
187
|
-
return encode_biome(default_biome.namespace, default_biome.base_name)
|
|
188
|
-
|
|
189
|
-
if chunk.has_component(Biome2DComponent):
|
|
190
|
-
biome_2d_component = chunk.get_component(Biome2DComponent)
|
|
191
|
-
palette_indexes, array = numpy.unique(
|
|
192
|
-
biome_2d_component.array, return_inverse=True
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
biome_id_palette = _encode_biome_palette(
|
|
196
|
-
biome_2d_component.palette,
|
|
197
|
-
palette_indexes,
|
|
198
|
-
encode_biome,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
biomes_array = numpy.asarray(biome_id_palette, dtype=numpy.uint8)[
|
|
202
|
-
array
|
|
203
|
-
].T.tobytes()
|
|
204
|
-
chunk_data[b"\x2D"] = height + biomes_array
|
|
205
|
-
|
|
206
|
-
elif chunk.has_component(Biome3DComponent):
|
|
207
|
-
biome_3d_component = chunk.get_component(Biome3DComponent)
|
|
208
|
-
d2d: list[bytes] = [height]
|
|
209
|
-
|
|
210
|
-
biomes: SubChunkArrayContainer = biome_3d_component.sections
|
|
211
|
-
biome_palette = biome_3d_component.palette
|
|
212
|
-
|
|
213
|
-
def encode_biome_section(array: numpy.ndarray) -> bytes:
|
|
214
|
-
palette_indexes, arr_uniq = numpy.unique(array, return_inverse=True)
|
|
215
|
-
|
|
216
|
-
biome_id_palette = _encode_biome_palette(
|
|
217
|
-
biome_palette,
|
|
218
|
-
palette_indexes,
|
|
219
|
-
encode_biome,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
if len(biome_id_palette) == 1:
|
|
223
|
-
encoded_section = b"\x01"
|
|
224
|
-
else:
|
|
225
|
-
encoded_section = _encode_packed_array(
|
|
226
|
-
arr_uniq.reshape(array.shape)
|
|
227
|
-
) + struct.pack("<I", len(biome_id_palette))
|
|
228
|
-
return (
|
|
229
|
-
encoded_section
|
|
230
|
-
+ numpy.asarray(biome_id_palette, dtype=numpy.uint32).tobytes()
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
@cache
|
|
234
|
-
def get_default_biome_section() -> bytes:
|
|
235
|
-
default_section = biomes.default_array
|
|
236
|
-
if isinstance(default_section, int):
|
|
237
|
-
default_section = numpy.full(
|
|
238
|
-
(16, 16, 16), default_section, dtype=numpy.uint32
|
|
239
|
-
)
|
|
240
|
-
return encode_biome_section(default_section)
|
|
241
|
-
|
|
242
|
-
highest_cy = next(
|
|
243
|
-
(cy for cy in range(ceil_cy, floor_cy - 1, -1) if cy in biomes), floor_cy
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
for cy in range(floor_cy, floor_cy + 25):
|
|
247
|
-
if cy in biomes:
|
|
248
|
-
d2d.append(encode_biome_section(biomes[cy]))
|
|
249
|
-
elif cy <= highest_cy:
|
|
250
|
-
# Everything below the highest must be defined
|
|
251
|
-
d2d.append(get_default_biome_section())
|
|
252
|
-
else:
|
|
253
|
-
d2d.append(b"\xFF")
|
|
254
|
-
|
|
255
|
-
chunk_data[b"+"] = b"".join(d2d)
|
|
256
|
-
else:
|
|
257
|
-
raise RuntimeError
|
|
258
|
-
|
|
259
|
-
return raw_chunk
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def _encode_biome_palette(
|
|
263
|
-
biome_palette: BiomePalette,
|
|
264
|
-
palette_indexes: Iterable[int],
|
|
265
|
-
encode_biome: Callable[[str, str], int],
|
|
266
|
-
) -> list[int]:
|
|
267
|
-
biome_int_palette: list[int] = []
|
|
268
|
-
for palette_index in palette_indexes:
|
|
269
|
-
biome = biome_palette[palette_index]
|
|
270
|
-
biome_int_palette.append(encode_biome(biome.namespace, biome.base_name))
|
|
271
|
-
return biome_int_palette
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def _encode_blocks_v0(
|
|
275
|
-
block_component: BlockComponentData,
|
|
276
|
-
encode_block: Callable[[Block], tuple[int, int]],
|
|
277
|
-
) -> dict[int, bytes]:
|
|
278
|
-
blocks: SubChunkArrayContainer = block_component.sections
|
|
279
|
-
block_palette = block_component.palette
|
|
280
|
-
sections: dict[int, bytes] = {}
|
|
281
|
-
|
|
282
|
-
palette = numpy.array(
|
|
283
|
-
[encode_block(block_stack[0]) for block_stack in block_palette]
|
|
284
|
-
)
|
|
285
|
-
for cy in range(16):
|
|
286
|
-
if cy in blocks:
|
|
287
|
-
block_sub_array = palette[numpy.transpose(blocks[cy], (0, 2, 1)).ravel()]
|
|
288
|
-
if numpy.any(block_sub_array):
|
|
289
|
-
data_sub_array = block_sub_array[:, 1]
|
|
290
|
-
block_sub_array = block_sub_array[:, 0]
|
|
291
|
-
sections[cy] = (
|
|
292
|
-
b"\00"
|
|
293
|
-
+ block_sub_array.astype("uint8").tobytes()
|
|
294
|
-
+ to_nibble_array(data_sub_array).tobytes()
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
return sections
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
@cache
|
|
301
|
-
def _encode_block(block: Block) -> NamedTag:
|
|
302
|
-
"""Encode a block to its NameTag form.
|
|
303
|
-
|
|
304
|
-
Note that this may return a cached result. Do not modify the returned value.
|
|
305
|
-
|
|
306
|
-
:param block: The block to encode
|
|
307
|
-
:return: The encoded block.
|
|
308
|
-
"""
|
|
309
|
-
if block.version >= VersionNumber(1, 13): # This version may be wrong
|
|
310
|
-
# block with block state
|
|
311
|
-
return NamedTag(
|
|
312
|
-
CompoundTag(
|
|
313
|
-
{
|
|
314
|
-
"name": StringTag(block.namespaced_name),
|
|
315
|
-
"states": CompoundTag(
|
|
316
|
-
{
|
|
317
|
-
key: val
|
|
318
|
-
for key, val in block.properties.items()
|
|
319
|
-
if isinstance(val, PropertyValueType)
|
|
320
|
-
}
|
|
321
|
-
),
|
|
322
|
-
"version": IntTag(pack_block_version(block.version)),
|
|
323
|
-
}
|
|
324
|
-
)
|
|
325
|
-
)
|
|
326
|
-
else:
|
|
327
|
-
# block with block data
|
|
328
|
-
block_data = block.properties.get("block_data", IntTag(0))
|
|
329
|
-
if isinstance(block_data, IntTag):
|
|
330
|
-
block_data_int = block_data.py_int
|
|
331
|
-
else:
|
|
332
|
-
block_data_int = 0
|
|
333
|
-
return NamedTag(
|
|
334
|
-
CompoundTag(
|
|
335
|
-
{
|
|
336
|
-
"name": StringTag(block.namespaced_name),
|
|
337
|
-
"val": ShortTag(block_data_int),
|
|
338
|
-
}
|
|
339
|
-
)
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
def _encode_block_palette(
|
|
344
|
-
block_palette: BlockPalette, default_block: Block, max_block_depth: int
|
|
345
|
-
) -> list[tuple[list[NamedTag], numpy.ndarray]]:
|
|
346
|
-
"""Encode the block palette.
|
|
347
|
-
|
|
348
|
-
Remove duplicates from each layer of the palette.
|
|
349
|
-
Return the encoded block palette and a numpy index array to remap the old block array.
|
|
350
|
-
|
|
351
|
-
:param block_palette: The block palette to encode.
|
|
352
|
-
:param default_block: The default block to use in extra layers where the block stack is not long enough.
|
|
353
|
-
:param max_block_depth: The maximum block depth to process.
|
|
354
|
-
:return: The encoded block palette and numpy inverse array for each layer.
|
|
355
|
-
"""
|
|
356
|
-
block: Block | None
|
|
357
|
-
|
|
358
|
-
# Transpose the block palette
|
|
359
|
-
block_layers: list[list[Block | None]] = [
|
|
360
|
-
[None] * len(block_palette) for _ in range(max_block_depth)
|
|
361
|
-
]
|
|
362
|
-
for block_index, block_stack in enumerate(block_palette):
|
|
363
|
-
for block_layer, block in enumerate(block_stack):
|
|
364
|
-
if block_layer < max_block_depth:
|
|
365
|
-
block_layers[block_layer][block_index] = block
|
|
366
|
-
|
|
367
|
-
# Crop layers without extra blocks
|
|
368
|
-
block_layers = block_layers[
|
|
369
|
-
: next(
|
|
370
|
-
(
|
|
371
|
-
i + 1
|
|
372
|
-
for i in range(len(block_layers) - 1, 0, -1)
|
|
373
|
-
if any(block_layers[i])
|
|
374
|
-
),
|
|
375
|
-
1,
|
|
376
|
-
)
|
|
377
|
-
]
|
|
378
|
-
|
|
379
|
-
compact_layers: list[tuple[list[NamedTag], numpy.ndarray]] = []
|
|
380
|
-
|
|
381
|
-
for layer in block_layers:
|
|
382
|
-
compact_palette_lut: dict[Block, int] = {}
|
|
383
|
-
encoded_palette: list[NamedTag] = []
|
|
384
|
-
remap: list[int] = []
|
|
385
|
-
for block in layer:
|
|
386
|
-
if block is None:
|
|
387
|
-
block = default_block
|
|
388
|
-
palette_index = compact_palette_lut.get(block)
|
|
389
|
-
if palette_index is None:
|
|
390
|
-
palette_index = len(compact_palette_lut)
|
|
391
|
-
compact_palette_lut[block] = palette_index
|
|
392
|
-
encoded_palette.append(_encode_block(block))
|
|
393
|
-
remap.append(palette_index)
|
|
394
|
-
|
|
395
|
-
compact_layers.append((encoded_palette, numpy.array(remap)))
|
|
396
|
-
|
|
397
|
-
return compact_layers
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
def _encode_block_palette_layer(
|
|
401
|
-
layer_palette: list[NamedTag],
|
|
402
|
-
layer_palette_lut: numpy.ndarray,
|
|
403
|
-
palette_lut: numpy.ndarray,
|
|
404
|
-
unique_block_array: numpy.ndarray,
|
|
405
|
-
) -> bytes:
|
|
406
|
-
"""Encode one palettised layer.
|
|
407
|
-
|
|
408
|
-
The two look up tables are so that the unique can be computed once per sub-chunk rather than once per layer.
|
|
409
|
-
|
|
410
|
-
:param layer_palette: The encoded block palette. This may contain unused values. All values must be unique.
|
|
411
|
-
:param layer_palette_lut: A LUT mapping from the original block palette to the compacted palette for this layer.
|
|
412
|
-
:param palette_lut: A LUT mapping from the unique block array to the original block palette.
|
|
413
|
-
:param unique_block_array: The block array containing only unique values.
|
|
414
|
-
:return: The encoded bytes
|
|
415
|
-
"""
|
|
416
|
-
# merge the luts
|
|
417
|
-
lut = layer_palette_lut[palette_lut]
|
|
418
|
-
# compute the unique. This is faster than computing the unique for the whole array for each layer.
|
|
419
|
-
palette_indexes, lut = numpy.unique(lut, return_inverse=True)
|
|
420
|
-
|
|
421
|
-
unique_layer_palette = [layer_palette[i] for i in palette_indexes]
|
|
422
|
-
block_array = lut[unique_block_array]
|
|
423
|
-
|
|
424
|
-
return b"".join(
|
|
425
|
-
(
|
|
426
|
-
_encode_packed_array(block_array.reshape((16, 16, 16))),
|
|
427
|
-
struct.pack("<I", len(unique_layer_palette)),
|
|
428
|
-
*[
|
|
429
|
-
block.save_to(
|
|
430
|
-
compressed=False,
|
|
431
|
-
little_endian=True,
|
|
432
|
-
string_encoding=utf8_escape_encoding,
|
|
433
|
-
)
|
|
434
|
-
for block in unique_layer_palette
|
|
435
|
-
],
|
|
436
|
-
)
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
def _encode_palettized_chunk(
|
|
441
|
-
block_component: BlockComponentData,
|
|
442
|
-
min_cy: int,
|
|
443
|
-
max_cy: int,
|
|
444
|
-
default_block: Block,
|
|
445
|
-
max_block_depth: int,
|
|
446
|
-
) -> dict[int, list[bytes]]:
|
|
447
|
-
"""Encode the palettized sections of the chunk data. The caller handles the header."""
|
|
448
|
-
blocks: SubChunkArrayContainer = block_component.sections
|
|
449
|
-
block_palette: BlockPalette = block_component.palette
|
|
450
|
-
encoded_block_palette = _encode_block_palette(
|
|
451
|
-
block_palette, default_block, max_block_depth
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
encoded = {}
|
|
455
|
-
for cy in range(min_cy, max_cy):
|
|
456
|
-
if cy in blocks:
|
|
457
|
-
block_lut, block_arr = unique_inverse(blocks[cy])
|
|
458
|
-
encoded[cy] = [
|
|
459
|
-
_encode_block_palette_layer(
|
|
460
|
-
layer_palette, layer_palette_lut, block_lut, block_arr
|
|
461
|
-
)
|
|
462
|
-
for layer_palette, layer_palette_lut in encoded_block_palette
|
|
463
|
-
]
|
|
464
|
-
return encoded
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
def _encode_blocks_v1(
|
|
468
|
-
block_component: BlockComponentData, default_block: Block
|
|
469
|
-
) -> dict[int, bytes]:
|
|
470
|
-
return {
|
|
471
|
-
cy: b"".join((b"\x01", *layers))
|
|
472
|
-
for cy, layers in _encode_palettized_chunk(
|
|
473
|
-
block_component, 0, 16, default_block, 1
|
|
474
|
-
).items()
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def _encode_blocks_v8(
|
|
479
|
-
block_component: BlockComponentData, min_cy: int, max_cy: int, default_block: Block
|
|
480
|
-
) -> dict[int, bytes]:
|
|
481
|
-
return {
|
|
482
|
-
cy: b"".join((b"\x08", struct.pack("b", len(layers)), *layers))
|
|
483
|
-
for cy, layers in _encode_palettized_chunk(
|
|
484
|
-
block_component, min_cy, max_cy, default_block, 2
|
|
485
|
-
).items()
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def _encode_blocks_v9(
|
|
490
|
-
block_component: BlockComponentData, min_cy: int, max_cy: int, default_block: Block
|
|
491
|
-
) -> dict[int, bytes]:
|
|
492
|
-
return {
|
|
493
|
-
cy: b"".join((b"\x09", struct.pack("bb", len(layers), cy), *layers))
|
|
494
|
-
for cy, layers in _encode_palettized_chunk(
|
|
495
|
-
block_component, min_cy, max_cy, default_block, 2
|
|
496
|
-
).items()
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
def _encode_packed_array(arr: numpy.ndarray, min_bit_size: int = 1) -> bytes:
|
|
501
|
-
bits_per_value = max(int(numpy.amax(arr)).bit_length(), min_bit_size)
|
|
502
|
-
if bits_per_value == 7:
|
|
503
|
-
bits_per_value = 8
|
|
504
|
-
elif 9 <= bits_per_value <= 15:
|
|
505
|
-
bits_per_value = 16
|
|
506
|
-
header = bytes([bits_per_value << 1])
|
|
507
|
-
|
|
508
|
-
values_per_word = 32 // bits_per_value # Word = 4 bytes, basis of compacting.
|
|
509
|
-
word_count = -(-4096 // values_per_word) # Ceiling divide is inverted floor divide
|
|
510
|
-
|
|
511
|
-
arr = arr.swapaxes(1, 2).ravel()
|
|
512
|
-
packed_arr = bytes(
|
|
513
|
-
reversed(
|
|
514
|
-
numpy.packbits(
|
|
515
|
-
numpy.pad(
|
|
516
|
-
numpy.pad(
|
|
517
|
-
numpy.unpackbits(
|
|
518
|
-
numpy.ascontiguousarray(arr[::-1], dtype=">i").view(
|
|
519
|
-
dtype="uint8"
|
|
520
|
-
)
|
|
521
|
-
).reshape(4096, -1)[:, -bits_per_value:],
|
|
522
|
-
[(word_count * values_per_word - 4096, 0), (0, 0)],
|
|
523
|
-
"constant",
|
|
524
|
-
).reshape(-1, values_per_word * bits_per_value),
|
|
525
|
-
[(0, 0), (32 - values_per_word * bits_per_value, 0)],
|
|
526
|
-
"constant",
|
|
527
|
-
)
|
|
528
|
-
)
|
|
529
|
-
.view(dtype=">i4")
|
|
530
|
-
.tobytes()
|
|
531
|
-
)
|
|
532
|
-
)
|
|
533
|
-
return header + packed_arr
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def _pack_nbt_list(nbt_list: list[NamedTag]) -> bytes:
|
|
537
|
-
return b"".join(
|
|
538
|
-
[
|
|
539
|
-
nbt.save_to(
|
|
540
|
-
compressed=False,
|
|
541
|
-
little_endian=True,
|
|
542
|
-
string_encoding=utf8_escape_encoding,
|
|
543
|
-
)
|
|
544
|
-
for nbt in nbt_list
|
|
545
|
-
]
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
def _encode_entities(entities: EntityComponentData, str_id: bool) -> list[NamedTag]:
|
|
550
|
-
entities_out = []
|
|
551
|
-
for entity in entities:
|
|
552
|
-
nbt = _encode_entity(entity, str_id)
|
|
553
|
-
if nbt is not None:
|
|
554
|
-
entities_out.append(nbt)
|
|
555
|
-
|
|
556
|
-
return entities_out
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
def _encode_entity(entity: Entity, str_id: bool) -> NamedTag | None:
|
|
560
|
-
named_tag = entity.nbt
|
|
561
|
-
tag = named_tag.tag
|
|
562
|
-
if not isinstance(tag, CompoundTag):
|
|
563
|
-
return None
|
|
564
|
-
|
|
565
|
-
if entity.namespace == "numerical" and entity.base_name.isnumeric():
|
|
566
|
-
tag["id"] = IntTag(int(entity.base_name))
|
|
567
|
-
elif str_id:
|
|
568
|
-
tag["identifier"] = StringTag(entity.namespaced_name)
|
|
569
|
-
else:
|
|
570
|
-
return None
|
|
571
|
-
|
|
572
|
-
tag["Pos"] = ListTag(
|
|
573
|
-
[
|
|
574
|
-
FloatTag(float(entity.x)),
|
|
575
|
-
FloatTag(float(entity.y)),
|
|
576
|
-
FloatTag(float(entity.z)),
|
|
577
|
-
]
|
|
578
|
-
)
|
|
579
|
-
return named_tag
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
def _encode_block_entities(block_entities: BlockEntityComponentData) -> list[NamedTag]:
|
|
583
|
-
entities_out = []
|
|
584
|
-
for location, block_entity in block_entities.items():
|
|
585
|
-
nbt = _encode_block_entity(location, block_entity)
|
|
586
|
-
if nbt is not None:
|
|
587
|
-
entities_out.append(nbt)
|
|
588
|
-
return entities_out
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
def _encode_block_entity(
|
|
592
|
-
location: tuple[int, int, int], block_entity: BlockEntity
|
|
593
|
-
) -> NamedTag | None:
|
|
594
|
-
named_tag = block_entity.nbt
|
|
595
|
-
tag = named_tag.tag
|
|
596
|
-
if not isinstance(tag, CompoundTag):
|
|
597
|
-
return None
|
|
598
|
-
tag["id"] = StringTag(block_entity.base_name)
|
|
599
|
-
tag["x"] = IntTag(location[0])
|
|
600
|
-
tag["y"] = IntTag(location[1])
|
|
601
|
-
tag["z"] = IntTag(location[2])
|
|
602
|
-
return named_tag
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from amulet.selection import SelectionBox, SelectionGroup
|
|
2
|
-
|
|
3
|
-
LOCAL_PLAYER = "~local_player"
|
|
4
|
-
OVERWORLD = "minecraft:overworld"
|
|
5
|
-
THE_NETHER = "minecraft:the_nether"
|
|
6
|
-
THE_END = "minecraft:the_end"
|
|
7
|
-
DefaultSelection = SelectionGroup(
|
|
8
|
-
SelectionBox((-30_000_000, 0, -30_000_000), (30_000_000, 256, 30_000_000))
|
|
9
|
-
)
|