amulet-core 2.0a3__cp312-cp312-win_amd64.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__.cp312-win_amd64.pyd +0 -0
- amulet/__init__.pyi +30 -0
- amulet/__pyinstaller/__init__.py +2 -0
- amulet/__pyinstaller/hook-amulet.py +4 -0
- amulet/_init.py +28 -0
- amulet/_version.py +21 -0
- amulet/biome.cpp +36 -0
- amulet/biome.hpp +43 -0
- amulet/biome.pyi +77 -0
- amulet/block.cpp +435 -0
- amulet/block.hpp +119 -0
- amulet/block.pyi +273 -0
- amulet/block_entity.cpp +12 -0
- amulet/block_entity.hpp +56 -0
- amulet/block_entity.pyi +80 -0
- amulet/chunk.cpp +16 -0
- amulet/chunk.hpp +99 -0
- amulet/chunk.pyi +30 -0
- amulet/chunk_/components/biome.py +155 -0
- amulet/chunk_/components/block_entity.py +117 -0
- amulet/chunk_/components/entity.py +64 -0
- amulet/chunk_/components/height_2d.py +16 -0
- amulet/chunk_components.pyi +95 -0
- amulet/collections.pyi +37 -0
- amulet/data_types.py +29 -0
- amulet/entity.py +180 -0
- amulet/errors.py +63 -0
- amulet/game/__init__.py +7 -0
- amulet/game/_game.py +152 -0
- amulet/game/_universal/__init__.py +1 -0
- amulet/game/_universal/_biome.py +17 -0
- amulet/game/_universal/_block.py +47 -0
- amulet/game/_universal/_version.py +68 -0
- amulet/game/abc/__init__.py +22 -0
- amulet/game/abc/_block_specification.py +150 -0
- amulet/game/abc/biome.py +213 -0
- amulet/game/abc/block.py +331 -0
- amulet/game/abc/game_version_container.py +25 -0
- amulet/game/abc/json_interface.py +27 -0
- amulet/game/abc/version.py +44 -0
- amulet/game/bedrock/__init__.py +1 -0
- amulet/game/bedrock/_biome.py +35 -0
- amulet/game/bedrock/_block.py +42 -0
- amulet/game/bedrock/_version.py +165 -0
- amulet/game/java/__init__.py +2 -0
- amulet/game/java/_biome.py +35 -0
- amulet/game/java/_block.py +60 -0
- amulet/game/java/_version.py +176 -0
- amulet/game/translate/__init__.py +12 -0
- amulet/game/translate/_functions/__init__.py +15 -0
- amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
- amulet/game/translate/_functions/_code_functions/_text.py +553 -0
- amulet/game/translate/_functions/_code_functions/banner_pattern.py +67 -0
- amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +88 -0
- amulet/game/translate/_functions/_code_functions/bedrock_sign.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +16 -0
- amulet/game/translate/_functions/_code_functions/custom_name.py +146 -0
- amulet/game/translate/_functions/_frozen.py +66 -0
- amulet/game/translate/_functions/_state.py +54 -0
- amulet/game/translate/_functions/_typing.py +98 -0
- amulet/game/translate/_functions/abc.py +116 -0
- amulet/game/translate/_functions/carry_nbt.py +160 -0
- amulet/game/translate/_functions/carry_properties.py +80 -0
- amulet/game/translate/_functions/code.py +143 -0
- amulet/game/translate/_functions/map_block_name.py +66 -0
- amulet/game/translate/_functions/map_nbt.py +111 -0
- amulet/game/translate/_functions/map_properties.py +93 -0
- amulet/game/translate/_functions/multiblock.py +112 -0
- amulet/game/translate/_functions/new_block.py +42 -0
- amulet/game/translate/_functions/new_entity.py +43 -0
- amulet/game/translate/_functions/new_nbt.py +206 -0
- amulet/game/translate/_functions/new_properties.py +64 -0
- amulet/game/translate/_functions/sequence.py +51 -0
- amulet/game/translate/_functions/walk_input_nbt.py +331 -0
- amulet/game/translate/_translator.py +433 -0
- amulet/item.py +75 -0
- amulet/level/__init__.pyi +27 -0
- amulet/level/_load.py +100 -0
- amulet/level/abc/__init__.py +12 -0
- amulet/level/abc/_chunk_handle.py +335 -0
- amulet/level/abc/_dimension.py +86 -0
- amulet/level/abc/_history/__init__.py +1 -0
- amulet/level/abc/_history/_cache.py +224 -0
- amulet/level/abc/_history/_history_manager.py +291 -0
- amulet/level/abc/_level/__init__.py +5 -0
- amulet/level/abc/_level/_compactable_level.py +10 -0
- amulet/level/abc/_level/_creatable_level.py +29 -0
- amulet/level/abc/_level/_disk_level.py +17 -0
- amulet/level/abc/_level/_level.py +453 -0
- amulet/level/abc/_level/_loadable_level.py +42 -0
- amulet/level/abc/_player_storage.py +7 -0
- amulet/level/abc/_raw_level.py +187 -0
- amulet/level/abc/_registry.py +40 -0
- amulet/level/bedrock/__init__.py +2 -0
- amulet/level/bedrock/_chunk_handle.py +19 -0
- amulet/level/bedrock/_dimension.py +22 -0
- amulet/level/bedrock/_level.py +187 -0
- amulet/level/bedrock/_raw/__init__.py +5 -0
- amulet/level/bedrock/_raw/_actor_counter.py +53 -0
- amulet/level/bedrock/_raw/_chunk.py +54 -0
- amulet/level/bedrock/_raw/_chunk_decode.py +668 -0
- amulet/level/bedrock/_raw/_chunk_encode.py +602 -0
- amulet/level/bedrock/_raw/_constant.py +9 -0
- amulet/level/bedrock/_raw/_dimension.py +343 -0
- amulet/level/bedrock/_raw/_level.py +463 -0
- amulet/level/bedrock/_raw/_level_dat.py +90 -0
- amulet/level/bedrock/_raw/_typing.py +6 -0
- amulet/level/bedrock/_raw/leveldb_chunk_versions.py +83 -0
- amulet/level/bedrock/chunk/__init__.py +1 -0
- amulet/level/bedrock/chunk/_chunk.py +126 -0
- amulet/level/bedrock/chunk/components/__init__.py +0 -0
- amulet/level/bedrock/chunk/components/chunk_version.py +12 -0
- amulet/level/bedrock/chunk/components/finalised_state.py +13 -0
- amulet/level/bedrock/chunk/components/raw_chunk.py +15 -0
- amulet/level/construction/__init__.py +0 -0
- amulet/level/java/__init__.pyi +21 -0
- amulet/level/java/_chunk_handle.py +17 -0
- amulet/level/java/_chunk_handle.pyi +15 -0
- amulet/level/java/_dimension.py +20 -0
- amulet/level/java/_dimension.pyi +13 -0
- amulet/level/java/_level.py +184 -0
- amulet/level/java/_level.pyi +120 -0
- amulet/level/java/_raw/__init__.pyi +19 -0
- amulet/level/java/_raw/_chunk.pyi +23 -0
- amulet/level/java/_raw/_chunk_decode.py +561 -0
- amulet/level/java/_raw/_chunk_encode.py +463 -0
- amulet/level/java/_raw/_constant.py +9 -0
- amulet/level/java/_raw/_constant.pyi +20 -0
- amulet/level/java/_raw/_data_pack/__init__.py +2 -0
- amulet/level/java/_raw/_data_pack/__init__.pyi +8 -0
- amulet/level/java/_raw/_data_pack/data_pack.py +241 -0
- amulet/level/java/_raw/_data_pack/data_pack.pyi +197 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.py +77 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +75 -0
- amulet/level/java/_raw/_dimension.py +86 -0
- amulet/level/java/_raw/_dimension.pyi +72 -0
- amulet/level/java/_raw/_level.py +507 -0
- amulet/level/java/_raw/_level.pyi +238 -0
- amulet/level/java/_raw/_typing.py +3 -0
- amulet/level/java/_raw/_typing.pyi +5 -0
- amulet/level/java/anvil/__init__.py +2 -0
- amulet/level/java/anvil/__init__.pyi +11 -0
- amulet/level/java/anvil/_dimension.py +170 -0
- amulet/level/java/anvil/_dimension.pyi +109 -0
- amulet/level/java/anvil/_region.py +421 -0
- amulet/level/java/anvil/_region.pyi +197 -0
- amulet/level/java/anvil/_sector_manager.py +223 -0
- amulet/level/java/anvil/_sector_manager.pyi +142 -0
- amulet/level/java/chunk.pyi +81 -0
- amulet/level/java/chunk_/_chunk.py +260 -0
- amulet/level/java/chunk_/components/inhabited_time.py +12 -0
- amulet/level/java/chunk_/components/last_update.py +12 -0
- amulet/level/java/chunk_/components/legacy_version.py +12 -0
- amulet/level/java/chunk_/components/light_populated.py +12 -0
- amulet/level/java/chunk_/components/named_height_2d.py +37 -0
- amulet/level/java/chunk_/components/status.py +11 -0
- amulet/level/java/chunk_/components/terrain_populated.py +12 -0
- amulet/level/java/chunk_components.pyi +22 -0
- amulet/level/java/long_array.pyi +38 -0
- amulet/level/java_forge/__init__.py +0 -0
- amulet/level/mcstructure/__init__.py +0 -0
- amulet/level/nbt/__init__.py +0 -0
- amulet/level/schematic/__init__.py +0 -0
- amulet/level/sponge_schematic/__init__.py +0 -0
- amulet/level/temporary_level/__init__.py +1 -0
- amulet/level/temporary_level/_level.py +16 -0
- amulet/palette/__init__.pyi +8 -0
- amulet/palette/biome_palette.pyi +45 -0
- amulet/palette/block_palette.pyi +45 -0
- amulet/player.py +64 -0
- amulet/py.typed +0 -0
- amulet/selection/__init__.py +2 -0
- amulet/selection/abstract_selection.py +342 -0
- amulet/selection/box.py +852 -0
- amulet/selection/group.py +481 -0
- amulet/utils/__init__.pyi +28 -0
- amulet/utils/call_spec/__init__.py +24 -0
- amulet/utils/call_spec/__init__.pyi +53 -0
- amulet/utils/call_spec/_call_spec.py +262 -0
- amulet/utils/call_spec/_call_spec.pyi +272 -0
- amulet/utils/format_utils.py +41 -0
- amulet/utils/generator.py +18 -0
- amulet/utils/matrix.py +243 -0
- amulet/utils/matrix.pyi +177 -0
- amulet/utils/numpy.pyi +11 -0
- amulet/utils/numpy_helpers.py +19 -0
- amulet/utils/shareable_lock.py +335 -0
- amulet/utils/shareable_lock.pyi +190 -0
- amulet/utils/signal/__init__.py +10 -0
- amulet/utils/signal/__init__.pyi +25 -0
- amulet/utils/signal/_signal.py +228 -0
- amulet/utils/signal/_signal.pyi +84 -0
- amulet/utils/task_manager.py +235 -0
- amulet/utils/task_manager.pyi +168 -0
- amulet/utils/typed_property.py +111 -0
- amulet/utils/typing.py +4 -0
- amulet/utils/typing.pyi +6 -0
- amulet/utils/weakref.py +70 -0
- amulet/utils/weakref.pyi +50 -0
- amulet/utils/world_utils.py +102 -0
- amulet/utils/world_utils.pyi +109 -0
- amulet/version.cpp +136 -0
- amulet/version.hpp +142 -0
- amulet/version.pyi +94 -0
- amulet_core-2.0a3.dist-info/METADATA +103 -0
- amulet_core-2.0a3.dist-info/RECORD +210 -0
- amulet_core-2.0a3.dist-info/WHEEL +5 -0
- amulet_core-2.0a3.dist-info/entry_points.txt +2 -0
- amulet_core-2.0a3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
from threading import Lock
|
|
5
|
+
from typing import Sequence, Protocol, TypeVar, Generic, Mapping, Any
|
|
6
|
+
from weakref import WeakSet, WeakValueDictionary
|
|
7
|
+
from collections.abc import MutableMapping
|
|
8
|
+
|
|
9
|
+
from amulet.utils.signal import Signal, SignalInstanceCacheName
|
|
10
|
+
|
|
11
|
+
from ._cache import GlobalDiskCache
|
|
12
|
+
|
|
13
|
+
# TODO: consider adding a max undo option
|
|
14
|
+
# TODO: if we clear old undo info we should remove that data from the cache
|
|
15
|
+
# TODO: compact the cache periodically
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ResourceId(Protocol):
|
|
19
|
+
def __hash__(self) -> int:
|
|
20
|
+
"""A constant hash"""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
def __eq__(self, other: Any) -> bool: ...
|
|
24
|
+
|
|
25
|
+
def __bytes__(self) -> bytes:
|
|
26
|
+
"""A constant bytes representation"""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
ResourceIdT = TypeVar("ResourceIdT", bound=ResourceId)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Resource:
|
|
34
|
+
__slots__ = ("index", "saved_index", "global_index", "exists")
|
|
35
|
+
|
|
36
|
+
def __init__(self) -> None:
|
|
37
|
+
# The index of the currently active revision
|
|
38
|
+
self.index: int = 0
|
|
39
|
+
# The index of the saved revision. -1 if the index no longer exists (overwritten or destroyed future)
|
|
40
|
+
self.saved_index: int = 0
|
|
41
|
+
# The global history index
|
|
42
|
+
self.global_index: int = 0
|
|
43
|
+
# Does the resource data exist
|
|
44
|
+
self.exists: list[bool] = [True]
|
|
45
|
+
|
|
46
|
+
def get_resource_key(self, uuid: bytes, resource_id: ResourceId) -> bytes:
|
|
47
|
+
return b"/".join((uuid, bytes(resource_id), str(self.index).encode()))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class HistoryManagerPrivate:
|
|
51
|
+
lock: Lock
|
|
52
|
+
resources: WeakValueDictionary[bytes, MutableMapping[ResourceId, Resource]]
|
|
53
|
+
history: list[WeakSet[Resource]]
|
|
54
|
+
history_index: int
|
|
55
|
+
has_redo: bool
|
|
56
|
+
cache: GlobalDiskCache
|
|
57
|
+
|
|
58
|
+
def __init__(self) -> None:
|
|
59
|
+
self.lock = Lock()
|
|
60
|
+
self.resources = WeakValueDictionary()
|
|
61
|
+
self.history = [WeakSet()]
|
|
62
|
+
self.history_index = 0
|
|
63
|
+
self.has_redo = False
|
|
64
|
+
self.cache = GlobalDiskCache.instance()
|
|
65
|
+
|
|
66
|
+
def invalidate_future(self) -> None:
|
|
67
|
+
"""Destroy all future redo bins. Caller must acquire the lock"""
|
|
68
|
+
if self.has_redo:
|
|
69
|
+
self.has_redo = False
|
|
70
|
+
del self.history[self.history_index + 1 :]
|
|
71
|
+
for layer in self.resources.values():
|
|
72
|
+
for resource in layer.values():
|
|
73
|
+
if resource.saved_index > resource.index:
|
|
74
|
+
# A future index is saved that just got invalidated
|
|
75
|
+
resource.saved_index = -1
|
|
76
|
+
|
|
77
|
+
def reset(self) -> None:
|
|
78
|
+
with self.lock:
|
|
79
|
+
for layer in self.resources.values():
|
|
80
|
+
layer.clear()
|
|
81
|
+
self.history = [WeakSet()]
|
|
82
|
+
self.history_index = 0
|
|
83
|
+
self.has_redo = False
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CustomDict(dict):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class HistoryManager:
|
|
91
|
+
__slots__ = (SignalInstanceCacheName, "_h")
|
|
92
|
+
|
|
93
|
+
_h: HistoryManagerPrivate
|
|
94
|
+
|
|
95
|
+
def __init__(self) -> None:
|
|
96
|
+
self._h = HistoryManagerPrivate()
|
|
97
|
+
|
|
98
|
+
def new_layer(self) -> HistoryManagerLayer:
|
|
99
|
+
uuid = uuid4().bytes
|
|
100
|
+
resources: MutableMapping[ResourceId, Resource] = CustomDict()
|
|
101
|
+
self._h.resources[uuid] = resources
|
|
102
|
+
return HistoryManagerLayer(self._h, uuid, resources)
|
|
103
|
+
|
|
104
|
+
history_changed = Signal[()]()
|
|
105
|
+
|
|
106
|
+
def create_undo_bin(self) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Call this to create a new undo bin.
|
|
109
|
+
All changes made after this point will be part of the same undo bin until this is called again.
|
|
110
|
+
If this is not called, all changes will be part of the previous undo bin.
|
|
111
|
+
"""
|
|
112
|
+
with self._h.lock:
|
|
113
|
+
self._h.invalidate_future()
|
|
114
|
+
self._h.history_index += 1
|
|
115
|
+
self._h.history.append(WeakSet())
|
|
116
|
+
|
|
117
|
+
def mark_saved(self) -> None:
|
|
118
|
+
with self._h.lock:
|
|
119
|
+
for layer in self._h.resources.values():
|
|
120
|
+
for resource in layer.values():
|
|
121
|
+
resource.saved_index = resource.index
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def undo_count(self) -> int:
|
|
125
|
+
"""The number of times undo can be called."""
|
|
126
|
+
with self._h.lock:
|
|
127
|
+
return self._h.history_index
|
|
128
|
+
|
|
129
|
+
def undo(self) -> None:
|
|
130
|
+
"""Undo the changes in the current undo bin."""
|
|
131
|
+
with self._h.lock:
|
|
132
|
+
if self._h.history_index <= 0:
|
|
133
|
+
raise RuntimeError
|
|
134
|
+
for resource in self._h.history[self._h.history_index]:
|
|
135
|
+
resource.index -= 1
|
|
136
|
+
self._h.history_index -= 1
|
|
137
|
+
self._h.has_redo = True
|
|
138
|
+
|
|
139
|
+
def _redo_count(self) -> int:
|
|
140
|
+
return len(self._h.history) - (self._h.history_index + 1)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def redo_count(self) -> int:
|
|
144
|
+
"""The number of times redo can be called."""
|
|
145
|
+
with self._h.lock:
|
|
146
|
+
return self._redo_count()
|
|
147
|
+
|
|
148
|
+
def redo(self) -> None:
|
|
149
|
+
"""Redo the changes in the next undo bin."""
|
|
150
|
+
with self._h.lock:
|
|
151
|
+
if not self._h.has_redo:
|
|
152
|
+
raise RuntimeError
|
|
153
|
+
self._h.history_index += 1
|
|
154
|
+
for resource in self._h.history[self._h.history_index]:
|
|
155
|
+
resource.index += 1
|
|
156
|
+
self._h.has_redo = bool(self._redo_count())
|
|
157
|
+
|
|
158
|
+
def reset(self) -> None:
|
|
159
|
+
"""Reset to the factory state."""
|
|
160
|
+
self._h.reset()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class HistoryManagerLayer(Generic[ResourceIdT]):
|
|
164
|
+
__slots__ = ("_h", "_uuid", "_resources")
|
|
165
|
+
|
|
166
|
+
_h: HistoryManagerPrivate
|
|
167
|
+
_uuid: bytes
|
|
168
|
+
_resources: MutableMapping[ResourceIdT, Resource]
|
|
169
|
+
|
|
170
|
+
def __init__(
|
|
171
|
+
self,
|
|
172
|
+
_h: HistoryManagerPrivate,
|
|
173
|
+
uuid: bytes,
|
|
174
|
+
resources: MutableMapping[ResourceIdT, Resource],
|
|
175
|
+
) -> None:
|
|
176
|
+
"""This must not be used directly."""
|
|
177
|
+
self._h = _h
|
|
178
|
+
self._uuid = uuid
|
|
179
|
+
self._resources = resources
|
|
180
|
+
|
|
181
|
+
def resources(self) -> Sequence[ResourceIdT]:
|
|
182
|
+
"""
|
|
183
|
+
Get all resource ids from this layer.
|
|
184
|
+
:return:
|
|
185
|
+
"""
|
|
186
|
+
with self._h.lock:
|
|
187
|
+
return list(self._resources)
|
|
188
|
+
|
|
189
|
+
def changed_resources(self) -> Sequence[ResourceIdT]:
|
|
190
|
+
"""
|
|
191
|
+
Get all resource ids from this layer that have changed since the last call to mark_saved.
|
|
192
|
+
:return:
|
|
193
|
+
"""
|
|
194
|
+
with self._h.lock:
|
|
195
|
+
return [
|
|
196
|
+
resource_id
|
|
197
|
+
for resource_id, resource in self._resources.items()
|
|
198
|
+
if resource.saved_index != resource.index
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
def resources_exist_map(self) -> Mapping[ResourceIdT, bool]:
|
|
202
|
+
"""
|
|
203
|
+
Get a mapping from the resource ids to a bool stating if the data exists for that resource.
|
|
204
|
+
If false that resource has been deleted.
|
|
205
|
+
"""
|
|
206
|
+
with self._h.lock:
|
|
207
|
+
return {
|
|
208
|
+
resource_id: resource.exists[resource.index]
|
|
209
|
+
for resource_id, resource in self._resources.items()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
def has_resource(self, resource_id: ResourceIdT) -> bool:
|
|
213
|
+
"""
|
|
214
|
+
Check if a resource entry exists.
|
|
215
|
+
If the resource has been loaded this will be True regardless if the resource data has been deleted.
|
|
216
|
+
Use :meth:`resource_exists` to check if the resource data has been deleted.
|
|
217
|
+
Calling :meth:`get_resource` or :meth:`set_resource` when this is False will error.
|
|
218
|
+
:param resource_id: The resource identifier
|
|
219
|
+
:return:
|
|
220
|
+
"""
|
|
221
|
+
with self._h.lock:
|
|
222
|
+
return resource_id in self._resources
|
|
223
|
+
|
|
224
|
+
def resource_exists(self, resource_id: ResourceIdT) -> bool:
|
|
225
|
+
"""
|
|
226
|
+
A fast way to check if the resource data exists without loading it.
|
|
227
|
+
:param resource_id: The resource identifier
|
|
228
|
+
:return: True if the data exists.
|
|
229
|
+
"""
|
|
230
|
+
with self._h.lock:
|
|
231
|
+
resource = self._resources[resource_id]
|
|
232
|
+
return resource.exists[resource.index]
|
|
233
|
+
|
|
234
|
+
def get_resource(self, resource_id: ResourceIdT) -> bytes:
|
|
235
|
+
"""
|
|
236
|
+
Get the newest resource data.
|
|
237
|
+
:param resource_id: The resource identifier
|
|
238
|
+
:return: The binary data that was previously set. An empty bytes object for the deleted state.
|
|
239
|
+
"""
|
|
240
|
+
with self._h.lock:
|
|
241
|
+
resource = self._resources[resource_id]
|
|
242
|
+
if resource.exists[resource.index]:
|
|
243
|
+
return self._h.cache[resource.get_resource_key(self._uuid, resource_id)]
|
|
244
|
+
else:
|
|
245
|
+
return b""
|
|
246
|
+
|
|
247
|
+
def set_initial_resource(self, resource_id: ResourceIdT, data: bytes) -> None:
|
|
248
|
+
"""
|
|
249
|
+
Set the data for the resource.
|
|
250
|
+
This can only be used if the resource does not already exist.
|
|
251
|
+
:param resource_id: The resource identifier
|
|
252
|
+
:param data: The binary data to set. An empty bytes object for the deleted state.
|
|
253
|
+
:return:
|
|
254
|
+
"""
|
|
255
|
+
with self._h.lock:
|
|
256
|
+
if resource_id in self._resources:
|
|
257
|
+
raise RuntimeError("Resource already exists")
|
|
258
|
+
resource = self._resources[resource_id] = Resource()
|
|
259
|
+
if data:
|
|
260
|
+
# Save the data to the cache if it exists
|
|
261
|
+
self._h.cache[resource.get_resource_key(self._uuid, resource_id)] = data
|
|
262
|
+
# Store a flag if it exists
|
|
263
|
+
resource.exists[resource.index] = bool(data)
|
|
264
|
+
|
|
265
|
+
def set_resource(self, resource_id: ResourceIdT, data: bytes) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Set the data for the resource.
|
|
268
|
+
:param resource_id: The resource identifier
|
|
269
|
+
:param data: The binary data to set. An empty bytes object for the deleted state.
|
|
270
|
+
:return:
|
|
271
|
+
"""
|
|
272
|
+
with self._h.lock:
|
|
273
|
+
self._h.invalidate_future()
|
|
274
|
+
resource = self._resources[resource_id]
|
|
275
|
+
if resource.global_index != self._h.history_index:
|
|
276
|
+
# The global history index has been increased since the last change to this resource
|
|
277
|
+
# Add a new state
|
|
278
|
+
resource.index += 1
|
|
279
|
+
resource.global_index = self._h.history_index
|
|
280
|
+
resource.exists.append(False)
|
|
281
|
+
if resource.index == resource.saved_index:
|
|
282
|
+
# The saved index has been directly modified
|
|
283
|
+
resource.saved_index = -1
|
|
284
|
+
if data:
|
|
285
|
+
# Save the data to the cache if it exists
|
|
286
|
+
self._h.cache[resource.get_resource_key(self._uuid, resource_id)] = data
|
|
287
|
+
# Store a flag if it exists
|
|
288
|
+
resource.exists[resource.index] = bool(data)
|
|
289
|
+
if self._h.history_index:
|
|
290
|
+
# Add the resource to the history bin if one has been created
|
|
291
|
+
self._h.history[self._h.history_index].add(resource)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from amulet.utils.typing import Intersection
|
|
6
|
+
from amulet.utils.call_spec import method_spec
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ._level import Level
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CreatableLevel(ABC):
|
|
14
|
+
"""Level extension class for levels that can be created without data."""
|
|
15
|
+
|
|
16
|
+
__slots__ = ()
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
@abstractmethod
|
|
20
|
+
@method_spec()
|
|
21
|
+
def create(cls, *args: Any, **kwargs: Any) -> Intersection[Level, CreatableLevel]:
|
|
22
|
+
"""
|
|
23
|
+
Create a new instance without any existing data.
|
|
24
|
+
You must call :meth:`~amulet.level.abc.Level.open` to open the level for editing.
|
|
25
|
+
:return: A new Level instance
|
|
26
|
+
"""
|
|
27
|
+
# If writing data to disk, it must write a valid level.
|
|
28
|
+
# If only setting attributes, the open method must be aware that it should not load data from disk.
|
|
29
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
|
|
5
|
+
from ._level import Level, OpenLevelDataT, RawLevelT, DimensionT
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DiskLevel(Level[OpenLevelDataT, DimensionT, RawLevelT]):
|
|
9
|
+
"""A level base class for all levels with data on disk."""
|
|
10
|
+
|
|
11
|
+
__slots__ = ()
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def path(self) -> str:
|
|
16
|
+
"""The path to the level on disk."""
|
|
17
|
+
raise NotImplementedError
|