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.

Files changed (198) hide show
  1. amulet/__init__.py +27 -27
  2. amulet/__pyinstaller/__init__.py +2 -2
  3. amulet/__pyinstaller/hook-amulet.py +4 -4
  4. amulet/_version.py +21 -21
  5. amulet/api/__init__.py +2 -2
  6. amulet/api/abstract_base_entity.py +128 -128
  7. amulet/api/block.py +630 -630
  8. amulet/api/block_entity.py +71 -71
  9. amulet/api/cache.py +107 -107
  10. amulet/api/chunk/__init__.py +6 -6
  11. amulet/api/chunk/biomes.py +207 -207
  12. amulet/api/chunk/block_entity_dict.py +175 -175
  13. amulet/api/chunk/blocks.py +46 -46
  14. amulet/api/chunk/chunk.py +389 -389
  15. amulet/api/chunk/entity_list.py +75 -75
  16. amulet/api/chunk/status.py +167 -167
  17. amulet/api/data_types/__init__.py +4 -4
  18. amulet/api/data_types/generic_types.py +4 -4
  19. amulet/api/data_types/operation_types.py +16 -16
  20. amulet/api/data_types/world_types.py +49 -49
  21. amulet/api/data_types/wrapper_types.py +71 -71
  22. amulet/api/entity.py +74 -74
  23. amulet/api/errors.py +119 -119
  24. amulet/api/history/__init__.py +36 -36
  25. amulet/api/history/base/__init__.py +3 -3
  26. amulet/api/history/base/base_history.py +26 -26
  27. amulet/api/history/base/history_manager.py +63 -63
  28. amulet/api/history/base/revision_manager.py +73 -73
  29. amulet/api/history/changeable.py +15 -15
  30. amulet/api/history/data_types.py +7 -7
  31. amulet/api/history/history_manager/__init__.py +3 -3
  32. amulet/api/history/history_manager/container.py +102 -102
  33. amulet/api/history/history_manager/database.py +279 -279
  34. amulet/api/history/history_manager/meta.py +93 -93
  35. amulet/api/history/history_manager/object.py +116 -116
  36. amulet/api/history/revision_manager/__init__.py +2 -2
  37. amulet/api/history/revision_manager/disk.py +33 -33
  38. amulet/api/history/revision_manager/ram.py +12 -12
  39. amulet/api/item.py +75 -75
  40. amulet/api/level/__init__.py +4 -4
  41. amulet/api/level/base_level/__init__.py +1 -1
  42. amulet/api/level/base_level/base_level.py +1035 -1026
  43. amulet/api/level/base_level/chunk_manager.py +227 -227
  44. amulet/api/level/base_level/clone.py +389 -389
  45. amulet/api/level/base_level/player_manager.py +101 -101
  46. amulet/api/level/immutable_structure/__init__.py +1 -1
  47. amulet/api/level/immutable_structure/immutable_structure.py +94 -94
  48. amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
  49. amulet/api/level/structure.py +22 -22
  50. amulet/api/level/world.py +19 -19
  51. amulet/api/partial_3d_array/__init__.py +2 -2
  52. amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
  53. amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
  54. amulet/api/partial_3d_array/data_types.py +15 -15
  55. amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
  56. amulet/api/partial_3d_array/util.py +152 -152
  57. amulet/api/player.py +65 -65
  58. amulet/api/registry/__init__.py +2 -2
  59. amulet/api/registry/base_registry.py +34 -34
  60. amulet/api/registry/biome_manager.py +153 -153
  61. amulet/api/registry/block_manager.py +156 -156
  62. amulet/api/selection/__init__.py +2 -2
  63. amulet/api/selection/abstract_selection.py +315 -315
  64. amulet/api/selection/box.py +805 -805
  65. amulet/api/selection/group.py +488 -488
  66. amulet/api/structure.py +37 -37
  67. amulet/api/wrapper/__init__.py +8 -8
  68. amulet/api/wrapper/chunk/interface.py +441 -441
  69. amulet/api/wrapper/chunk/translator.py +567 -567
  70. amulet/api/wrapper/format_wrapper.py +772 -772
  71. amulet/api/wrapper/structure_format_wrapper.py +116 -116
  72. amulet/api/wrapper/world_format_wrapper.py +63 -63
  73. amulet/level/__init__.py +1 -1
  74. amulet/level/formats/anvil_forge_world.py +40 -40
  75. amulet/level/formats/anvil_world/__init__.py +3 -3
  76. amulet/level/formats/anvil_world/_sector_manager.py +291 -384
  77. amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
  78. amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
  79. amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
  80. amulet/level/formats/anvil_world/dimension.py +177 -177
  81. amulet/level/formats/anvil_world/format.py +769 -769
  82. amulet/level/formats/anvil_world/region.py +384 -384
  83. amulet/level/formats/construction/__init__.py +3 -3
  84. amulet/level/formats/construction/format_wrapper.py +515 -515
  85. amulet/level/formats/construction/interface.py +134 -134
  86. amulet/level/formats/construction/section.py +60 -60
  87. amulet/level/formats/construction/util.py +165 -165
  88. amulet/level/formats/leveldb_world/__init__.py +3 -3
  89. amulet/level/formats/leveldb_world/chunk.py +33 -33
  90. amulet/level/formats/leveldb_world/dimension.py +385 -419
  91. amulet/level/formats/leveldb_world/format.py +659 -641
  92. amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
  93. amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
  94. amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
  95. amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
  96. amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
  97. amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
  98. amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
  99. amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
  100. amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
  101. amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
  102. amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
  103. amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
  104. amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
  105. amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
  106. amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
  107. amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
  108. amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
  109. amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
  110. amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
  111. amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
  112. amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
  113. amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
  114. amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
  115. amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
  116. amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
  117. amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
  118. amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
  119. amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
  120. amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
  121. amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
  122. amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
  123. amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
  124. amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
  125. amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
  126. amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
  127. amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
  128. amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
  129. amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
  130. amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
  131. amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
  132. amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
  133. amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
  134. amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
  135. amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
  136. amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
  137. amulet/level/formats/mcstructure/__init__.py +3 -3
  138. amulet/level/formats/mcstructure/chunk.py +50 -50
  139. amulet/level/formats/mcstructure/format_wrapper.py +408 -408
  140. amulet/level/formats/mcstructure/interface.py +175 -175
  141. amulet/level/formats/schematic/__init__.py +3 -3
  142. amulet/level/formats/schematic/chunk.py +55 -55
  143. amulet/level/formats/schematic/data_types.py +4 -4
  144. amulet/level/formats/schematic/format_wrapper.py +373 -373
  145. amulet/level/formats/schematic/interface.py +142 -142
  146. amulet/level/formats/sponge_schem/__init__.py +4 -4
  147. amulet/level/formats/sponge_schem/chunk.py +62 -62
  148. amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
  149. amulet/level/formats/sponge_schem/interface.py +118 -118
  150. amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
  151. amulet/level/formats/sponge_schem/varint/varint.py +87 -87
  152. amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
  153. amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
  154. amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
  155. amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
  156. amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
  157. amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
  158. amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
  159. amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
  160. amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
  161. amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
  162. amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
  163. amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
  164. amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
  165. amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
  166. amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
  167. amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
  168. amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
  169. amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
  170. amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
  171. amulet/level/load.py +59 -59
  172. amulet/level/loader.py +95 -95
  173. amulet/level/translators/chunk/bedrock/__init__.py +267 -267
  174. amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
  175. amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
  176. amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
  177. amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
  178. amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
  179. amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
  180. amulet/libs/leveldb/__init__.py +7 -7
  181. amulet/operations/__init__.py +5 -5
  182. amulet/operations/clone.py +18 -18
  183. amulet/operations/delete_chunk.py +32 -32
  184. amulet/operations/fill.py +30 -30
  185. amulet/operations/paste.py +65 -65
  186. amulet/operations/replace.py +58 -58
  187. amulet/utils/__init__.py +14 -14
  188. amulet/utils/format_utils.py +41 -41
  189. amulet/utils/generator.py +15 -15
  190. amulet/utils/matrix.py +243 -243
  191. amulet/utils/numpy_helpers.py +46 -46
  192. amulet/utils/world_utils.py +349 -349
  193. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
  194. amulet_core-1.9.20.dist-info/RECORD +208 -0
  195. amulet_core-1.9.19.dist-info/RECORD +0 -208
  196. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
  197. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
  198. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
