mca-json 1.0.0

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.
Files changed (231) hide show
  1. package/.editorconfig +14 -0
  2. package/.prettierrc +4 -0
  3. package/README.md +373 -0
  4. package/dist/bin/mca-chunks.d.ts +3 -0
  5. package/dist/bin/mca-chunks.d.ts.map +1 -0
  6. package/dist/bin/mca-chunks.js +43 -0
  7. package/dist/bin/mca-chunks.js.map +1 -0
  8. package/dist/bin/mca-find-chunks-with-signs.d.ts +3 -0
  9. package/dist/bin/mca-find-chunks-with-signs.d.ts.map +1 -0
  10. package/dist/bin/mca-find-chunks-with-signs.js +79 -0
  11. package/dist/bin/mca-find-chunks-with-signs.js.map +1 -0
  12. package/dist/bin/mca-json.d.ts +3 -0
  13. package/dist/bin/mca-json.d.ts.map +1 -0
  14. package/dist/bin/mca-json.js +73 -0
  15. package/dist/bin/mca-json.js.map +1 -0
  16. package/dist/bin/mca-trim-chunks-without-signs.d.ts +3 -0
  17. package/dist/bin/mca-trim-chunks-without-signs.d.ts.map +1 -0
  18. package/dist/bin/mca-trim-chunks-without-signs.js +117 -0
  19. package/dist/bin/mca-trim-chunks-without-signs.js.map +1 -0
  20. package/dist/bin/nbt-get-player-location.d.ts +3 -0
  21. package/dist/bin/nbt-get-player-location.d.ts.map +1 -0
  22. package/dist/bin/nbt-get-player-location.js +83 -0
  23. package/dist/bin/nbt-get-player-location.js.map +1 -0
  24. package/dist/bin/nbt-json.d.ts +3 -0
  25. package/dist/bin/nbt-json.d.ts.map +1 -0
  26. package/dist/bin/nbt-json.js +64 -0
  27. package/dist/bin/nbt-json.js.map +1 -0
  28. package/dist/block/banner.d.ts +13 -0
  29. package/dist/block/banner.d.ts.map +1 -0
  30. package/dist/block/banner.js +28 -0
  31. package/dist/block/banner.js.map +1 -0
  32. package/dist/block/barrel.d.ts +17 -0
  33. package/dist/block/barrel.d.ts.map +1 -0
  34. package/dist/block/barrel.js +21 -0
  35. package/dist/block/barrel.js.map +1 -0
  36. package/dist/block/beacon.d.ts +14 -0
  37. package/dist/block/beacon.d.ts.map +1 -0
  38. package/dist/block/beacon.js +14 -0
  39. package/dist/block/beacon.js.map +1 -0
  40. package/dist/block/bed.d.ts +4 -0
  41. package/dist/block/bed.d.ts.map +1 -0
  42. package/dist/block/bed.js +4 -0
  43. package/dist/block/bed.js.map +1 -0
  44. package/dist/block/beehive.d.ts +9 -0
  45. package/dist/block/beehive.d.ts.map +1 -0
  46. package/dist/block/beehive.js +10 -0
  47. package/dist/block/beehive.js.map +1 -0
  48. package/dist/block/bell.d.ts +4 -0
  49. package/dist/block/bell.d.ts.map +1 -0
  50. package/dist/block/bell.js +4 -0
  51. package/dist/block/bell.js.map +1 -0
  52. package/dist/block/block.d.ts +23 -0
  53. package/dist/block/block.d.ts.map +1 -0
  54. package/dist/block/block.js +49 -0
  55. package/dist/block/block.js.map +1 -0
  56. package/dist/block/generic.d.ts +10 -0
  57. package/dist/block/generic.d.ts.map +1 -0
  58. package/dist/block/generic.js +14 -0
  59. package/dist/block/generic.js.map +1 -0
  60. package/dist/block/has-entity-data.d.ts +8 -0
  61. package/dist/block/has-entity-data.d.ts.map +1 -0
  62. package/dist/block/has-entity-data.js +12 -0
  63. package/dist/block/has-entity-data.js.map +1 -0
  64. package/dist/block/mixins/custom-name.d.ts +5 -0
  65. package/dist/block/mixins/custom-name.d.ts.map +1 -0
  66. package/dist/block/mixins/custom-name.js +7 -0
  67. package/dist/block/mixins/custom-name.js.map +1 -0
  68. package/dist/block/mixins/lock.d.ts +5 -0
  69. package/dist/block/mixins/lock.d.ts.map +1 -0
  70. package/dist/block/mixins/lock.js +7 -0
  71. package/dist/block/mixins/lock.js.map +1 -0
  72. package/dist/block/mixins/mixin.d.ts +27 -0
  73. package/dist/block/mixins/mixin.d.ts.map +1 -0
  74. package/dist/block/mixins/mixin.js +36 -0
  75. package/dist/block/mixins/mixin.js.map +1 -0
  76. package/dist/block/sign.d.ts +15 -0
  77. package/dist/block/sign.d.ts.map +1 -0
  78. package/dist/block/sign.js +91 -0
  79. package/dist/block/sign.js.map +1 -0
  80. package/dist/index.d.ts +39 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +39 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/lib/anvil.d.ts +54 -0
  85. package/dist/lib/anvil.d.ts.map +1 -0
  86. package/dist/lib/anvil.js +141 -0
  87. package/dist/lib/anvil.js.map +1 -0
  88. package/dist/lib/binary-data.d.ts +48 -0
  89. package/dist/lib/binary-data.d.ts.map +1 -0
  90. package/dist/lib/binary-data.js +188 -0
  91. package/dist/lib/binary-data.js.map +1 -0
  92. package/dist/lib/bit-data.d.ts +16 -0
  93. package/dist/lib/bit-data.d.ts.map +1 -0
  94. package/dist/lib/bit-data.js +75 -0
  95. package/dist/lib/bit-data.js.map +1 -0
  96. package/dist/lib/block-data.d.ts +20 -0
  97. package/dist/lib/block-data.d.ts.map +1 -0
  98. package/dist/lib/block-data.js +125 -0
  99. package/dist/lib/block-data.js.map +1 -0
  100. package/dist/lib/chunk.d.ts +112 -0
  101. package/dist/lib/chunk.d.ts.map +1 -0
  102. package/dist/lib/chunk.js +299 -0
  103. package/dist/lib/chunk.js.map +1 -0
  104. package/dist/nbt/nbt-base.d.ts +20 -0
  105. package/dist/nbt/nbt-base.d.ts.map +1 -0
  106. package/dist/nbt/nbt-base.js +30 -0
  107. package/dist/nbt/nbt-base.js.map +1 -0
  108. package/dist/nbt/nbt-byte-array.d.ts +13 -0
  109. package/dist/nbt/nbt-byte-array.d.ts.map +1 -0
  110. package/dist/nbt/nbt-byte-array.js +30 -0
  111. package/dist/nbt/nbt-byte-array.js.map +1 -0
  112. package/dist/nbt/nbt-byte.d.ts +13 -0
  113. package/dist/nbt/nbt-byte.d.ts.map +1 -0
  114. package/dist/nbt/nbt-byte.js +25 -0
  115. package/dist/nbt/nbt-byte.js.map +1 -0
  116. package/dist/nbt/nbt-compound.d.ts +15 -0
  117. package/dist/nbt/nbt-compound.d.ts.map +1 -0
  118. package/dist/nbt/nbt-compound.js +68 -0
  119. package/dist/nbt/nbt-compound.js.map +1 -0
  120. package/dist/nbt/nbt-double.d.ts +13 -0
  121. package/dist/nbt/nbt-double.d.ts.map +1 -0
  122. package/dist/nbt/nbt-double.js +25 -0
  123. package/dist/nbt/nbt-double.js.map +1 -0
  124. package/dist/nbt/nbt-end.d.ts +11 -0
  125. package/dist/nbt/nbt-end.d.ts.map +1 -0
  126. package/dist/nbt/nbt-end.js +22 -0
  127. package/dist/nbt/nbt-end.js.map +1 -0
  128. package/dist/nbt/nbt-float.d.ts +13 -0
  129. package/dist/nbt/nbt-float.d.ts.map +1 -0
  130. package/dist/nbt/nbt-float.js +25 -0
  131. package/dist/nbt/nbt-float.js.map +1 -0
  132. package/dist/nbt/nbt-int-array.d.ts +13 -0
  133. package/dist/nbt/nbt-int-array.d.ts.map +1 -0
  134. package/dist/nbt/nbt-int-array.js +30 -0
  135. package/dist/nbt/nbt-int-array.js.map +1 -0
  136. package/dist/nbt/nbt-int.d.ts +13 -0
  137. package/dist/nbt/nbt-int.d.ts.map +1 -0
  138. package/dist/nbt/nbt-int.js +25 -0
  139. package/dist/nbt/nbt-int.js.map +1 -0
  140. package/dist/nbt/nbt-list.d.ts +17 -0
  141. package/dist/nbt/nbt-list.d.ts.map +1 -0
  142. package/dist/nbt/nbt-list.js +70 -0
  143. package/dist/nbt/nbt-list.js.map +1 -0
  144. package/dist/nbt/nbt-long-array.d.ts +13 -0
  145. package/dist/nbt/nbt-long-array.d.ts.map +1 -0
  146. package/dist/nbt/nbt-long-array.js +32 -0
  147. package/dist/nbt/nbt-long-array.js.map +1 -0
  148. package/dist/nbt/nbt-long.d.ts +13 -0
  149. package/dist/nbt/nbt-long.d.ts.map +1 -0
  150. package/dist/nbt/nbt-long.js +25 -0
  151. package/dist/nbt/nbt-long.js.map +1 -0
  152. package/dist/nbt/nbt-short.d.ts +13 -0
  153. package/dist/nbt/nbt-short.d.ts.map +1 -0
  154. package/dist/nbt/nbt-short.js +25 -0
  155. package/dist/nbt/nbt-short.js.map +1 -0
  156. package/dist/nbt/nbt-string.d.ts +13 -0
  157. package/dist/nbt/nbt-string.d.ts.map +1 -0
  158. package/dist/nbt/nbt-string.js +27 -0
  159. package/dist/nbt/nbt-string.js.map +1 -0
  160. package/dist/nbt/nbt-tag-type.d.ts +16 -0
  161. package/dist/nbt/nbt-tag-type.d.ts.map +1 -0
  162. package/dist/nbt/nbt-tag-type.js +17 -0
  163. package/dist/nbt/nbt-tag-type.js.map +1 -0
  164. package/dist/nbt/nbt.d.ts +11 -0
  165. package/dist/nbt/nbt.d.ts.map +1 -0
  166. package/dist/nbt/nbt.js +57 -0
  167. package/dist/nbt/nbt.js.map +1 -0
  168. package/dist/nbt/snbt-data.d.ts +18 -0
  169. package/dist/nbt/snbt-data.d.ts.map +1 -0
  170. package/dist/nbt/snbt-data.js +34 -0
  171. package/dist/nbt/snbt-data.js.map +1 -0
  172. package/dist/nbt/snbt-parse.d.ts +3 -0
  173. package/dist/nbt/snbt-parse.d.ts.map +1 -0
  174. package/dist/nbt/snbt-parse.js +201 -0
  175. package/dist/nbt/snbt-parse.js.map +1 -0
  176. package/dist/nbt/snbt-to-nbt.d.ts +7 -0
  177. package/dist/nbt/snbt-to-nbt.d.ts.map +1 -0
  178. package/dist/nbt/snbt-to-nbt.js +178 -0
  179. package/dist/nbt/snbt-to-nbt.js.map +1 -0
  180. package/dist/types/coords.d.ts +3 -0
  181. package/dist/types/coords.d.ts.map +1 -0
  182. package/dist/types/coords.js +2 -0
  183. package/dist/types/coords.js.map +1 -0
  184. package/package.json +28 -0
  185. package/src/bin/mca-chunks.ts +54 -0
  186. package/src/bin/mca-find-chunks-with-signs.ts +109 -0
  187. package/src/bin/mca-json.ts +96 -0
  188. package/src/bin/mca-trim-chunks-without-signs.ts +146 -0
  189. package/src/bin/nbt-get-player-location.ts +102 -0
  190. package/src/bin/nbt-json.ts +85 -0
  191. package/src/block/banner.ts +50 -0
  192. package/src/block/barrel.ts +34 -0
  193. package/src/block/beacon.ts +20 -0
  194. package/src/block/bed.ts +3 -0
  195. package/src/block/beehive.ts +17 -0
  196. package/src/block/bell.ts +3 -0
  197. package/src/block/block.ts +62 -0
  198. package/src/block/generic.ts +14 -0
  199. package/src/block/has-entity-data.ts +20 -0
  200. package/src/block/mixins/custom-name.ts +8 -0
  201. package/src/block/mixins/lock.ts +8 -0
  202. package/src/block/mixins/mixin.ts +53 -0
  203. package/src/block/sign.ts +121 -0
  204. package/src/index.ts +38 -0
  205. package/src/lib/anvil.ts +178 -0
  206. package/src/lib/binary-data.ts +247 -0
  207. package/src/lib/bit-data.ts +101 -0
  208. package/src/lib/block-data.ts +180 -0
  209. package/src/lib/chunk.ts +389 -0
  210. package/src/nbt/nbt-base.ts +38 -0
  211. package/src/nbt/nbt-byte-array.ts +38 -0
  212. package/src/nbt/nbt-byte.ts +31 -0
  213. package/src/nbt/nbt-compound.ts +95 -0
  214. package/src/nbt/nbt-double.ts +31 -0
  215. package/src/nbt/nbt-end.ts +27 -0
  216. package/src/nbt/nbt-float.ts +31 -0
  217. package/src/nbt/nbt-int-array.ts +38 -0
  218. package/src/nbt/nbt-int.ts +31 -0
  219. package/src/nbt/nbt-list.ts +103 -0
  220. package/src/nbt/nbt-long-array.ts +40 -0
  221. package/src/nbt/nbt-long.ts +31 -0
  222. package/src/nbt/nbt-short.ts +31 -0
  223. package/src/nbt/nbt-string.ts +35 -0
  224. package/src/nbt/nbt-tag-type.ts +15 -0
  225. package/src/nbt/nbt.ts +70 -0
  226. package/src/nbt/snbt-data.ts +70 -0
  227. package/src/nbt/snbt-parse.ts +256 -0
  228. package/src/nbt/snbt-to-nbt.ts +219 -0
  229. package/src/types/coords.ts +2 -0
  230. package/src/types/neodoc.d.ts +28 -0
  231. package/tsconfig.json +25 -0
