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.

Files changed (210) hide show
  1. amulet/__init__.cp312-win_amd64.pyd +0 -0
  2. amulet/__init__.pyi +30 -0
  3. amulet/__pyinstaller/__init__.py +2 -0
  4. amulet/__pyinstaller/hook-amulet.py +4 -0
  5. amulet/_init.py +28 -0
  6. amulet/_version.py +21 -0
  7. amulet/biome.cpp +36 -0
  8. amulet/biome.hpp +43 -0
  9. amulet/biome.pyi +77 -0
  10. amulet/block.cpp +435 -0
  11. amulet/block.hpp +119 -0
  12. amulet/block.pyi +273 -0
  13. amulet/block_entity.cpp +12 -0
  14. amulet/block_entity.hpp +56 -0
  15. amulet/block_entity.pyi +80 -0
  16. amulet/chunk.cpp +16 -0
  17. amulet/chunk.hpp +99 -0
  18. amulet/chunk.pyi +30 -0
  19. amulet/chunk_/components/biome.py +155 -0
  20. amulet/chunk_/components/block_entity.py +117 -0
  21. amulet/chunk_/components/entity.py +64 -0
  22. amulet/chunk_/components/height_2d.py +16 -0
  23. amulet/chunk_components.pyi +95 -0
  24. amulet/collections.pyi +37 -0
  25. amulet/data_types.py +29 -0
  26. amulet/entity.py +180 -0
  27. amulet/errors.py +63 -0
  28. amulet/game/__init__.py +7 -0
  29. amulet/game/_game.py +152 -0
  30. amulet/game/_universal/__init__.py +1 -0
  31. amulet/game/_universal/_biome.py +17 -0
  32. amulet/game/_universal/_block.py +47 -0
  33. amulet/game/_universal/_version.py +68 -0
  34. amulet/game/abc/__init__.py +22 -0
  35. amulet/game/abc/_block_specification.py +150 -0
  36. amulet/game/abc/biome.py +213 -0
  37. amulet/game/abc/block.py +331 -0
  38. amulet/game/abc/game_version_container.py +25 -0
  39. amulet/game/abc/json_interface.py +27 -0
  40. amulet/game/abc/version.py +44 -0
  41. amulet/game/bedrock/__init__.py +1 -0
  42. amulet/game/bedrock/_biome.py +35 -0
  43. amulet/game/bedrock/_block.py +42 -0
  44. amulet/game/bedrock/_version.py +165 -0
  45. amulet/game/java/__init__.py +2 -0
  46. amulet/game/java/_biome.py +35 -0
  47. amulet/game/java/_block.py +60 -0
  48. amulet/game/java/_version.py +176 -0
  49. amulet/game/translate/__init__.py +12 -0
  50. amulet/game/translate/_functions/__init__.py +15 -0
  51. amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
  52. amulet/game/translate/_functions/_code_functions/_text.py +553 -0
  53. amulet/game/translate/_functions/_code_functions/banner_pattern.py +67 -0
  54. amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +152 -0
  55. amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +88 -0
  56. amulet/game/translate/_functions/_code_functions/bedrock_sign.py +152 -0
  57. amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +16 -0
  58. amulet/game/translate/_functions/_code_functions/custom_name.py +146 -0
  59. amulet/game/translate/_functions/_frozen.py +66 -0
  60. amulet/game/translate/_functions/_state.py +54 -0
  61. amulet/game/translate/_functions/_typing.py +98 -0
  62. amulet/game/translate/_functions/abc.py +116 -0
  63. amulet/game/translate/_functions/carry_nbt.py +160 -0
  64. amulet/game/translate/_functions/carry_properties.py +80 -0
  65. amulet/game/translate/_functions/code.py +143 -0
  66. amulet/game/translate/_functions/map_block_name.py +66 -0
  67. amulet/game/translate/_functions/map_nbt.py +111 -0
  68. amulet/game/translate/_functions/map_properties.py +93 -0
  69. amulet/game/translate/_functions/multiblock.py +112 -0
  70. amulet/game/translate/_functions/new_block.py +42 -0
  71. amulet/game/translate/_functions/new_entity.py +43 -0
  72. amulet/game/translate/_functions/new_nbt.py +206 -0
  73. amulet/game/translate/_functions/new_properties.py +64 -0
  74. amulet/game/translate/_functions/sequence.py +51 -0
  75. amulet/game/translate/_functions/walk_input_nbt.py +331 -0
  76. amulet/game/translate/_translator.py +433 -0
  77. amulet/item.py +75 -0
  78. amulet/level/__init__.pyi +27 -0
  79. amulet/level/_load.py +100 -0
  80. amulet/level/abc/__init__.py +12 -0
  81. amulet/level/abc/_chunk_handle.py +335 -0
  82. amulet/level/abc/_dimension.py +86 -0
  83. amulet/level/abc/_history/__init__.py +1 -0
  84. amulet/level/abc/_history/_cache.py +224 -0
  85. amulet/level/abc/_history/_history_manager.py +291 -0
  86. amulet/level/abc/_level/__init__.py +5 -0
  87. amulet/level/abc/_level/_compactable_level.py +10 -0
  88. amulet/level/abc/_level/_creatable_level.py +29 -0
  89. amulet/level/abc/_level/_disk_level.py +17 -0
  90. amulet/level/abc/_level/_level.py +453 -0
  91. amulet/level/abc/_level/_loadable_level.py +42 -0
  92. amulet/level/abc/_player_storage.py +7 -0
  93. amulet/level/abc/_raw_level.py +187 -0
  94. amulet/level/abc/_registry.py +40 -0
  95. amulet/level/bedrock/__init__.py +2 -0
  96. amulet/level/bedrock/_chunk_handle.py +19 -0
  97. amulet/level/bedrock/_dimension.py +22 -0
  98. amulet/level/bedrock/_level.py +187 -0
  99. amulet/level/bedrock/_raw/__init__.py +5 -0
  100. amulet/level/bedrock/_raw/_actor_counter.py +53 -0
  101. amulet/level/bedrock/_raw/_chunk.py +54 -0
  102. amulet/level/bedrock/_raw/_chunk_decode.py +668 -0
  103. amulet/level/bedrock/_raw/_chunk_encode.py +602 -0
  104. amulet/level/bedrock/_raw/_constant.py +9 -0
  105. amulet/level/bedrock/_raw/_dimension.py +343 -0
  106. amulet/level/bedrock/_raw/_level.py +463 -0
  107. amulet/level/bedrock/_raw/_level_dat.py +90 -0
  108. amulet/level/bedrock/_raw/_typing.py +6 -0
  109. amulet/level/bedrock/_raw/leveldb_chunk_versions.py +83 -0
  110. amulet/level/bedrock/chunk/__init__.py +1 -0
  111. amulet/level/bedrock/chunk/_chunk.py +126 -0
  112. amulet/level/bedrock/chunk/components/__init__.py +0 -0
  113. amulet/level/bedrock/chunk/components/chunk_version.py +12 -0
  114. amulet/level/bedrock/chunk/components/finalised_state.py +13 -0
  115. amulet/level/bedrock/chunk/components/raw_chunk.py +15 -0
  116. amulet/level/construction/__init__.py +0 -0
  117. amulet/level/java/__init__.pyi +21 -0
  118. amulet/level/java/_chunk_handle.py +17 -0
  119. amulet/level/java/_chunk_handle.pyi +15 -0
  120. amulet/level/java/_dimension.py +20 -0
  121. amulet/level/java/_dimension.pyi +13 -0
  122. amulet/level/java/_level.py +184 -0
  123. amulet/level/java/_level.pyi +120 -0
  124. amulet/level/java/_raw/__init__.pyi +19 -0
  125. amulet/level/java/_raw/_chunk.pyi +23 -0
  126. amulet/level/java/_raw/_chunk_decode.py +561 -0
  127. amulet/level/java/_raw/_chunk_encode.py +463 -0
  128. amulet/level/java/_raw/_constant.py +9 -0
  129. amulet/level/java/_raw/_constant.pyi +20 -0
  130. amulet/level/java/_raw/_data_pack/__init__.py +2 -0
  131. amulet/level/java/_raw/_data_pack/__init__.pyi +8 -0
  132. amulet/level/java/_raw/_data_pack/data_pack.py +241 -0
  133. amulet/level/java/_raw/_data_pack/data_pack.pyi +197 -0
  134. amulet/level/java/_raw/_data_pack/data_pack_manager.py +77 -0
  135. amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +75 -0
  136. amulet/level/java/_raw/_dimension.py +86 -0
  137. amulet/level/java/_raw/_dimension.pyi +72 -0
  138. amulet/level/java/_raw/_level.py +507 -0
  139. amulet/level/java/_raw/_level.pyi +238 -0
  140. amulet/level/java/_raw/_typing.py +3 -0
  141. amulet/level/java/_raw/_typing.pyi +5 -0
  142. amulet/level/java/anvil/__init__.py +2 -0
  143. amulet/level/java/anvil/__init__.pyi +11 -0
  144. amulet/level/java/anvil/_dimension.py +170 -0
  145. amulet/level/java/anvil/_dimension.pyi +109 -0
  146. amulet/level/java/anvil/_region.py +421 -0
  147. amulet/level/java/anvil/_region.pyi +197 -0
  148. amulet/level/java/anvil/_sector_manager.py +223 -0
  149. amulet/level/java/anvil/_sector_manager.pyi +142 -0
  150. amulet/level/java/chunk.pyi +81 -0
  151. amulet/level/java/chunk_/_chunk.py +260 -0
  152. amulet/level/java/chunk_/components/inhabited_time.py +12 -0
  153. amulet/level/java/chunk_/components/last_update.py +12 -0
  154. amulet/level/java/chunk_/components/legacy_version.py +12 -0
  155. amulet/level/java/chunk_/components/light_populated.py +12 -0
  156. amulet/level/java/chunk_/components/named_height_2d.py +37 -0
  157. amulet/level/java/chunk_/components/status.py +11 -0
  158. amulet/level/java/chunk_/components/terrain_populated.py +12 -0
  159. amulet/level/java/chunk_components.pyi +22 -0
  160. amulet/level/java/long_array.pyi +38 -0
  161. amulet/level/java_forge/__init__.py +0 -0
  162. amulet/level/mcstructure/__init__.py +0 -0
  163. amulet/level/nbt/__init__.py +0 -0
  164. amulet/level/schematic/__init__.py +0 -0
  165. amulet/level/sponge_schematic/__init__.py +0 -0
  166. amulet/level/temporary_level/__init__.py +1 -0
  167. amulet/level/temporary_level/_level.py +16 -0
  168. amulet/palette/__init__.pyi +8 -0
  169. amulet/palette/biome_palette.pyi +45 -0
  170. amulet/palette/block_palette.pyi +45 -0
  171. amulet/player.py +64 -0
  172. amulet/py.typed +0 -0
  173. amulet/selection/__init__.py +2 -0
  174. amulet/selection/abstract_selection.py +342 -0
  175. amulet/selection/box.py +852 -0
  176. amulet/selection/group.py +481 -0
  177. amulet/utils/__init__.pyi +28 -0
  178. amulet/utils/call_spec/__init__.py +24 -0
  179. amulet/utils/call_spec/__init__.pyi +53 -0
  180. amulet/utils/call_spec/_call_spec.py +262 -0
  181. amulet/utils/call_spec/_call_spec.pyi +272 -0
  182. amulet/utils/format_utils.py +41 -0
  183. amulet/utils/generator.py +18 -0
  184. amulet/utils/matrix.py +243 -0
  185. amulet/utils/matrix.pyi +177 -0
  186. amulet/utils/numpy.pyi +11 -0
  187. amulet/utils/numpy_helpers.py +19 -0
  188. amulet/utils/shareable_lock.py +335 -0
  189. amulet/utils/shareable_lock.pyi +190 -0
  190. amulet/utils/signal/__init__.py +10 -0
  191. amulet/utils/signal/__init__.pyi +25 -0
  192. amulet/utils/signal/_signal.py +228 -0
  193. amulet/utils/signal/_signal.pyi +84 -0
  194. amulet/utils/task_manager.py +235 -0
  195. amulet/utils/task_manager.pyi +168 -0
  196. amulet/utils/typed_property.py +111 -0
  197. amulet/utils/typing.py +4 -0
  198. amulet/utils/typing.pyi +6 -0
  199. amulet/utils/weakref.py +70 -0
  200. amulet/utils/weakref.pyi +50 -0
  201. amulet/utils/world_utils.py +102 -0
  202. amulet/utils/world_utils.pyi +109 -0
  203. amulet/version.cpp +136 -0
  204. amulet/version.hpp +142 -0
  205. amulet/version.pyi +94 -0
  206. amulet_core-2.0a3.dist-info/METADATA +103 -0
  207. amulet_core-2.0a3.dist-info/RECORD +210 -0
  208. amulet_core-2.0a3.dist-info/WHEEL +5 -0
  209. amulet_core-2.0a3.dist-info/entry_points.txt +2 -0
  210. amulet_core-2.0a3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,553 @@
