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
amulet/api/block_entity.py
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import copy
|
|
3
|
-
import numpy
|
|
4
|
-
|
|
5
|
-
from amulet_nbt import NamedTag
|
|
6
|
-
from amulet.api.data_types import BlockCoordinates
|
|
7
|
-
from .abstract_base_entity import AbstractBaseEntity
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class BlockEntity(AbstractBaseEntity):
|
|
11
|
-
"""
|
|
12
|
-
A class to contain all the data to define a BlockEntity.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
obj_name = "BlockEntity"
|
|
16
|
-
coord_types = (int, numpy.integer)
|
|
17
|
-
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
namespace: str,
|
|
21
|
-
base_name: str,
|
|
22
|
-
x: int,
|
|
23
|
-
y: int,
|
|
24
|
-
z: int,
|
|
25
|
-
nbt: NamedTag,
|
|
26
|
-
):
|
|
27
|
-
"""
|
|
28
|
-
Constructs a :class:`BlockEntity` instance.
|
|
29
|
-
|
|
30
|
-
:param namespace: The namespace of the block entity eg "minecraft"
|
|
31
|
-
:param base_name: The base name of the block entity eg "chest"
|
|
32
|
-
:param x: The x coordinate of the block entity
|
|
33
|
-
:param y: The y coordinate of the block entity
|
|
34
|
-
:param z: The z coordinate of the block entity
|
|
35
|
-
:param nbt: The NBT stored with the block entity
|
|
36
|
-
"""
|
|
37
|
-
super().__init__(namespace, base_name, x, y, z, nbt)
|
|
38
|
-
|
|
39
|
-
@property
|
|
40
|
-
def x(self) -> int:
|
|
41
|
-
"""The x location of the BlockEntity. Read Only"""
|
|
42
|
-
return self._x
|
|
43
|
-
|
|
44
|
-
@property
|
|
45
|
-
def y(self) -> int:
|
|
46
|
-
"""The y location of the BlockEntity. Read Only"""
|
|
47
|
-
return self._y
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def z(self) -> int:
|
|
51
|
-
"""The z location of the BlockEntity. Read Only"""
|
|
52
|
-
return self._z
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def location(self) -> BlockCoordinates:
|
|
56
|
-
"""The location of the BlockEntity. Read Only"""
|
|
57
|
-
return self._x, self._y, self._z
|
|
58
|
-
|
|
59
|
-
def new_at_location(self, x: int, y: int, z: int) -> BlockEntity:
|
|
60
|
-
"""
|
|
61
|
-
Creates a copy of this BlockEntity at a new location
|
|
62
|
-
BlockEntities are stored in the chunk based on their location so location cannot be mutable
|
|
63
|
-
"""
|
|
64
|
-
return BlockEntity(
|
|
65
|
-
self._namespace,
|
|
66
|
-
self._base_name,
|
|
67
|
-
x,
|
|
68
|
-
y,
|
|
69
|
-
z,
|
|
70
|
-
copy.deepcopy(self._nbt),
|
|
71
|
-
)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import copy
|
|
3
|
+
import numpy
|
|
4
|
+
|
|
5
|
+
from amulet_nbt import NamedTag
|
|
6
|
+
from amulet.api.data_types import BlockCoordinates
|
|
7
|
+
from .abstract_base_entity import AbstractBaseEntity
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlockEntity(AbstractBaseEntity):
|
|
11
|
+
"""
|
|
12
|
+
A class to contain all the data to define a BlockEntity.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
obj_name = "BlockEntity"
|
|
16
|
+
coord_types = (int, numpy.integer)
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
namespace: str,
|
|
21
|
+
base_name: str,
|
|
22
|
+
x: int,
|
|
23
|
+
y: int,
|
|
24
|
+
z: int,
|
|
25
|
+
nbt: NamedTag,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Constructs a :class:`BlockEntity` instance.
|
|
29
|
+
|
|
30
|
+
:param namespace: The namespace of the block entity eg "minecraft"
|
|
31
|
+
:param base_name: The base name of the block entity eg "chest"
|
|
32
|
+
:param x: The x coordinate of the block entity
|
|
33
|
+
:param y: The y coordinate of the block entity
|
|
34
|
+
:param z: The z coordinate of the block entity
|
|
35
|
+
:param nbt: The NBT stored with the block entity
|
|
36
|
+
"""
|
|
37
|
+
super().__init__(namespace, base_name, x, y, z, nbt)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def x(self) -> int:
|
|
41
|
+
"""The x location of the BlockEntity. Read Only"""
|
|
42
|
+
return self._x
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def y(self) -> int:
|
|
46
|
+
"""The y location of the BlockEntity. Read Only"""
|
|
47
|
+
return self._y
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def z(self) -> int:
|
|
51
|
+
"""The z location of the BlockEntity. Read Only"""
|
|
52
|
+
return self._z
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def location(self) -> BlockCoordinates:
|
|
56
|
+
"""The location of the BlockEntity. Read Only"""
|
|
57
|
+
return self._x, self._y, self._z
|
|
58
|
+
|
|
59
|
+
def new_at_location(self, x: int, y: int, z: int) -> BlockEntity:
|
|
60
|
+
"""
|
|
61
|
+
Creates a copy of this BlockEntity at a new location
|
|
62
|
+
BlockEntities are stored in the chunk based on their location so location cannot be mutable
|
|
63
|
+
"""
|
|
64
|
+
return BlockEntity(
|
|
65
|
+
self._namespace,
|
|
66
|
+
self._base_name,
|
|
67
|
+
x,
|
|
68
|
+
y,
|
|
69
|
+
z,
|
|
70
|
+
copy.deepcopy(self._nbt),
|
|
71
|
+
)
|
amulet/api/cache.py
CHANGED
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
# A cache for objects implemented using leveldb for speed.
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
import time
|
|
5
|
-
import logging
|
|
6
|
-
import glob
|
|
7
|
-
import re
|
|
8
|
-
import tempfile
|
|
9
|
-
|
|
10
|
-
import portalocker
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _get_cache_dir() -> str:
|
|
14
|
-
return os.environ.get("AMULET_LEVEL_CACHE_DIR", None) or os.path.join(
|
|
15
|
-
os.environ.get("CACHE_DIR"), "level_data"
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
log = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _clear_legacy_cache():
|
|
23
|
-
legacy_cache_dir = _get_cache_dir()
|
|
24
|
-
world_temp_dir = os.path.join(legacy_cache_dir, "world_temp")
|
|
25
|
-
if not os.path.isdir(world_temp_dir):
|
|
26
|
-
return
|
|
27
|
-
paths = [
|
|
28
|
-
os.path.join(legacy_cache_dir, t)
|
|
29
|
-
for t in os.listdir(world_temp_dir)
|
|
30
|
-
if t.isnumeric() and int(t) < (time.time() - 7 * 24 * 3600)
|
|
31
|
-
]
|
|
32
|
-
if paths:
|
|
33
|
-
log.info("Removing legacy cache.")
|
|
34
|
-
|
|
35
|
-
# remove all cache directories that were created before a week ago
|
|
36
|
-
# Sometimes if the program is stopped or it crashes the cleanup won't happen
|
|
37
|
-
for path in paths:
|
|
38
|
-
shutil.rmtree(path, ignore_errors=True)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
_clear_legacy_cache()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
TempPattern = re.compile(r"amulettmp.*?-(?P<time>\d+)")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _clear_temp_dirs():
|
|
48
|
-
"""
|
|
49
|
-
Try and delete historic temporary directories.
|
|
50
|
-
If things went very wrong in past sessions temporary directories may still exist.
|
|
51
|
-
"""
|
|
52
|
-
temp_dir = tempfile.gettempdir()
|
|
53
|
-
for path in glob.glob(os.path.join(glob.escape(temp_dir), "amulettmp*")):
|
|
54
|
-
name = os.path.relpath(path, temp_dir)
|
|
55
|
-
match = TempPattern.fullmatch(name)
|
|
56
|
-
if match and int(match.group("time")) < (time.time() - 7 * 24 * 3600):
|
|
57
|
-
lock_path = os.path.join(path, "lock")
|
|
58
|
-
if os.path.exists(lock_path):
|
|
59
|
-
with open(lock_path) as lock:
|
|
60
|
-
# make sure it is not locked by another process
|
|
61
|
-
try:
|
|
62
|
-
portalocker.lock(lock, portalocker.LockFlags.EXCLUSIVE)
|
|
63
|
-
except:
|
|
64
|
-
continue
|
|
65
|
-
else:
|
|
66
|
-
portalocker.unlock(lock)
|
|
67
|
-
shutil.rmtree(path, ignore_errors=True)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
_clear_temp_dirs()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class TempDir(str):
|
|
74
|
-
"""
|
|
75
|
-
A temporary directory to do with as you wish.
|
|
76
|
-
|
|
77
|
-
>>> t = TempDir()
|
|
78
|
-
>>> path = os.path.join(t, "your_file.txt") # TempDir is a subclass of str
|
|
79
|
-
>>> # make sure all files in the temporary directory are closed before releasing or closing this object.
|
|
80
|
-
>>> # The temporary directory will be deleted when the last reference to `t` is lost or when `t.close()` is called
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
def __new__(cls):
|
|
84
|
-
cache_dir = _get_cache_dir()
|
|
85
|
-
os.makedirs(cache_dir, exist_ok=True)
|
|
86
|
-
self = super().__new__(
|
|
87
|
-
cls,
|
|
88
|
-
tempfile.mkdtemp(
|
|
89
|
-
prefix="amulettmp",
|
|
90
|
-
suffix=f"-{time.time():.0f}",
|
|
91
|
-
dir=cache_dir,
|
|
92
|
-
),
|
|
93
|
-
)
|
|
94
|
-
self.__lock = open(os.path.join(self, "lock"), "w")
|
|
95
|
-
portalocker.lock(self.__lock, portalocker.LockFlags.EXCLUSIVE)
|
|
96
|
-
return self
|
|
97
|
-
|
|
98
|
-
def close(self):
|
|
99
|
-
"""Close the lock and delete the directory."""
|
|
100
|
-
if self.__lock is not None:
|
|
101
|
-
portalocker.unlock(self.__lock)
|
|
102
|
-
self.__lock.close()
|
|
103
|
-
self.__lock = None
|
|
104
|
-
shutil.rmtree(self)
|
|
105
|
-
|
|
106
|
-
def __del__(self):
|
|
107
|
-
self.close()
|
|
1
|
+
# A cache for objects implemented using leveldb for speed.
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import time
|
|
5
|
+
import logging
|
|
6
|
+
import glob
|
|
7
|
+
import re
|
|
8
|
+
import tempfile
|
|
9
|
+
|
|
10
|
+
import portalocker
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_cache_dir() -> str:
|
|
14
|
+
return os.environ.get("AMULET_LEVEL_CACHE_DIR", None) or os.path.join(
|
|
15
|
+
os.environ.get("CACHE_DIR"), "level_data"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _clear_legacy_cache():
|
|
23
|
+
legacy_cache_dir = _get_cache_dir()
|
|
24
|
+
world_temp_dir = os.path.join(legacy_cache_dir, "world_temp")
|
|
25
|
+
if not os.path.isdir(world_temp_dir):
|
|
26
|
+
return
|
|
27
|
+
paths = [
|
|
28
|
+
os.path.join(legacy_cache_dir, t)
|
|
29
|
+
for t in os.listdir(world_temp_dir)
|
|
30
|
+
if t.isnumeric() and int(t) < (time.time() - 7 * 24 * 3600)
|
|
31
|
+
]
|
|
32
|
+
if paths:
|
|
33
|
+
log.info("Removing legacy cache.")
|
|
34
|
+
|
|
35
|
+
# remove all cache directories that were created before a week ago
|
|
36
|
+
# Sometimes if the program is stopped or it crashes the cleanup won't happen
|
|
37
|
+
for path in paths:
|
|
38
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_clear_legacy_cache()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
TempPattern = re.compile(r"amulettmp.*?-(?P<time>\d+)")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _clear_temp_dirs():
|
|
48
|
+
"""
|
|
49
|
+
Try and delete historic temporary directories.
|
|
50
|
+
If things went very wrong in past sessions temporary directories may still exist.
|
|
51
|
+
"""
|
|
52
|
+
temp_dir = tempfile.gettempdir()
|
|
53
|
+
for path in glob.glob(os.path.join(glob.escape(temp_dir), "amulettmp*")):
|
|
54
|
+
name = os.path.relpath(path, temp_dir)
|
|
55
|
+
match = TempPattern.fullmatch(name)
|
|
56
|
+
if match and int(match.group("time")) < (time.time() - 7 * 24 * 3600):
|
|
57
|
+
lock_path = os.path.join(path, "lock")
|
|
58
|
+
if os.path.exists(lock_path):
|
|
59
|
+
with open(lock_path) as lock:
|
|
60
|
+
# make sure it is not locked by another process
|
|
61
|
+
try:
|
|
62
|
+
portalocker.lock(lock, portalocker.LockFlags.EXCLUSIVE)
|
|
63
|
+
except:
|
|
64
|
+
continue
|
|
65
|
+
else:
|
|
66
|
+
portalocker.unlock(lock)
|
|
67
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_clear_temp_dirs()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TempDir(str):
|
|
74
|
+
"""
|
|
75
|
+
A temporary directory to do with as you wish.
|
|
76
|
+
|
|
77
|
+
>>> t = TempDir()
|
|
78
|
+
>>> path = os.path.join(t, "your_file.txt") # TempDir is a subclass of str
|
|
79
|
+
>>> # make sure all files in the temporary directory are closed before releasing or closing this object.
|
|
80
|
+
>>> # The temporary directory will be deleted when the last reference to `t` is lost or when `t.close()` is called
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __new__(cls):
|
|
84
|
+
cache_dir = _get_cache_dir()
|
|
85
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
86
|
+
self = super().__new__(
|
|
87
|
+
cls,
|
|
88
|
+
tempfile.mkdtemp(
|
|
89
|
+
prefix="amulettmp",
|
|
90
|
+
suffix=f"-{time.time():.0f}",
|
|
91
|
+
dir=cache_dir,
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
self.__lock = open(os.path.join(self, "lock"), "w")
|
|
95
|
+
portalocker.lock(self.__lock, portalocker.LockFlags.EXCLUSIVE)
|
|
96
|
+
return self
|
|
97
|
+
|
|
98
|
+
def close(self):
|
|
99
|
+
"""Close the lock and delete the directory."""
|
|
100
|
+
if self.__lock is not None:
|
|
101
|
+
portalocker.unlock(self.__lock)
|
|
102
|
+
self.__lock.close()
|
|
103
|
+
self.__lock = None
|
|
104
|
+
shutil.rmtree(self)
|
|
105
|
+
|
|
106
|
+
def __del__(self):
|
|
107
|
+
self.close()
|
amulet/api/chunk/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from .blocks import Blocks
|
|
2
|
-
from .status import Status, StatusFormats
|
|
3
|
-
from .biomes import Biomes, BiomesShape
|
|
4
|
-
from .entity_list import EntityList
|
|
5
|
-
from .block_entity_dict import BlockEntityDict
|
|
6
|
-
from .chunk import Chunk
|
|
1
|
+
from .blocks import Blocks
|
|
2
|
+
from .status import Status, StatusFormats
|
|
3
|
+
from .biomes import Biomes, BiomesShape
|
|
4
|
+
from .entity_list import EntityList
|
|
5
|
+
from .block_entity_dict import BlockEntityDict
|
|
6
|
+
from .chunk import Chunk
|