amulet-core 2.0a8__cp311-cp311-win_amd64.whl → 2.0.1.0.1297307203.19.43.34808.0a1__cp311-cp311-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.

Files changed (271) hide show
  1. amulet/core/__init__.py +36 -0
  2. amulet/core/__pyinstaller/hook-amulet.core.py +4 -0
  3. amulet/core/_amulet_core.cp311-win_amd64.pyd +0 -0
  4. amulet/core/_amulet_core.pyi +7 -0
  5. amulet/{_version.py → core/_version.py} +3 -3
  6. amulet/core/amulet_core.dll +0 -0
  7. amulet/core/amulet_core.lib +0 -0
  8. amulet/core/amulet_coreConfig.cmake +18 -0
  9. amulet/{biome.pyi → core/biome/__init__.pyi} +3 -3
  10. amulet/core/biome/biome.hpp +53 -0
  11. amulet/{block.pyi → core/block/__init__.pyi} +25 -26
  12. amulet/core/block/block.hpp +156 -0
  13. amulet/{block_entity.pyi → core/block_entity/__init__.pyi} +7 -7
  14. amulet/core/block_entity/block_entity.hpp +84 -0
  15. amulet/{errors.py → core/chunk/__init__.pyi} +37 -33
  16. amulet/core/chunk/chunk.hpp +126 -0
  17. amulet/core/chunk/component/__init__.pyi +18 -0
  18. amulet/core/chunk/component/biome_3d_component.hpp +96 -0
  19. amulet/core/chunk/component/block_component.hpp +101 -0
  20. amulet/core/chunk/component/block_component.pyi +28 -0
  21. amulet/core/chunk/component/block_entity_component.hpp +119 -0
  22. amulet/core/chunk/component/section_array_map.hpp +129 -0
  23. amulet/{chunk_components.pyi → core/chunk/component/section_array_map.pyi} +4 -24
  24. amulet/core/dll.hpp +21 -0
  25. amulet/core/entity/__init__.pyi +105 -0
  26. amulet/core/entity/entity.hpp +100 -0
  27. amulet/{palette → core/palette}/__init__.pyi +2 -2
  28. amulet/core/palette/biome_palette.hpp +65 -0
  29. amulet/{palette → core/palette}/biome_palette.pyi +8 -8
  30. amulet/core/palette/block_palette.hpp +71 -0
  31. amulet/{palette → core/palette}/block_palette.pyi +12 -10
  32. amulet/core/selection/__init__.pyi +8 -0
  33. amulet/core/selection/box.hpp +86 -0
  34. amulet/core/selection/box.pyi +215 -0
  35. amulet/core/selection/group.hpp +80 -0
  36. amulet/core/selection/group.pyi +213 -0
  37. amulet/{version.pyi → core/version/__init__.pyi} +58 -10
  38. amulet/core/version/version.hpp +204 -0
  39. {amulet_core-2.0a8.dist-info → amulet_core-2.0.1.0.1297307203.19.43.34808.0a1.dist-info}/METADATA +25 -20
  40. amulet_core-2.0.1.0.1297307203.19.43.34808.0a1.dist-info/RECORD +45 -0
  41. {amulet_core-2.0a8.dist-info → amulet_core-2.0.1.0.1297307203.19.43.34808.0a1.dist-info}/WHEEL +1 -1
  42. amulet_core-2.0.1.0.1297307203.19.43.34808.0a1.dist-info/entry_points.txt +2 -0
  43. amulet/__init__.cp311-win_amd64.pyd +0 -0
  44. amulet/__init__.py.cpp +0 -45
  45. amulet/__init__.pyi +0 -30
  46. amulet/__pyinstaller/hook-amulet.py +0 -4
  47. amulet/_init.py +0 -26
  48. amulet/biome.cpp +0 -36
  49. amulet/biome.hpp +0 -43
  50. amulet/biome.py.cpp +0 -122
  51. amulet/block.cpp +0 -435
  52. amulet/block.hpp +0 -119
  53. amulet/block.py.cpp +0 -377
  54. amulet/block_entity.cpp +0 -12
  55. amulet/block_entity.hpp +0 -56
  56. amulet/block_entity.py.cpp +0 -115
  57. amulet/chunk.cpp +0 -16
  58. amulet/chunk.hpp +0 -100
  59. amulet/chunk.py.cpp +0 -80
  60. amulet/chunk.pyi +0 -28
  61. amulet/chunk_components/biome_3d_component.cpp +0 -5
  62. amulet/chunk_components/biome_3d_component.hpp +0 -79
  63. amulet/chunk_components/block_component.cpp +0 -41
  64. amulet/chunk_components/block_component.hpp +0 -88
  65. amulet/chunk_components/block_entity_component.cpp +0 -5
  66. amulet/chunk_components/block_entity_component.hpp +0 -147
  67. amulet/chunk_components/section_array_map.cpp +0 -129
  68. amulet/chunk_components/section_array_map.hpp +0 -147
  69. amulet/collections/eq.py.hpp +0 -37
  70. amulet/collections/hash.py.hpp +0 -27
  71. amulet/collections/holder.py.hpp +0 -37
  72. amulet/collections/iterator.py.hpp +0 -80
  73. amulet/collections/mapping.py.hpp +0 -199
  74. amulet/collections/mutable_mapping.py.hpp +0 -226
  75. amulet/collections/sequence.py.hpp +0 -163
  76. amulet/collections.pyi +0 -40
  77. amulet/data_types.py +0 -29
  78. amulet/entity.py +0 -182
  79. amulet/game/__init__.py +0 -7
  80. amulet/game/_game.py +0 -152
  81. amulet/game/_universal/__init__.py +0 -1
  82. amulet/game/_universal/_biome.py +0 -17
  83. amulet/game/_universal/_block.py +0 -47
  84. amulet/game/_universal/_version.py +0 -68
  85. amulet/game/abc/__init__.py +0 -22
  86. amulet/game/abc/_block_specification.py +0 -150
  87. amulet/game/abc/biome.py +0 -213
  88. amulet/game/abc/block.py +0 -331
  89. amulet/game/abc/game_version_container.py +0 -25
  90. amulet/game/abc/json_interface.py +0 -27
  91. amulet/game/abc/version.py +0 -44
  92. amulet/game/bedrock/__init__.py +0 -1
  93. amulet/game/bedrock/_biome.py +0 -35
  94. amulet/game/bedrock/_block.py +0 -42
  95. amulet/game/bedrock/_version.py +0 -165
  96. amulet/game/java/__init__.py +0 -2
  97. amulet/game/java/_biome.py +0 -35
  98. amulet/game/java/_block.py +0 -60
  99. amulet/game/java/_version.py +0 -176
  100. amulet/game/translate/__init__.py +0 -12
  101. amulet/game/translate/_functions/__init__.py +0 -15
  102. amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
  103. amulet/game/translate/_functions/_code_functions/_text.py +0 -553
  104. amulet/game/translate/_functions/_code_functions/banner_pattern.py +0 -67
  105. amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +0 -152
  106. amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +0 -88
  107. amulet/game/translate/_functions/_code_functions/bedrock_sign.py +0 -152
  108. amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +0 -16
  109. amulet/game/translate/_functions/_code_functions/custom_name.py +0 -146
  110. amulet/game/translate/_functions/_frozen.py +0 -66
  111. amulet/game/translate/_functions/_state.py +0 -54
  112. amulet/game/translate/_functions/_typing.py +0 -98
  113. amulet/game/translate/_functions/abc.py +0 -123
  114. amulet/game/translate/_functions/carry_nbt.py +0 -160
  115. amulet/game/translate/_functions/carry_properties.py +0 -80
  116. amulet/game/translate/_functions/code.py +0 -143
  117. amulet/game/translate/_functions/map_block_name.py +0 -66
  118. amulet/game/translate/_functions/map_nbt.py +0 -111
  119. amulet/game/translate/_functions/map_properties.py +0 -93
  120. amulet/game/translate/_functions/multiblock.py +0 -112
  121. amulet/game/translate/_functions/new_block.py +0 -42
  122. amulet/game/translate/_functions/new_entity.py +0 -43
  123. amulet/game/translate/_functions/new_nbt.py +0 -206
  124. amulet/game/translate/_functions/new_properties.py +0 -64
  125. amulet/game/translate/_functions/sequence.py +0 -51
  126. amulet/game/translate/_functions/walk_input_nbt.py +0 -331
  127. amulet/game/translate/_translator.py +0 -433
  128. amulet/img/__init__.py +0 -10
  129. amulet/img/missing_no.png +0 -0
  130. amulet/img/missing_pack.png +0 -0
  131. amulet/img/missing_world.png +0 -0
  132. amulet/io/binary_reader.hpp +0 -45
  133. amulet/io/binary_writer.hpp +0 -30
  134. amulet/item.py +0 -75
  135. amulet/level/__init__.pyi +0 -23
  136. amulet/level/_load.py +0 -100
  137. amulet/level/abc/__init__.py +0 -12
  138. amulet/level/abc/_chunk_handle.py +0 -358
  139. amulet/level/abc/_dimension.py +0 -86
  140. amulet/level/abc/_history/__init__.py +0 -1
  141. amulet/level/abc/_history/_cache.py +0 -224
  142. amulet/level/abc/_history/_history_manager.py +0 -291
  143. amulet/level/abc/_level/__init__.py +0 -5
  144. amulet/level/abc/_level/_compactable_level.py +0 -10
  145. amulet/level/abc/_level/_creatable_level.py +0 -28
  146. amulet/level/abc/_level/_disk_level.py +0 -17
  147. amulet/level/abc/_level/_level.py +0 -449
  148. amulet/level/abc/_level/_loadable_level.py +0 -42
  149. amulet/level/abc/_player_storage.py +0 -7
  150. amulet/level/abc/_raw_level.py +0 -187
  151. amulet/level/abc/_registry.py +0 -40
  152. amulet/level/java/__init__.pyi +0 -16
  153. amulet/level/java/_chunk_handle.py +0 -17
  154. amulet/level/java/_dimension.py +0 -20
  155. amulet/level/java/_level.py +0 -184
  156. amulet/level/java/_raw/__init__.pyi +0 -15
  157. amulet/level/java/_raw/_chunk.pyi +0 -23
  158. amulet/level/java/_raw/_constant.py +0 -9
  159. amulet/level/java/_raw/_data_pack/__init__.py +0 -2
  160. amulet/level/java/_raw/_data_pack/data_pack.py +0 -241
  161. amulet/level/java/_raw/_data_pack/data_pack_manager.py +0 -77
  162. amulet/level/java/_raw/_dimension.py +0 -86
  163. amulet/level/java/_raw/_level.py +0 -507
  164. amulet/level/java/_raw/_typing.py +0 -3
  165. amulet/level/java/_raw/java_chunk_decode.cpp +0 -531
  166. amulet/level/java/_raw/java_chunk_decode.hpp +0 -23
  167. amulet/level/java/_raw/java_chunk_encode.cpp +0 -25
  168. amulet/level/java/_raw/java_chunk_encode.hpp +0 -23
  169. amulet/level/java/anvil/__init__.py +0 -2
  170. amulet/level/java/anvil/_dimension.py +0 -170
  171. amulet/level/java/anvil/_region.py +0 -421
  172. amulet/level/java/anvil/_sector_manager.py +0 -223
  173. amulet/level/java/chunk.pyi +0 -81
  174. amulet/level/java/chunk_/_chunk.py +0 -260
  175. amulet/level/java/chunk_/components/inhabited_time.py +0 -12
  176. amulet/level/java/chunk_/components/last_update.py +0 -12
  177. amulet/level/java/chunk_/components/legacy_version.py +0 -12
  178. amulet/level/java/chunk_/components/light_populated.py +0 -12
  179. amulet/level/java/chunk_/components/named_height_2d.py +0 -37
  180. amulet/level/java/chunk_/components/status.py +0 -11
  181. amulet/level/java/chunk_/components/terrain_populated.py +0 -12
  182. amulet/level/java/chunk_components/data_version_component.cpp +0 -32
  183. amulet/level/java/chunk_components/data_version_component.hpp +0 -31
  184. amulet/level/java/chunk_components/java_raw_chunk_component.cpp +0 -56
  185. amulet/level/java/chunk_components/java_raw_chunk_component.hpp +0 -45
  186. amulet/level/java/chunk_components.pyi +0 -22
  187. amulet/level/java/java_chunk.cpp +0 -170
  188. amulet/level/java/java_chunk.hpp +0 -141
  189. amulet/level/java/long_array.hpp +0 -175
  190. amulet/level/java/long_array.pyi +0 -39
  191. amulet/level/temporary_level/__init__.py +0 -1
  192. amulet/level/temporary_level/_level.py +0 -16
  193. amulet/mesh/__init__.py +0 -0
  194. amulet/mesh/block/__init__.pyi +0 -301
  195. amulet/mesh/block/_cube.py +0 -198
  196. amulet/mesh/block/_missing_block.py +0 -20
  197. amulet/mesh/block/block_mesh.cpp +0 -107
  198. amulet/mesh/block/block_mesh.hpp +0 -207
  199. amulet/mesh/util.py +0 -17
  200. amulet/palette/biome_palette.hpp +0 -85
  201. amulet/palette/block_palette.cpp +0 -32
  202. amulet/palette/block_palette.hpp +0 -93
  203. amulet/player.py +0 -62
  204. amulet/pybind11/collections.hpp +0 -118
  205. amulet/pybind11/numpy.hpp +0 -26
  206. amulet/pybind11/py_module.hpp +0 -34
  207. amulet/pybind11/type_hints.hpp +0 -51
  208. amulet/pybind11/types.hpp +0 -25
  209. amulet/pybind11/typing.hpp +0 -7
  210. amulet/resource_pack/__init__.py +0 -63
  211. amulet/resource_pack/abc/__init__.py +0 -2
  212. amulet/resource_pack/abc/resource_pack.py +0 -38
  213. amulet/resource_pack/abc/resource_pack_manager.py +0 -85
  214. amulet/resource_pack/java/__init__.py +0 -2
  215. amulet/resource_pack/java/download_resources.py +0 -212
  216. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_black.png +0 -0
  217. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_blue.png +0 -0
  218. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_brown.png +0 -0
  219. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_cyan.png +0 -0
  220. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_gray.png +0 -0
  221. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_green.png +0 -0
  222. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_blue.png +0 -0
  223. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_light_gray.png +0 -0
  224. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_lime.png +0 -0
  225. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_magenta.png +0 -0
  226. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_orange.png +0 -0
  227. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_pink.png +0 -0
  228. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_purple.png +0 -0
  229. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_red.png +0 -0
  230. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_white.png +0 -0
  231. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/banner/banner_yellow.png +0 -0
  232. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/barrier.png +0 -0
  233. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/end_portal.png +0 -0
  234. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/grass.png +0 -0
  235. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/lava.png +0 -0
  236. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/structure_void.png +0 -0
  237. amulet/resource_pack/java/java_vanilla_fix/assets/minecraft/textures/block/water.png +0 -0
  238. amulet/resource_pack/java/java_vanilla_fix/pack.png +0 -0
  239. amulet/resource_pack/java/resource_pack.py +0 -44
  240. amulet/resource_pack/java/resource_pack_manager.py +0 -563
  241. amulet/resource_pack/unknown_resource_pack.py +0 -10
  242. amulet/selection/__init__.py +0 -2
  243. amulet/selection/abstract_selection.py +0 -342
  244. amulet/selection/box.py +0 -852
  245. amulet/selection/group.py +0 -481
  246. amulet/utils/__init__.pyi +0 -23
  247. amulet/utils/call_spec/__init__.py +0 -24
  248. amulet/utils/call_spec/_call_spec.py +0 -257
  249. amulet/utils/cast.py +0 -10
  250. amulet/utils/comment_json.py +0 -188
  251. amulet/utils/format_utils.py +0 -41
  252. amulet/utils/generator.py +0 -18
  253. amulet/utils/matrix.py +0 -243
  254. amulet/utils/numpy.hpp +0 -36
  255. amulet/utils/numpy.pyi +0 -11
  256. amulet/utils/numpy_helpers.py +0 -19
  257. amulet/utils/shareable_lock.py +0 -335
  258. amulet/utils/signal/__init__.py +0 -10
  259. amulet/utils/signal/_signal.py +0 -228
  260. amulet/utils/task_manager.py +0 -235
  261. amulet/utils/typed_property.py +0 -111
  262. amulet/utils/weakref.py +0 -70
  263. amulet/utils/world_utils.py +0 -102
  264. amulet/version.cpp +0 -136
  265. amulet/version.hpp +0 -142
  266. amulet/version.py.cpp +0 -281
  267. amulet_core-2.0a8.dist-info/RECORD +0 -241
  268. amulet_core-2.0a8.dist-info/entry_points.txt +0 -2
  269. /amulet/{__pyinstaller → core/__pyinstaller}/__init__.py +0 -0
  270. /amulet/{py.typed → core/py.typed} +0 -0
  271. {amulet_core-2.0a8.dist-info → amulet_core-2.0.1.0.1297307203.19.43.34808.0a1.dist-info}/top_level.txt +0 -0