1
+ from __future__ import annotations
2
+ from typing import List, overload, Literal, TypeVar
3
+ from dataclasses import dataclass, field
4
+ from copy import deepcopy
5
+ from enum import Enum
6
+
7
+ # section_string is a raw string containing section (§) codes
8
+ # raw text is a stringified json object
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class Colour:
13
+ r: int
14
+ g: int
15
+ b: int
16
+
17
+ def __post_init__(self) -> None:
18
+ assert 0 <= self.r <= 255
19
+ assert 0 <= self.g <= 255
20
+ assert 0 <= self.b <= 255
21
+
22
+
23
+ class Formatting(Enum):
24
+ Reset = "reset"
25
+ Bold = "bold"
26
+ Italic = "italic"
27
+ Underlined = "underlined"
28
+ Strikethrough = "strikethrough"
29
+ Obfuscated = "obfuscated"
30
+
31
+
32
+ class JavaNameHexColourFactory:
33
+ Colour2Name = {
34
+ Colour(0x00, 0x00, 0x00): "black",
35
+ Colour(0x00, 0x00, 0xAA): "dark_blue",
36
+ Colour(0x00, 0xAA, 0x00): "dark_green",
37
+ Colour(0x00, 0xAA, 0xAA): "dark_aqua",
38
+ Colour(0xAA, 0x00, 0x00): "dark_red",
39
+ Colour(0xAA, 0x00, 0xAA): "dark_purple",
40
+ Colour(0xFF, 0xAA, 0x00): "gold",
41
+ Colour(0xAA, 0xAA, 0xAA): "gray",
42
+ Colour(0x55, 0x55, 0x55): "dark_gray",
43
+ Colour(0x55, 0x55, 0xFF): "blue",
44
+ Colour(0x55, 0xFF, 0x55): "green",
45
+ Colour(0x55, 0xFF, 0xFF): "aqua",
46
+ Colour(0xFF, 0x55, 0x55): "red",
47
+ Colour(0xFF, 0x55, 0xFF): "light_purple",
48
+ Colour(0xFF, 0xFF, 0x55): "yellow",
49
+ Colour(0xFF, 0xFF, 0xFF): "white",
50
+ }
51
+ Name2Colour = {name: colour for colour, name in Colour2Name.items()}
52
+
53
+ @classmethod
54
+ def read(cls, raw_colour: str) -> Colour:
55
+ if raw_colour in cls.Name2Colour:
56
+ return cls.Name2Colour[raw_colour]
57
+ elif raw_colour.startswith("#"):
58
+ num = int(raw_colour[1:], 16)
59
+ r = (num >> 16) & 0xFF
60
+ g = (num >> 8) & 0xFF
61
+ b = num & 0xFF
62
+ return Colour(r, g, b)
63
+ raise ValueError(raw_colour)
64
+
65
+ @classmethod
66
+ def write(cls, colour: Colour) -> str:
67
+ if colour in cls.Colour2Name:
68
+ return cls.Colour2Name[colour]
69
+ else:
70
+ return f"#{colour.r % 256:02X}{colour.g % 256:02X}{colour.b % 256:02X}"
71
+
72
+
73
+ class AbstractSectionParser:
74
+ """A class to serialise and deserialise section codes"""
75
+
76
+ Code2Colour: dict[str, Colour] = {}
77
+ Colour2Code: dict[Colour, str] = {}
78
+ Format2Code: dict[Formatting, str]
79
+ Code2Format: dict[str, Formatting]
80
+
81
+ @classmethod
82
+ def read(cls, section_code: str) -> Colour | Formatting | None:
83
+ return cls.Code2Colour.get(section_code) or cls.Code2Format.get(section_code)
84
+
85
+ @classmethod
86
+ def write(cls, value: Colour | Formatting) -> str | None:
87
+ if isinstance(value, Colour):
88
+ if value not in cls.Colour2Code:
89
+ # Find the closest colour to this colour
90
+ # This is a dumb city block search
91
+ value = min(
92
+ cls.Colour2Code,
93
+ key=lambda col: abs(value.r - col.r)
94
+ + abs(value.g - col.g)
95
+ + abs(value.b - col.b),
96
+ )
97
+ return cls.Colour2Code[value]
98
+ elif isinstance(value, Formatting) and value in cls.Format2Code:
99
+ return cls.Format2Code[value]
100
+ return None
101
+
102
+
103
+ class JavaSectionParser(AbstractSectionParser):
104
+ Colour2Code = {
105
+ Colour(0x00, 0x00, 0x00): "0",
106
+ Colour(0x00, 0x00, 0xAA): "1",
107
+ Colour(0x00, 0xAA, 0x00): "2",
108
+ Colour(0x00, 0xAA, 0xAA): "3",
109
+ Colour(0xAA, 0x00, 0x00): "4",
110
+ Colour(0xAA, 0x00, 0xAA): "5",
111
+ Colour(0xFF, 0xAA, 0x00): "6",
112
+ Colour(0xAA, 0xAA, 0xAA): "7",
113
+ Colour(0x55, 0x55, 0x55): "8",
114
+ Colour(0x55, 0x55, 0xFF): "9",
115
+ Colour(0x55, 0xFF, 0x55): "a",
116
+ Colour(0x55, 0xFF, 0xFF): "b",
117
+ Colour(0xFF, 0x55, 0x55): "c",
118
+ Colour(0xFF, 0x55, 0xFF): "d",
119
+ Colour(0xFF, 0xFF, 0x55): "e",
120
+ Colour(0xFF, 0xFF, 0xFF): "f",
121
+ }
122
+ Code2Colour = {name: colour for colour, name in Colour2Code.items()}
123
+ Format2Code = {
124
+ Formatting.Reset: "r",
125
+ Formatting.Bold: "l",
126
+ Formatting.Strikethrough: "m",
127
+ Formatting.Underlined: "n",
128
+ Formatting.Italic: "o",
129
+ Formatting.Obfuscated: "k",
130
+ }
131
+ Code2Format = {c: f for f, c in Format2Code.items()}
132
+
133
+
134
+ class BedrockSectionParser(AbstractSectionParser):
135
+ Colour2Code = {
136
+ Colour(0x00, 0x00, 0x00): "0",
137
+ Colour(0x00, 0x00, 0xAA): "1",
138
+ Colour(0x00, 0xAA, 0x00): "2",
139
+ Colour(0x00, 0xAA, 0xAA): "3",
140
+ Colour(0xAA, 0x00, 0x00): "4",
141
+ Colour(0xAA, 0x00, 0xAA): "5",
142
+ Colour(0xFF, 0xAA, 0x00): "6",
143
+ Colour(0xAA, 0xAA, 0xAA): "7",
144
+ Colour(0x55, 0x55, 0x55): "8",
145
+ Colour(0x55, 0x55, 0xFF): "9",
146
+ Colour(0x55, 0xFF, 0x55): "a",
147
+ Colour(0x55, 0xFF, 0xFF): "b",
148
+ Colour(0xFF, 0x55, 0x55): "c",
149
+ Colour(0xFF, 0x55, 0xFF): "d",
150
+ Colour(0xFF, 0xFF, 0x55): "e",
151
+ Colour(0xFF, 0xFF, 0xFF): "f",
152
+ }
153
+ Code2Colour = {name: colour for colour, name in Colour2Code.items()}
154
+ Format2Code = {
155
+ Formatting.Reset: "r",
156
+ Formatting.Bold: "l",
157
+ Formatting.Italic: "o",
158
+ Formatting.Obfuscated: "k",
159
+ }
160
+ Code2Format = {c: f for f, c in Format2Code.items()}
161
+
162
+
163
+ class ExtendedBedrockSectionParser(AbstractSectionParser):
164
+ Colour2Code = {
165
+ Colour(0x00, 0x00, 0x00): "0",
166
+ Colour(0x00, 0x00, 0xAA): "1",
167
+ Colour(0x00, 0xAA, 0x00): "2",
168
+ Colour(0x00, 0xAA, 0xAA): "3",
169
+ Colour(0xAA, 0x00, 0x00): "4",
170
+ Colour(0xAA, 0x00, 0xAA): "5",
171
+ Colour(0xFF, 0xAA, 0x00): "6",
172
+ Colour(0xAA, 0xAA, 0xAA): "7",
173
+ Colour(0x55, 0x55, 0x55): "8",
174
+ Colour(0x55, 0x55, 0xFF): "9",
175
+ Colour(0x55, 0xFF, 0x55): "a",
176
+ Colour(0x55, 0xFF, 0xFF): "b",
177
+ Colour(0xFF, 0x55, 0x55): "c",
178
+ Colour(0xFF, 0x55, 0xFF): "d",
179
+ Colour(0xFF, 0xFF, 0x55): "e",
180
+ Colour(0xFF, 0xFF, 0xFF): "f",
181
+ Colour(0xDD, 0xD6, 0x05): "g",
182
+ Colour(0xE3, 0xD4, 0xD1): "h",
183
+ Colour(0xCE, 0xCA, 0xCA): "i",
184
+ Colour(0x44, 0x3A, 0x3B): "j",
185
+ Colour(0x97, 0x16, 0x07): "m",
186
+ Colour(0xB4, 0x68, 0x4D): "n",
187
+ Colour(0xDE, 0xB1, 0x2D): "p",
188
+ Colour(0x47, 0xA0, 0x36): "q",
189
+ Colour(0x2C, 0xBA, 0xA8): "s",
190
+ Colour(0x21, 0x49, 0x7B): "t",
191
+ Colour(0x9A, 0x5C, 0xC6): "u",
192
+ }
193
+ Code2Colour = {name: colour for colour, name in Colour2Code.items()}
194
+ Format2Code = {
195
+ Formatting.Reset: "r",
196
+ Formatting.Bold: "l",
197
+ Formatting.Italic: "o",
198
+ Formatting.Obfuscated: "k",
199
+ }
200
+ Code2Format = {c: f for f, c in Format2Code.items()}
201
+
202
+
203
+ @dataclass
204
+ class RawTextFormatting:
205
+ colour: Colour | None = None
206
+ bold: bool | None = None
207
+ italic: bool | None = None
208
+ underlined: bool | None = None
209
+ strikethrough: bool | None = None
210
+ obfuscated: bool | None = None
211
+
212
+
213
+ T = TypeVar("T")
214
+
215
+
216
+ @dataclass
217
+ class RawTextComponent:
218
+ """
219
+ This class supports the core subset of the full raw text specification.
220
+ This is not an attempt to support the full specification.
221
+ """
222
+
223
+ text: str = ""
224
+ formatting: RawTextFormatting = field(default_factory=RawTextFormatting)
225
+ children: list[RawTextComponent] = field(default_factory=list)
226
+
227
+ @classmethod
228
+ def from_java_raw_text(
229
+ cls, obj: str | bool | float | list | dict
230
+ ) -> RawTextComponent:
231
+ """
232
+ Parse a raw JSON text object and unpack it into this class.
233
+ Note that the input is not a raw JSON string but the result from json.loads
234
+ """
235
+ if isinstance(obj, str):
236
+ return cls.from_java_raw_text({"text": obj})
237
+ elif isinstance(obj, bool):
238
+ return cls.from_java_raw_text("true" if obj else "false")
239
+ elif isinstance(obj, float):
240
+ return cls.from_java_raw_text(str(obj))
241
+ elif isinstance(obj, list):
242
+ if obj:
243
+ self = cls.from_java_raw_text(obj[0])
244
+ for el in obj[1:]:
245
+ self.children.append(cls.from_java_raw_text(el))
246
+ return self
247
+ else:
248
+ return cls.from_java_raw_text("")
249
+ elif isinstance(obj, dict):
250
+ text = obj.pop("text", "")
251
+ if isinstance(text, bool):
252
+ text = "true" if text else "false"
253
+ else:
254
+ text = str(text)
255
+
256
+ extra: list = obj.pop("extra", None)
257
+ children: list[RawTextComponent] = (
258
+ [cls.from_java_raw_text(child) for child in extra]
259
+ if isinstance(extra, list)
260
+ else []
261
+ )
262
+
263
+ def get_bool(key: str) -> bool | None:
264
+ val = obj.get(key)
265
+ if isinstance(val, bool):
266
+ return val
267
+ return None
268
+
269
+ bold = get_bool("bold")
270
+ italic = get_bool("italic")
271
+ underlined = get_bool("underlined")
272
+ strikethrough = get_bool("strikethrough")
273
+ obfuscated = get_bool("obfuscated")
274
+
275
+ colour_text = obj.get("color")
276
+ if isinstance(colour_text, str):
277
+ colour = JavaNameHexColourFactory.read(colour_text)
278
+ else:
279
+ colour = None
280
+
281
+ return cls(
282
+ text,
283
+ RawTextFormatting(
284
+ colour,
285
+ bold,
286
+ italic,
287
+ underlined,
288
+ strikethrough,
289
+ obfuscated,
290
+ ),
291
+ children,
292
+ )
293
+ else:
294
+ raise TypeError
295
+
296
+ def to_java_raw_text(self) -> str | dict:
297
+ component: dict[str, list | str | bool] = {"text": self.text}
298
+ if self.children:
299
+ component["extra"] = [child.to_java_raw_text() for child in self.children]
300
+ if self.formatting.colour is not None:
301
+ component["color"] = JavaNameHexColourFactory.write(self.formatting.colour)
302
+ if self.formatting.bold is not None:
303
+ component["bold"] = self.formatting.bold
304
+ if self.formatting.italic is not None:
305
+ component["italic"] = self.formatting.italic
306
+ if self.formatting.underlined is not None:
307
+ component["underlined"] = self.formatting.underlined
308
+ if self.formatting.strikethrough is not None:
309
+ component["strikethrough"] = self.formatting.strikethrough
310
+ if self.formatting.obfuscated is not None:
311
+ component["obfuscated"] = self.formatting.obfuscated
312
+ if component.keys() == {"text"}:
313
+ return self.text
314
+ else:
315
+ return component
316
+
317
+ @overload
318
+ @classmethod
319
+ def from_section_text(
320
+ cls, section_text: str, section_parser: type[AbstractSectionParser]
321
+ ) -> RawTextComponent: ...
322
+
323
+ @overload
324
+ @classmethod
325
+ def from_section_text(
326
+ cls,
327
+ section_text: str,
328
+ section_parser: type[AbstractSectionParser],
329
+ split_newline: Literal[False],
330
+ ) -> RawTextComponent: ...
331
+
332
+ @overload
333
+ @classmethod
334
+ def from_section_text(
335
+ cls,
336
+ section_text: str,
337
+ section_parser: type[AbstractSectionParser],
338
+ split_newline: Literal[True],
339
+ ) -> List[RawTextComponent]: ...
340
+
341
+ @classmethod
342
+ def from_section_text(
343
+ cls,
344
+ section_text: str,
345
+ section_parser: type[AbstractSectionParser],
346
+ split_newline: bool = False,
347
+ ) -> RawTextComponent | list[RawTextComponent]:
348
+ """Parse a section string and convert it to raw JSON text format."""
349
+ # Completed lines
350
+ lines: list[RawTextComponent] = []
351
+ # Components that make up this line
352
+ components: list[RawTextComponent] = []
353
+
354
+ # The formatting found so far in the line
355
+ formatting = RawTextFormatting()
356
+
357
+ # The character index in the string
358
+ index = 0
359
+ max_index = len(section_text)
360
+
361
+ # The index of the next section character and newline.
362
+ # If not found, these equal the length of the string
363
+ next_section_index = section_text.find("§", index) % (max_index + 1)
364
+ next_newline_index = (
365
+ section_text.find("\n", index) % (max_index + 1)
366
+ if split_newline
367
+ else max_index
368
+ )
369
+
370
+ def append_section(text: str) -> None:
371
+ if text:
372
+ components.append(cls(text, deepcopy(formatting)))
373
+
374
+ def append_line() -> None:
375
+ # Push all components to a new line
376
+ lines.append(cls(children=components.copy()))
377
+ components.clear()
378
+
379
+ while True:
380
+ if next_section_index < next_newline_index:
381
+ # section char first
382
+
383
+ if next_section_index > index:
384
+ append_section(section_text[index:next_section_index])
385
+
386
+ index = next_section_index + 1
387
+ section_code = section_text[index : index + 1]
388
+ value = section_parser.read(section_code)
389
+ if isinstance(value, Formatting):
390
+ if value is Formatting.Obfuscated: # obfuscated
391
+ formatting.obfuscated = True
392
+ index += 1
393
+ elif value is Formatting.Bold: # bold
394
+ formatting.bold = True
395
+ index += 1
396
+ elif value is Formatting.Strikethrough: # strikethrough
397
+ formatting.strikethrough = True
398
+ index += 1
399
+ elif value is Formatting.Underlined: # underlined
400
+ formatting.underlined = True
401
+ index += 1
402
+ elif value is Formatting.Italic: # italic
403
+ formatting.italic = True
404
+ index += 1
405
+ elif value is Formatting.Reset: # reset
406
+ formatting.obfuscated = False
407
+ formatting.bold = False
408
+ formatting.strikethrough = False
409
+ formatting.underlined = False
410
+ formatting.italic = False
411
+ formatting.colour = None
412
+ index += 1
413
+ elif isinstance(value, Colour):
414
+ formatting.colour = value
415
+ index += 1
416
+
417
+ next_section_index = section_text.find("§", index) % (max_index + 1)
418
+
419
+ elif next_newline_index == max_index:
420
+ # No more in the string
421
+ append_section(section_text[index:])
422
+ append_line()
423
+ break
424
+
425
+ elif split_newline:
426
+ # newline first
427
+
428
+ append_section(section_text[index:next_newline_index])
429
+ append_line()
430
+
431
+ index = next_newline_index + 1
432
+ next_newline_index = section_text.find("\n", index) % (max_index + 1)
433
+ else:
434
+ raise RuntimeError
435
+
436
+ def compact(src: RawTextComponent) -> RawTextComponent:
437
+ if len(src.children) == 1:
438
+ # Don't need the parent node
439
+ return src.children[0]
440
+ return src
441
+
442
+ if split_newline:
443
+ return list(map(compact, lines))
444
+ else:
445
+ return compact(lines[0])
446
+
447
+ def to_section_text(self, section_parser: type[AbstractSectionParser]) -> str:
448
+ """
449
+ Convert the raw text object to a section text string
450
+ :param section_parser: The colour palette to use.
451
+ :return: The section text.
452
+ """
453
+
454
+ text: list[str] = []
455
+
456
+ current_formatting = RawTextFormatting()
457
+
458
+ reset_code = section_parser.write(Formatting.Reset)
459
+ bold_code = section_parser.write(Formatting.Bold)
460
+ italic_code = section_parser.write(Formatting.Italic)
461
+ underlined_code = section_parser.write(Formatting.Underlined)
462
+ strikethrough_code = section_parser.write(Formatting.Strikethrough)
463
+ obfuscated_code = section_parser.write(Formatting.Obfuscated)
464
+
465
+ def merge_formatting(
466
+ a: RawTextFormatting, b: RawTextFormatting
467
+ ) -> RawTextFormatting:
468
+ # Merge b into a
469
+ return RawTextFormatting(
470
+ a.colour if b.colour is None else b.colour,
471
+ a.bold if b.bold is None else b.bold,
472
+ a.italic if b.italic is None else b.italic,
473
+ a.underlined if b.underlined is None else b.underlined,
474
+ a.strikethrough if b.strikethrough is None else b.strikethrough,
475
+ a.obfuscated if b.obfuscated is None else b.obfuscated,
476
+ )
477
+
478
+ def to_section_text(
479
+ section: RawTextComponent, parent_formatting: RawTextFormatting
480
+ ) -> None:
481
+ desired_formatting = merge_formatting(parent_formatting, section.formatting)
482
+
483
+ if section.text:
484
+ if (
485
+ (
486
+ current_formatting.colour is not None
487
+ and desired_formatting.colour is None
488
+ )
489
+ or (current_formatting.bold and not desired_formatting.bold)
490
+ or (current_formatting.italic and not desired_formatting.italic)
491
+ or (
492
+ current_formatting.underlined
493
+ and not desired_formatting.underlined
494
+ )
495
+ or (
496
+ current_formatting.strikethrough
497
+ and not desired_formatting.strikethrough
498
+ )
499
+ or (
500
+ current_formatting.obfuscated
501
+ and not desired_formatting.obfuscated
502
+ )
503
+ ):
504
+ # A property has been unset. Reset the formatting and apply everything again
505
+ if reset_code is not None:
506
+ text.append(f"§{reset_code}")
507
+ current_formatting.colour = None
508
+ current_formatting.bold = False
509
+ current_formatting.italic = False
510
+ current_formatting.underlined = False
511
+ current_formatting.strikethrough = False
512
+ current_formatting.obfuscated = False
513
+
514
+ # If the formatting has been set and the original formatting is not set
515
+ if desired_formatting.bold and not current_formatting.bold:
516
+ if bold_code is not None:
517
+ text.append(f"§{bold_code}")
518
+ current_formatting.bold = True
519
+ if desired_formatting.italic and not current_formatting.italic:
520
+ if italic_code is not None:
521
+ text.append(f"§{italic_code}")
522
+ current_formatting.italic = True
523
+ if desired_formatting.underlined and not current_formatting.underlined:
524
+ if underlined_code is not None:
525
+ text.append(f"§{underlined_code}")
526
+ current_formatting.underlined = True
527
+ if (
528
+ desired_formatting.strikethrough
529
+ and not current_formatting.strikethrough
530
+ ):
531
+ if strikethrough_code is not None:
532
+ text.append(f"§{strikethrough_code}")
533
+ current_formatting.strikethrough = True
534
+ if desired_formatting.obfuscated and not current_formatting.obfuscated:
535
+ if obfuscated_code is not None:
536
+ text.append(f"§{obfuscated_code}")
537
+ current_formatting.obfuscated = True
538
+ if (
539
+ desired_formatting.colour is not None
540
+ and desired_formatting.colour != current_formatting.colour
541
+ ):
542
+ code = section_parser.write(desired_formatting.colour)
543
+ assert code is not None
544
+ text.append("§" + code)
545
+ current_formatting.colour = desired_formatting.colour
546
+
547
+ text.append(section.text)
548
+
549
+ for child in section.children:
550
+ to_section_text(child, desired_formatting)
551
+
552
+ to_section_text(self, RawTextFormatting())
553
+ return "".join(text)
@@ -0,0 +1,67 @@
1
+ from amulet_nbt import CompoundTag, ListTag, IntTag
2
+ from .._state import SrcData, StateData, DstData
3
+
4
+
5
+ def to_universal(src: SrcData, state: StateData, dst: DstData) -> None:
6
+ nbt = src.nbt
7
+ if nbt is None:
8
+ return
9
+
10
+ tag = nbt.tag
11
+ if not isinstance(tag, CompoundTag):
12
+ return
13
+
14
+ patterns = tag.get("Patterns")
15
+ if not isinstance(patterns, ListTag):
16
+ return
17
+
18
+ for index, pattern in enumerate(patterns):
19
+ if not isinstance(pattern, CompoundTag):
20
+ continue
21
+ colour = pattern.get("Color")
22
+ if not isinstance(colour, IntTag):
23
+ continue
24
+
25
+ dst.nbt.append(
26
+ (
27
+ "",
28
+ CompoundTag,
29
+ (("utags", CompoundTag), ("Patterns", ListTag), (index, CompoundTag)),
30
+ "Color",
31
+ IntTag(15 - colour.py_int),
32
+ )
33
+ )
34
+
35
+
36
+ def from_universal(src: SrcData, state: StateData, dst: DstData) -> None:
37
+ nbt = src.nbt
38
+ if nbt is None:
39
+ return
40
+
41
+ tag = nbt.tag
42
+ if not isinstance(tag, CompoundTag):
43
+ return
44
+
45
+ utags = tag.get("utags")
46
+ if not isinstance(utags, CompoundTag):
47
+ return
48
+
49
+ patterns = utags.get("Patterns")
50
+ if not isinstance(patterns, ListTag):
51
+ return
52
+
53
+ for index, pattern in enumerate(patterns):
54
+ if not isinstance(pattern, CompoundTag):
55
+ continue
56
+ colour = pattern.get("Color")
57
+ if not isinstance(colour, IntTag):
58
+ continue
59
+ dst.nbt.append(
60
+ (
61
+ "",
62
+ CompoundTag,
63
+ (("Patterns", ListTag), (index, CompoundTag)),
64
+ "Color",
65
+ IntTag(15 - colour.py_int),
66
+ )
67
+ )