@@ -1,349 +1,349 @@
1
- from __future__ import annotations
2
-
3
- import math
4
- import sys
5
- import gzip
6
- from io import StringIO
7
- from typing import Tuple, Optional
8
- import numpy
9
- from numpy import ndarray, zeros, uint8
10
- from amulet.api.data_types import ChunkCoordinates
11
-
12
-
13
- # depreciated and will be removed
14
- SECTOR_BYTES = 4096
15
- SECTOR_INTS = SECTOR_BYTES / 4
16
- CHUNK_HEADER_SIZE = 5
17
- VERSION_GZIP = 1
18
- VERSION_DEFLATE = 2
19
-
20
-
21
- def block_coords_to_chunk_coords(
22
- *args: int, sub_chunk_size: int = 16
23
- ) -> Tuple[int, ...]:
24
- """
25
- Converts the supplied block coordinates into chunk coordinates
26
-
27
- :param args: The coordinate of the block(s)
28
- :param sub_chunk_size: The dimension of the chunk (Optional. Default 16)
29
- :return: The resulting chunk coordinates in (x, z) order
30
- """
31
- return tuple(int(math.floor(coord / sub_chunk_size)) for coord in args)
32
-
33
-
34
- def chunk_coords_to_block_coords(
35
- x: int, z: int, chunk_x_size: int = 16, chunk_z_size: int = 16
36
- ) -> ChunkCoordinates:
37
- """
38
- Converts the supplied chunk coordinates into block coordinates
39
-
40
- :param x: The x coordinate of the chunk
41
- :param z: The z coordinate of the chunk
42
- :param chunk_x_size: The dimension of the chunk in the x direction (Optional. Default 16)
43
- :param chunk_z_size: The dimension of the chunk in the z direction (Optional. Default 16)
44
- :return: The resulting block coordinates in (x, z) order
45
- """
46
- return x * chunk_x_size, z * chunk_z_size
47
-
48
-
49
- def chunk_coords_to_region_coords(cx: int, cz: int) -> ChunkCoordinates:
50
- """
51
- Converts the supplied chunk coordinates into region coordinates
52
-
53
- :param cx: The x coordinate of the chunk
54
- :param cz: The z coordinate of the chunk
55
- :return: The resulting region coordinates in (x, z) order
56
- """
57
- return cx >> 5, cz >> 5
58
-
59
-
60
- def region_coords_to_chunk_coords(rx: int, rz: int) -> ChunkCoordinates:
61
- """
62
- Converts the supplied region coordinates into the minimum chunk coordinates of that region
63
-
64
- :param rx: The x coordinate of the region
65
- :param rz: The y coordinate of the region
66
- :return: The resulting minimum chunk coordinates of that region in (x, z) order
67
- """
68
- return rx << 5, rz << 5
69
-
70
-
71
- def blocks_slice_to_chunk_slice(
72
- blocks_slice: slice, chunk_shape: int, chunk_coord: int
73
- ) -> slice:
74
- """
75
- Converts the supplied blocks slice into chunk slice
76
-
77
- :param blocks_slice: The slice of the blocks
78
- :param chunk_shape: The shape of the chunk in this direction
79
- :param chunk_coord: The coordinate of the chunk in this direction
80
- :return: The resulting chunk slice
81
- """
82
- return slice(
83
- min(max(0, blocks_slice.start - chunk_coord * chunk_shape), chunk_shape),
84
- min(max(0, blocks_slice.stop - chunk_coord * chunk_shape), chunk_shape),
85
- )
86
-
87
-
88
- def gunzip(data):
89
- """
90
- Decompresses data that is in Gzip format
91
- """
92
- return gzip.GzipFile(fileobj=StringIO(data)).read()
93
-
94
-
95
- def from_nibble_array(arr: ndarray) -> ndarray:
96
- """
97
- Unpacks a flat nibble array into a full size numpy array
98
-
99
- :param arr: The nibble array
100
- :return: The resulting array
101
- """
102
- shape = arr.size
103
-
104
- new_arr = zeros((shape * 2), dtype=uint8)
105
-
106
- new_arr[::2] = arr & 0xF
107
- new_arr[1::2] = arr >> 4
108
-
109
- return new_arr
110
-
111
-
112
- def to_nibble_array(arr: ndarray) -> ndarray:
113
- """
114
- packs a full size numpy array into a nibble array.
115
-
116
- :param arr: Full numpy array
117
- :return: The nibble array
118
- """
119
- arr = arr.ravel()
120
- return (arr[::2] + (arr[1::2] << 4)).astype("uint8")
121
-
122
-
123
- """
124
- Minecraft Java edition stores the block and height arrays in a compacted long array format.
125
- The format stores one or more entries per long, using the fewest number of bits required to store the data.
126
- There are two storage methods, the compact version was used prior to 1.16 and the less compact version in 1.16 and above.
127
- Apparently the less compact version is quicker to pack and unpack.
128
- The compact version effectively stores the values as a bit array spanning one or more values in the long array.
129
- There may be some padding if the bit array does not fill all the long values. (The letter "P" signifies an unused padding bit)
130
- PPAAAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGGG GHHHHHHHHHIIIIIIIIIJJJJJJJJJKKKKKKKKKLLLLLLLLLMMMMMMMMMOOOOOOOOO
131
- The less compact version does not allow entries to straddle long values. Instead, if required, there is padding within each long.
132
- PAAAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGGGG PHHHHHHHHHIIIIIIIIIJJJJJJJJJKKKKKKKKKLLLLLLLLLMMMMMMMMMOOOOOOOOO
133
-
134
- The functions below can be used to pack and unpack both formats.
135
- """
136
-
137
-
138
- def decode_long_array(
139
- long_array: numpy.ndarray,
140
- size: int,
141
- bits_per_entry: int,
142
- dense=True,
143
- signed: bool = False,
144
- ) -> numpy.ndarray:
145
- """
146
- Decode a long array (from BlockStates or Heightmaps)
147
-
148
- :param long_array: Encoded long array
149
- :param size: The expected size of the returned array
150
- :param bits_per_entry: The number of bits per entry in the encoded array.
151
- :param dense: If true the long arrays will be treated as a bit stream. If false they are distinct values with padding
152
- :param signed: Should the returned array be signed.
153
- :return: Decoded array as numpy array
154
- """
155
- # validate the inputs and throw an error if there is a problem
156
- if not isinstance(bits_per_entry, int):
157
- raise ValueError(f"The bits_per_entry input must be an int.")
158
-
159
- assert (
160
- 1 <= bits_per_entry <= 64
161
- ), f"bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
162
-
163
- # force the array to be a signed long array
164
- long_array = long_array.astype(">q")
165
-
166
- if dense:
167
- expected_len = math.ceil(size * bits_per_entry / 64)
168
- else:
169
- expected_len = math.ceil(size / (64 // bits_per_entry))
170
- if len(long_array) != expected_len:
171
- raise Exception(
172
- f"{'Dense e' if dense else 'E'}ncoded long array with {bits_per_entry} bits per entry should contain {expected_len} longs but got {len(long_array)}."
173
- )
174
-
175
- # unpack the long array into a bit array
176
- bits = numpy.unpackbits(long_array[::-1].astype(">i8").view("uint8"))
177
- if dense:
178
- if bits.size % bits_per_entry:
179
- # if the array is densely packed and there is extra padding, remove it
180
- bits = bits[bits.size % bits_per_entry :]
181
- else:
182
- # if not densely packed remove the padding per long
183
- entry_per_long = 64 // bits_per_entry
184
- bits = bits.reshape(-1, 64)[:, -entry_per_long * bits_per_entry :]
185
-
186
- byte_length = 2 ** math.ceil(math.log(math.ceil(bits_per_entry / 8), 2))
187
- dtype = {1: "B", 2: ">H", 4: ">I", 8: ">Q"}[byte_length]
188
-
189
- # pad the bits to fill one of the above data types
190
- arr = numpy.packbits(
191
- numpy.pad(
192
- bits.reshape(-1, bits_per_entry)[-size:, :],
193
- [(0, 0), (byte_length * 8 - bits_per_entry, 0)],
194
- "constant",
195
- )
196
- ).view(dtype=dtype)[::-1]
197
- if signed:
198
- # convert to a signed array if requested
199
- sarray = arr.astype({1: "b", 2: ">h", 4: ">i", 8: ">q"}[byte_length])
200
- if bits_per_entry < 64:
201
- mask = arr >= 2 ** (bits_per_entry - 1)
202
- sarray[mask] = numpy.subtract(
203
- arr[mask], 2**bits_per_entry, dtype=numpy.int64
204
- )
205
- arr = sarray
206
- return arr
207
-
208
-
209
- def encode_long_array(
210
- array: numpy.ndarray,
211
- bits_per_entry: Optional[int] = None,
212
- dense: bool = True,
213
- min_bits_per_entry=1,
214
- ) -> numpy.ndarray:
215
- """
216
- Encode a long array (from BlockStates or Heightmaps)
217
-
218
- :param array: A numpy array of the data to be encoded.
219
- :param bits_per_entry: The number of bits to use to store each value. If left as None will use the smallest bits per entry.
220
- :param dense: If true the long arrays will be treated as a bit stream. If false they are distinct values with padding
221
- :param min_bits_per_entry: The mimimum value that bits_per_entry can be. If it is less than this it will be capped at this value.
222
- :return: Encoded array as numpy array
223
- """
224
- assert (
225
- 1 <= min_bits_per_entry <= 64
226
- ), f"min_bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
227
- # cast to a signed longlong array
228
- array = array.astype(">q")
229
- # work out how many bits are required to store the
230
- required_bits_per_entry = max(
231
- max(
232
- int(numpy.amin(array)).bit_length(),
233
- int(numpy.amax(array)).bit_length(),
234
- ),
235
- min_bits_per_entry,
236
- )
237
- if bits_per_entry is None:
238
- # if a bit depth has not been requested use the minimum required
239
- bits_per_entry = required_bits_per_entry
240
- elif isinstance(bits_per_entry, int):
241
- assert (
242
- 1 <= bits_per_entry <= 64
243
- ), f"bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
244
- # if a bit depth has been set and it is smaller than what is required throw an error
245
- if required_bits_per_entry > bits_per_entry:
246
- raise Exception(
247
- f"The array requires at least {required_bits_per_entry} bits per value which is more than the specified {bits_per_entry} bits"
248
- )
249
- else:
250
- raise ValueError(
251
- "bits_per_entry must be an int between 1 and 64 inclusive or None."
252
- )
253
- # make the negative values positive to make bit storage easier
254
- uarray = array.astype(">Q")
255
- if bits_per_entry < 64:
256
- mask = array < 0
257
- uarray[mask] = numpy.add(
258
- array[mask], 2**bits_per_entry, dtype=numpy.uint64, casting="unsafe"
259
- )
260
- array = uarray
261
-
262
- # unpack the individual values into a bit array
263
- bits: numpy.ndarray = numpy.unpackbits(
264
- numpy.ascontiguousarray(array[::-1]).view("uint8")
265
- ).reshape(-1, 64)[:, -bits_per_entry:]
266
- if dense:
267
- if bits.size % 64:
268
- # if the bit array does not fill a whole long
269
- # add padding to the last long if required
270
- bits = numpy.pad(
271
- bits.ravel(),
272
- [(64 - (bits.size % 64), 0)],
273
- "constant",
274
- )
275
- else:
276
- # if the array is not dense add padding
277
- entry_per_long = 64 // bits_per_entry
278
- if bits.shape[0] % entry_per_long:
279
- # add padding to the last long if required
280
- bits = numpy.pad(
281
- bits,
282
- [(entry_per_long - (bits.shape[0] % entry_per_long), 0), (0, 0)],
283
- "constant",
284
- )
285
- # add padding for each long
286
- bits = numpy.pad(
287
- bits.reshape(-1, bits_per_entry * entry_per_long),
288
- [(0, 0), (64 - bits_per_entry * entry_per_long, 0)],
289
- "constant",
290
- )
291
-
292
- # pack the bits into a long array
293
- return numpy.packbits(bits).view(dtype=">q")[::-1]
294
-
295
-
296
- def get_size(obj, seen=None):
297
- """Recursively finds size of objects"""
298
- size = sys.getsizeof(obj)
299
- if seen is None:
300
- seen = set()
301
- obj_id = id(obj)
302
- if obj_id in seen:
303
- return 0
304
-
305
- # Important mark as seen *before* entering recursion to gracefully handle
306
- # self-referential objects
307
- seen.add(obj_id)
308
- if isinstance(obj, dict):
309
- size += sum([get_size(v, seen) for v in obj.values()])
310
- size += sum([get_size(k, seen) for k in obj.keys()])
311
- elif hasattr(obj, "__dict__"):
312
- size += get_size(obj.__dict__, seen)
313
- elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
314
- size += sum([get_size(i, seen) for i in obj])
315
- return size
316
-
317
-
318
- def get_smallest_dtype(arr: ndarray, uint: bool = True) -> int:
319
- """
320
- Returns the smallest dtype (number) that the array can afford
321
-
322
- :param arr: The array to check on
323
- :param uint: Should the array fit in uint or not (default: True)
324
- :return: The number of bits all the elements can be represented with
325
- """
326
- possible_dtypes = (2**x for x in range(3, 8))
327
- max_number = numpy.amax(arr)
328
- if not uint:
329
- max_number = max_number * 2
330
- if max_number == 0:
331
- max_number = 1
332
- return next(dtype for dtype in possible_dtypes if dtype > math.log(max_number, 2))
333
-
334
-
335
- def entity_position_to_chunk_coordinates(
336
- entity_coordinates: Tuple[float, float, float]
337
- ):
338
- return (
339
- int(math.floor(entity_coordinates[0])) >> 4,
340
- int(math.floor(entity_coordinates[2])) >> 4,
341
- )
342
-
343
-
344
- def fast_unique(array: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray]:
345
- uni = numpy.unique(array)
346
- lut = numpy.zeros(numpy.amax(uni) + 1, dtype=numpy.uint32)
347
- lut[uni] = numpy.arange(uni.size)
348
- inv = lut[array]
349
- return uni, inv
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import sys
5
+ import gzip
6
+ from io import StringIO
7
+ from typing import Tuple, Optional
8
+ import numpy
9
+ from numpy import ndarray, zeros, uint8
10
+ from amulet.api.data_types import ChunkCoordinates
11
+
12
+
13
+ # depreciated and will be removed
14
+ SECTOR_BYTES = 4096
15
+ SECTOR_INTS = SECTOR_BYTES / 4
16
+ CHUNK_HEADER_SIZE = 5
17
+ VERSION_GZIP = 1
18
+ VERSION_DEFLATE = 2
19
+
20
+
21
+ def block_coords_to_chunk_coords(
22
+ *args: int, sub_chunk_size: int = 16
23
+ ) -> Tuple[int, ...]:
24
+ """
25
+ Converts the supplied block coordinates into chunk coordinates
26
+
27
+ :param args: The coordinate of the block(s)
28
+ :param sub_chunk_size: The dimension of the chunk (Optional. Default 16)
29
+ :return: The resulting chunk coordinates in (x, z) order
30
+ """
31
+ return tuple(int(math.floor(coord / sub_chunk_size)) for coord in args)
32
+
33
+
34
+ def chunk_coords_to_block_coords(
35
+ x: int, z: int, chunk_x_size: int = 16, chunk_z_size: int = 16
36
+ ) -> ChunkCoordinates:
37
+ """
38
+ Converts the supplied chunk coordinates into block coordinates
39
+
40
+ :param x: The x coordinate of the chunk
41
+ :param z: The z coordinate of the chunk
42
+ :param chunk_x_size: The dimension of the chunk in the x direction (Optional. Default 16)
43
+ :param chunk_z_size: The dimension of the chunk in the z direction (Optional. Default 16)
44
+ :return: The resulting block coordinates in (x, z) order
45
+ """
46
+ return x * chunk_x_size, z * chunk_z_size
47
+
48
+
49
+ def chunk_coords_to_region_coords(cx: int, cz: int) -> ChunkCoordinates:
50
+ """
51
+ Converts the supplied chunk coordinates into region coordinates
52
+
53
+ :param cx: The x coordinate of the chunk
54
+ :param cz: The z coordinate of the chunk
55
+ :return: The resulting region coordinates in (x, z) order
56
+ """
57
+ return cx >> 5, cz >> 5
58
+
59
+
60
+ def region_coords_to_chunk_coords(rx: int, rz: int) -> ChunkCoordinates:
61
+ """
62
+ Converts the supplied region coordinates into the minimum chunk coordinates of that region
63
+
64
+ :param rx: The x coordinate of the region
65
+ :param rz: The y coordinate of the region
66
+ :return: The resulting minimum chunk coordinates of that region in (x, z) order
67
+ """
68
+ return rx << 5, rz << 5
69
+
70
+
71
+ def blocks_slice_to_chunk_slice(
72
+ blocks_slice: slice, chunk_shape: int, chunk_coord: int
73
+ ) -> slice:
74
+ """
75
+ Converts the supplied blocks slice into chunk slice
76
+
77
+ :param blocks_slice: The slice of the blocks
78
+ :param chunk_shape: The shape of the chunk in this direction
79
+ :param chunk_coord: The coordinate of the chunk in this direction
80
+ :return: The resulting chunk slice
81
+ """
82
+ return slice(
83
+ min(max(0, blocks_slice.start - chunk_coord * chunk_shape), chunk_shape),
84
+ min(max(0, blocks_slice.stop - chunk_coord * chunk_shape), chunk_shape),
85
+ )
86
+
87
+
88
+ def gunzip(data):
89
+ """
90
+ Decompresses data that is in Gzip format
91
+ """
92
+ return gzip.GzipFile(fileobj=StringIO(data)).read()
93
+
94
+
95
+ def from_nibble_array(arr: ndarray) -> ndarray:
96
+ """
97
+ Unpacks a flat nibble array into a full size numpy array
98
+
99
+ :param arr: The nibble array
100
+ :return: The resulting array
101
+ """
102
+ shape = arr.size
103
+
104
+ new_arr = zeros((shape * 2), dtype=uint8)
105
+
106
+ new_arr[::2] = arr & 0xF
107
+ new_arr[1::2] = arr >> 4
108
+
109
+ return new_arr
110
+
111
+
112
+ def to_nibble_array(arr: ndarray) -> ndarray:
113
+ """
114
+ packs a full size numpy array into a nibble array.
115
+
116
+ :param arr: Full numpy array
117
+ :return: The nibble array
118
+ """
119
+ arr = arr.ravel()
120
+ return (arr[::2] + (arr[1::2] << 4)).astype("uint8")
121
+
122
+
123
+ """
124
+ Minecraft Java edition stores the block and height arrays in a compacted long array format.
125
+ The format stores one or more entries per long, using the fewest number of bits required to store the data.
126
+ There are two storage methods, the compact version was used prior to 1.16 and the less compact version in 1.16 and above.
127
+ Apparently the less compact version is quicker to pack and unpack.
128
+ The compact version effectively stores the values as a bit array spanning one or more values in the long array.
129
+ There may be some padding if the bit array does not fill all the long values. (The letter "P" signifies an unused padding bit)
130
+ PPAAAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGGG GHHHHHHHHHIIIIIIIIIJJJJJJJJJKKKKKKKKKLLLLLLLLLMMMMMMMMMOOOOOOOOO
131
+ The less compact version does not allow entries to straddle long values. Instead, if required, there is padding within each long.
132
+ PAAAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDDEEEEEEEEEFFFFFFFFFGGGGGGGGG PHHHHHHHHHIIIIIIIIIJJJJJJJJJKKKKKKKKKLLLLLLLLLMMMMMMMMMOOOOOOOOO
133
+
134
+ The functions below can be used to pack and unpack both formats.
135
+ """
136
+
137
+
138
+ def decode_long_array(
139
+ long_array: numpy.ndarray,
140
+ size: int,
141
+ bits_per_entry: int,
142
+ dense=True,
143
+ signed: bool = False,
144
+ ) -> numpy.ndarray:
145
+ """
146
+ Decode a long array (from BlockStates or Heightmaps)
147
+
148
+ :param long_array: Encoded long array
149
+ :param size: The expected size of the returned array
150
+ :param bits_per_entry: The number of bits per entry in the encoded array.
151
+ :param dense: If true the long arrays will be treated as a bit stream. If false they are distinct values with padding
152
+ :param signed: Should the returned array be signed.
153
+ :return: Decoded array as numpy array
154
+ """
155
+ # validate the inputs and throw an error if there is a problem
156
+ if not isinstance(bits_per_entry, int):
157
+ raise ValueError(f"The bits_per_entry input must be an int.")
158
+
159
+ assert (
160
+ 1 <= bits_per_entry <= 64
161
+ ), f"bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
162
+
163
+ # force the array to be a signed long array
164
+ long_array = long_array.astype(">q")
165
+
166
+ if dense:
167
+ expected_len = math.ceil(size * bits_per_entry / 64)
168
+ else:
169
+ expected_len = math.ceil(size / (64 // bits_per_entry))
170
+ if len(long_array) != expected_len:
171
+ raise Exception(
172
+ f"{'Dense e' if dense else 'E'}ncoded long array with {bits_per_entry} bits per entry should contain {expected_len} longs but got {len(long_array)}."
173
+ )
174
+
175
+ # unpack the long array into a bit array
176
+ bits = numpy.unpackbits(long_array[::-1].astype(">i8").view("uint8"))
177
+ if dense:
178
+ if bits.size % bits_per_entry:
179
+ # if the array is densely packed and there is extra padding, remove it
180
+ bits = bits[bits.size % bits_per_entry :]
181
+ else:
182
+ # if not densely packed remove the padding per long
183
+ entry_per_long = 64 // bits_per_entry
184
+ bits = bits.reshape(-1, 64)[:, -entry_per_long * bits_per_entry :]
185
+
186
+ byte_length = 2 ** math.ceil(math.log(math.ceil(bits_per_entry / 8), 2))
187
+ dtype = {1: "B", 2: ">H", 4: ">I", 8: ">Q"}[byte_length]
188
+
189
+ # pad the bits to fill one of the above data types
190
+ arr = numpy.packbits(
191
+ numpy.pad(
192
+ bits.reshape(-1, bits_per_entry)[-size:, :],
193
+ [(0, 0), (byte_length * 8 - bits_per_entry, 0)],
194
+ "constant",
195
+ )
196
+ ).view(dtype=dtype)[::-1]
197
+ if signed:
198
+ # convert to a signed array if requested
199
+ sarray = arr.astype({1: "b", 2: ">h", 4: ">i", 8: ">q"}[byte_length])
200
+ if bits_per_entry < 64:
201
+ mask = arr >= 2 ** (bits_per_entry - 1)
202
+ sarray[mask] = numpy.subtract(
203
+ arr[mask], 2**bits_per_entry, dtype=numpy.int64
204
+ )
205
+ arr = sarray
206
+ return arr
207
+
208
+
209
+ def encode_long_array(
210
+ array: numpy.ndarray,
211
+ bits_per_entry: Optional[int] = None,
212
+ dense: bool = True,
213
+ min_bits_per_entry=1,
214
+ ) -> numpy.ndarray:
215
+ """
216
+ Encode a long array (from BlockStates or Heightmaps)
217
+
218
+ :param array: A numpy array of the data to be encoded.
219
+ :param bits_per_entry: The number of bits to use to store each value. If left as None will use the smallest bits per entry.
220
+ :param dense: If true the long arrays will be treated as a bit stream. If false they are distinct values with padding
221
+ :param min_bits_per_entry: The mimimum value that bits_per_entry can be. If it is less than this it will be capped at this value.
222
+ :return: Encoded array as numpy array
223
+ """
224
+ assert (
225
+ 1 <= min_bits_per_entry <= 64
226
+ ), f"min_bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
227
+ # cast to a signed longlong array
228
+ array = array.astype(">q")
229
+ # work out how many bits are required to store the
230
+ required_bits_per_entry = max(
231
+ max(
232
+ int(numpy.amin(array)).bit_length(),
233
+ int(numpy.amax(array)).bit_length(),
234
+ ),
235
+ min_bits_per_entry,
236
+ )
237
+ if bits_per_entry is None:
238
+ # if a bit depth has not been requested use the minimum required
239
+ bits_per_entry = required_bits_per_entry
240
+ elif isinstance(bits_per_entry, int):
241
+ assert (
242
+ 1 <= bits_per_entry <= 64
243
+ ), f"bits_per_entry must be between 1 and 64 inclusive. Got {bits_per_entry}"
244
+ # if a bit depth has been set and it is smaller than what is required throw an error
245
+ if required_bits_per_entry > bits_per_entry:
246
+ raise Exception(
247
+ f"The array requires at least {required_bits_per_entry} bits per value which is more than the specified {bits_per_entry} bits"
248
+ )
249
+ else:
250
+ raise ValueError(
251
+ "bits_per_entry must be an int between 1 and 64 inclusive or None."
252
+ )
253
+ # make the negative values positive to make bit storage easier
254
+ uarray = array.astype(">Q")
255
+ if bits_per_entry < 64:
256
+ mask = array < 0
257
+ uarray[mask] = numpy.add(
258
+ array[mask], 2**bits_per_entry, dtype=numpy.uint64, casting="unsafe"
259
+ )
260
+ array = uarray
261
+
262
+ # unpack the individual values into a bit array
263
+ bits: numpy.ndarray = numpy.unpackbits(
264
+ numpy.ascontiguousarray(array[::-1]).view("uint8")
265
+ ).reshape(-1, 64)[:, -bits_per_entry:]
266
+ if dense:
267
+ if bits.size % 64:
268
+ # if the bit array does not fill a whole long
269
+ # add padding to the last long if required
270
+ bits = numpy.pad(
271
+ bits.ravel(),
272
+ [(64 - (bits.size % 64), 0)],
273
+ "constant",
274
+ )
275
+ else:
276
+ # if the array is not dense add padding
277
+ entry_per_long = 64 // bits_per_entry
278
+ if bits.shape[0] % entry_per_long:
279
+ # add padding to the last long if required
280
+ bits = numpy.pad(
281
+ bits,
282
+ [(entry_per_long - (bits.shape[0] % entry_per_long), 0), (0, 0)],
283
+ "constant",
284
+ )
285
+ # add padding for each long
286
+ bits = numpy.pad(
287
+ bits.reshape(-1, bits_per_entry * entry_per_long),
288
+ [(0, 0), (64 - bits_per_entry * entry_per_long, 0)],
289
+ "constant",
290
+ )
291
+
292
+ # pack the bits into a long array
293
+ return numpy.packbits(bits).view(dtype=">q")[::-1]
294
+
295
+
296
+ def get_size(obj, seen=None):
297
+ """Recursively finds size of objects"""
298
+ size = sys.getsizeof(obj)
299
+ if seen is None:
300
+ seen = set()
301
+ obj_id = id(obj)
302
+ if obj_id in seen:
303
+ return 0
304
+
305
+ # Important mark as seen *before* entering recursion to gracefully handle
306
+ # self-referential objects
307
+ seen.add(obj_id)
308
+ if isinstance(obj, dict):
309
+ size += sum([get_size(v, seen) for v in obj.values()])
310
+ size += sum([get_size(k, seen) for k in obj.keys()])
311
+ elif hasattr(obj, "__dict__"):
312
+ size += get_size(obj.__dict__, seen)
313
+ elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
314
+ size += sum([get_size(i, seen) for i in obj])
315
+ return size
316
+
317
+
318
+ def get_smallest_dtype(arr: ndarray, uint: bool = True) -> int:
319
+ """
320
+ Returns the smallest dtype (number) that the array can afford
321
+
322
+ :param arr: The array to check on
323
+ :param uint: Should the array fit in uint or not (default: True)
324
+ :return: The number of bits all the elements can be represented with
325
+ """
326
+ possible_dtypes = (2**x for x in range(3, 8))
327
+ max_number = numpy.amax(arr)
328
+ if not uint:
329
+ max_number = max_number * 2
330
+ if max_number == 0:
331
+ max_number = 1
332
+ return next(dtype for dtype in possible_dtypes if dtype > math.log(max_number, 2))
333
+
334
+
335
+ def entity_position_to_chunk_coordinates(
336
+ entity_coordinates: Tuple[float, float, float]
337
+ ):
338
+ return (
339
+ int(math.floor(entity_coordinates[0])) >> 4,
340
+ int(math.floor(entity_coordinates[2])) >> 4,
341
+ )
342
+
343
+
344
+ def fast_unique(array: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray]:
345
+ uni = numpy.unique(array)
346
+ lut = numpy.zeros(numpy.amax(uni) + 1, dtype=numpy.uint32)
347
+ lut[uni] = numpy.arange(uni.size)
348
+ inv = lut[array]
349
+ return uni, inv