@@ -1,224 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import shutil
4
- from typing import Optional, Callable, cast, IO, Self
5
- from threading import Lock
6
- import os
7
- from weakref import ref, finalize
8
- import time
9
- import glob
10
- import re
11
- import tempfile
12
-
13
- import portalocker
14
- from leveldb import LevelDB
15
- from amulet.utils.weakref import CallableWeakMethod
16
-
17
-
18
- TempPattern = re.compile(r"amulettmp.*?-(?P<time>\d+)")
19
-
20
-
21
- def _temp_dir() -> str:
22
- temp_dir = os.environ.get("CACHE_DIR")
23
- if temp_dir is None:
24
- raise RuntimeError
25
- return temp_dir
26
-
27
-
28
- def _clear_temp_dirs() -> None:
29
- """
30
- Try and delete historic temporary directories.
31
- If things went very wrong in past sessions temporary directories may still exist.
32
- """
33
- for path in glob.glob(
34
- os.path.join(glob.escape(tempfile.gettempdir()), "amulettmp*")
35
- ) + glob.glob(
36
- os.path.join(glob.escape(_temp_dir()), "**", "amulettmp*"), recursive=True
37
- ):
38
- name = os.path.basename(path)
39
- match = TempPattern.fullmatch(name)
40
- if match and int(match.group("time")) < (time.time() - 7 * 24 * 3600):
41
- lock_path = os.path.join(path, "lock")
42
- if os.path.exists(lock_path):
43
- with open(lock_path) as lock:
44
- # make sure it is not locked by another process
45
- try:
46
- portalocker.lock(lock, portalocker.LockFlags.EXCLUSIVE)
47
- except:
48
- continue
49
- else:
50
- portalocker.unlock(lock)
51
- shutil.rmtree(path, ignore_errors=True)
52
-
53
-
54
- _clear_temp_dirs()
55
-
56
-
57
- class TempDir(str):
58
- """
59
- A temporary directory to do with as you wish.
60
-
61
- >>> t = TempDir()
62
- >>> path = os.path.join(t, "your_file.txt") # TempDir is a subclass of str
63
- >>> # make sure all files in the temporary directory are closed before releasing or closing this object.
64
- >>> # The temporary directory will be deleted when the last reference to `t` is lost or when `t.close()` is called
65
- """
66
-
67
- __lock: IO | None
68
- __finalise: finalize
69
-
70
- def __new__(cls, group: str) -> Self:
71
- cache_dir = os.path.join(_temp_dir(), group)
72
- os.makedirs(cache_dir, exist_ok=True)
73
- return super().__new__(
74
- cls,
75
- tempfile.mkdtemp(
76
- prefix="amulettmp",
77
- suffix=f"-{time.time():.0f}",
78
- dir=cache_dir,
79
- ),
80
- )
81
-
82
- def __init__(self, group: str) -> None:
83
- self.__lock = open(os.path.join(self, "lock"), "w")
84
- portalocker.lock(self.__lock, portalocker.LockFlags.EXCLUSIVE)
85
- self.__finalise = finalize(self, CallableWeakMethod(self._close))
86
-
87
- def _close(self) -> None:
88
- if self.__lock is not None:
89
- portalocker.unlock(self.__lock)
90
- self.__lock.close()
91
- self.__lock = None
92
- shutil.rmtree(self)
93
-
94
- def close(self) -> None:
95
- """Close the lock and delete the directory."""
96
- self.__finalise()
97
-
98
- def __del__(self) -> None:
99
- self.__finalise()
100
-
101
-
102
- class DiskCache:
103
- """
104
- A key, value database with a fast access RAM component and a longer term storage disk component.
105
- Keys and values are both bytes.
106
- The disk component is a leveldb database.
107
- """
108
-
109
- def __init__(self, path: str, max_size: int) -> None:
110
- """
111
- Create a new DiskCache
112
- :param path: The path to save the disk component to.
113
- :param max_size: The maximum amount of RAM that values can occupy. Key size is assumed negligible.
114
- When this is overflowed, the least recently used entries are unloaded to the disk storage.
115
- """
116
- self._lock = Lock()
117
- self._ram: dict[bytes, tuple[bytes, bool]] = {}
118
- self._path = path
119
- self._disk = LevelDB(path, create_if_missing=True)
120
- self._max_size: int = max_size
121
- self._size: int = 0
122
- self.__finalise = finalize(self, CallableWeakMethod(self._close))
123
-
124
- def _close(self) -> None:
125
- self._disk.close()
126
- shutil.rmtree(self._path, ignore_errors=True)
127
-
128
- def __del__(self) -> None:
129
- self.__finalise()
130
-
131
- @property
132
- def max_size(self) -> int:
133
- return self._max_size
134
-
135
- @max_size.setter
136
- def max_size(self, max_size: int) -> None:
137
- if not isinstance(max_size, int):
138
- raise TypeError
139
- with self._lock:
140
- self._max_size = max_size
141
- self._free()
142
-
143
- def __setitem__(self, key: bytes, value: bytes) -> None:
144
- with self._lock:
145
- self._remove(key)
146
- self._ram[key] = (value, True)
147
- self._size += len(value)
148
- self._free()
149
-
150
- def _remove(self, key: bytes) -> None:
151
- if key in self._ram:
152
- data = self._ram.pop(key)[0]
153
- self._size -= len(data)
154
-
155
- def __delitem__(self, key: bytes) -> None:
156
- with self._lock:
157
- self._remove(key)
158
- if key in self._disk:
159
- del self._disk[key]
160
-
161
- def _free(self) -> None:
162
- """Push some values to disk"""
163
- if self._size > self._max_size:
164
- keys = iter(self._ram.copy())
165
- while self._size > self._max_size:
166
- key = next(keys)
167
- value, changed = self._ram.pop(key)
168
- self._size -= len(value)
169
- if changed:
170
- self._disk[key] = value
171
-
172
- def __getitem__(self, key: bytes) -> bytes:
173
- with self._lock:
174
- if key in self._ram:
175
- value = self._ram.pop(key)
176
- # Push it to the end
177
- self._ram[key] = value
178
- return value[0]
179
- elif key in self._disk:
180
- data = self._disk[key]
181
- self._ram[key] = (data, False)
182
- self._size += len(data)
183
- self._free()
184
- return data
185
- else:
186
- raise KeyError
187
-
188
-
189
- class GlobalDiskCache(DiskCache):
190
- _instance_ref: Callable[[], Optional[GlobalDiskCache]] = cast(
191
- Callable[[], Optional["GlobalDiskCache"]], lambda: None
192
- )
193
- _cache_size = 100_000_000
194
-
195
- @classmethod
196
- def instance(cls) -> GlobalDiskCache:
197
- """
198
- Get the global disk cache instance.
199
- The caller must store a strong reference to the returned value otherwise it will be destroyed.
200
- """
201
- instance: Optional[GlobalDiskCache] = cls._instance_ref()
202
- if instance is None:
203
- instance = GlobalDiskCache()
204
- cls._instance_ref = ref(instance)
205
- return instance
206
-
207
- @classmethod
208
- def cache_size(cls) -> int:
209
- instance: Optional[GlobalDiskCache] = cls._instance_ref()
210
- if instance is None:
211
- return cls._cache_size
212
- else:
213
- return instance.max_size
214
-
215
- @classmethod
216
- def set_cache_size(cls, size: int) -> None:
217
- instance: Optional[GlobalDiskCache] = cls._instance_ref()
218
- cls._cache_size = size
219
- if instance is not None:
220
- instance.max_size = size
221
-
222
- def __init__(self) -> None:
223
- self._temp_dir = TempDir("level_data")
224
- super().__init__(os.path.join(self._temp_dir, "history_db"), 100_000_000)
@@ -1,291 +0,0 @@
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)
@@ -1,5 +0,0 @@
1
- from ._level import LevelOpenData, Level, LevelT, LevelFriend
2
- from ._compactable_level import CompactableLevel
3
- from ._creatable_level import CreatableLevel
4
- from ._disk_level import DiskLevel
5
- from ._loadable_level import LoadableLevel
@@ -1,10 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
-
4
- class CompactableLevel(ABC):
5
- __slots__ = ()
6
-
7
- @abstractmethod
8
- def compact(self) -> None:
9
- """Compact level data to reduce file size."""
10
- raise NotImplementedError
@@ -1,28 +0,0 @@
1
- from __future__ import annotations
2
- from abc import ABC, abstractmethod
3
- from typing import TYPE_CHECKING, Any
4
-
5
- from amulet.utils.call_spec import method_spec
6
-
7
-
8
- if TYPE_CHECKING:
9
- from ._level import Level
10
-
11
-
12
- class CreatableLevel(ABC):
13
- """Level extension class for levels that can be created without data."""
14
-
15
- __slots__ = ()
16
-
17
- @classmethod
18
- @abstractmethod
19
- @method_spec()
20
- def create(cls, *args: Any, **kwargs: Any) -> Level:
21
- """
22
- Create a new instance without any existing data.
23
- You must call :meth:`~amulet.level.abc.Level.open` to open the level for editing.
24
- :return: A new Level instance
25
- """
26
- # If writing data to disk, it must write a valid level.
27
- # If only setting attributes, the open method must be aware that it should not load data from disk.
28
- raise NotImplementedError
@@ -1,17 +0,0 @@
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