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,373 +1,373 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import (
|
|
3
|
-
Optional,
|
|
4
|
-
Tuple,
|
|
5
|
-
Iterable,
|
|
6
|
-
TYPE_CHECKING,
|
|
7
|
-
BinaryIO,
|
|
8
|
-
Dict,
|
|
9
|
-
Union,
|
|
10
|
-
List,
|
|
11
|
-
NamedTuple,
|
|
12
|
-
)
|
|
13
|
-
import numpy
|
|
14
|
-
import copy
|
|
15
|
-
|
|
16
|
-
from amulet_nbt import (
|
|
17
|
-
ShortTag,
|
|
18
|
-
StringTag,
|
|
19
|
-
ListTag,
|
|
20
|
-
CompoundTag,
|
|
21
|
-
ByteArrayTag,
|
|
22
|
-
NamedTag,
|
|
23
|
-
load as load_nbt,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
from amulet.api.data_types import (
|
|
27
|
-
AnyNDArray,
|
|
28
|
-
VersionNumberAny,
|
|
29
|
-
VersionNumberTuple,
|
|
30
|
-
Dimension,
|
|
31
|
-
PlatformType,
|
|
32
|
-
PointCoordinates,
|
|
33
|
-
ChunkCoordinates,
|
|
34
|
-
)
|
|
35
|
-
from amulet.api.registry import BlockManager
|
|
36
|
-
from amulet.api.wrapper import StructureFormatWrapper
|
|
37
|
-
from amulet.api.chunk import Chunk
|
|
38
|
-
from amulet.api.selection import SelectionBox, SelectionGroup
|
|
39
|
-
from amulet.api.errors import ObjectReadError, ObjectWriteError, ChunkDoesNotExist
|
|
40
|
-
from .interface import (
|
|
41
|
-
JavaSchematicInterface,
|
|
42
|
-
BedrockSchematicInterface,
|
|
43
|
-
SchematicInterface,
|
|
44
|
-
)
|
|
45
|
-
from .chunk import SchematicChunk
|
|
46
|
-
|
|
47
|
-
if TYPE_CHECKING:
|
|
48
|
-
from amulet.api.wrapper import Translator, Interface
|
|
49
|
-
|
|
50
|
-
java_interface = JavaSchematicInterface()
|
|
51
|
-
bedrock_interface = BedrockSchematicInterface()
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _is_schematic(path: str):
|
|
55
|
-
"""Check if a file is actually a sponge schematic file."""
|
|
56
|
-
try:
|
|
57
|
-
return "Blocks" in load_nbt(path).compound
|
|
58
|
-
except:
|
|
59
|
-
return False
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class SchematicFormatWrapper(StructureFormatWrapper[VersionNumberTuple]):
|
|
63
|
-
"""
|
|
64
|
-
This FormatWrapper class exists to interface with the legacy schematic structure format.
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
def __init__(self, path: str):
|
|
68
|
-
"""
|
|
69
|
-
Construct a new instance of :class:`SchematicFormatWrapper`.
|
|
70
|
-
|
|
71
|
-
This should not be used directly. You should instead use :func:`amulet.load_format`.
|
|
72
|
-
|
|
73
|
-
:param path: The file path to the serialised data.
|
|
74
|
-
"""
|
|
75
|
-
super().__init__(path)
|
|
76
|
-
self._chunks: Dict[
|
|
77
|
-
ChunkCoordinates,
|
|
78
|
-
SchematicChunk,
|
|
79
|
-
] = {}
|
|
80
|
-
|
|
81
|
-
def _create(
|
|
82
|
-
self,
|
|
83
|
-
overwrite: bool,
|
|
84
|
-
bounds: Union[
|
|
85
|
-
SelectionGroup, Dict[Dimension, Optional[SelectionGroup]], None
|
|
86
|
-
] = None,
|
|
87
|
-
**kwargs,
|
|
88
|
-
):
|
|
89
|
-
if not overwrite and os.path.isfile(self.path):
|
|
90
|
-
raise ObjectWriteError(f"There is already a file at {self.path}")
|
|
91
|
-
if self._platform == "bedrock":
|
|
92
|
-
self._version = (1, 12, 0)
|
|
93
|
-
else:
|
|
94
|
-
self._platform = "java"
|
|
95
|
-
self._version = (1, 12, 2)
|
|
96
|
-
self._chunks = {}
|
|
97
|
-
self._set_selection(bounds)
|
|
98
|
-
self._is_open = True
|
|
99
|
-
self._has_lock = True
|
|
100
|
-
|
|
101
|
-
def open_from(self, f: BinaryIO):
|
|
102
|
-
schematic = load_nbt(f).compound
|
|
103
|
-
if "BlockData" in schematic:
|
|
104
|
-
raise ObjectReadError("This file is not a legacy schematic file.")
|
|
105
|
-
materials = schematic.get_string("Materials", StringTag("Alpha")).py_str
|
|
106
|
-
if materials == "Alpha":
|
|
107
|
-
self._platform = "java"
|
|
108
|
-
self._version = (1, 12, 2)
|
|
109
|
-
elif materials == "Pocket":
|
|
110
|
-
self._platform = "bedrock"
|
|
111
|
-
self._version = (1, 12, 0)
|
|
112
|
-
else:
|
|
113
|
-
raise Exception(
|
|
114
|
-
f'"{materials}" is not a supported platform for a schematic file.'
|
|
115
|
-
)
|
|
116
|
-
self._chunks = {}
|
|
117
|
-
selection_box = SelectionBox(
|
|
118
|
-
(0, 0, 0),
|
|
119
|
-
(
|
|
120
|
-
schematic.get_short("Width").py_int,
|
|
121
|
-
schematic.get_short("Height").py_int,
|
|
122
|
-
schematic.get_short("Length").py_int,
|
|
123
|
-
),
|
|
124
|
-
)
|
|
125
|
-
self._bounds[self.dimensions[0]] = SelectionGroup(selection_box)
|
|
126
|
-
entities: ListTag = schematic.get_list("Entities", ListTag())
|
|
127
|
-
block_entities: ListTag = schematic.get_list("TileEntities", ListTag())
|
|
128
|
-
blocks: numpy.ndarray = (
|
|
129
|
-
schematic.get_byte_array("Blocks")
|
|
130
|
-
.np_array.astype(numpy.uint8)
|
|
131
|
-
.astype(numpy.uint16)
|
|
132
|
-
)
|
|
133
|
-
if "AddBlocks" in schematic:
|
|
134
|
-
add_blocks = schematic.get_byte_array("AddBlocks").np_array
|
|
135
|
-
blocks = (
|
|
136
|
-
blocks
|
|
137
|
-
+ (
|
|
138
|
-
numpy.concatenate([[(add_blocks & 0xF0) >> 4], [add_blocks & 0xF]])
|
|
139
|
-
.T.ravel()
|
|
140
|
-
.astype(numpy.uint16)
|
|
141
|
-
<< 8
|
|
142
|
-
)[: blocks.size]
|
|
143
|
-
)
|
|
144
|
-
max_point = selection_box.max
|
|
145
|
-
temp_shape = (max_point[1], max_point[2], max_point[0])
|
|
146
|
-
blocks = numpy.transpose(blocks.reshape(temp_shape), (2, 0, 1)) # YZX => XYZ
|
|
147
|
-
data = numpy.transpose(
|
|
148
|
-
schematic.get_byte_array("Data").np_array.reshape(temp_shape), (2, 0, 1)
|
|
149
|
-
).astype(numpy.uint8)
|
|
150
|
-
for cx, cz in selection_box.chunk_locations():
|
|
151
|
-
box = SelectionBox(
|
|
152
|
-
(cx * self.sub_chunk_size, 0, cz * self.sub_chunk_size),
|
|
153
|
-
(
|
|
154
|
-
min((cx + 1) * self.sub_chunk_size, selection_box.size_x),
|
|
155
|
-
selection_box.size_y,
|
|
156
|
-
min((cz + 1) * self.sub_chunk_size, selection_box.size_z),
|
|
157
|
-
),
|
|
158
|
-
)
|
|
159
|
-
self._chunks[(cx, cz)] = SchematicChunk(
|
|
160
|
-
box, blocks[box.slice], data[box.slice], [], []
|
|
161
|
-
)
|
|
162
|
-
for e in block_entities:
|
|
163
|
-
if isinstance(e, CompoundTag) and all(key in e for key in ("x", "y", "z")):
|
|
164
|
-
x = e.get_int("x").py_int
|
|
165
|
-
y = e.get_int("y").py_int
|
|
166
|
-
z = e.get_int("z").py_int
|
|
167
|
-
if (x, y, z) in selection_box:
|
|
168
|
-
cx = x >> 4
|
|
169
|
-
cz = z >> 4
|
|
170
|
-
self._chunks[(cx, cz)].block_entities.append(e)
|
|
171
|
-
for e in entities:
|
|
172
|
-
if isinstance(e, CompoundTag) and "Pos" in e:
|
|
173
|
-
pos: PointCoordinates = tuple(map(float, e.get_list("Pos", ListTag())))
|
|
174
|
-
if len(pos) == 3:
|
|
175
|
-
if pos in selection_box:
|
|
176
|
-
cx = int(pos[0]) >> 4
|
|
177
|
-
cz = int(pos[2]) >> 4
|
|
178
|
-
self._chunks[(cx, cz)].entities.append(e)
|
|
179
|
-
|
|
180
|
-
@staticmethod
|
|
181
|
-
def is_valid(path: str) -> bool:
|
|
182
|
-
return (
|
|
183
|
-
os.path.isfile(path) and path.endswith(".schematic") and _is_schematic(path)
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
@property
|
|
187
|
-
def valid_formats(self) -> Dict[PlatformType, Tuple[bool, bool]]:
|
|
188
|
-
return {"java": (True, True), "bedrock": (True, True)}
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
def extensions(self) -> Tuple[str, ...]:
|
|
192
|
-
return (".schematic",)
|
|
193
|
-
|
|
194
|
-
def _get_interface(self, raw_chunk_data=None) -> SchematicInterface:
|
|
195
|
-
if self._platform == "java":
|
|
196
|
-
return java_interface
|
|
197
|
-
elif self._platform == "bedrock":
|
|
198
|
-
return bedrock_interface
|
|
199
|
-
else:
|
|
200
|
-
raise Exception(f"{self._platform} is not a supported platform")
|
|
201
|
-
|
|
202
|
-
def _get_interface_and_translator(
|
|
203
|
-
self, raw_chunk_data=None
|
|
204
|
-
) -> Tuple["Interface", "Translator", VersionNumberAny]:
|
|
205
|
-
interface = self._get_interface(raw_chunk_data)
|
|
206
|
-
translator, version_identifier = interface.get_translator(
|
|
207
|
-
self.max_world_version, raw_chunk_data, self.translation_manager
|
|
208
|
-
)
|
|
209
|
-
return interface, translator, version_identifier
|
|
210
|
-
|
|
211
|
-
def save_to(self, f: BinaryIO):
|
|
212
|
-
if self._platform == "java":
|
|
213
|
-
materials = "Alpha"
|
|
214
|
-
elif self._platform == "bedrock":
|
|
215
|
-
materials = "Pocket"
|
|
216
|
-
else:
|
|
217
|
-
raise ObjectWriteError(
|
|
218
|
-
f'"{self._platform}" is not a supported platform for a schematic file.'
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
selection = self._bounds[self.dimensions[0]].selection_boxes[0]
|
|
222
|
-
|
|
223
|
-
tag = CompoundTag(
|
|
224
|
-
{
|
|
225
|
-
"TileTicks": ListTag(),
|
|
226
|
-
"Width": ShortTag(selection.size_x),
|
|
227
|
-
"Height": ShortTag(selection.size_y),
|
|
228
|
-
"Length": ShortTag(selection.size_z),
|
|
229
|
-
"Materials": StringTag(materials),
|
|
230
|
-
}
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
entities = []
|
|
234
|
-
block_entities = []
|
|
235
|
-
blocks = numpy.zeros(
|
|
236
|
-
selection.shape, dtype=numpy.uint16
|
|
237
|
-
) # only 12 bits are actually used at most
|
|
238
|
-
block_data = numpy.zeros(
|
|
239
|
-
selection.shape, dtype=numpy.uint8
|
|
240
|
-
) # only 4 bits are used
|
|
241
|
-
|
|
242
|
-
for chunk in self._chunks.values():
|
|
243
|
-
if chunk.selection.intersects(selection):
|
|
244
|
-
box = chunk.selection.create_moved_box(selection.min, subtract=True)
|
|
245
|
-
blocks[box.slice] = chunk.blocks
|
|
246
|
-
block_data[box.slice] = chunk.data
|
|
247
|
-
for be in chunk.block_entities:
|
|
248
|
-
coord_type = be["x"].__class__
|
|
249
|
-
be["x"] = coord_type(be["x"].py_int - selection.min_x)
|
|
250
|
-
be["y"] = coord_type(be["y"].py_int - selection.min_y)
|
|
251
|
-
be["z"] = coord_type(be["z"].py_int - selection.min_z)
|
|
252
|
-
block_entities.append(be)
|
|
253
|
-
for e in chunk.entities:
|
|
254
|
-
coord_type = e["Pos"][0].__class__
|
|
255
|
-
e["Pos"][0] = coord_type(e["Pos"][0] - selection.min_x)
|
|
256
|
-
e["Pos"][1] = coord_type(e["Pos"][1] - selection.min_y)
|
|
257
|
-
e["Pos"][2] = coord_type(e["Pos"][2] - selection.min_z)
|
|
258
|
-
entities.append(e)
|
|
259
|
-
|
|
260
|
-
tag["Entities"] = ListTag(entities)
|
|
261
|
-
tag["TileEntities"] = ListTag(block_entities)
|
|
262
|
-
tag["Data"] = ByteArrayTag(numpy.transpose(block_data, (1, 2, 0))) # XYZ => YZX
|
|
263
|
-
tag["Blocks"] = ByteArrayTag(
|
|
264
|
-
numpy.transpose((blocks & 0xFF).astype(numpy.uint8), (1, 2, 0))
|
|
265
|
-
)
|
|
266
|
-
if numpy.max(blocks) > 0xFF:
|
|
267
|
-
add_blocks = (numpy.transpose(blocks & 0xF00, (1, 2, 0)) >> 8).ravel()
|
|
268
|
-
tag["AddBlocks"] = ByteArrayTag((add_blocks[::2] << 4) + add_blocks[1::2])
|
|
269
|
-
NamedTag(tag, "Schematic").save_to(f)
|
|
270
|
-
|
|
271
|
-
def _close(self):
|
|
272
|
-
"""Close the disk database"""
|
|
273
|
-
self._chunks.clear()
|
|
274
|
-
|
|
275
|
-
def unload(self):
|
|
276
|
-
pass
|
|
277
|
-
|
|
278
|
-
def all_chunk_coords(
|
|
279
|
-
self, dimension: Optional[Dimension] = None
|
|
280
|
-
) -> Iterable[ChunkCoordinates]:
|
|
281
|
-
yield from self._chunks.keys()
|
|
282
|
-
|
|
283
|
-
def has_chunk(self, cx: int, cz: int, dimension: Dimension) -> bool:
|
|
284
|
-
return (cx, cz) in self._chunks
|
|
285
|
-
|
|
286
|
-
def _pack(
|
|
287
|
-
self,
|
|
288
|
-
chunk: "Chunk",
|
|
289
|
-
translator: "Translator",
|
|
290
|
-
chunk_version: VersionNumberAny,
|
|
291
|
-
) -> Tuple["Chunk", AnyNDArray]:
|
|
292
|
-
version = self.translation_manager.get_version(
|
|
293
|
-
*translator.translator_key(chunk_version)
|
|
294
|
-
)
|
|
295
|
-
palette = []
|
|
296
|
-
for entry in chunk.block_palette.blocks:
|
|
297
|
-
b = version.block.block_to_ints(entry)
|
|
298
|
-
if b is None:
|
|
299
|
-
b = (0, 0)
|
|
300
|
-
palette.append(b)
|
|
301
|
-
return (
|
|
302
|
-
chunk,
|
|
303
|
-
numpy.array(palette),
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
def _encode(
|
|
307
|
-
self,
|
|
308
|
-
interface: SchematicInterface,
|
|
309
|
-
chunk: Chunk,
|
|
310
|
-
dimension: Dimension,
|
|
311
|
-
chunk_palette: AnyNDArray,
|
|
312
|
-
):
|
|
313
|
-
return interface.encode(
|
|
314
|
-
chunk,
|
|
315
|
-
chunk_palette,
|
|
316
|
-
self.max_world_version,
|
|
317
|
-
SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
|
|
318
|
-
self._bounds[dimension].to_box()
|
|
319
|
-
),
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
def _unpack(
|
|
323
|
-
self,
|
|
324
|
-
translator: "Translator",
|
|
325
|
-
game_version: VersionNumberAny,
|
|
326
|
-
chunk: "Chunk",
|
|
327
|
-
chunk_palette: AnyNDArray,
|
|
328
|
-
) -> "Chunk":
|
|
329
|
-
version = self.translation_manager.get_version(
|
|
330
|
-
*translator.translator_key(game_version)
|
|
331
|
-
)
|
|
332
|
-
palette = chunk._block_palette = BlockManager()
|
|
333
|
-
lut = numpy.array(
|
|
334
|
-
[
|
|
335
|
-
palette.get_add_block(version.block.ints_to_block(block, data))
|
|
336
|
-
for block, data in chunk_palette
|
|
337
|
-
]
|
|
338
|
-
)
|
|
339
|
-
if len(palette.blocks) != len(chunk_palette):
|
|
340
|
-
# if a blockstate was defined twice
|
|
341
|
-
for cy in chunk.blocks.sub_chunks:
|
|
342
|
-
chunk.blocks.add_sub_chunk(cy, lut[chunk.blocks.get_sub_chunk(cy)])
|
|
343
|
-
|
|
344
|
-
return chunk
|
|
345
|
-
|
|
346
|
-
def _delete_chunk(self, cx: int, cz: int, dimension: Optional[Dimension] = None):
|
|
347
|
-
if (cx, cz) in self._chunks:
|
|
348
|
-
del self._chunks[(cx, cz)]
|
|
349
|
-
|
|
350
|
-
def _put_raw_chunk_data(
|
|
351
|
-
self,
|
|
352
|
-
cx: int,
|
|
353
|
-
cz: int,
|
|
354
|
-
section: SchematicChunk,
|
|
355
|
-
dimension: Optional[Dimension] = None,
|
|
356
|
-
):
|
|
357
|
-
self._chunks[(cx, cz)] = copy.deepcopy(section)
|
|
358
|
-
|
|
359
|
-
def _get_raw_chunk_data(
|
|
360
|
-
self, cx: int, cz: int, dimension: Optional[Dimension] = None
|
|
361
|
-
) -> SchematicChunk:
|
|
362
|
-
"""
|
|
363
|
-
Return the raw data as loaded from disk.
|
|
364
|
-
|
|
365
|
-
:param cx: The x coordinate of the chunk.
|
|
366
|
-
:param cz: The z coordinate of the chunk.
|
|
367
|
-
:param dimension: The dimension to load the data from.
|
|
368
|
-
:return: The raw chunk data.
|
|
369
|
-
"""
|
|
370
|
-
if (cx, cz) in self._chunks:
|
|
371
|
-
return copy.deepcopy(self._chunks[(cx, cz)])
|
|
372
|
-
else:
|
|
373
|
-
raise ChunkDoesNotExist
|
|
1
|
+
import os
|
|
2
|
+
from typing import (
|
|
3
|
+
Optional,
|
|
4
|
+
Tuple,
|
|
5
|
+
Iterable,
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
BinaryIO,
|
|
8
|
+
Dict,
|
|
9
|
+
Union,
|
|
10
|
+
List,
|
|
11
|
+
NamedTuple,
|
|
12
|
+
)
|
|
13
|
+
import numpy
|
|
14
|
+
import copy
|
|
15
|
+
|
|
16
|
+
from amulet_nbt import (
|
|
17
|
+
ShortTag,
|
|
18
|
+
StringTag,
|
|
19
|
+
ListTag,
|
|
20
|
+
CompoundTag,
|
|
21
|
+
ByteArrayTag,
|
|
22
|
+
NamedTag,
|
|
23
|
+
load as load_nbt,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from amulet.api.data_types import (
|
|
27
|
+
AnyNDArray,
|
|
28
|
+
VersionNumberAny,
|
|
29
|
+
VersionNumberTuple,
|
|
30
|
+
Dimension,
|
|
31
|
+
PlatformType,
|
|
32
|
+
PointCoordinates,
|
|
33
|
+
ChunkCoordinates,
|
|
34
|
+
)
|
|
35
|
+
from amulet.api.registry import BlockManager
|
|
36
|
+
from amulet.api.wrapper import StructureFormatWrapper
|
|
37
|
+
from amulet.api.chunk import Chunk
|
|
38
|
+
from amulet.api.selection import SelectionBox, SelectionGroup
|
|
39
|
+
from amulet.api.errors import ObjectReadError, ObjectWriteError, ChunkDoesNotExist
|
|
40
|
+
from .interface import (
|
|
41
|
+
JavaSchematicInterface,
|
|
42
|
+
BedrockSchematicInterface,
|
|
43
|
+
SchematicInterface,
|
|
44
|
+
)
|
|
45
|
+
from .chunk import SchematicChunk
|
|
46
|
+
|
|
47
|
+
if TYPE_CHECKING:
|
|
48
|
+
from amulet.api.wrapper import Translator, Interface
|
|
49
|
+
|
|
50
|
+
java_interface = JavaSchematicInterface()
|
|
51
|
+
bedrock_interface = BedrockSchematicInterface()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_schematic(path: str):
|
|
55
|
+
"""Check if a file is actually a sponge schematic file."""
|
|
56
|
+
try:
|
|
57
|
+
return "Blocks" in load_nbt(path).compound
|
|
58
|
+
except:
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class SchematicFormatWrapper(StructureFormatWrapper[VersionNumberTuple]):
|
|
63
|
+
"""
|
|
64
|
+
This FormatWrapper class exists to interface with the legacy schematic structure format.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, path: str):
|
|
68
|
+
"""
|
|
69
|
+
Construct a new instance of :class:`SchematicFormatWrapper`.
|
|
70
|
+
|
|
71
|
+
This should not be used directly. You should instead use :func:`amulet.load_format`.
|
|
72
|
+
|
|
73
|
+
:param path: The file path to the serialised data.
|
|
74
|
+
"""
|
|
75
|
+
super().__init__(path)
|
|
76
|
+
self._chunks: Dict[
|
|
77
|
+
ChunkCoordinates,
|
|
78
|
+
SchematicChunk,
|
|
79
|
+
] = {}
|
|
80
|
+
|
|
81
|
+
def _create(
|
|
82
|
+
self,
|
|
83
|
+
overwrite: bool,
|
|
84
|
+
bounds: Union[
|
|
85
|
+
SelectionGroup, Dict[Dimension, Optional[SelectionGroup]], None
|
|
86
|
+
] = None,
|
|
87
|
+
**kwargs,
|
|
88
|
+
):
|
|
89
|
+
if not overwrite and os.path.isfile(self.path):
|
|
90
|
+
raise ObjectWriteError(f"There is already a file at {self.path}")
|
|
91
|
+
if self._platform == "bedrock":
|
|
92
|
+
self._version = (1, 12, 0)
|
|
93
|
+
else:
|
|
94
|
+
self._platform = "java"
|
|
95
|
+
self._version = (1, 12, 2)
|
|
96
|
+
self._chunks = {}
|
|
97
|
+
self._set_selection(bounds)
|
|
98
|
+
self._is_open = True
|
|
99
|
+
self._has_lock = True
|
|
100
|
+
|
|
101
|
+
def open_from(self, f: BinaryIO):
|
|
102
|
+
schematic = load_nbt(f).compound
|
|
103
|
+
if "BlockData" in schematic:
|
|
104
|
+
raise ObjectReadError("This file is not a legacy schematic file.")
|
|
105
|
+
materials = schematic.get_string("Materials", StringTag("Alpha")).py_str
|
|
106
|
+
if materials == "Alpha":
|
|
107
|
+
self._platform = "java"
|
|
108
|
+
self._version = (1, 12, 2)
|
|
109
|
+
elif materials == "Pocket":
|
|
110
|
+
self._platform = "bedrock"
|
|
111
|
+
self._version = (1, 12, 0)
|
|
112
|
+
else:
|
|
113
|
+
raise Exception(
|
|
114
|
+
f'"{materials}" is not a supported platform for a schematic file.'
|
|
115
|
+
)
|
|
116
|
+
self._chunks = {}
|
|
117
|
+
selection_box = SelectionBox(
|
|
118
|
+
(0, 0, 0),
|
|
119
|
+
(
|
|
120
|
+
schematic.get_short("Width").py_int,
|
|
121
|
+
schematic.get_short("Height").py_int,
|
|
122
|
+
schematic.get_short("Length").py_int,
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
self._bounds[self.dimensions[0]] = SelectionGroup(selection_box)
|
|
126
|
+
entities: ListTag = schematic.get_list("Entities", ListTag())
|
|
127
|
+
block_entities: ListTag = schematic.get_list("TileEntities", ListTag())
|
|
128
|
+
blocks: numpy.ndarray = (
|
|
129
|
+
schematic.get_byte_array("Blocks")
|
|
130
|
+
.np_array.astype(numpy.uint8)
|
|
131
|
+
.astype(numpy.uint16)
|
|
132
|
+
)
|
|
133
|
+
if "AddBlocks" in schematic:
|
|
134
|
+
add_blocks = schematic.get_byte_array("AddBlocks").np_array
|
|
135
|
+
blocks = (
|
|
136
|
+
blocks
|
|
137
|
+
+ (
|
|
138
|
+
numpy.concatenate([[(add_blocks & 0xF0) >> 4], [add_blocks & 0xF]])
|
|
139
|
+
.T.ravel()
|
|
140
|
+
.astype(numpy.uint16)
|
|
141
|
+
<< 8
|
|
142
|
+
)[: blocks.size]
|
|
143
|
+
)
|
|
144
|
+
max_point = selection_box.max
|
|
145
|
+
temp_shape = (max_point[1], max_point[2], max_point[0])
|
|
146
|
+
blocks = numpy.transpose(blocks.reshape(temp_shape), (2, 0, 1)) # YZX => XYZ
|
|
147
|
+
data = numpy.transpose(
|
|
148
|
+
schematic.get_byte_array("Data").np_array.reshape(temp_shape), (2, 0, 1)
|
|
149
|
+
).astype(numpy.uint8)
|
|
150
|
+
for cx, cz in selection_box.chunk_locations():
|
|
151
|
+
box = SelectionBox(
|
|
152
|
+
(cx * self.sub_chunk_size, 0, cz * self.sub_chunk_size),
|
|
153
|
+
(
|
|
154
|
+
min((cx + 1) * self.sub_chunk_size, selection_box.size_x),
|
|
155
|
+
selection_box.size_y,
|
|
156
|
+
min((cz + 1) * self.sub_chunk_size, selection_box.size_z),
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
self._chunks[(cx, cz)] = SchematicChunk(
|
|
160
|
+
box, blocks[box.slice], data[box.slice], [], []
|
|
161
|
+
)
|
|
162
|
+
for e in block_entities:
|
|
163
|
+
if isinstance(e, CompoundTag) and all(key in e for key in ("x", "y", "z")):
|
|
164
|
+
x = e.get_int("x").py_int
|
|
165
|
+
y = e.get_int("y").py_int
|
|
166
|
+
z = e.get_int("z").py_int
|
|
167
|
+
if (x, y, z) in selection_box:
|
|
168
|
+
cx = x >> 4
|
|
169
|
+
cz = z >> 4
|
|
170
|
+
self._chunks[(cx, cz)].block_entities.append(e)
|
|
171
|
+
for e in entities:
|
|
172
|
+
if isinstance(e, CompoundTag) and "Pos" in e:
|
|
173
|
+
pos: PointCoordinates = tuple(map(float, e.get_list("Pos", ListTag())))
|
|
174
|
+
if len(pos) == 3:
|
|
175
|
+
if pos in selection_box:
|
|
176
|
+
cx = int(pos[0]) >> 4
|
|
177
|
+
cz = int(pos[2]) >> 4
|
|
178
|
+
self._chunks[(cx, cz)].entities.append(e)
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def is_valid(path: str) -> bool:
|
|
182
|
+
return (
|
|
183
|
+
os.path.isfile(path) and path.endswith(".schematic") and _is_schematic(path)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def valid_formats(self) -> Dict[PlatformType, Tuple[bool, bool]]:
|
|
188
|
+
return {"java": (True, True), "bedrock": (True, True)}
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def extensions(self) -> Tuple[str, ...]:
|
|
192
|
+
return (".schematic",)
|
|
193
|
+
|
|
194
|
+
def _get_interface(self, raw_chunk_data=None) -> SchematicInterface:
|
|
195
|
+
if self._platform == "java":
|
|
196
|
+
return java_interface
|
|
197
|
+
elif self._platform == "bedrock":
|
|
198
|
+
return bedrock_interface
|
|
199
|
+
else:
|
|
200
|
+
raise Exception(f"{self._platform} is not a supported platform")
|
|
201
|
+
|
|
202
|
+
def _get_interface_and_translator(
|
|
203
|
+
self, raw_chunk_data=None
|
|
204
|
+
) -> Tuple["Interface", "Translator", VersionNumberAny]:
|
|
205
|
+
interface = self._get_interface(raw_chunk_data)
|
|
206
|
+
translator, version_identifier = interface.get_translator(
|
|
207
|
+
self.max_world_version, raw_chunk_data, self.translation_manager
|
|
208
|
+
)
|
|
209
|
+
return interface, translator, version_identifier
|
|
210
|
+
|
|
211
|
+
def save_to(self, f: BinaryIO):
|
|
212
|
+
if self._platform == "java":
|
|
213
|
+
materials = "Alpha"
|
|
214
|
+
elif self._platform == "bedrock":
|
|
215
|
+
materials = "Pocket"
|
|
216
|
+
else:
|
|
217
|
+
raise ObjectWriteError(
|
|
218
|
+
f'"{self._platform}" is not a supported platform for a schematic file.'
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
selection = self._bounds[self.dimensions[0]].selection_boxes[0]
|
|
222
|
+
|
|
223
|
+
tag = CompoundTag(
|
|
224
|
+
{
|
|
225
|
+
"TileTicks": ListTag(),
|
|
226
|
+
"Width": ShortTag(selection.size_x),
|
|
227
|
+
"Height": ShortTag(selection.size_y),
|
|
228
|
+
"Length": ShortTag(selection.size_z),
|
|
229
|
+
"Materials": StringTag(materials),
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
entities = []
|
|
234
|
+
block_entities = []
|
|
235
|
+
blocks = numpy.zeros(
|
|
236
|
+
selection.shape, dtype=numpy.uint16
|
|
237
|
+
) # only 12 bits are actually used at most
|
|
238
|
+
block_data = numpy.zeros(
|
|
239
|
+
selection.shape, dtype=numpy.uint8
|
|
240
|
+
) # only 4 bits are used
|
|
241
|
+
|
|
242
|
+
for chunk in self._chunks.values():
|
|
243
|
+
if chunk.selection.intersects(selection):
|
|
244
|
+
box = chunk.selection.create_moved_box(selection.min, subtract=True)
|
|
245
|
+
blocks[box.slice] = chunk.blocks
|
|
246
|
+
block_data[box.slice] = chunk.data
|
|
247
|
+
for be in chunk.block_entities:
|
|
248
|
+
coord_type = be["x"].__class__
|
|
249
|
+
be["x"] = coord_type(be["x"].py_int - selection.min_x)
|
|
250
|
+
be["y"] = coord_type(be["y"].py_int - selection.min_y)
|
|
251
|
+
be["z"] = coord_type(be["z"].py_int - selection.min_z)
|
|
252
|
+
block_entities.append(be)
|
|
253
|
+
for e in chunk.entities:
|
|
254
|
+
coord_type = e["Pos"][0].__class__
|
|
255
|
+
e["Pos"][0] = coord_type(e["Pos"][0] - selection.min_x)
|
|
256
|
+
e["Pos"][1] = coord_type(e["Pos"][1] - selection.min_y)
|
|
257
|
+
e["Pos"][2] = coord_type(e["Pos"][2] - selection.min_z)
|
|
258
|
+
entities.append(e)
|
|
259
|
+
|
|
260
|
+
tag["Entities"] = ListTag(entities)
|
|
261
|
+
tag["TileEntities"] = ListTag(block_entities)
|
|
262
|
+
tag["Data"] = ByteArrayTag(numpy.transpose(block_data, (1, 2, 0))) # XYZ => YZX
|
|
263
|
+
tag["Blocks"] = ByteArrayTag(
|
|
264
|
+
numpy.transpose((blocks & 0xFF).astype(numpy.uint8), (1, 2, 0))
|
|
265
|
+
)
|
|
266
|
+
if numpy.max(blocks) > 0xFF:
|
|
267
|
+
add_blocks = (numpy.transpose(blocks & 0xF00, (1, 2, 0)) >> 8).ravel()
|
|
268
|
+
tag["AddBlocks"] = ByteArrayTag((add_blocks[::2] << 4) + add_blocks[1::2])
|
|
269
|
+
NamedTag(tag, "Schematic").save_to(f)
|
|
270
|
+
|
|
271
|
+
def _close(self):
|
|
272
|
+
"""Close the disk database"""
|
|
273
|
+
self._chunks.clear()
|
|
274
|
+
|
|
275
|
+
def unload(self):
|
|
276
|
+
pass
|
|
277
|
+
|
|
278
|
+
def all_chunk_coords(
|
|
279
|
+
self, dimension: Optional[Dimension] = None
|
|
280
|
+
) -> Iterable[ChunkCoordinates]:
|
|
281
|
+
yield from self._chunks.keys()
|
|
282
|
+
|
|
283
|
+
def has_chunk(self, cx: int, cz: int, dimension: Dimension) -> bool:
|
|
284
|
+
return (cx, cz) in self._chunks
|
|
285
|
+
|
|
286
|
+
def _pack(
|
|
287
|
+
self,
|
|
288
|
+
chunk: "Chunk",
|
|
289
|
+
translator: "Translator",
|
|
290
|
+
chunk_version: VersionNumberAny,
|
|
291
|
+
) -> Tuple["Chunk", AnyNDArray]:
|
|
292
|
+
version = self.translation_manager.get_version(
|
|
293
|
+
*translator.translator_key(chunk_version)
|
|
294
|
+
)
|
|
295
|
+
palette = []
|
|
296
|
+
for entry in chunk.block_palette.blocks:
|
|
297
|
+
b = version.block.block_to_ints(entry)
|
|
298
|
+
if b is None:
|
|
299
|
+
b = (0, 0)
|
|
300
|
+
palette.append(b)
|
|
301
|
+
return (
|
|
302
|
+
chunk,
|
|
303
|
+
numpy.array(palette),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def _encode(
|
|
307
|
+
self,
|
|
308
|
+
interface: SchematicInterface,
|
|
309
|
+
chunk: Chunk,
|
|
310
|
+
dimension: Dimension,
|
|
311
|
+
chunk_palette: AnyNDArray,
|
|
312
|
+
):
|
|
313
|
+
return interface.encode(
|
|
314
|
+
chunk,
|
|
315
|
+
chunk_palette,
|
|
316
|
+
self.max_world_version,
|
|
317
|
+
SelectionBox.create_chunk_box(chunk.cx, chunk.cz).intersection(
|
|
318
|
+
self._bounds[dimension].to_box()
|
|
319
|
+
),
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def _unpack(
|
|
323
|
+
self,
|
|
324
|
+
translator: "Translator",
|
|
325
|
+
game_version: VersionNumberAny,
|
|
326
|
+
chunk: "Chunk",
|
|
327
|
+
chunk_palette: AnyNDArray,
|
|
328
|
+
) -> "Chunk":
|
|
329
|
+
version = self.translation_manager.get_version(
|
|
330
|
+
*translator.translator_key(game_version)
|
|
331
|
+
)
|
|
332
|
+
palette = chunk._block_palette = BlockManager()
|
|
333
|
+
lut = numpy.array(
|
|
334
|
+
[
|
|
335
|
+
palette.get_add_block(version.block.ints_to_block(block, data))
|
|
336
|
+
for block, data in chunk_palette
|
|
337
|
+
]
|
|
338
|
+
)
|
|
339
|
+
if len(palette.blocks) != len(chunk_palette):
|
|
340
|
+
# if a blockstate was defined twice
|
|
341
|
+
for cy in chunk.blocks.sub_chunks:
|
|
342
|
+
chunk.blocks.add_sub_chunk(cy, lut[chunk.blocks.get_sub_chunk(cy)])
|
|
343
|
+
|
|
344
|
+
return chunk
|
|
345
|
+
|
|
346
|
+
def _delete_chunk(self, cx: int, cz: int, dimension: Optional[Dimension] = None):
|
|
347
|
+
if (cx, cz) in self._chunks:
|
|
348
|
+
del self._chunks[(cx, cz)]
|
|
349
|
+
|
|
350
|
+
def _put_raw_chunk_data(
|
|
351
|
+
self,
|
|
352
|
+
cx: int,
|
|
353
|
+
cz: int,
|
|
354
|
+
section: SchematicChunk,
|
|
355
|
+
dimension: Optional[Dimension] = None,
|
|
356
|
+
):
|
|
357
|
+
self._chunks[(cx, cz)] = copy.deepcopy(section)
|
|
358
|
+
|
|
359
|
+
def _get_raw_chunk_data(
|
|
360
|
+
self, cx: int, cz: int, dimension: Optional[Dimension] = None
|
|
361
|
+
) -> SchematicChunk:
|
|
362
|
+
"""
|
|
363
|
+
Return the raw data as loaded from disk.
|
|
364
|
+
|
|
365
|
+
:param cx: The x coordinate of the chunk.
|
|
366
|
+
:param cz: The z coordinate of the chunk.
|
|
367
|
+
:param dimension: The dimension to load the data from.
|
|
368
|
+
:return: The raw chunk data.
|
|
369
|
+
"""
|
|
370
|
+
if (cx, cz) in self._chunks:
|
|
371
|
+
return copy.deepcopy(self._chunks[(cx, cz)])
|
|
372
|
+
else:
|
|
373
|
+
raise ChunkDoesNotExist
|