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,419 +1,385 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import struct
|
|
4
|
-
from typing import (
|
|
5
|
-
Dict,
|
|
6
|
-
Set,
|
|
7
|
-
Optional,
|
|
8
|
-
List,
|
|
9
|
-
TYPE_CHECKING,
|
|
10
|
-
Tuple,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
from amulet.api.
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
self.
|
|
103
|
-
|
|
104
|
-
self.
|
|
105
|
-
|
|
106
|
-
self.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
self
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
else:
|
|
387
|
-
batch[key] = val
|
|
388
|
-
if batch:
|
|
389
|
-
self._db.putBatch(batch)
|
|
390
|
-
|
|
391
|
-
def delete_chunk(self, cx: int, cz: int, dimension: "Dimension"):
|
|
392
|
-
if dimension not in self._dimension_name_map:
|
|
393
|
-
return # dimension does not exists so chunk cannot
|
|
394
|
-
|
|
395
|
-
internal_dimension = self._dimension_name_map[dimension]
|
|
396
|
-
if not self._has_chunk(cx, cz, internal_dimension):
|
|
397
|
-
return # chunk does not exists
|
|
398
|
-
|
|
399
|
-
prefix = self._get_key(cx, cz, internal_dimension)
|
|
400
|
-
prefix_len = len(prefix)
|
|
401
|
-
iter_end = prefix + b"\xff\xff\xff\xff"
|
|
402
|
-
keys = []
|
|
403
|
-
for key, _ in self._db.iterate(prefix, iter_end):
|
|
404
|
-
if key[:prefix_len] == prefix and len(key) <= prefix_len + 2:
|
|
405
|
-
keys.append(key)
|
|
406
|
-
|
|
407
|
-
try:
|
|
408
|
-
digp = self._db.get(b"digp" + prefix)
|
|
409
|
-
except KeyError:
|
|
410
|
-
pass
|
|
411
|
-
else:
|
|
412
|
-
self._db.delete(b"digp" + prefix)
|
|
413
|
-
for i in range(0, len(digp) // 8 * 8, 8):
|
|
414
|
-
actor_key = b"actorprefix" + digp[i : i + 8]
|
|
415
|
-
self._db.delete(actor_key)
|
|
416
|
-
|
|
417
|
-
self._levels[internal_dimension].remove((cx, cz))
|
|
418
|
-
for key in keys:
|
|
419
|
-
self._db.delete(key)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import struct
|
|
4
|
+
from typing import (
|
|
5
|
+
Dict,
|
|
6
|
+
Set,
|
|
7
|
+
Optional,
|
|
8
|
+
List,
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
Tuple,
|
|
11
|
+
)
|
|
12
|
+
from threading import RLock
|
|
13
|
+
import logging
|
|
14
|
+
from contextlib import suppress
|
|
15
|
+
|
|
16
|
+
from amulet_nbt import (
|
|
17
|
+
NamedTag,
|
|
18
|
+
LongTag,
|
|
19
|
+
StringTag,
|
|
20
|
+
CompoundTag,
|
|
21
|
+
NBTLoadError,
|
|
22
|
+
load as load_nbt,
|
|
23
|
+
utf8_escape_decoder,
|
|
24
|
+
utf8_escape_encoder,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from amulet.api.errors import ChunkDoesNotExist
|
|
28
|
+
from amulet.api.data_types import ChunkCoordinates
|
|
29
|
+
from leveldb import LevelDB
|
|
30
|
+
from .chunk import ChunkData
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from .format import LevelDBFormat
|
|
34
|
+
|
|
35
|
+
log = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
InternalDimension = Optional[int]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ActorCounter:
|
|
41
|
+
_lock: RLock
|
|
42
|
+
_session: int
|
|
43
|
+
_count: int
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self._lock = RLock()
|
|
47
|
+
self._session = -1
|
|
48
|
+
self._count = 0
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_level(cls, level: LevelDBFormat):
|
|
52
|
+
session = level.root_tag.compound.get_long(
|
|
53
|
+
"worldStartCount", LongTag(0xFFFFFFFF)
|
|
54
|
+
).py_int
|
|
55
|
+
# for some reason this is a signed int stored in a signed long. Manually apply the sign correctly
|
|
56
|
+
session -= (session & 0x80000000) << 1
|
|
57
|
+
|
|
58
|
+
# create the counter object and set the session
|
|
59
|
+
counter = ActorCounter()
|
|
60
|
+
counter._session = session
|
|
61
|
+
|
|
62
|
+
# increment and write back so there are no conflicts
|
|
63
|
+
session -= 1
|
|
64
|
+
if session < 0:
|
|
65
|
+
session += 0x100000000
|
|
66
|
+
level.root_tag.compound["worldStartCount"] = LongTag(session)
|
|
67
|
+
level.root_tag.save()
|
|
68
|
+
|
|
69
|
+
return counter
|
|
70
|
+
|
|
71
|
+
def next(self) -> Tuple[int, int]:
|
|
72
|
+
"""
|
|
73
|
+
Get the next unique session id and actor counter.
|
|
74
|
+
Session id is usually negative
|
|
75
|
+
|
|
76
|
+
:return: Tuple[session id, actor id]
|
|
77
|
+
"""
|
|
78
|
+
with self._lock:
|
|
79
|
+
count = self._count
|
|
80
|
+
self._count += 1
|
|
81
|
+
return self._session, count
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class LevelDBDimensionManager:
|
|
85
|
+
# tag_ids = {45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 118}
|
|
86
|
+
|
|
87
|
+
# A borrowed reference to the leveldb
|
|
88
|
+
_db: LevelDB
|
|
89
|
+
# A class to keep track of unique actor ids
|
|
90
|
+
_actor_counter: Optional[ActorCounter]
|
|
91
|
+
|
|
92
|
+
def __init__(self, level: LevelDBFormat):
|
|
93
|
+
"""
|
|
94
|
+
:param level: The leveldb format to read data from
|
|
95
|
+
"""
|
|
96
|
+
self._db = level.level_db
|
|
97
|
+
self._actor_counter = ActorCounter.from_level(level)
|
|
98
|
+
# self._levels format Dict[level, Dict[Tuple[cx, cz], List[Tuple[full_key, key_extension]]]]
|
|
99
|
+
self._levels: Dict[InternalDimension, Set[ChunkCoordinates]] = {}
|
|
100
|
+
self._lock = RLock()
|
|
101
|
+
|
|
102
|
+
self.register_dimension(None) # overworld
|
|
103
|
+
self.register_dimension(1) # the nether
|
|
104
|
+
self.register_dimension(2) # the end
|
|
105
|
+
|
|
106
|
+
for key in self._db.keys():
|
|
107
|
+
if 9 <= len(key) <= 10 and key[8] in [44, 118]: # "," "v"
|
|
108
|
+
self._add_chunk(key)
|
|
109
|
+
|
|
110
|
+
elif 13 <= len(key) <= 14 and key[12] in [44, 118]: # "," "v"
|
|
111
|
+
self._add_chunk(key, has_level=True)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def dimensions(self) -> List[InternalDimension]:
|
|
115
|
+
"""A list of all the levels contained in the world"""
|
|
116
|
+
return list(self._levels)
|
|
117
|
+
|
|
118
|
+
def register_dimension(self, dimension: InternalDimension):
|
|
119
|
+
"""
|
|
120
|
+
Register a new dimension.
|
|
121
|
+
|
|
122
|
+
:param dimension: The internal representation of the dimension
|
|
123
|
+
:return:
|
|
124
|
+
"""
|
|
125
|
+
with self._lock:
|
|
126
|
+
if dimension not in self._levels:
|
|
127
|
+
self._levels[dimension] = set()
|
|
128
|
+
|
|
129
|
+
def all_chunk_coords(self, dimension: InternalDimension) -> Set[ChunkCoordinates]:
|
|
130
|
+
if dimension in self._levels:
|
|
131
|
+
return self._levels[dimension]
|
|
132
|
+
else:
|
|
133
|
+
return set()
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def _get_key(cx: int, cz: int, dimension: InternalDimension) -> bytes:
|
|
137
|
+
if dimension is None:
|
|
138
|
+
return struct.pack("<ii", cx, cz)
|
|
139
|
+
else:
|
|
140
|
+
return struct.pack("<iii", cx, cz, dimension)
|
|
141
|
+
|
|
142
|
+
def has_chunk(self, cx: int, cz: int, dimension: InternalDimension) -> bool:
|
|
143
|
+
return dimension in self._levels and (cx, cz) in self._levels[dimension]
|
|
144
|
+
|
|
145
|
+
def _add_chunk(self, key_: bytes, has_level: bool = False):
|
|
146
|
+
if has_level:
|
|
147
|
+
cx, cz, level = struct.unpack("<iii", key_[:12])
|
|
148
|
+
else:
|
|
149
|
+
cx, cz = struct.unpack("<ii", key_[:8])
|
|
150
|
+
level = None
|
|
151
|
+
if level not in self._levels:
|
|
152
|
+
self.register_dimension(level)
|
|
153
|
+
self._levels[level].add((cx, cz))
|
|
154
|
+
|
|
155
|
+
def get_chunk_data(
|
|
156
|
+
self, cx: int, cz: int, dimension: InternalDimension
|
|
157
|
+
) -> ChunkData:
|
|
158
|
+
"""Get a dictionary of chunk key extension in bytes to the raw data in the key.
|
|
159
|
+
chunk key extension are the character(s) after <cx><cz>[level] in the key
|
|
160
|
+
Will raise ChunkDoesNotExist if the chunk does not exist
|
|
161
|
+
"""
|
|
162
|
+
if self.has_chunk(cx, cz, dimension):
|
|
163
|
+
prefix = self._get_key(cx, cz, dimension)
|
|
164
|
+
prefix_len = len(prefix)
|
|
165
|
+
iter_end = prefix + b"\xff\xff\xff\xff"
|
|
166
|
+
|
|
167
|
+
chunk_data = ChunkData()
|
|
168
|
+
for key, val in self._db.iterate(prefix, iter_end):
|
|
169
|
+
if key[:prefix_len] == prefix and len(key) <= prefix_len + 2:
|
|
170
|
+
chunk_data[key[prefix_len:]] = val
|
|
171
|
+
|
|
172
|
+
with suppress(KeyError):
|
|
173
|
+
digp_key = b"digp" + prefix
|
|
174
|
+
digp = self._db.get(digp_key)
|
|
175
|
+
chunk_data[
|
|
176
|
+
b"digp"
|
|
177
|
+
] = b"" # The presence of this key signals to the put method that this should be created and written
|
|
178
|
+
for i in range(0, (len(digp) // 8) * 8, 8):
|
|
179
|
+
actor_key = b"actorprefix" + digp[i : i + 8]
|
|
180
|
+
try:
|
|
181
|
+
actor_bytes = self._db.get(actor_key)
|
|
182
|
+
actor = load_nbt(
|
|
183
|
+
actor_bytes,
|
|
184
|
+
little_endian=True,
|
|
185
|
+
string_decoder=utf8_escape_decoder,
|
|
186
|
+
)
|
|
187
|
+
actor_tag = actor.compound
|
|
188
|
+
except KeyError:
|
|
189
|
+
log.error(f"Could not find actor {actor_key}. Skipping.")
|
|
190
|
+
except NBTLoadError:
|
|
191
|
+
log.error(f"Failed to parse actor {actor_key}. Skipping.")
|
|
192
|
+
else:
|
|
193
|
+
actor_tag.pop("UniqueID", None)
|
|
194
|
+
internal_components = actor_tag.setdefault(
|
|
195
|
+
"internalComponents",
|
|
196
|
+
CompoundTag(
|
|
197
|
+
EntityStorageKeyComponent=CompoundTag(
|
|
198
|
+
StorageKey=StringTag()
|
|
199
|
+
)
|
|
200
|
+
),
|
|
201
|
+
) # 717
|
|
202
|
+
if (
|
|
203
|
+
isinstance(internal_components, CompoundTag)
|
|
204
|
+
and internal_components
|
|
205
|
+
):
|
|
206
|
+
if "EntityStorageKeyComponent" in internal_components:
|
|
207
|
+
# it is an entity
|
|
208
|
+
entity_component = internal_components[
|
|
209
|
+
"EntityStorageKeyComponent"
|
|
210
|
+
]
|
|
211
|
+
if isinstance(entity_component, CompoundTag):
|
|
212
|
+
# delete the storage key component
|
|
213
|
+
if isinstance(
|
|
214
|
+
entity_component.get("StorageKey"), StringTag
|
|
215
|
+
):
|
|
216
|
+
del entity_component["StorageKey"]
|
|
217
|
+
# if there is no other data then delete internalComponents
|
|
218
|
+
if (
|
|
219
|
+
len(entity_component) == 0
|
|
220
|
+
and len(internal_components) == 1
|
|
221
|
+
):
|
|
222
|
+
del actor_tag["internalComponents"]
|
|
223
|
+
else:
|
|
224
|
+
log.warning(
|
|
225
|
+
f"Extra components found {repr(entity_component)}"
|
|
226
|
+
)
|
|
227
|
+
else:
|
|
228
|
+
log.warning(
|
|
229
|
+
f"Unrecognised EntityStorageKeyComponent type {repr(entity_component)}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
chunk_data.entity_actor.append(actor)
|
|
233
|
+
else:
|
|
234
|
+
# it is an unknown actor
|
|
235
|
+
log.warning(
|
|
236
|
+
f"Actor {actor_key} has an unknown format. Please report this to a developer {repr(internal_components)}"
|
|
237
|
+
)
|
|
238
|
+
for k, v in internal_components.items():
|
|
239
|
+
if isinstance(v, CompoundTag) and isinstance(
|
|
240
|
+
v.get("StorageKey"), StringTag
|
|
241
|
+
):
|
|
242
|
+
v["StorageKey"] = StringTag()
|
|
243
|
+
chunk_data.unknown_actor.append(actor)
|
|
244
|
+
else:
|
|
245
|
+
log.error(
|
|
246
|
+
f"internalComponents was not valid for actor {actor_key}. Skipping."
|
|
247
|
+
)
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
return chunk_data
|
|
251
|
+
else:
|
|
252
|
+
raise ChunkDoesNotExist
|
|
253
|
+
|
|
254
|
+
def put_chunk_data(
|
|
255
|
+
self,
|
|
256
|
+
cx: int,
|
|
257
|
+
cz: int,
|
|
258
|
+
chunk_data: ChunkData,
|
|
259
|
+
dimension: InternalDimension,
|
|
260
|
+
):
|
|
261
|
+
"""pass data to the region file class"""
|
|
262
|
+
# get the region key
|
|
263
|
+
self._levels[dimension].add((cx, cz))
|
|
264
|
+
key_prefix = self._get_key(cx, cz, dimension)
|
|
265
|
+
|
|
266
|
+
batch = {}
|
|
267
|
+
|
|
268
|
+
if b"digp" in chunk_data:
|
|
269
|
+
# if writing the digp key we need to delete all actors pointed to by the old digp key otherwise there will be memory leaks
|
|
270
|
+
digp_key = b"digp" + key_prefix
|
|
271
|
+
try:
|
|
272
|
+
old_digp = self._db.get(digp_key)
|
|
273
|
+
except KeyError:
|
|
274
|
+
pass
|
|
275
|
+
else:
|
|
276
|
+
for i in range(0, len(old_digp) // 8 * 8, 8):
|
|
277
|
+
actor_key = b"actorprefix" + old_digp[i : i + 8]
|
|
278
|
+
self._db.delete(actor_key)
|
|
279
|
+
|
|
280
|
+
digp = []
|
|
281
|
+
|
|
282
|
+
def add_actor(actor: NamedTag, is_entity: bool):
|
|
283
|
+
if not (
|
|
284
|
+
isinstance(actor, NamedTag) and isinstance(actor.tag, CompoundTag)
|
|
285
|
+
):
|
|
286
|
+
log.error(f"Actor must be a NamedTag[Compound]")
|
|
287
|
+
return
|
|
288
|
+
actor_tag = actor.compound
|
|
289
|
+
internal_components = actor_tag.setdefault(
|
|
290
|
+
"internalComponents", CompoundTag()
|
|
291
|
+
)
|
|
292
|
+
if not isinstance(internal_components, CompoundTag):
|
|
293
|
+
log.error(
|
|
294
|
+
f"Invalid internalComponents value. Must be Compound. Skipping. {repr(internal_components)}"
|
|
295
|
+
)
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
if is_entity:
|
|
299
|
+
entity_storage = internal_components.setdefault(
|
|
300
|
+
"EntityStorageKeyComponent", CompoundTag()
|
|
301
|
+
)
|
|
302
|
+
if not isinstance(entity_storage, CompoundTag):
|
|
303
|
+
log.error(
|
|
304
|
+
f"Invalid EntityStorageKeyComponent value. Must be Compound. Skipping. {repr(entity_storage)}"
|
|
305
|
+
)
|
|
306
|
+
return
|
|
307
|
+
storages = [entity_storage]
|
|
308
|
+
else:
|
|
309
|
+
storages = []
|
|
310
|
+
for storage in internal_components.values():
|
|
311
|
+
if (
|
|
312
|
+
isinstance(storage, CompoundTag)
|
|
313
|
+
and storage.get("StorageKey") == StringTag()
|
|
314
|
+
):
|
|
315
|
+
storages.append(storage)
|
|
316
|
+
if not storages:
|
|
317
|
+
log.error(
|
|
318
|
+
f"No valid StorageKeyComponent to write in for unknown actor {internal_components}"
|
|
319
|
+
)
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
session, uid = self._actor_counter.next()
|
|
323
|
+
# session is already negative
|
|
324
|
+
key = struct.pack(">ii", -session, uid)
|
|
325
|
+
# b'\x00\x00\x00\x01\x00\x00\x00\x0c' 1, 12
|
|
326
|
+
for storage in storages:
|
|
327
|
+
storage["StorageKey"] = StringTag(utf8_escape_decoder(key))
|
|
328
|
+
# -4294967284 ">q" b'\xff\xff\xff\xff\x00\x00\x00\x0c' ">ii" -1, 12
|
|
329
|
+
actor_tag["UniqueID"] = LongTag(
|
|
330
|
+
struct.unpack(">q", struct.pack(">ii", session, uid))[0]
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
batch[b"actorprefix" + key] = actor.save_to(
|
|
334
|
+
little_endian=True,
|
|
335
|
+
compressed=False,
|
|
336
|
+
string_encoder=utf8_escape_encoder,
|
|
337
|
+
)
|
|
338
|
+
digp.append(key)
|
|
339
|
+
|
|
340
|
+
for actor_ in chunk_data.entity_actor:
|
|
341
|
+
add_actor(actor_, True)
|
|
342
|
+
|
|
343
|
+
for actor_ in chunk_data.unknown_actor:
|
|
344
|
+
add_actor(actor_, False)
|
|
345
|
+
|
|
346
|
+
del chunk_data[b"digp"]
|
|
347
|
+
batch[digp_key] = b"".join(digp)
|
|
348
|
+
|
|
349
|
+
for key, val in chunk_data.items():
|
|
350
|
+
key = key_prefix + key
|
|
351
|
+
if val is None:
|
|
352
|
+
self._db.delete(key)
|
|
353
|
+
else:
|
|
354
|
+
batch[key] = val
|
|
355
|
+
if batch:
|
|
356
|
+
self._db.putBatch(batch)
|
|
357
|
+
|
|
358
|
+
def delete_chunk(self, cx: int, cz: int, dimension: InternalDimension):
|
|
359
|
+
if dimension not in self._levels:
|
|
360
|
+
return # dimension does not exists so chunk cannot
|
|
361
|
+
|
|
362
|
+
if not self.has_chunk(cx, cz, dimension):
|
|
363
|
+
return # chunk does not exists
|
|
364
|
+
|
|
365
|
+
prefix = self._get_key(cx, cz, dimension)
|
|
366
|
+
prefix_len = len(prefix)
|
|
367
|
+
iter_end = prefix + b"\xff\xff\xff\xff"
|
|
368
|
+
keys = []
|
|
369
|
+
for key, _ in self._db.iterate(prefix, iter_end):
|
|
370
|
+
if key[:prefix_len] == prefix and len(key) <= prefix_len + 2:
|
|
371
|
+
keys.append(key)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
digp = self._db.get(b"digp" + prefix)
|
|
375
|
+
except KeyError:
|
|
376
|
+
pass
|
|
377
|
+
else:
|
|
378
|
+
self._db.delete(b"digp" + prefix)
|
|
379
|
+
for i in range(0, len(digp) // 8 * 8, 8):
|
|
380
|
+
actor_key = b"actorprefix" + digp[i : i + 8]
|
|
381
|
+
self._db.delete(actor_key)
|
|
382
|
+
|
|
383
|
+
self._levels[dimension].remove((cx, cz))
|
|
384
|
+
for key in keys:
|
|
385
|
+
self._db.delete(key)
|