@@ -0,0 +1,389 @@
1
+ /**
2
+ * A Chunk is a 16 x 383 x 16 portion of a Minecraft world. It's stored as NBT
3
+ * data in sections of 16 blocks in the Y axis. The Y axis range is from -64 to
4
+ * 320.
5
+ *
6
+ * This class helps manipulate the chunk data.
7
+ */
8
+
9
+ import { Block, BlockInstance } from '../block/block';
10
+ import { BlockData } from './block-data';
11
+ import { Coords2d, Coords3d } from '../types/coords';
12
+ import { NbtBase } from '../nbt/nbt-base';
13
+ import { NbtCompound } from '../nbt/nbt-compound';
14
+ import { NbtInt } from '../nbt/nbt-int';
15
+ import { NbtList } from '../nbt/nbt-list';
16
+ import { NbtLong } from '../nbt/nbt-long';
17
+ import { NbtLongArray } from '../nbt/nbt-long-array';
18
+ import { NbtString } from '../nbt/nbt-string';
19
+ import { NbtTagType } from '../nbt/nbt-tag-type';
20
+
21
+ export class Chunk {
22
+ private dataVersion: number;
23
+ private parsedSections = new Map<NbtCompound, BlockData>();
24
+ private rootNbt: NbtCompound;
25
+
26
+ /**
27
+ * Constructs a new chunk object from a given NBT tag.
28
+ *
29
+ * @param root the NBT tag containing the chunk's data
30
+ */
31
+ constructor(root: NbtBase<any>) {
32
+ if (!this.isValidChunkRoot(root)) {
33
+ throw new Error('Invalid chunk root tag');
34
+ }
35
+
36
+ this.rootNbt = root;
37
+
38
+ // 1.2.1 does not have this tag.
39
+ this.dataVersion = root.findChild<NbtInt>('DataVersion')?.data ?? 0;
40
+ }
41
+
42
+ /**
43
+ * Gets the block entity list tag.
44
+ */
45
+ blockEntities(): NbtList<NbtCompound> | undefined {
46
+ return (
47
+ // 1.18+
48
+ this.rootNbt.findChild<NbtList<NbtCompound>>('block_entities') ||
49
+ // up to 1.17
50
+ this.rootNbt.findChild<NbtList<NbtCompound>>('Level/TileEntities')
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Gets a block's entity data
56
+ */
57
+ blockEntityData(coords: Coords3d): NbtCompound | undefined {
58
+ return this.blockEntities()?.data.find((tag: NbtCompound) => {
59
+ const x = tag.findChild<NbtInt>('x');
60
+ const y = tag.findChild<NbtInt>('y');
61
+ const z = tag.findChild<NbtInt>('z');
62
+
63
+ return (
64
+ x?.data === coords[0] &&
65
+ y?.data === coords[1] &&
66
+ z?.data === coords[2]
67
+ );
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Gets the chunk's location in chunk coordinates.
73
+ *
74
+ * Returns a tuple of the chunk's X and Z coordinates.
75
+ */
76
+ chunkCoordinates(): Coords2d | undefined {
77
+ const x =
78
+ // 1.18+
79
+ this.rootNbt.findChild<NbtInt>('xPos') ||
80
+ // up to 1.17
81
+ this.rootNbt.findChild<NbtInt>('Level/xPos');
82
+ const z =
83
+ // 1.18+
84
+ this.rootNbt.findChild<NbtInt>('zPos') ||
85
+ // up to 1.17
86
+ this.rootNbt.findChild<NbtInt>('Level/zPos');
87
+
88
+ if (typeof x?.data !== 'number' || typeof z?.data !== 'number') {
89
+ return;
90
+ }
91
+
92
+ return [x.data, z.data];
93
+ }
94
+
95
+ /**
96
+ * Returns a unique id for this chunk. It's just the chunk's X and Z
97
+ * coordinates in number of chunks from the world's origin.
98
+ */
99
+ chunkKey(): string | undefined {
100
+ const coords = this.chunkCoordinates();
101
+
102
+ if (!coords) {
103
+ return;
104
+ }
105
+
106
+ return `${coords[0]},${coords[1]}`;
107
+ }
108
+
109
+ /**
110
+ * Returns an instance of a block at a given location using world
111
+ * coordinates.
112
+ */
113
+ getBlock(coords: Coords3d): BlockInstance {
114
+ const sectionY = Math.floor(coords[1] / 16);
115
+ const sectionsTag = this.sectionsTag();
116
+
117
+ if (!sectionsTag) {
118
+ throw new Error('Invalid chunk - no sections found');
119
+ }
120
+
121
+ const section = sectionsTag.data.find(
122
+ (section) => this.sectionY(section) === sectionY
123
+ );
124
+
125
+ if (!section) {
126
+ throw new Error('Block not found in any sections');
127
+ }
128
+
129
+ const blockData = this.parseSection(section);
130
+ const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];
131
+ const xChunk = coords[0] - xWorld;
132
+ const zChunk = coords[2] - zWorld;
133
+
134
+ if (xChunk < 0 || xChunk > 15) {
135
+ throw new Error('X coordinate out of bounds');
136
+ }
137
+
138
+ if (zChunk < 0 || zChunk > 15) {
139
+ throw new Error('Z coordinate out of bounds');
140
+ }
141
+
142
+ const index = blockData.chunkCoordinatesToIndex([
143
+ xChunk,
144
+ coords[1] % 16,
145
+ zChunk,
146
+ ]);
147
+ const name = blockData.getBlockByIndex(index);
148
+
149
+ return Block.create(name, coords, this.blockEntityData(coords));
150
+ }
151
+
152
+ /**
153
+ * Finds all blocks by a given name. Provides [X, Y, Z] real-world coordinates.
154
+ */
155
+ findBlocksByName(name: string): Coords3d[] {
156
+ const sectionsTag = this.sectionsTag();
157
+
158
+ if (!sectionsTag) {
159
+ return [];
160
+ }
161
+
162
+ return sectionsTag.data.flatMap((section) => {
163
+ const yWorld = this.sectionY(section, true);
164
+ const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];
165
+ const palette = this.sectionPalette(section);
166
+
167
+ if (palette === undefined) {
168
+ return [];
169
+ }
170
+
171
+ // // If it's not in the palette, we can skip processing.
172
+ if (!this.paletteNameList(palette).includes(name)) {
173
+ return [];
174
+ }
175
+
176
+ const blockData = this.parseSection(section);
177
+
178
+ return blockData.findBlocksByName(name).map((index): Coords3d => {
179
+ const chunkCoordinates =
180
+ blockData.indexToChunkCoordinates(index);
181
+
182
+ return [
183
+ xWorld + chunkCoordinates[0],
184
+ yWorld + chunkCoordinates[1],
185
+ zWorld + chunkCoordinates[2],
186
+ ];
187
+ });
188
+ });
189
+ }
190
+
191
+ /**
192
+ * See how long the chunk has been inhabited. This number increases by 1
193
+ * for each game tick, which is 20 times per second when a player is
194
+ * nearby. It can also advance slowly when players are farther away.
195
+ *
196
+ * On newly created chunks, this value does not match what is expected. Use
197
+ * this with caution.
198
+ */
199
+ inhabitedTime(): bigint | undefined {
200
+ return (
201
+ // 1.18+
202
+ this.rootNbt.findChild<NbtLong>('InhabitedTime')?.data ||
203
+ // up to 1.17
204
+ this.rootNbt.findChild<NbtLong>('Level/InhabitedTime')?.data
205
+ );
206
+ }
207
+
208
+ /**
209
+ * Converts the chunk data to a JSON object.
210
+ */
211
+ toObject(): any {
212
+ return this.rootNbt.toObject();
213
+ }
214
+
215
+ /**
216
+ * Returns a list of unique block names in the chunk as a Set.
217
+ */
218
+ uniqueBlockNames(): Set<string> {
219
+ const sectionsTag = this.sectionsTag();
220
+
221
+ if (!sectionsTag) {
222
+ return new Set();
223
+ }
224
+
225
+ const names = sectionsTag.data.flatMap((section) => {
226
+ const palette = this.sectionPalette(section);
227
+
228
+ if (!palette) {
229
+ return [];
230
+ }
231
+
232
+ return this.paletteNameList(palette);
233
+ });
234
+
235
+ return new Set(names);
236
+ }
237
+
238
+ /**
239
+ * Gets the chunk's location in real-world coordinates.
240
+ *
241
+ * Returns a tuple of the starting block's world X and Z coordinates. The
242
+ * chunk contains the range from X to X+15 (going East), Y to Y+15 (going
243
+ * up), and Z to Z+15 (going South).
244
+ */
245
+ worldCoordinates(): Coords2d | undefined {
246
+ const result = this.chunkCoordinates();
247
+
248
+ if (!result) {
249
+ return;
250
+ }
251
+
252
+ return [result[0] * 16, result[1] * 16];
253
+ }
254
+
255
+ /**
256
+ * Checks if the given NBT tag is a valid chunk root tag.
257
+ * @param tag the tag to check.
258
+ */
259
+ private isValidChunkRoot(nbt: NbtBase<any>): nbt is NbtCompound {
260
+ return nbt.isCompound('');
261
+ }
262
+
263
+ /**
264
+ * Checks if the given NBT tag is a valid chunk section tag, containing a list of chunk sections.
265
+ */
266
+ private isValidChunkSection(
267
+ nbt: NbtBase<any>
268
+ ): nbt is NbtList<NbtCompound> {
269
+ if (!nbt.isList(NbtTagType.COMPOUND)) {
270
+ return false;
271
+ }
272
+
273
+ // Java uses "sections", unsure what uses "Sections"
274
+ if (nbt.name.toLowerCase() === 'sections') {
275
+ return true;
276
+ }
277
+
278
+ return false;
279
+ }
280
+
281
+ /**
282
+ * Returns a list of names within a palette tag.
283
+ */
284
+ private paletteNameList(palette: NbtBase<any>): string[] {
285
+ if (!palette.isList(NbtTagType.COMPOUND)) {
286
+ return [];
287
+ }
288
+
289
+ return (palette as NbtList<NbtCompound>).data.map((tag) => {
290
+ const name = tag.findChild<NbtString>('Name');
291
+
292
+ return name?.data ?? '';
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Parses a section tag into BlockData.
298
+ *
299
+ * Uses a cached version if available;
300
+ */
301
+ private parseSection(section: NbtCompound): BlockData {
302
+ if (this.parsedSections.has(section)) {
303
+ return this.parsedSections.get(section)!;
304
+ }
305
+
306
+ const palette = this.sectionPalette(section);
307
+
308
+ if (!palette) {
309
+ throw new Error('Invalid chunk section - no palette found');
310
+ }
311
+
312
+ const blockStates = this.sectionBlockStates(section);
313
+
314
+ if (!blockStates) {
315
+ throw new Error('Invalid chunk section - no block states found');
316
+ }
317
+
318
+ const blockData = BlockData.fromPaletteBlockStates(
319
+ this.dataVersion,
320
+ palette,
321
+ blockStates
322
+ );
323
+ this.parsedSections.set(section, blockData);
324
+
325
+ return blockData;
326
+ }
327
+
328
+ /**
329
+ * Finds the BlockStates tag in a section.
330
+ */
331
+ private sectionBlockStates(section: NbtCompound): NbtLongArray | undefined {
332
+ return (
333
+ // 1.18+
334
+ section.findChild<NbtLongArray>('block_states/data') ||
335
+ // up to 1.17
336
+ section.findChild<NbtLongArray>('BlockStates')
337
+ );
338
+ }
339
+
340
+ /**
341
+ * Finds the block palette in a section.
342
+ */
343
+ private sectionPalette(
344
+ section: NbtCompound
345
+ ): NbtList<NbtCompound> | undefined {
346
+ return (
347
+ // 1.18+
348
+ section.findChild<NbtList<NbtCompound>>('block_states/palette') ||
349
+ // up to 1.17
350
+ section.findChild<NbtList<NbtCompound>>('Palette')
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Returns all sections
356
+ */
357
+ private sectionsTag(): NbtList<NbtCompound> | undefined {
358
+ const section =
359
+ // 1.18+
360
+ this.rootNbt.findChild<NbtList<NbtCompound>>('sections') ||
361
+ // up to 1.17
362
+ this.rootNbt.findChild<NbtList<NbtCompound>>('Level/Sections');
363
+
364
+ if (section && this.isValidChunkSection(section)) {
365
+ return section;
366
+ }
367
+
368
+ return;
369
+ }
370
+
371
+ /**
372
+ * Returns the Y coordinate of a section, which goes from 0 to 256. If you want the world coordinate, pass true to get -128 to 4031.
373
+ */
374
+ private sectionY(section: NbtCompound, useWorldCoordinate = false): number {
375
+ const ySection = section.findChild<NbtInt>('Y')?.data || 0;
376
+
377
+ if (!useWorldCoordinate) {
378
+ return ySection;
379
+ }
380
+
381
+ let yWorld = ySection * 16;
382
+
383
+ if (yWorld >= 4032) {
384
+ yWorld -= 4096;
385
+ }
386
+
387
+ return yWorld;
388
+ }
389
+ }
@@ -0,0 +1,38 @@
1
+ import { BinaryData } from '../lib/binary-data';
2
+ import { NbtTagType } from './nbt-tag-type';
3
+
4
+ export abstract class NbtBase<T> {
5
+ static readName(bd: BinaryData) {
6
+ const nameLength = bd.getUShort();
7
+
8
+ return bd.getString(nameLength);
9
+ }
10
+
11
+ constructor(
12
+ public type: NbtTagType,
13
+ public data: T,
14
+ public name = '',
15
+ ) {}
16
+
17
+ /**
18
+ * Search for a child by path. Use '/' as separators.
19
+ *
20
+ * nbt.findChild('Level/Sections/0/BlockStates')
21
+ */
22
+ findChild<T extends NbtBase<any>>(_path: string): T | undefined {
23
+ // By default, most elements do not have children.
24
+ return;
25
+ }
26
+
27
+ isCompound(_name?: string): boolean {
28
+ return false;
29
+ }
30
+
31
+ isList(_subtype?: NbtTagType): boolean {
32
+ return false;
33
+ }
34
+
35
+ abstract toObject(): any;
36
+
37
+ abstract toSnbt(): string;
38
+ }
@@ -0,0 +1,38 @@
1
+ import debug from 'debug';
2
+ import { BinaryData } from '../lib/binary-data';
3
+ import { NbtBase } from './nbt-base';
4
+ import { NbtTagType } from './nbt-tag-type';
5
+
6
+ const debugLogFromBinaryData = debug('nbt:byte-array:from-binary-data');
7
+
8
+ export class NbtByteArray extends NbtBase<number[]> {
9
+ static fromBinaryData(bd: BinaryData, name?: string): NbtByteArray {
10
+ name ??= NbtByteArray.readName(bd);
11
+ const data: number[] = [];
12
+ const length = bd.getInt();
13
+ debugLogFromBinaryData(`BYTE_ARRAY, name ${name}, length ${length}`);
14
+
15
+ for (let i = 0; i < length; i++) {
16
+ data.push(bd.getByte());
17
+ }
18
+
19
+ debugLogFromBinaryData(`BYTE_ARRAY, data ${data}`);
20
+
21
+ return new NbtByteArray(data, name);
22
+ }
23
+
24
+ constructor(data: number[], name?: string) {
25
+ super(NbtTagType.BYTE_ARRAY, data, name);
26
+ }
27
+
28
+ toObject() {
29
+ return {
30
+ type: this.type,
31
+ byteArray: this.data,
32
+ };
33
+ }
34
+
35
+ toSnbt() {
36
+ return `[B;${this.data.map((v) => `${v}b`).join(',')}]`;
37
+ }
38
+ }
@@ -0,0 +1,31 @@
1
+ import debug from 'debug';
2
+ import { BinaryData } from '../lib/binary-data';
3
+ import { NbtBase } from './nbt-base';
4
+ import { NbtTagType } from './nbt-tag-type';
5
+
6
+ const debugLogFromBinaryData = debug('nbt:byte:from-binary-data');
7
+
8
+ export class NbtByte extends NbtBase<number> {
9
+ static fromBinaryData(bd: BinaryData, name?: string): NbtByte {
10
+ name ??= NbtByte.readName(bd);
11
+ const data = bd.getByte();
12
+ debugLogFromBinaryData(`BYTE, name ${name}, data ${data}`);
13
+
14
+ return new NbtByte(data, name);
15
+ }
16
+
17
+ constructor(data: number, name?: string) {
18
+ super(NbtTagType.BYTE, data, name);
19
+ }
20
+
21
+ toObject() {
22
+ return {
23
+ type: this.type,
24
+ byte: this.data,
25
+ };
26
+ }
27
+
28
+ toSnbt() {
29
+ return `${this.data}b`;
30
+ }
31
+ }
@@ -0,0 +1,95 @@
1
+ import debug from 'debug';
2
+ import { BinaryData } from '../lib/binary-data';
3
+ import { Nbt } from './nbt';
4
+ import { NbtBase } from './nbt-base';
5
+ import { NbtTagType } from './nbt-tag-type';
6
+
7
+ const debugLogFromBinaryData = debug('nbt:compound:from-binary-data');
8
+ const debugLogFindChild = debug('nbt:compound:find-child');
9
+
10
+ export class NbtCompound extends NbtBase<NbtBase<any>[]> {
11
+ static fromBinaryData<T>(bd: BinaryData, name?: string): NbtCompound {
12
+ name ??= NbtCompound.readName(bd);
13
+ const data: NbtBase<T>[] = [];
14
+ debugLogFromBinaryData(`COMPOUND, name ${name}, starting`);
15
+
16
+ // Careful - this does not store the END tag
17
+ let tag = Nbt.getTag(bd);
18
+ debugLogFromBinaryData(
19
+ `COMPOUND, name ${name}, tag ${tag.type} was retrieved`
20
+ );
21
+
22
+ while (tag && tag.type !== NbtTagType.END) {
23
+ data.push(tag);
24
+ tag = Nbt.getTag(bd);
25
+ debugLogFromBinaryData(
26
+ `COMPOUND, name ${name}, tag ${tag.type} was retrieved`
27
+ );
28
+ }
29
+
30
+ debugLogFromBinaryData(`COMPOUND, name ${name}, finished`);
31
+
32
+ return new NbtCompound(data, name);
33
+ }
34
+
35
+ constructor(data: NbtBase<any>[], name?: string) {
36
+ super(NbtTagType.COMPOUND, data, name);
37
+ }
38
+
39
+ override findChild<RESULT extends NbtBase<any>>(path: string) {
40
+ const pathSegments = path.split('/');
41
+ const currentSegment = pathSegments.shift();
42
+ const result = this.data.find((item) => item.name === currentSegment);
43
+ debugLogFindChild(
44
+ `COMPOUND, name ${this.name}, path ${path}, found? ${!!result}`
45
+ );
46
+
47
+ if (!result) {
48
+ return;
49
+ }
50
+
51
+ if (pathSegments.length) {
52
+ return result.findChild<RESULT>(pathSegments.join('/'));
53
+ }
54
+
55
+ return result as RESULT;
56
+ }
57
+
58
+ override isCompound(name?: string) {
59
+ if (name !== undefined && this.name !== name) {
60
+ return false;
61
+ }
62
+
63
+ return true;
64
+ }
65
+
66
+ toObject() {
67
+ const compound: Record<string, any> = {};
68
+
69
+ for (const item of this.data) {
70
+ const name = item.name;
71
+
72
+ if (compound[name]) {
73
+ console.error('Corrupt chunk - duplicate key found', name);
74
+ }
75
+
76
+ compound[name] = item.toObject();
77
+ }
78
+
79
+ return {
80
+ type: this.type,
81
+ compound,
82
+ };
83
+ }
84
+
85
+ toSnbt() {
86
+ const compound: string[] = [];
87
+
88
+ for (const item of this.data) {
89
+ const itemSnbt = item.toSnbt();
90
+ compound.push(`${item.name}:${itemSnbt}`);
91
+ }
92
+
93
+ return `{${compound.join(',')}}`;
94
+ }
95
+ }
@@ -0,0 +1,31 @@
1
+ import debug from 'debug';
2
+ import { BinaryData } from '../lib/binary-data';
3
+ import { NbtBase } from './nbt-base';
4
+ import { NbtTagType } from './nbt-tag-type';
5
+
6
+ const debugLogFromBinaryData = debug('nbt:double:from-binary-data');
7
+
8
+ export class NbtDouble extends NbtBase<number> {
9
+ static fromBinaryData(bd: BinaryData, name?: string): NbtDouble {
10
+ name ??= NbtDouble.readName(bd);
11
+ const data = bd.getDouble();
12
+ debugLogFromBinaryData(`DOUBLE, name ${name}, data ${data}`);
13
+
14
+ return new NbtDouble(data, name);
15
+ }
16
+
17
+ constructor(data: number, name?: string) {
18
+ super(NbtTagType.DOUBLE, data, name);
19
+ }
20
+
21
+ toObject() {
22
+ return {
23
+ type: this.type,
24
+ double: this.data,
25
+ };
26
+ }
27
+
28
+ toSnbt() {
29
+ return `${this.data}d`;
30
+ }
31
+ }
@@ -0,0 +1,27 @@
1
+ import debug from 'debug';
2
+ import { NbtBase } from './nbt-base';
3
+ import { NbtTagType } from './nbt-tag-type';
4
+
5
+ const debugLogFromBinaryData = debug('nbt:end:from-binary-data');
6
+
7
+ export class NbtEnd extends NbtBase<null> {
8
+ static fromBinaryData(): NbtEnd {
9
+ debugLogFromBinaryData('END');
10
+
11
+ return new NbtEnd();
12
+ }
13
+
14
+ constructor() {
15
+ super(NbtTagType.END, null, '');
16
+ }
17
+
18
+ toObject() {
19
+ return {
20
+ type: this.type,
21
+ };
22
+ }
23
+
24
+ toSnbt(): string {
25
+ throw new Error('END tag cannot be converted to SNBT');
26
+ }
27
+ }
@@ -0,0 +1,31 @@
1
+ import debug from 'debug';
2
+ import { BinaryData } from '../lib/binary-data';
3
+ import { NbtBase } from './nbt-base';
4
+ import { NbtTagType } from './nbt-tag-type';
5
+
6
+ const debugLogFromBinaryData = debug('nbt:float:from-binary-data');
7
+
8
+ export class NbtFloat extends NbtBase<number> {
9
+ static fromBinaryData(bd: BinaryData, name?: string): NbtFloat {
10
+ name ??= NbtFloat.readName(bd);
11
+ const data = bd.getFloat();
12
+ debugLogFromBinaryData(`FLOAT, name ${name}, data ${data}`);
13
+
14
+ return new NbtFloat(data, name);
15
+ }
16
+
17
+ constructor(data: number, name?: string) {
18
+ super(NbtTagType.FLOAT, data, name);
19
+ }
20
+
21
+ toObject() {
22
+ return {
23
+ type: this.type,
24
+ float: this.data,
25
+ };
26
+ }
27
+
28
+ toSnbt() {
29
+ return `${this.data}f`;
30
+ }
31
+ }