mcbe-leveldb 1.5.7 → 1.6.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/Changelog.md +21 -1
- package/LevelUtils.d.ts +11 -1
- package/LevelUtils.js +16 -0
- package/LevelUtils.js.map +1 -1
- package/nbtSchemas.d.ts +869 -282
- package/nbtSchemas.js +266 -98
- package/nbtSchemas.js.map +1 -1
- package/package.json +4 -7
- package/DBUtils.ts +0 -178
- package/LevelUtils.ts +0 -2631
- package/SNBTUtils.ts +0 -2132
- package/__biome_data__.ts +0 -282
- package/index.ts +0 -14
- package/nbtSchemas.ts +0 -18254
- package/utils/JSONB.ts +0 -566
package/SNBTUtils.ts
DELETED
|
@@ -1,2132 +0,0 @@
|
|
|
1
|
-
import NBT, { type Compound } from "prismarine-nbt";
|
|
2
|
-
import type { OmitNeverValueKeys, VerifyConstraint } from "./types.js";
|
|
3
|
-
|
|
4
|
-
function toPrimitive(tag: any): number {
|
|
5
|
-
switch (tag.type) {
|
|
6
|
-
case "byte":
|
|
7
|
-
case "short":
|
|
8
|
-
case "int":
|
|
9
|
-
case "float":
|
|
10
|
-
case "double":
|
|
11
|
-
return tag.value;
|
|
12
|
-
case "long":
|
|
13
|
-
return Number(toLong(tag.value));
|
|
14
|
-
default:
|
|
15
|
-
throw new SyntaxError("Cannot convert to primitive: " + tag.type);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function uuidToIntArray(uuidStr: string): number[] {
|
|
20
|
-
const hex: string = uuidStr.replace(/-/g, "");
|
|
21
|
-
if (hex.length !== 32)
|
|
22
|
-
throw new SyntaxError("Invalid UUID: " + uuidStr, {
|
|
23
|
-
cause: {
|
|
24
|
-
position: 0,
|
|
25
|
-
stack: [
|
|
26
|
-
{
|
|
27
|
-
input: uuidStr,
|
|
28
|
-
positionInInput: 0,
|
|
29
|
-
function: "uuidToIntArray",
|
|
30
|
-
error: { type: "InvalidUUID", uuid: uuidStr },
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
} as const satisfies SNBTParseErrorCause,
|
|
34
|
-
});
|
|
35
|
-
const arr: number[] = [];
|
|
36
|
-
for (let i: number = 0; i < 16; i += 4) {
|
|
37
|
-
arr.push((parseInt(hex.slice(i, i + 4), 16) << 16) >> 16); // 32-bit signed chunks
|
|
38
|
-
}
|
|
39
|
-
return arr;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The type of an SNBT parse error.
|
|
44
|
-
*/
|
|
45
|
-
export type SNBTParseErrorType =
|
|
46
|
-
| NonNullable<
|
|
47
|
-
Extract<
|
|
48
|
-
SNBTParseErrorCauseStackItem,
|
|
49
|
-
{
|
|
50
|
-
/**
|
|
51
|
-
* @ignore
|
|
52
|
-
*/
|
|
53
|
-
error?: any;
|
|
54
|
-
}
|
|
55
|
-
>["error"]
|
|
56
|
-
>["type"]
|
|
57
|
-
| "ExpectedEndOfInput"
|
|
58
|
-
| "ExpectedCompoundOrList";
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Maps SNBT parse error types to error codes.
|
|
62
|
-
*/
|
|
63
|
-
export const SNBTParseErrorTypeToCode = {
|
|
64
|
-
/**
|
|
65
|
-
* An error that occurred because a disallowed type was found in a typed array.
|
|
66
|
-
*/
|
|
67
|
-
DisallowedTypeInTypedArray: "disallowed-type-in-typed-array",
|
|
68
|
-
/**
|
|
69
|
-
* An error that occurred because an expected compound or list was not found.
|
|
70
|
-
*/
|
|
71
|
-
ExpectedCompoundOrList: "expected-compound-or-list",
|
|
72
|
-
/**
|
|
73
|
-
* An error that occurred because an argument to a function was invalid.
|
|
74
|
-
*/
|
|
75
|
-
InvalidArgumentToFunction: "invalid-argument-to-function",
|
|
76
|
-
/**
|
|
77
|
-
* An error that occurred because an SNBT key was invalid.
|
|
78
|
-
*/
|
|
79
|
-
InvalidSNBTKey: "invalid-snbt-key",
|
|
80
|
-
/**
|
|
81
|
-
* An error that occurred because an SNBT string was invalid.
|
|
82
|
-
*/
|
|
83
|
-
InvalidSNBTString: "invalid-snbt-string",
|
|
84
|
-
/**
|
|
85
|
-
* An error that occurred because a UUID was invalid.
|
|
86
|
-
*/
|
|
87
|
-
InvalidUUID: "invalid-uuid",
|
|
88
|
-
/**
|
|
89
|
-
* An error that occurred because an unsupported function was called.
|
|
90
|
-
*/
|
|
91
|
-
UnsupportedFunction: "unsupported-function",
|
|
92
|
-
/**
|
|
93
|
-
* An error that occurred because an unsupported SNBT primitive was found.
|
|
94
|
-
*/
|
|
95
|
-
UnsupportedSNBTPrimitive: "unsupported-snbt-primitive",
|
|
96
|
-
/**
|
|
97
|
-
* An error that occurred because a list contained multiple types.
|
|
98
|
-
*/
|
|
99
|
-
MixedListTypesNotAllowed: "mixed-list-types-not-allowed",
|
|
100
|
-
/**
|
|
101
|
-
* An error that occurred because an unsupported type was found in a typed array.
|
|
102
|
-
*/
|
|
103
|
-
UnsupportedTypeInTypedArray: "unsupported-type-in-typed-array",
|
|
104
|
-
/**
|
|
105
|
-
* An error that occurred because the end of the input was expected.
|
|
106
|
-
*/
|
|
107
|
-
ExpectedEndOfInput: "expected-end-of-input",
|
|
108
|
-
} as const satisfies Record<SNBTParseErrorType, string>;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* The namespace of SNBT parse errors.
|
|
112
|
-
*/
|
|
113
|
-
export const SNBTParseErrorDisplayNamespace = "mcbe-leveldb";
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* A stack item in an SNBT parse error.
|
|
117
|
-
*/
|
|
118
|
-
export type SNBTParseErrorCauseStackItem =
|
|
119
|
-
| {
|
|
120
|
-
/**
|
|
121
|
-
* The position of the error in the input.
|
|
122
|
-
*/
|
|
123
|
-
positionInInput: number;
|
|
124
|
-
/**
|
|
125
|
-
* The input.
|
|
126
|
-
*/
|
|
127
|
-
input: any;
|
|
128
|
-
/**
|
|
129
|
-
* The function associated with this stack item.
|
|
130
|
-
*/
|
|
131
|
-
function: "parseSNBTPrimitive";
|
|
132
|
-
/**
|
|
133
|
-
* The error associated with this stack item.
|
|
134
|
-
*/
|
|
135
|
-
error?:
|
|
136
|
-
| {
|
|
137
|
-
/**
|
|
138
|
-
* The type of this error.
|
|
139
|
-
*/
|
|
140
|
-
type: "InvalidArgumentToFunction";
|
|
141
|
-
/**
|
|
142
|
-
* The name of the function.
|
|
143
|
-
*/
|
|
144
|
-
functionName: "bool" | "uuid";
|
|
145
|
-
/**
|
|
146
|
-
* The argument that caused this error.
|
|
147
|
-
*/
|
|
148
|
-
argument: string;
|
|
149
|
-
}
|
|
150
|
-
| {
|
|
151
|
-
/**
|
|
152
|
-
* The type of this error.
|
|
153
|
-
*/
|
|
154
|
-
type: "UnsupportedFunction";
|
|
155
|
-
/**
|
|
156
|
-
* The name of the unsupported function.
|
|
157
|
-
*/
|
|
158
|
-
functionName: string;
|
|
159
|
-
}
|
|
160
|
-
| {
|
|
161
|
-
/**
|
|
162
|
-
* The type of this error.
|
|
163
|
-
*/
|
|
164
|
-
type: "UnsupportedSNBTPrimitive";
|
|
165
|
-
/**
|
|
166
|
-
* The raw SNBT primitive value that caused this error.
|
|
167
|
-
*/
|
|
168
|
-
raw: any;
|
|
169
|
-
}
|
|
170
|
-
| {
|
|
171
|
-
/**
|
|
172
|
-
* The type of this error.
|
|
173
|
-
*/
|
|
174
|
-
type: "InvalidSNBTString";
|
|
175
|
-
/**
|
|
176
|
-
* The raw SNBT string value that caused this error.
|
|
177
|
-
*/
|
|
178
|
-
raw: any;
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
| {
|
|
182
|
-
/**
|
|
183
|
-
* The position of the error in the input.
|
|
184
|
-
*/
|
|
185
|
-
positionInInput: number;
|
|
186
|
-
/**
|
|
187
|
-
* The input.
|
|
188
|
-
*/
|
|
189
|
-
input: string;
|
|
190
|
-
/**
|
|
191
|
-
* The function associated with this stack item.
|
|
192
|
-
*/
|
|
193
|
-
function: "uuidToIntArray";
|
|
194
|
-
/**
|
|
195
|
-
* The error associated with this stack item.
|
|
196
|
-
*/
|
|
197
|
-
error: {
|
|
198
|
-
/**
|
|
199
|
-
* The type of this error.
|
|
200
|
-
*/
|
|
201
|
-
type: "InvalidUUID";
|
|
202
|
-
/**
|
|
203
|
-
* The invalid UUID that caused this error.
|
|
204
|
-
*/
|
|
205
|
-
uuid: string;
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
| {
|
|
209
|
-
/**
|
|
210
|
-
* The position of the error in the input item.
|
|
211
|
-
*/
|
|
212
|
-
positionInInputItem: number;
|
|
213
|
-
/**
|
|
214
|
-
* The input items (the items in the typed array).
|
|
215
|
-
*/
|
|
216
|
-
inputItems: string[];
|
|
217
|
-
/**
|
|
218
|
-
* The input item associated with this stack item.
|
|
219
|
-
*/
|
|
220
|
-
inputItem: string;
|
|
221
|
-
/**
|
|
222
|
-
* The index of the input item associated with this stack item.
|
|
223
|
-
*/
|
|
224
|
-
inputItemIndex: number;
|
|
225
|
-
/**
|
|
226
|
-
* The type of the typed array associated with this stack item.
|
|
227
|
-
*/
|
|
228
|
-
arrayType: `${TypedArrayLetterTypes.B | TypedArrayLetterTypes.S | TypedArrayLetterTypes.I | TypedArrayLetterTypes.L}`;
|
|
229
|
-
/**
|
|
230
|
-
* The function associated with this stack item.
|
|
231
|
-
*/
|
|
232
|
-
function: "parseTypedArray";
|
|
233
|
-
/**
|
|
234
|
-
* The error associated with this stack item.
|
|
235
|
-
*/
|
|
236
|
-
error?:
|
|
237
|
-
| {
|
|
238
|
-
/**
|
|
239
|
-
* The type of this error.
|
|
240
|
-
*/
|
|
241
|
-
type: "DisallowedTypeInTypedArray";
|
|
242
|
-
/**
|
|
243
|
-
* The type of the item that caused this error.
|
|
244
|
-
*/
|
|
245
|
-
itemType: `${TypedArrayLetterTypes.B | TypedArrayLetterTypes.S | TypedArrayLetterTypes.I | TypedArrayLetterTypes.L}`;
|
|
246
|
-
/**
|
|
247
|
-
* The allowed types.
|
|
248
|
-
*/
|
|
249
|
-
allowedTypes: `${TypedArrayLetterTypes.B | TypedArrayLetterTypes.S | TypedArrayLetterTypes.I | TypedArrayLetterTypes.L}`[];
|
|
250
|
-
}
|
|
251
|
-
| {
|
|
252
|
-
/**
|
|
253
|
-
* The type of this error.
|
|
254
|
-
*/
|
|
255
|
-
type: "UnsupportedTypeInTypedArray";
|
|
256
|
-
/**
|
|
257
|
-
* Should only be undefined if the item is unable to be parsed.
|
|
258
|
-
*/
|
|
259
|
-
itemType?: `${NBT.TagType}` | undefined;
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
| {
|
|
263
|
-
/**
|
|
264
|
-
* The position of the error in the input item.
|
|
265
|
-
*/
|
|
266
|
-
positionInInputItem: number;
|
|
267
|
-
/**
|
|
268
|
-
* The input items (the items in the list).
|
|
269
|
-
*/
|
|
270
|
-
inputItems: string[];
|
|
271
|
-
/**
|
|
272
|
-
* The input item associated with this stack item.
|
|
273
|
-
*/
|
|
274
|
-
inputItem: string;
|
|
275
|
-
/**
|
|
276
|
-
* The index of the input item associated with this stack item.
|
|
277
|
-
*/
|
|
278
|
-
inputItemIndex: number;
|
|
279
|
-
/**
|
|
280
|
-
* The function associated with this stack item.
|
|
281
|
-
*/
|
|
282
|
-
function: "parseList";
|
|
283
|
-
/**
|
|
284
|
-
* The error associated with this stack item.
|
|
285
|
-
*/
|
|
286
|
-
error?: {
|
|
287
|
-
/**
|
|
288
|
-
* The type of this error.
|
|
289
|
-
*/
|
|
290
|
-
type: "MixedListTypesNotAllowed";
|
|
291
|
-
/**
|
|
292
|
-
* The type of the item that caused this error.
|
|
293
|
-
*/
|
|
294
|
-
itemType: `${NBT.TagType}`;
|
|
295
|
-
/**
|
|
296
|
-
* The detected type of the item that caused this error.
|
|
297
|
-
*/
|
|
298
|
-
item: NBT.Tags[NBT.TagType];
|
|
299
|
-
/**
|
|
300
|
-
* The detected type of the list.
|
|
301
|
-
*/
|
|
302
|
-
detectedArrayType: `${NBT.TagType}`;
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
| {
|
|
306
|
-
/**
|
|
307
|
-
* The position of the error in the input.
|
|
308
|
-
*/
|
|
309
|
-
positionInInput: number;
|
|
310
|
-
/**
|
|
311
|
-
* The input.
|
|
312
|
-
*/
|
|
313
|
-
input: string;
|
|
314
|
-
/**
|
|
315
|
-
* The property key associated with this stack item.
|
|
316
|
-
*/
|
|
317
|
-
key?: string;
|
|
318
|
-
/**
|
|
319
|
-
* The function associated with this stack item.
|
|
320
|
-
*/
|
|
321
|
-
function: "extractSNBT" | "parseSNBTCompoundString";
|
|
322
|
-
/**
|
|
323
|
-
* The error associated with this stack item.
|
|
324
|
-
*/
|
|
325
|
-
error?: {
|
|
326
|
-
/**
|
|
327
|
-
* The type of this error.
|
|
328
|
-
*/
|
|
329
|
-
type: "InvalidSNBTKey";
|
|
330
|
-
/**
|
|
331
|
-
* The raw SNBT key value that caused this error.
|
|
332
|
-
*/
|
|
333
|
-
raw: any;
|
|
334
|
-
};
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* The cause of an SNBT parse error.
|
|
339
|
-
*/
|
|
340
|
-
export interface SNBTParseErrorCause {
|
|
341
|
-
/**
|
|
342
|
-
* The position of the error in the input.
|
|
343
|
-
*/
|
|
344
|
-
position: number;
|
|
345
|
-
/**
|
|
346
|
-
* The stack trace of the error.
|
|
347
|
-
*/
|
|
348
|
-
stack: [initialError: SNBTParseErrorCauseStackItem, ...outerStack: SNBTParseErrorCauseStackItem[]];
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Represents an error that occurred while parsing SNBT.
|
|
353
|
-
*
|
|
354
|
-
* @template T Whether the error's full stack has been resolved.
|
|
355
|
-
*/
|
|
356
|
-
export interface SNBTParseError<T extends boolean = boolean> extends Error {
|
|
357
|
-
/**
|
|
358
|
-
* The cause of the error.
|
|
359
|
-
*/
|
|
360
|
-
cause: SNBTParseErrorCause;
|
|
361
|
-
/**
|
|
362
|
-
* Whether the error's full stack has been resolved.
|
|
363
|
-
*/
|
|
364
|
-
isResolved: T;
|
|
365
|
-
/**
|
|
366
|
-
* The original input that caused the error.
|
|
367
|
-
*/
|
|
368
|
-
originalInput: T extends true ? string : null;
|
|
369
|
-
/**
|
|
370
|
-
* Gets the position of the error in the input.
|
|
371
|
-
*
|
|
372
|
-
* @returns The position of the error in the input.
|
|
373
|
-
*/
|
|
374
|
-
getErrorPosition(): T extends true ? [line: number, column: number] : null;
|
|
375
|
-
/**
|
|
376
|
-
* Gets the end position of the error in the input.
|
|
377
|
-
*
|
|
378
|
-
* @returns The end position of the error in the input.
|
|
379
|
-
*/
|
|
380
|
-
getErrorEndPosition(): T extends true ? [line: number, column: number] : null;
|
|
381
|
-
/**
|
|
382
|
-
* Gets the position of the error in the input with an offset.
|
|
383
|
-
*
|
|
384
|
-
* @param offset The offset to add to the error position.
|
|
385
|
-
* @returns The position of the error in the input.
|
|
386
|
-
*/
|
|
387
|
-
getErrorPositionWithOffset(offset: number): T extends true ? [line: number, column: number] : null;
|
|
388
|
-
/**
|
|
389
|
-
* Gets the position of the error in the input for a stack item.
|
|
390
|
-
*
|
|
391
|
-
* @param stackItem The stack item to get the error position for.
|
|
392
|
-
* @returns The position of the error in the input.
|
|
393
|
-
*/
|
|
394
|
-
getErrorPositionForStackItem(stackItem: SNBTParseErrorCauseStackItem): [line: number, column: number];
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* The options for an SNBT parse error.
|
|
399
|
-
*/
|
|
400
|
-
export interface SNBTParseErrorOptions extends ErrorOptions {
|
|
401
|
-
/**
|
|
402
|
-
* The cause of the error.
|
|
403
|
-
*/
|
|
404
|
-
cause: SNBTParseErrorCause;
|
|
405
|
-
/**
|
|
406
|
-
* Whether the error's full stack has been resolved.
|
|
407
|
-
*
|
|
408
|
-
* Only set this to true if this was from a function that was not called by another SNBT parser function, if the SNBT parser has more to the stack, this should be false.
|
|
409
|
-
*
|
|
410
|
-
* @default false
|
|
411
|
-
*/
|
|
412
|
-
resolved?: boolean | undefined;
|
|
413
|
-
/**
|
|
414
|
-
* Only set this if {@link resolved} is also true, this is the original input of the SNBT parser function.
|
|
415
|
-
*/
|
|
416
|
-
originalInput?: string | undefined;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* The constructor for an SNBT parse error.
|
|
421
|
-
*/
|
|
422
|
-
export interface SNBTParseErrorConstructor extends Omit<ErrorConstructor, "prototype"> {
|
|
423
|
-
/**
|
|
424
|
-
* Creates a new SNBTParseError.
|
|
425
|
-
*
|
|
426
|
-
* @param message The message of the error.
|
|
427
|
-
* @param options The options for the error.
|
|
428
|
-
* @returns A new SNBTParseError.
|
|
429
|
-
*/
|
|
430
|
-
new (message: string, options: SNBTParseErrorOptions): SNBTParseError;
|
|
431
|
-
/**
|
|
432
|
-
* Creates a new SNBTParseError.
|
|
433
|
-
*
|
|
434
|
-
* @param message The message of the error.
|
|
435
|
-
* @param options The options for the error.
|
|
436
|
-
* @returns A new SNBTParseError.
|
|
437
|
-
*/
|
|
438
|
-
(message: string, options: SNBTParseErrorOptions): SNBTParseError;
|
|
439
|
-
/**
|
|
440
|
-
* The prototype of an SNBTParseError.
|
|
441
|
-
*/
|
|
442
|
-
prototype: SNBTParseError;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* An SNBT parse error.
|
|
447
|
-
*/
|
|
448
|
-
export var SNBTParseError: SNBTParseErrorConstructor = new Proxy(
|
|
449
|
-
/**
|
|
450
|
-
* An SNBT parse error.
|
|
451
|
-
*/
|
|
452
|
-
class SNBTParseError extends Error implements SNBTParseError {
|
|
453
|
-
public declare cause: SNBTParseErrorCause;
|
|
454
|
-
public isResolved: boolean = false;
|
|
455
|
-
public originalInput: string | null = null;
|
|
456
|
-
public constructor(message: string, options: SNBTParseErrorOptions) {
|
|
457
|
-
if (arguments.length !== 2) throw new TypeError(`Incorrect number of arguments to constructor, expected 2 but got ${arguments.length} instead.`);
|
|
458
|
-
if (typeof message !== "string") throw new TypeError(`Expected args[0] (message) to be a string but got ${typeof message} instead.`);
|
|
459
|
-
if (typeof options !== "object" || options === null)
|
|
460
|
-
throw new TypeError(`Expected args[1] (options) to be an object but got ${options === null ? "null" : typeof options} instead.`);
|
|
461
|
-
if (typeof options.cause !== "object" || options.cause === null)
|
|
462
|
-
throw new TypeError(`Expected options.cause to be an object but got ${options === null ? "null" : typeof options.cause} instead.`);
|
|
463
|
-
super(message, options);
|
|
464
|
-
this.isResolved = options.resolved ?? false;
|
|
465
|
-
this.originalInput = options.originalInput ?? null;
|
|
466
|
-
}
|
|
467
|
-
public getErrorPosition(): [line: number, column: number] | null {
|
|
468
|
-
if (!this.isResolved || this.originalInput === null) return null;
|
|
469
|
-
const cause: SNBTParseErrorCause = this.cause;
|
|
470
|
-
const position: SNBTParseErrorCause["position"] = cause.position;
|
|
471
|
-
const input: string = this.originalInput;
|
|
472
|
-
return [input.slice(0, position).split("\n").length, position - input.slice(0, position).lastIndexOf("\n") + 1];
|
|
473
|
-
}
|
|
474
|
-
public getErrorEndPosition(): [line: number, column: number] | null {
|
|
475
|
-
if (!this.isResolved || this.originalInput === null) return null;
|
|
476
|
-
const cause: SNBTParseErrorCause = this.cause;
|
|
477
|
-
let offset: number = 0;
|
|
478
|
-
const stackItem: SNBTParseErrorCauseStackItem = cause.stack[0];
|
|
479
|
-
stackItemFunctionSwitcher: switch (stackItem.function) {
|
|
480
|
-
case "extractSNBT":
|
|
481
|
-
switch (stackItem.error?.type) {
|
|
482
|
-
case "InvalidSNBTKey":
|
|
483
|
-
offset = stackItem.error.raw.length;
|
|
484
|
-
break stackItemFunctionSwitcher;
|
|
485
|
-
default:
|
|
486
|
-
offset = stackItem.input.length - stackItem.positionInInput;
|
|
487
|
-
break stackItemFunctionSwitcher;
|
|
488
|
-
}
|
|
489
|
-
case "parseList":
|
|
490
|
-
switch (stackItem.error?.type) {
|
|
491
|
-
case "MixedListTypesNotAllowed":
|
|
492
|
-
offset = stackItem.inputItem.length - stackItem.positionInInputItem;
|
|
493
|
-
break stackItemFunctionSwitcher;
|
|
494
|
-
default:
|
|
495
|
-
offset = stackItem.inputItem.length - stackItem.positionInInputItem;
|
|
496
|
-
break stackItemFunctionSwitcher;
|
|
497
|
-
}
|
|
498
|
-
case "parseSNBTCompoundString":
|
|
499
|
-
switch (stackItem.error?.type) {
|
|
500
|
-
case "InvalidSNBTKey":
|
|
501
|
-
offset = stackItem.error.raw.length;
|
|
502
|
-
break stackItemFunctionSwitcher;
|
|
503
|
-
default:
|
|
504
|
-
offset = stackItem.input.length - stackItem.positionInInput;
|
|
505
|
-
break stackItemFunctionSwitcher;
|
|
506
|
-
}
|
|
507
|
-
case "parseSNBTPrimitive":
|
|
508
|
-
switch (stackItem.error?.type) {
|
|
509
|
-
case "InvalidArgumentToFunction":
|
|
510
|
-
offset = stackItem.error.argument.length;
|
|
511
|
-
break stackItemFunctionSwitcher;
|
|
512
|
-
case "UnsupportedFunction":
|
|
513
|
-
offset = stackItem.error.functionName.length;
|
|
514
|
-
break stackItemFunctionSwitcher;
|
|
515
|
-
case "UnsupportedSNBTPrimitive":
|
|
516
|
-
offset = stackItem.error.raw.length;
|
|
517
|
-
break stackItemFunctionSwitcher;
|
|
518
|
-
case "InvalidSNBTString":
|
|
519
|
-
offset = stackItem.error.raw.length;
|
|
520
|
-
break stackItemFunctionSwitcher;
|
|
521
|
-
default:
|
|
522
|
-
offset = stackItem.input.length - stackItem.positionInInput;
|
|
523
|
-
break stackItemFunctionSwitcher;
|
|
524
|
-
}
|
|
525
|
-
case "parseTypedArray":
|
|
526
|
-
switch (stackItem.error?.type) {
|
|
527
|
-
case "DisallowedTypeInTypedArray":
|
|
528
|
-
offset = stackItem.inputItem.length - stackItem.positionInInputItem;
|
|
529
|
-
break stackItemFunctionSwitcher;
|
|
530
|
-
case "UnsupportedTypeInTypedArray":
|
|
531
|
-
offset = stackItem.inputItem.length - stackItem.positionInInputItem;
|
|
532
|
-
break stackItemFunctionSwitcher;
|
|
533
|
-
default:
|
|
534
|
-
offset = stackItem.inputItem.length - stackItem.positionInInputItem;
|
|
535
|
-
break stackItemFunctionSwitcher;
|
|
536
|
-
}
|
|
537
|
-
case "uuidToIntArray":
|
|
538
|
-
offset = stackItem.error.uuid.length;
|
|
539
|
-
break;
|
|
540
|
-
}
|
|
541
|
-
const position: SNBTParseErrorCause["position"] = cause.position + offset;
|
|
542
|
-
const input: string = this.originalInput;
|
|
543
|
-
return [input.slice(0, position).split("\n").length, position - input.slice(0, position).lastIndexOf("\n") + 1];
|
|
544
|
-
}
|
|
545
|
-
public getErrorPositionWithOffset(offset: number): [line: number, column: number] | null {
|
|
546
|
-
if (!this.isResolved || this.originalInput === null) return null;
|
|
547
|
-
const cause: SNBTParseErrorCause = this.cause;
|
|
548
|
-
const position: SNBTParseErrorCause["position"] = cause.position + offset;
|
|
549
|
-
const input: string = this.originalInput;
|
|
550
|
-
return [input.slice(0, position).split("\n").length, position - input.slice(0, position).lastIndexOf("\n") + 1];
|
|
551
|
-
}
|
|
552
|
-
public getErrorPositionForStackItem(stackItem: SNBTParseErrorCauseStackItem): [line: number, column: number] {
|
|
553
|
-
const input: string = "input" in stackItem ? stackItem.input : stackItem.inputItem;
|
|
554
|
-
const position: number = "positionInInput" in stackItem ? stackItem.positionInInput : stackItem.positionInInputItem;
|
|
555
|
-
return [input.slice(0, position).split("\n").length, position - input.slice(0, position).lastIndexOf("\n") + 1];
|
|
556
|
-
}
|
|
557
|
-
} as unknown as SNBTParseErrorConstructor,
|
|
558
|
-
{
|
|
559
|
-
apply(target: SNBTParseErrorConstructor, thisArg: any, argumentsList: [any, any]): SNBTParseError {
|
|
560
|
-
return new target(...argumentsList);
|
|
561
|
-
},
|
|
562
|
-
}
|
|
563
|
-
);
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Options for parsing an SNBT-like primitive.
|
|
567
|
-
*
|
|
568
|
-
* @internal
|
|
569
|
-
*/
|
|
570
|
-
interface ParseSNBTPrimitiveOptions extends ParseSNBTBaseOptions {}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Parses an SNBT-like primitive.
|
|
574
|
-
*
|
|
575
|
-
* @internal
|
|
576
|
-
*/
|
|
577
|
-
function parseSNBTPrimitive(
|
|
578
|
-
raw: any,
|
|
579
|
-
options: ParseSNBTPrimitiveOptions & { keepGoingAfterError: true }
|
|
580
|
-
): { value?: NBT.Tags[NBT.TagType]; errors: SNBTParseError[] };
|
|
581
|
-
/**
|
|
582
|
-
* Parses an SNBT-like primitive.
|
|
583
|
-
*
|
|
584
|
-
* @internal
|
|
585
|
-
*/
|
|
586
|
-
function parseSNBTPrimitive(raw: any, options?: ParseSNBTPrimitiveOptions & { keepGoingAfterError?: false }): NBT.Tags[NBT.TagType];
|
|
587
|
-
/**
|
|
588
|
-
* Parses an SNBT-like primitive.
|
|
589
|
-
*
|
|
590
|
-
* @internal
|
|
591
|
-
*/
|
|
592
|
-
function parseSNBTPrimitive(raw: any, options?: ParseSNBTPrimitiveOptions): NBT.Tags[NBT.TagType] | { value?: NBT.Tags[NBT.TagType]; errors: SNBTParseError[] };
|
|
593
|
-
function parseSNBTPrimitive(
|
|
594
|
-
raw: any,
|
|
595
|
-
options: ParseSNBTPrimitiveOptions = {}
|
|
596
|
-
): NBT.Tags[NBT.TagType] | { value?: NBT.Tags[NBT.TagType]; errors: SNBTParseError[] } {
|
|
597
|
-
const errors: SNBTParseError[] = [];
|
|
598
|
-
function structureResult(val?: NBT.Tags[NBT.TagType]): ReturnType<typeof parseSNBTPrimitive> {
|
|
599
|
-
if (options.keepGoingAfterError) return { value: val, errors };
|
|
600
|
-
return val!;
|
|
601
|
-
}
|
|
602
|
-
try {
|
|
603
|
-
if (typeof raw === "string") {
|
|
604
|
-
const originalInput: string = raw;
|
|
605
|
-
raw = raw.trim();
|
|
606
|
-
|
|
607
|
-
const funcMatch: RegExpMatchArray | null = raw.match(/^(\w+)\((.*)\)$/s);
|
|
608
|
-
if (funcMatch) {
|
|
609
|
-
const fn: string = funcMatch[1]!;
|
|
610
|
-
const arg: string = funcMatch[2]!.trim();
|
|
611
|
-
switch (fn) {
|
|
612
|
-
case "bool": {
|
|
613
|
-
const baseVal = parseSNBTPrimitive(arg, options);
|
|
614
|
-
if ("errors" in baseVal) {
|
|
615
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
616
|
-
err.cause.stack.push({
|
|
617
|
-
input: originalInput,
|
|
618
|
-
positionInInput: originalInput.indexOf(raw) + fn.length + 1,
|
|
619
|
-
function: "parseSNBTPrimitive",
|
|
620
|
-
});
|
|
621
|
-
err.cause.position += originalInput.indexOf(raw) + fn.length + 1;
|
|
622
|
-
});
|
|
623
|
-
errors.push(...baseVal.errors);
|
|
624
|
-
}
|
|
625
|
-
const val = "errors" in baseVal ? baseVal.value : baseVal;
|
|
626
|
-
if (!val) return structureResult();
|
|
627
|
-
if (val.type === "short" || val.type === "int" || val.type === "long" || val.type === "float" || val.type === "double") {
|
|
628
|
-
const num: number = Number(toPrimitive(val));
|
|
629
|
-
return structureResult(NBT.byte(num === 0 ? 0 : 1));
|
|
630
|
-
}
|
|
631
|
-
if (val.type === "byte") return val;
|
|
632
|
-
throw new SNBTParseError(`Invalid argument to bool(): ${arg}`, {
|
|
633
|
-
cause: {
|
|
634
|
-
position: originalInput.indexOf(raw) + fn.length + 1,
|
|
635
|
-
stack: [
|
|
636
|
-
{
|
|
637
|
-
input: originalInput,
|
|
638
|
-
positionInInput: originalInput.indexOf(raw) + fn.length + 1,
|
|
639
|
-
function: "parseSNBTPrimitive",
|
|
640
|
-
error: { type: "InvalidArgumentToFunction", functionName: "bool", argument: arg },
|
|
641
|
-
},
|
|
642
|
-
],
|
|
643
|
-
},
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
case "uuid": {
|
|
647
|
-
const uuidStr: string = arg.replace(/^["']|["']$/g, "");
|
|
648
|
-
try {
|
|
649
|
-
const uuidIntArray: number[] = uuidToIntArray(uuidStr);
|
|
650
|
-
return structureResult(NBT.intArray(uuidIntArray));
|
|
651
|
-
} catch (e) {
|
|
652
|
-
if (e instanceof Error && e.cause) {
|
|
653
|
-
(e.cause as SNBTParseErrorCause).stack.push({
|
|
654
|
-
positionInInput: originalInput.indexOf(raw) + fn.length + 1,
|
|
655
|
-
input: originalInput,
|
|
656
|
-
function: "parseSNBTPrimitive",
|
|
657
|
-
error: {
|
|
658
|
-
type: "InvalidArgumentToFunction",
|
|
659
|
-
functionName: "uuid",
|
|
660
|
-
argument: arg,
|
|
661
|
-
},
|
|
662
|
-
});
|
|
663
|
-
(e.cause as SNBTParseErrorCause).position += originalInput.indexOf(raw) + fn.length + 1;
|
|
664
|
-
}
|
|
665
|
-
throw e;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
default:
|
|
669
|
-
throw (
|
|
670
|
-
(new ReferenceError(`Unsupported SNBT function: ${fn}`),
|
|
671
|
-
{
|
|
672
|
-
cause: {
|
|
673
|
-
position: originalInput.indexOf(raw) + fn.length + 1,
|
|
674
|
-
stack: [
|
|
675
|
-
{
|
|
676
|
-
input: originalInput,
|
|
677
|
-
positionInInput: originalInput.indexOf(raw) + fn.length + 1,
|
|
678
|
-
function: "parseSNBTPrimitive",
|
|
679
|
-
error: { type: "UnsupportedFunction", functionName: fn },
|
|
680
|
-
},
|
|
681
|
-
],
|
|
682
|
-
} as const satisfies SNBTParseErrorCause,
|
|
683
|
-
})
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
const arrMatch: RegExpMatchArray | null = raw.match(/^\[(B|S|I|L);\s*(.*?)\]$/is);
|
|
689
|
-
if (arrMatch) {
|
|
690
|
-
const type = arrMatch[1]!.toUpperCase() as "B" | "S" | "I" | "L";
|
|
691
|
-
const items: string[] = arrMatch[2]!.split(/\s*,\s*/).filter(Boolean);
|
|
692
|
-
const baseVal = parseTypedArray(type, items, options);
|
|
693
|
-
if ("errors" in baseVal) {
|
|
694
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
695
|
-
const lastStackItem = err.cause.stack.at(-1)! as Extract<SNBTParseErrorCauseStackItem, { function: "parseTypedArray" }>;
|
|
696
|
-
const baseOffset: number = arrMatch[2]!.match(new RegExp(`^(.*?\\s*(,\\s*|$)){${lastStackItem.inputItemIndex}}`))?.[0]?.length ?? 0;
|
|
697
|
-
err.cause.stack.push({
|
|
698
|
-
input: originalInput,
|
|
699
|
-
positionInInput: originalInput.indexOf(raw) + raw.indexOf(arrMatch[2]) + baseOffset,
|
|
700
|
-
function: "parseSNBTPrimitive",
|
|
701
|
-
});
|
|
702
|
-
err.cause.position += originalInput.indexOf(raw) + raw.indexOf(arrMatch[2]) + baseOffset;
|
|
703
|
-
});
|
|
704
|
-
errors.push(...baseVal.errors);
|
|
705
|
-
}
|
|
706
|
-
return structureResult("errors" in baseVal ? baseVal.value : baseVal);
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
const listMatch: RegExpMatchArray | null = raw.match(/^\[\s*(.*?)\]$/is);
|
|
710
|
-
if (listMatch) {
|
|
711
|
-
const baseVal = extractSNBT(raw, options);
|
|
712
|
-
if ("errors" in baseVal) {
|
|
713
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
714
|
-
err.cause.stack.push({
|
|
715
|
-
input: originalInput,
|
|
716
|
-
positionInInput: originalInput.indexOf(raw),
|
|
717
|
-
function: "parseSNBTPrimitive",
|
|
718
|
-
});
|
|
719
|
-
err.cause.position += originalInput.indexOf(raw);
|
|
720
|
-
});
|
|
721
|
-
errors.push(...baseVal.errors);
|
|
722
|
-
}
|
|
723
|
-
return structureResult("errors" in baseVal ? baseVal.value : baseVal.value);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
const match: RegExpMatchArray | null = raw.match(/^([-+]?0x[\da-fA-F]+|0b[01]+|[-+]?\d*\.?\d*(?:[eE][-+]?\d+)?)([bsilfdBSILFD])?$/i);
|
|
727
|
-
if (match) {
|
|
728
|
-
const numStr: string = match[1]!;
|
|
729
|
-
const suffix: string | undefined = match[2]?.toLowerCase();
|
|
730
|
-
let value: number | bigint;
|
|
731
|
-
if (numStr.startsWith("0x")) value = parseInt(numStr, 16);
|
|
732
|
-
else if (numStr.startsWith("0b")) value = parseInt(numStr.slice(2), 2);
|
|
733
|
-
else if (suffix === "l") value = BigInt(numStr);
|
|
734
|
-
else value = Number(numStr);
|
|
735
|
-
|
|
736
|
-
switch (suffix) {
|
|
737
|
-
case "b":
|
|
738
|
-
return structureResult(NBT.byte(Number(value)));
|
|
739
|
-
case "s":
|
|
740
|
-
return structureResult(NBT.short(Number(value)));
|
|
741
|
-
case "i":
|
|
742
|
-
return structureResult(NBT.int(Number(value)));
|
|
743
|
-
case "l":
|
|
744
|
-
return structureResult(NBT.long(toLongParts(BigInt(value))));
|
|
745
|
-
case "f":
|
|
746
|
-
return structureResult(NBT.float(Number(value)));
|
|
747
|
-
case "d":
|
|
748
|
-
return structureResult(NBT.double(Number(value)));
|
|
749
|
-
default:
|
|
750
|
-
if (typeof value === "bigint") return structureResult(NBT.long(toLongParts(value)));
|
|
751
|
-
if (Number.isInteger(value)) return structureResult(NBT.int(value));
|
|
752
|
-
return structureResult(NBT.double(value));
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (["true", "false"].includes(raw)) return structureResult(NBT.byte(+(raw === "true")));
|
|
757
|
-
|
|
758
|
-
try {
|
|
759
|
-
return structureResult({
|
|
760
|
-
type: "string",
|
|
761
|
-
value: parseFormattedString(raw),
|
|
762
|
-
});
|
|
763
|
-
} catch {
|
|
764
|
-
const error = new SNBTParseError("Invalid SNBT string: " + raw, {
|
|
765
|
-
cause: {
|
|
766
|
-
position: originalInput.indexOf(raw),
|
|
767
|
-
stack: [
|
|
768
|
-
{
|
|
769
|
-
function: "parseSNBTPrimitive",
|
|
770
|
-
input: originalInput,
|
|
771
|
-
positionInInput: originalInput.indexOf(raw),
|
|
772
|
-
error: {
|
|
773
|
-
type: "InvalidSNBTString",
|
|
774
|
-
raw,
|
|
775
|
-
},
|
|
776
|
-
},
|
|
777
|
-
],
|
|
778
|
-
},
|
|
779
|
-
});
|
|
780
|
-
if (options.keepGoingAfterError) errors.push(error);
|
|
781
|
-
else throw error;
|
|
782
|
-
return structureResult();
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
if (typeof raw === "number") {
|
|
787
|
-
if (raw % 1 !== 0) return structureResult(NBT.double(raw));
|
|
788
|
-
return structureResult(NBT.int(raw));
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
if (typeof raw === "bigint") return structureResult(NBT.long(toLongParts(raw)));
|
|
792
|
-
if (typeof raw === "boolean") return structureResult(NBT.byte(+raw));
|
|
793
|
-
|
|
794
|
-
throw new SNBTParseError("Unsupported SNBT primitive: " + raw, {
|
|
795
|
-
cause: {
|
|
796
|
-
position: 0,
|
|
797
|
-
stack: [
|
|
798
|
-
{
|
|
799
|
-
function: "parseSNBTPrimitive",
|
|
800
|
-
input: raw,
|
|
801
|
-
positionInInput: 0,
|
|
802
|
-
error: {
|
|
803
|
-
type: "UnsupportedSNBTPrimitive",
|
|
804
|
-
raw,
|
|
805
|
-
},
|
|
806
|
-
},
|
|
807
|
-
],
|
|
808
|
-
},
|
|
809
|
-
});
|
|
810
|
-
} catch (e) {
|
|
811
|
-
if (options.keepGoingAfterError && e instanceof SNBTParseError) errors.push(e);
|
|
812
|
-
else throw e;
|
|
813
|
-
return structureResult();
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* The letter types of typed arrays.
|
|
819
|
-
*
|
|
820
|
-
* @internal
|
|
821
|
-
*/
|
|
822
|
-
enum TypedArrayLetterTypes {
|
|
823
|
-
/**
|
|
824
|
-
* A byte array.
|
|
825
|
-
*/
|
|
826
|
-
B = "byte",
|
|
827
|
-
/**
|
|
828
|
-
* A short array.
|
|
829
|
-
*/
|
|
830
|
-
S = "short",
|
|
831
|
-
/**
|
|
832
|
-
* An integer array.
|
|
833
|
-
*/
|
|
834
|
-
I = "int",
|
|
835
|
-
/**
|
|
836
|
-
* A long array.
|
|
837
|
-
*/
|
|
838
|
-
L = "long",
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* A typed array.
|
|
843
|
-
*
|
|
844
|
-
* @internal
|
|
845
|
-
*/
|
|
846
|
-
type TypedArray = NBT.Tags[NBT.TagType.ByteArray | NBT.TagType.ShortArray | NBT.TagType.IntArray | NBT.TagType.LongArray];
|
|
847
|
-
|
|
848
|
-
function parseTypedArray(
|
|
849
|
-
type: "B" | "S" | "I" | "L",
|
|
850
|
-
items: string[],
|
|
851
|
-
options: ParseSNBTBaseOptions & { keepGoingAfterError: true }
|
|
852
|
-
): { value: TypedArray; errors: SNBTParseError[] };
|
|
853
|
-
function parseTypedArray(type: "B" | "S" | "I" | "L", items: string[], options?: ParseSNBTBaseOptions & { keepGoingAfterError?: false }): TypedArray;
|
|
854
|
-
function parseTypedArray(
|
|
855
|
-
type: "B" | "S" | "I" | "L",
|
|
856
|
-
items: string[],
|
|
857
|
-
options?: ParseSNBTBaseOptions
|
|
858
|
-
): TypedArray | { value: TypedArray; errors: SNBTParseError[] };
|
|
859
|
-
function parseTypedArray(
|
|
860
|
-
type: "B" | "S" | "I" | "L",
|
|
861
|
-
items: string[],
|
|
862
|
-
options: ParseSNBTBaseOptions = {}
|
|
863
|
-
): TypedArray | { value: TypedArray; errors: SNBTParseError[] } {
|
|
864
|
-
const errors: SNBTParseError[] = [];
|
|
865
|
-
const values: any[] = [];
|
|
866
|
-
|
|
867
|
-
let i: number = 0;
|
|
868
|
-
for (const x of items) {
|
|
869
|
-
try {
|
|
870
|
-
if (/^-?\d+b$/i.test(x)) {
|
|
871
|
-
const n: number = Number(x.slice(0, -1));
|
|
872
|
-
if (type === "L") values.push(toLongParts(BigInt(n)));
|
|
873
|
-
else if (!["B", "S", "I", "L"].includes(type))
|
|
874
|
-
throw new SNBTParseError(`Byte value not allowed in ${TypedArrayLetterTypes[type]} array: ${x}`, {
|
|
875
|
-
cause: {
|
|
876
|
-
position: 0,
|
|
877
|
-
stack: [
|
|
878
|
-
{
|
|
879
|
-
function: "parseTypedArray",
|
|
880
|
-
inputItems: items,
|
|
881
|
-
inputItem: x,
|
|
882
|
-
inputItemIndex: i,
|
|
883
|
-
positionInInputItem: 0,
|
|
884
|
-
arrayType: TypedArrayLetterTypes[type],
|
|
885
|
-
error: {
|
|
886
|
-
type: "DisallowedTypeInTypedArray",
|
|
887
|
-
allowedTypes: [TypedArrayLetterTypes.B, TypedArrayLetterTypes.S, TypedArrayLetterTypes.I, TypedArrayLetterTypes.L],
|
|
888
|
-
itemType: NBT.TagType.Byte,
|
|
889
|
-
},
|
|
890
|
-
},
|
|
891
|
-
],
|
|
892
|
-
},
|
|
893
|
-
});
|
|
894
|
-
else values.push(n);
|
|
895
|
-
} else if (/^-?\d+s$/i.test(x)) {
|
|
896
|
-
const n: number = Number(x.slice(0, -1));
|
|
897
|
-
if (type === "L") values.push(toLongParts(BigInt(n)));
|
|
898
|
-
else if (!["S", "I", "L"].includes(type))
|
|
899
|
-
throw new SNBTParseError(`Short value not allowed in ${TypedArrayLetterTypes[type]} array: ${x}`, {
|
|
900
|
-
cause: {
|
|
901
|
-
position: 0,
|
|
902
|
-
stack: [
|
|
903
|
-
{
|
|
904
|
-
function: "parseTypedArray",
|
|
905
|
-
inputItems: items,
|
|
906
|
-
inputItem: x,
|
|
907
|
-
inputItemIndex: i,
|
|
908
|
-
positionInInputItem: 0,
|
|
909
|
-
arrayType: TypedArrayLetterTypes[type],
|
|
910
|
-
error: {
|
|
911
|
-
type: "DisallowedTypeInTypedArray",
|
|
912
|
-
allowedTypes: [TypedArrayLetterTypes.B, TypedArrayLetterTypes.S, TypedArrayLetterTypes.I, TypedArrayLetterTypes.L],
|
|
913
|
-
itemType: NBT.TagType.Short,
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
],
|
|
917
|
-
},
|
|
918
|
-
});
|
|
919
|
-
else values.push(n);
|
|
920
|
-
} else if (/^-?\d+l$/i.test(x)) {
|
|
921
|
-
if (type !== "L")
|
|
922
|
-
throw new SNBTParseError(`Long value not allowed in ${TypedArrayLetterTypes[type]} array: ${x}`, {
|
|
923
|
-
cause: {
|
|
924
|
-
position: 0,
|
|
925
|
-
stack: [
|
|
926
|
-
{
|
|
927
|
-
function: "parseTypedArray",
|
|
928
|
-
inputItems: items,
|
|
929
|
-
inputItem: x,
|
|
930
|
-
inputItemIndex: i,
|
|
931
|
-
positionInInputItem: 0,
|
|
932
|
-
arrayType: TypedArrayLetterTypes[type],
|
|
933
|
-
error: {
|
|
934
|
-
type: "DisallowedTypeInTypedArray",
|
|
935
|
-
allowedTypes: [TypedArrayLetterTypes.B, TypedArrayLetterTypes.S, TypedArrayLetterTypes.I, TypedArrayLetterTypes.L],
|
|
936
|
-
itemType: NBT.TagType.Long,
|
|
937
|
-
},
|
|
938
|
-
},
|
|
939
|
-
],
|
|
940
|
-
},
|
|
941
|
-
});
|
|
942
|
-
values.push(parseLong(x));
|
|
943
|
-
} else if (/^-?\d+i?$/.test(x)) {
|
|
944
|
-
const n: number = x.toLowerCase().endsWith("i") ? Number(x.slice(0, -1)) : Number(x);
|
|
945
|
-
if (type === "L") values.push(toLongParts(BigInt(n)));
|
|
946
|
-
else if (!["I", "L"].includes(type))
|
|
947
|
-
throw new SNBTParseError(`Int value not allowed in ${TypedArrayLetterTypes[type]} array: ${x}`, {
|
|
948
|
-
cause: {
|
|
949
|
-
position: 0,
|
|
950
|
-
stack: [
|
|
951
|
-
{
|
|
952
|
-
function: "parseTypedArray",
|
|
953
|
-
inputItems: items,
|
|
954
|
-
inputItem: x,
|
|
955
|
-
inputItemIndex: i,
|
|
956
|
-
positionInInputItem: 0,
|
|
957
|
-
arrayType: TypedArrayLetterTypes[type],
|
|
958
|
-
error: {
|
|
959
|
-
type: "DisallowedTypeInTypedArray",
|
|
960
|
-
allowedTypes: [TypedArrayLetterTypes.B, TypedArrayLetterTypes.S, TypedArrayLetterTypes.I, TypedArrayLetterTypes.L],
|
|
961
|
-
itemType: NBT.TagType.Int,
|
|
962
|
-
},
|
|
963
|
-
},
|
|
964
|
-
],
|
|
965
|
-
},
|
|
966
|
-
});
|
|
967
|
-
else values.push(n);
|
|
968
|
-
} else if (["true", "false"].includes(x)) {
|
|
969
|
-
values.push(+(x === "true"));
|
|
970
|
-
} else {
|
|
971
|
-
throw new SNBTParseError(`Unsupported value in ${TypedArrayLetterTypes[type]} array: ${x}`, {
|
|
972
|
-
cause: {
|
|
973
|
-
position: 0,
|
|
974
|
-
stack: [
|
|
975
|
-
{
|
|
976
|
-
function: "parseTypedArray",
|
|
977
|
-
inputItems: items,
|
|
978
|
-
inputItem: x,
|
|
979
|
-
inputItemIndex: i,
|
|
980
|
-
positionInInputItem: 0,
|
|
981
|
-
arrayType: TypedArrayLetterTypes[type],
|
|
982
|
-
error: {
|
|
983
|
-
type: "UnsupportedTypeInTypedArray",
|
|
984
|
-
itemType: ((): `${NBT.TagType}` | undefined => {
|
|
985
|
-
try {
|
|
986
|
-
return parseSNBTPrimitive(x).type;
|
|
987
|
-
} catch {
|
|
988
|
-
return undefined;
|
|
989
|
-
}
|
|
990
|
-
})(),
|
|
991
|
-
},
|
|
992
|
-
},
|
|
993
|
-
],
|
|
994
|
-
},
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
} catch (e) {
|
|
998
|
-
if (options.keepGoingAfterError && e instanceof SNBTParseError) errors.push(e);
|
|
999
|
-
else throw e;
|
|
1000
|
-
}
|
|
1001
|
-
i++;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
let result: TypedArray;
|
|
1005
|
-
|
|
1006
|
-
switch (type) {
|
|
1007
|
-
case "B":
|
|
1008
|
-
result = NBT.byteArray(values as number[]);
|
|
1009
|
-
break;
|
|
1010
|
-
case "S":
|
|
1011
|
-
result = NBT.shortArray(values as number[]);
|
|
1012
|
-
break;
|
|
1013
|
-
case "I":
|
|
1014
|
-
result = NBT.intArray(values as number[]);
|
|
1015
|
-
break;
|
|
1016
|
-
case "L":
|
|
1017
|
-
result = NBT.longArray(values as [high: number, low: number][]);
|
|
1018
|
-
break;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
return options.keepGoingAfterError ? { value: result, errors } : result;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
interface ParseListOptions extends ParseSNBTBaseOptions {}
|
|
1025
|
-
|
|
1026
|
-
function parseList(items: string[], options: ParseListOptions & { keepGoingAfterError: true }): { value: NBT.List<NBT.TagType>; errors: SNBTParseError[] };
|
|
1027
|
-
function parseList(items: string[], options?: ParseListOptions & { keepGoingAfterError?: false }): NBT.List<NBT.TagType>;
|
|
1028
|
-
function parseList(items: string[], options?: ParseListOptions): NBT.List<NBT.TagType> | { value: NBT.List<NBT.TagType>; errors: SNBTParseError[] };
|
|
1029
|
-
function parseList(items: string[], options: ParseListOptions = {}): NBT.List<NBT.TagType> | { value: NBT.List<NBT.TagType>; errors: SNBTParseError[] } {
|
|
1030
|
-
console.log(items, items.length);
|
|
1031
|
-
const errors: SNBTParseError[] = [];
|
|
1032
|
-
let values: NBT.Tags[NBT.TagType][] = [];
|
|
1033
|
-
let valueIndices: number[] = [];
|
|
1034
|
-
let i: number = 0;
|
|
1035
|
-
for (const x of items) {
|
|
1036
|
-
try {
|
|
1037
|
-
if (x.trim().startsWith("{")) {
|
|
1038
|
-
const baseVal = parseSNBTCompoundString(x, options);
|
|
1039
|
-
if ("errors" in baseVal) {
|
|
1040
|
-
baseVal.errors.forEach((err) => {
|
|
1041
|
-
err.cause.stack.push({
|
|
1042
|
-
inputItem: x,
|
|
1043
|
-
inputItemIndex: i,
|
|
1044
|
-
inputItems: items,
|
|
1045
|
-
positionInInputItem: 0,
|
|
1046
|
-
function: "parseList",
|
|
1047
|
-
});
|
|
1048
|
-
err.cause.position += 0;
|
|
1049
|
-
});
|
|
1050
|
-
errors.push(...baseVal.errors);
|
|
1051
|
-
}
|
|
1052
|
-
values.push("errors" in baseVal ? baseVal.value : baseVal);
|
|
1053
|
-
} else {
|
|
1054
|
-
const baseVal = parseSNBTPrimitive(x, options);
|
|
1055
|
-
if ("errors" in baseVal) {
|
|
1056
|
-
baseVal.errors.forEach((err) => {
|
|
1057
|
-
err.cause.stack.push({
|
|
1058
|
-
inputItem: x,
|
|
1059
|
-
inputItemIndex: i,
|
|
1060
|
-
inputItems: items,
|
|
1061
|
-
positionInInputItem: 0,
|
|
1062
|
-
function: "parseList",
|
|
1063
|
-
});
|
|
1064
|
-
err.cause.position += 0;
|
|
1065
|
-
});
|
|
1066
|
-
errors.push(...baseVal.errors);
|
|
1067
|
-
}
|
|
1068
|
-
const val = "errors" in baseVal ? baseVal.value : baseVal;
|
|
1069
|
-
if (val !== undefined) values.push(val);
|
|
1070
|
-
}
|
|
1071
|
-
valueIndices.push(i);
|
|
1072
|
-
} catch (e) {
|
|
1073
|
-
if (options.keepGoingAfterError && e instanceof SNBTParseError) errors.push(e);
|
|
1074
|
-
else throw e;
|
|
1075
|
-
}
|
|
1076
|
-
i++;
|
|
1077
|
-
}
|
|
1078
|
-
let type: `${NBT.TagType}` | undefined = values[0]?.type;
|
|
1079
|
-
if (
|
|
1080
|
-
type &&
|
|
1081
|
-
!values.every((v, i) => {
|
|
1082
|
-
if (v.type === type) {
|
|
1083
|
-
return true;
|
|
1084
|
-
}
|
|
1085
|
-
if (!(options.mixedListsAllowed ?? true)) {
|
|
1086
|
-
if (!options.keepGoingAfterError)
|
|
1087
|
-
throw new SNBTParseError("Mixed list types not allowed.", {
|
|
1088
|
-
cause: {
|
|
1089
|
-
position: 0,
|
|
1090
|
-
stack: [
|
|
1091
|
-
{
|
|
1092
|
-
function: "parseList",
|
|
1093
|
-
inputItems: items,
|
|
1094
|
-
inputItem: items[i]!,
|
|
1095
|
-
inputItemIndex: valueIndices[i]!,
|
|
1096
|
-
positionInInputItem: 0,
|
|
1097
|
-
error: {
|
|
1098
|
-
type: "MixedListTypesNotAllowed",
|
|
1099
|
-
itemType: v.type,
|
|
1100
|
-
item: v,
|
|
1101
|
-
detectedArrayType: type,
|
|
1102
|
-
},
|
|
1103
|
-
},
|
|
1104
|
-
],
|
|
1105
|
-
},
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
return false;
|
|
1109
|
-
})
|
|
1110
|
-
) {
|
|
1111
|
-
if (!(options.mixedListsAllowed ?? true) && options.keepGoingAfterError) {
|
|
1112
|
-
const numOfEachType = values.reduce((acc, v) => ({ ...acc, [v.type]: (acc[v.type] ?? 0) + 1 }), {} as Record<`${NBT.TagType}`, number>);
|
|
1113
|
-
const mostCommonType = Object.entries(numOfEachType).reduce((a, b) => (a[1] > b[1] ? a : b))[0] as `${NBT.TagType}`;
|
|
1114
|
-
for (let i: number = 0; i < values.length; i++) {
|
|
1115
|
-
if (values[i]!.type === mostCommonType) continue;
|
|
1116
|
-
errors.push(
|
|
1117
|
-
new SNBTParseError("Mixed list types not allowed.", {
|
|
1118
|
-
cause: {
|
|
1119
|
-
position: 0,
|
|
1120
|
-
stack: [
|
|
1121
|
-
{
|
|
1122
|
-
function: "parseList",
|
|
1123
|
-
inputItems: items,
|
|
1124
|
-
inputItem: items[i]!,
|
|
1125
|
-
inputItemIndex: valueIndices[i]!,
|
|
1126
|
-
positionInInputItem: 0,
|
|
1127
|
-
error: {
|
|
1128
|
-
type: "MixedListTypesNotAllowed",
|
|
1129
|
-
itemType: values[i]!.type,
|
|
1130
|
-
item: values[i]!,
|
|
1131
|
-
detectedArrayType: type,
|
|
1132
|
-
},
|
|
1133
|
-
},
|
|
1134
|
-
],
|
|
1135
|
-
},
|
|
1136
|
-
})
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
if (options.convertMixedListsToCompoundLists ?? true) values = values.map((v, i) => ({ type: NBT.TagType.Compound, value: { "": v } }));
|
|
1141
|
-
}
|
|
1142
|
-
const result = NBT.list({ type: values[0]?.type ?? "end", value: values.map((v) => v.value) }) as any;
|
|
1143
|
-
return options.keepGoingAfterError ? { value: result, errors } : result;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
/**
|
|
1147
|
-
* Options for parsing SNBT.
|
|
1148
|
-
*/
|
|
1149
|
-
export interface ParseSNBTBaseOptions {
|
|
1150
|
-
/**
|
|
1151
|
-
* Whether to allow lists of mixed types.
|
|
1152
|
-
*
|
|
1153
|
-
* @default true
|
|
1154
|
-
*/
|
|
1155
|
-
mixedListsAllowed?: boolean;
|
|
1156
|
-
/**
|
|
1157
|
-
* Whether to convert lists of mixed types to compound lists.
|
|
1158
|
-
*
|
|
1159
|
-
* @default true
|
|
1160
|
-
*/
|
|
1161
|
-
convertMixedListsToCompoundLists?: boolean;
|
|
1162
|
-
/**
|
|
1163
|
-
* Whether the function is being called from within an inner stack of a parent SNBT parser function.
|
|
1164
|
-
*
|
|
1165
|
-
* This is internal only and should not be specified by users.
|
|
1166
|
-
*
|
|
1167
|
-
* @internal
|
|
1168
|
-
* @ignore
|
|
1169
|
-
*
|
|
1170
|
-
* @default false
|
|
1171
|
-
*/
|
|
1172
|
-
isInnerStack?: boolean;
|
|
1173
|
-
/**
|
|
1174
|
-
* Whether to keep parsing the SNBT string even if there are errors.
|
|
1175
|
-
*
|
|
1176
|
-
* It will cause the function to return an object containing the errors, as well as the parts of the value that were able to be extracted.
|
|
1177
|
-
*
|
|
1178
|
-
* @default false
|
|
1179
|
-
*/
|
|
1180
|
-
keepGoingAfterError?: boolean;
|
|
1181
|
-
/**
|
|
1182
|
-
* Whether to stop parsing the SNBT string at a negative depth.
|
|
1183
|
-
*
|
|
1184
|
-
* @default true
|
|
1185
|
-
*/
|
|
1186
|
-
stopAtNegativeDepth?: boolean;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
/**
|
|
1190
|
-
* The result of the {@link parseSNBTCompoundString} function when {@link ParseSNBTBaseOptions.keepGoingAfterError} is `true`.
|
|
1191
|
-
*/
|
|
1192
|
-
export type ParseSNBTCompoundStringResultWithErrors = {
|
|
1193
|
-
/**
|
|
1194
|
-
* The parsed SNBT compound.
|
|
1195
|
-
*/
|
|
1196
|
-
value: Compound;
|
|
1197
|
-
/**
|
|
1198
|
-
* The errors that occurred while parsing the SNBT compound.
|
|
1199
|
-
*/
|
|
1200
|
-
errors: SNBTParseError[];
|
|
1201
|
-
};
|
|
1202
|
-
|
|
1203
|
-
/**
|
|
1204
|
-
* Parse an SNBT compound string into Prismarine-NBT JSON.
|
|
1205
|
-
*
|
|
1206
|
-
* @param input The SNBT compound string to parse.
|
|
1207
|
-
* @param options Options for parsing the SNBT compound string.
|
|
1208
|
-
* @returns The parsed SNBT compound.
|
|
1209
|
-
*/
|
|
1210
|
-
export function parseSNBTCompoundString(input: string, options: ParseSNBTBaseOptions & { keepGoingAfterError: true }): ParseSNBTCompoundStringResultWithErrors;
|
|
1211
|
-
export function parseSNBTCompoundString(input: string, options?: ParseSNBTBaseOptions & { keepGoingAfterError?: false }): Compound;
|
|
1212
|
-
export function parseSNBTCompoundString(input: string, options?: ParseSNBTBaseOptions): Compound | ParseSNBTCompoundStringResultWithErrors;
|
|
1213
|
-
export function parseSNBTCompoundString(input: string, options: ParseSNBTBaseOptions = {}): Compound | ParseSNBTCompoundStringResultWithErrors {
|
|
1214
|
-
const errors: SNBTParseError[] = [];
|
|
1215
|
-
const originalInput: string = input;
|
|
1216
|
-
input = input.trim();
|
|
1217
|
-
|
|
1218
|
-
if (input.startsWith("{")) {
|
|
1219
|
-
input = input.slice(1);
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
const result: Record<string, any> = {};
|
|
1223
|
-
let depth: number = 0;
|
|
1224
|
-
let currentValuePos: number = -1;
|
|
1225
|
-
let currentKey: string = "";
|
|
1226
|
-
let currentValue: string = "";
|
|
1227
|
-
let inString: string | null = null;
|
|
1228
|
-
|
|
1229
|
-
function commitPair(): void {
|
|
1230
|
-
if (!currentKey) return;
|
|
1231
|
-
result[currentKey.trim()] = parseValue(currentValue.trim(), currentKey, currentValuePos);
|
|
1232
|
-
currentKey = "";
|
|
1233
|
-
currentValue = "";
|
|
1234
|
-
currentValuePos = -1;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
function parseValue(val: string, key: string, pos: number): NBT.Tags[NBT.TagType] | undefined {
|
|
1238
|
-
const originalVal: string = val;
|
|
1239
|
-
val = val.trim();
|
|
1240
|
-
if (!val) return NBT.comp({});
|
|
1241
|
-
|
|
1242
|
-
if (val.startsWith("{") && val.endsWith("}")) {
|
|
1243
|
-
const baseVal = parseSNBTCompoundString(val, { ...options, isInnerStack: true });
|
|
1244
|
-
if ("errors" in baseVal) {
|
|
1245
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
1246
|
-
err.cause.stack.push({
|
|
1247
|
-
input: originalInput,
|
|
1248
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1249
|
-
key,
|
|
1250
|
-
function: "extractSNBT",
|
|
1251
|
-
});
|
|
1252
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1253
|
-
});
|
|
1254
|
-
errors.push(...baseVal.errors);
|
|
1255
|
-
}
|
|
1256
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
if (val.startsWith("[") && val.endsWith("]")) {
|
|
1260
|
-
const baseVal = parseSNBTPrimitive(val, { ...options, isInnerStack: true });
|
|
1261
|
-
if ("errors" in baseVal) {
|
|
1262
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
1263
|
-
err.cause.stack.push({
|
|
1264
|
-
input: originalInput,
|
|
1265
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1266
|
-
key,
|
|
1267
|
-
function: "extractSNBT",
|
|
1268
|
-
});
|
|
1269
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1270
|
-
});
|
|
1271
|
-
errors.push(...baseVal.errors);
|
|
1272
|
-
}
|
|
1273
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
const baseVal = parseSNBTPrimitive(val, { ...options, isInnerStack: true });
|
|
1277
|
-
if ("errors" in baseVal) {
|
|
1278
|
-
baseVal.errors.forEach((err: SNBTParseError): void => {
|
|
1279
|
-
err.cause.stack.push({
|
|
1280
|
-
input: originalInput,
|
|
1281
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1282
|
-
key,
|
|
1283
|
-
function: "extractSNBT",
|
|
1284
|
-
});
|
|
1285
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1286
|
-
});
|
|
1287
|
-
errors.push(...baseVal.errors);
|
|
1288
|
-
}
|
|
1289
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
let readingKey: boolean = true;
|
|
1293
|
-
for (let i: number = 0; i < input.length; i++) {
|
|
1294
|
-
if (depth < 0) break;
|
|
1295
|
-
const c: string | undefined = input[i];
|
|
1296
|
-
|
|
1297
|
-
if (c === '"' || c === "'") {
|
|
1298
|
-
if (inString === c) inString = null;
|
|
1299
|
-
else if (!inString) inString = c;
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
if (!inString) {
|
|
1303
|
-
if (c === ":" && readingKey) {
|
|
1304
|
-
readingKey = false;
|
|
1305
|
-
continue;
|
|
1306
|
-
} else if (c === "," && depth === 0) {
|
|
1307
|
-
commitPair();
|
|
1308
|
-
readingKey = true;
|
|
1309
|
-
continue;
|
|
1310
|
-
} else if (c === "{" || c === "[") {
|
|
1311
|
-
depth++;
|
|
1312
|
-
} else if (c === "}" || c === "]") {
|
|
1313
|
-
depth--;
|
|
1314
|
-
if (depth === -1) {
|
|
1315
|
-
commitPair();
|
|
1316
|
-
readingKey = true;
|
|
1317
|
-
continue;
|
|
1318
|
-
}
|
|
1319
|
-
if ((options.stopAtNegativeDepth ?? true) && depth < 0) {
|
|
1320
|
-
i++;
|
|
1321
|
-
break;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
if (readingKey) currentKey += c;
|
|
1327
|
-
else {
|
|
1328
|
-
currentValue += c;
|
|
1329
|
-
if (currentValuePos === -1 && depth === 0) currentValuePos = i;
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
commitPair();
|
|
1334
|
-
|
|
1335
|
-
const val = NBT.comp(
|
|
1336
|
-
Object.fromEntries(
|
|
1337
|
-
Object.entries(result)
|
|
1338
|
-
.map(([k, v]: [string, any]): [string | undefined, any] => [
|
|
1339
|
-
((): string | undefined => {
|
|
1340
|
-
try {
|
|
1341
|
-
return parseFormattedKey(k.trim());
|
|
1342
|
-
} catch {
|
|
1343
|
-
const error = new SNBTParseError("Invalid SNBT key: " + k.trim(), {
|
|
1344
|
-
cause: {
|
|
1345
|
-
position: k.indexOf(k.trim()),
|
|
1346
|
-
stack: [
|
|
1347
|
-
{
|
|
1348
|
-
function: "parseSNBTCompoundString",
|
|
1349
|
-
input: k,
|
|
1350
|
-
positionInInput: k.indexOf(k.trim()),
|
|
1351
|
-
key: k.trim(),
|
|
1352
|
-
error: {
|
|
1353
|
-
type: "InvalidSNBTKey",
|
|
1354
|
-
raw: k.trim(),
|
|
1355
|
-
},
|
|
1356
|
-
},
|
|
1357
|
-
],
|
|
1358
|
-
},
|
|
1359
|
-
});
|
|
1360
|
-
if (options.keepGoingAfterError) errors.push(error);
|
|
1361
|
-
else throw error;
|
|
1362
|
-
return undefined;
|
|
1363
|
-
}
|
|
1364
|
-
})(),
|
|
1365
|
-
v,
|
|
1366
|
-
])
|
|
1367
|
-
.filter((v: [string | undefined, any]): v is [string, any] => v[0] !== undefined)
|
|
1368
|
-
)
|
|
1369
|
-
);
|
|
1370
|
-
|
|
1371
|
-
return options.keepGoingAfterError ? { value: val, errors } : val;
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
/**
|
|
1375
|
-
* The result of the {@link extractSNBT} function when {@link ParseSNBTBaseOptions.keepGoingAfterError} is `false` or not provided.
|
|
1376
|
-
*/
|
|
1377
|
-
export interface ExtractSNBTResult {
|
|
1378
|
-
/**
|
|
1379
|
-
* The parsed value.
|
|
1380
|
-
*/
|
|
1381
|
-
value: Compound | NBT.List<NBT.TagType>;
|
|
1382
|
-
/**
|
|
1383
|
-
* The start position of the parsed value in the input string.
|
|
1384
|
-
*/
|
|
1385
|
-
startPos: number;
|
|
1386
|
-
/**
|
|
1387
|
-
* The end position of the parsed value in the input string.
|
|
1388
|
-
*/
|
|
1389
|
-
endPos: number;
|
|
1390
|
-
/**
|
|
1391
|
-
* The remaining text after the parsed value.
|
|
1392
|
-
*/
|
|
1393
|
-
remaining: string;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
/**
|
|
1397
|
-
* The result of the {@link extractSNBT} function when {@link ParseSNBTBaseOptions.keepGoingAfterError} is `true`.
|
|
1398
|
-
*/
|
|
1399
|
-
export interface ExtractSNBTResultWithErrors extends ExtractSNBTResult {
|
|
1400
|
-
/**
|
|
1401
|
-
* The errors that occurred while parsing the input string.
|
|
1402
|
-
*/
|
|
1403
|
-
errors: SNBTParseError[];
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
/**
|
|
1407
|
-
* Parses an SNBT string into Prismarine-NBT JSON.
|
|
1408
|
-
*
|
|
1409
|
-
* @param input The SNBT string to parse.
|
|
1410
|
-
* @param options The options to use when parsing the SNBT string.
|
|
1411
|
-
* @returns An object containing the parsed value, start position, end position, remaining text, and errors.
|
|
1412
|
-
*/
|
|
1413
|
-
export function extractSNBT(input: string, options: ParseSNBTBaseOptions & { keepGoingAfterError: true }): ExtractSNBTResultWithErrors;
|
|
1414
|
-
export function extractSNBT(input: string, options?: ParseSNBTBaseOptions & { keepGoingAfterError?: false }): ExtractSNBTResult;
|
|
1415
|
-
export function extractSNBT(input: string, options?: ParseSNBTBaseOptions): ExtractSNBTResult | ExtractSNBTResultWithErrors;
|
|
1416
|
-
export function extractSNBT(input: string, options: ParseSNBTBaseOptions = {}): ExtractSNBTResult | ExtractSNBTResultWithErrors {
|
|
1417
|
-
const errors: SNBTParseError[] = [];
|
|
1418
|
-
const originalInput: string = input;
|
|
1419
|
-
input = input.trim();
|
|
1420
|
-
let resultType: "list" | "compound" = "compound";
|
|
1421
|
-
|
|
1422
|
-
if (input.startsWith("{")) {
|
|
1423
|
-
input = input.slice(1);
|
|
1424
|
-
resultType = "compound";
|
|
1425
|
-
} else if (input.startsWith("[")) {
|
|
1426
|
-
input = input.slice(1);
|
|
1427
|
-
resultType = "list";
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
const result: Record<string, any> = {};
|
|
1431
|
-
const listResult: [value: string, pos: number][] = [];
|
|
1432
|
-
let depth: number = 0;
|
|
1433
|
-
let currentKey: string = "";
|
|
1434
|
-
let currentValue: string = "";
|
|
1435
|
-
let currentValuePos: number = -1;
|
|
1436
|
-
let inString: string | null = null;
|
|
1437
|
-
|
|
1438
|
-
function commitPair(): void {
|
|
1439
|
-
if (resultType === "compound") {
|
|
1440
|
-
if (!currentKey) return;
|
|
1441
|
-
result[currentKey.trim()] = parseValue(currentValue.trim(), currentKey, currentValuePos);
|
|
1442
|
-
currentKey = "";
|
|
1443
|
-
currentValue = "";
|
|
1444
|
-
currentValuePos = -1;
|
|
1445
|
-
} else if (resultType === "list") {
|
|
1446
|
-
listResult.push([currentValue.trim(), currentValuePos + currentValue.indexOf(currentValue.trim())]);
|
|
1447
|
-
currentValue = "";
|
|
1448
|
-
currentValuePos = -1;
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
function parseValue(val: string, key: string, pos: number) {
|
|
1453
|
-
const originalVal: string = val;
|
|
1454
|
-
val = val.trim();
|
|
1455
|
-
if (!val) return NBT.comp({});
|
|
1456
|
-
|
|
1457
|
-
if (val.startsWith("{") && val.endsWith("}")) {
|
|
1458
|
-
const baseVal = parseSNBTCompoundString(val, { ...options, isInnerStack: true });
|
|
1459
|
-
if ("errors" in baseVal) {
|
|
1460
|
-
baseVal.errors.forEach((err) => {
|
|
1461
|
-
err.cause.stack.push({
|
|
1462
|
-
input: originalInput,
|
|
1463
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1464
|
-
key,
|
|
1465
|
-
function: "extractSNBT",
|
|
1466
|
-
});
|
|
1467
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1468
|
-
});
|
|
1469
|
-
errors.push(...baseVal.errors);
|
|
1470
|
-
}
|
|
1471
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
if (val.startsWith("[") && val.endsWith("]")) {
|
|
1475
|
-
const baseVal = parseSNBTPrimitive(val, { ...options, isInnerStack: true });
|
|
1476
|
-
if ("errors" in baseVal) {
|
|
1477
|
-
baseVal.errors.forEach((err) => {
|
|
1478
|
-
err.cause.stack.push({
|
|
1479
|
-
input: originalInput,
|
|
1480
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1481
|
-
key,
|
|
1482
|
-
function: "extractSNBT",
|
|
1483
|
-
});
|
|
1484
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1485
|
-
});
|
|
1486
|
-
errors.push(...baseVal.errors);
|
|
1487
|
-
}
|
|
1488
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
const baseVal = parseSNBTPrimitive(val, { ...options, isInnerStack: true });
|
|
1492
|
-
if ("errors" in baseVal) {
|
|
1493
|
-
baseVal.errors.forEach((err) => {
|
|
1494
|
-
err.cause.stack.push({
|
|
1495
|
-
input: originalInput,
|
|
1496
|
-
positionInInput: originalInput.indexOf(input) + pos + originalVal.indexOf(val),
|
|
1497
|
-
key,
|
|
1498
|
-
function: "extractSNBT",
|
|
1499
|
-
});
|
|
1500
|
-
err.cause.position += originalInput.indexOf(input) + pos + originalVal.indexOf(val);
|
|
1501
|
-
});
|
|
1502
|
-
errors.push(...baseVal.errors);
|
|
1503
|
-
}
|
|
1504
|
-
return "errors" in baseVal ? baseVal.value : baseVal;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
let readingKey: boolean = resultType === "list" ? false : true;
|
|
1508
|
-
let bracketTypeStack: ("list" | "compound")[] = [resultType];
|
|
1509
|
-
let i: number = 0;
|
|
1510
|
-
for (i; i < input.length; i++) {
|
|
1511
|
-
const c: string | undefined = input[i];
|
|
1512
|
-
|
|
1513
|
-
if (c === '"' || c === "'") {
|
|
1514
|
-
if (inString === c) inString = null;
|
|
1515
|
-
else if (!inString) inString = c;
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
if (!inString) {
|
|
1519
|
-
if (c === ":" && readingKey) {
|
|
1520
|
-
readingKey = false;
|
|
1521
|
-
continue;
|
|
1522
|
-
} else if (c === "," && depth === 0) {
|
|
1523
|
-
resultType !== "list" || currentValue.trim() !== "" ? commitPair() : ((currentKey = ""), (currentValue = ""), (currentValuePos = -1));
|
|
1524
|
-
if (resultType === "compound") readingKey = true;
|
|
1525
|
-
continue;
|
|
1526
|
-
} else if (c === "{" || c === "[") {
|
|
1527
|
-
bracketTypeStack.push(c === "{" ? "compound" : "list");
|
|
1528
|
-
depth++;
|
|
1529
|
-
} else if (c === "}" || c === "]") {
|
|
1530
|
-
bracketTypeStack.pop();
|
|
1531
|
-
depth--;
|
|
1532
|
-
if ((options.stopAtNegativeDepth ?? true) && depth < 0) {
|
|
1533
|
-
i++;
|
|
1534
|
-
break;
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
if (readingKey) currentKey += c;
|
|
1540
|
-
else {
|
|
1541
|
-
currentValue += c;
|
|
1542
|
-
if (currentValuePos === -1 && depth === 0) currentValuePos = i;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
resultType !== "list" || currentValue.trim() !== "" ? commitPair() : ((currentKey = ""), (currentValue = ""), (currentValuePos = -1));
|
|
1547
|
-
|
|
1548
|
-
if (resultType === "list") {
|
|
1549
|
-
const baseVal = parseList(
|
|
1550
|
-
listResult.map(([v]: [value: string, pos: number]): string => v),
|
|
1551
|
-
{ ...options, isInnerStack: true }
|
|
1552
|
-
);
|
|
1553
|
-
if ("errors" in baseVal) {
|
|
1554
|
-
baseVal.errors.forEach((err) => {
|
|
1555
|
-
const lastStackItem = err.cause.stack.at(-1)! as Extract<SNBTParseErrorCauseStackItem, { function: "parseTypedArray" }>;
|
|
1556
|
-
// console.log(listResult, lastStackItem, lastStackItem.inputItemIndex, err);
|
|
1557
|
-
const baseOffset: number = listResult[lastStackItem.inputItemIndex]![1];
|
|
1558
|
-
err.cause.stack.push({
|
|
1559
|
-
input: originalInput,
|
|
1560
|
-
positionInInput: originalInput.indexOf(input) + baseOffset,
|
|
1561
|
-
function: "extractSNBT",
|
|
1562
|
-
});
|
|
1563
|
-
err.cause.position += originalInput.indexOf(input) + baseOffset;
|
|
1564
|
-
});
|
|
1565
|
-
errors.push(...baseVal.errors);
|
|
1566
|
-
}
|
|
1567
|
-
if (!options.isInnerStack) {
|
|
1568
|
-
errors.forEach((err: SNBTParseError): void => {
|
|
1569
|
-
err.isResolved = true;
|
|
1570
|
-
err.originalInput = originalInput;
|
|
1571
|
-
});
|
|
1572
|
-
}
|
|
1573
|
-
return {
|
|
1574
|
-
value: "errors" in baseVal ? baseVal.value : baseVal,
|
|
1575
|
-
remaining: originalInput.slice(i + originalInput.indexOf(input)),
|
|
1576
|
-
startPos: originalInput.indexOf(input),
|
|
1577
|
-
endPos: i + originalInput.indexOf(input),
|
|
1578
|
-
...(options.keepGoingAfterError ? { errors } : undefined),
|
|
1579
|
-
};
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
if (!options.isInnerStack) {
|
|
1583
|
-
errors.forEach((err) => {
|
|
1584
|
-
err.isResolved = true;
|
|
1585
|
-
err.originalInput = originalInput;
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
return {
|
|
1590
|
-
value: NBT.comp(
|
|
1591
|
-
Object.fromEntries(
|
|
1592
|
-
Object.entries(result)
|
|
1593
|
-
.map(([k, v]: [string, any]): [string | undefined, any] => [
|
|
1594
|
-
((): string | undefined => {
|
|
1595
|
-
try {
|
|
1596
|
-
return parseFormattedKey(k.trim());
|
|
1597
|
-
} catch {
|
|
1598
|
-
const error = new SNBTParseError("Invalid SNBT key: " + k.trim(), {
|
|
1599
|
-
cause: {
|
|
1600
|
-
position: k.indexOf(k.trim()),
|
|
1601
|
-
stack: [
|
|
1602
|
-
{
|
|
1603
|
-
function: "parseSNBTCompoundString",
|
|
1604
|
-
input: k,
|
|
1605
|
-
positionInInput: k.indexOf(k.trim()),
|
|
1606
|
-
key: k.trim(),
|
|
1607
|
-
error: {
|
|
1608
|
-
type: "InvalidSNBTKey",
|
|
1609
|
-
raw: k.trim(),
|
|
1610
|
-
},
|
|
1611
|
-
},
|
|
1612
|
-
],
|
|
1613
|
-
},
|
|
1614
|
-
});
|
|
1615
|
-
if (options.keepGoingAfterError) errors.push(error);
|
|
1616
|
-
else throw error;
|
|
1617
|
-
return undefined;
|
|
1618
|
-
}
|
|
1619
|
-
})(),
|
|
1620
|
-
v,
|
|
1621
|
-
])
|
|
1622
|
-
.filter((v: [string | undefined, any]): v is [string, any] => v[0] !== undefined)
|
|
1623
|
-
)
|
|
1624
|
-
),
|
|
1625
|
-
remaining: originalInput.slice(i + originalInput.indexOf(input)),
|
|
1626
|
-
startPos: originalInput.indexOf(input),
|
|
1627
|
-
endPos: i + originalInput.indexOf(input),
|
|
1628
|
-
...(options.keepGoingAfterError ? { errors } : undefined),
|
|
1629
|
-
};
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
// console.log(
|
|
1633
|
-
// parseSNBTCompoundString(`{
|
|
1634
|
-
// id: SculkCatalyst,
|
|
1635
|
-
// isMovable: 1b,
|
|
1636
|
-
// x: -345,
|
|
1637
|
-
// y: -40,
|
|
1638
|
-
// z: 449
|
|
1639
|
-
// }`)
|
|
1640
|
-
// );
|
|
1641
|
-
|
|
1642
|
-
// console.log(
|
|
1643
|
-
// prismarineToSNBT(
|
|
1644
|
-
// parseSNBTCompoundString(`{
|
|
1645
|
-
// "id": SculkCatalyst,
|
|
1646
|
-
// "isMovable": 1b,
|
|
1647
|
-
// "x": -345,
|
|
1648
|
-
// "y": -40,
|
|
1649
|
-
// "z": 449
|
|
1650
|
-
// }`)
|
|
1651
|
-
// )
|
|
1652
|
-
// );
|
|
1653
|
-
|
|
1654
|
-
// console.log(
|
|
1655
|
-
// snbtToPrismarine(
|
|
1656
|
-
// prismarineToSNBT(
|
|
1657
|
-
// parseSNBTCompoundString(`{
|
|
1658
|
-
// "id": SculkCatalyst,
|
|
1659
|
-
// "isMovable": 1b,
|
|
1660
|
-
// "x": -345,
|
|
1661
|
-
// "y": -40,
|
|
1662
|
-
// "z": 449
|
|
1663
|
-
// }`)
|
|
1664
|
-
// )
|
|
1665
|
-
// )
|
|
1666
|
-
// );
|
|
1667
|
-
|
|
1668
|
-
// console.log(
|
|
1669
|
-
// prettyPrintSNBT(
|
|
1670
|
-
// prismarineToSNBT(
|
|
1671
|
-
// parseSNBTCompoundString(`{
|
|
1672
|
-
// "id": SculkCatalyst,
|
|
1673
|
-
// "isMovable": 1b,
|
|
1674
|
-
// "x": -345,
|
|
1675
|
-
// "y": -40,
|
|
1676
|
-
// "z": 449
|
|
1677
|
-
// }`)
|
|
1678
|
-
// ),
|
|
1679
|
-
// { indent: 4, inlineArrays: false }
|
|
1680
|
-
// )
|
|
1681
|
-
// );
|
|
1682
|
-
|
|
1683
|
-
/**
|
|
1684
|
-
* Converts SNBT-like JSON into Prismarine-NBT JSON.
|
|
1685
|
-
*
|
|
1686
|
-
* @param snbt The SNBT-like JSON to convert.
|
|
1687
|
-
* @returns The Prismarine-NBT JSON.
|
|
1688
|
-
*/
|
|
1689
|
-
export function snbtToPrismarine(snbt: any): Compound {
|
|
1690
|
-
const value: Record<string, NBT.Tags[NBT.TagType]> = {};
|
|
1691
|
-
|
|
1692
|
-
for (const [key, raw] of Object.entries(snbt)) {
|
|
1693
|
-
if (Array.isArray(raw)) {
|
|
1694
|
-
if (raw.length > 0 && typeof raw[0] === "object" && !Array.isArray(raw[0])) {
|
|
1695
|
-
value[key] = {
|
|
1696
|
-
type: "list",
|
|
1697
|
-
value: {
|
|
1698
|
-
type: "compound",
|
|
1699
|
-
value: raw.map((entry: any) => {
|
|
1700
|
-
const inner: Record<string, NBT.Tags[NBT.TagType]> = {};
|
|
1701
|
-
for (const [k, v] of Object.entries(entry)) {
|
|
1702
|
-
inner[k] = snbtToPrismarine({ [k]: v });
|
|
1703
|
-
}
|
|
1704
|
-
return inner;
|
|
1705
|
-
}),
|
|
1706
|
-
},
|
|
1707
|
-
};
|
|
1708
|
-
} else {
|
|
1709
|
-
const parsed: NBT.Tags[NBT.TagType][] = raw.map((v) => parseSNBTPrimitive(v));
|
|
1710
|
-
const types = new Set(parsed.map((p: NBT.Tags[NBT.TagType]): `${NBT.TagType}` => p.type));
|
|
1711
|
-
if (types.size === 1) {
|
|
1712
|
-
const type: `${NBT.TagType}` = [...types][0]!;
|
|
1713
|
-
switch (type) {
|
|
1714
|
-
case "byte":
|
|
1715
|
-
value[key] = NBT.byteArray(parsed.map((p: NBT.Tags[NBT.TagType]): number => p.value as number));
|
|
1716
|
-
continue;
|
|
1717
|
-
case "short":
|
|
1718
|
-
value[key] = NBT.shortArray(parsed.map((p: NBT.Tags[NBT.TagType]): number => p.value as number));
|
|
1719
|
-
continue;
|
|
1720
|
-
case "int":
|
|
1721
|
-
value[key] = NBT.intArray(parsed.map((p: NBT.Tags[NBT.TagType]): number => p.value as number));
|
|
1722
|
-
continue;
|
|
1723
|
-
case "long":
|
|
1724
|
-
value[key] = NBT.longArray(parsed.map((p: NBT.Tags[NBT.TagType]): [high: number, low: number] => p.value as [number, number]));
|
|
1725
|
-
continue;
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
value[key] = {
|
|
1729
|
-
type: "list",
|
|
1730
|
-
value: {
|
|
1731
|
-
type: parsed[0]?.type ?? "int",
|
|
1732
|
-
value: parsed.map((p: NBT.Tags[NBT.TagType]): NBT.Tags[NBT.TagType]["value"] => p.value),
|
|
1733
|
-
},
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
} else if (typeof raw === "object" && raw !== null) {
|
|
1737
|
-
if ("__typed" in raw && "value" in raw) {
|
|
1738
|
-
value[key] = parseTypedArray(
|
|
1739
|
-
raw.__typed as any,
|
|
1740
|
-
(raw.value as any[]).map((v: any): string => (typeof v === "string" ? v : String(v)))
|
|
1741
|
-
);
|
|
1742
|
-
} else {
|
|
1743
|
-
value[key] = snbtToPrismarine(raw);
|
|
1744
|
-
}
|
|
1745
|
-
} else {
|
|
1746
|
-
value[key] = parseSNBTPrimitive(raw);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
return NBT.comp(value);
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
function formatString(s: string): string {
|
|
1754
|
-
const unquotedPattern = /^[A-Za-z_][A-Za-z0-9_\-\+\.]*$/;
|
|
1755
|
-
if (unquotedPattern.test(s)) return s;
|
|
1756
|
-
|
|
1757
|
-
let quote: '"' | "'" = '"';
|
|
1758
|
-
if (!s.includes("'") && s.includes('"')) {
|
|
1759
|
-
quote = "'";
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
const escaped: string = s
|
|
1763
|
-
.replace(/\\/g, "\\\\")
|
|
1764
|
-
.replace(new RegExp(quote, "g"), "\\" + quote)
|
|
1765
|
-
.replace(/\n/g, "\\n")
|
|
1766
|
-
.replace(/\r/g, "\\r")
|
|
1767
|
-
.replace(/\t/g, "\\t")
|
|
1768
|
-
.replace(/\x08/g, "\\b")
|
|
1769
|
-
.replace(/\f/g, "\\f")
|
|
1770
|
-
.replaceAll(/([\0-\x1f\u2028\u2029\ud800-\udfff])/g, (string: string): string => {
|
|
1771
|
-
return `\\u${string.codePointAt(0)!.toString(16).padStart(4, "0")}`;
|
|
1772
|
-
});
|
|
1773
|
-
|
|
1774
|
-
return `${quote}${escaped}${quote}`;
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
function formatKey(key: string): string {
|
|
1778
|
-
const unquotedPattern = /^[A-Za-z0-9_\-\+\.]+$/;
|
|
1779
|
-
if (unquotedPattern.test(key)) return key;
|
|
1780
|
-
return formatString(key);
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
function parseFormattedString(string: string): string {
|
|
1784
|
-
return string.startsWith('"') && string.endsWith('"')
|
|
1785
|
-
? JSON.parse(string)
|
|
1786
|
-
: string.startsWith("'") && string.endsWith("'")
|
|
1787
|
-
? JSON.parse('"' + string.replaceAll('"', '\\"').slice(1, -1) + '"')
|
|
1788
|
-
: /^[A-Za-z_][A-Za-z0-9_\-\+\.]*$/.test(string)
|
|
1789
|
-
? string
|
|
1790
|
-
: ((): never => {
|
|
1791
|
-
throw new SyntaxError("Invalid SNBT string: " + string);
|
|
1792
|
-
})();
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
function parseFormattedKey(key: string): string {
|
|
1796
|
-
return /^[A-Za-z0-9_\-\+\.]+$/.test(key)
|
|
1797
|
-
? key
|
|
1798
|
-
: key.startsWith('"') && key.endsWith('"')
|
|
1799
|
-
? JSON.parse(key)
|
|
1800
|
-
: key.startsWith("'") && key.endsWith("'")
|
|
1801
|
-
? JSON.parse('"' + key.replaceAll('"', '\\"').slice(1, -1) + '"')
|
|
1802
|
-
: /^[A-Za-z_][A-Za-z0-9_\-\+\.]*$/.test(key)
|
|
1803
|
-
? key
|
|
1804
|
-
: ((): never => {
|
|
1805
|
-
throw new SyntaxError("Invalid SNBT key: " + key);
|
|
1806
|
-
})();
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
/**
|
|
1810
|
-
* The tag types of SNBT-like JSON.
|
|
1811
|
-
*/
|
|
1812
|
-
export enum SNBTLikeJSONTagType {
|
|
1813
|
-
/**
|
|
1814
|
-
* A byte.
|
|
1815
|
-
*/
|
|
1816
|
-
Byte = "byte",
|
|
1817
|
-
/**
|
|
1818
|
-
* A short.
|
|
1819
|
-
*/
|
|
1820
|
-
Short = "short",
|
|
1821
|
-
/**
|
|
1822
|
-
* An integer.
|
|
1823
|
-
*/
|
|
1824
|
-
Int = "int",
|
|
1825
|
-
/**
|
|
1826
|
-
* A long.
|
|
1827
|
-
*/
|
|
1828
|
-
Long = "long",
|
|
1829
|
-
/**
|
|
1830
|
-
* A float.
|
|
1831
|
-
*/
|
|
1832
|
-
Float = "float",
|
|
1833
|
-
/**
|
|
1834
|
-
* A double.
|
|
1835
|
-
*/
|
|
1836
|
-
Double = "double",
|
|
1837
|
-
/**
|
|
1838
|
-
* A string.
|
|
1839
|
-
*/
|
|
1840
|
-
String = "string",
|
|
1841
|
-
/**
|
|
1842
|
-
* A list.
|
|
1843
|
-
*/
|
|
1844
|
-
List = "list",
|
|
1845
|
-
/**
|
|
1846
|
-
* A compound.
|
|
1847
|
-
*/
|
|
1848
|
-
Compound = "compound",
|
|
1849
|
-
/**
|
|
1850
|
-
* An array of bytes.
|
|
1851
|
-
*/
|
|
1852
|
-
ByteArray = "byteArray",
|
|
1853
|
-
/**
|
|
1854
|
-
* An array of shorts.
|
|
1855
|
-
*/
|
|
1856
|
-
ShortArray = "shortArray",
|
|
1857
|
-
/**
|
|
1858
|
-
* An array of integers.
|
|
1859
|
-
*/
|
|
1860
|
-
IntArray = "intArray",
|
|
1861
|
-
/**
|
|
1862
|
-
* An array of longs.
|
|
1863
|
-
*/
|
|
1864
|
-
LongArray = "longArray",
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
/**
|
|
1868
|
-
* The types of tag types of SNBT-like JSON.
|
|
1869
|
-
*/
|
|
1870
|
-
export type SNBTLikeJSONTags = {
|
|
1871
|
-
/**
|
|
1872
|
-
* A byte.
|
|
1873
|
-
*/
|
|
1874
|
-
[SNBTLikeJSONTagType.Byte]: `${bigint}${"b" | "B"}`;
|
|
1875
|
-
/**
|
|
1876
|
-
* A short.
|
|
1877
|
-
*/
|
|
1878
|
-
[SNBTLikeJSONTagType.Short]: `${bigint}${"s" | "S"}`;
|
|
1879
|
-
/**
|
|
1880
|
-
* An integer.
|
|
1881
|
-
*/
|
|
1882
|
-
[SNBTLikeJSONTagType.Int]: `${bigint}${"" | "i" | "I"}`;
|
|
1883
|
-
/**
|
|
1884
|
-
* A long.
|
|
1885
|
-
*/
|
|
1886
|
-
[SNBTLikeJSONTagType.Long]: `${bigint}${"l" | "L"}`;
|
|
1887
|
-
/**
|
|
1888
|
-
* A float.
|
|
1889
|
-
*/
|
|
1890
|
-
[SNBTLikeJSONTagType.Float]: `${number}${"f" | "F"}`;
|
|
1891
|
-
/**
|
|
1892
|
-
* A double.
|
|
1893
|
-
*/
|
|
1894
|
-
[SNBTLikeJSONTagType.Double]: `${number}${"d" | "D"}`;
|
|
1895
|
-
/**
|
|
1896
|
-
* A string.
|
|
1897
|
-
*/
|
|
1898
|
-
[SNBTLikeJSONTagType.String]: `"${string}"` | `'${string}'` | `${string}`;
|
|
1899
|
-
/**
|
|
1900
|
-
* A list.
|
|
1901
|
-
*/
|
|
1902
|
-
[SNBTLikeJSONTagType.List]: SNBTLikeJSONList<SNBTLikeJSONTagType>;
|
|
1903
|
-
/**
|
|
1904
|
-
* A compound.
|
|
1905
|
-
*/
|
|
1906
|
-
[SNBTLikeJSONTagType.Compound]: SNBTLikeJSONCompound;
|
|
1907
|
-
/**
|
|
1908
|
-
* An array of bytes.
|
|
1909
|
-
*/
|
|
1910
|
-
[SNBTLikeJSONTagType.ByteArray]: SNBTLikeJSONTypedArray<TypedArrayLetterTypes.B>;
|
|
1911
|
-
/**
|
|
1912
|
-
* An array of shorts.
|
|
1913
|
-
*/
|
|
1914
|
-
[SNBTLikeJSONTagType.ShortArray]: SNBTLikeJSONTypedArray<TypedArrayLetterTypes.S>;
|
|
1915
|
-
/**
|
|
1916
|
-
* An array of integers.
|
|
1917
|
-
*/
|
|
1918
|
-
[SNBTLikeJSONTagType.IntArray]: SNBTLikeJSONTypedArray<TypedArrayLetterTypes.I>;
|
|
1919
|
-
/**
|
|
1920
|
-
* An array of longs.
|
|
1921
|
-
*/
|
|
1922
|
-
[SNBTLikeJSONTagType.LongArray]: SNBTLikeJSONTypedArray<TypedArrayLetterTypes.L>;
|
|
1923
|
-
};
|
|
1924
|
-
|
|
1925
|
-
/**
|
|
1926
|
-
* A list tag of SNBT-like JSON.
|
|
1927
|
-
*/
|
|
1928
|
-
export type SNBTLikeJSONList<T extends SNBTLikeJSONTagType> = SNBTLikeJSONTags[T][];
|
|
1929
|
-
|
|
1930
|
-
/**
|
|
1931
|
-
* A typed array tag of SNBT-like JSON.
|
|
1932
|
-
*/
|
|
1933
|
-
export type SNBTLikeJSONTypedArray<T extends TypedArrayLetterTypes> = {
|
|
1934
|
-
/**
|
|
1935
|
-
* The type of the typed array.
|
|
1936
|
-
*/
|
|
1937
|
-
__typed: keyof OmitNeverValueKeys<{ [key in keyof typeof TypedArrayLetterTypes]: (typeof TypedArrayLetterTypes)[key] extends T ? key : never }>;
|
|
1938
|
-
/**
|
|
1939
|
-
* The values of the typed array.
|
|
1940
|
-
*/
|
|
1941
|
-
value: SNBTLikeJSONTags[VerifyConstraint<T, `${SNBTLikeJSONTagType}`>][];
|
|
1942
|
-
};
|
|
1943
|
-
|
|
1944
|
-
/**
|
|
1945
|
-
* A compound tag of SNBT-like JSON.
|
|
1946
|
-
*/
|
|
1947
|
-
export type SNBTLikeJSONCompound = { [key: string]: SNBTLikeJSONTag<SNBTLikeJSONTagType> };
|
|
1948
|
-
|
|
1949
|
-
/**
|
|
1950
|
-
* An SNBT-like JSON tag.
|
|
1951
|
-
*/
|
|
1952
|
-
export type SNBTLikeJSONTag<T extends `${SNBTLikeJSONTagType}` | SNBTLikeJSONTagType> = { [key in SNBTLikeJSONTagType as `${key}`]: SNBTLikeJSONTags[key] }[T];
|
|
1953
|
-
|
|
1954
|
-
/**
|
|
1955
|
-
* Converts Prismarine-NBT JSON to SNBT-like JSON.
|
|
1956
|
-
*
|
|
1957
|
-
* @param nbt The Prismarine-NBT JSON to convert.
|
|
1958
|
-
* @returns The resulting SNBT-like JSON.
|
|
1959
|
-
*/
|
|
1960
|
-
export function prismarineToSNBT<T extends NBT.Tags[NBT.TagType]>(nbt: T): SNBTLikeJSONTag<T["type"]> {
|
|
1961
|
-
if (nbt.type === "compound") {
|
|
1962
|
-
const obj: SNBTLikeJSONCompound = {};
|
|
1963
|
-
for (const [key, val] of Object.entries(nbt.value)) {
|
|
1964
|
-
obj[key] = prismarineToSNBT(val!);
|
|
1965
|
-
}
|
|
1966
|
-
return obj as any;
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
if (nbt.type === "list") {
|
|
1970
|
-
if (nbt.value.type === "compound") {
|
|
1971
|
-
return nbt.value.value.map((entry: any): Record<string, any> => {
|
|
1972
|
-
const obj: Record<string, any> = {};
|
|
1973
|
-
for (const [k, v] of Object.entries(entry)) {
|
|
1974
|
-
obj[k] = prismarineToSNBT(v as any);
|
|
1975
|
-
}
|
|
1976
|
-
return obj;
|
|
1977
|
-
}) as any;
|
|
1978
|
-
} else {
|
|
1979
|
-
return nbt.value.value.map((v) => prismarineToSNBT({ type: nbt.value.type, value: v as any })) as any;
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
|
|
1983
|
-
switch (nbt.type) {
|
|
1984
|
-
case "byteArray":
|
|
1985
|
-
return { __typed: "B", value: nbt.value.map((v: number): `${number}b` => `${v}b` as const) } as any;
|
|
1986
|
-
case "shortArray":
|
|
1987
|
-
return { __typed: "S", value: nbt.value.map((v: number): `${number}s` => `${v}s` as const) } as any;
|
|
1988
|
-
case "intArray":
|
|
1989
|
-
return { __typed: "I", value: nbt.value.map((v: number): `${number}` => `${v}` as const) } as any;
|
|
1990
|
-
case "longArray":
|
|
1991
|
-
return { __typed: "L", value: nbt.value.map((v: [number, number]): `${bigint}L` => `${toLong(v)}L` as const) } as any;
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
switch (nbt.type) {
|
|
1995
|
-
case "byte":
|
|
1996
|
-
return `${nbt.value}b` as const as any;
|
|
1997
|
-
case "short":
|
|
1998
|
-
return `${nbt.value}s` as const as any;
|
|
1999
|
-
case "int":
|
|
2000
|
-
return nbt.value as any;
|
|
2001
|
-
case "long":
|
|
2002
|
-
return `${toLong(nbt.value).toString()}L` as const as any;
|
|
2003
|
-
case "float":
|
|
2004
|
-
return `${nbt.value}f` as const as any;
|
|
2005
|
-
case "double":
|
|
2006
|
-
return `${nbt.value}d` as const as any;
|
|
2007
|
-
case "string":
|
|
2008
|
-
return formatString(nbt.value) as any;
|
|
2009
|
-
}
|
|
2010
|
-
|
|
2011
|
-
return (nbt as any).value;
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
/**
|
|
2015
|
-
* Converts a long in bigint format to tuple form.
|
|
2016
|
-
*
|
|
2017
|
-
* @param longVal The long as a bigint.
|
|
2018
|
-
* @returns The long as a tuple.
|
|
2019
|
-
*/
|
|
2020
|
-
export function toLongParts(longVal: bigint): [high: number, low: number] {
|
|
2021
|
-
const low: number = Number(longVal & 0xffffffffn) | 0;
|
|
2022
|
-
const high: number = Number((longVal >> 32n) & 0xffffffffn) | 0;
|
|
2023
|
-
return [high, low];
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
/**
|
|
2027
|
-
* Converts a long as a string to a tuple.
|
|
2028
|
-
*
|
|
2029
|
-
* @param literal A long as a string.
|
|
2030
|
-
* @returns The long as a tuple.
|
|
2031
|
-
*/
|
|
2032
|
-
export function parseLong(literal: string): [high: number, low: number] {
|
|
2033
|
-
const numStr: string = literal.slice(0, -1);
|
|
2034
|
-
const value: bigint = BigInt(numStr);
|
|
2035
|
-
return toLongParts(value);
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
/**
|
|
2039
|
-
* Converts a long in tuple form to bigint form.
|
|
2040
|
-
*
|
|
2041
|
-
* @param param0 The long as a tuple.
|
|
2042
|
-
* @returns The long as a bigint.
|
|
2043
|
-
*/
|
|
2044
|
-
export function toLong([high, low]: [high: number, low: number]): bigint {
|
|
2045
|
-
const lo: bigint = BigInt(low) & BigInt(0xffffffff);
|
|
2046
|
-
const hi: bigint = BigInt(high);
|
|
2047
|
-
return (hi << 32n) | lo;
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
|
-
/**
|
|
2051
|
-
* Options for {@link prettyPrintSNBT}.
|
|
2052
|
-
*/
|
|
2053
|
-
export interface PrettyPrintOptions {
|
|
2054
|
-
/**
|
|
2055
|
-
* Number of spaces to indent.
|
|
2056
|
-
*
|
|
2057
|
-
* @default 2
|
|
2058
|
-
*/
|
|
2059
|
-
indent?: number;
|
|
2060
|
-
/**
|
|
2061
|
-
* Whether to inline arrays if possible.
|
|
2062
|
-
*
|
|
2063
|
-
* @default true
|
|
2064
|
-
*/
|
|
2065
|
-
inlineArrays?: boolean;
|
|
2066
|
-
/**
|
|
2067
|
-
* Maximum length of an inline array.
|
|
2068
|
-
*
|
|
2069
|
-
* @default 40
|
|
2070
|
-
*/
|
|
2071
|
-
maxInlineLength?: number;
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
/**
|
|
2075
|
-
* Pretty-prints SNBT.
|
|
2076
|
-
*
|
|
2077
|
-
* @param obj The SNBT-like JSON to pretty-print.
|
|
2078
|
-
* @param options The options to use.
|
|
2079
|
-
* @returns The pretty-printed SNBT.
|
|
2080
|
-
*
|
|
2081
|
-
* @example
|
|
2082
|
-
* ```typescript
|
|
2083
|
-
* prettyPrintSNBT(prismarineToSNBT({
|
|
2084
|
-
* type: "compound",
|
|
2085
|
-
* value: {
|
|
2086
|
-
* enabled: {
|
|
2087
|
-
* type: "byte",
|
|
2088
|
-
* value: 1
|
|
2089
|
-
* }
|
|
2090
|
-
* }
|
|
2091
|
-
* }))
|
|
2092
|
-
* ```
|
|
2093
|
-
*/
|
|
2094
|
-
export function prettyPrintSNBT(obj: any, options: PrettyPrintOptions = {}): string {
|
|
2095
|
-
const { indent = 2, inlineArrays = true, maxInlineLength = 40 } = options;
|
|
2096
|
-
|
|
2097
|
-
function format(value: any, level: number): string {
|
|
2098
|
-
const pad: string = " ".repeat(level * indent);
|
|
2099
|
-
|
|
2100
|
-
if (value && typeof value === "object" && value.__typed) {
|
|
2101
|
-
const type: "B" | "S" | "I" | "L" = value.__typed;
|
|
2102
|
-
const inner: string = value.value.join(", ");
|
|
2103
|
-
return `[${type}; ${inner}]`;
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
|
-
if (Array.isArray(value)) {
|
|
2107
|
-
if (value.length === 0) return "[]";
|
|
2108
|
-
const inline = `[${value.map((v: any): string => format(v, 0)).join(", ")}]`;
|
|
2109
|
-
if (inlineArrays && inline.length <= maxInlineLength && !value.some((v: any): boolean => typeof v === "object")) {
|
|
2110
|
-
return inline;
|
|
2111
|
-
}
|
|
2112
|
-
return "[\n" + value.map((v: any): string => pad + " ".repeat(indent) + format(v, level + 1)).join(",\n") + "\n" + pad + "]";
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
if (typeof value === "object" && value !== null) {
|
|
2116
|
-
const entries: [string, unknown][] = Object.entries(value);
|
|
2117
|
-
if (entries.length === 0) return "{}";
|
|
2118
|
-
|
|
2119
|
-
return (
|
|
2120
|
-
"{\n" +
|
|
2121
|
-
entries.map(([k, v]: [string, unknown]): string => pad + " ".repeat(indent) + `${formatKey(k)}: ${format(v, level + 1)}`).join(",\n") +
|
|
2122
|
-
"\n" +
|
|
2123
|
-
pad +
|
|
2124
|
-
"}"
|
|
2125
|
-
);
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
return String(value);
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
return format(obj, 0);
|
|
2132
|
-
}
|