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,441 +1,441 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import (
|
|
5
|
-
Tuple,
|
|
6
|
-
Any,
|
|
7
|
-
Union,
|
|
8
|
-
TYPE_CHECKING,
|
|
9
|
-
Optional,
|
|
10
|
-
overload,
|
|
11
|
-
Type,
|
|
12
|
-
Sequence,
|
|
13
|
-
Callable,
|
|
14
|
-
)
|
|
15
|
-
from enum import Enum
|
|
16
|
-
|
|
17
|
-
from amulet.api.block_entity import BlockEntity
|
|
18
|
-
from amulet.api.entity import Entity
|
|
19
|
-
from amulet.api.data_types import AnyNDArray, VersionNumberAny, VersionIdentifierType
|
|
20
|
-
from amulet_nbt import (
|
|
21
|
-
AbstractBaseTag,
|
|
22
|
-
ListTag,
|
|
23
|
-
CompoundTag,
|
|
24
|
-
AnyNBT,
|
|
25
|
-
NamedTag,
|
|
26
|
-
StringTag,
|
|
27
|
-
IntTag,
|
|
28
|
-
IntArrayTag,
|
|
29
|
-
DoubleTag,
|
|
30
|
-
FloatTag,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
if TYPE_CHECKING:
|
|
34
|
-
from amulet.api.wrapper import Translator
|
|
35
|
-
from amulet.api.chunk import Chunk
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class EntityIDType(Enum):
|
|
39
|
-
int_id = 0
|
|
40
|
-
str_id = 1
|
|
41
|
-
namespace_str_id = 2
|
|
42
|
-
namespace_str_Id = 3
|
|
43
|
-
namespace_str_identifier = 4
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class EntityCoordType(Enum):
|
|
47
|
-
xyz_int = 0
|
|
48
|
-
Pos_list_float = 1
|
|
49
|
-
Pos_list_double = 2
|
|
50
|
-
Pos_list_int = 3
|
|
51
|
-
Pos_array_int = 4
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
PosTypeMap = {
|
|
55
|
-
3: EntityCoordType.Pos_list_int,
|
|
56
|
-
5: EntityCoordType.Pos_list_float,
|
|
57
|
-
6: EntityCoordType.Pos_list_double,
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class Interface(ABC):
|
|
62
|
-
@abstractmethod
|
|
63
|
-
def decode(self, *args, **kwargs) -> Tuple["Chunk", AnyNDArray]:
|
|
64
|
-
raise NotImplementedError
|
|
65
|
-
|
|
66
|
-
def _decode_entity(
|
|
67
|
-
self,
|
|
68
|
-
nbt: NamedTag,
|
|
69
|
-
id_type: EntityIDType,
|
|
70
|
-
coord_type: EntityCoordType,
|
|
71
|
-
) -> Optional[Entity]:
|
|
72
|
-
entity = self._decode_base_entity(nbt, id_type, coord_type)
|
|
73
|
-
if entity is not None:
|
|
74
|
-
namespace, base_name, x, y, z, nbt = entity
|
|
75
|
-
return Entity(
|
|
76
|
-
namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
def _decode_block_entity(
|
|
80
|
-
self,
|
|
81
|
-
nbt: NamedTag,
|
|
82
|
-
id_type: EntityIDType,
|
|
83
|
-
coord_type: EntityCoordType,
|
|
84
|
-
) -> Optional[BlockEntity]:
|
|
85
|
-
entity = self._decode_base_entity(nbt, id_type, coord_type)
|
|
86
|
-
if entity is not None:
|
|
87
|
-
namespace, base_name, x, y, z, nbt = entity
|
|
88
|
-
return BlockEntity(
|
|
89
|
-
namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
@staticmethod
|
|
93
|
-
def _decode_base_entity(
|
|
94
|
-
named_tag: NamedTag, id_type: EntityIDType, coord_type: EntityCoordType
|
|
95
|
-
) -> Optional[
|
|
96
|
-
Tuple[
|
|
97
|
-
str,
|
|
98
|
-
str,
|
|
99
|
-
Union[int, float],
|
|
100
|
-
Union[int, float],
|
|
101
|
-
Union[int, float],
|
|
102
|
-
NamedTag,
|
|
103
|
-
]
|
|
104
|
-
]:
|
|
105
|
-
if not (
|
|
106
|
-
isinstance(named_tag, NamedTag) and isinstance(named_tag.tag, CompoundTag)
|
|
107
|
-
):
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
nbt_tag = named_tag.tag
|
|
111
|
-
|
|
112
|
-
if id_type == EntityIDType.namespace_str_id:
|
|
113
|
-
entity_id = nbt_tag.pop("id", StringTag(""))
|
|
114
|
-
if (
|
|
115
|
-
not isinstance(entity_id, StringTag)
|
|
116
|
-
or entity_id.py_str == ""
|
|
117
|
-
or ":" not in entity_id.py_str
|
|
118
|
-
):
|
|
119
|
-
return
|
|
120
|
-
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
121
|
-
|
|
122
|
-
elif id_type == EntityIDType.namespace_str_Id:
|
|
123
|
-
entity_id = nbt_tag.pop("Id", StringTag(""))
|
|
124
|
-
if (
|
|
125
|
-
not isinstance(entity_id, StringTag)
|
|
126
|
-
or entity_id.py_str == ""
|
|
127
|
-
or ":" not in entity_id.py_str
|
|
128
|
-
):
|
|
129
|
-
return
|
|
130
|
-
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
131
|
-
|
|
132
|
-
elif id_type == EntityIDType.str_id:
|
|
133
|
-
entity_id = nbt_tag.pop("id", StringTag(""))
|
|
134
|
-
if not isinstance(entity_id, StringTag) or entity_id.py_str == "":
|
|
135
|
-
return
|
|
136
|
-
namespace = ""
|
|
137
|
-
base_name = entity_id.py_str
|
|
138
|
-
|
|
139
|
-
elif id_type in [EntityIDType.namespace_str_identifier, EntityIDType.int_id]:
|
|
140
|
-
if "identifier" in nbt_tag:
|
|
141
|
-
entity_id = nbt_tag.pop("identifier")
|
|
142
|
-
if (
|
|
143
|
-
not isinstance(entity_id, StringTag)
|
|
144
|
-
or entity_id.py_str == ""
|
|
145
|
-
or ":" not in entity_id.py_str
|
|
146
|
-
):
|
|
147
|
-
return
|
|
148
|
-
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
149
|
-
elif "id" in nbt_tag:
|
|
150
|
-
entity_id = nbt_tag.pop("id")
|
|
151
|
-
if not isinstance(entity_id, IntTag):
|
|
152
|
-
return
|
|
153
|
-
namespace = ""
|
|
154
|
-
base_name = str(entity_id.py_int)
|
|
155
|
-
else:
|
|
156
|
-
return
|
|
157
|
-
else:
|
|
158
|
-
raise NotImplementedError(f"Entity id type {id_type}")
|
|
159
|
-
|
|
160
|
-
if coord_type in [
|
|
161
|
-
EntityCoordType.Pos_list_double,
|
|
162
|
-
EntityCoordType.Pos_list_float,
|
|
163
|
-
EntityCoordType.Pos_list_int,
|
|
164
|
-
]:
|
|
165
|
-
if "Pos" not in nbt_tag:
|
|
166
|
-
return
|
|
167
|
-
pos = nbt_tag.pop("Pos")
|
|
168
|
-
pos: ListTag
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
not isinstance(pos, ListTag)
|
|
172
|
-
or len(pos) != 3
|
|
173
|
-
or PosTypeMap.get(pos.list_data_type) != coord_type
|
|
174
|
-
):
|
|
175
|
-
return
|
|
176
|
-
x, y, z = [c.py_data for c in pos]
|
|
177
|
-
elif coord_type == EntityCoordType.Pos_array_int:
|
|
178
|
-
if "Pos" not in nbt_tag:
|
|
179
|
-
return
|
|
180
|
-
pos = nbt_tag.pop("Pos")
|
|
181
|
-
pos: IntArrayTag
|
|
182
|
-
|
|
183
|
-
if not isinstance(pos, IntArrayTag) or len(pos) != 3:
|
|
184
|
-
return
|
|
185
|
-
x, y, z = pos
|
|
186
|
-
elif coord_type == EntityCoordType.xyz_int:
|
|
187
|
-
if not all(
|
|
188
|
-
c in nbt_tag and isinstance(nbt_tag[c], IntTag) for c in ("x", "y", "z")
|
|
189
|
-
):
|
|
190
|
-
return
|
|
191
|
-
x, y, z = [nbt_tag.pop(c).py_int for c in ("x", "y", "z")]
|
|
192
|
-
else:
|
|
193
|
-
raise NotImplementedError(f"Entity coord type {coord_type}")
|
|
194
|
-
|
|
195
|
-
return namespace, base_name, x, y, z, named_tag
|
|
196
|
-
|
|
197
|
-
@abstractmethod
|
|
198
|
-
def encode(self, *args, **kwargs) -> Any:
|
|
199
|
-
"""
|
|
200
|
-
Take a version-specific chunk and encode it to raw data for the format to store.
|
|
201
|
-
"""
|
|
202
|
-
raise NotImplementedError
|
|
203
|
-
|
|
204
|
-
def _encode_entity(
|
|
205
|
-
self, entity: Entity, id_type: EntityIDType, coord_type: EntityCoordType
|
|
206
|
-
) -> Optional[NamedTag]:
|
|
207
|
-
return self._encode_base_entity(entity, id_type, coord_type)
|
|
208
|
-
|
|
209
|
-
def _encode_block_entity(
|
|
210
|
-
self, entity: BlockEntity, id_type: EntityIDType, coord_type: EntityCoordType
|
|
211
|
-
) -> Optional[NamedTag]:
|
|
212
|
-
return self._encode_base_entity(entity, id_type, coord_type)
|
|
213
|
-
|
|
214
|
-
@staticmethod
|
|
215
|
-
def _encode_base_entity(
|
|
216
|
-
entity: Union[Entity, BlockEntity],
|
|
217
|
-
id_type: EntityIDType,
|
|
218
|
-
coord_type: EntityCoordType,
|
|
219
|
-
) -> Optional[NamedTag]:
|
|
220
|
-
if not isinstance(entity.nbt, NamedTag) and isinstance(
|
|
221
|
-
entity.nbt.tag, CompoundTag
|
|
222
|
-
):
|
|
223
|
-
return
|
|
224
|
-
named_tag = entity.nbt
|
|
225
|
-
tag = named_tag.compound
|
|
226
|
-
|
|
227
|
-
if id_type == EntityIDType.namespace_str_id:
|
|
228
|
-
tag["id"] = StringTag(entity.namespaced_name)
|
|
229
|
-
elif id_type == EntityIDType.namespace_str_Id:
|
|
230
|
-
tag["Id"] = StringTag(entity.namespaced_name)
|
|
231
|
-
elif id_type == EntityIDType.namespace_str_identifier:
|
|
232
|
-
tag["identifier"] = StringTag(entity.namespaced_name)
|
|
233
|
-
elif id_type == EntityIDType.str_id:
|
|
234
|
-
tag["id"] = StringTag(entity.base_name)
|
|
235
|
-
elif id_type == EntityIDType.int_id:
|
|
236
|
-
if not entity.base_name.isnumeric():
|
|
237
|
-
return
|
|
238
|
-
tag["id"] = IntTag(int(entity.base_name))
|
|
239
|
-
else:
|
|
240
|
-
raise NotImplementedError(f"Entity id type {id_type}")
|
|
241
|
-
|
|
242
|
-
if coord_type == EntityCoordType.Pos_list_double:
|
|
243
|
-
tag["Pos"] = ListTag(
|
|
244
|
-
[
|
|
245
|
-
DoubleTag(float(entity.x)),
|
|
246
|
-
DoubleTag(float(entity.y)),
|
|
247
|
-
DoubleTag(float(entity.z)),
|
|
248
|
-
]
|
|
249
|
-
)
|
|
250
|
-
elif coord_type == EntityCoordType.Pos_list_float:
|
|
251
|
-
tag["Pos"] = ListTag(
|
|
252
|
-
[
|
|
253
|
-
FloatTag(float(entity.x)),
|
|
254
|
-
FloatTag(float(entity.y)),
|
|
255
|
-
FloatTag(float(entity.z)),
|
|
256
|
-
]
|
|
257
|
-
)
|
|
258
|
-
elif coord_type == EntityCoordType.Pos_list_int:
|
|
259
|
-
tag["Pos"] = ListTag(
|
|
260
|
-
[
|
|
261
|
-
IntTag(int(entity.x)),
|
|
262
|
-
IntTag(int(entity.y)),
|
|
263
|
-
IntTag(int(entity.z)),
|
|
264
|
-
]
|
|
265
|
-
)
|
|
266
|
-
elif coord_type == EntityCoordType.Pos_array_int:
|
|
267
|
-
tag["Pos"] = IntArrayTag([int(entity.x), int(entity.y), int(entity.z)])
|
|
268
|
-
elif coord_type == EntityCoordType.xyz_int:
|
|
269
|
-
tag["x"] = IntTag(int(entity.x))
|
|
270
|
-
tag["y"] = IntTag(int(entity.y))
|
|
271
|
-
tag["z"] = IntTag(int(entity.z))
|
|
272
|
-
else:
|
|
273
|
-
raise NotImplementedError(f"Entity coord type {coord_type}")
|
|
274
|
-
|
|
275
|
-
return named_tag
|
|
276
|
-
|
|
277
|
-
@overload
|
|
278
|
-
@staticmethod
|
|
279
|
-
def check_type(obj: CompoundTag, key: str, dtype: Type[AnyNBT]) -> bool:
|
|
280
|
-
...
|
|
281
|
-
|
|
282
|
-
@overload
|
|
283
|
-
@staticmethod
|
|
284
|
-
def check_type(obj: ListTag, key: int, dtype: Type[AnyNBT]) -> bool:
|
|
285
|
-
...
|
|
286
|
-
|
|
287
|
-
@staticmethod
|
|
288
|
-
def check_type(
|
|
289
|
-
obj: Union[CompoundTag, ListTag], key: Union[str, int], dtype: Type[AnyNBT]
|
|
290
|
-
) -> bool:
|
|
291
|
-
"""Check the key exists and the type is correct."""
|
|
292
|
-
return key in obj and isinstance(obj[key], dtype)
|
|
293
|
-
|
|
294
|
-
@overload
|
|
295
|
-
def get_obj(
|
|
296
|
-
self,
|
|
297
|
-
obj: CompoundTag,
|
|
298
|
-
key: str,
|
|
299
|
-
dtype: Type[AnyNBT],
|
|
300
|
-
default: Optional[AnyNBT] = None,
|
|
301
|
-
) -> Optional[AnyNBT]:
|
|
302
|
-
...
|
|
303
|
-
|
|
304
|
-
@overload
|
|
305
|
-
def get_obj(
|
|
306
|
-
self,
|
|
307
|
-
obj: ListTag,
|
|
308
|
-
key: int,
|
|
309
|
-
dtype: Type[AnyNBT],
|
|
310
|
-
default: Optional[AnyNBT] = None,
|
|
311
|
-
) -> Optional[AnyNBT]:
|
|
312
|
-
...
|
|
313
|
-
|
|
314
|
-
def get_obj(
|
|
315
|
-
self, obj, key, dtype: Type[AnyNBT], default: Optional[AnyNBT] = None
|
|
316
|
-
) -> Optional[AnyNBT]:
|
|
317
|
-
"""Pop a key from a container object if it exists and the type is correct. Otherwise return default.
|
|
318
|
-
This works in much the same way as dict.get but uses default if the data type does not match.
|
|
319
|
-
|
|
320
|
-
:param obj: The CompoundTag to read from.
|
|
321
|
-
:param key: The key to use.
|
|
322
|
-
:param dtype: The expected data type.
|
|
323
|
-
:param default: The default value to use if the existing is not valid. If None will use dtype()
|
|
324
|
-
:return: The final value in the key.
|
|
325
|
-
"""
|
|
326
|
-
if key in obj:
|
|
327
|
-
if isinstance(obj[key], dtype):
|
|
328
|
-
# if it exists and is correct
|
|
329
|
-
return obj.pop(key)
|
|
330
|
-
if default is None:
|
|
331
|
-
return dtype()
|
|
332
|
-
return default
|
|
333
|
-
|
|
334
|
-
def get_nested_obj(
|
|
335
|
-
self,
|
|
336
|
-
obj: Union[CompoundTag, ListTag],
|
|
337
|
-
path: Sequence[ # The path to the object.
|
|
338
|
-
Tuple[Union[str, int], Type[AbstractBaseTag]],
|
|
339
|
-
],
|
|
340
|
-
default: Union[None, AnyNBT, Callable[[], Any]] = None,
|
|
341
|
-
*,
|
|
342
|
-
pop_last=False,
|
|
343
|
-
):
|
|
344
|
-
"""
|
|
345
|
-
Get an object from a nested NBT structure
|
|
346
|
-
|
|
347
|
-
:param obj: The root NBT object
|
|
348
|
-
:param path: The path to the desired object (key, dtype)
|
|
349
|
-
:param default: The default value to use if the existing is not valid. If default is callable then return the called result.
|
|
350
|
-
:param pop_last: If true the last key will be popped
|
|
351
|
-
:return:
|
|
352
|
-
"""
|
|
353
|
-
try:
|
|
354
|
-
last_index = len(path) - 1
|
|
355
|
-
for i, (key, dtype) in enumerate(path):
|
|
356
|
-
if i == last_index and pop_last:
|
|
357
|
-
obj = obj.pop(key)
|
|
358
|
-
else:
|
|
359
|
-
obj = obj[key]
|
|
360
|
-
if dtype is not obj.__class__:
|
|
361
|
-
raise TypeError
|
|
362
|
-
except (KeyError, IndexError, TypeError):
|
|
363
|
-
if default is None or isinstance(default, AbstractBaseTag):
|
|
364
|
-
return default
|
|
365
|
-
elif callable(default):
|
|
366
|
-
return default()
|
|
367
|
-
else:
|
|
368
|
-
raise TypeError(
|
|
369
|
-
"default must be None, an NBT instance or an NBT class."
|
|
370
|
-
)
|
|
371
|
-
else:
|
|
372
|
-
return obj
|
|
373
|
-
|
|
374
|
-
@staticmethod
|
|
375
|
-
def set_obj(
|
|
376
|
-
obj: CompoundTag,
|
|
377
|
-
key: str,
|
|
378
|
-
dtype: Type[AnyNBT],
|
|
379
|
-
default: Union[None, AnyNBT, Callable[[], Any]] = None,
|
|
380
|
-
path: Sequence[str] = (), # The path to the object.
|
|
381
|
-
*,
|
|
382
|
-
setdefault=False,
|
|
383
|
-
) -> AnyNBT:
|
|
384
|
-
"""
|
|
385
|
-
Works like setdefualt on a dictionary but works with an optional nested path.
|
|
386
|
-
|
|
387
|
-
:param obj: The compound tag to get the data from
|
|
388
|
-
:param key: The key to setdefault
|
|
389
|
-
:param dtype: The dtype that the key must be
|
|
390
|
-
:param default: The default value to set if it does not exist or the type is wrong
|
|
391
|
-
:param path: Optional path to the nested compound.
|
|
392
|
-
:param setdefault: If True will behave like setdefault. If False will replace existing data.
|
|
393
|
-
:return: The data at the path
|
|
394
|
-
"""
|
|
395
|
-
for path_key in path:
|
|
396
|
-
obj_ = obj.get(path_key, None)
|
|
397
|
-
if not isinstance(obj_, CompoundTag):
|
|
398
|
-
# if it does not exist or the type is wrong then create it
|
|
399
|
-
obj_ = obj[path_key] = CompoundTag()
|
|
400
|
-
obj = obj_
|
|
401
|
-
obj_ = obj.get(key, None)
|
|
402
|
-
if not setdefault or not isinstance(obj_, dtype):
|
|
403
|
-
# if it does not exist or the type is wrong then create it
|
|
404
|
-
if default is None:
|
|
405
|
-
obj_ = dtype()
|
|
406
|
-
elif isinstance(default, AbstractBaseTag):
|
|
407
|
-
obj_ = default
|
|
408
|
-
elif callable(default):
|
|
409
|
-
obj_ = default()
|
|
410
|
-
else:
|
|
411
|
-
raise TypeError(
|
|
412
|
-
"default must be None, an NBT instance or an NBT class."
|
|
413
|
-
)
|
|
414
|
-
obj[key] = obj_
|
|
415
|
-
return obj_
|
|
416
|
-
|
|
417
|
-
@abstractmethod
|
|
418
|
-
def get_translator(
|
|
419
|
-
self,
|
|
420
|
-
max_world_version: VersionIdentifierType,
|
|
421
|
-
data: Any = None,
|
|
422
|
-
) -> Tuple["Translator", VersionNumberAny]:
|
|
423
|
-
"""
|
|
424
|
-
Get the Translator class for the requested version.
|
|
425
|
-
:param max_world_version: The game version the world was last opened in. Version number tuple or data version number.
|
|
426
|
-
:param data: Optional data to get translator based on chunk version rather than world version
|
|
427
|
-
:return: Tuple[Translator, version number for PyMCTranslate to use]
|
|
428
|
-
"""
|
|
429
|
-
raise NotImplementedError
|
|
430
|
-
|
|
431
|
-
@staticmethod
|
|
432
|
-
@abstractmethod
|
|
433
|
-
def is_valid(key: Tuple) -> bool:
|
|
434
|
-
"""
|
|
435
|
-
Returns whether this Interface is able to interface with the chunk type with a given identifier key,
|
|
436
|
-
generated by the format.
|
|
437
|
-
|
|
438
|
-
:param key: The key who's decodability needs to be checked.
|
|
439
|
-
:return: True if this interface can interface with the chunk version associated with the key, False otherwise.
|
|
440
|
-
"""
|
|
441
|
-
raise NotImplementedError
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import (
|
|
5
|
+
Tuple,
|
|
6
|
+
Any,
|
|
7
|
+
Union,
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Optional,
|
|
10
|
+
overload,
|
|
11
|
+
Type,
|
|
12
|
+
Sequence,
|
|
13
|
+
Callable,
|
|
14
|
+
)
|
|
15
|
+
from enum import Enum
|
|
16
|
+
|
|
17
|
+
from amulet.api.block_entity import BlockEntity
|
|
18
|
+
from amulet.api.entity import Entity
|
|
19
|
+
from amulet.api.data_types import AnyNDArray, VersionNumberAny, VersionIdentifierType
|
|
20
|
+
from amulet_nbt import (
|
|
21
|
+
AbstractBaseTag,
|
|
22
|
+
ListTag,
|
|
23
|
+
CompoundTag,
|
|
24
|
+
AnyNBT,
|
|
25
|
+
NamedTag,
|
|
26
|
+
StringTag,
|
|
27
|
+
IntTag,
|
|
28
|
+
IntArrayTag,
|
|
29
|
+
DoubleTag,
|
|
30
|
+
FloatTag,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from amulet.api.wrapper import Translator
|
|
35
|
+
from amulet.api.chunk import Chunk
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class EntityIDType(Enum):
|
|
39
|
+
int_id = 0
|
|
40
|
+
str_id = 1
|
|
41
|
+
namespace_str_id = 2
|
|
42
|
+
namespace_str_Id = 3
|
|
43
|
+
namespace_str_identifier = 4
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class EntityCoordType(Enum):
|
|
47
|
+
xyz_int = 0
|
|
48
|
+
Pos_list_float = 1
|
|
49
|
+
Pos_list_double = 2
|
|
50
|
+
Pos_list_int = 3
|
|
51
|
+
Pos_array_int = 4
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
PosTypeMap = {
|
|
55
|
+
3: EntityCoordType.Pos_list_int,
|
|
56
|
+
5: EntityCoordType.Pos_list_float,
|
|
57
|
+
6: EntityCoordType.Pos_list_double,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Interface(ABC):
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def decode(self, *args, **kwargs) -> Tuple["Chunk", AnyNDArray]:
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
|
|
66
|
+
def _decode_entity(
|
|
67
|
+
self,
|
|
68
|
+
nbt: NamedTag,
|
|
69
|
+
id_type: EntityIDType,
|
|
70
|
+
coord_type: EntityCoordType,
|
|
71
|
+
) -> Optional[Entity]:
|
|
72
|
+
entity = self._decode_base_entity(nbt, id_type, coord_type)
|
|
73
|
+
if entity is not None:
|
|
74
|
+
namespace, base_name, x, y, z, nbt = entity
|
|
75
|
+
return Entity(
|
|
76
|
+
namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def _decode_block_entity(
|
|
80
|
+
self,
|
|
81
|
+
nbt: NamedTag,
|
|
82
|
+
id_type: EntityIDType,
|
|
83
|
+
coord_type: EntityCoordType,
|
|
84
|
+
) -> Optional[BlockEntity]:
|
|
85
|
+
entity = self._decode_base_entity(nbt, id_type, coord_type)
|
|
86
|
+
if entity is not None:
|
|
87
|
+
namespace, base_name, x, y, z, nbt = entity
|
|
88
|
+
return BlockEntity(
|
|
89
|
+
namespace=namespace, base_name=base_name, x=x, y=y, z=z, nbt=nbt
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _decode_base_entity(
|
|
94
|
+
named_tag: NamedTag, id_type: EntityIDType, coord_type: EntityCoordType
|
|
95
|
+
) -> Optional[
|
|
96
|
+
Tuple[
|
|
97
|
+
str,
|
|
98
|
+
str,
|
|
99
|
+
Union[int, float],
|
|
100
|
+
Union[int, float],
|
|
101
|
+
Union[int, float],
|
|
102
|
+
NamedTag,
|
|
103
|
+
]
|
|
104
|
+
]:
|
|
105
|
+
if not (
|
|
106
|
+
isinstance(named_tag, NamedTag) and isinstance(named_tag.tag, CompoundTag)
|
|
107
|
+
):
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
nbt_tag = named_tag.tag
|
|
111
|
+
|
|
112
|
+
if id_type == EntityIDType.namespace_str_id:
|
|
113
|
+
entity_id = nbt_tag.pop("id", StringTag(""))
|
|
114
|
+
if (
|
|
115
|
+
not isinstance(entity_id, StringTag)
|
|
116
|
+
or entity_id.py_str == ""
|
|
117
|
+
or ":" not in entity_id.py_str
|
|
118
|
+
):
|
|
119
|
+
return
|
|
120
|
+
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
121
|
+
|
|
122
|
+
elif id_type == EntityIDType.namespace_str_Id:
|
|
123
|
+
entity_id = nbt_tag.pop("Id", StringTag(""))
|
|
124
|
+
if (
|
|
125
|
+
not isinstance(entity_id, StringTag)
|
|
126
|
+
or entity_id.py_str == ""
|
|
127
|
+
or ":" not in entity_id.py_str
|
|
128
|
+
):
|
|
129
|
+
return
|
|
130
|
+
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
131
|
+
|
|
132
|
+
elif id_type == EntityIDType.str_id:
|
|
133
|
+
entity_id = nbt_tag.pop("id", StringTag(""))
|
|
134
|
+
if not isinstance(entity_id, StringTag) or entity_id.py_str == "":
|
|
135
|
+
return
|
|
136
|
+
namespace = ""
|
|
137
|
+
base_name = entity_id.py_str
|
|
138
|
+
|
|
139
|
+
elif id_type in [EntityIDType.namespace_str_identifier, EntityIDType.int_id]:
|
|
140
|
+
if "identifier" in nbt_tag:
|
|
141
|
+
entity_id = nbt_tag.pop("identifier")
|
|
142
|
+
if (
|
|
143
|
+
not isinstance(entity_id, StringTag)
|
|
144
|
+
or entity_id.py_str == ""
|
|
145
|
+
or ":" not in entity_id.py_str
|
|
146
|
+
):
|
|
147
|
+
return
|
|
148
|
+
namespace, base_name = entity_id.py_str.split(":", 1)
|
|
149
|
+
elif "id" in nbt_tag:
|
|
150
|
+
entity_id = nbt_tag.pop("id")
|
|
151
|
+
if not isinstance(entity_id, IntTag):
|
|
152
|
+
return
|
|
153
|
+
namespace = ""
|
|
154
|
+
base_name = str(entity_id.py_int)
|
|
155
|
+
else:
|
|
156
|
+
return
|
|
157
|
+
else:
|
|
158
|
+
raise NotImplementedError(f"Entity id type {id_type}")
|
|
159
|
+
|
|
160
|
+
if coord_type in [
|
|
161
|
+
EntityCoordType.Pos_list_double,
|
|
162
|
+
EntityCoordType.Pos_list_float,
|
|
163
|
+
EntityCoordType.Pos_list_int,
|
|
164
|
+
]:
|
|
165
|
+
if "Pos" not in nbt_tag:
|
|
166
|
+
return
|
|
167
|
+
pos = nbt_tag.pop("Pos")
|
|
168
|
+
pos: ListTag
|
|
169
|
+
|
|
170
|
+
if (
|
|
171
|
+
not isinstance(pos, ListTag)
|
|
172
|
+
or len(pos) != 3
|
|
173
|
+
or PosTypeMap.get(pos.list_data_type) != coord_type
|
|
174
|
+
):
|
|
175
|
+
return
|
|
176
|
+
x, y, z = [c.py_data for c in pos]
|
|
177
|
+
elif coord_type == EntityCoordType.Pos_array_int:
|
|
178
|
+
if "Pos" not in nbt_tag:
|
|
179
|
+
return
|
|
180
|
+
pos = nbt_tag.pop("Pos")
|
|
181
|
+
pos: IntArrayTag
|
|
182
|
+
|
|
183
|
+
if not isinstance(pos, IntArrayTag) or len(pos) != 3:
|
|
184
|
+
return
|
|
185
|
+
x, y, z = pos
|
|
186
|
+
elif coord_type == EntityCoordType.xyz_int:
|
|
187
|
+
if not all(
|
|
188
|
+
c in nbt_tag and isinstance(nbt_tag[c], IntTag) for c in ("x", "y", "z")
|
|
189
|
+
):
|
|
190
|
+
return
|
|
191
|
+
x, y, z = [nbt_tag.pop(c).py_int for c in ("x", "y", "z")]
|
|
192
|
+
else:
|
|
193
|
+
raise NotImplementedError(f"Entity coord type {coord_type}")
|
|
194
|
+
|
|
195
|
+
return namespace, base_name, x, y, z, named_tag
|
|
196
|
+
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def encode(self, *args, **kwargs) -> Any:
|
|
199
|
+
"""
|
|
200
|
+
Take a version-specific chunk and encode it to raw data for the format to store.
|
|
201
|
+
"""
|
|
202
|
+
raise NotImplementedError
|
|
203
|
+
|
|
204
|
+
def _encode_entity(
|
|
205
|
+
self, entity: Entity, id_type: EntityIDType, coord_type: EntityCoordType
|
|
206
|
+
) -> Optional[NamedTag]:
|
|
207
|
+
return self._encode_base_entity(entity, id_type, coord_type)
|
|
208
|
+
|
|
209
|
+
def _encode_block_entity(
|
|
210
|
+
self, entity: BlockEntity, id_type: EntityIDType, coord_type: EntityCoordType
|
|
211
|
+
) -> Optional[NamedTag]:
|
|
212
|
+
return self._encode_base_entity(entity, id_type, coord_type)
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def _encode_base_entity(
|
|
216
|
+
entity: Union[Entity, BlockEntity],
|
|
217
|
+
id_type: EntityIDType,
|
|
218
|
+
coord_type: EntityCoordType,
|
|
219
|
+
) -> Optional[NamedTag]:
|
|
220
|
+
if not isinstance(entity.nbt, NamedTag) and isinstance(
|
|
221
|
+
entity.nbt.tag, CompoundTag
|
|
222
|
+
):
|
|
223
|
+
return
|
|
224
|
+
named_tag = entity.nbt
|
|
225
|
+
tag = named_tag.compound
|
|
226
|
+
|
|
227
|
+
if id_type == EntityIDType.namespace_str_id:
|
|
228
|
+
tag["id"] = StringTag(entity.namespaced_name)
|
|
229
|
+
elif id_type == EntityIDType.namespace_str_Id:
|
|
230
|
+
tag["Id"] = StringTag(entity.namespaced_name)
|
|
231
|
+
elif id_type == EntityIDType.namespace_str_identifier:
|
|
232
|
+
tag["identifier"] = StringTag(entity.namespaced_name)
|
|
233
|
+
elif id_type == EntityIDType.str_id:
|
|
234
|
+
tag["id"] = StringTag(entity.base_name)
|
|
235
|
+
elif id_type == EntityIDType.int_id:
|
|
236
|
+
if not entity.base_name.isnumeric():
|
|
237
|
+
return
|
|
238
|
+
tag["id"] = IntTag(int(entity.base_name))
|
|
239
|
+
else:
|
|
240
|
+
raise NotImplementedError(f"Entity id type {id_type}")
|
|
241
|
+
|
|
242
|
+
if coord_type == EntityCoordType.Pos_list_double:
|
|
243
|
+
tag["Pos"] = ListTag(
|
|
244
|
+
[
|
|
245
|
+
DoubleTag(float(entity.x)),
|
|
246
|
+
DoubleTag(float(entity.y)),
|
|
247
|
+
DoubleTag(float(entity.z)),
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
elif coord_type == EntityCoordType.Pos_list_float:
|
|
251
|
+
tag["Pos"] = ListTag(
|
|
252
|
+
[
|
|
253
|
+
FloatTag(float(entity.x)),
|
|
254
|
+
FloatTag(float(entity.y)),
|
|
255
|
+
FloatTag(float(entity.z)),
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
elif coord_type == EntityCoordType.Pos_list_int:
|
|
259
|
+
tag["Pos"] = ListTag(
|
|
260
|
+
[
|
|
261
|
+
IntTag(int(entity.x)),
|
|
262
|
+
IntTag(int(entity.y)),
|
|
263
|
+
IntTag(int(entity.z)),
|
|
264
|
+
]
|
|
265
|
+
)
|
|
266
|
+
elif coord_type == EntityCoordType.Pos_array_int:
|
|
267
|
+
tag["Pos"] = IntArrayTag([int(entity.x), int(entity.y), int(entity.z)])
|
|
268
|
+
elif coord_type == EntityCoordType.xyz_int:
|
|
269
|
+
tag["x"] = IntTag(int(entity.x))
|
|
270
|
+
tag["y"] = IntTag(int(entity.y))
|
|
271
|
+
tag["z"] = IntTag(int(entity.z))
|
|
272
|
+
else:
|
|
273
|
+
raise NotImplementedError(f"Entity coord type {coord_type}")
|
|
274
|
+
|
|
275
|
+
return named_tag
|
|
276
|
+
|
|
277
|
+
@overload
|
|
278
|
+
@staticmethod
|
|
279
|
+
def check_type(obj: CompoundTag, key: str, dtype: Type[AnyNBT]) -> bool:
|
|
280
|
+
...
|
|
281
|
+
|
|
282
|
+
@overload
|
|
283
|
+
@staticmethod
|
|
284
|
+
def check_type(obj: ListTag, key: int, dtype: Type[AnyNBT]) -> bool:
|
|
285
|
+
...
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
def check_type(
|
|
289
|
+
obj: Union[CompoundTag, ListTag], key: Union[str, int], dtype: Type[AnyNBT]
|
|
290
|
+
) -> bool:
|
|
291
|
+
"""Check the key exists and the type is correct."""
|
|
292
|
+
return key in obj and isinstance(obj[key], dtype)
|
|
293
|
+
|
|
294
|
+
@overload
|
|
295
|
+
def get_obj(
|
|
296
|
+
self,
|
|
297
|
+
obj: CompoundTag,
|
|
298
|
+
key: str,
|
|
299
|
+
dtype: Type[AnyNBT],
|
|
300
|
+
default: Optional[AnyNBT] = None,
|
|
301
|
+
) -> Optional[AnyNBT]:
|
|
302
|
+
...
|
|
303
|
+
|
|
304
|
+
@overload
|
|
305
|
+
def get_obj(
|
|
306
|
+
self,
|
|
307
|
+
obj: ListTag,
|
|
308
|
+
key: int,
|
|
309
|
+
dtype: Type[AnyNBT],
|
|
310
|
+
default: Optional[AnyNBT] = None,
|
|
311
|
+
) -> Optional[AnyNBT]:
|
|
312
|
+
...
|
|
313
|
+
|
|
314
|
+
def get_obj(
|
|
315
|
+
self, obj, key, dtype: Type[AnyNBT], default: Optional[AnyNBT] = None
|
|
316
|
+
) -> Optional[AnyNBT]:
|
|
317
|
+
"""Pop a key from a container object if it exists and the type is correct. Otherwise return default.
|
|
318
|
+
This works in much the same way as dict.get but uses default if the data type does not match.
|
|
319
|
+
|
|
320
|
+
:param obj: The CompoundTag to read from.
|
|
321
|
+
:param key: The key to use.
|
|
322
|
+
:param dtype: The expected data type.
|
|
323
|
+
:param default: The default value to use if the existing is not valid. If None will use dtype()
|
|
324
|
+
:return: The final value in the key.
|
|
325
|
+
"""
|
|
326
|
+
if key in obj:
|
|
327
|
+
if isinstance(obj[key], dtype):
|
|
328
|
+
# if it exists and is correct
|
|
329
|
+
return obj.pop(key)
|
|
330
|
+
if default is None:
|
|
331
|
+
return dtype()
|
|
332
|
+
return default
|
|
333
|
+
|
|
334
|
+
def get_nested_obj(
|
|
335
|
+
self,
|
|
336
|
+
obj: Union[CompoundTag, ListTag],
|
|
337
|
+
path: Sequence[ # The path to the object.
|
|
338
|
+
Tuple[Union[str, int], Type[AbstractBaseTag]],
|
|
339
|
+
],
|
|
340
|
+
default: Union[None, AnyNBT, Callable[[], Any]] = None,
|
|
341
|
+
*,
|
|
342
|
+
pop_last=False,
|
|
343
|
+
):
|
|
344
|
+
"""
|
|
345
|
+
Get an object from a nested NBT structure
|
|
346
|
+
|
|
347
|
+
:param obj: The root NBT object
|
|
348
|
+
:param path: The path to the desired object (key, dtype)
|
|
349
|
+
:param default: The default value to use if the existing is not valid. If default is callable then return the called result.
|
|
350
|
+
:param pop_last: If true the last key will be popped
|
|
351
|
+
:return:
|
|
352
|
+
"""
|
|
353
|
+
try:
|
|
354
|
+
last_index = len(path) - 1
|
|
355
|
+
for i, (key, dtype) in enumerate(path):
|
|
356
|
+
if i == last_index and pop_last:
|
|
357
|
+
obj = obj.pop(key)
|
|
358
|
+
else:
|
|
359
|
+
obj = obj[key]
|
|
360
|
+
if dtype is not obj.__class__:
|
|
361
|
+
raise TypeError
|
|
362
|
+
except (KeyError, IndexError, TypeError):
|
|
363
|
+
if default is None or isinstance(default, AbstractBaseTag):
|
|
364
|
+
return default
|
|
365
|
+
elif callable(default):
|
|
366
|
+
return default()
|
|
367
|
+
else:
|
|
368
|
+
raise TypeError(
|
|
369
|
+
"default must be None, an NBT instance or an NBT class."
|
|
370
|
+
)
|
|
371
|
+
else:
|
|
372
|
+
return obj
|
|
373
|
+
|
|
374
|
+
@staticmethod
|
|
375
|
+
def set_obj(
|
|
376
|
+
obj: CompoundTag,
|
|
377
|
+
key: str,
|
|
378
|
+
dtype: Type[AnyNBT],
|
|
379
|
+
default: Union[None, AnyNBT, Callable[[], Any]] = None,
|
|
380
|
+
path: Sequence[str] = (), # The path to the object.
|
|
381
|
+
*,
|
|
382
|
+
setdefault=False,
|
|
383
|
+
) -> AnyNBT:
|
|
384
|
+
"""
|
|
385
|
+
Works like setdefualt on a dictionary but works with an optional nested path.
|
|
386
|
+
|
|
387
|
+
:param obj: The compound tag to get the data from
|
|
388
|
+
:param key: The key to setdefault
|
|
389
|
+
:param dtype: The dtype that the key must be
|
|
390
|
+
:param default: The default value to set if it does not exist or the type is wrong
|
|
391
|
+
:param path: Optional path to the nested compound.
|
|
392
|
+
:param setdefault: If True will behave like setdefault. If False will replace existing data.
|
|
393
|
+
:return: The data at the path
|
|
394
|
+
"""
|
|
395
|
+
for path_key in path:
|
|
396
|
+
obj_ = obj.get(path_key, None)
|
|
397
|
+
if not isinstance(obj_, CompoundTag):
|
|
398
|
+
# if it does not exist or the type is wrong then create it
|
|
399
|
+
obj_ = obj[path_key] = CompoundTag()
|
|
400
|
+
obj = obj_
|
|
401
|
+
obj_ = obj.get(key, None)
|
|
402
|
+
if not setdefault or not isinstance(obj_, dtype):
|
|
403
|
+
# if it does not exist or the type is wrong then create it
|
|
404
|
+
if default is None:
|
|
405
|
+
obj_ = dtype()
|
|
406
|
+
elif isinstance(default, AbstractBaseTag):
|
|
407
|
+
obj_ = default
|
|
408
|
+
elif callable(default):
|
|
409
|
+
obj_ = default()
|
|
410
|
+
else:
|
|
411
|
+
raise TypeError(
|
|
412
|
+
"default must be None, an NBT instance or an NBT class."
|
|
413
|
+
)
|
|
414
|
+
obj[key] = obj_
|
|
415
|
+
return obj_
|
|
416
|
+
|
|
417
|
+
@abstractmethod
|
|
418
|
+
def get_translator(
|
|
419
|
+
self,
|
|
420
|
+
max_world_version: VersionIdentifierType,
|
|
421
|
+
data: Any = None,
|
|
422
|
+
) -> Tuple["Translator", VersionNumberAny]:
|
|
423
|
+
"""
|
|
424
|
+
Get the Translator class for the requested version.
|
|
425
|
+
:param max_world_version: The game version the world was last opened in. Version number tuple or data version number.
|
|
426
|
+
:param data: Optional data to get translator based on chunk version rather than world version
|
|
427
|
+
:return: Tuple[Translator, version number for PyMCTranslate to use]
|
|
428
|
+
"""
|
|
429
|
+
raise NotImplementedError
|
|
430
|
+
|
|
431
|
+
@staticmethod
|
|
432
|
+
@abstractmethod
|
|
433
|
+
def is_valid(key: Tuple) -> bool:
|
|
434
|
+
"""
|
|
435
|
+
Returns whether this Interface is able to interface with the chunk type with a given identifier key,
|
|
436
|
+
generated by the format.
|
|
437
|
+
|
|
438
|
+
:param key: The key who's decodability needs to be checked.
|
|
439
|
+
:return: True if this interface can interface with the chunk version associated with the key, False otherwise.
|
|
440
|
+
"""
|
|
441
|
+
raise NotImplementedError
|