amulet-core 1.9.19__py3-none-any.whl → 1.9.20__py3-none-any.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__.py +27 -27
- amulet/__pyinstaller/__init__.py +2 -2
- amulet/__pyinstaller/hook-amulet.py +4 -4
- amulet/_version.py +21 -21
- amulet/api/__init__.py +2 -2
- amulet/api/abstract_base_entity.py +128 -128
- amulet/api/block.py +630 -630
- amulet/api/block_entity.py +71 -71
- amulet/api/cache.py +107 -107
- amulet/api/chunk/__init__.py +6 -6
- amulet/api/chunk/biomes.py +207 -207
- amulet/api/chunk/block_entity_dict.py +175 -175
- amulet/api/chunk/blocks.py +46 -46
- amulet/api/chunk/chunk.py +389 -389
- amulet/api/chunk/entity_list.py +75 -75
- amulet/api/chunk/status.py +167 -167
- amulet/api/data_types/__init__.py +4 -4
- amulet/api/data_types/generic_types.py +4 -4
- amulet/api/data_types/operation_types.py +16 -16
- amulet/api/data_types/world_types.py +49 -49
- amulet/api/data_types/wrapper_types.py +71 -71
- amulet/api/entity.py +74 -74
- amulet/api/errors.py +119 -119
- amulet/api/history/__init__.py +36 -36
- amulet/api/history/base/__init__.py +3 -3
- amulet/api/history/base/base_history.py +26 -26
- amulet/api/history/base/history_manager.py +63 -63
- amulet/api/history/base/revision_manager.py +73 -73
- amulet/api/history/changeable.py +15 -15
- amulet/api/history/data_types.py +7 -7
- amulet/api/history/history_manager/__init__.py +3 -3
- amulet/api/history/history_manager/container.py +102 -102
- amulet/api/history/history_manager/database.py +279 -279
- amulet/api/history/history_manager/meta.py +93 -93
- amulet/api/history/history_manager/object.py +116 -116
- amulet/api/history/revision_manager/__init__.py +2 -2
- amulet/api/history/revision_manager/disk.py +33 -33
- amulet/api/history/revision_manager/ram.py +12 -12
- amulet/api/item.py +75 -75
- amulet/api/level/__init__.py +4 -4
- amulet/api/level/base_level/__init__.py +1 -1
- amulet/api/level/base_level/base_level.py +1035 -1026
- amulet/api/level/base_level/chunk_manager.py +227 -227
- amulet/api/level/base_level/clone.py +389 -389
- amulet/api/level/base_level/player_manager.py +101 -101
- amulet/api/level/immutable_structure/__init__.py +1 -1
- amulet/api/level/immutable_structure/immutable_structure.py +94 -94
- amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
- amulet/api/level/structure.py +22 -22
- amulet/api/level/world.py +19 -19
- amulet/api/partial_3d_array/__init__.py +2 -2
- amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
- amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
- amulet/api/partial_3d_array/data_types.py +15 -15
- amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
- amulet/api/partial_3d_array/util.py +152 -152
- amulet/api/player.py +65 -65
- amulet/api/registry/__init__.py +2 -2
- amulet/api/registry/base_registry.py +34 -34
- amulet/api/registry/biome_manager.py +153 -153
- amulet/api/registry/block_manager.py +156 -156
- amulet/api/selection/__init__.py +2 -2
- amulet/api/selection/abstract_selection.py +315 -315
- amulet/api/selection/box.py +805 -805
- amulet/api/selection/group.py +488 -488
- amulet/api/structure.py +37 -37
- amulet/api/wrapper/__init__.py +8 -8
- amulet/api/wrapper/chunk/interface.py +441 -441
- amulet/api/wrapper/chunk/translator.py +567 -567
- amulet/api/wrapper/format_wrapper.py +772 -772
- amulet/api/wrapper/structure_format_wrapper.py +116 -116
- amulet/api/wrapper/world_format_wrapper.py +63 -63
- amulet/level/__init__.py +1 -1
- amulet/level/formats/anvil_forge_world.py +40 -40
- amulet/level/formats/anvil_world/__init__.py +3 -3
- amulet/level/formats/anvil_world/_sector_manager.py +291 -384
- amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
- amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
- amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
- amulet/level/formats/anvil_world/dimension.py +177 -177
- amulet/level/formats/anvil_world/format.py +769 -769
- amulet/level/formats/anvil_world/region.py +384 -384
- amulet/level/formats/construction/__init__.py +3 -3
- amulet/level/formats/construction/format_wrapper.py +515 -515
- amulet/level/formats/construction/interface.py +134 -134
- amulet/level/formats/construction/section.py +60 -60
- amulet/level/formats/construction/util.py +165 -165
- amulet/level/formats/leveldb_world/__init__.py +3 -3
- amulet/level/formats/leveldb_world/chunk.py +33 -33
- amulet/level/formats/leveldb_world/dimension.py +385 -419
- amulet/level/formats/leveldb_world/format.py +659 -641
- amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
- amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
- amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
- amulet/level/formats/mcstructure/__init__.py +3 -3
- amulet/level/formats/mcstructure/chunk.py +50 -50
- amulet/level/formats/mcstructure/format_wrapper.py +408 -408
- amulet/level/formats/mcstructure/interface.py +175 -175
- amulet/level/formats/schematic/__init__.py +3 -3
- amulet/level/formats/schematic/chunk.py +55 -55
- amulet/level/formats/schematic/data_types.py +4 -4
- amulet/level/formats/schematic/format_wrapper.py +373 -373
- amulet/level/formats/schematic/interface.py +142 -142
- amulet/level/formats/sponge_schem/__init__.py +4 -4
- amulet/level/formats/sponge_schem/chunk.py +62 -62
- amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
- amulet/level/formats/sponge_schem/interface.py +118 -118
- amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
- amulet/level/formats/sponge_schem/varint/varint.py +87 -87
- amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
- amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
- amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
- amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
- amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
- amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
- amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
- amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
- amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
- amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
- amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
- amulet/level/load.py +59 -59
- amulet/level/loader.py +95 -95
- amulet/level/translators/chunk/bedrock/__init__.py +267 -267
- amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
- amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
- amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
- amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
- amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
- amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
- amulet/libs/leveldb/__init__.py +7 -7
- amulet/operations/__init__.py +5 -5
- amulet/operations/clone.py +18 -18
- amulet/operations/delete_chunk.py +32 -32
- amulet/operations/fill.py +30 -30
- amulet/operations/paste.py +65 -65
- amulet/operations/replace.py +58 -58
- amulet/utils/__init__.py +14 -14
- amulet/utils/format_utils.py +41 -41
- amulet/utils/generator.py +15 -15
- amulet/utils/matrix.py +243 -243
- amulet/utils/numpy_helpers.py +46 -46
- amulet/utils/world_utils.py +349 -349
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
- amulet_core-1.9.20.dist-info/RECORD +208 -0
- amulet_core-1.9.19.dist-info/RECORD +0 -208
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
|
@@ -1,408 +1,408 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Optional, Tuple, Iterable, TYPE_CHECKING, BinaryIO, Dict, List, Union
|
|
3
|
-
import numpy
|
|
4
|
-
import copy
|
|
5
|
-
|
|
6
|
-
from amulet_nbt import (
|
|
7
|
-
IntTag,
|
|
8
|
-
StringTag,
|
|
9
|
-
ListTag,
|
|
10
|
-
CompoundTag,
|
|
11
|
-
load as load_nbt,
|
|
12
|
-
utf8_escape_decoder,
|
|
13
|
-
utf8_escape_encoder,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
from amulet.api.data_types import (
|
|
17
|
-
VersionNumberAny,
|
|
18
|
-
VersionNumberTuple,
|
|
19
|
-
ChunkCoordinates,
|
|
20
|
-
AnyNDArray,
|
|
21
|
-
Dimension,
|
|
22
|
-
PlatformType,
|
|
23
|
-
)
|
|
24
|
-
from amulet.api.wrapper import StructureFormatWrapper
|
|
25
|
-
from amulet.api.chunk import Chunk
|
|
26
|
-
from amulet.api.selection import SelectionGroup, SelectionBox
|
|
27
|
-
from amulet.api.errors import ChunkDoesNotExist, ObjectWriteError
|
|
28
|
-
from amulet.utils.numpy_helpers import brute_sort_objects_no_hash
|
|
29
|
-
|
|
30
|
-
from .chunk import MCStructureChunk
|
|
31
|
-
from .interface import MCStructureInterface
|
|
32
|
-
|
|
33
|
-
if TYPE_CHECKING:
|
|
34
|
-
from amulet.api.wrapper import Translator, Interface
|
|
35
|
-
|
|
36
|
-
mcstructure_interface = MCStructureInterface()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class MCStructureFormatWrapper(StructureFormatWrapper[VersionNumberTuple]):
|
|
40
|
-
"""
|
|
41
|
-
This FormatWrapper class exists to interface with the Bedrock mcstructure structure block format.
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
def __init__(self, path: str):
|
|
45
|
-
"""
|
|
46
|
-
Construct a new instance of :class:`MCStructureFormatWrapper`.
|
|
47
|
-
|
|
48
|
-
This should not be used directly. You should instead use :func:`amulet.load_format`.
|
|
49
|
-
|
|
50
|
-
:param path: The file path to the serialised data.
|
|
51
|
-
"""
|
|
52
|
-
super().__init__(path)
|
|
53
|
-
self._chunks: Dict[
|
|
54
|
-
ChunkCoordinates,
|
|
55
|
-
Tuple[
|
|
56
|
-
SelectionBox,
|
|
57
|
-
numpy.ndarray,
|
|
58
|
-
AnyNDArray,
|
|
59
|
-
List[CompoundTag],
|
|
60
|
-
List[CompoundTag],
|
|
61
|
-
],
|
|
62
|
-
] = {}
|
|
63
|
-
|
|
64
|
-
def _create(
|
|
65
|
-
self,
|
|
66
|
-
overwrite: bool,
|
|
67
|
-
bounds: Union[
|
|
68
|
-
SelectionGroup, Dict[Dimension, Optional[SelectionGroup]], None
|
|
69
|
-
] = None,
|
|
70
|
-
**kwargs,
|
|
71
|
-
):
|
|
72
|
-
if not overwrite and os.path.isfile(self.path):
|
|
73
|
-
raise ObjectWriteError(f"There is already a file at {self.path}")
|
|
74
|
-
translator_version = self.translation_manager.get_version(
|
|
75
|
-
"bedrock", (999, 999, 999)
|
|
76
|
-
)
|
|
77
|
-
self._platform = translator_version.platform
|
|
78
|
-
self._version = translator_version.version_number
|
|
79
|
-
self._chunks = {}
|
|
80
|
-
self._set_selection(bounds)
|
|
81
|
-
self._is_open = True
|
|
82
|
-
self._has_lock = True
|
|
83
|
-
|
|
84
|
-
def open_from(self, f: BinaryIO):
|
|
85
|
-
mcstructure = load_nbt(
|
|
86
|
-
f, little_endian=True, string_decoder=utf8_escape_decoder
|
|
87
|
-
).compound
|
|
88
|
-
if mcstructure.get_int("format_version").py_int == 1:
|
|
89
|
-
min_point = numpy.array(
|
|
90
|
-
tuple(c.py_int for c in mcstructure.get_list("structure_world_origin"))
|
|
91
|
-
)
|
|
92
|
-
max_point = min_point + tuple(
|
|
93
|
-
c.py_int for c in mcstructure.get_list("size")
|
|
94
|
-
)
|
|
95
|
-
selection = SelectionBox(min_point, max_point)
|
|
96
|
-
self._bounds[self.dimensions[0]] = SelectionGroup(selection)
|
|
97
|
-
translator_version = self.translation_manager.get_version(
|
|
98
|
-
"bedrock", (999, 999, 999)
|
|
99
|
-
)
|
|
100
|
-
self._platform = translator_version.platform
|
|
101
|
-
self._version = translator_version.version_number
|
|
102
|
-
structure = mcstructure.get_compound("structure")
|
|
103
|
-
indices = structure.get_list("block_indices")
|
|
104
|
-
blocks_array: numpy.ndarray = numpy.array(
|
|
105
|
-
[[b.py_int for b in layer] for layer in indices],
|
|
106
|
-
dtype=numpy.int32,
|
|
107
|
-
).reshape((len(indices), *selection.shape))
|
|
108
|
-
|
|
109
|
-
palette_tag = structure.get_compound("palette")
|
|
110
|
-
palette_key = next(iter(palette_tag))
|
|
111
|
-
sub_palette_tag = palette_tag.get_compound(palette_key)
|
|
112
|
-
block_palette = list(sub_palette_tag.get_list("block_palette"))
|
|
113
|
-
|
|
114
|
-
if -1 in blocks_array[0]:
|
|
115
|
-
blocks_array[0][blocks_array[0] == -1] = len(block_palette)
|
|
116
|
-
block_palette.append(
|
|
117
|
-
CompoundTag(
|
|
118
|
-
{
|
|
119
|
-
"name": StringTag("minecraft:structure_void"),
|
|
120
|
-
"states": CompoundTag(),
|
|
121
|
-
"version": IntTag(17694723), # 1.13.0
|
|
122
|
-
}
|
|
123
|
-
)
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
for cx, cz in selection.chunk_locations():
|
|
127
|
-
chunk_box = SelectionBox.create_chunk_box(cx, cz).intersection(
|
|
128
|
-
selection
|
|
129
|
-
)
|
|
130
|
-
array_slice = (slice(None),) + chunk_box.create_moved_box(
|
|
131
|
-
selection.min, subtract=True
|
|
132
|
-
).slice
|
|
133
|
-
chunk_blocks_: numpy.ndarray = blocks_array[array_slice]
|
|
134
|
-
chunk_palette_indexes, chunk_blocks = numpy.unique(
|
|
135
|
-
chunk_blocks_.reshape((chunk_blocks_.shape[0], -1)).T,
|
|
136
|
-
return_inverse=True,
|
|
137
|
-
axis=0,
|
|
138
|
-
)
|
|
139
|
-
chunk_blocks = chunk_blocks.reshape(chunk_blocks_.shape[1:])
|
|
140
|
-
|
|
141
|
-
chunk_palette = numpy.empty(len(chunk_palette_indexes), dtype=object)
|
|
142
|
-
for palette_index, indexes in enumerate(chunk_palette_indexes):
|
|
143
|
-
chunk_palette[palette_index] = tuple(
|
|
144
|
-
block_palette[index] for index in indexes if index >= 0
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
self._chunks[(cx, cz)] = (
|
|
148
|
-
chunk_box,
|
|
149
|
-
chunk_blocks,
|
|
150
|
-
chunk_palette,
|
|
151
|
-
[],
|
|
152
|
-
[],
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
block_entities = {
|
|
156
|
-
int(key): val["block_entity_data"]
|
|
157
|
-
for key, val in sub_palette_tag.get_compound(
|
|
158
|
-
"block_position_data"
|
|
159
|
-
).items()
|
|
160
|
-
if "block_entity_data" in val
|
|
161
|
-
}
|
|
162
|
-
for location, block_entity in block_entities.items():
|
|
163
|
-
if all(key in block_entity for key in "xyz"):
|
|
164
|
-
x, y, z = (
|
|
165
|
-
block_entity["x"].py_int,
|
|
166
|
-
block_entity["y"].py_int,
|
|
167
|
-
block_entity["z"].py_int,
|
|
168
|
-
)
|
|
169
|
-
cx, cz = x >> 4, z >> 4
|
|
170
|
-
if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(cx, cz)][
|
|
171
|
-
0
|
|
172
|
-
]:
|
|
173
|
-
self._chunks[(cx, cz)][3].append(block_entity)
|
|
174
|
-
|
|
175
|
-
for entity in structure.get_list("entities"):
|
|
176
|
-
if "Pos" in entity:
|
|
177
|
-
pos = entity.get_list("Pos")
|
|
178
|
-
x = pos[0].py_float
|
|
179
|
-
y = pos[1].py_float
|
|
180
|
-
z = pos[2].py_float
|
|
181
|
-
cx, cz = numpy.floor([x, z]).astype(int) >> 4
|
|
182
|
-
if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(cx, cz)][
|
|
183
|
-
0
|
|
184
|
-
]:
|
|
185
|
-
self._chunks[(cx, cz)][4].append(entity)
|
|
186
|
-
|
|
187
|
-
else:
|
|
188
|
-
raise Exception(
|
|
189
|
-
f"mcstructure file with format_version=={mcstructure.get_int('format_version').py_int} cannot be read"
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
@staticmethod
|
|
193
|
-
def is_valid(path: str) -> bool:
|
|
194
|
-
return os.path.isfile(path) and path.endswith(".mcstructure")
|
|
195
|
-
|
|
196
|
-
@property
|
|
197
|
-
def valid_formats(self) -> Dict[PlatformType, Tuple[bool, bool]]:
|
|
198
|
-
return {"bedrock": (False, True)}
|
|
199
|
-
|
|
200
|
-
@property
|
|
201
|
-
def extensions(self) -> Tuple[str, ...]:
|
|
202
|
-
return (".mcstructure",)
|
|
203
|
-
|
|
204
|
-
def _get_interface(self, raw_chunk_data=None) -> "MCStructureInterface":
|
|
205
|
-
return mcstructure_interface
|
|
206
|
-
|
|
207
|
-
def _get_interface_and_translator(
|
|
208
|
-
self, raw_chunk_data=None
|
|
209
|
-
) -> Tuple["Interface", "Translator", VersionNumberAny]:
|
|
210
|
-
interface = self._get_interface(raw_chunk_data)
|
|
211
|
-
translator, version_identifier = interface.get_translator(
|
|
212
|
-
self.max_world_version, raw_chunk_data, self.translation_manager
|
|
213
|
-
)
|
|
214
|
-
return interface, translator, version_identifier
|
|
215
|
-
|
|
216
|
-
def save_to(self, f: BinaryIO):
|
|
217
|
-
selection = self._bounds[self.dimensions[0]].selection_boxes[0]
|
|
218
|
-
mcstructure = CompoundTag(
|
|
219
|
-
{
|
|
220
|
-
"format_version": IntTag(1),
|
|
221
|
-
"structure_world_origin": ListTag(
|
|
222
|
-
[
|
|
223
|
-
IntTag(selection.min_x),
|
|
224
|
-
IntTag(selection.min_y),
|
|
225
|
-
IntTag(selection.min_z),
|
|
226
|
-
]
|
|
227
|
-
),
|
|
228
|
-
"size": ListTag(
|
|
229
|
-
[
|
|
230
|
-
IntTag(selection.size_x),
|
|
231
|
-
IntTag(selection.size_y),
|
|
232
|
-
IntTag(selection.size_z),
|
|
233
|
-
]
|
|
234
|
-
),
|
|
235
|
-
}
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
entities = []
|
|
239
|
-
block_entities = []
|
|
240
|
-
blocks = numpy.zeros(selection.shape, dtype=numpy.uint32)
|
|
241
|
-
palette: List[AnyNDArray] = []
|
|
242
|
-
if self.version < (1, 13, 0):
|
|
243
|
-
raise Exception(
|
|
244
|
-
"Writing to mcstructre files in pre-1.13 format is not currently supported."
|
|
245
|
-
)
|
|
246
|
-
else:
|
|
247
|
-
arr = numpy.empty(1, dtype=object)
|
|
248
|
-
arr[0] = [
|
|
249
|
-
CompoundTag(
|
|
250
|
-
{
|
|
251
|
-
"name": StringTag("minecraft:air"),
|
|
252
|
-
"states": CompoundTag(),
|
|
253
|
-
"version": IntTag(17694723),
|
|
254
|
-
}
|
|
255
|
-
)
|
|
256
|
-
]
|
|
257
|
-
palette.append(arr)
|
|
258
|
-
|
|
259
|
-
palette_len = 1
|
|
260
|
-
|
|
261
|
-
for (
|
|
262
|
-
selection_,
|
|
263
|
-
blocks_,
|
|
264
|
-
palette_,
|
|
265
|
-
block_entities_,
|
|
266
|
-
entities_,
|
|
267
|
-
) in self._chunks.values():
|
|
268
|
-
if selection_.intersects(selection):
|
|
269
|
-
box = selection_.create_moved_box(selection.min, subtract=True)
|
|
270
|
-
blocks[box.slice] = blocks_ + palette_len
|
|
271
|
-
palette.append(palette_)
|
|
272
|
-
palette_len += len(palette_)
|
|
273
|
-
block_entities += block_entities_
|
|
274
|
-
entities += entities_
|
|
275
|
-
|
|
276
|
-
compact_palette, lut = brute_sort_objects_no_hash(numpy.concatenate(palette))
|
|
277
|
-
blocks = lut[blocks].ravel()
|
|
278
|
-
block_palette = []
|
|
279
|
-
block_palette_indices = []
|
|
280
|
-
for block_list in compact_palette:
|
|
281
|
-
indexed_block = [-1] * 2
|
|
282
|
-
for block_layer, block in enumerate(block_list):
|
|
283
|
-
if block_layer >= 2:
|
|
284
|
-
break
|
|
285
|
-
if block["name"] != StringTag("minecraft:structure_void"):
|
|
286
|
-
if block in block_palette:
|
|
287
|
-
indexed_block[block_layer] = block_palette.index(block)
|
|
288
|
-
else:
|
|
289
|
-
indexed_block[block_layer] = len(block_palette)
|
|
290
|
-
block_palette.append(block)
|
|
291
|
-
block_palette_indices.append(indexed_block)
|
|
292
|
-
|
|
293
|
-
block_indices = numpy.array(block_palette_indices, dtype=numpy.int32)[blocks].T
|
|
294
|
-
|
|
295
|
-
mcstructure["structure"] = CompoundTag(
|
|
296
|
-
{
|
|
297
|
-
"block_indices": ListTag(
|
|
298
|
-
[ # a list of tag ints that index into the block_palette. One list per block layer
|
|
299
|
-
ListTag([IntTag(block) for block in layer])
|
|
300
|
-
for layer in block_indices
|
|
301
|
-
]
|
|
302
|
-
),
|
|
303
|
-
"entities": ListTag(entities),
|
|
304
|
-
"palette": CompoundTag(
|
|
305
|
-
{
|
|
306
|
-
"default": CompoundTag(
|
|
307
|
-
{
|
|
308
|
-
"block_palette": ListTag(block_palette),
|
|
309
|
-
"block_position_data": CompoundTag(
|
|
310
|
-
{
|
|
311
|
-
str(
|
|
312
|
-
(
|
|
313
|
-
(
|
|
314
|
-
block_entity["x"].py_int
|
|
315
|
-
- selection.min_x
|
|
316
|
-
)
|
|
317
|
-
* selection.size_y
|
|
318
|
-
+ (
|
|
319
|
-
block_entity["y"].py_int
|
|
320
|
-
- selection.min_y
|
|
321
|
-
)
|
|
322
|
-
)
|
|
323
|
-
* selection.size_z
|
|
324
|
-
+ block_entity["z"].py_int
|
|
325
|
-
- selection.min_z
|
|
326
|
-
): CompoundTag(
|
|
327
|
-
{"block_entity_data": block_entity}
|
|
328
|
-
)
|
|
329
|
-
for block_entity in block_entities
|
|
330
|
-
}
|
|
331
|
-
),
|
|
332
|
-
}
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
),
|
|
336
|
-
}
|
|
337
|
-
)
|
|
338
|
-
mcstructure.save_to(
|
|
339
|
-
f, compressed=False, little_endian=True, string_encoder=utf8_escape_encoder
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
def _close(self):
|
|
343
|
-
"""Close the disk database"""
|
|
344
|
-
self._chunks.clear()
|
|
345
|
-
|
|
346
|
-
def unload(self):
|
|
347
|
-
pass
|
|
348
|
-
|
|
349
|
-
def all_chunk_coords(
|
|
350
|
-
self, dimension: Optional[Dimension] = None
|
|
351
|
-
) -> Iterable[ChunkCoordinates]:
|
|
352
|
-
yield from self._chunks.keys()
|
|
353
|
-
|
|
354
|
-
def has_chunk(self, cx: int, cz: int, dimension: Dimension) -> bool:
|
|
355
|
-
return (cx, cz) in self._chunks
|
|
356
|
-
|
|
357
|
-
def _encode(
|
|
358
|
-
self,
|
|
359
|
-
interface: MCStructureInterface,
|
|
360
|
-
chunk: Chunk,
|
|
361
|
-
dimension: Dimension,
|
|
362
|
-
chunk_palette: AnyNDArray,
|
|
363
|
-
):
|
|
364
|
-
return interface.encode(
|
|
365
|
-
chunk,
|
|
366
|
-
chunk_palette,
|
|
367
|
-
self.max_world_version,
|
|
368
|
-
SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
|
|
369
|
-
self._bounds[dimension].to_box()
|
|
370
|
-
),
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
def _delete_chunk(self, cx: int, cz: int, dimension: Optional[Dimension] = None):
|
|
374
|
-
if (cx, cz) in self._chunks:
|
|
375
|
-
del self._chunks[(cx, cz)]
|
|
376
|
-
|
|
377
|
-
def _put_raw_chunk_data(
|
|
378
|
-
self,
|
|
379
|
-
cx: int,
|
|
380
|
-
cz: int,
|
|
381
|
-
section: MCStructureChunk,
|
|
382
|
-
dimension: Optional[Dimension] = None,
|
|
383
|
-
):
|
|
384
|
-
self._chunks[(cx, cz)] = copy.deepcopy(
|
|
385
|
-
(
|
|
386
|
-
section.selection,
|
|
387
|
-
section.blocks,
|
|
388
|
-
section.palette,
|
|
389
|
-
section.block_entities,
|
|
390
|
-
section.entities,
|
|
391
|
-
)
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
def _get_raw_chunk_data(
|
|
395
|
-
self, cx: int, cz: int, dimension: Optional[Dimension] = None
|
|
396
|
-
) -> MCStructureChunk:
|
|
397
|
-
"""
|
|
398
|
-
Return the raw data as loaded from disk.
|
|
399
|
-
|
|
400
|
-
:param cx: The x coordinate of the chunk.
|
|
401
|
-
:param cz: The z coordinate of the chunk.
|
|
402
|
-
:param dimension: The dimension to load the data from.
|
|
403
|
-
:return: The raw chunk data.
|
|
404
|
-
"""
|
|
405
|
-
if (cx, cz) in self._chunks:
|
|
406
|
-
return MCStructureChunk(*copy.deepcopy(self._chunks[(cx, cz)]))
|
|
407
|
-
else:
|
|
408
|
-
raise ChunkDoesNotExist
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional, Tuple, Iterable, TYPE_CHECKING, BinaryIO, Dict, List, Union
|
|
3
|
+
import numpy
|
|
4
|
+
import copy
|
|
5
|
+
|
|
6
|
+
from amulet_nbt import (
|
|
7
|
+
IntTag,
|
|
8
|
+
StringTag,
|
|
9
|
+
ListTag,
|
|
10
|
+
CompoundTag,
|
|
11
|
+
load as load_nbt,
|
|
12
|
+
utf8_escape_decoder,
|
|
13
|
+
utf8_escape_encoder,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from amulet.api.data_types import (
|
|
17
|
+
VersionNumberAny,
|
|
18
|
+
VersionNumberTuple,
|
|
19
|
+
ChunkCoordinates,
|
|
20
|
+
AnyNDArray,
|
|
21
|
+
Dimension,
|
|
22
|
+
PlatformType,
|
|
23
|
+
)
|
|
24
|
+
from amulet.api.wrapper import StructureFormatWrapper
|
|
25
|
+
from amulet.api.chunk import Chunk
|
|
26
|
+
from amulet.api.selection import SelectionGroup, SelectionBox
|
|
27
|
+
from amulet.api.errors import ChunkDoesNotExist, ObjectWriteError
|
|
28
|
+
from amulet.utils.numpy_helpers import brute_sort_objects_no_hash
|
|
29
|
+
|
|
30
|
+
from .chunk import MCStructureChunk
|
|
31
|
+
from .interface import MCStructureInterface
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from amulet.api.wrapper import Translator, Interface
|
|
35
|
+
|
|
36
|
+
mcstructure_interface = MCStructureInterface()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MCStructureFormatWrapper(StructureFormatWrapper[VersionNumberTuple]):
|
|
40
|
+
"""
|
|
41
|
+
This FormatWrapper class exists to interface with the Bedrock mcstructure structure block format.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, path: str):
|
|
45
|
+
"""
|
|
46
|
+
Construct a new instance of :class:`MCStructureFormatWrapper`.
|
|
47
|
+
|
|
48
|
+
This should not be used directly. You should instead use :func:`amulet.load_format`.
|
|
49
|
+
|
|
50
|
+
:param path: The file path to the serialised data.
|
|
51
|
+
"""
|
|
52
|
+
super().__init__(path)
|
|
53
|
+
self._chunks: Dict[
|
|
54
|
+
ChunkCoordinates,
|
|
55
|
+
Tuple[
|
|
56
|
+
SelectionBox,
|
|
57
|
+
numpy.ndarray,
|
|
58
|
+
AnyNDArray,
|
|
59
|
+
List[CompoundTag],
|
|
60
|
+
List[CompoundTag],
|
|
61
|
+
],
|
|
62
|
+
] = {}
|
|
63
|
+
|
|
64
|
+
def _create(
|
|
65
|
+
self,
|
|
66
|
+
overwrite: bool,
|
|
67
|
+
bounds: Union[
|
|
68
|
+
SelectionGroup, Dict[Dimension, Optional[SelectionGroup]], None
|
|
69
|
+
] = None,
|
|
70
|
+
**kwargs,
|
|
71
|
+
):
|
|
72
|
+
if not overwrite and os.path.isfile(self.path):
|
|
73
|
+
raise ObjectWriteError(f"There is already a file at {self.path}")
|
|
74
|
+
translator_version = self.translation_manager.get_version(
|
|
75
|
+
"bedrock", (999, 999, 999)
|
|
76
|
+
)
|
|
77
|
+
self._platform = translator_version.platform
|
|
78
|
+
self._version = translator_version.version_number
|
|
79
|
+
self._chunks = {}
|
|
80
|
+
self._set_selection(bounds)
|
|
81
|
+
self._is_open = True
|
|
82
|
+
self._has_lock = True
|
|
83
|
+
|
|
84
|
+
def open_from(self, f: BinaryIO):
|
|
85
|
+
mcstructure = load_nbt(
|
|
86
|
+
f, little_endian=True, string_decoder=utf8_escape_decoder
|
|
87
|
+
).compound
|
|
88
|
+
if mcstructure.get_int("format_version").py_int == 1:
|
|
89
|
+
min_point = numpy.array(
|
|
90
|
+
tuple(c.py_int for c in mcstructure.get_list("structure_world_origin"))
|
|
91
|
+
)
|
|
92
|
+
max_point = min_point + tuple(
|
|
93
|
+
c.py_int for c in mcstructure.get_list("size")
|
|
94
|
+
)
|
|
95
|
+
selection = SelectionBox(min_point, max_point)
|
|
96
|
+
self._bounds[self.dimensions[0]] = SelectionGroup(selection)
|
|
97
|
+
translator_version = self.translation_manager.get_version(
|
|
98
|
+
"bedrock", (999, 999, 999)
|
|
99
|
+
)
|
|
100
|
+
self._platform = translator_version.platform
|
|
101
|
+
self._version = translator_version.version_number
|
|
102
|
+
structure = mcstructure.get_compound("structure")
|
|
103
|
+
indices = structure.get_list("block_indices")
|
|
104
|
+
blocks_array: numpy.ndarray = numpy.array(
|
|
105
|
+
[[b.py_int for b in layer] for layer in indices],
|
|
106
|
+
dtype=numpy.int32,
|
|
107
|
+
).reshape((len(indices), *selection.shape))
|
|
108
|
+
|
|
109
|
+
palette_tag = structure.get_compound("palette")
|
|
110
|
+
palette_key = next(iter(palette_tag))
|
|
111
|
+
sub_palette_tag = palette_tag.get_compound(palette_key)
|
|
112
|
+
block_palette = list(sub_palette_tag.get_list("block_palette"))
|
|
113
|
+
|
|
114
|
+
if -1 in blocks_array[0]:
|
|
115
|
+
blocks_array[0][blocks_array[0] == -1] = len(block_palette)
|
|
116
|
+
block_palette.append(
|
|
117
|
+
CompoundTag(
|
|
118
|
+
{
|
|
119
|
+
"name": StringTag("minecraft:structure_void"),
|
|
120
|
+
"states": CompoundTag(),
|
|
121
|
+
"version": IntTag(17694723), # 1.13.0
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
for cx, cz in selection.chunk_locations():
|
|
127
|
+
chunk_box = SelectionBox.create_chunk_box(cx, cz).intersection(
|
|
128
|
+
selection
|
|
129
|
+
)
|
|
130
|
+
array_slice = (slice(None),) + chunk_box.create_moved_box(
|
|
131
|
+
selection.min, subtract=True
|
|
132
|
+
).slice
|
|
133
|
+
chunk_blocks_: numpy.ndarray = blocks_array[array_slice]
|
|
134
|
+
chunk_palette_indexes, chunk_blocks = numpy.unique(
|
|
135
|
+
chunk_blocks_.reshape((chunk_blocks_.shape[0], -1)).T,
|
|
136
|
+
return_inverse=True,
|
|
137
|
+
axis=0,
|
|
138
|
+
)
|
|
139
|
+
chunk_blocks = chunk_blocks.reshape(chunk_blocks_.shape[1:])
|
|
140
|
+
|
|
141
|
+
chunk_palette = numpy.empty(len(chunk_palette_indexes), dtype=object)
|
|
142
|
+
for palette_index, indexes in enumerate(chunk_palette_indexes):
|
|
143
|
+
chunk_palette[palette_index] = tuple(
|
|
144
|
+
block_palette[index] for index in indexes if index >= 0
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
self._chunks[(cx, cz)] = (
|
|
148
|
+
chunk_box,
|
|
149
|
+
chunk_blocks,
|
|
150
|
+
chunk_palette,
|
|
151
|
+
[],
|
|
152
|
+
[],
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
block_entities = {
|
|
156
|
+
int(key): val["block_entity_data"]
|
|
157
|
+
for key, val in sub_palette_tag.get_compound(
|
|
158
|
+
"block_position_data"
|
|
159
|
+
).items()
|
|
160
|
+
if "block_entity_data" in val
|
|
161
|
+
}
|
|
162
|
+
for location, block_entity in block_entities.items():
|
|
163
|
+
if all(key in block_entity for key in "xyz"):
|
|
164
|
+
x, y, z = (
|
|
165
|
+
block_entity["x"].py_int,
|
|
166
|
+
block_entity["y"].py_int,
|
|
167
|
+
block_entity["z"].py_int,
|
|
168
|
+
)
|
|
169
|
+
cx, cz = x >> 4, z >> 4
|
|
170
|
+
if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(cx, cz)][
|
|
171
|
+
0
|
|
172
|
+
]:
|
|
173
|
+
self._chunks[(cx, cz)][3].append(block_entity)
|
|
174
|
+
|
|
175
|
+
for entity in structure.get_list("entities"):
|
|
176
|
+
if "Pos" in entity:
|
|
177
|
+
pos = entity.get_list("Pos")
|
|
178
|
+
x = pos[0].py_float
|
|
179
|
+
y = pos[1].py_float
|
|
180
|
+
z = pos[2].py_float
|
|
181
|
+
cx, cz = numpy.floor([x, z]).astype(int) >> 4
|
|
182
|
+
if (cx, cz) in self._chunks and (x, y, z) in self._chunks[(cx, cz)][
|
|
183
|
+
0
|
|
184
|
+
]:
|
|
185
|
+
self._chunks[(cx, cz)][4].append(entity)
|
|
186
|
+
|
|
187
|
+
else:
|
|
188
|
+
raise Exception(
|
|
189
|
+
f"mcstructure file with format_version=={mcstructure.get_int('format_version').py_int} cannot be read"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
@staticmethod
|
|
193
|
+
def is_valid(path: str) -> bool:
|
|
194
|
+
return os.path.isfile(path) and path.endswith(".mcstructure")
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def valid_formats(self) -> Dict[PlatformType, Tuple[bool, bool]]:
|
|
198
|
+
return {"bedrock": (False, True)}
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def extensions(self) -> Tuple[str, ...]:
|
|
202
|
+
return (".mcstructure",)
|
|
203
|
+
|
|
204
|
+
def _get_interface(self, raw_chunk_data=None) -> "MCStructureInterface":
|
|
205
|
+
return mcstructure_interface
|
|
206
|
+
|
|
207
|
+
def _get_interface_and_translator(
|
|
208
|
+
self, raw_chunk_data=None
|
|
209
|
+
) -> Tuple["Interface", "Translator", VersionNumberAny]:
|
|
210
|
+
interface = self._get_interface(raw_chunk_data)
|
|
211
|
+
translator, version_identifier = interface.get_translator(
|
|
212
|
+
self.max_world_version, raw_chunk_data, self.translation_manager
|
|
213
|
+
)
|
|
214
|
+
return interface, translator, version_identifier
|
|
215
|
+
|
|
216
|
+
def save_to(self, f: BinaryIO):
|
|
217
|
+
selection = self._bounds[self.dimensions[0]].selection_boxes[0]
|
|
218
|
+
mcstructure = CompoundTag(
|
|
219
|
+
{
|
|
220
|
+
"format_version": IntTag(1),
|
|
221
|
+
"structure_world_origin": ListTag(
|
|
222
|
+
[
|
|
223
|
+
IntTag(selection.min_x),
|
|
224
|
+
IntTag(selection.min_y),
|
|
225
|
+
IntTag(selection.min_z),
|
|
226
|
+
]
|
|
227
|
+
),
|
|
228
|
+
"size": ListTag(
|
|
229
|
+
[
|
|
230
|
+
IntTag(selection.size_x),
|
|
231
|
+
IntTag(selection.size_y),
|
|
232
|
+
IntTag(selection.size_z),
|
|
233
|
+
]
|
|
234
|
+
),
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
entities = []
|
|
239
|
+
block_entities = []
|
|
240
|
+
blocks = numpy.zeros(selection.shape, dtype=numpy.uint32)
|
|
241
|
+
palette: List[AnyNDArray] = []
|
|
242
|
+
if self.version < (1, 13, 0):
|
|
243
|
+
raise Exception(
|
|
244
|
+
"Writing to mcstructre files in pre-1.13 format is not currently supported."
|
|
245
|
+
)
|
|
246
|
+
else:
|
|
247
|
+
arr = numpy.empty(1, dtype=object)
|
|
248
|
+
arr[0] = [
|
|
249
|
+
CompoundTag(
|
|
250
|
+
{
|
|
251
|
+
"name": StringTag("minecraft:air"),
|
|
252
|
+
"states": CompoundTag(),
|
|
253
|
+
"version": IntTag(17694723),
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
]
|
|
257
|
+
palette.append(arr)
|
|
258
|
+
|
|
259
|
+
palette_len = 1
|
|
260
|
+
|
|
261
|
+
for (
|
|
262
|
+
selection_,
|
|
263
|
+
blocks_,
|
|
264
|
+
palette_,
|
|
265
|
+
block_entities_,
|
|
266
|
+
entities_,
|
|
267
|
+
) in self._chunks.values():
|
|
268
|
+
if selection_.intersects(selection):
|
|
269
|
+
box = selection_.create_moved_box(selection.min, subtract=True)
|
|
270
|
+
blocks[box.slice] = blocks_ + palette_len
|
|
271
|
+
palette.append(palette_)
|
|
272
|
+
palette_len += len(palette_)
|
|
273
|
+
block_entities += block_entities_
|
|
274
|
+
entities += entities_
|
|
275
|
+
|
|
276
|
+
compact_palette, lut = brute_sort_objects_no_hash(numpy.concatenate(palette))
|
|
277
|
+
blocks = lut[blocks].ravel()
|
|
278
|
+
block_palette = []
|
|
279
|
+
block_palette_indices = []
|
|
280
|
+
for block_list in compact_palette:
|
|
281
|
+
indexed_block = [-1] * 2
|
|
282
|
+
for block_layer, block in enumerate(block_list):
|
|
283
|
+
if block_layer >= 2:
|
|
284
|
+
break
|
|
285
|
+
if block["name"] != StringTag("minecraft:structure_void"):
|
|
286
|
+
if block in block_palette:
|
|
287
|
+
indexed_block[block_layer] = block_palette.index(block)
|
|
288
|
+
else:
|
|
289
|
+
indexed_block[block_layer] = len(block_palette)
|
|
290
|
+
block_palette.append(block)
|
|
291
|
+
block_palette_indices.append(indexed_block)
|
|
292
|
+
|
|
293
|
+
block_indices = numpy.array(block_palette_indices, dtype=numpy.int32)[blocks].T
|
|
294
|
+
|
|
295
|
+
mcstructure["structure"] = CompoundTag(
|
|
296
|
+
{
|
|
297
|
+
"block_indices": ListTag(
|
|
298
|
+
[ # a list of tag ints that index into the block_palette. One list per block layer
|
|
299
|
+
ListTag([IntTag(block) for block in layer])
|
|
300
|
+
for layer in block_indices
|
|
301
|
+
]
|
|
302
|
+
),
|
|
303
|
+
"entities": ListTag(entities),
|
|
304
|
+
"palette": CompoundTag(
|
|
305
|
+
{
|
|
306
|
+
"default": CompoundTag(
|
|
307
|
+
{
|
|
308
|
+
"block_palette": ListTag(block_palette),
|
|
309
|
+
"block_position_data": CompoundTag(
|
|
310
|
+
{
|
|
311
|
+
str(
|
|
312
|
+
(
|
|
313
|
+
(
|
|
314
|
+
block_entity["x"].py_int
|
|
315
|
+
- selection.min_x
|
|
316
|
+
)
|
|
317
|
+
* selection.size_y
|
|
318
|
+
+ (
|
|
319
|
+
block_entity["y"].py_int
|
|
320
|
+
- selection.min_y
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
* selection.size_z
|
|
324
|
+
+ block_entity["z"].py_int
|
|
325
|
+
- selection.min_z
|
|
326
|
+
): CompoundTag(
|
|
327
|
+
{"block_entity_data": block_entity}
|
|
328
|
+
)
|
|
329
|
+
for block_entity in block_entities
|
|
330
|
+
}
|
|
331
|
+
),
|
|
332
|
+
}
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
),
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
mcstructure.save_to(
|
|
339
|
+
f, compressed=False, little_endian=True, string_encoder=utf8_escape_encoder
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def _close(self):
|
|
343
|
+
"""Close the disk database"""
|
|
344
|
+
self._chunks.clear()
|
|
345
|
+
|
|
346
|
+
def unload(self):
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
def all_chunk_coords(
|
|
350
|
+
self, dimension: Optional[Dimension] = None
|
|
351
|
+
) -> Iterable[ChunkCoordinates]:
|
|
352
|
+
yield from self._chunks.keys()
|
|
353
|
+
|
|
354
|
+
def has_chunk(self, cx: int, cz: int, dimension: Dimension) -> bool:
|
|
355
|
+
return (cx, cz) in self._chunks
|
|
356
|
+
|
|
357
|
+
def _encode(
|
|
358
|
+
self,
|
|
359
|
+
interface: MCStructureInterface,
|
|
360
|
+
chunk: Chunk,
|
|
361
|
+
dimension: Dimension,
|
|
362
|
+
chunk_palette: AnyNDArray,
|
|
363
|
+
):
|
|
364
|
+
return interface.encode(
|
|
365
|
+
chunk,
|
|
366
|
+
chunk_palette,
|
|
367
|
+
self.max_world_version,
|
|
368
|
+
SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
|
|
369
|
+
self._bounds[dimension].to_box()
|
|
370
|
+
),
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def _delete_chunk(self, cx: int, cz: int, dimension: Optional[Dimension] = None):
|
|
374
|
+
if (cx, cz) in self._chunks:
|
|
375
|
+
del self._chunks[(cx, cz)]
|
|
376
|
+
|
|
377
|
+
def _put_raw_chunk_data(
|
|
378
|
+
self,
|
|
379
|
+
cx: int,
|
|
380
|
+
cz: int,
|
|
381
|
+
section: MCStructureChunk,
|
|
382
|
+
dimension: Optional[Dimension] = None,
|
|
383
|
+
):
|
|
384
|
+
self._chunks[(cx, cz)] = copy.deepcopy(
|
|
385
|
+
(
|
|
386
|
+
section.selection,
|
|
387
|
+
section.blocks,
|
|
388
|
+
section.palette,
|
|
389
|
+
section.block_entities,
|
|
390
|
+
section.entities,
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
def _get_raw_chunk_data(
|
|
395
|
+
self, cx: int, cz: int, dimension: Optional[Dimension] = None
|
|
396
|
+
) -> MCStructureChunk:
|
|
397
|
+
"""
|
|
398
|
+
Return the raw data as loaded from disk.
|
|
399
|
+
|
|
400
|
+
:param cx: The x coordinate of the chunk.
|
|
401
|
+
:param cz: The z coordinate of the chunk.
|
|
402
|
+
:param dimension: The dimension to load the data from.
|
|
403
|
+
:return: The raw chunk data.
|
|
404
|
+
"""
|
|
405
|
+
if (cx, cz) in self._chunks:
|
|
406
|
+
return MCStructureChunk(*copy.deepcopy(self._chunks[(cx, cz)]))
|
|
407
|
+
else:
|
|
408
|
+
raise ChunkDoesNotExist
|