mcbe-leveldb 1.10.2 → 1.11.0-jsonly

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.
package/LevelUtils.ts DELETED
@@ -1,3602 +0,0 @@
1
- import { appendFileSync } from "node:fs";
2
- import NBT from "prismarine-nbt";
3
- import BiomeData from "./__biome_data__.ts";
4
- import type { LevelDB } from "@8crafter/leveldb-zlib";
5
- import type { NBTSchemas } from "./nbtSchemas.ts";
6
- import type { Range } from "./types.js";
7
- import { toLongParts } from "./SNBTUtils.ts";
8
-
9
- //#region Local Constants
10
-
11
- const DEBUG = false;
12
- const fileNameEncodeCharacterRegExp = /[/:>?\\]/g;
13
- const fileNameCharacterFilterRegExp = /[^a-zA-Z0-9-_+,-.;=@~/:>?\\]/g;
14
-
15
- //#endregion
16
-
17
- //#region Local Functions
18
-
19
- /**
20
- * A tuple of length 16.
21
- *
22
- * @template T The type of the elements in the tuple.
23
- */
24
- type TupleOfLength16<T> = [T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T];
25
-
26
- function readInt16LE(buf: Uint8Array, offset: number): number {
27
- const val = buf[offset]! | (buf[offset + 1]! << 8);
28
- return val >= 0x8000 ? val - 0x10000 : val;
29
- }
30
-
31
- function writeInt16LE(value: number): Buffer<ArrayBuffer> {
32
- const buf = Buffer.alloc(2);
33
- buf.writeInt16LE(value, 0);
34
- return buf;
35
- }
36
-
37
- function writeInt32LE(value: number): Buffer<ArrayBuffer> {
38
- const buf = Buffer.alloc(4);
39
- buf.writeInt32LE(value, 0);
40
- return buf;
41
- }
42
-
43
- // export function readData3dValue(rawvalue: Uint8Array | null): {
44
- // /**
45
- // * A height map in the form [x][z].
46
- // */
47
- // heightMap: TupleOfLength16<TupleOfLength16<number>>;
48
- // /**
49
- // * A biome map in the form [x][y][z].
50
- // */
51
- // biomeMap: TupleOfLength16<TupleOfLength16<TupleOfLength16<number>>>;
52
- // } | null {
53
- // if (!rawvalue) {
54
- // return null;
55
- // }
56
-
57
- // // --- Height map (first 512 bytes -> 256 signed 16-bit ints) ---
58
- // const heightMap: TupleOfLength16<TupleOfLength16<number>> = Array.from(
59
- // { length: 16 },
60
- // (): TupleOfLength16<number> => Array(16).fill(0) as TupleOfLength16<number>
61
- // ) as TupleOfLength16<TupleOfLength16<number>>;
62
- // for (let i: number = 0; i < 256; i++) {
63
- // const val: number = readInt16LE(rawvalue, i * 2);
64
- // const x: number = i % 16;
65
- // const z: number = Math.floor(i / 16);
66
- // heightMap[x]![z] = val;
67
- // }
68
-
69
- // // --- Biome subchunks (remaining bytes) ---
70
- // const biomeBytes: Uint8Array<ArrayBuffer> = rawvalue.slice(512);
71
- // let b: BiomePalette[] = readChunkBiomes(biomeBytes);
72
-
73
- // // Validate Biome Data
74
- // if (b.length === 0 || b[0]!.values === null) {
75
- // throw new Error("Value does not contain at least one subchunk of biome data.");
76
- // }
77
-
78
- // // Enlarge list to length 24 if necessary
79
- // if (b.length < 24) {
80
- // while (b.length < 24) b.push({ values: null, palette: [] });
81
- // }
82
-
83
- // // Trim biome data
84
- // if (b.length > 24) {
85
- // if (b.slice(24).some((sub: BiomePalette): boolean => sub.values !== null)) {
86
- // console.warn(`Trimming biome data from ${b.length} to 24 subchunks.`);
87
- // }
88
- // b = b.slice(0, 24);
89
- // }
90
-
91
- // // --- Fill biome_map [16][24*16][16] (x,y,z order) ---
92
- // const biomeMap: TupleOfLength16<TupleOfLength16<TupleOfLength16<number>>> = Array.from(
93
- // { length: 16 },
94
- // (): TupleOfLength16<TupleOfLength16<number>> =>
95
- // Array.from({ length: 24 * 16 }, (): TupleOfLength16<number> => Array(16).fill(NaN) as TupleOfLength16<number>) as TupleOfLength16<
96
- // TupleOfLength16<number>
97
- // >
98
- // ) as TupleOfLength16<TupleOfLength16<TupleOfLength16<number>>>;
99
-
100
- // const hasData: boolean[] = b.map((sub: BiomePalette): boolean => sub.values !== null);
101
- // const ii: number[] = hasData
102
- // .map((has: boolean, idx: number): number | null => (has ? idx + 1 : null))
103
- // .filter((i: number | null): i is number => i !== null);
104
-
105
- // for (const i of ii) {
106
- // const bb: BiomePalette = b[i - 1]!;
107
- // if (!bb.values) continue;
108
-
109
- // for (let u: number = 0; u < 4096; u++) {
110
- // const val: number = bb.palette[bb.values[u]! - 1]!; // R is 1-based
111
- // const x: number = u % 16;
112
- // const z: number = Math.floor(u / 16) % 16;
113
- // const y: number = Math.floor(u / 256);
114
- // biomeMap[x]![16 * (i - 1) + y]![z] = val;
115
- // }
116
- // }
117
-
118
- // // Fill missing subchunks by copying top of last data subchunk
119
- // const iMax: number = Math.max(...ii);
120
- // if (iMax < 24) {
121
- // const y: number = 16 * iMax - 1;
122
- // for (let yy: number = y + 1; yy < 24 * 16; yy++) {
123
- // for (let x: number = 0; x < 16; x++) {
124
- // for (let z: number = 0; z < 16; z++) {
125
- // biomeMap[x]![yy]![z] = biomeMap[x]![y]![z]!;
126
- // }
127
- // }
128
- // }
129
- // }
130
-
131
- // return { heightMap, biomeMap };
132
- // }
133
- /**
134
- * Reads the value of the Data3D content type.
135
- *
136
- * @param rawvalue The raw value to read.
137
- * @returns The height map and biome map.
138
- */
139
- export function readData3dValue(rawvalue: Uint8Array | null): {
140
- /**
141
- * A height map in the form [x][z].
142
- */
143
- heightMap: TupleOfLength16<TupleOfLength16<number>>;
144
- /**
145
- * A biome map as an array of 24 or more BiomePalette objects.
146
- */
147
- biomes: BiomePalette[];
148
- } | null {
149
- if (!rawvalue) {
150
- return null;
151
- }
152
-
153
- // --- Height map (first 512 bytes -> 256 signed 16-bit ints) ---
154
- const heightMap: TupleOfLength16<TupleOfLength16<number>> = Array.from(
155
- { length: 16 },
156
- (): TupleOfLength16<number> => Array(16).fill(0) as TupleOfLength16<number>
157
- ) as TupleOfLength16<TupleOfLength16<number>>;
158
-
159
- for (let i: number = 0; i < 256; i++) {
160
- const val: number = readInt16LE(rawvalue, i * 2);
161
- const x: number = i % 16;
162
- const z: number = Math.floor(i / 16);
163
- heightMap[x]![z] = val;
164
- }
165
-
166
- // --- Biome subchunks (remaining bytes) ---
167
- const biomeBytes: Uint8Array = rawvalue.slice(512);
168
- let biomes: BiomePalette[] = readChunkBiomes(biomeBytes);
169
-
170
- // Validate Biome Data
171
- if (biomes.length === 0 || biomes[0]!.values === null) {
172
- throw new Error("Value does not contain at least one subchunk of biome data.");
173
- }
174
-
175
- // Enlarge list to length 24 if necessary
176
- if (biomes.length < 24) {
177
- while (biomes.length < 24) {
178
- biomes.push({ values: null, palette: [] });
179
- }
180
- }
181
-
182
- // Trim biome data (DISABLED (so that when increasing the height limits, it still works properly))
183
- // if (biomeMap.length > 24) {
184
- // if (biomeMap.slice(24).some((sub: BiomePalette): boolean => sub.values !== null)) {
185
- // console.warn(`Trimming biome data from ${biomeMap.length} to 24 subchunks.`);
186
- // }
187
- // biomeMap = biomeMap.slice(0, 24);
188
- // }
189
-
190
- return { heightMap, biomes };
191
- }
192
-
193
- /**
194
- * Writes the value of the Data3D content type.
195
- *
196
- * @param heightMap A height map in the form [x][z].
197
- * @param biomes A biome map as an array of 24 or more BiomePalette objects.
198
- * @returns The raw value to write.
199
- */
200
- export function writeData3DValue(heightMap: TupleOfLength16<TupleOfLength16<number>>, biomes: BiomePalette[]): Buffer<ArrayBuffer> {
201
- // heightMap is 16x16, flatten to 256 shorts
202
- const flatHeight: number[] = heightMap.flatMap((v: TupleOfLength16<number>, i: number): number[] =>
203
- v.map((_v2: number, i2: number): number => heightMap[i2]![i]!)
204
- );
205
-
206
- // Write height map (512 bytes)
207
- const heightBufs: Buffer[] = flatHeight.map((v: number): Buffer<ArrayBuffer> => writeInt16LE(v));
208
- const heightBuf: Buffer<ArrayBuffer> = Buffer.concat(heightBufs);
209
-
210
- // Write biome data
211
- const biomeBuf: Buffer<ArrayBuffer> = writeChunkBiomes(biomes);
212
-
213
- // Combine
214
- return Buffer.concat([heightBuf, biomeBuf]);
215
- }
216
-
217
- /**
218
- * Reads chunk biome data from a buffer.
219
- *
220
- * @param rawValue The raw value to read.
221
- * @returns The biome data.
222
- *
223
- * @internal
224
- */
225
- function readChunkBiomes(rawValue: Uint8Array): BiomePalette[] {
226
- let p: number = 0;
227
- const end: number = rawValue.length;
228
- const result: BiomePalette[] = [];
229
-
230
- while (p < end) {
231
- const { values, isPersistent, paletteSize, newOffset } = readSubchunkPaletteIds(rawValue, p, end);
232
- p = newOffset;
233
-
234
- if (values === null) {
235
- result.push({ values: null, palette: [] });
236
- continue;
237
- }
238
-
239
- if (isPersistent) {
240
- throw new Error("Biome palette does not have runtime ids.", { cause: result.length });
241
- }
242
-
243
- if (end - p < paletteSize * 4) {
244
- throw new Error("Subchunk biome palette is truncated.");
245
- }
246
-
247
- const palette: number[] = [];
248
- for (let i: number = 0; i < paletteSize; i++) {
249
- const val: number = rawValue[p]! | (rawValue[p + 1]! << 8) | (rawValue[p + 2]! << 16) | (rawValue[p + 3]! << 24);
250
- palette.push(val);
251
- p += 4;
252
- }
253
-
254
- result.push({ values, palette });
255
- }
256
-
257
- return result;
258
- }
259
-
260
- /**
261
- * Writes the chunk biome data from a BiomePalette array to a buffer.
262
- *
263
- * @param biomes The biome data to write.
264
- * @returns The resulting buffer.
265
- *
266
- * @internal
267
- */
268
- function writeChunkBiomes(biomes: BiomePalette[]): Buffer<ArrayBuffer> {
269
- const buffers: Buffer<ArrayBuffer>[] = [];
270
-
271
- for (const bb of biomes) {
272
- if (!bb || bb.values === null || bb.values.length === 0) continue; // NULL case in R
273
- const { values, palette } = bb;
274
-
275
- // --- Write subchunk palette ids (bitpacked) ---
276
- const idsBuf: Buffer<ArrayBuffer> = writeSubchunkPaletteIds(values!, palette.length);
277
-
278
- // --- Write palette size ---
279
- const paletteSizeBuf: Buffer<ArrayBuffer> = writeInt32LE(palette.length);
280
-
281
- // --- Write palette values ---
282
- const paletteBufs: Buffer<ArrayBuffer>[] = palette.map((v: number): Buffer<ArrayBuffer> => writeInt32LE(v));
283
-
284
- buffers.push(idsBuf, paletteSizeBuf, ...paletteBufs);
285
- }
286
-
287
- return Buffer.concat(buffers);
288
- }
289
-
290
- function readSubchunkPaletteIds(
291
- buffer: Uint8Array,
292
- offset: number,
293
- end: number
294
- ): {
295
- values: number[] | null;
296
- isPersistent: boolean;
297
- paletteSize: number;
298
- newOffset: number;
299
- } {
300
- let p = offset;
301
-
302
- if (end - p < 1) {
303
- throw new Error("Subchunk biome error: not enough data for flags.");
304
- }
305
-
306
- const flags = buffer[p++]!;
307
- const isPersistent = (flags & 1) === 0;
308
- const bitsPerBlock = flags >> 1;
309
-
310
- // Special case: palette copy
311
- if (bitsPerBlock === 127) {
312
- return { values: null, isPersistent, paletteSize: 0, newOffset: p };
313
- }
314
-
315
- const values = new Array(4096);
316
-
317
- if (bitsPerBlock > 0) {
318
- const blocksPerWord = 32 / bitsPerBlock;
319
- const wordCount = Math.floor(4095 / blocksPerWord) + 1;
320
- const mask = (1 << bitsPerBlock) - 1;
321
-
322
- // console.warn(
323
- // "blocksPerWord:",
324
- // blocksPerWord,
325
- // "wordCount:",
326
- // wordCount,
327
- // "bitsPerBlock:",
328
- // bitsPerBlock,
329
- // "mask:",
330
- // mask,
331
- // "p:",
332
- // p,
333
- // "end:",
334
- // end,
335
- // "end-p:",
336
- // end - p,
337
- // "4*wordCount:",
338
- // 4 * wordCount,
339
- // "flags:",
340
- // flags,
341
- // "isPersistent:",
342
- // isPersistent,
343
- // "offset:",
344
- // offset
345
- // );
346
-
347
- if (end - p < 4 * wordCount) {
348
- throw new Error("Subchunk biome error: not enough data for block words.");
349
- }
350
- // const originalP = p;
351
-
352
- let u = 0;
353
- for (let j = 0; j < wordCount; j++) {
354
- let temp = buffer[p]! | (buffer[p + 1]! << 8) | (buffer[p + 2]! << 16) | (buffer[p + 3]! << 24);
355
- p += 4;
356
-
357
- for (let k = 0; k < blocksPerWord && u < 4096; k++) {
358
- // const x = (u >> 8) & 0xf;
359
- // const z = (u >> 4) & 0xf;
360
- // const y = u & 0xf;
361
-
362
- values[u] = temp & mask;
363
- temp >>= bitsPerBlock;
364
- u++;
365
- }
366
- }
367
-
368
- if (end - p < 4) {
369
- throw new Error("Subchunk biome error: missing palette size.");
370
- }
371
-
372
- const paletteSize = buffer[p]! | (buffer[p + 1]! << 8) | (buffer[p + 2]! << 16) | (buffer[p + 3]! << 24);
373
- p += 4;
374
-
375
- // UNDONE: This does not actually restore the original data.
376
- // Attempt to repair corrupted value data from versions <=v1.9.0 of the module.
377
- // if (blocksPerWord !== Math.floor(blocksPerWord) && end - originalP < 4 * (Math.floor(4095 / Math.floor(blocksPerWord)) + 1)) {
378
- // for (let u = 0; u < 4096; u++) {
379
- // const v = values[u]!;
380
- // const repairedValue: number = Math.floor(v / 2);
381
- // values[u] = repairedValue;
382
- // }
383
- // }
384
-
385
- return { values, isPersistent, paletteSize, newOffset: p };
386
- } else {
387
- // bitsPerBlock == 0 -> everything is ID 1
388
- for (let u = 0; u < 4096; u++) {
389
- values[u] = 0;
390
- }
391
- return { values, isPersistent, paletteSize: 1, newOffset: p };
392
- }
393
- }
394
-
395
- function writeSubchunkPaletteIds(values: number[], paletteSize: number): Buffer<ArrayBuffer> {
396
- const blockCount: number = values.length; // usually 16*16*16 = 4096
397
- const bitsPerBlockOriginal: number = Math.max(1, Math.ceil(Math.log2(paletteSize)));
398
- const bitsPerBlockDivisors: number[] = [1, 2, 4, 8, 16, 32];
399
-
400
- const bitsPerBlock: number = bitsPerBlockDivisors.find((d: number): boolean => d >= bitsPerBlockOriginal) ?? 32;
401
-
402
- // console.log(bitsPerBlock);
403
-
404
- const wordsPerBlock: number = Math.ceil((blockCount * bitsPerBlock) / 32);
405
- const words = new Uint32Array(wordsPerBlock);
406
-
407
- // let bitIndex: number = 0;
408
- // for (const v of values) {
409
- // let idx: number = v >>> 0; // ensure unsigned
410
- // for (let i: number = 0; i < bitsPerBlock; i++) {
411
- // if (idx & (1 << i)) {
412
- // const wordIndex: number = Math.floor(bitIndex / 32);
413
- // const bitOffset: number = bitIndex % 32;
414
- // words[wordIndex]! |= 1 << bitOffset;
415
- // }
416
- // bitIndex++;
417
- // }
418
- // }
419
- let bitIndex = 0;
420
- for (let i = 0; i < values.length; i++) {
421
- let val = values[i]! & ((1 << bitsPerBlock) - 1); // mask to bpe bits
422
- const wordIndex = Math.floor(bitIndex / 32);
423
- const bitOffset = bitIndex % 32;
424
- words[wordIndex]! |= val << bitOffset;
425
- if (bitOffset + bitsPerBlock > 32) {
426
- // spill over into next word
427
- words[wordIndex + 1]! |= val >>> (32 - bitOffset);
428
- }
429
- bitIndex += bitsPerBlock;
430
- }
431
- // words.forEach((val, i) => {
432
- // words[i] = (-val - 1) & 0xffffffff;
433
- // });
434
-
435
- // --- Write header byte ---
436
- const header: Buffer<ArrayBuffer> = Buffer.alloc(1);
437
- header.writeUInt8((bitsPerBlock << 1) | 1, 0);
438
-
439
- // --- Write packed data ---
440
- const packed: Buffer<ArrayBuffer> = Buffer.alloc(words.length * 4);
441
- for (let i: number = 0; i < words.length; i++) {
442
- packed.writeUInt32LE(words[i]!, i * 4);
443
- }
444
-
445
- return Buffer.concat([header, packed]);
446
- }
447
-
448
- function fakeAssertType<T>(value: unknown): asserts value is T {
449
- void value;
450
- return;
451
- }
452
-
453
- //#endregion
454
-
455
- // --------------------------------------------------------------------------------
456
- // Constants
457
- // --------------------------------------------------------------------------------
458
-
459
- //#region Constants
460
-
461
- /**
462
- * The list of Minecraft dimensions.
463
- */
464
- export const dimensions = ["overworld", "nether", "the_end"] as const;
465
-
466
- /**
467
- * The list of Minecraft game modes, with their indices corresponding to their numeric gamemode IDs.
468
- */
469
- export const gameModes = ["survival", "creative", "adventure", , , "default", "spectator"] as const;
470
-
471
- /**
472
- * The content types for LevelDB entries.
473
- */
474
- export const DBEntryContentTypes = [
475
- // Biome Linked
476
- "Data3D",
477
- "Version",
478
- "Data2D",
479
- "Data2DLegacy",
480
- "SubChunkPrefix",
481
- "LegacyTerrain",
482
- "BlockEntity",
483
- "Entity",
484
- "PendingTicks",
485
- "LegacyBlockExtraData",
486
- "BiomeState",
487
- "FinalizedState",
488
- "ConversionData",
489
- "BorderBlocks",
490
- "HardcodedSpawners",
491
- "RandomTicks",
492
- "Checksums",
493
- "GenerationSeed",
494
- "GeneratedPreCavesAndCliffsBlending",
495
- "BlendingBiomeHeight",
496
- "MetaDataHash",
497
- "BlendingData",
498
- "ActorDigestVersion",
499
- "LegacyVersion",
500
- "AABBVolumes", // TO-DO: Figure out how to parse this, it seems to have data for trial chambers and trail ruins.
501
- // Village
502
- "VillageDwellers",
503
- "VillageInfo",
504
- "VillagePOI",
505
- "VillagePlayers",
506
- "VillageRaid",
507
- // Standalone
508
- "Player",
509
- "PlayerClient",
510
- "ActorPrefix",
511
- "ChunkLoadedRequest",
512
- "Digest",
513
- "Map",
514
- "Portals",
515
- "SchedulerWT",
516
- "StructureTemplate",
517
- "TickingArea",
518
- "FlatWorldLayers",
519
- "LegacyOverworld",
520
- "LegacyNether",
521
- "LegacyTheEnd",
522
- "MVillages",
523
- "Villages",
524
- "LevelSpawnWasFixed",
525
- "PositionTrackingDB",
526
- "PositionTrackingLastId",
527
- "Scoreboard",
528
- "Overworld",
529
- "Nether",
530
- "TheEnd",
531
- "AutonomousEntities",
532
- "BiomeData",
533
- "BiomeIdsTable",
534
- "MobEvents",
535
- "DynamicProperties",
536
- "LevelChunkMetaDataDictionary",
537
- "RealmsStoriesData",
538
- "LevelDat",
539
- // Dev Version
540
- "ForcedWorldCorruption",
541
- // Misc.
542
- "Unknown",
543
- ] as const;
544
-
545
- /**
546
- * Maps content types to grouping labels.
547
- *
548
- * Most content types are not grouped, only a few are.
549
- */
550
- export const DBEntryContentTypesGrouping = {
551
- // Biome Linked
552
- Data3D: "Data3D",
553
- Version: "Version",
554
- Data2D: "Data2D",
555
- Data2DLegacy: "Data2DLegacy",
556
- SubChunkPrefix: "SubChunkPrefix",
557
- LegacyTerrain: "LegacyTerrain",
558
- BlockEntity: "BlockEntity",
559
- Entity: "Entity",
560
- PendingTicks: "PendingTicks",
561
- LegacyBlockExtraData: "LegacyBlockExtraData",
562
- BiomeState: "BiomeState",
563
- FinalizedState: "FinalizedState",
564
- ConversionData: "ConversionData",
565
- BorderBlocks: "BorderBlocks",
566
- HardcodedSpawners: "HardcodedSpawners",
567
- RandomTicks: "RandomTicks",
568
- Checksums: "Checksums",
569
- GenerationSeed: "GenerationSeed",
570
- GeneratedPreCavesAndCliffsBlending: "GeneratedPreCavesAndCliffsBlending",
571
- BlendingBiomeHeight: "BlendingBiomeHeight",
572
- MetaDataHash: "MetaDataHash",
573
- BlendingData: "BlendingData",
574
- ActorDigestVersion: "ActorDigestVersion",
575
- LegacyVersion: "LegacyVersion",
576
- AABBVolumes: "AABBVolumes",
577
- // Village
578
- VillageDwellers: "VillageDwellers",
579
- VillageInfo: "VillageInfo",
580
- VillagePOI: "VillagePOI",
581
- VillagePlayers: "VillagePlayers",
582
- VillageRaid: "VillageRaid",
583
- // Standalone
584
- Player: "Player",
585
- PlayerClient: "PlayerClient",
586
- ActorPrefix: "ActorPrefix",
587
- ChunkLoadedRequest: "ChunkLoadedRequest",
588
- Digest: "Digest",
589
- Map: "Map",
590
- Portals: "Portals",
591
- SchedulerWT: "SchedulerWT",
592
- StructureTemplate: "StructureTemplate",
593
- TickingArea: "TickingArea",
594
- FlatWorldLayers: "FlatWorldLayers",
595
- LegacyOverworld: "LegacyDimension",
596
- LegacyNether: "LegacyDimension",
597
- LegacyTheEnd: "LegacyDimension",
598
- MVillages: "MVillages",
599
- Villages: "Villages",
600
- LevelSpawnWasFixed: "LevelSpawnWasFixed",
601
- PositionTrackingDB: "PositionTrackingDB",
602
- PositionTrackingLastId: "PositionTrackingLastId",
603
- Scoreboard: "Scoreboard",
604
- Overworld: "Dimension",
605
- Nether: "Dimension",
606
- TheEnd: "Dimension",
607
- AutonomousEntities: "AutonomousEntities",
608
- BiomeData: "BiomeData",
609
- BiomeIdsTable: "BiomeIdsTable",
610
- MobEvents: "MobEvents",
611
- DynamicProperties: "DynamicProperties",
612
- LevelChunkMetaDataDictionary: "LevelChunkMetaDataDictionary",
613
- RealmsStoriesData: "RealmsStoriesData",
614
- LevelDat: "LevelDat",
615
- // Dev Version
616
- ForcedWorldCorruption: "ForcedWorldCorruption",
617
- // Misc.
618
- Unknown: "Unknown",
619
- } as const satisfies Record<DBEntryContentType, string>;
620
-
621
- // TODO: Look into the supposed `idcounts` LevelDB key that was supposedly in MCPE v0.13.0.
622
- // TODO: Add support for the LegacyPlayer content type which is based on a number stored in `cilentid.txt`.
623
- // TODO: Look into if LegacyVillageManager (may just be another name for MVillages, key is allegedly `VillageManager`) is a real content type.
624
-
625
- /**
626
- * The content type to format mapping for LevelDB entries.
627
- */
628
- export const entryContentTypeToFormatMap = {
629
- /**
630
- * The Data3D content type contains heightmap and biome data for 16x16x16 chunks of the world.
631
- *
632
- * @see {@link NBTSchemas.nbtSchemas.Data3D}
633
- */
634
- Data3D: {
635
- /**
636
- * The format type of the data.
637
- */
638
- type: "custom",
639
- /**
640
- * The format type that results from the {@link entryContentTypeToFormatMap.Data3D.parse | parse} method.
641
- */
642
- resultType: "JSONNBT",
643
- /**
644
- * The function to parse the data.
645
- *
646
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
647
- *
648
- * @param data The data to parse, as a buffer.
649
- * @returns The parsed data.
650
- *
651
- * @throws {Error} If the value does not contain at least one subchunk of biome data.
652
- */
653
- parse(data: Buffer): NBTSchemas.NBTSchemaTypes.Data3D {
654
- const result = readData3dValue(data);
655
- return {
656
- type: "compound",
657
- value: {
658
- heightMap: {
659
- type: "list",
660
- value: {
661
- type: "list",
662
- value: result!.heightMap.map((row) => ({
663
- type: "short",
664
- value: row,
665
- })),
666
- },
667
- },
668
- biomes: {
669
- type: "list",
670
- value: {
671
- type: "compound",
672
- value: result!.biomes.map(
673
- (subchunk: BiomePalette): NBTSchemas.NBTSchemaTypes.Data3D["value"]["biomes"]["value"]["value"][number] => ({
674
- values: {
675
- type: "list",
676
- value:
677
- subchunk.values ?
678
- {
679
- type: "int",
680
- value: subchunk.values,
681
- }
682
- : ({
683
- type: "end",
684
- value: [],
685
- } as any),
686
- },
687
- palette: {
688
- type: "list",
689
- value: {
690
- type: "int",
691
- value: subchunk.palette,
692
- },
693
- },
694
- })
695
- ),
696
- },
697
- },
698
- },
699
- };
700
- },
701
- /**
702
- * The function to serialize the data.
703
- *
704
- * This result of this can be written directly to the file or LevelDB entry.
705
- *
706
- * @param data The data to serialize.
707
- * @returns The serialized data, as a buffer.
708
- */
709
- serialize(data: NBTSchemas.NBTSchemaTypes.Data3D): Buffer<ArrayBuffer> {
710
- return writeData3DValue(
711
- data.value.heightMap.value.value.map((row: { type: "short"; value: number[] }): number[] => row.value) as any,
712
- data.value.biomes.value.value.map(
713
- (subchunk: NBTSchemas.NBTSchemaTypes.Data3D["value"]["biomes"]["value"]["value"][number]): BiomePalette => ({
714
- palette: subchunk.palette.value.value,
715
- values: !subchunk.values.value.value?.length ? null : subchunk.values.value.value,
716
- })
717
- )
718
- );
719
- },
720
- // TO-DO: Add a default value for this.
721
- },
722
- /**
723
- * The version of a chunk.
724
- *
725
- * The current chunk version as of `v1.21.111` is `41` (`0x29`).
726
- *
727
- * Deleting think key causes the game to completely regenerate the corresponding chunk.
728
- */
729
- Version: {
730
- /**
731
- * The format type of the data.
732
- */
733
- type: "int",
734
- /**
735
- * How many bytes this integer is.
736
- */
737
- bytes: 1,
738
- /**
739
- * The endianness of the data.
740
- */
741
- format: "LE",
742
- /**
743
- * The signedness of the data.
744
- */
745
- signed: false,
746
- /**
747
- * The default value to use when initializing a new entry.
748
- *
749
- * Bytes:
750
- * ```json
751
- * [41]
752
- * ```
753
- */
754
- defaultValue: Buffer.from([41]),
755
- },
756
- /**
757
- * The Data2D content type contains heightmap and biome data for 16x128x16 chunks of the world.
758
- *
759
- * Unlike {@link entryContentTypeToFormatMap.Data3D | Data3D}, this only stores biome data for xz coordinates, so in this format all y coordinates have the same biome.
760
- *
761
- * @deprecated Only used in versions < 1.18.0.
762
- */
763
- Data2D: {
764
- /**
765
- * The format type of the data.
766
- */
767
- type: "custom",
768
- /**
769
- * The format type that results from the {@link entryContentTypeToFormatMap.Data2D.parse | parse} method.
770
- */
771
- resultType: "JSONNBT",
772
- /**
773
- * The function to parse the data.
774
- *
775
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
776
- *
777
- * @param data The data to parse, as a buffer.
778
- * @returns The parsed data.
779
- */
780
- parse(data: Buffer): NBTSchemas.NBTSchemaTypes.Data2D {
781
- const heightMap: TupleOfLength16<TupleOfLength16<number>> = Array.from(
782
- { length: 16 },
783
- (): TupleOfLength16<number> => Array(16).fill(0) as TupleOfLength16<number>
784
- ) as TupleOfLength16<TupleOfLength16<number>>;
785
-
786
- for (let i = 0; i < 256; i++) {
787
- const val: number = readInt16LE(data, i * 2);
788
- const x: number = i % 16;
789
- const z: number = Math.floor(i / 16);
790
- heightMap[x]![z] = val;
791
- }
792
-
793
- const biomeData: TupleOfLength16<TupleOfLength16<number>> = Array.from(
794
- { length: 16 },
795
- (): TupleOfLength16<number> => Array(16).fill(0) as TupleOfLength16<number>
796
- ) as TupleOfLength16<TupleOfLength16<number>>;
797
-
798
- for (let i = 0; i < 256; i++) {
799
- const val: number = data.readUInt8(512 + i);
800
- const x: number = i % 16;
801
- const z: number = Math.floor(i / 16);
802
- biomeData[x]![z] = val;
803
- }
804
-
805
- return {
806
- type: "compound",
807
- value: {
808
- heightMap: {
809
- type: "list",
810
- value: {
811
- type: "list",
812
- value: heightMap.map((row: TupleOfLength16<number>) => ({
813
- type: "short",
814
- value: row,
815
- })),
816
- },
817
- },
818
- biomeData: {
819
- type: "list",
820
- value: {
821
- type: "list",
822
- value: biomeData.map((row: TupleOfLength16<number>) => ({
823
- type: "byte",
824
- value: row,
825
- })),
826
- },
827
- },
828
- },
829
- };
830
- },
831
- /**
832
- * The function to serialize the data.
833
- *
834
- * This result of this can be written directly to the file or LevelDB entry.
835
- *
836
- * @param data The data to serialize.
837
- * @returns The serialized data, as a buffer.
838
- */
839
- serialize(data: NBTSchemas.NBTSchemaTypes.Data2D): Buffer<ArrayBuffer> {
840
- const heightMap = data.value.heightMap.value.value;
841
- const biomeData = data.value.biomeData.value.value;
842
-
843
- const buffer: Buffer<ArrayBuffer> = Buffer.alloc(512 + 256);
844
-
845
- for (let z = 0; z < 16; z++) {
846
- for (let x: number = 0; x < 16; x++) {
847
- const i: number = z * 16 + x;
848
- const val: number = heightMap[x]!.value[z]!;
849
- buffer.writeInt16LE(val, i * 2);
850
- }
851
- }
852
-
853
- for (let z = 0; z < 16; z++) {
854
- for (let x: number = 0; x < 16; x++) {
855
- const i: number = z * 16 + x;
856
- const val: number = biomeData[x]!.value[z]!;
857
- buffer.writeUInt8(val, 512 + i);
858
- }
859
- }
860
-
861
- return buffer;
862
- },
863
- },
864
- /**
865
- * The Data2D content type contains heightmap and biome data for 16x128x16 chunks of the world.
866
- *
867
- * Unlike {@link entryContentTypeToFormatMap.Data3D | Data3D}, this only stores biome data for xz coordinates, so in this format all y coordinates have the same biome.
868
- *
869
- * Unlike both {@link entryContentTypeToFormatMap.Data3D | Data3D} and {@link entryContentTypeToFormatMap.Data2D | Data2D}, this also stores biome color data.
870
- *
871
- * @deprecated Only used in versions < 1.0.0.
872
- */
873
- Data2DLegacy: {
874
- /**
875
- * The format type of the data.
876
- */
877
- type: "custom",
878
- /**
879
- * The format type that results from the {@link entryContentTypeToFormatMap.Data2DLegacy.parse | parse} method.
880
- */
881
- resultType: "JSONNBT",
882
- /**
883
- * The function to parse the data.
884
- *
885
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
886
- *
887
- * @param data The data to parse, as a buffer.
888
- * @returns The parsed data.
889
- */
890
- parse(data: Buffer): NBTSchemas.NBTSchemaTypes.Data2DLegacy {
891
- const heightMap: TupleOfLength16<TupleOfLength16<number>> = Array.from(
892
- { length: 16 },
893
- (): TupleOfLength16<number> => Array(16).fill(0) as TupleOfLength16<number>
894
- ) as TupleOfLength16<TupleOfLength16<number>>;
895
-
896
- for (let i = 0; i < 256; i++) {
897
- const val: number = readInt16LE(data, i * 2);
898
- const x: number = i % 16;
899
- const z: number = Math.floor(i / 16);
900
- heightMap[x]![z] = val;
901
- }
902
-
903
- const biomeData: TupleOfLength16<TupleOfLength16<[biomeId: number, red: number, green: number, blue: number]>> = Array.from(
904
- { length: 16 },
905
- (): TupleOfLength16<[biomeId: number, red: number, green: number, blue: number]> =>
906
- Array(16).fill(0) as TupleOfLength16<[biomeId: number, red: number, green: number, blue: number]>
907
- ) as TupleOfLength16<TupleOfLength16<[biomeId: number, red: number, green: number, blue: number]>>;
908
-
909
- for (let i = 0; i < 256; i++) {
910
- const vals: [biomeId: number, red: number, green: number, blue: number] = [
911
- data.readUInt8(512 + i * 4),
912
- data.readUInt8(512 + i * 4 + 1),
913
- data.readUInt8(512 + i * 4 + 2),
914
- data.readUInt8(512 + i * 4 + 3),
915
- ];
916
- const x: number = i % 16;
917
- const z: number = Math.floor(i / 16);
918
- biomeData[x]![z] = vals;
919
- }
920
-
921
- return {
922
- type: "compound",
923
- value: {
924
- heightMap: {
925
- type: "list",
926
- value: {
927
- type: "list",
928
- value: heightMap.map((row: TupleOfLength16<number>) => ({
929
- type: "short",
930
- value: row,
931
- })),
932
- },
933
- },
934
- biomeData: {
935
- type: "list",
936
- value: {
937
- type: "list",
938
- value: biomeData.map((row: TupleOfLength16<[biomeId: number, red: number, green: number, blue: number]>) => ({
939
- type: "list",
940
- value: row.map((val: [biomeId: number, red: number, green: number, blue: number]) => ({
941
- type: "byte",
942
- value: val,
943
- })),
944
- })),
945
- },
946
- },
947
- },
948
- };
949
- },
950
- /**
951
- * The function to serialize the data.
952
- *
953
- * This result of this can be written directly to the file or LevelDB entry.
954
- *
955
- * @param data The data to serialize.
956
- * @returns The serialized data, as a buffer.
957
- */
958
- serialize(data: NBTSchemas.NBTSchemaTypes.Data2DLegacy): Buffer<ArrayBuffer> {
959
- const heightMap = data.value.heightMap.value.value;
960
- const biomeData = data.value.biomeData.value.value;
961
-
962
- const buffer: Buffer<ArrayBuffer> = Buffer.alloc(512 + 1024);
963
-
964
- for (let z = 0; z < 16; z++) {
965
- for (let x: number = 0; x < 16; x++) {
966
- const i: number = z * 16 + x;
967
- const val: number = heightMap[x]!.value[z]!;
968
- buffer.writeInt16LE(val, i * 2);
969
- }
970
- }
971
-
972
- for (let z = 0; z < 16; z++) {
973
- for (let x: number = 0; x < 16; x++) {
974
- const i: number = z * 16 + x;
975
- const vals: [biomeId: number, red: number, green: number, blue: number] = biomeData[x]!.value[z]!.value;
976
- vals.forEach((val: number, index: number): void => void buffer.writeUInt8(val, 512 + i * 4 + index));
977
- }
978
- }
979
-
980
- return buffer;
981
- },
982
- },
983
- /**
984
- * The SubChunkPrefix content type contains block data for 16x16x16 subchunks of the world.
985
- *
986
- * In older versions, it may also contain sky and block light data.
987
- *
988
- * @see {@link NBTSchemas.nbtSchemas.SubChunkPrefix}
989
- */
990
- SubChunkPrefix: {
991
- /**
992
- * The format type of the data.
993
- */
994
- type: "custom",
995
- /**
996
- * The format type that results from the {@link entryContentTypeToFormatMap.SubChunkPrefix.parse | parse} method.
997
- */
998
- resultType: "JSONNBT",
999
- /**
1000
- * The function to parse the data.
1001
- *
1002
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
1003
- *
1004
- * @param data The data to parse, as a buffer.
1005
- * @returns A promise that resolves with the parsed data.
1006
- *
1007
- * @throws {Error} If the SubChunkPrefix version is unknown.
1008
- * @throws {Error} If the storage version is unknown.
1009
- */
1010
- async parse(data: Buffer): Promise<NBTSchemas.NBTSchemaTypes.SubChunkPrefix> {
1011
- let currentOffset: number = 0;
1012
- /**
1013
- * The version of the SubChunkPrefix.
1014
- *
1015
- * Should be `0x00`/`0x02`/`0x03`/`0x04`/`0x05`/`0x06`/`0x07` (1.0.0 <= x < 1.2.13), `0x01` (beta 1.2.13.5), `0x08` (1.2.13 <= x < 1.18.0), or `0x09` (1.18.0 <= x).
1016
- *
1017
- * @todo Add handling for `0x00`, `0x02`, `0x03`, `0x04`, `0x05`, `0x06`, and `0x07`.
1018
- */
1019
- const version: 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 = data[currentOffset++]! as any;
1020
- if (![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].includes(version)) throw new Error(`Unknown SubChunkPrefix version: ${version}`);
1021
- if (version === 0x00 || version === 0x02 || version === 0x03 || version === 0x04 || version === 0x05 || version === 0x06 || version === 0x07) {
1022
- const block_ids: number[] = [...data.subarray(currentOffset, currentOffset + 4096)];
1023
- currentOffset += 4096;
1024
-
1025
- function unpackNibbleArray(byteCount: number): Range<0, 15>[] {
1026
- const slice = data.subarray(currentOffset, currentOffset + byteCount);
1027
- currentOffset += byteCount;
1028
-
1029
- return [...slice].flatMap((n: number): number[] => [n >> 4, n & 0x0f]) as Range<0, 15>[];
1030
- }
1031
-
1032
- const block_data: Range<0, 15>[] = unpackNibbleArray(2048);
1033
- const sky_light: Range<0, 15>[] | undefined = data.length - currentOffset > 0 ? unpackNibbleArray(2048) : undefined;
1034
- const block_light: Range<0, 15>[] | undefined = data.length - currentOffset > 0 ? unpackNibbleArray(2048) : undefined;
1035
- return NBT.comp({
1036
- version: NBT.byte<0x00 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07>(version),
1037
- block_data: { type: "list", value: { type: "byte", value: block_data } },
1038
- block_ids: { type: "list", value: { type: "byte", value: block_ids } },
1039
- ...(sky_light ? { sky_light: { type: "list", value: { type: "byte", value: sky_light } } } : {}),
1040
- ...(block_light ? { block_light: { type: "list", value: { type: "byte", value: block_light } } } : {}),
1041
- } as const satisfies NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v0["value"]);
1042
- }
1043
- const layers: NBTSchemas.NBTSchemaTypes.SubChunkPrefixLayer["value"][] = [];
1044
- /**
1045
- * How many blocks are in each location (ex. 2 might mean here is a waterlog layer).
1046
- */
1047
- let numStorageBlocks: number = version === 0x01 ? 1 : data[currentOffset++]!;
1048
- const subChunkIndex: number | undefined = version >= 0x09 ? data[currentOffset++] : undefined;
1049
- for (let blockIndex: number = 0; blockIndex < numStorageBlocks; blockIndex++) {
1050
- const storageVersion: number = data[currentOffset]!;
1051
- if (
1052
- !(
1053
- storageVersion === 1 ||
1054
- storageVersion === 2 ||
1055
- storageVersion === 3 ||
1056
- storageVersion === 4 ||
1057
- storageVersion === 5 ||
1058
- storageVersion === 6 ||
1059
- storageVersion === 8 ||
1060
- storageVersion === 16
1061
- )
1062
- )
1063
- throw new Error(`Unknown storage version: ${storageVersion}`);
1064
- currentOffset++;
1065
- const bitsPerBlock: number = storageVersion >> 1;
1066
- const blocksPerWord: number = Math.floor(32 / bitsPerBlock);
1067
- const numints: number = Math.ceil(4096 / blocksPerWord);
1068
- const blockDataOffset: number = currentOffset;
1069
- let paletteOffset: number = blockDataOffset + 4 * numints;
1070
- let psize: number = bitsPerBlock === 0 ? 0 : getInt32Val(data, paletteOffset);
1071
- paletteOffset += Math.sign(bitsPerBlock) * 4;
1072
- // const debugInfo = {
1073
- // version,
1074
- // blockIndex,
1075
- // storageVersion,
1076
- // bitsPerBlock,
1077
- // blocksPerWord,
1078
- // numints,
1079
- // blockDataOffset,
1080
- // paletteOffset,
1081
- // psize,
1082
- // // blockData,
1083
- // // palette,
1084
- // };
1085
- const palette: {
1086
- [paletteIndex: `${bigint}`]: NBTSchemas.NBTSchemaTypes.Block;
1087
- } = {};
1088
- for (let i: bigint = 0n; i < psize; i++) {
1089
- // console.log(debugInfo);
1090
- // appendFileSync("./test1.bin", JSON.stringify(debugInfo) + "\n");
1091
- const result = (await NBT.parse(data.subarray(paletteOffset), "little")) as unknown as {
1092
- parsed: NBTSchemas.NBTSchemaTypes.Block;
1093
- type: NBT.NBTFormat;
1094
- metadata: NBT.Metadata;
1095
- };
1096
- paletteOffset += result.metadata.size;
1097
- palette[`${i}`] = result.parsed;
1098
- // console.log(result);
1099
- // appendFileSync("./test1.bin", JSON.stringify(result));
1100
- }
1101
- currentOffset = paletteOffset;
1102
- const block_indices: number[] = [];
1103
- for (let i: number = 0; i < 4096; i++) {
1104
- const maskVal: number = getInt32Val(data, blockDataOffset + Math.floor(i / blocksPerWord) * 4);
1105
- const blockVal: number = (maskVal >> ((i % blocksPerWord) * bitsPerBlock)) & ((1 << bitsPerBlock) - 1);
1106
- // const blockType: DataTypes_Block | undefined = palette[`${blockVal as unknown as bigint}`];
1107
- // if (!blockType && blockVal !== -1) throw new ReferenceError(`Invalid block palette index ${blockVal} for block ${i}`);
1108
- /* const chunkOffset: Vector3 = {
1109
- x: (i >> 8) & 0xf,
1110
- y: (i >> 4) & 0xf,
1111
- z: (i >> 0) & 0xf,
1112
- }; */
1113
- block_indices.push(blockVal);
1114
- }
1115
- layers.push({
1116
- storageVersion: NBT.byte(storageVersion),
1117
- palette: NBT.comp(palette),
1118
- block_indices: { type: "list", value: { type: "int", value: block_indices } },
1119
- });
1120
- }
1121
- if (version === 0x01) {
1122
- return NBT.comp({
1123
- version: NBT.byte<0x01>(version),
1124
- layerCount: NBT.byte<1>(numStorageBlocks as 1),
1125
- layers: { type: "list", value: NBT.comp(layers as [(typeof layers)[0]]) },
1126
- } as const satisfies NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v1["value"]);
1127
- }
1128
- return NBT.comp({
1129
- version: NBT.byte<0x08 | 0x09>(version),
1130
- layerCount: NBT.byte(numStorageBlocks),
1131
- layers: { type: "list", value: NBT.comp(layers) },
1132
- ...(version >= 0x09 ?
1133
- {
1134
- subChunkIndex: NBT.byte(subChunkIndex!),
1135
- }
1136
- : {}),
1137
- } as const satisfies NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v8["value"]);
1138
- },
1139
- /**
1140
- * The function to serialize the data.
1141
- *
1142
- * This result of this can be written directly to the file or LevelDB entry.
1143
- *
1144
- * @param data The data to serialize.
1145
- * @returns The serialized data, as a buffer.
1146
- */
1147
- serialize(data: NBTSchemas.NBTSchemaTypes.SubChunkPrefix): Buffer<ArrayBuffer> {
1148
- if (![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].includes(data.value.version.value))
1149
- throw new Error(`Unsupported subchunk prefix version: ${data.value.version.value}`);
1150
- switch (data.value.version.value) {
1151
- case 0x00:
1152
- case 0x02:
1153
- case 0x03:
1154
- case 0x04:
1155
- case 0x05:
1156
- case 0x06:
1157
- case 0x07: {
1158
- fakeAssertType<NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v0>(data);
1159
- function packNibbles(arr: number[]): number[] {
1160
- return arr.reduce(
1161
- (p: number[], v: number, i: number): number[] =>
1162
- i % 2 === 0 ? [...p, v & 0x0f] : [...p.slice(0, -1), (p.at(-1)! << 4) | (v & 0x0f)],
1163
- []
1164
- );
1165
- }
1166
-
1167
- const buffer: Buffer<ArrayBuffer> = Buffer.from([data.value.version.value]);
1168
- return Buffer.concat([
1169
- buffer,
1170
- Buffer.from(data.value.block_ids.value.value),
1171
- Buffer.from(packNibbles(data.value.block_data.value.value)),
1172
- ...(data.value.sky_light ? [Buffer.from(packNibbles(data.value.sky_light.value.value))] : []),
1173
- ...(data.value.block_light ? [Buffer.from(packNibbles(data.value.block_light.value.value))] : []),
1174
- ]);
1175
- }
1176
- case 0x01:
1177
- case 0x08:
1178
- case 0x09: {
1179
- fakeAssertType<NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v1 | NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v8>(data);
1180
- const buffer: Buffer<ArrayBuffer> = Buffer.from([
1181
- data.value.version.value,
1182
- ...(data.value.version.value >= 0x08 ? [data.value.layerCount.value] : []),
1183
- ...(data.value.version.value >= 0x09 ? [(data as NBTSchemas.NBTSchemaTypes.SubChunkPrefix_v8).value.subChunkIndex!.value] : []),
1184
- ]);
1185
- const layerBuffers: Buffer<ArrayBuffer>[] = data.value.layers.value.value.map(
1186
- (layer: NBTSchemas.NBTSchemaTypes.SubChunkPrefixLayer["value"]): Buffer<ArrayBuffer> => {
1187
- const bitsPerBlock: number = layer.storageVersion.value >> 1;
1188
- const blocksPerWord: number = Math.floor(32 / bitsPerBlock);
1189
- const numints: number = Math.ceil(4096 / blocksPerWord);
1190
- const bytes: number[] = [layer.storageVersion.value];
1191
- const blockIndicesBuffer: Buffer<ArrayBuffer> = Buffer.alloc(Math.ceil(numints * 4));
1192
- writeBlockIndices(blockIndicesBuffer, 0, layer.block_indices.value.value, bitsPerBlock, blocksPerWord);
1193
- bytes.push(...blockIndicesBuffer);
1194
- const paletteLengthBuffer: Buffer<ArrayBuffer> = Buffer.alloc(4);
1195
- setInt32Val(paletteLengthBuffer, 0, Object.keys(layer.palette.value).length);
1196
- bytes.push(...paletteLengthBuffer);
1197
- const paletteKeys: `${bigint}`[] = (Object.keys(layer.palette.value) as `${bigint}`[]).sort(
1198
- (a: `${bigint}`, b: `${bigint}`): number => Number(a) - Number(b)
1199
- );
1200
- for (let paletteIndex: number = 0; paletteIndex < paletteKeys.length; paletteIndex++) {
1201
- const block: NBTSchemas.NBTSchemaTypes.Block = layer.palette.value[paletteKeys[paletteIndex]!]!;
1202
- bytes.push(...NBT.writeUncompressed({ name: "", ...block }, "little"));
1203
- }
1204
- return Buffer.from(bytes);
1205
- }
1206
- );
1207
- return Buffer.concat([buffer, ...layerBuffers]);
1208
- }
1209
- }
1210
- },
1211
- // TO-DO: Add a default value for this.
1212
- },
1213
- /**
1214
- * The LegacyTerrain content type contains block, sky light, block light, dirty columns, and grass color data for 16x16x128 chunks of the world.
1215
- *
1216
- * @deprecated Only used in versions < 1.0.0.
1217
- *
1218
- * @see {@link NBTSchemas.nbtSchemas.LegacyTerrain}
1219
- */
1220
- LegacyTerrain: {
1221
- /**
1222
- * The format type of the data.
1223
- */
1224
- type: "custom",
1225
- /**
1226
- * The format type that results from the {@link entryContentTypeToFormatMap.LegacyTerrain.parse | parse} method.
1227
- */
1228
- resultType: "JSONNBT",
1229
- /**
1230
- * The function to parse the data.
1231
- *
1232
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
1233
- *
1234
- * @param data The data to parse, as a buffer.
1235
- * @returns The parsed data.
1236
- */
1237
- parse(data: Buffer): NBTSchemas.NBTSchemaTypes.LegacyTerrain {
1238
- let currentOffset = 0;
1239
-
1240
- const block_ids: number[] = [...data.subarray(currentOffset, currentOffset + 32768)];
1241
- currentOffset += 32768;
1242
-
1243
- function unpackNibbleArray(byteCount: number): Range<0, 15>[] {
1244
- const slice = data.subarray(currentOffset, currentOffset + byteCount);
1245
- currentOffset += byteCount;
1246
-
1247
- return [...slice].flatMap((n: number): number[] => [n >> 4, n & 0x0f]) as Range<0, 15>[];
1248
- }
1249
-
1250
- const block_data: Range<0, 15>[] = unpackNibbleArray(16384);
1251
-
1252
- const sky_light: Range<0, 15>[] = unpackNibbleArray(16384);
1253
-
1254
- const block_light: Range<0, 15>[] = unpackNibbleArray(16384);
1255
-
1256
- const dirty_columns: number[] = [...data.subarray(currentOffset, currentOffset + 256)];
1257
- currentOffset += 256;
1258
-
1259
- const grass_color: number[] = [...data.subarray(currentOffset, currentOffset + 1024)];
1260
- currentOffset += 1024;
1261
-
1262
- return NBT.comp({
1263
- block_ids: { type: "list", value: { type: "byte", value: block_ids } },
1264
- block_data: { type: "list", value: { type: "byte", value: block_data } },
1265
- sky_light: { type: "list", value: { type: "byte", value: sky_light } },
1266
- block_light: { type: "list", value: { type: "byte", value: block_light } },
1267
- dirty_columns: { type: "list", value: { type: "byte", value: dirty_columns } },
1268
- grass_color: { type: "list", value: { type: "byte", value: grass_color } },
1269
- } as const satisfies NBTSchemas.NBTSchemaTypes.LegacyTerrain["value"]);
1270
- },
1271
- /**
1272
- * The function to serialize the data.
1273
- *
1274
- * This result of this can be written directly to the file or LevelDB entry.
1275
- *
1276
- * @param data The data to serialize.
1277
- * @returns The serialized data, as a buffer.
1278
- */
1279
- serialize(data: NBTSchemas.NBTSchemaTypes.LegacyTerrain): Buffer<ArrayBuffer> {
1280
- function packNibbles(arr: number[]): number[] {
1281
- return arr.reduce(
1282
- (p: number[], v: number, i: number): number[] => (i % 2 === 0 ? [...p, v & 0x0f] : [...p.slice(0, -1), (p.at(-1)! << 4) | (v & 0x0f)]),
1283
- []
1284
- );
1285
- }
1286
-
1287
- return Buffer.concat([
1288
- Buffer.from(data.value.block_ids.value.value),
1289
- Buffer.from(packNibbles(data.value.block_data.value.value)),
1290
- Buffer.from(packNibbles(data.value.sky_light.value.value)),
1291
- Buffer.from(packNibbles(data.value.block_light.value.value)),
1292
- Buffer.from(data.value.dirty_columns.value.value),
1293
- Buffer.from(data.value.grass_color.value.value),
1294
- ]);
1295
- },
1296
- // TO-DO: Add a default value for this.
1297
- },
1298
- /**
1299
- * A list of block entities associated with a chunk.
1300
- *
1301
- * @see {@link NBTSchemas.nbtSchemas.BlockEntity}
1302
- */
1303
- BlockEntity: {
1304
- /**
1305
- * The format type of the data.
1306
- */
1307
- type: "custom",
1308
- /**
1309
- * The format type that results from the {@link entryContentTypeToFormatMap.BlockEntity.parse | parse} method.
1310
- */
1311
- resultType: "JSONNBT",
1312
- /**
1313
- * The function to parse the data.
1314
- *
1315
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
1316
- *
1317
- * @param data The data to parse, as a buffer.
1318
- * @returns A promise that resolves with the parsed data.
1319
- *
1320
- * @throws {any} If an error occurs while parsing the data.
1321
- */
1322
- async parse(data: Buffer): Promise<{
1323
- type: "compound";
1324
- value: {
1325
- blockEntities: {
1326
- type: "list";
1327
- value: { type: "compound"; value: NBTSchemas.NBTSchemaTypes.BlockEntity["value"][] };
1328
- };
1329
- };
1330
- }> {
1331
- const blockEntities: NBTSchemas.NBTSchemaTypes.BlockEntity["value"][] = [];
1332
- let currentIndex: number = 0;
1333
- while (currentIndex < data.length) {
1334
- const result = await NBT.parse(data.subarray(currentIndex), "little");
1335
- currentIndex += result.metadata.size;
1336
- blockEntities.push(result.parsed.value as NBTSchemas.NBTSchemaTypes.BlockEntity["value"]);
1337
- }
1338
- return {
1339
- type: "compound",
1340
- value: {
1341
- blockEntities: {
1342
- type: "list",
1343
- value: {
1344
- type: "compound",
1345
- value: blockEntities,
1346
- },
1347
- },
1348
- },
1349
- };
1350
- },
1351
- /**
1352
- * The function to serialize the data.
1353
- *
1354
- * This result of this can be written directly to the file or LevelDB entry.
1355
- *
1356
- * @param data The data to serialize.
1357
- * @returns The serialized data, as a buffer.
1358
- */
1359
- serialize(data: {
1360
- type: "compound";
1361
- value: {
1362
- blockEntities: {
1363
- type: "list";
1364
- value: { type: "compound"; value: NBTSchemas.NBTSchemaTypes.BlockEntity["value"][] };
1365
- };
1366
- };
1367
- }): Buffer<ArrayBuffer> {
1368
- const nbtData: Buffer[] = data.value.blockEntities.value.value.map(
1369
- (blockEntity: NBTSchemas.NBTSchemaTypes.BlockEntity["value"]): Buffer =>
1370
- NBT.writeUncompressed({ name: "", type: "compound", value: blockEntity }, "little")
1371
- );
1372
- return Buffer.concat(nbtData);
1373
- },
1374
- },
1375
- /**
1376
- * A list of entities associated with a chunk.
1377
- *
1378
- * @deprecated No longer used.
1379
- *
1380
- * @todo Figure out what version this was deprecated in (it exists in v1.16.40 but not in 1.21.51).
1381
- */
1382
- Entity: {
1383
- /**
1384
- * The format type of the data.
1385
- */
1386
- type: "custom",
1387
- /**
1388
- * The format type that results from the {@link entryContentTypeToFormatMap.Entity.parse | parse} method.
1389
- */
1390
- resultType: "JSONNBT",
1391
- /**
1392
- * The function to parse the data.
1393
- *
1394
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
1395
- *
1396
- * @param data The data to parse, as a buffer.
1397
- * @returns A promise that resolves with the parsed data.
1398
- *
1399
- * @throws {any} If an error occurs while parsing the data.
1400
- */
1401
- async parse(data: Buffer): Promise<{
1402
- type: "compound";
1403
- value: {
1404
- entities: {
1405
- type: "list";
1406
- value: { type: "compound"; value: NBTSchemas.NBTSchemaTypes.ActorPrefix["value"][] };
1407
- };
1408
- };
1409
- }> {
1410
- const entities: NBTSchemas.NBTSchemaTypes.ActorPrefix["value"][] = [];
1411
- let currentIndex: number = 0;
1412
- while (currentIndex < data.length) {
1413
- const result = await NBT.parse(data.subarray(currentIndex), "little");
1414
- currentIndex += result.metadata.size;
1415
- entities.push(result.parsed.value as NBTSchemas.NBTSchemaTypes.ActorPrefix["value"]);
1416
- }
1417
- return {
1418
- type: "compound",
1419
- value: {
1420
- entities: {
1421
- type: "list",
1422
- value: {
1423
- type: "compound",
1424
- value: entities,
1425
- },
1426
- },
1427
- },
1428
- };
1429
- },
1430
- /**
1431
- * The function to serialize the data.
1432
- *
1433
- * This result of this can be written directly to the file or LevelDB entry.
1434
- *
1435
- * @param data The data to serialize.
1436
- * @returns The serialized data, as a buffer.
1437
- */
1438
- serialize(data: {
1439
- type: "compound";
1440
- value: {
1441
- entities: {
1442
- type: "list";
1443
- value: { type: "compound"; value: NBTSchemas.NBTSchemaTypes.ActorPrefix["value"][] };
1444
- };
1445
- };
1446
- }): Buffer<ArrayBuffer> {
1447
- const nbtData: Buffer[] = data.value.entities.value.value.map(
1448
- (entity: NBTSchemas.NBTSchemaTypes.ActorPrefix["value"]): Buffer =>
1449
- NBT.writeUncompressed({ name: "", type: "compound", value: entity }, "little")
1450
- );
1451
- return Buffer.concat(nbtData);
1452
- },
1453
- },
1454
- /**
1455
- * @see {@link NBTSchemas.nbtSchemas.PendingTicks}
1456
- *
1457
- * @todo Add a description for this.
1458
- */
1459
- PendingTicks: {
1460
- /**
1461
- * The format type of the data.
1462
- */
1463
- type: "NBT",
1464
- /**
1465
- * The default value to use when initializing a new entry.
1466
- *
1467
- * @todo Add a link to the object with the default value once it is made.
1468
- */
1469
- get defaultValue(): Buffer<ArrayBuffer> {
1470
- const result: Buffer<ArrayBuffer> = Buffer.from(
1471
- NBT.writeUncompressed(
1472
- {
1473
- name: "",
1474
- type: "compound",
1475
- value: {
1476
- currentTick: {
1477
- type: "int",
1478
- value: 0,
1479
- },
1480
- tickList: {
1481
- type: "list",
1482
- value: {
1483
- type: "compound",
1484
- value: [
1485
- {
1486
- tileID: {
1487
- type: "int",
1488
- value: 0,
1489
- },
1490
- blockState: {
1491
- type: "compound",
1492
- value: {
1493
- name: {
1494
- type: "string",
1495
- value: "minecraft:air",
1496
- },
1497
- states: {
1498
- type: "compound",
1499
- value: {},
1500
- },
1501
- version: {
1502
- type: "int",
1503
- value: 0,
1504
- },
1505
- },
1506
- },
1507
- time: {
1508
- type: "long",
1509
- value: toLongParts(0n),
1510
- },
1511
- x: {
1512
- type: "int",
1513
- value: 0,
1514
- },
1515
- y: {
1516
- type: "int",
1517
- value: 0,
1518
- },
1519
- z: {
1520
- type: "int",
1521
- value: 0,
1522
- },
1523
- },
1524
- ],
1525
- },
1526
- },
1527
- },
1528
- } satisfies NBTSchemas.NBTSchemaTypes.PendingTicks & NBT.NBT,
1529
- "little"
1530
- )
1531
- );
1532
- Object.defineProperty(this, "defaultValue", { value: result, configurable: true, enumerable: true, writable: false });
1533
- return result;
1534
- },
1535
- },
1536
- /**
1537
- * @deprecated Only used in versions < 1.2.3.
1538
- *
1539
- * @todo Figure out how to parse this.
1540
- * @todo Add a description for this.
1541
- */
1542
- LegacyBlockExtraData: {
1543
- /**
1544
- * The format type of the data.
1545
- */
1546
- type: "unknown",
1547
- },
1548
- /**
1549
- * @todo Figure out how to parse this.
1550
- * @todo Add a description for this.
1551
- */
1552
- BiomeState: {
1553
- /**
1554
- * The format type of the data.
1555
- */
1556
- type: "unknown",
1557
- },
1558
- /**
1559
- * A value that indicates the finalization state of a chunk's data.
1560
- *
1561
- * Possible values:
1562
- * - `0`: Unknown
1563
- * - `1`: Unknown
1564
- * - `2`: Unknown
1565
- *
1566
- * @todo Figure out the meanings of the values.
1567
- */
1568
- FinalizedState: {
1569
- /**
1570
- * The format type of the data.
1571
- */
1572
- type: "int",
1573
- /**
1574
- * How many bytes this integer is.
1575
- */
1576
- bytes: 4,
1577
- /**
1578
- * The endianness of the data.
1579
- */
1580
- format: "LE",
1581
- /**
1582
- * The signedness of the data.
1583
- */
1584
- signed: false,
1585
- /**
1586
- * The default value to use when initializing a new entry.
1587
- *
1588
- * Bytes:
1589
- * ```json
1590
- * [0,0,0,0]
1591
- * ```
1592
- */
1593
- defaultValue: Buffer.from([0, 0, 0, 0]),
1594
- },
1595
- /**
1596
- * @deprecated No longer used.
1597
- *
1598
- * @todo Figure out how to parse this.
1599
- * @todo Add a description for this.
1600
- */
1601
- ConversionData: {
1602
- /**
1603
- * The format type of the data.
1604
- */
1605
- type: "unknown",
1606
- },
1607
- /**
1608
- * @todo Figure out how to parse this.
1609
- * @todo Add a description for this.
1610
- */
1611
- BorderBlocks: {
1612
- /**
1613
- * The format type of the data.
1614
- */
1615
- type: "unknown",
1616
- },
1617
- /**
1618
- * Bounding boxes for structure spawns, such as a Nether Fortress or Pillager Outpost.
1619
- *
1620
- * @deprecated Replaced with {@link AABBVolumes} in either 1.21.120 or one of the 1.21.120 previews.
1621
- *
1622
- * @todo Figure out how to parse this.
1623
- */
1624
- HardcodedSpawners: {
1625
- /**
1626
- * The format type of the data.
1627
- */
1628
- type: "unknown",
1629
- },
1630
- /**
1631
- * @see {@link NBTSchemas.nbtSchemas.RandomTicks}
1632
- *
1633
- * @todo Add a description for this.
1634
- */
1635
- RandomTicks: {
1636
- /**
1637
- * The format type of the data.
1638
- */
1639
- type: "NBT",
1640
- /**
1641
- * The default value to use when initializing a new entry.
1642
- *
1643
- * @todo Add a link to the object with the default value once it is made.
1644
- */
1645
- get defaultValue(): Buffer<ArrayBuffer> {
1646
- const result: Buffer<ArrayBuffer> = Buffer.from(
1647
- NBT.writeUncompressed(
1648
- {
1649
- name: "",
1650
- type: "compound",
1651
- value: {
1652
- currentTick: {
1653
- type: "int",
1654
- value: 0,
1655
- },
1656
- tickList: {
1657
- type: "list",
1658
- value: {
1659
- type: "compound",
1660
- value: [
1661
- {
1662
- blockState: {
1663
- type: "compound",
1664
- value: {
1665
- name: {
1666
- type: "string",
1667
- value: "minecraft:air",
1668
- },
1669
- states: {
1670
- type: "compound",
1671
- value: {},
1672
- },
1673
- version: {
1674
- type: "int",
1675
- value: 0,
1676
- },
1677
- },
1678
- },
1679
- time: {
1680
- type: "long",
1681
- value: toLongParts(0n),
1682
- },
1683
- x: {
1684
- type: "int",
1685
- value: 0,
1686
- },
1687
- y: {
1688
- type: "int",
1689
- value: 0,
1690
- },
1691
- z: {
1692
- type: "int",
1693
- value: 0,
1694
- },
1695
- },
1696
- ],
1697
- },
1698
- },
1699
- },
1700
- } satisfies NBTSchemas.NBTSchemaTypes.RandomTicks & NBT.NBT,
1701
- "little"
1702
- )
1703
- );
1704
- Object.defineProperty(this, "defaultValue", { value: result, configurable: true, enumerable: true, writable: false });
1705
- return result;
1706
- },
1707
- },
1708
- /**
1709
- * The low segment of the 4 byte halves of the seed value used to generate the chunk.
1710
- *
1711
- * The low segment can also be found by reading the first 4 bytes of the seed as a little-endian signed integer when it is encoded as a little-endian 64-bit signed integer.
1712
- *
1713
- * @todo Check if this still exists (I have only seen it in a world from a 1.19.60.22 dev build).
1714
- */
1715
- GenerationSeed: {
1716
- /**
1717
- * The format type of the data.
1718
- */
1719
- type: "int",
1720
- /**
1721
- * How many bytes this integer is.
1722
- */
1723
- bytes: 4,
1724
- /**
1725
- * The endianness of the data.
1726
- */
1727
- format: "LE",
1728
- /**
1729
- * The signedness of the data.
1730
- */
1731
- signed: true,
1732
- },
1733
- /**
1734
- * xxHash64 checksums of `SubChunkPrefix`, `BlockEntity`, `Entity`, and `Data2D` values.
1735
- *
1736
- * @deprecated Only used in versions < 1.18.0.
1737
- *
1738
- * @todo Figure out how to parse this.
1739
- */
1740
- Checksums: {
1741
- /**
1742
- * The format type of the data.
1743
- */
1744
- type: "unknown",
1745
- },
1746
- /**
1747
- * @deprecated Unused.
1748
- *
1749
- * @todo Figure out how to parse this.
1750
- * @todo Add a description for this.
1751
- */
1752
- GeneratedPreCavesAndCliffsBlending: {
1753
- /**
1754
- * The format type of the data.
1755
- */
1756
- type: "unknown",
1757
- },
1758
- /**
1759
- * @todo Figure out how to parse this.
1760
- * @todo Add a description for this.
1761
- */
1762
- BlendingBiomeHeight: {
1763
- /**
1764
- * The format type of the data.
1765
- */
1766
- type: "unknown",
1767
- },
1768
- /**
1769
- * A hash which is the key in the `LevelChunkMetaDataDictionary` record
1770
- * for the NBT metadata of this chunk. Seems like it might default to something dependent
1771
- * on the current game or chunk version.
1772
- *
1773
- * @todo Figure out how to parse this.
1774
- */
1775
- MetaDataHash: {
1776
- /**
1777
- * The format type of the data.
1778
- */
1779
- type: "unknown",
1780
- },
1781
- /**
1782
- * @todo Figure out how to parse this.
1783
- * @todo Add a description for this.
1784
- */
1785
- BlendingData: {
1786
- /**
1787
- * The format type of the data.
1788
- */
1789
- type: "unknown",
1790
- },
1791
- /**
1792
- * @todo Figure out how to parse this.
1793
- * @todo Add a description for this.
1794
- *
1795
- * Seems to always be one byte with a value of `0x00`.
1796
- */
1797
- ActorDigestVersion: {
1798
- /**
1799
- * The format type of the data.
1800
- */
1801
- type: "unknown",
1802
- },
1803
- /**
1804
- * @deprecated Only used in versions < 1.16.100. Later versions use {@link entryContentTypeToFormatMap.Version}
1805
- *
1806
- * @todo Add a description for this.
1807
- */
1808
- LegacyVersion: {
1809
- /**
1810
- * The format type of the data.
1811
- */
1812
- type: "int",
1813
- /**
1814
- * How many bytes this integer is.
1815
- */
1816
- bytes: 1,
1817
- /**
1818
- * The endianness of the data.
1819
- */
1820
- format: "LE",
1821
- /**
1822
- * The signedness of the data.
1823
- */
1824
- signed: false,
1825
- },
1826
- /**
1827
- * @deprecated It is unknown when this was removed, it was found in a Windows 10 Edition Beta v0.15.0 world.
1828
- *
1829
- * @todo Add a schema for this.
1830
- * @todo Add a description for this.
1831
- */
1832
- MVillages: {
1833
- /**
1834
- * The format type of the data.
1835
- */
1836
- type: "NBT",
1837
- },
1838
- /**
1839
- * @deprecated It is unknown when this was removed.
1840
- *
1841
- * @todo Add a schema for this.
1842
- * @todo Add a description for this.
1843
- */
1844
- Villages: {
1845
- /**
1846
- * The format type of the data.
1847
- */
1848
- type: "NBT",
1849
- },
1850
- /**
1851
- * @see {@link NBTSchemas.nbtSchemas.VillageDwellers}
1852
- *
1853
- * @todo Add a description for this.
1854
- */
1855
- VillageDwellers: {
1856
- /**
1857
- * The format type of the data.
1858
- */
1859
- type: "NBT",
1860
- },
1861
- /**
1862
- * @see {@link NBTSchemas.nbtSchemas.VillageInfo}
1863
- *
1864
- * @todo Add a description for this.
1865
- */
1866
- VillageInfo: {
1867
- /**
1868
- * The format type of the data.
1869
- */
1870
- type: "NBT",
1871
- },
1872
- /**
1873
- * @see {@link NBTSchemas.nbtSchemas.VillagePOI}
1874
- *
1875
- * @todo Add a description for this.
1876
- */
1877
- VillagePOI: {
1878
- /**
1879
- * The format type of the data.
1880
- */
1881
- type: "NBT",
1882
- },
1883
- /**
1884
- * @see {@link NBTSchemas.nbtSchemas.VillagePlayers}
1885
- *
1886
- * @todo Add a description for this.
1887
- */
1888
- VillagePlayers: {
1889
- /**
1890
- * The format type of the data.
1891
- */
1892
- type: "NBT",
1893
- },
1894
- /**
1895
- * @see {@link NBTSchemas.nbtSchemas.VillageRaid}
1896
- *
1897
- * @todo Add a description for this.
1898
- */
1899
- VillageRaid: {
1900
- /**
1901
- * The format type of the data.
1902
- */
1903
- type: "NBT",
1904
- },
1905
- /**
1906
- * A player's data.
1907
- *
1908
- * @see {@link NBTSchemas.nbtSchemas.Player}
1909
- */
1910
- Player: {
1911
- /**
1912
- * The format type of the data.
1913
- */
1914
- type: "NBT",
1915
- },
1916
- /**
1917
- * A player's client data.
1918
- *
1919
- * This includes the key for the player's {@link entryContentTypeToFormatMap.Player | server data}, the player's Microsoft account ID, and the player's self-signed ID.
1920
- *
1921
- * @see {@link NBTSchemas.nbtSchemas.PlayerClient}
1922
- */
1923
- PlayerClient: {
1924
- /**
1925
- * The format type of the data.
1926
- */
1927
- type: "NBT",
1928
- },
1929
- /**
1930
- * The data for an entity in the world.
1931
- *
1932
- * @see {@link NBTSchemas.nbtSchemas.ActorPrefix}
1933
- */
1934
- ActorPrefix: {
1935
- /**
1936
- * The format type of the data.
1937
- */
1938
- type: "NBT",
1939
- },
1940
- /**
1941
- * A list of entity IDs in a chunk.
1942
- *
1943
- * These IDs are the ones used in the entities' LevelDB keys.
1944
- *
1945
- * The IDs are 32-bit signed integers.
1946
- */
1947
- Digest: {
1948
- /**
1949
- * The format type of the data.
1950
- */
1951
- type: "custom",
1952
- /**
1953
- * The format type that results from the {@link entryContentTypeToFormatMap.Digest.parse | parse} method.
1954
- */
1955
- resultType: "JSONNBT",
1956
- /**
1957
- * The function to parse the data.
1958
- *
1959
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
1960
- *
1961
- * @param data The data to parse, as a buffer.
1962
- * @returns A promise that resolves with the parsed data.
1963
- *
1964
- * @throws {any} If an error occurs while parsing the data.
1965
- */
1966
- async parse(data: Buffer): Promise<NBTSchemas.NBTSchemaTypes.Digest> {
1967
- const entityIds: [high: number, low: number][] = [];
1968
- for (let i = 0; i < data.length; i += 8) {
1969
- entityIds.push([data.readInt32LE(i), data.readInt32LE(i + 4)]);
1970
- }
1971
- return {
1972
- type: "compound",
1973
- value: {
1974
- entityIds: {
1975
- type: "list",
1976
- value: {
1977
- type: "long",
1978
- value: entityIds,
1979
- },
1980
- },
1981
- },
1982
- };
1983
- },
1984
- /**
1985
- * The function to serialize the data.
1986
- *
1987
- * This result of this can be written directly to the file or LevelDB entry.
1988
- *
1989
- * @param data The data to serialize.
1990
- * @returns The serialized data, as a buffer.
1991
- *
1992
- * @throws {any} If an error occurs while parsing the data.
1993
- */
1994
- serialize(data: NBTSchemas.NBTSchemaTypes.Digest): Buffer<ArrayBuffer> {
1995
- const rawData: Buffer<ArrayBuffer>[] = data.value.entityIds.value.value.map((entityIds: [high: number, low: number]): Buffer<ArrayBuffer> => {
1996
- const buffer: Buffer<ArrayBuffer> = Buffer.alloc(8);
1997
- buffer.writeInt32LE(entityIds[0], 0);
1998
- buffer.writeInt32LE(entityIds[1], 4);
1999
- return buffer;
2000
- });
2001
- return Buffer.concat(rawData);
2002
- },
2003
- },
2004
- /**
2005
- * The data for a map.
2006
- *
2007
- * This includes things such as location, image data, and decorations.
2008
- *
2009
- * @see {@link NBTSchemas.nbtSchemas.Map}
2010
- */
2011
- Map: {
2012
- /**
2013
- * The format type of the data.
2014
- */
2015
- type: "NBT",
2016
- },
2017
- /**
2018
- * The content type of the `portals` LevelDB key, which stores portal data.
2019
- *
2020
- * @see {@link NBTSchemas.nbtSchemas.Portals}
2021
- */
2022
- Portals: {
2023
- /**
2024
- * The format type of the data.
2025
- */
2026
- type: "NBT",
2027
- },
2028
- /**
2029
- * The content type of the `schedulerWT` LevelDB key, which stores wandering trader data.
2030
- *
2031
- * @see {@link NBTSchemas.nbtSchemas.SchedulerWT}
2032
- */
2033
- SchedulerWT: {
2034
- /**
2035
- * The format type of the data.
2036
- */
2037
- type: "NBT",
2038
- },
2039
- /**
2040
- * Date for a structure (the kind saved by a structure block or the [`/structure`](https://minecraft.wiki/w/Commands/structure) command).
2041
- *
2042
- * @see {@link NBTSchemas.nbtSchemas.StructureTemplate}
2043
- */
2044
- StructureTemplate: {
2045
- /**
2046
- * The format type of the data.
2047
- */
2048
- type: "NBT",
2049
- /**
2050
- * The raw file extension of the data.
2051
- */
2052
- rawFileExtension: "mcstructure",
2053
- },
2054
- /**
2055
- * A ticking area, either from an entity or the [`/tickingarea`](https://minecraft.wiki/w/Commands/tickingarea) command.
2056
- *
2057
- * @see {@link NBTSchemas.nbtSchemas.TickingArea}
2058
- */
2059
- TickingArea: {
2060
- /**
2061
- * The format type of the data.
2062
- */
2063
- type: "NBT",
2064
- },
2065
- /**
2066
- * @deprecated Only used in versions < 1.5.0.
2067
- *
2068
- * @todo Add a description for this.
2069
- */
2070
- FlatWorldLayers: {
2071
- /**
2072
- * The format type of the data.
2073
- */
2074
- type: "ASCII",
2075
- },
2076
- /**
2077
- * @deprecated It is unknown when this was removed, it was found in a Windows 10 Edition Beta v0.15.0 world.
2078
- *
2079
- * @todo Add a description for this.
2080
- */
2081
- LevelSpawnWasFixed: {
2082
- /**
2083
- * The format type of the data.
2084
- */
2085
- type: "UTF-8",
2086
- /**
2087
- * The default value to use when initializing a new entry.
2088
- *
2089
- * Value:
2090
- * ```typescript
2091
- * Buffer.from("True", "utf-8")
2092
- * ```
2093
- */
2094
- defaultValue: Buffer.from("True", "utf-8"),
2095
- },
2096
- /**
2097
- * Stores the location of a lodestone compass.
2098
- *
2099
- * @todo Add a schema for this.
2100
- */
2101
- PositionTrackingDB: {
2102
- /**
2103
- * The format type of the data.
2104
- */
2105
- type: "NBT",
2106
- },
2107
- /**
2108
- * The last ID used for a lodestone compass.
2109
- *
2110
- * @todo Add a schema for this.
2111
- */
2112
- PositionTrackingLastId: {
2113
- /**
2114
- * The format type of the data.
2115
- */
2116
- type: "NBT",
2117
- },
2118
- /**
2119
- * The scoreboard data (ex. from the [`/scoreboard`](https://minecraft.wiki/w/Commands/scoreboard) command).
2120
- *
2121
- * @see {@link NBTSchemas.nbtSchemas.Scoreboard}
2122
- */
2123
- Scoreboard: {
2124
- /**
2125
- * The format type of the data.
2126
- */
2127
- type: "NBT",
2128
- },
2129
- /**
2130
- * The structure data of the Overworld dimension.
2131
- *
2132
- * This content type coexists with the {@link entryContentTypeToFormatMap.Overworld | Overworld} content type in some versions (such as v1.0.0.16 and v1.1.5.0).
2133
- *
2134
- * @deprecated It is unknown when this was removed, it was found in a Windows 10 Edition Beta v0.15.0 world.
2135
- *
2136
- * @see {@link NBTSchemas.nbtSchemas.LegacyOverworld}
2137
- */
2138
- LegacyOverworld: {
2139
- /**
2140
- * The format type of the data.
2141
- */
2142
- type: "NBT",
2143
- },
2144
- /**
2145
- * The structure data of the Nether dimension.
2146
- *
2147
- * This content type coexists with the {@link entryContentTypeToFormatMap.Nether | Nether} content type in some versions (such as v1.0.0.16 and v1.1.5.0).
2148
- *
2149
- * @deprecated It is unknown when this was removed, it was found in a Windows 10 Edition Beta v0.15.0 world.
2150
- *
2151
- * @see {@link NBTSchemas.nbtSchemas.LegacyNether}
2152
- */
2153
- LegacyNether: {
2154
- /**
2155
- * The format type of the data.
2156
- */
2157
- type: "NBT",
2158
- },
2159
- /**
2160
- * The structure data of the End dimension.
2161
- *
2162
- * This content type coexists with the {@link entryContentTypeToFormatMap.TheEnd | TheEnd} content type in some versions (such as v1.0.0.16 and v1.1.5.0).
2163
- *
2164
- * @deprecated It is unknown when this was removed, it was found in a Windows 10 Edition Beta v0.15.0 world.
2165
- *
2166
- * @see {@link NBTSchemas.nbtSchemas.LegacyTheEnd}
2167
- */
2168
- LegacyTheEnd: {
2169
- /**
2170
- * The format type of the data.
2171
- */
2172
- type: "NBT",
2173
- },
2174
- /**
2175
- * The data of the Overworld dimension.
2176
- *
2177
- * @see {@link NBTSchemas.nbtSchemas.Overworld}
2178
- */
2179
- Overworld: {
2180
- /**
2181
- * The format type of the data.
2182
- */
2183
- type: "NBT",
2184
- },
2185
- /**
2186
- * The data of the Nether dimension.
2187
- *
2188
- * @see {@link NBTSchemas.nbtSchemas.Nether}
2189
- */
2190
- Nether: {
2191
- /**
2192
- * The format type of the data.
2193
- */
2194
- type: "NBT",
2195
- },
2196
- /**
2197
- * The data of the End dimension.
2198
- *
2199
- * @see {@link NBTSchemas.nbtSchemas.TheEnd}
2200
- */
2201
- TheEnd: {
2202
- /**
2203
- * The format type of the data.
2204
- */
2205
- type: "NBT",
2206
- },
2207
- /**
2208
- * The content type of the `AutonomousEntities` LevelDB key, which stores a list of autonomous entities.
2209
- *
2210
- * @see {@link NBTSchemas.nbtSchemas.AutonomousEntities}
2211
- *
2212
- * @todo Add a better description for this, that includes what an autonomous entity is.
2213
- */
2214
- AutonomousEntities: {
2215
- /**
2216
- * The format type of the data.
2217
- */
2218
- type: "NBT",
2219
- },
2220
- /**
2221
- * The content type of the `BiomeData` LevelDB key, which stores data for certain biome types.
2222
- *
2223
- * @see {@link NBTSchemas.nbtSchemas.BiomeData}
2224
- */
2225
- BiomeData: {
2226
- /**
2227
- * The format type of the data.
2228
- */
2229
- type: "NBT",
2230
- },
2231
- /**
2232
- * The content type of the `BiomeIdsTable` LevelDB key, which stores the numeric ID mappings for custom biomes.
2233
- *
2234
- * @see {@link NBTSchemas.nbtSchemas.BiomeIdsTable}
2235
- */
2236
- BiomeIdsTable: {
2237
- /**
2238
- * The format type of the data.
2239
- */
2240
- type: "NBT",
2241
- },
2242
- /**
2243
- * The content type of the `mobevents` LevelDB key, which stores what mob events are enabled or disabled.
2244
- *
2245
- * @see {@link NBTSchemas.nbtSchemas.MobEvents}
2246
- */
2247
- MobEvents: {
2248
- /**
2249
- * The format type of the data.
2250
- */
2251
- type: "NBT",
2252
- // TO-DO: Add a default value for this.
2253
- },
2254
- /**
2255
- * The content type of the `level.dat` and `level.dat_old` files.
2256
- *
2257
- * This stores all the world settings.
2258
- *
2259
- * @see {@link NBTSchemas.nbtSchemas.LevelDat}
2260
- */
2261
- LevelDat: {
2262
- /**
2263
- * The format type of the data.
2264
- */
2265
- type: "custom",
2266
- /**
2267
- * The format type that results from the {@link entryContentTypeToFormatMap.LevelDat.parse | parse} method.
2268
- */
2269
- resultType: "JSONNBT",
2270
- /**
2271
- * The function to parse the data.
2272
- *
2273
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
2274
- *
2275
- * @param data The data to parse, as a buffer.
2276
- * @returns A promise that resolves with the parsed data.
2277
- *
2278
- * @throws {any} If an error occurs while parsing the data.
2279
- */
2280
- async parse(data: Buffer): Promise<NBTSchemas.NBTSchemaTypes.LevelDat> {
2281
- return (await NBT.parse(data)).parsed;
2282
- },
2283
- /**
2284
- * The function to serialize the data.
2285
- *
2286
- * This result of this can be written directly to the file or LevelDB entry.
2287
- *
2288
- * @param data The data to serialize.
2289
- * @returns The serialized data, as a buffer.
2290
- *
2291
- * @throws {TypeError} If {@link data} has a name property at the top level that is not of type string.
2292
- */
2293
- serialize(data: NBTSchemas.NBTSchemaTypes.LevelDat): Buffer<ArrayBuffer> {
2294
- const nbtData: Buffer = NBT.writeUncompressed({ name: "", ...data }, "little");
2295
- return Buffer.concat([Buffer.from("0A000000", "hex"), writeSpecificIntType(Buffer.alloc(4), BigInt(nbtData.length), 4, "LE", false), nbtData]);
2296
- },
2297
- /**
2298
- * The default value to use when initializing a new entry.
2299
- *
2300
- * @todo Add a link to the object with the default level.dat value once it is made.
2301
- */
2302
- get defaultValue(): Buffer<ArrayBuffer> {
2303
- // TO-DO: Add a full default level.dat value.
2304
- const nbtData: Buffer = NBT.writeUncompressed(
2305
- { name: "", type: "compound", value: {} } as /* @todo Remove this partial later. */ Partial<NBTSchemas.NBTSchemaTypes.LevelDat> & NBT.NBT,
2306
- "little"
2307
- );
2308
- const result: Buffer<ArrayBuffer> = Buffer.concat([
2309
- Buffer.from("0A000000", "hex"),
2310
- writeSpecificIntType(Buffer.alloc(4), BigInt(nbtData.length), 4, "LE", false),
2311
- nbtData,
2312
- ]);
2313
- Object.defineProperty(this, "defaultValue", { value: result, configurable: true, enumerable: true, writable: false });
2314
- return result;
2315
- },
2316
- /**
2317
- * The raw file extension of the data.
2318
- */
2319
- rawFileExtension: "dat",
2320
- },
2321
- /**
2322
- * Bounding boxes for structures, including structure spawns (such as a Pillager Outpost)
2323
- * and volumes where mobs cannot spawn through the normal biome-based means (such as
2324
- * Trial Chambers).
2325
- *
2326
- * @todo Figure out how to parse this.
2327
- */
2328
- AABBVolumes: {
2329
- /**
2330
- * The format type of the data.
2331
- */
2332
- type: "unknown",
2333
- },
2334
- /**
2335
- * The content type of the `DynamicProperties` LevelDB key, which stores dynamic properties data for add-ons.
2336
- *
2337
- * @see {@link NBTSchemas.nbtSchemas.DynamicProperties}
2338
- */
2339
- DynamicProperties: {
2340
- /**
2341
- * The format type of the data.
2342
- */
2343
- type: "NBT",
2344
- /**
2345
- * The default value to use when initializing a new entry.
2346
- *
2347
- * Value:
2348
- * ```json
2349
- * { "name": "", "type": "compound", "value": {} }
2350
- * ```
2351
- */
2352
- defaultValue: NBT.writeUncompressed({ name: "", type: "compound", value: {} }, "little"),
2353
- },
2354
- /**
2355
- * Stores the NBT metadata of all chunks. Maps the xxHash64 hash of NBT data
2356
- * to that NBT data, so that each chunk need only store 8 bytes instead of the entire
2357
- * NBT; most chunks have the same metadata.
2358
- *
2359
- * The first 4 bytes represent the number of entries as a 32-bit little-endian integer (it is unknown if it is signed or not).
2360
- *
2361
- * The first 4 bytes are followed by multiple chunks of data formatted as the 8 byte hash of the NBT data plus the NBT compound.
2362
- *
2363
- * ```
2364
- * {BYTEx4}{BYTEx8}{NBTCompound}{BYTEx8}{NBTCompound}{BYTEx8}{NBTCompound}{BYTEx8}{NBTCompound}
2365
- * ```
2366
- */
2367
- LevelChunkMetaDataDictionary: {
2368
- /**
2369
- * The format type of the data.
2370
- */
2371
- type: "custom",
2372
- resultType: "JSONNBT",
2373
- // TODO: Make an NBT schema for the `LevelChunkMetaDataDictionary` format and use it as the return type here.
2374
- async parse(data: Buffer): Promise<NBTSchemas.NBTSchemaTypes.LevelChunkMetaDataDictionary> {
2375
- const parsedData: NBTSchemas.NBTSchemaTypes.LevelChunkMetaDataDictionary["value"] = {};
2376
- for (let i = 12; i < data.length; i += 8) {
2377
- const hash = data.subarray(i - 8, i);
2378
- // TODO: Figure out how to use parseUncompressed in this situation to make it sync while still being able to get the size offset.
2379
- const parsed = await NBT.parse(data.subarray(i), "little");
2380
- // parsedData.push([data.slice(i - 8, i), parsed]);
2381
- parsedData[hash.toString("hex")] = parsed.parsed as unknown as NBTSchemas.NBTSchemaTypes.LevelChunkMetaDataDictionary["value"][string];
2382
- i += parsed.metadata.size;
2383
- }
2384
- return { type: "compound", value: parsedData };
2385
- },
2386
- serialize(data: NBTSchemas.NBTSchemaTypes.LevelChunkMetaDataDictionary): Buffer<ArrayBuffer> {
2387
- // TODO: Find a way to properly hash the NBT data, since the `xxhashjs` and `xxhash-wasm` modules don't match up with the hashes generated by the game.
2388
- const entryCountBuffer: Buffer<ArrayBuffer> = Buffer.alloc(4);
2389
- entryCountBuffer.writeUInt32LE(Object.keys(data.value).length, 0);
2390
- const dataBuffers: Buffer[] = [entryCountBuffer];
2391
- for (const [hashHex, value] of Object.entries(data.value)) {
2392
- if (!/^[0-9a-f]{16}$/i.test(hashHex)) throw new TypeError(`Invalid hash: ${hashHex}`);
2393
- const hash: Buffer<ArrayBuffer> = Buffer.from(hashHex, "hex");
2394
- const nbtBuffer: Buffer = NBT.writeUncompressed({ name: "", ...value }, "little");
2395
- dataBuffers.push(hash);
2396
- dataBuffers.push(nbtBuffer);
2397
- }
2398
- return Buffer.concat(dataBuffers);
2399
- },
2400
- },
2401
- /**
2402
- * @todo Figure out how to parse this. (It seems that each one just has a value of 1 (`0x31`). It also seems that the data is actually based on the key, which has an id that can be used with the realms API to get the corresponding data.)
2403
- * @todo Add a description for this.
2404
- */
2405
- RealmsStoriesData: {
2406
- /**
2407
- * The format type of the data.
2408
- */
2409
- type: "unknown",
2410
- },
2411
- /**
2412
- * The content type used for LevelDB keys that are used for the forced world corruption feature of the developer version of Bedrock Edition.
2413
- *
2414
- * These keys are normally only found when the [`/corruptworld`](https://minecraft.wiki/w/Commands/corruptworld) command is used.
2415
- *
2416
- * Removing these keys fixes the forced world corruption.
2417
- */
2418
- ForcedWorldCorruption: {
2419
- /**
2420
- * The format type of the data.
2421
- */
2422
- type: "UTF-8",
2423
- /**
2424
- * The default value to use when initializing a new entry.
2425
- *
2426
- * Value:
2427
- * ```typescript
2428
- * Buffer.from("true", "utf-8")
2429
- * ```
2430
- */
2431
- defaultValue: Buffer.from("true", "utf-8"),
2432
- },
2433
- /**
2434
- * @todo Add a schema for this.
2435
- * @todo Add a description for this.
2436
- */
2437
- ChunkLoadedRequest: {
2438
- /**
2439
- * The format type of the data.
2440
- */
2441
- type: "NBT",
2442
- // TO-DO: Add a default value for this.
2443
- },
2444
- /**
2445
- * All data that has a key that is not recognized.
2446
- */
2447
- Unknown: {
2448
- /**
2449
- * The format type of the data.
2450
- */
2451
- type: "unknown",
2452
- },
2453
- } as const satisfies {
2454
- [key in DBEntryContentType]: EntryContentTypeFormatData;
2455
- };
2456
-
2457
- /**
2458
- * The format data for an entry content type.
2459
- *
2460
- * Use this type if you want to have support for content type formats that aren't currently is use but may be in the future.
2461
- *
2462
- * This is used in the {@link entryContentTypeToFormatMap} object.
2463
- */
2464
- export type EntryContentTypeFormatData = (
2465
- | {
2466
- /**
2467
- * The format type of the data.
2468
- */
2469
- readonly type: "JSON" | "SNBT" | "ASCII" | "binary" | "binaryPlainText" | "hex" | "UTF-8" | "unknown";
2470
- }
2471
- | {
2472
- /**
2473
- * The format type of the data.
2474
- */
2475
- readonly type: "NBT";
2476
- /**
2477
- * The endianness of the data.
2478
- *
2479
- * If not present, `"LE"` should be assumed.
2480
- *
2481
- * - `"BE"`: Big Endian
2482
- * - `"LE"`: Little Endian
2483
- * - `"LEV"`: Little Varint
2484
- *
2485
- * @default "LE"
2486
- */
2487
- readonly format?: "BE" | "LE" | "LEV";
2488
- }
2489
- | {
2490
- /**
2491
- * The format type of the data.
2492
- */
2493
- readonly type: "int";
2494
- /**
2495
- * How many bytes this integer is.
2496
- */
2497
- readonly bytes: number;
2498
- /**
2499
- * The endianness of the data.
2500
- */
2501
- readonly format: "BE" | "LE";
2502
- /**
2503
- * The signedness of the data.
2504
- */
2505
- readonly signed: boolean;
2506
- }
2507
- | {
2508
- /**
2509
- * The format type of the data.
2510
- */
2511
- readonly type: "custom";
2512
- /**
2513
- * The format type that results from the {@link parse} method.
2514
- */
2515
- readonly resultType: "JSONNBT";
2516
- /**
2517
- * The function to parse the data.
2518
- *
2519
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
2520
- *
2521
- * @param data The data to parse, as a buffer.
2522
- * @returns The parsed data.
2523
- */
2524
- parse(data: Buffer): NBT.Compound | Promise<NBT.Compound>;
2525
- /**
2526
- * The function to serialize the data.
2527
- *
2528
- * This result of this can be written directly to the file or LevelDB entry.
2529
- *
2530
- * @param data The data to serialize.
2531
- * @returns The serialized data, as a buffer.
2532
- */
2533
- serialize(data: NBT.Compound): Buffer | Promise<Buffer>;
2534
- }
2535
- | {
2536
- /**
2537
- * The format type of the data.
2538
- */
2539
- readonly type: "custom";
2540
- /**
2541
- * The format type that results from the {@link parse} method.
2542
- */
2543
- readonly resultType: "SNBT";
2544
- /**
2545
- * The function to parse the data.
2546
- *
2547
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
2548
- *
2549
- * @param data The data to parse, as a buffer.
2550
- * @returns The parsed data.
2551
- */
2552
- parse(data: Buffer): string | Promise<string>;
2553
- /**
2554
- * The function to serialize the data.
2555
- *
2556
- * This result of this can be written directly to the file or LevelDB entry.
2557
- *
2558
- * @param data The data to serialize.
2559
- * @returns The serialized data, as a buffer.
2560
- */
2561
- serialize(data: string): Buffer | Promise<Buffer>;
2562
- }
2563
- | {
2564
- /**
2565
- * The format type of the data.
2566
- */
2567
- readonly type: "custom";
2568
- /**
2569
- * The format type that results from the {@link parse} method.
2570
- */
2571
- readonly resultType: "buffer";
2572
- /**
2573
- * The function to parse the data.
2574
- *
2575
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
2576
- *
2577
- * @param data The data to parse, as a buffer.
2578
- * @returns The parsed data.
2579
- */
2580
- parse(data: Buffer): Buffer | Promise<Buffer>;
2581
- /**
2582
- * The function to serialize the data.
2583
- *
2584
- * This result of this can be written directly to the file or LevelDB entry.
2585
- *
2586
- * @param data The data to serialize.
2587
- * @returns The serialized data, as a buffer.
2588
- */
2589
- serialize(data: Buffer): Buffer | Promise<Buffer>;
2590
- }
2591
- | {
2592
- /**
2593
- * The format type of the data.
2594
- */
2595
- readonly type: "custom";
2596
- /**
2597
- * The format type that results from the {@link parse} method.
2598
- */
2599
- readonly resultType: "unknown";
2600
- /**
2601
- * The function to parse the data.
2602
- *
2603
- * The {@link data} parameter should be the buffer read directly from the file or LevelDB entry.
2604
- *
2605
- * @param data The data to parse, as a buffer.
2606
- * @returns The parsed data.
2607
- */
2608
- parse(data: Buffer): any | Promise<any>;
2609
- /**
2610
- * The function to serialize the data.
2611
- *
2612
- * This result of this can be written directly to the file or LevelDB entry.
2613
- *
2614
- * @param data The data to serialize.
2615
- * @returns The serialized data, as a buffer.
2616
- */
2617
- serialize(data: any): Buffer | Promise<Buffer>;
2618
- }
2619
- ) & {
2620
- /**
2621
- * The raw file extension of the data.
2622
- *
2623
- * If not present, `"bin"` should be assumed.
2624
- *
2625
- * @default "bin"
2626
- */
2627
- readonly rawFileExtension?: string;
2628
- /**
2629
- * The default value to use when initializing a new entry.
2630
- *
2631
- * @default undefined
2632
- */
2633
- readonly defaultValue?: Buffer;
2634
- };
2635
-
2636
- //#endregion
2637
-
2638
- // --------------------------------------------------------------------------------
2639
- // Types
2640
- // --------------------------------------------------------------------------------
2641
-
2642
- //#region Types
2643
-
2644
- /**
2645
- * Represents a two-directional vector.
2646
- */
2647
- export interface Vector2 {
2648
- /**
2649
- * X component of this vector.
2650
- */
2651
- x: number;
2652
- /**
2653
- * Y component of this vector.
2654
- */
2655
- y: number;
2656
- }
2657
-
2658
- /**
2659
- * Represents a two-directional vector with X and Z components.
2660
- */
2661
- export interface VectorXZ {
2662
- /**
2663
- * X component of this vector.
2664
- */
2665
- x: number;
2666
- /**
2667
- * Z component of this vector.
2668
- */
2669
- z: number;
2670
- }
2671
-
2672
- /**
2673
- * Represents a three-directional vector.
2674
- */
2675
- export interface Vector3 {
2676
- /**
2677
- * X component of this vector.
2678
- */
2679
- x: number;
2680
- /**
2681
- * Y component of this vector.
2682
- */
2683
- y: number;
2684
- /**
2685
- * Z component of this vector.
2686
- */
2687
- z: number;
2688
- }
2689
-
2690
- /**
2691
- * An ID of a Minecraft dimension.
2692
- */
2693
- export type Dimension = (typeof dimensions)[number];
2694
-
2695
- /**
2696
- * Represents a three-directional vector with an associated dimension.
2697
- */
2698
- export interface DimensionLocation extends Vector3 {
2699
- /**
2700
- * Dimension that this coordinate is associated with.
2701
- */
2702
- dimension: Dimension;
2703
- }
2704
-
2705
- /**
2706
- * Represents a two-directional vector with an associated dimension.
2707
- */
2708
- export interface DimensionVector2 extends Vector2 {
2709
- /**
2710
- * Dimension that this coordinate is associated with.
2711
- */
2712
- dimension: Dimension;
2713
- }
2714
-
2715
- /**
2716
- * Represents a two-directional vector with X and Z components and an associated dimension.
2717
- */
2718
- export interface DimensionVectorXZ extends VectorXZ {
2719
- /**
2720
- * Dimension that this coordinate is associated with.
2721
- */
2722
- dimension: Dimension;
2723
- }
2724
-
2725
- /**
2726
- * Represents a two-directional vector with X and Z components and an associated dimension and a sub-chunk index.
2727
- */
2728
- export interface SubChunkIndexDimensionVectorXZ extends DimensionVectorXZ {
2729
- /**
2730
- * The index of this sub-chunk.
2731
- *
2732
- * Should be between 0 and 15 (inclusive).
2733
- */
2734
- subChunkIndex: number;
2735
- }
2736
-
2737
- /**
2738
- * @todo
2739
- */
2740
- export interface StructureSectionData extends NBT.Compound {
2741
- /**
2742
- * The size of the structure, as a tuple of 3 integers.
2743
- */
2744
- size: NBTSchemas.NBTSchemaTypes.StructureTemplate["value"]["size"];
2745
- /**
2746
- * The block indices.
2747
- *
2748
- * These are two arrays of indices in the block palette.
2749
- *
2750
- * The first layer is the block layer.
2751
- *
2752
- * The second layer is the waterlog layer, even though it is mainly used for waterlogging, other blocks can be put here to,
2753
- * which allows for putting two blocks in the same location, or creating ghost blocks (as blocks in this layer cannot be interacted with,
2754
- * however when the corresponding block in the block layer is broken, this block gets moved to the block layer).
2755
- */
2756
- block_indices: {
2757
- type: `${NBT.TagType.List}`;
2758
- value: {
2759
- type: `${NBT.TagType.List}`;
2760
- value: [
2761
- blockLayer: {
2762
- type: `${NBT.TagType.Int}`;
2763
- value: number[];
2764
- },
2765
- waterlogLayer: {
2766
- type: `${NBT.TagType.Int}`;
2767
- value: number[];
2768
- },
2769
- ];
2770
- };
2771
- };
2772
- /**
2773
- * The block palette.
2774
- */
2775
- palette: {
2776
- type: `${NBT.TagType.List}`;
2777
- value: {
2778
- type: `${NBT.TagType.Compound}`;
2779
- value: NBTSchemas.NBTSchemaTypes.Block["value"][];
2780
- };
2781
- };
2782
- }
2783
-
2784
- /**
2785
- * Biome palette data.
2786
- */
2787
- export interface BiomePalette {
2788
- /**
2789
- * The data for the individual blocks, or `null` if this sub-chunk has no biome data.
2790
- *
2791
- * The values of this map to the index of the biome in the palette.
2792
- */
2793
- values: number[] | null;
2794
- /**
2795
- * The palette of biomes as a list of biome numeric IDs.
2796
- */
2797
- palette: number[];
2798
- }
2799
-
2800
- /**
2801
- * The content type of a LevelDB entry.
2802
- */
2803
- export type DBEntryContentType = (typeof DBEntryContentTypes)[number];
2804
-
2805
- /**
2806
- * A content type of a LevelDB chunk key entry.
2807
- */
2808
- export type DBChunkKeyEntryContentType =
2809
- | "Data3D"
2810
- | "Version"
2811
- | "Data2D"
2812
- | "Data2DLegacy"
2813
- | "SubChunkPrefix"
2814
- | "LegacyTerrain"
2815
- | "BlockEntity"
2816
- | "Entity"
2817
- | "PendingTicks"
2818
- | "LegacyBlockExtraData"
2819
- | "BiomeState"
2820
- | "FinalizedState"
2821
- | "ConversionData"
2822
- | "BorderBlocks"
2823
- | "HardcodedSpawners"
2824
- | "RandomTicks"
2825
- | "Checksums"
2826
- | "GenerationSeed"
2827
- | "GeneratedPreCavesAndCliffsBlending"
2828
- | "BlendingBiomeHeight"
2829
- | "MetaDataHash"
2830
- | "BlendingData"
2831
- | "ActorDigestVersion"
2832
- | "LegacyVersion"
2833
- | "AABBVolumes";
2834
-
2835
- /**
2836
- * The a grouping type of LevelDB entry content types.
2837
- */
2838
- export type DBEntryContentTypeGroup = (typeof DBEntryContentTypesGrouping)[DBEntryContentType];
2839
-
2840
- //#endregion
2841
-
2842
- // --------------------------------------------------------------------------------
2843
- // Functions
2844
- // --------------------------------------------------------------------------------
2845
-
2846
- //#region Functions
2847
-
2848
- /**
2849
- * Parses an integer from a buffer gives the number of bytes, endianness, signedness and offset.
2850
- *
2851
- * @param buffer The buffer to read from.
2852
- * @param bytes The number of bytes to read.
2853
- * @param format The endianness of the data.
2854
- * @param signed The signedness of the data. Defaults to `false`.
2855
- * @param offset The offset to read from. Defaults to `0`.
2856
- * @returns The parsed integer.
2857
- *
2858
- * @throws {RangeError} If the byte length is less than 1.
2859
- * @throws {RangeError} If the buffer does not contain enough data at the specified offset.
2860
- */
2861
- export function parseSpecificIntType(buffer: Buffer, bytes: number, format: "BE" | "LE", signed: boolean = false, offset: number = 0): bigint {
2862
- if (bytes < 1) {
2863
- throw new RangeError("Byte length must be at least 1");
2864
- }
2865
- if (offset + bytes > buffer.length) {
2866
- throw new RangeError("Buffer does not contain enough data at the specified offset");
2867
- }
2868
-
2869
- let result: bigint = 0n;
2870
-
2871
- if (format === "BE") {
2872
- for (let i: number = 0; i < bytes; i++) {
2873
- result = (result << 8n) | BigInt(buffer[offset + i]!);
2874
- }
2875
- } else {
2876
- for (let i: number = bytes - 1; i >= 0; i--) {
2877
- result = (result << 8n) | BigInt(buffer[offset + i]!);
2878
- }
2879
- }
2880
-
2881
- if (signed) {
2882
- const signBit: bigint = 1n << BigInt(bytes * 8 - 1);
2883
- if (result & signBit) {
2884
- result -= 1n << BigInt(bytes * 8);
2885
- }
2886
- }
2887
-
2888
- return result;
2889
- }
2890
-
2891
- /**
2892
- * Options for {@link writeSpecificIntType}.
2893
- */
2894
- export interface WriteSpecificIntTypeOptions {
2895
- /**
2896
- * Whether to wrap the value if it is out of range.
2897
- *
2898
- * If `false`, an error will be thrown if the value is out of range.
2899
- *
2900
- * @default false
2901
- */
2902
- wrap?: boolean;
2903
- }
2904
-
2905
- /**
2906
- * Writes an integer to a buffer.
2907
- *
2908
- * @template TArrayBuffer The type of the buffer.
2909
- * @param buffer The buffer to write to.
2910
- * @param value The integer to write.
2911
- * @param bytes The number of bytes to write.
2912
- * @param format The endianness of the data.
2913
- * @param signed The signedness of the data. Defaults to `false`.
2914
- * @param offset The offset to write to. Defaults to `0`.
2915
- * @param options The options to use.
2916
- * @returns The buffer from the {@link buffer} parameter.
2917
- *
2918
- * @throws {RangeError} If the byte length is less than 1.
2919
- * @throws {RangeError} If the buffer does not have enough space at the specified offset.
2920
- * @throws {RangeError} If the value is out of range and {@link WriteSpecificIntTypeOptions.wrap | options.wrap} is `false`.
2921
- */
2922
- export function writeSpecificIntType<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>(
2923
- buffer: Buffer<TArrayBuffer>,
2924
- value: bigint,
2925
- bytes: number,
2926
- format: "BE" | "LE",
2927
- signed: boolean = false,
2928
- offset: number = 0,
2929
- options?: WriteSpecificIntTypeOptions
2930
- ): Buffer<TArrayBuffer> {
2931
- if (bytes < 1) {
2932
- throw new RangeError("Byte length must be at least 1");
2933
- }
2934
- if (offset + bytes > buffer.length) {
2935
- throw new RangeError("Buffer does not have enough space at the specified offset");
2936
- }
2937
-
2938
- const bitSize: bigint = BigInt(bytes * 8);
2939
- const maxUnsigned: bigint = (1n << bitSize) - 1n;
2940
- const minSigned: bigint = -(1n << (bitSize - 1n));
2941
- const maxSigned: bigint = (1n << (bitSize - 1n)) - 1n;
2942
-
2943
- if (signed) {
2944
- if (value < minSigned || value > maxSigned) {
2945
- if (options?.wrap) {
2946
- value = (value + (1n << bitSize)) % (1n << bitSize);
2947
- } else {
2948
- throw new RangeError(`Signed value out of range for ${bytes} bytes`);
2949
- }
2950
- }
2951
- if (value < 0n) {
2952
- value += 1n << bitSize;
2953
- }
2954
- } else {
2955
- if (value < 0n || value > maxUnsigned) {
2956
- if (options?.wrap) {
2957
- value = value % (1n << bitSize);
2958
- } else {
2959
- throw new RangeError(`Unsigned value out of range for ${bytes} bytes`);
2960
- }
2961
- }
2962
- }
2963
-
2964
- for (let i: number = 0; i < bytes; i++) {
2965
- const shift: bigint = format === "BE" ? BigInt((bytes - 1 - i) * 8) : BigInt(i * 8);
2966
- buffer[offset + i] = Number((value >> shift) & 0xffn);
2967
- }
2968
-
2969
- return buffer;
2970
- }
2971
-
2972
- /**
2973
- * Sanitizes a filename.
2974
- *
2975
- * @param filename The filename to sanitize.
2976
- * @returns The sanitized filename.
2977
- */
2978
- export function sanitizeFilename(filename: string): string {
2979
- return filename
2980
- .replaceAll(fileNameCharacterFilterRegExp, /* (substring: string): string => encodeURIComponent(substring) */ "")
2981
- .replaceAll(fileNameEncodeCharacterRegExp, (substring: string): string => encodeURIComponent(substring));
2982
- }
2983
-
2984
- /**
2985
- * Sanitizes a display key.
2986
- *
2987
- * @param key The key to sanitize.
2988
- * @returns The sanitized key.
2989
- */
2990
- export function sanitizeDisplayKey(key: string): string {
2991
- return key.replaceAll(/[^a-zA-Z0-9-_+,-.;=@~/:>?\\]/g, (substring: string): string => encodeURIComponent(substring) /* "" */);
2992
- }
2993
-
2994
- /**
2995
- * Converts a chunk block index to an offset from the minimum corner of the chunk.
2996
- *
2997
- * @param index The chunk block index.
2998
- * @returns The offset from the minimum corner of the chunk.
2999
- */
3000
- export function chunkBlockIndexToOffset(index: number): Vector3 {
3001
- return {
3002
- x: (index >> 8) & 0xf,
3003
- y: (index >> 0) & 0xf,
3004
- z: (index >> 4) & 0xf,
3005
- };
3006
- }
3007
-
3008
- /**
3009
- * Converts an offset from the minimum corner of the chunk to a chunk block index.
3010
- *
3011
- * @param offset The offset from the minimum corner of the chunk.
3012
- * @returns The chunk block index.
3013
- */
3014
- export function offsetToChunkBlockIndex(offset: Vector3): number {
3015
- return ((offset.x & 0xf) << 8) | (offset.y & 0xf) | ((offset.z & 0xf) << 4);
3016
- }
3017
-
3018
- /**
3019
- * Reads a 32-bit integer value from a Buffer at the given offset (little-endian).
3020
- *
3021
- * @param data The Buffer to read from.
3022
- * @param offset The offset to read from.
3023
- * @returns The 32-bit integer value.
3024
- */
3025
- export function getInt32Val(data: Buffer, offset: number): number {
3026
- let retval: number = 0;
3027
- // need to switch this to union based like the others.
3028
- for (let i: number = 0; i < 4; i++) {
3029
- // if I don't do the static cast, the top bit will be sign extended.
3030
- retval |= data[offset + i]! << (i * 8);
3031
- }
3032
- return retval;
3033
- }
3034
-
3035
- /**
3036
- * Writes a 32-bit integer value into a Buffer at the given offset (little-endian).
3037
- *
3038
- * @param buffer The Buffer to write into.
3039
- * @param offset The offset to write into.
3040
- * @param value The 32-bit integer value to write.
3041
- */
3042
- export function setInt32Val(buffer: Buffer, offset: number, value: number): void {
3043
- for (let i: number = 0; i < 4; i++) {
3044
- buffer[offset + i] = (value >> (i * 8)) & 0xff;
3045
- }
3046
- }
3047
-
3048
- /**
3049
- * Splits a range into smaller ranges of a given size.
3050
- *
3051
- * @param param0 The range to split.
3052
- * @param size The size of each range.
3053
- * @returns The split ranges.
3054
- */
3055
- export function splitRange([min, max]: [min: number, max: number], size: number): [from: number, to: number][] {
3056
- const result: [from: number, to: number][] = [];
3057
- let start: number = min;
3058
-
3059
- while (start <= max) {
3060
- const end: number = Math.min(start + size - 1, max);
3061
- result.push([start, end]);
3062
- start = end + 1;
3063
- }
3064
-
3065
- return result;
3066
- }
3067
-
3068
- /**
3069
- * Packs block indices into the buffer using the same scheme as the read loop.
3070
- *
3071
- * @param buffer The buffer to write into.
3072
- * @param blockDataOffset The offset where block data begins in the buffer.
3073
- * @param block_indices The list of block indices to pack.
3074
- * @param bitsPerBlock The number of bits used per block.
3075
- * @param blocksPerWord How many blocks fit inside one 32-bit integer.
3076
- */
3077
- export function writeBlockIndices(buffer: Buffer, blockDataOffset: number, block_indices: number[], bitsPerBlock: number, blocksPerWord: number): void {
3078
- const wordCount: number = Math.ceil(block_indices.length / blocksPerWord);
3079
-
3080
- for (let wordIndex: number = 0; wordIndex < wordCount; wordIndex++) {
3081
- let maskVal: number = 0;
3082
-
3083
- for (let j: number = 0; j < blocksPerWord; j++) {
3084
- const blockIndex: number = wordIndex * blocksPerWord + j;
3085
- if (blockIndex >= block_indices.length) break;
3086
-
3087
- const blockVal: number = block_indices[blockIndex]!;
3088
- const shiftAmount: number = j * bitsPerBlock;
3089
- maskVal |= blockVal /* & ((1 << bitsPerBlock) - 1) */ << shiftAmount;
3090
- }
3091
-
3092
- setInt32Val(buffer, blockDataOffset + wordIndex * 4, maskVal);
3093
- }
3094
- }
3095
-
3096
- /**
3097
- * Gets the chunk indices from a LevelDB key.
3098
- *
3099
- * The key must be a [chunk key](https://minecraft.wiki/w/Bedrock_Edition_level_format#Chunk_key_format).
3100
- *
3101
- * @param key The key to get the chunk indices for, as a Buffer.
3102
- * @returns The chunk indices.
3103
- */
3104
- export function getChunkKeyIndices(key: Buffer): SubChunkIndexDimensionVectorXZ | DimensionVectorXZ {
3105
- return {
3106
- x: getInt32Val(key, 0),
3107
- z: getInt32Val(key, 4),
3108
- dimension: [13, 14].includes(key.length) ? (dimensions[getInt32Val(key, 8)] ?? "overworld") : "overworld",
3109
- ...([10, 14].includes(key.length) ? { subChunkIndex: (key.at(-1)! << 24) >> 24 } : undefined),
3110
- };
3111
- }
3112
-
3113
- /**
3114
- * Generates a raw chunk key from chunk indices.
3115
- *
3116
- * @param indices The chunk indices.
3117
- * @param chunkKeyType The chunk key type.
3118
- * @returns The raw chunk key.
3119
- */
3120
- export function generateChunkKeyFromIndices(
3121
- indices: SubChunkIndexDimensionVectorXZ | DimensionVectorXZ,
3122
- chunkKeyType: DBChunkKeyEntryContentType
3123
- ): Buffer<ArrayBuffer> {
3124
- const buffer: Buffer<ArrayBuffer> = Buffer.alloc(
3125
- (indices.dimension === "overworld" ? 9 : 13) + +("subChunkIndex" in indices && indices.subChunkIndex !== undefined)
3126
- );
3127
- setInt32Val(buffer, 0, indices.x);
3128
- setInt32Val(buffer, 4, indices.z);
3129
- if (indices.dimension !== "overworld") setInt32Val(buffer, 8, dimensions.indexOf(indices.dimension) ?? 0);
3130
- buffer[8 + +(indices.dimension !== "overworld") * 4] = getIntFromChunkKeyType(chunkKeyType);
3131
- if ("subChunkIndex" in indices && indices.subChunkIndex !== undefined) buffer[9 + +(indices.dimension !== "overworld") * 4] = indices.subChunkIndex;
3132
- return buffer;
3133
- }
3134
-
3135
- /**
3136
- * Converts a chunk key type to the integer value that represents it.
3137
- *
3138
- * @param chunkKeyType The chunk key type.
3139
- * @returns The integer value.
3140
- */
3141
- function getIntFromChunkKeyType(chunkKeyType: DBChunkKeyEntryContentType): number {
3142
- switch (chunkKeyType) {
3143
- case "Data3D":
3144
- return 0x2b;
3145
- case "Version":
3146
- return 0x2c;
3147
- case "Data2D":
3148
- return 0x2d;
3149
- case "Data2DLegacy":
3150
- return 0x2e;
3151
- case "SubChunkPrefix":
3152
- return 0x2f;
3153
- case "LegacyTerrain":
3154
- return 0x30;
3155
- case "BlockEntity":
3156
- return 0x31;
3157
- case "Entity":
3158
- return 0x32;
3159
- case "PendingTicks":
3160
- return 0x33;
3161
- case "LegacyBlockExtraData":
3162
- return 0x34;
3163
- case "BiomeState":
3164
- return 0x35;
3165
- case "FinalizedState":
3166
- return 0x36;
3167
- case "ConversionData":
3168
- return 0x37;
3169
- case "BorderBlocks":
3170
- return 0x38;
3171
- case "HardcodedSpawners":
3172
- return 0x39;
3173
- case "RandomTicks":
3174
- return 0x3a;
3175
- case "Checksums":
3176
- return 0x3b;
3177
- case "GenerationSeed":
3178
- return 0x3c;
3179
- case "GeneratedPreCavesAndCliffsBlending":
3180
- return 0x3d;
3181
- case "BlendingBiomeHeight":
3182
- return 0x3e;
3183
- case "MetaDataHash":
3184
- return 0x3f;
3185
- case "BlendingData":
3186
- return 0x40;
3187
- case "ActorDigestVersion":
3188
- return 0x41;
3189
- case "LegacyVersion":
3190
- return 0x76;
3191
- case "AABBVolumes":
3192
- return 0x77;
3193
- }
3194
- }
3195
-
3196
- /**
3197
- * Gets a human-readable version of a LevelDB key.
3198
- *
3199
- * @param key The key to get the display name for, as a Buffer.
3200
- * @returns A human-readable version of the key.
3201
- */
3202
- export function getKeyDisplayName(key: Buffer): string {
3203
- const contentType: DBEntryContentType = getContentTypeFromDBKey(key);
3204
- switch (contentType) {
3205
- case "Data3D":
3206
- case "Version":
3207
- case "Data2D":
3208
- case "Data2DLegacy":
3209
- case "SubChunkPrefix":
3210
- case "LegacyTerrain":
3211
- case "BlockEntity":
3212
- case "Entity":
3213
- case "PendingTicks":
3214
- case "LegacyBlockExtraData":
3215
- case "BiomeState":
3216
- case "FinalizedState":
3217
- case "ConversionData":
3218
- case "BorderBlocks":
3219
- case "HardcodedSpawners":
3220
- case "RandomTicks":
3221
- case "Checksums":
3222
- case "GenerationSeed":
3223
- case "GeneratedPreCavesAndCliffsBlending":
3224
- case "BlendingBiomeHeight":
3225
- case "MetaDataHash":
3226
- case "BlendingData":
3227
- case "ActorDigestVersion":
3228
- case "LegacyVersion":
3229
- case "AABBVolumes": {
3230
- const indices: SubChunkIndexDimensionVectorXZ | DimensionVectorXZ = getChunkKeyIndices(key);
3231
- return `${indices.dimension}_${indices.x}_${indices.z}${
3232
- "subChunkIndex" in indices && indices.subChunkIndex !== undefined ? `_${indices.subChunkIndex}` : ""
3233
- }_${contentType}`;
3234
- }
3235
- case "Digest": {
3236
- const indices: DimensionVectorXZ = getChunkKeyIndices(key.subarray(4));
3237
- return `digp_${indices.dimension}_${indices.x}_${indices.z}`;
3238
- }
3239
- case "ActorPrefix": {
3240
- return `actorprefix_${getInt32Val(key, key.length - 8)}_${getInt32Val(key, key.length - 4)}`;
3241
- }
3242
- case "AutonomousEntities":
3243
- case "BiomeData":
3244
- case "BiomeIdsTable":
3245
- case "ChunkLoadedRequest":
3246
- case "Overworld":
3247
- case "Nether":
3248
- case "TheEnd":
3249
- case "DynamicProperties":
3250
- case "FlatWorldLayers":
3251
- case "ForcedWorldCorruption":
3252
- case "LegacyOverworld":
3253
- case "LegacyNether":
3254
- case "LegacyTheEnd":
3255
- case "LevelChunkMetaDataDictionary":
3256
- case "LevelDat":
3257
- case "LevelSpawnWasFixed":
3258
- case "PositionTrackingDB":
3259
- case "PositionTrackingLastId":
3260
- case "Map":
3261
- case "MobEvents":
3262
- case "MVillages":
3263
- case "Player":
3264
- case "PlayerClient":
3265
- case "Portals":
3266
- case "RealmsStoriesData":
3267
- case "SchedulerWT":
3268
- case "Scoreboard":
3269
- case "StructureTemplate":
3270
- case "TickingArea":
3271
- case "VillageDwellers":
3272
- case "VillageInfo":
3273
- case "VillagePOI":
3274
- case "VillagePlayers":
3275
- case "VillageRaid":
3276
- case "Villages":
3277
- case "Unknown":
3278
- default:
3279
- return key.toString("binary");
3280
- }
3281
- }
3282
-
3283
- /**
3284
- * Gets the content type of a LevelDB key.
3285
- *
3286
- * @param key The key to get the content type for, as a Buffer.
3287
- * @returns The content type of the key.
3288
- */
3289
- export function getContentTypeFromDBKey(key: Buffer): DBEntryContentType {
3290
- if ([9, 10, 13, 14].includes(key.length)) {
3291
- switch (key.at([10, 14].includes(key.length) ? -2 : -1)) {
3292
- case 0x2b:
3293
- return "Data3D";
3294
- case 0x2c:
3295
- return "Version";
3296
- case 0x2d:
3297
- return "Data2D";
3298
- case 0x2e:
3299
- return "Data2DLegacy";
3300
- case 0x2f:
3301
- return "SubChunkPrefix";
3302
- case 0x30:
3303
- return "LegacyTerrain";
3304
- case 0x31:
3305
- return "BlockEntity";
3306
- case 0x32:
3307
- return "Entity";
3308
- case 0x33:
3309
- return "PendingTicks";
3310
- case 0x34:
3311
- return "LegacyBlockExtraData";
3312
- case 0x35:
3313
- return "BiomeState";
3314
- case 0x36:
3315
- return "FinalizedState";
3316
- case 0x37:
3317
- return "ConversionData";
3318
- case 0x38:
3319
- return "BorderBlocks";
3320
- case 0x39:
3321
- return "HardcodedSpawners";
3322
- case 0x3a:
3323
- return "RandomTicks";
3324
- case 0x3b:
3325
- return "Checksums";
3326
- case 0x3c:
3327
- return "GenerationSeed";
3328
- case 0x3d:
3329
- return "GeneratedPreCavesAndCliffsBlending";
3330
- case 0x3e:
3331
- return "BlendingBiomeHeight";
3332
- case 0x3f:
3333
- return "MetaDataHash";
3334
- case 0x40:
3335
- return "BlendingData";
3336
- case 0x41:
3337
- return "ActorDigestVersion";
3338
- case 0x76:
3339
- return "LegacyVersion";
3340
- case 0x77:
3341
- return "AABBVolumes";
3342
- }
3343
- }
3344
- const stringKey: string = key.toString();
3345
- switch (stringKey) {
3346
- case "~local_player":
3347
- return "Player";
3348
- case "game_flatworldlayers":
3349
- return "FlatWorldLayers";
3350
- case "Overworld":
3351
- return "Overworld";
3352
- case "Nether":
3353
- return "Nether";
3354
- case "TheEnd":
3355
- return "TheEnd";
3356
- case "mobevents":
3357
- return "MobEvents";
3358
- case "BiomeData":
3359
- return "BiomeData";
3360
- case "BiomeIdsTable":
3361
- return "BiomeIdsTable";
3362
- case "AutonomousEntities":
3363
- return "AutonomousEntities";
3364
- case "PositionTrackDB-LastId":
3365
- return "PositionTrackingLastId";
3366
- case "scoreboard":
3367
- return "Scoreboard";
3368
- case "schedulerWT":
3369
- return "SchedulerWT";
3370
- case "portals":
3371
- return "Portals";
3372
- case "DynamicProperties":
3373
- return "DynamicProperties";
3374
- case "LevelChunkMetaDataDictionary":
3375
- return "LevelChunkMetaDataDictionary";
3376
- case "SST_SALOG":
3377
- case "SST_WORD":
3378
- case "SST_WORD_":
3379
- case "DedicatedServerForcedCorruption":
3380
- return "ForcedWorldCorruption";
3381
- case "dimension0":
3382
- return "LegacyOverworld";
3383
- case "dimension1":
3384
- return "LegacyNether";
3385
- case "dimension2":
3386
- return "LegacyTheEnd";
3387
- case "mVillages":
3388
- return "MVillages";
3389
- case "villages":
3390
- return "Villages";
3391
- case "LevelSpawnWasFixed":
3392
- return "LevelSpawnWasFixed";
3393
- }
3394
- switch (true) {
3395
- case stringKey.startsWith("PosTrackDB-0x"):
3396
- return "PositionTrackingDB";
3397
- case stringKey.startsWith("actorprefix"):
3398
- return "ActorPrefix";
3399
- case stringKey.startsWith("structuretemplate"):
3400
- return "StructureTemplate";
3401
- case stringKey.startsWith("tickingarea"):
3402
- return "TickingArea";
3403
- case stringKey.startsWith("portals"):
3404
- return "Portals";
3405
- case stringKey.startsWith("map_"):
3406
- return "Map";
3407
- case stringKey.startsWith("player_server_"):
3408
- return "Player";
3409
- case stringKey.startsWith("player_"):
3410
- return "PlayerClient";
3411
- case stringKey.startsWith("digp"):
3412
- return "Digest";
3413
- case /^chunk_loaded_request_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_\d+$/.test(stringKey):
3414
- return "ChunkLoadedRequest";
3415
- case stringKey.startsWith("RealmsStoriesData_"):
3416
- return "RealmsStoriesData";
3417
- case /^VILLAGE_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_[0-9a-f\\-]+_POI$/.test(stringKey):
3418
- return "VillagePOI";
3419
- case /^VILLAGE_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_[0-9a-f\\-]+_INFO$/.test(stringKey):
3420
- return "VillageInfo";
3421
- case /^VILLAGE_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_[0-9a-f\\-]+_DWELLERS$/.test(stringKey):
3422
- return "VillageDwellers";
3423
- case /^VILLAGE_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_[0-9a-f\\-]+_PLAYERS$/.test(stringKey):
3424
- return "VillagePlayers";
3425
- case /^VILLAGE_(?:[Oo][Vv][Ee][Rr][Ww][Oo][Rr][Ll][Dd]|[Tt][Hh][Ee]_[Ee][Nn][Dd]|[Nn][Ee][Tt][Hh][Ee][Rr])_[0-9a-f\\-]+_RAID$/.test(stringKey):
3426
- return "VillageRaid";
3427
- }
3428
- return "Unknown";
3429
- }
3430
-
3431
- /**
3432
- * Gets the biome type from its ID.
3433
- *
3434
- * Only works with vanilla biomes.
3435
- *
3436
- * @param id The ID of the biome.
3437
- * @returns The biome type.
3438
- */
3439
- export function getBiomeTypeFromID(id: number): keyof (typeof BiomeData)["int_map"] | undefined {
3440
- return (Object.keys(BiomeData.int_map) as (keyof (typeof BiomeData)["int_map"])[]).find(
3441
- (key: keyof (typeof BiomeData)["int_map"]): boolean => BiomeData.int_map[key] === id
3442
- );
3443
- }
3444
-
3445
- /**
3446
- * Gets the biome ID from its type.
3447
- *
3448
- * Only works with vanilla biomes.
3449
- *
3450
- * @param type The type of the biome.
3451
- * @returns The biome ID.
3452
- */
3453
- export function getBiomeIDFromType(type: keyof (typeof BiomeData)["int_map"]): number | undefined {
3454
- return BiomeData.int_map[type];
3455
- }
3456
-
3457
- //#endregion
3458
-
3459
- // --------------------------------------------------------------------------------
3460
- // Classes
3461
- // --------------------------------------------------------------------------------
3462
-
3463
- //#region Classes
3464
-
3465
- /**
3466
- * @todo
3467
- */
3468
- export class Structure {
3469
- /**
3470
- * @todo
3471
- */
3472
- public target?:
3473
- | {
3474
- /**
3475
- * The type of the target structure data.
3476
- */
3477
- type: "LevelDBEntry";
3478
- /**
3479
- * The LevelDB containing the target structure data.
3480
- */
3481
- db: LevelDB;
3482
- /**
3483
- * The key of the target structure data in the LevelDB.
3484
- */
3485
- key: Buffer;
3486
- }
3487
- | {
3488
- /**
3489
- * The type of the target structure data.
3490
- */
3491
- type: "File";
3492
- /**
3493
- * The absolute path to the target structure data.
3494
- */
3495
- path: string;
3496
- }
3497
- | undefined;
3498
- /**
3499
- * @todo
3500
- */
3501
- public constructor(options: { target?: Structure["target"] }) {
3502
- this.target = options.target;
3503
- }
3504
- /**
3505
- * @todo
3506
- */
3507
- public fillBlocks(from: Vector3, to: Vector3, block: NBTSchemas.NBTSchemaTypes.Block): any {
3508
- throw new Error("Method not implemented.");
3509
- }
3510
- /**
3511
- * @todo
3512
- */
3513
- public saveChanges(): any {
3514
- throw new Error("Method not implemented.");
3515
- }
3516
- /**
3517
- * @todo
3518
- */
3519
- public delete(): any {
3520
- throw new Error("Method not implemented.");
3521
- }
3522
- /**
3523
- * @todo
3524
- */
3525
- public expand(min: Vector3, max: Vector3): any {
3526
- throw new Error("Method not implemented.");
3527
- }
3528
- /**
3529
- * @todo
3530
- */
3531
- public shrink(min: Vector3, max: Vector3): any {
3532
- throw new Error("Method not implemented.");
3533
- }
3534
- /**
3535
- * @todo
3536
- */
3537
- public move(min: Vector3, max: Vector3): any {
3538
- throw new Error("Method not implemented.");
3539
- }
3540
- /**
3541
- * @todo
3542
- */
3543
- public scale(scale: Vector3 | number): any {
3544
- throw new Error("Method not implemented.");
3545
- }
3546
- /**
3547
- * @todo
3548
- */
3549
- public rotate(angle: 0 | 90 | 180 | 270, axis: "x" | "y" | "z" = "y"): any {
3550
- throw new Error("Method not implemented.");
3551
- }
3552
- /**
3553
- * @todo
3554
- */
3555
- public mirror(axis: "x" | "y" | "z"): any {
3556
- throw new Error("Method not implemented.");
3557
- }
3558
- /**
3559
- * @todo
3560
- */
3561
- public clear(): any {
3562
- throw new Error("Method not implemented.");
3563
- }
3564
- /**
3565
- * @todo
3566
- */
3567
- public clearSectionData(from: Vector3, to: Vector3): any {
3568
- throw new Error("Method not implemented.");
3569
- }
3570
- /**
3571
- * @todo
3572
- */
3573
- public getSectionData(from: Vector3, to: Vector3): StructureSectionData {
3574
- throw new Error("Method not implemented.");
3575
- }
3576
- /**
3577
- * @todo
3578
- */
3579
- public replaceSectionData(offset: Vector3, data: StructureSectionData, options: StructureReplaceSectionDataOptions = {}): any {
3580
- throw new Error("Method not implemented.");
3581
- }
3582
- /**
3583
- * @todo
3584
- */
3585
- public exportPrismarineNBT(): NBTSchemas.NBTSchemaTypes.StructureTemplate {
3586
- throw new Error("Method not implemented.");
3587
- }
3588
- }
3589
-
3590
- /**
3591
- * Options for {@link Structure.replaceSectionData}.
3592
- */
3593
- export interface StructureReplaceSectionDataOptions {
3594
- /**
3595
- * Whether to automatically expand the structure to fit the data if necessary, instead of cropping it.
3596
- *
3597
- * @default false
3598
- */
3599
- autoExpand?: boolean;
3600
- }
3601
-
3602
- //#endregion