@sfmc-bds/bds-tools 0.2.0-beta.12 → 0.2.0-beta.14
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/dist/level-dat.d.ts +21 -3
- package/dist/level-dat.d.ts.map +1 -1
- package/dist/level-dat.js +221 -57
- package/dist/level-dat.js.map +2 -2
- package/dist/level-dat.test.js +50 -3
- package/dist/level-dat.test.js.map +2 -2
- package/dist/world-packs.d.ts +28 -1
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +93 -16
- package/dist/world-packs.js.map +3 -3
- package/dist/world-packs.test.js +22 -0
- package/dist/world-packs.test.js.map +2 -2
- package/package.json +3 -3
package/dist/level-dat.d.ts
CHANGED
|
@@ -34,11 +34,23 @@ export interface NbtTagMeta {
|
|
|
34
34
|
endOffset: number;
|
|
35
35
|
value?: unknown;
|
|
36
36
|
}
|
|
37
|
+
export interface KnownExperimentDefinition {
|
|
38
|
+
id: string;
|
|
39
|
+
nbtKey: string;
|
|
40
|
+
nameZh: string;
|
|
41
|
+
nameEn: string;
|
|
42
|
+
aliases?: string[];
|
|
43
|
+
isRootTag?: boolean;
|
|
44
|
+
extraExpTags?: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare const KNOWN_EXPERIMENTS: readonly KnownExperimentDefinition[];
|
|
47
|
+
export declare function resolveExperimentDefinition(query: string): KnownExperimentDefinition | null;
|
|
37
48
|
export interface LevelDatExperimentsInfo {
|
|
38
49
|
experimentsEverUsed: boolean;
|
|
39
50
|
savedWithToggledExperiments: boolean;
|
|
40
51
|
experiments: Record<string, number | boolean>;
|
|
41
52
|
hasBetaApis: boolean;
|
|
53
|
+
knownExperiments: Record<string, boolean>;
|
|
42
54
|
}
|
|
43
55
|
export interface LevelDatMutateResult {
|
|
44
56
|
success: boolean;
|
|
@@ -46,6 +58,7 @@ export interface LevelDatMutateResult {
|
|
|
46
58
|
backupPath?: string;
|
|
47
59
|
error?: string;
|
|
48
60
|
previousBetaApisState?: boolean;
|
|
61
|
+
enabledExperiments?: string[];
|
|
49
62
|
}
|
|
50
63
|
/**
|
|
51
64
|
* 遍历并跳过指定类型的 NBT 载荷,返回下一个 tag 的 offset。
|
|
@@ -73,15 +86,20 @@ export declare function parseBedrockLevelDat(buf: Buffer): {
|
|
|
73
86
|
*/
|
|
74
87
|
export declare function readLevelDatExperiments(levelDatPath: string): LevelDatExperimentsInfo | null;
|
|
75
88
|
/**
|
|
76
|
-
* 为指定 level.dat
|
|
89
|
+
* 为指定 level.dat 开启指定的实验性开关(若未指定或传 "all" 则开启全部已知实验性开关)。
|
|
77
90
|
*
|
|
78
91
|
* 保证:
|
|
79
92
|
* - 自动备份原文件至 `${levelDatPath}.bak`;
|
|
80
|
-
* - 自动更新 experiments
|
|
93
|
+
* - 自动更新 root 与 experiments 复合标签中的目标 byte tags;
|
|
81
94
|
* - 自动更新 experiments_ever_used = 1b 与 saved_with_toggled_experiments = 1b;
|
|
82
95
|
* - 精确校准 8 字节文件头部的 dataLength;
|
|
83
96
|
* - 原子写操作(.tmp -> rename);
|
|
84
|
-
* -
|
|
97
|
+
* - 若所请求开关已全部处于开启状态,直接返回 changed: false,不破坏文件。
|
|
98
|
+
*/
|
|
99
|
+
export declare function enableExperimentsInLevelDat(levelDatPath: string, targetExperimentIds?: string[] | "all"): Promise<LevelDatMutateResult>;
|
|
100
|
+
/**
|
|
101
|
+
* 为指定 level.dat 开启 Beta APIs(gametest)。
|
|
102
|
+
* 保持向后兼容,委托给 enableExperimentsInLevelDat。
|
|
85
103
|
*/
|
|
86
104
|
export declare function enableBetaApisInLevelDat(levelDatPath: string): Promise<LevelDatMutateResult>;
|
|
87
105
|
//# sourceMappingURL=level-dat.d.ts.map
|
package/dist/level-dat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"level-dat.d.ts","sourceRoot":"","sources":["../src/level-dat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,2BAA2B,EAAE,OAAO,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAC9C,WAAW,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"level-dat.d.ts","sourceRoot":"","sources":["../src/level-dat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,QAAQ,IAAI,CAAC;AAC1B,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,yBAAyB,EA8DxD,CAAC;AAEX,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI,CAY3F;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,2BAA2B,EAAE,OAAO,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA2DhF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,mBAAmB,EAAE,MAAM,GAC1B;IAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CA6ClE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;CACvB,CAwBA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CA2E5F;AAkCD;;;;;;;;;;GAUG;AACH,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,MAAM,EACpB,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,GACrC,OAAO,CAAC,oBAAoB,CAAC,CA+M/B;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAElG"}
|
package/dist/level-dat.js
CHANGED
|
@@ -12,6 +12,82 @@ const TAG_LIST = 9;
|
|
|
12
12
|
const TAG_COMPOUND = 10;
|
|
13
13
|
const TAG_INT_ARRAY = 11;
|
|
14
14
|
const TAG_LONG_ARRAY = 12;
|
|
15
|
+
const KNOWN_EXPERIMENTS = [
|
|
16
|
+
{
|
|
17
|
+
id: "gametest",
|
|
18
|
+
nbtKey: "gametest",
|
|
19
|
+
nameZh: "\u6D4B\u8BD5\u7248 API",
|
|
20
|
+
nameEn: "Beta APIs (gametest)",
|
|
21
|
+
aliases: ["beta", "beta_apis", "betapis", "gametest"]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "upcoming_creator_features",
|
|
25
|
+
nbtKey: "upcoming_creator_features",
|
|
26
|
+
nameZh: "\u5373\u5C06\u63A8\u51FA\u7684\u521B\u4F5C\u8005\u529F\u80FD",
|
|
27
|
+
nameEn: "Upcoming Creator Features",
|
|
28
|
+
aliases: ["upcoming", "creator_features", "upcoming_features"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "experimental_creator_cameras",
|
|
32
|
+
nbtKey: "experimental_creator_cameras",
|
|
33
|
+
nameZh: "\u521B\u5EFA\u8005\u7167\u76F8\u673A\u7684\u5B9E\u9A8C\u6027\u529F\u80FD",
|
|
34
|
+
nameEn: "Experimental Creator Cameras",
|
|
35
|
+
aliases: ["camera", "cameras", "creator_cameras", "creator_camera"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "voxel_shapes",
|
|
39
|
+
nbtKey: "voxel_shapes",
|
|
40
|
+
nameZh: "\u5B9E\u9A8C\u6027Voxel\u5F62\u72B6\u7279\u5F81",
|
|
41
|
+
nameEn: "Experimental Voxel Shapes",
|
|
42
|
+
aliases: ["voxel", "voxel_shape", "experimental_voxel_shapes", "voxelshapes"]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "villager_trades_rebalance",
|
|
46
|
+
nbtKey: "villager_trades_rebalance",
|
|
47
|
+
nameZh: "\u6751\u6C11\u8D38\u6613\u518D\u5E73\u8861",
|
|
48
|
+
nameEn: "Villager Trade Rebalance",
|
|
49
|
+
aliases: ["villager", "trades", "villager_trades", "trade_rebalance"]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "drop_3_2026",
|
|
53
|
+
nbtKey: "drop_3_2026",
|
|
54
|
+
nameZh: "2026\u5E74\u7B2C3\u6B21\u66F4\u65B0",
|
|
55
|
+
nameEn: "Drop 3 of 2026",
|
|
56
|
+
aliases: [
|
|
57
|
+
"drop3",
|
|
58
|
+
"drop_3",
|
|
59
|
+
"drop_3_2026",
|
|
60
|
+
"wilderness",
|
|
61
|
+
"wilderness_bound",
|
|
62
|
+
"update_2026_drop_3",
|
|
63
|
+
"update_26_3",
|
|
64
|
+
"update_drop_3"
|
|
65
|
+
],
|
|
66
|
+
extraExpTags: ["update_2026_drop_3"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "education",
|
|
70
|
+
nbtKey: "educationFeaturesEnabled",
|
|
71
|
+
nameZh: "Minecraft Education \u529F\u80FD",
|
|
72
|
+
nameEn: "Minecraft Education Features",
|
|
73
|
+
isRootTag: true,
|
|
74
|
+
aliases: ["edu", "education", "education_edition", "educationfeatures"],
|
|
75
|
+
extraExpTags: ["education"]
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
function resolveExperimentDefinition(query) {
|
|
79
|
+
const q = query.trim().toLowerCase().replace(/[-_]/g, "");
|
|
80
|
+
for (const def of KNOWN_EXPERIMENTS) {
|
|
81
|
+
if (def.id.replace(/[-_]/g, "") === q) return def;
|
|
82
|
+
if (def.nbtKey.replace(/[-_]/g, "") === q) return def;
|
|
83
|
+
if (def.aliases) {
|
|
84
|
+
for (const a of def.aliases) {
|
|
85
|
+
if (a.replace(/[-_]/g, "") === q) return def;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
15
91
|
function skipNbtPayload(buf, offset, type) {
|
|
16
92
|
switch (type) {
|
|
17
93
|
case TAG_BYTE:
|
|
@@ -172,11 +248,30 @@ function readLevelDatExperiments(levelDatPath) {
|
|
|
172
248
|
}
|
|
173
249
|
}
|
|
174
250
|
}
|
|
251
|
+
const knownExperiments = {};
|
|
252
|
+
for (const def of KNOWN_EXPERIMENTS) {
|
|
253
|
+
let enabled = false;
|
|
254
|
+
if (def.isRootTag) {
|
|
255
|
+
const rootTag = parsed.rootChildren.get(def.nbtKey);
|
|
256
|
+
if (rootTag && Number(rootTag.value) === 1) {
|
|
257
|
+
enabled = true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const keysToCheck = [def.nbtKey, ...def.aliases ?? []];
|
|
261
|
+
for (const k of keysToCheck) {
|
|
262
|
+
if (experiments[k] === 1) {
|
|
263
|
+
enabled = true;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
knownExperiments[def.id] = enabled;
|
|
268
|
+
}
|
|
175
269
|
return {
|
|
176
270
|
experimentsEverUsed,
|
|
177
271
|
savedWithToggledExperiments,
|
|
178
272
|
experiments,
|
|
179
|
-
hasBetaApis
|
|
273
|
+
hasBetaApis,
|
|
274
|
+
knownExperiments
|
|
180
275
|
};
|
|
181
276
|
} catch {
|
|
182
277
|
return null;
|
|
@@ -204,83 +299,145 @@ function createCompoundTagBuffer(name, byteTags) {
|
|
|
204
299
|
nameBuf.copy(header, 3);
|
|
205
300
|
return Buffer.concat([header, ...childBuffers]);
|
|
206
301
|
}
|
|
207
|
-
async function
|
|
302
|
+
async function enableExperimentsInLevelDat(levelDatPath, targetExperimentIds) {
|
|
208
303
|
if (!fs.existsSync(levelDatPath)) {
|
|
209
304
|
return { success: false, changed: false, error: `File not found: ${levelDatPath}` };
|
|
210
305
|
}
|
|
211
306
|
try {
|
|
212
307
|
const raw = await fs.promises.readFile(levelDatPath);
|
|
213
308
|
const parsed = parseBedrockLevelDat(raw);
|
|
309
|
+
let targetDefs = [];
|
|
310
|
+
if (!targetExperimentIds || targetExperimentIds === "all") {
|
|
311
|
+
targetDefs = [...KNOWN_EXPERIMENTS];
|
|
312
|
+
} else {
|
|
313
|
+
const seen = /* @__PURE__ */ new Set();
|
|
314
|
+
for (const id of targetExperimentIds) {
|
|
315
|
+
const def = resolveExperimentDefinition(id);
|
|
316
|
+
if (def && !seen.has(def.id)) {
|
|
317
|
+
seen.add(def.id);
|
|
318
|
+
targetDefs.push(def);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (targetDefs.length === 0) {
|
|
322
|
+
return { success: true, changed: false, enabledExperiments: [] };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
214
325
|
const expCompoundTag = parsed.rootChildren.get("experiments");
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
326
|
+
const existingExpChildren = expCompoundTag && expCompoundTag.type === TAG_COMPOUND ? parseCompoundChildren(raw, expCompoundTag.valueOffset).children : /* @__PURE__ */ new Map();
|
|
327
|
+
let needsChange = false;
|
|
328
|
+
const enabledIds = [];
|
|
329
|
+
if (!expCompoundTag) {
|
|
330
|
+
needsChange = true;
|
|
331
|
+
} else {
|
|
332
|
+
const expEverUsed = existingExpChildren.get("experiments_ever_used");
|
|
333
|
+
if (!expEverUsed || Number(expEverUsed.value) !== 1) needsChange = true;
|
|
334
|
+
const expSaved = existingExpChildren.get("saved_with_toggled_experiments");
|
|
335
|
+
if (!expSaved || Number(expSaved.value) !== 1) needsChange = true;
|
|
336
|
+
}
|
|
337
|
+
const rootEverUsed = parsed.rootChildren.get("experiments_ever_used");
|
|
338
|
+
if (rootEverUsed && Number(rootEverUsed.value) !== 1) needsChange = true;
|
|
339
|
+
const rootSaved = parsed.rootChildren.get("saved_with_toggled_experiments");
|
|
340
|
+
if (rootSaved && Number(rootSaved.value) !== 1) needsChange = true;
|
|
341
|
+
let previousBetaApisState = false;
|
|
342
|
+
const currentGametest = existingExpChildren.get("gametest");
|
|
343
|
+
if (currentGametest && Number(currentGametest.value) === 1) {
|
|
344
|
+
previousBetaApisState = true;
|
|
345
|
+
}
|
|
346
|
+
for (const def of targetDefs) {
|
|
347
|
+
enabledIds.push(def.id);
|
|
348
|
+
if (def.isRootTag) {
|
|
349
|
+
const rootTag = parsed.rootChildren.get(def.nbtKey);
|
|
350
|
+
if (!rootTag || Number(rootTag.value) !== 1) {
|
|
351
|
+
needsChange = true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!def.isRootTag) {
|
|
355
|
+
const tag = existingExpChildren.get(def.nbtKey);
|
|
356
|
+
if (!tag || Number(tag.value) !== 1) {
|
|
357
|
+
needsChange = true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (def.extraExpTags) {
|
|
361
|
+
for (const extra of def.extraExpTags) {
|
|
362
|
+
const tag = existingExpChildren.get(extra);
|
|
363
|
+
if (!tag || Number(tag.value) !== 1) {
|
|
364
|
+
needsChange = true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
221
367
|
}
|
|
222
368
|
}
|
|
223
|
-
if (
|
|
369
|
+
if (!needsChange) {
|
|
224
370
|
return {
|
|
225
371
|
success: true,
|
|
226
372
|
changed: false,
|
|
227
|
-
previousBetaApisState
|
|
373
|
+
previousBetaApisState,
|
|
374
|
+
enabledExperiments: enabledIds
|
|
228
375
|
};
|
|
229
376
|
}
|
|
230
377
|
const backupPath = `${levelDatPath}.bak`;
|
|
231
378
|
await fs.promises.copyFile(levelDatPath, backupPath);
|
|
379
|
+
let mutatedRaw = Buffer.from(raw);
|
|
232
380
|
let nbtBody;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const beforeEnd = raw.subarray(8, rootEndTagIndex);
|
|
241
|
-
const afterEnd = raw.subarray(rootEndTagIndex);
|
|
242
|
-
nbtBody = Buffer.concat([beforeEnd, insertCompoundBuf, afterEnd]);
|
|
243
|
-
} else {
|
|
244
|
-
const { children: expChildren, compoundEndOffset: expEndOffset } = parseCompoundChildren(
|
|
245
|
-
raw,
|
|
246
|
-
expCompoundTag.valueOffset
|
|
247
|
-
);
|
|
248
|
-
const expEndTagIndex = expEndOffset - 1;
|
|
249
|
-
const toAppend = [];
|
|
250
|
-
let mutatedRaw = Buffer.from(raw);
|
|
251
|
-
const gametestTag = expChildren.get("gametest");
|
|
252
|
-
if (gametestTag) {
|
|
253
|
-
mutatedRaw.writeInt8(1, gametestTag.valueOffset);
|
|
254
|
-
} else {
|
|
255
|
-
toAppend.push(createByteTagBuffer("gametest", 1));
|
|
256
|
-
}
|
|
257
|
-
const everUsedTag = expChildren.get("experiments_ever_used");
|
|
258
|
-
if (everUsedTag) {
|
|
259
|
-
mutatedRaw.writeInt8(1, everUsedTag.valueOffset);
|
|
260
|
-
} else {
|
|
261
|
-
toAppend.push(createByteTagBuffer("experiments_ever_used", 1));
|
|
381
|
+
const requiredExpTags = {
|
|
382
|
+
experiments_ever_used: 1,
|
|
383
|
+
saved_with_toggled_experiments: 1
|
|
384
|
+
};
|
|
385
|
+
for (const def of targetDefs) {
|
|
386
|
+
if (!def.isRootTag) {
|
|
387
|
+
requiredExpTags[def.nbtKey] = 1;
|
|
262
388
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
toAppend.push(createByteTagBuffer("saved_with_toggled_experiments", 1));
|
|
389
|
+
if (def.extraExpTags) {
|
|
390
|
+
for (const extra of def.extraExpTags) {
|
|
391
|
+
requiredExpTags[extra] = 1;
|
|
392
|
+
}
|
|
268
393
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
394
|
+
}
|
|
395
|
+
const rootNeedsEducation = targetDefs.some((d) => d.id === "education");
|
|
396
|
+
if (rootEverUsed) {
|
|
397
|
+
mutatedRaw.writeInt8(1, rootEverUsed.valueOffset);
|
|
398
|
+
}
|
|
399
|
+
if (rootSaved) {
|
|
400
|
+
mutatedRaw.writeInt8(1, rootSaved.valueOffset);
|
|
401
|
+
}
|
|
402
|
+
if (rootNeedsEducation) {
|
|
403
|
+
const eduTag = parsed.rootChildren.get("educationFeaturesEnabled");
|
|
404
|
+
if (eduTag) {
|
|
405
|
+
mutatedRaw.writeInt8(1, eduTag.valueOffset);
|
|
272
406
|
}
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
mutatedRaw.writeInt8(
|
|
407
|
+
const optOut = parsed.rootChildren.get("has_opted_out_of_education");
|
|
408
|
+
if (optOut) {
|
|
409
|
+
mutatedRaw.writeInt8(0, optOut.valueOffset);
|
|
276
410
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
411
|
+
}
|
|
412
|
+
const rootTagsToAppend = [];
|
|
413
|
+
if (rootNeedsEducation && !parsed.rootChildren.has("educationFeaturesEnabled")) {
|
|
414
|
+
rootTagsToAppend.push(createByteTagBuffer("educationFeaturesEnabled", 1));
|
|
415
|
+
}
|
|
416
|
+
if (!expCompoundTag) {
|
|
417
|
+
const newExpCompoundBuf = createCompoundTagBuffer("experiments", requiredExpTags);
|
|
418
|
+
const rootEndTagIndex = parsed.rootEndOffset - 1;
|
|
419
|
+
const beforeEnd = mutatedRaw.subarray(8, rootEndTagIndex);
|
|
420
|
+
const afterEnd = mutatedRaw.subarray(rootEndTagIndex);
|
|
421
|
+
nbtBody = Buffer.concat([beforeEnd, ...rootTagsToAppend, newExpCompoundBuf, afterEnd]);
|
|
422
|
+
} else {
|
|
423
|
+
const { compoundEndOffset: expEndOffset } = parseCompoundChildren(raw, expCompoundTag.valueOffset);
|
|
424
|
+
const expEndTagIndex = expEndOffset - 1;
|
|
425
|
+
const rootEndTagIndex = parsed.rootEndOffset - 1;
|
|
426
|
+
const expTagsToAppend = [];
|
|
427
|
+
for (const [name, val] of Object.entries(requiredExpTags)) {
|
|
428
|
+
const existing = existingExpChildren.get(name);
|
|
429
|
+
if (existing) {
|
|
430
|
+
mutatedRaw.writeInt8(val, existing.valueOffset);
|
|
431
|
+
} else {
|
|
432
|
+
expTagsToAppend.push(createByteTagBuffer(name, val));
|
|
433
|
+
}
|
|
283
434
|
}
|
|
435
|
+
const part1 = mutatedRaw.subarray(8, expEndTagIndex);
|
|
436
|
+
const part2 = Buffer.concat(expTagsToAppend);
|
|
437
|
+
const part3 = mutatedRaw.subarray(expEndTagIndex, rootEndTagIndex);
|
|
438
|
+
const part4 = Buffer.concat(rootTagsToAppend);
|
|
439
|
+
const part5 = mutatedRaw.subarray(rootEndTagIndex);
|
|
440
|
+
nbtBody = Buffer.concat([part1, part2, part3, part4, part5]);
|
|
284
441
|
}
|
|
285
442
|
const newHeader = Buffer.alloc(8);
|
|
286
443
|
newHeader.writeUInt32LE(parsed.version, 0);
|
|
@@ -293,7 +450,8 @@ async function enableBetaApisInLevelDat(levelDatPath) {
|
|
|
293
450
|
success: true,
|
|
294
451
|
changed: true,
|
|
295
452
|
backupPath,
|
|
296
|
-
previousBetaApisState
|
|
453
|
+
previousBetaApisState,
|
|
454
|
+
enabledExperiments: enabledIds
|
|
297
455
|
};
|
|
298
456
|
} catch (err) {
|
|
299
457
|
return {
|
|
@@ -303,7 +461,11 @@ async function enableBetaApisInLevelDat(levelDatPath) {
|
|
|
303
461
|
};
|
|
304
462
|
}
|
|
305
463
|
}
|
|
464
|
+
async function enableBetaApisInLevelDat(levelDatPath) {
|
|
465
|
+
return await enableExperimentsInLevelDat(levelDatPath, ["gametest"]);
|
|
466
|
+
}
|
|
306
467
|
export {
|
|
468
|
+
KNOWN_EXPERIMENTS,
|
|
307
469
|
TAG_BYTE,
|
|
308
470
|
TAG_BYTE_ARRAY,
|
|
309
471
|
TAG_COMPOUND,
|
|
@@ -318,9 +480,11 @@ export {
|
|
|
318
480
|
TAG_SHORT,
|
|
319
481
|
TAG_STRING,
|
|
320
482
|
enableBetaApisInLevelDat,
|
|
483
|
+
enableExperimentsInLevelDat,
|
|
321
484
|
parseBedrockLevelDat,
|
|
322
485
|
parseCompoundChildren,
|
|
323
486
|
readLevelDatExperiments,
|
|
487
|
+
resolveExperimentDefinition,
|
|
324
488
|
skipNbtPayload
|
|
325
489
|
};
|
|
326
490
|
//# sourceMappingURL=level-dat.js.map
|
package/dist/level-dat.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/level-dat.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * level-dat.ts \u2014 Minecraft \u57FA\u5CA9\u7248\u4E16\u754C\u5B58\u6863 level.dat \u5C0F\u7AEF\u5E8F NBT \u89E3\u6790\u4E0E\u5B9E\u9A8C\u6027\u73A9\u6CD5\u5B89\u5168\u4FEE\u6539\u5668\n *\n * \u57FA\u5CA9\u7248 level.dat \u89C4\u8303\uFF1A\n * - Header\uFF088 \u5B57\u8282\uFF09\uFF1A\n * - [0..3] uint32 LE\uFF1A\u5B58\u50A8\u683C\u5F0F\u7248\u672C\uFF08\u5F53\u524D\u7248\u672C\u901A\u5E38\u4E3A 10\uFF09\n * - [4..7] uint32 LE\uFF1A\u6709\u6548 NBT \u6570\u636E\u5B57\u8282\u957F\u5EA6\uFF08data_length\uFF09\n * - Payload\uFF1A\u672A\u538B\u7F29\u7684\u5C0F\u7AEF\u5E8F\uFF08Little-Endian\uFF09\u6807\u51C6 NBT \u590D\u5408\u6807\u7B7E\uFF08TAG_Compound\uFF09\n *\n * \u6838\u5FC3\u80FD\u529B\uFF1A\n * - \u539F\u751F\u65E0\u5916\u90E8\u9ED1\u76D2\u4F9D\u8D56\uFF0CBuffer \u7EA7\u89E3\u6790\u4E0E\u7CBE\u786E\u5B57\u8282\u4F4D\u79FB\n * - \u8BFB\u53D6\u4E16\u754C\u5B9E\u9A8C\u6027\u73A9\u6CD5\u7684\u5F00\u542F\u72B6\u6001\uFF08\u7279\u522B\u662F Beta APIs / gametest\uFF09\n * - \u5B89\u5168\u6CE8\u5165\u6216\u5207\u6362\u5B9E\u9A8C\u6027\u5F00\u5173\u5E76\u81EA\u52A8\u5907\u4EFD\u539F\u6587\u4EF6\uFF08.bak\uFF09\u4E0E\u6821\u51C6 Header data_length\n */\n\nimport fs from \"node:fs\";\n\nexport const TAG_END = 0;\nexport const TAG_BYTE = 1;\nexport const TAG_SHORT = 2;\nexport const TAG_INT = 3;\nexport const TAG_LONG = 4;\nexport const TAG_FLOAT = 5;\nexport const TAG_DOUBLE = 6;\nexport const TAG_BYTE_ARRAY = 7;\nexport const TAG_STRING = 8;\nexport const TAG_LIST = 9;\nexport const TAG_COMPOUND = 10;\nexport const TAG_INT_ARRAY = 11;\nexport const TAG_LONG_ARRAY = 12;\n\nexport interface NbtTagMeta {\n type: number;\n name: string;\n startOffset: number; // \u5305\u542B tagType \u7684\u8D77\u59CB\u7EDD\u5BF9 offset\n nameOffset: number; // \u6807\u7B7E\u540D\u79F0\u8D77\u59CB offset\n valueOffset: number; // \u8F7D\u8377\u8D77\u59CB offset\n endOffset: number; // \u6807\u7B7E\u7ED3\u675F\u7EDD\u5BF9 offset\n value?: unknown;\n}\n\nexport interface LevelDatExperimentsInfo {\n experimentsEverUsed: boolean;\n savedWithToggledExperiments: boolean;\n experiments: Record<string, number | boolean>;\n hasBetaApis: boolean;\n}\n\nexport interface LevelDatMutateResult {\n success: boolean;\n changed: boolean;\n backupPath?: string;\n error?: string;\n previousBetaApisState?: boolean;\n}\n\n/**\n * \u904D\u5386\u5E76\u8DF3\u8FC7\u6307\u5B9A\u7C7B\u578B\u7684 NBT \u8F7D\u8377\uFF0C\u8FD4\u56DE\u4E0B\u4E00\u4E2A tag \u7684 offset\u3002\n */\nexport function skipNbtPayload(buf: Buffer, offset: number, type: number): number {\n switch (type) {\n case TAG_BYTE:\n return offset + 1;\n case TAG_SHORT:\n return offset + 2;\n case TAG_INT:\n case TAG_FLOAT:\n return offset + 4;\n case TAG_LONG:\n case TAG_DOUBLE:\n return offset + 8;\n case TAG_BYTE_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Byte_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len);\n }\n case TAG_STRING: {\n if (offset + 2 > buf.length) throw new Error(\"Unexpected EOF reading TAG_String length\");\n const len = buf.readUInt16LE(offset);\n return offset + 2 + len;\n }\n case TAG_LIST: {\n if (offset + 5 > buf.length) throw new Error(\"Unexpected EOF reading TAG_List header\");\n const elemType = buf.readUInt8(offset);\n const count = buf.readInt32LE(offset + 1);\n let cur = offset + 5;\n for (let i = 0; i < count; i++) {\n cur = skipNbtPayload(buf, cur, elemType);\n }\n return cur;\n }\n case TAG_COMPOUND: {\n let cur = offset;\n while (cur < buf.length) {\n const t = buf.readUInt8(cur);\n if (t === TAG_END) {\n return cur + 1;\n }\n if (cur + 3 > buf.length) throw new Error(\"Unexpected EOF reading compound child header\");\n const nameLen = buf.readUInt16LE(cur + 1);\n const childValOffset = cur + 3 + nameLen;\n cur = skipNbtPayload(buf, childValOffset, t);\n }\n return cur;\n }\n case TAG_INT_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Int_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len) * 4;\n }\n case TAG_LONG_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Long_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len) * 8;\n }\n default:\n throw new Error(`Unsupported or unknown NBT tag type: ${type} at offset ${offset}`);\n }\n}\n\n/**\n * \u89E3\u6790\u590D\u5408\u6807\u7B7E\u5185\u90E8\u6240\u6709\u76F4\u63A5\u5B50\u6807\u7B7E\u7684\u5143\u6570\u636E\u6620\u5C04\u3002\n */\nexport function parseCompoundChildren(\n buf: Buffer,\n compoundValueOffset: number\n): { children: Map<string, NbtTagMeta>; compoundEndOffset: number } {\n const children = new Map<string, NbtTagMeta>();\n let cur = compoundValueOffset;\n\n while (cur < buf.length) {\n const tagType = buf.readUInt8(cur);\n if (tagType === TAG_END) {\n return { children, compoundEndOffset: cur + 1 };\n }\n if (cur + 3 > buf.length) {\n throw new Error(`Unexpected EOF reading named tag header at offset ${cur}`);\n }\n const nameLen = buf.readUInt16LE(cur + 1);\n const nameOffset = cur + 3;\n if (nameOffset + nameLen > buf.length) {\n throw new Error(`Unexpected EOF reading tag name at offset ${cur}`);\n }\n const name = buf.toString(\"utf8\", nameOffset, nameOffset + nameLen);\n const valOffset = nameOffset + nameLen;\n const endOffset = skipNbtPayload(buf, valOffset, tagType);\n\n let val: unknown = undefined;\n if (tagType === TAG_BYTE) {\n val = buf.readInt8(valOffset);\n } else if (tagType === TAG_INT) {\n val = buf.readInt32LE(valOffset);\n } else if (tagType === TAG_STRING) {\n const sLen = buf.readUInt16LE(valOffset);\n val = buf.toString(\"utf8\", valOffset + 2, valOffset + 2 + sLen);\n }\n\n children.set(name, {\n type: tagType,\n name,\n startOffset: cur,\n nameOffset,\n valueOffset: valOffset,\n endOffset,\n value: val,\n });\n\n cur = endOffset;\n }\n\n return { children, compoundEndOffset: cur };\n}\n\n/**\n * \u89E3\u6790\u57FA\u5CA9\u7248 level.dat \u7684\u5B8C\u6574\u6587\u4EF6\u7ED3\u6784\u5E76\u5B9A\u4F4D\u6839 Compound\u3002\n */\nexport function parseBedrockLevelDat(buf: Buffer): {\n version: number;\n dataLength: number;\n rootCompoundName: string;\n rootChildren: Map<string, NbtTagMeta>;\n rootEndOffset: number;\n} {\n if (buf.length < 8) {\n throw new Error(\"Invalid level.dat: buffer smaller than 8-byte header\");\n }\n const version = buf.readUInt32LE(0);\n const dataLength = buf.readUInt32LE(4);\n\n // \u6839\u6807\u7B7E\u5FC5\u987B\u662F TAG_Compound\n if (buf.length < 9 || buf.readUInt8(8) !== TAG_COMPOUND) {\n throw new Error(\"Invalid level.dat: root tag is not TAG_Compound\");\n }\n const rootNameLen = buf.readUInt16LE(9);\n const rootCompoundName = buf.toString(\"utf8\", 11, 11 + rootNameLen);\n const rootValueOffset = 11 + rootNameLen;\n\n const { children: rootChildren, compoundEndOffset: rootEndOffset } = parseCompoundChildren(buf, rootValueOffset);\n\n return {\n version,\n dataLength,\n rootCompoundName,\n rootChildren,\n rootEndOffset,\n };\n}\n\n/**\n * \u8BFB\u53D6\u4E16\u754C\u5B58\u6863 level.dat \u7684\u5B9E\u9A8C\u6027\u914D\u7F6E\u3002\u82E5\u89E3\u6790\u5931\u8D25\u6216\u6587\u4EF6\u4E0D\u5B58\u5728\u8FD4\u56DE null\u3002\n */\nexport function readLevelDatExperiments(levelDatPath: string): LevelDatExperimentsInfo | null {\n if (!fs.existsSync(levelDatPath)) return null;\n try {\n const buf = fs.readFileSync(levelDatPath);\n const parsed = parseBedrockLevelDat(buf);\n\n let experimentsEverUsed = false;\n const everUsedTag = parsed.rootChildren.get(\"experiments_ever_used\");\n if (everUsedTag && everUsedTag.type === TAG_BYTE && Number(everUsedTag.value) === 1) {\n experimentsEverUsed = true;\n }\n\n let savedWithToggledExperiments = false;\n const savedTag = parsed.rootChildren.get(\"saved_with_toggled_experiments\");\n if (savedTag && savedTag.type === TAG_BYTE && Number(savedTag.value) === 1) {\n savedWithToggledExperiments = true;\n }\n\n const experiments: Record<string, number | boolean> = {};\n let hasBetaApis = false;\n\n const expCompoundTag = parsed.rootChildren.get(\"experiments\");\n if (expCompoundTag && expCompoundTag.type === TAG_COMPOUND) {\n const expChildren = parseCompoundChildren(buf, expCompoundTag.valueOffset).children;\n\n // \u90E8\u5206\u7248\u672C experiments_ever_used \u4E5F\u53EF\u80FD\u4F4D\u4E8E experiments \u5185\u90E8\n const innerEverUsed = expChildren.get(\"experiments_ever_used\");\n if (innerEverUsed && Number(innerEverUsed.value) === 1) {\n experimentsEverUsed = true;\n }\n const innerSaved = expChildren.get(\"saved_with_toggled_experiments\");\n if (innerSaved && Number(innerSaved.value) === 1) {\n savedWithToggledExperiments = true;\n }\n\n for (const [name, meta] of expChildren.entries()) {\n if (meta.type === TAG_BYTE) {\n const num = Number(meta.value);\n experiments[name] = num;\n if (name === \"gametest\" && num === 1) {\n hasBetaApis = true;\n }\n }\n }\n }\n\n return {\n experimentsEverUsed,\n savedWithToggledExperiments,\n experiments,\n hasBetaApis,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * \u6784\u9020\u4E00\u4E2A TAG_Byte \u7684\u4E8C\u8FDB\u5236 Buffer\u3002\n */\nfunction createByteTagBuffer(name: string, value: number): Buffer {\n const nameBuf = Buffer.from(name, \"utf8\");\n const buf = Buffer.alloc(1 + 2 + nameBuf.length + 1);\n buf.writeUInt8(TAG_BYTE, 0);\n buf.writeUInt16LE(nameBuf.length, 1);\n nameBuf.copy(buf, 3);\n buf.writeInt8(value, 3 + nameBuf.length);\n return buf;\n}\n\n/**\n * \u6784\u9020\u4E00\u4E2A\u5305\u542B\u6307\u5B9A\u952E\u503C\u5BF9\u7684 TAG_Compound Buffer\u3002\n */\nfunction createCompoundTagBuffer(name: string, byteTags: Record<string, number>): Buffer {\n const nameBuf = Buffer.from(name, \"utf8\");\n const childBuffers: Buffer[] = [];\n for (const [k, v] of Object.entries(byteTags)) {\n childBuffers.push(createByteTagBuffer(k, v));\n }\n childBuffers.push(Buffer.from([TAG_END]));\n\n const header = Buffer.alloc(1 + 2 + nameBuf.length);\n header.writeUInt8(TAG_COMPOUND, 0);\n header.writeUInt16LE(nameBuf.length, 1);\n nameBuf.copy(header, 3);\n\n return Buffer.concat([header, ...childBuffers]);\n}\n\n/**\n * \u4E3A\u6307\u5B9A level.dat \u5F00\u542F Beta APIs\uFF08gametest\uFF09\u3002\n *\n * \u4FDD\u8BC1\uFF1A\n * - \u81EA\u52A8\u5907\u4EFD\u539F\u6587\u4EF6\u81F3 `${levelDatPath}.bak`\uFF1B\n * - \u81EA\u52A8\u66F4\u65B0 experiments \u590D\u5408\u6807\u7B7E\u4E2D\u7684 gametest = 1b\uFF1B\n * - \u81EA\u52A8\u66F4\u65B0 experiments_ever_used = 1b \u4E0E saved_with_toggled_experiments = 1b\uFF1B\n * - \u7CBE\u786E\u6821\u51C6 8 \u5B57\u8282\u6587\u4EF6\u5934\u90E8\u7684 dataLength\uFF1B\n * - \u539F\u5B50\u5199\u64CD\u4F5C\uFF08.tmp -> rename\uFF09\uFF1B\n * - \u82E5\u5DF2\u5904\u4E8E\u5F00\u542F\u72B6\u6001\uFF0C\u76F4\u63A5\u8FD4\u56DE changed: false\uFF0C\u4E0D\u7834\u574F\u6587\u4EF6\u3002\n */\nexport async function enableBetaApisInLevelDat(levelDatPath: string): Promise<LevelDatMutateResult> {\n if (!fs.existsSync(levelDatPath)) {\n return { success: false, changed: false, error: `File not found: ${levelDatPath}` };\n }\n\n try {\n const raw = await fs.promises.readFile(levelDatPath);\n const parsed = parseBedrockLevelDat(raw);\n\n const expCompoundTag = parsed.rootChildren.get(\"experiments\");\n let currentBetaState = false;\n\n if (expCompoundTag && expCompoundTag.type === TAG_COMPOUND) {\n const expChildren = parseCompoundChildren(raw, expCompoundTag.valueOffset).children;\n const gametest = expChildren.get(\"gametest\");\n if (gametest && Number(gametest.value) === 1) {\n currentBetaState = true;\n }\n }\n\n if (currentBetaState) {\n return {\n success: true,\n changed: false,\n previousBetaApisState: true,\n };\n }\n\n // \u9700\u8981\u8FDB\u884C\u5907\u4EFD\n const backupPath = `${levelDatPath}.bak`;\n await fs.promises.copyFile(levelDatPath, backupPath);\n\n let nbtBody: Buffer;\n\n if (!expCompoundTag) {\n // \u573A\u666F 1\uFF1A\u5B8C\u5168\u7F3A\u5931 experiments compound\uFF0C\u5728\u6839 compound \u7ED3\u675F\u6807\u7B7E\u524D\u8FFD\u52A0\u5B8C\u6574 experiments compound\n const insertCompoundBuf = createCompoundTagBuffer(\"experiments\", {\n experiments_ever_used: 1,\n saved_with_toggled_experiments: 1,\n gametest: 1,\n });\n\n // \u6839\u6807\u7B7E\u7ED3\u675F\u70B9\u5728 parsed.rootEndOffset - 1\uFF08TAG_END \u5B57\u8282\u6240\u5728\u4F4D\u7F6E\uFF09\n const rootEndTagIndex = parsed.rootEndOffset - 1;\n const beforeEnd = raw.subarray(8, rootEndTagIndex);\n const afterEnd = raw.subarray(rootEndTagIndex); // \u5305\u542B TAG_END \u53CA\u540E\u7EED\uFF08\u82E5\u6709\uFF09\n nbtBody = Buffer.concat([beforeEnd, insertCompoundBuf, afterEnd]);\n } else {\n // \u573A\u666F 2\uFF1A\u5DF2\u6709 experiments compound\uFF0C\u5728 experiments compound \u5185\u90E8\u66F4\u65B0\u6216\u63D2\u5165\u7F3A\u5931\u5B57\u6BB5\n const { children: expChildren, compoundEndOffset: expEndOffset } = parseCompoundChildren(\n raw,\n expCompoundTag.valueOffset\n );\n const expEndTagIndex = expEndOffset - 1; // \u5185\u90E8 TAG_END \u6240\u5728\u7EDD\u5BF9\u7D22\u5F15\n\n // \u7EC4\u88C5\u8981\u5728 experiments \u5185\u90E8\u8FFD\u52A0\u7684 tag\uFF08\u82E5\u4E0D\u5B58\u5728\uFF09\n const toAppend: Buffer[] = [];\n let mutatedRaw = Buffer.from(raw);\n\n // \u5904\u7406 gametest\n const gametestTag = expChildren.get(\"gametest\");\n if (gametestTag) {\n mutatedRaw.writeInt8(1, gametestTag.valueOffset);\n } else {\n toAppend.push(createByteTagBuffer(\"gametest\", 1));\n }\n\n // \u5904\u7406 experiments_ever_used\n const everUsedTag = expChildren.get(\"experiments_ever_used\");\n if (everUsedTag) {\n mutatedRaw.writeInt8(1, everUsedTag.valueOffset);\n } else {\n toAppend.push(createByteTagBuffer(\"experiments_ever_used\", 1));\n }\n\n // \u5904\u7406 saved_with_toggled_experiments\n const savedTag = expChildren.get(\"saved_with_toggled_experiments\");\n if (savedTag) {\n mutatedRaw.writeInt8(1, savedTag.valueOffset);\n } else {\n toAppend.push(createByteTagBuffer(\"saved_with_toggled_experiments\", 1));\n }\n\n // \u4E5F\u628A root \u5C42\u7684 experiments_ever_used \u8BBE\u4E3A 1\uFF08\u82E5\u5B58\u5728\uFF09\n const rootEverUsed = parsed.rootChildren.get(\"experiments_ever_used\");\n if (rootEverUsed) {\n mutatedRaw.writeInt8(1, rootEverUsed.valueOffset);\n }\n const rootSaved = parsed.rootChildren.get(\"saved_with_toggled_experiments\");\n if (rootSaved) {\n mutatedRaw.writeInt8(1, rootSaved.valueOffset);\n }\n\n if (toAppend.length > 0) {\n const beforeExpEnd = mutatedRaw.subarray(8, expEndTagIndex);\n const afterExpEnd = mutatedRaw.subarray(expEndTagIndex);\n nbtBody = Buffer.concat([beforeExpEnd, ...toAppend, afterExpEnd]);\n } else {\n nbtBody = mutatedRaw.subarray(8);\n }\n }\n\n // \u91CD\u65B0\u6784\u9020 8 \u5B57\u8282 header\n const newHeader = Buffer.alloc(8);\n newHeader.writeUInt32LE(parsed.version, 0);\n newHeader.writeUInt32LE(nbtBody.length, 4);\n\n const finalFileBuffer = Buffer.concat([newHeader, nbtBody]);\n\n // \u539F\u5B50\u66FF\u6362\n const tmpFile = `${levelDatPath}.${process.pid}.tmp`;\n await fs.promises.writeFile(tmpFile, finalFileBuffer);\n await fs.promises.rename(tmpFile, levelDatPath);\n\n return {\n success: true,\n changed: true,\n backupPath,\n previousBetaApisState: false,\n };\n } catch (err) {\n return {\n success: false,\n changed: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * level-dat.ts \u2014 Minecraft \u57FA\u5CA9\u7248\u4E16\u754C\u5B58\u6863 level.dat \u5C0F\u7AEF\u5E8F NBT \u89E3\u6790\u4E0E\u5B9E\u9A8C\u6027\u73A9\u6CD5\u5B89\u5168\u4FEE\u6539\u5668\n *\n * \u57FA\u5CA9\u7248 level.dat \u89C4\u8303\uFF1A\n * - Header\uFF088 \u5B57\u8282\uFF09\uFF1A\n * - [0..3] uint32 LE\uFF1A\u5B58\u50A8\u683C\u5F0F\u7248\u672C\uFF08\u5F53\u524D\u7248\u672C\u901A\u5E38\u4E3A 10\uFF09\n * - [4..7] uint32 LE\uFF1A\u6709\u6548 NBT \u6570\u636E\u5B57\u8282\u957F\u5EA6\uFF08data_length\uFF09\n * - Payload\uFF1A\u672A\u538B\u7F29\u7684\u5C0F\u7AEF\u5E8F\uFF08Little-Endian\uFF09\u6807\u51C6 NBT \u590D\u5408\u6807\u7B7E\uFF08TAG_Compound\uFF09\n *\n * \u6838\u5FC3\u80FD\u529B\uFF1A\n * - \u539F\u751F\u65E0\u5916\u90E8\u9ED1\u76D2\u4F9D\u8D56\uFF0CBuffer \u7EA7\u89E3\u6790\u4E0E\u7CBE\u786E\u5B57\u8282\u4F4D\u79FB\n * - \u8BFB\u53D6\u4E16\u754C\u5B9E\u9A8C\u6027\u73A9\u6CD5\u7684\u5F00\u542F\u72B6\u6001\uFF08\u7279\u522B\u662F Beta APIs / gametest\uFF09\n * - \u5B89\u5168\u6CE8\u5165\u6216\u5207\u6362\u5B9E\u9A8C\u6027\u5F00\u5173\u5E76\u81EA\u52A8\u5907\u4EFD\u539F\u6587\u4EF6\uFF08.bak\uFF09\u4E0E\u6821\u51C6 Header data_length\n */\n\nimport fs from \"node:fs\";\n\nexport const TAG_END = 0;\nexport const TAG_BYTE = 1;\nexport const TAG_SHORT = 2;\nexport const TAG_INT = 3;\nexport const TAG_LONG = 4;\nexport const TAG_FLOAT = 5;\nexport const TAG_DOUBLE = 6;\nexport const TAG_BYTE_ARRAY = 7;\nexport const TAG_STRING = 8;\nexport const TAG_LIST = 9;\nexport const TAG_COMPOUND = 10;\nexport const TAG_INT_ARRAY = 11;\nexport const TAG_LONG_ARRAY = 12;\n\nexport interface NbtTagMeta {\n type: number;\n name: string;\n startOffset: number; // \u5305\u542B tagType \u7684\u8D77\u59CB\u7EDD\u5BF9 offset\n nameOffset: number; // \u6807\u7B7E\u540D\u79F0\u8D77\u59CB offset\n valueOffset: number; // \u8F7D\u8377\u8D77\u59CB offset\n endOffset: number; // \u6807\u7B7E\u7ED3\u675F\u7EDD\u5BF9 offset\n value?: unknown;\n}\n\nexport interface KnownExperimentDefinition {\n id: string;\n nbtKey: string;\n nameZh: string;\n nameEn: string;\n aliases?: string[];\n isRootTag?: boolean;\n extraExpTags?: string[];\n}\n\nexport const KNOWN_EXPERIMENTS: readonly KnownExperimentDefinition[] = [\n {\n id: \"gametest\",\n nbtKey: \"gametest\",\n nameZh: \"\u6D4B\u8BD5\u7248 API\",\n nameEn: \"Beta APIs (gametest)\",\n aliases: [\"beta\", \"beta_apis\", \"betapis\", \"gametest\"],\n },\n {\n id: \"upcoming_creator_features\",\n nbtKey: \"upcoming_creator_features\",\n nameZh: \"\u5373\u5C06\u63A8\u51FA\u7684\u521B\u4F5C\u8005\u529F\u80FD\",\n nameEn: \"Upcoming Creator Features\",\n aliases: [\"upcoming\", \"creator_features\", \"upcoming_features\"],\n },\n {\n id: \"experimental_creator_cameras\",\n nbtKey: \"experimental_creator_cameras\",\n nameZh: \"\u521B\u5EFA\u8005\u7167\u76F8\u673A\u7684\u5B9E\u9A8C\u6027\u529F\u80FD\",\n nameEn: \"Experimental Creator Cameras\",\n aliases: [\"camera\", \"cameras\", \"creator_cameras\", \"creator_camera\"],\n },\n {\n id: \"voxel_shapes\",\n nbtKey: \"voxel_shapes\",\n nameZh: \"\u5B9E\u9A8C\u6027Voxel\u5F62\u72B6\u7279\u5F81\",\n nameEn: \"Experimental Voxel Shapes\",\n aliases: [\"voxel\", \"voxel_shape\", \"experimental_voxel_shapes\", \"voxelshapes\"],\n },\n {\n id: \"villager_trades_rebalance\",\n nbtKey: \"villager_trades_rebalance\",\n nameZh: \"\u6751\u6C11\u8D38\u6613\u518D\u5E73\u8861\",\n nameEn: \"Villager Trade Rebalance\",\n aliases: [\"villager\", \"trades\", \"villager_trades\", \"trade_rebalance\"],\n },\n {\n id: \"drop_3_2026\",\n nbtKey: \"drop_3_2026\",\n nameZh: \"2026\u5E74\u7B2C3\u6B21\u66F4\u65B0\",\n nameEn: \"Drop 3 of 2026\",\n aliases: [\n \"drop3\",\n \"drop_3\",\n \"drop_3_2026\",\n \"wilderness\",\n \"wilderness_bound\",\n \"update_2026_drop_3\",\n \"update_26_3\",\n \"update_drop_3\",\n ],\n extraExpTags: [\"update_2026_drop_3\"],\n },\n {\n id: \"education\",\n nbtKey: \"educationFeaturesEnabled\",\n nameZh: \"Minecraft Education \u529F\u80FD\",\n nameEn: \"Minecraft Education Features\",\n isRootTag: true,\n aliases: [\"edu\", \"education\", \"education_edition\", \"educationfeatures\"],\n extraExpTags: [\"education\"],\n },\n] as const;\n\nexport function resolveExperimentDefinition(query: string): KnownExperimentDefinition | null {\n const q = query.trim().toLowerCase().replace(/[-_]/g, \"\");\n for (const def of KNOWN_EXPERIMENTS) {\n if (def.id.replace(/[-_]/g, \"\") === q) return def;\n if (def.nbtKey.replace(/[-_]/g, \"\") === q) return def;\n if (def.aliases) {\n for (const a of def.aliases) {\n if (a.replace(/[-_]/g, \"\") === q) return def;\n }\n }\n }\n return null;\n}\n\nexport interface LevelDatExperimentsInfo {\n experimentsEverUsed: boolean;\n savedWithToggledExperiments: boolean;\n experiments: Record<string, number | boolean>;\n hasBetaApis: boolean;\n knownExperiments: Record<string, boolean>;\n}\n\nexport interface LevelDatMutateResult {\n success: boolean;\n changed: boolean;\n backupPath?: string;\n error?: string;\n previousBetaApisState?: boolean;\n enabledExperiments?: string[];\n}\n\n/**\n * \u904D\u5386\u5E76\u8DF3\u8FC7\u6307\u5B9A\u7C7B\u578B\u7684 NBT \u8F7D\u8377\uFF0C\u8FD4\u56DE\u4E0B\u4E00\u4E2A tag \u7684 offset\u3002\n */\nexport function skipNbtPayload(buf: Buffer, offset: number, type: number): number {\n switch (type) {\n case TAG_BYTE:\n return offset + 1;\n case TAG_SHORT:\n return offset + 2;\n case TAG_INT:\n case TAG_FLOAT:\n return offset + 4;\n case TAG_LONG:\n case TAG_DOUBLE:\n return offset + 8;\n case TAG_BYTE_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Byte_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len);\n }\n case TAG_STRING: {\n if (offset + 2 > buf.length) throw new Error(\"Unexpected EOF reading TAG_String length\");\n const len = buf.readUInt16LE(offset);\n return offset + 2 + len;\n }\n case TAG_LIST: {\n if (offset + 5 > buf.length) throw new Error(\"Unexpected EOF reading TAG_List header\");\n const elemType = buf.readUInt8(offset);\n const count = buf.readInt32LE(offset + 1);\n let cur = offset + 5;\n for (let i = 0; i < count; i++) {\n cur = skipNbtPayload(buf, cur, elemType);\n }\n return cur;\n }\n case TAG_COMPOUND: {\n let cur = offset;\n while (cur < buf.length) {\n const t = buf.readUInt8(cur);\n if (t === TAG_END) {\n return cur + 1;\n }\n if (cur + 3 > buf.length) throw new Error(\"Unexpected EOF reading compound child header\");\n const nameLen = buf.readUInt16LE(cur + 1);\n const childValOffset = cur + 3 + nameLen;\n cur = skipNbtPayload(buf, childValOffset, t);\n }\n return cur;\n }\n case TAG_INT_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Int_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len) * 4;\n }\n case TAG_LONG_ARRAY: {\n if (offset + 4 > buf.length) throw new Error(\"Unexpected EOF reading TAG_Long_Array length\");\n const len = buf.readInt32LE(offset);\n return offset + 4 + Math.max(0, len) * 8;\n }\n default:\n throw new Error(`Unsupported or unknown NBT tag type: ${type} at offset ${offset}`);\n }\n}\n\n/**\n * \u89E3\u6790\u590D\u5408\u6807\u7B7E\u5185\u90E8\u6240\u6709\u76F4\u63A5\u5B50\u6807\u7B7E\u7684\u5143\u6570\u636E\u6620\u5C04\u3002\n */\nexport function parseCompoundChildren(\n buf: Buffer,\n compoundValueOffset: number\n): { children: Map<string, NbtTagMeta>; compoundEndOffset: number } {\n const children = new Map<string, NbtTagMeta>();\n let cur = compoundValueOffset;\n\n while (cur < buf.length) {\n const tagType = buf.readUInt8(cur);\n if (tagType === TAG_END) {\n return { children, compoundEndOffset: cur + 1 };\n }\n if (cur + 3 > buf.length) {\n throw new Error(`Unexpected EOF reading named tag header at offset ${cur}`);\n }\n const nameLen = buf.readUInt16LE(cur + 1);\n const nameOffset = cur + 3;\n if (nameOffset + nameLen > buf.length) {\n throw new Error(`Unexpected EOF reading tag name at offset ${cur}`);\n }\n const name = buf.toString(\"utf8\", nameOffset, nameOffset + nameLen);\n const valOffset = nameOffset + nameLen;\n const endOffset = skipNbtPayload(buf, valOffset, tagType);\n\n let val: unknown = undefined;\n if (tagType === TAG_BYTE) {\n val = buf.readInt8(valOffset);\n } else if (tagType === TAG_INT) {\n val = buf.readInt32LE(valOffset);\n } else if (tagType === TAG_STRING) {\n const sLen = buf.readUInt16LE(valOffset);\n val = buf.toString(\"utf8\", valOffset + 2, valOffset + 2 + sLen);\n }\n\n children.set(name, {\n type: tagType,\n name,\n startOffset: cur,\n nameOffset,\n valueOffset: valOffset,\n endOffset,\n value: val,\n });\n\n cur = endOffset;\n }\n\n return { children, compoundEndOffset: cur };\n}\n\n/**\n * \u89E3\u6790\u57FA\u5CA9\u7248 level.dat \u7684\u5B8C\u6574\u6587\u4EF6\u7ED3\u6784\u5E76\u5B9A\u4F4D\u6839 Compound\u3002\n */\nexport function parseBedrockLevelDat(buf: Buffer): {\n version: number;\n dataLength: number;\n rootCompoundName: string;\n rootChildren: Map<string, NbtTagMeta>;\n rootEndOffset: number;\n} {\n if (buf.length < 8) {\n throw new Error(\"Invalid level.dat: buffer smaller than 8-byte header\");\n }\n const version = buf.readUInt32LE(0);\n const dataLength = buf.readUInt32LE(4);\n\n // \u6839\u6807\u7B7E\u5FC5\u987B\u662F TAG_Compound\n if (buf.length < 9 || buf.readUInt8(8) !== TAG_COMPOUND) {\n throw new Error(\"Invalid level.dat: root tag is not TAG_Compound\");\n }\n const rootNameLen = buf.readUInt16LE(9);\n const rootCompoundName = buf.toString(\"utf8\", 11, 11 + rootNameLen);\n const rootValueOffset = 11 + rootNameLen;\n\n const { children: rootChildren, compoundEndOffset: rootEndOffset } = parseCompoundChildren(buf, rootValueOffset);\n\n return {\n version,\n dataLength,\n rootCompoundName,\n rootChildren,\n rootEndOffset,\n };\n}\n\n/**\n * \u8BFB\u53D6\u4E16\u754C\u5B58\u6863 level.dat \u7684\u5B9E\u9A8C\u6027\u914D\u7F6E\u3002\u82E5\u89E3\u6790\u5931\u8D25\u6216\u6587\u4EF6\u4E0D\u5B58\u5728\u8FD4\u56DE null\u3002\n */\nexport function readLevelDatExperiments(levelDatPath: string): LevelDatExperimentsInfo | null {\n if (!fs.existsSync(levelDatPath)) return null;\n try {\n const buf = fs.readFileSync(levelDatPath);\n const parsed = parseBedrockLevelDat(buf);\n\n let experimentsEverUsed = false;\n const everUsedTag = parsed.rootChildren.get(\"experiments_ever_used\");\n if (everUsedTag && everUsedTag.type === TAG_BYTE && Number(everUsedTag.value) === 1) {\n experimentsEverUsed = true;\n }\n\n let savedWithToggledExperiments = false;\n const savedTag = parsed.rootChildren.get(\"saved_with_toggled_experiments\");\n if (savedTag && savedTag.type === TAG_BYTE && Number(savedTag.value) === 1) {\n savedWithToggledExperiments = true;\n }\n\n const experiments: Record<string, number | boolean> = {};\n let hasBetaApis = false;\n\n const expCompoundTag = parsed.rootChildren.get(\"experiments\");\n if (expCompoundTag && expCompoundTag.type === TAG_COMPOUND) {\n const expChildren = parseCompoundChildren(buf, expCompoundTag.valueOffset).children;\n\n // \u90E8\u5206\u7248\u672C experiments_ever_used \u4E5F\u53EF\u80FD\u4F4D\u4E8E experiments \u5185\u90E8\n const innerEverUsed = expChildren.get(\"experiments_ever_used\");\n if (innerEverUsed && Number(innerEverUsed.value) === 1) {\n experimentsEverUsed = true;\n }\n const innerSaved = expChildren.get(\"saved_with_toggled_experiments\");\n if (innerSaved && Number(innerSaved.value) === 1) {\n savedWithToggledExperiments = true;\n }\n\n for (const [name, meta] of expChildren.entries()) {\n if (meta.type === TAG_BYTE) {\n const num = Number(meta.value);\n experiments[name] = num;\n if (name === \"gametest\" && num === 1) {\n hasBetaApis = true;\n }\n }\n }\n }\n\n const knownExperiments: Record<string, boolean> = {};\n for (const def of KNOWN_EXPERIMENTS) {\n let enabled = false;\n if (def.isRootTag) {\n const rootTag = parsed.rootChildren.get(def.nbtKey);\n if (rootTag && Number(rootTag.value) === 1) {\n enabled = true;\n }\n }\n const keysToCheck = [def.nbtKey, ...(def.aliases ?? [])];\n for (const k of keysToCheck) {\n if (experiments[k] === 1) {\n enabled = true;\n break;\n }\n }\n knownExperiments[def.id] = enabled;\n }\n\n return {\n experimentsEverUsed,\n savedWithToggledExperiments,\n experiments,\n hasBetaApis,\n knownExperiments,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * \u6784\u9020\u4E00\u4E2A TAG_Byte \u7684\u4E8C\u8FDB\u5236 Buffer\u3002\n */\nfunction createByteTagBuffer(name: string, value: number): Buffer {\n const nameBuf = Buffer.from(name, \"utf8\");\n const buf = Buffer.alloc(1 + 2 + nameBuf.length + 1);\n buf.writeUInt8(TAG_BYTE, 0);\n buf.writeUInt16LE(nameBuf.length, 1);\n nameBuf.copy(buf, 3);\n buf.writeInt8(value, 3 + nameBuf.length);\n return buf;\n}\n\n/**\n * \u6784\u9020\u4E00\u4E2A\u5305\u542B\u6307\u5B9A\u952E\u503C\u5BF9\u7684 TAG_Compound Buffer\u3002\n */\nfunction createCompoundTagBuffer(name: string, byteTags: Record<string, number>): Buffer {\n const nameBuf = Buffer.from(name, \"utf8\");\n const childBuffers: Buffer[] = [];\n for (const [k, v] of Object.entries(byteTags)) {\n childBuffers.push(createByteTagBuffer(k, v));\n }\n childBuffers.push(Buffer.from([TAG_END]));\n\n const header = Buffer.alloc(1 + 2 + nameBuf.length);\n header.writeUInt8(TAG_COMPOUND, 0);\n header.writeUInt16LE(nameBuf.length, 1);\n nameBuf.copy(header, 3);\n\n return Buffer.concat([header, ...childBuffers]);\n}\n\n/**\n * \u4E3A\u6307\u5B9A level.dat \u5F00\u542F\u6307\u5B9A\u7684\u5B9E\u9A8C\u6027\u5F00\u5173\uFF08\u82E5\u672A\u6307\u5B9A\u6216\u4F20 \"all\" \u5219\u5F00\u542F\u5168\u90E8\u5DF2\u77E5\u5B9E\u9A8C\u6027\u5F00\u5173\uFF09\u3002\n *\n * \u4FDD\u8BC1\uFF1A\n * - \u81EA\u52A8\u5907\u4EFD\u539F\u6587\u4EF6\u81F3 `${levelDatPath}.bak`\uFF1B\n * - \u81EA\u52A8\u66F4\u65B0 root \u4E0E experiments \u590D\u5408\u6807\u7B7E\u4E2D\u7684\u76EE\u6807 byte tags\uFF1B\n * - \u81EA\u52A8\u66F4\u65B0 experiments_ever_used = 1b \u4E0E saved_with_toggled_experiments = 1b\uFF1B\n * - \u7CBE\u786E\u6821\u51C6 8 \u5B57\u8282\u6587\u4EF6\u5934\u90E8\u7684 dataLength\uFF1B\n * - \u539F\u5B50\u5199\u64CD\u4F5C\uFF08.tmp -> rename\uFF09\uFF1B\n * - \u82E5\u6240\u8BF7\u6C42\u5F00\u5173\u5DF2\u5168\u90E8\u5904\u4E8E\u5F00\u542F\u72B6\u6001\uFF0C\u76F4\u63A5\u8FD4\u56DE changed: false\uFF0C\u4E0D\u7834\u574F\u6587\u4EF6\u3002\n */\nexport async function enableExperimentsInLevelDat(\n levelDatPath: string,\n targetExperimentIds?: string[] | \"all\"\n): Promise<LevelDatMutateResult> {\n if (!fs.existsSync(levelDatPath)) {\n return { success: false, changed: false, error: `File not found: ${levelDatPath}` };\n }\n\n try {\n const raw = await fs.promises.readFile(levelDatPath);\n const parsed = parseBedrockLevelDat(raw);\n\n // \u89E3\u6790\u76EE\u6807\u5B9E\u9A8C\u9879\n let targetDefs: KnownExperimentDefinition[] = [];\n if (!targetExperimentIds || targetExperimentIds === \"all\") {\n targetDefs = [...KNOWN_EXPERIMENTS];\n } else {\n const seen = new Set<string>();\n for (const id of targetExperimentIds) {\n const def = resolveExperimentDefinition(id);\n if (def && !seen.has(def.id)) {\n seen.add(def.id);\n targetDefs.push(def);\n }\n }\n if (targetDefs.length === 0) {\n return { success: true, changed: false, enabledExperiments: [] };\n }\n }\n\n const expCompoundTag = parsed.rootChildren.get(\"experiments\");\n const existingExpChildren =\n expCompoundTag && expCompoundTag.type === TAG_COMPOUND\n ? parseCompoundChildren(raw, expCompoundTag.valueOffset).children\n : new Map<string, NbtTagMeta>();\n\n // \u68C0\u67E5\u5F53\u524D\u72B6\u6001\n let needsChange = false;\n const enabledIds: string[] = [];\n\n // 1. \u68C0\u67E5 experiments compound \u53CA\u5185\u90E8\u5FC5\u8981\u7CFB\u7EDF\u6807\u7B7E\n if (!expCompoundTag) {\n needsChange = true;\n } else {\n const expEverUsed = existingExpChildren.get(\"experiments_ever_used\");\n if (!expEverUsed || Number(expEverUsed.value) !== 1) needsChange = true;\n\n const expSaved = existingExpChildren.get(\"saved_with_toggled_experiments\");\n if (!expSaved || Number(expSaved.value) !== 1) needsChange = true;\n }\n\n // \u82E5 root \u5C42\u5B58\u5728\u7CFB\u7EDF\u6807\u7B7E\u4F46\u672A\u8BBE\u4E3A 1\uFF0C\u4E5F\u9700\u4FEE\u6B63\n const rootEverUsed = parsed.rootChildren.get(\"experiments_ever_used\");\n if (rootEverUsed && Number(rootEverUsed.value) !== 1) needsChange = true;\n\n const rootSaved = parsed.rootChildren.get(\"saved_with_toggled_experiments\");\n if (rootSaved && Number(rootSaved.value) !== 1) needsChange = true;\n\n let previousBetaApisState = false;\n const currentGametest = existingExpChildren.get(\"gametest\");\n if (currentGametest && Number(currentGametest.value) === 1) {\n previousBetaApisState = true;\n }\n\n for (const def of targetDefs) {\n enabledIds.push(def.id);\n if (def.isRootTag) {\n const rootTag = parsed.rootChildren.get(def.nbtKey);\n if (!rootTag || Number(rootTag.value) !== 1) {\n needsChange = true;\n }\n }\n if (!def.isRootTag) {\n const tag = existingExpChildren.get(def.nbtKey);\n if (!tag || Number(tag.value) !== 1) {\n needsChange = true;\n }\n }\n if (def.extraExpTags) {\n for (const extra of def.extraExpTags) {\n const tag = existingExpChildren.get(extra);\n if (!tag || Number(tag.value) !== 1) {\n needsChange = true;\n }\n }\n }\n }\n\n if (!needsChange) {\n return {\n success: true,\n changed: false,\n previousBetaApisState,\n enabledExperiments: enabledIds,\n };\n }\n\n // \u5907\u4EFD\u539F\u6587\u4EF6\n const backupPath = `${levelDatPath}.bak`;\n await fs.promises.copyFile(levelDatPath, backupPath);\n\n let mutatedRaw = Buffer.from(raw);\n let nbtBody: Buffer;\n\n // \u6536\u96C6\u9700\u8981\u5728 experiments \u590D\u5408\u6807\u7B7E\u4E2D\u8BBE\u7F6E\u7684 tags\n const requiredExpTags: Record<string, number> = {\n experiments_ever_used: 1,\n saved_with_toggled_experiments: 1,\n };\n for (const def of targetDefs) {\n if (!def.isRootTag) {\n requiredExpTags[def.nbtKey] = 1;\n }\n if (def.extraExpTags) {\n for (const extra of def.extraExpTags) {\n requiredExpTags[extra] = 1;\n }\n }\n }\n\n // \u6536\u96C6 root \u5C42\u9700\u8981\u66F4\u65B0\u6216\u63D2\u5165\u7684 tags\n const rootNeedsEducation = targetDefs.some((d) => d.id === \"education\");\n\n // \u66F4\u65B0 root \u5C42\u5DF2\u5B58\u5728\u7684\u76F8\u5173 tags\n if (rootEverUsed) {\n mutatedRaw.writeInt8(1, rootEverUsed.valueOffset);\n }\n if (rootSaved) {\n mutatedRaw.writeInt8(1, rootSaved.valueOffset);\n }\n if (rootNeedsEducation) {\n const eduTag = parsed.rootChildren.get(\"educationFeaturesEnabled\");\n if (eduTag) {\n mutatedRaw.writeInt8(1, eduTag.valueOffset);\n }\n const optOut = parsed.rootChildren.get(\"has_opted_out_of_education\");\n if (optOut) {\n mutatedRaw.writeInt8(0, optOut.valueOffset);\n }\n }\n\n const rootTagsToAppend: Buffer[] = [];\n if (rootNeedsEducation && !parsed.rootChildren.has(\"educationFeaturesEnabled\")) {\n rootTagsToAppend.push(createByteTagBuffer(\"educationFeaturesEnabled\", 1));\n }\n\n if (!expCompoundTag) {\n // \u573A\u666F 1\uFF1A\u5B8C\u5168\u7F3A\u5931 experiments compound\n const newExpCompoundBuf = createCompoundTagBuffer(\"experiments\", requiredExpTags);\n const rootEndTagIndex = parsed.rootEndOffset - 1;\n const beforeEnd = mutatedRaw.subarray(8, rootEndTagIndex);\n const afterEnd = mutatedRaw.subarray(rootEndTagIndex);\n nbtBody = Buffer.concat([beforeEnd, ...rootTagsToAppend, newExpCompoundBuf, afterEnd]);\n } else {\n // \u573A\u666F 2\uFF1A\u5DF2\u6709 experiments compound\n const { compoundEndOffset: expEndOffset } = parseCompoundChildren(raw, expCompoundTag.valueOffset);\n const expEndTagIndex = expEndOffset - 1;\n const rootEndTagIndex = parsed.rootEndOffset - 1;\n\n const expTagsToAppend: Buffer[] = [];\n for (const [name, val] of Object.entries(requiredExpTags)) {\n const existing = existingExpChildren.get(name);\n if (existing) {\n mutatedRaw.writeInt8(val, existing.valueOffset);\n } else {\n expTagsToAppend.push(createByteTagBuffer(name, val));\n }\n }\n\n // \u7EC4\u88C5\u65B0 NBT body\n // Part 1: root \u5F00\u59CB\u5230 experiments TAG_END \u524D\n const part1 = mutatedRaw.subarray(8, expEndTagIndex);\n // Part 2: \u8FFD\u52A0\u5230 experiments \u5185\u90E8\u7684 tags\n const part2 = Buffer.concat(expTagsToAppend);\n // Part 3: experiments TAG_END \u5230 root TAG_END \u524D\n const part3 = mutatedRaw.subarray(expEndTagIndex, rootEndTagIndex);\n // Part 4: \u8FFD\u52A0\u5230 root \u5C42\u7684 tags (\u5982 educationFeaturesEnabled)\n const part4 = Buffer.concat(rootTagsToAppend);\n // Part 5: root TAG_END \u53CA\u540E\u7EED\n const part5 = mutatedRaw.subarray(rootEndTagIndex);\n\n nbtBody = Buffer.concat([part1, part2, part3, part4, part5]);\n }\n\n // \u91CD\u65B0\u6784\u9020 8 \u5B57\u8282 header\n const newHeader = Buffer.alloc(8);\n newHeader.writeUInt32LE(parsed.version, 0);\n newHeader.writeUInt32LE(nbtBody.length, 4);\n\n const finalFileBuffer = Buffer.concat([newHeader, nbtBody]);\n\n // \u539F\u5B50\u66FF\u6362\n const tmpFile = `${levelDatPath}.${process.pid}.tmp`;\n await fs.promises.writeFile(tmpFile, finalFileBuffer);\n await fs.promises.rename(tmpFile, levelDatPath);\n\n return {\n success: true,\n changed: true,\n backupPath,\n previousBetaApisState,\n enabledExperiments: enabledIds,\n };\n } catch (err) {\n return {\n success: false,\n changed: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n/**\n * \u4E3A\u6307\u5B9A level.dat \u5F00\u542F Beta APIs\uFF08gametest\uFF09\u3002\n * \u4FDD\u6301\u5411\u540E\u517C\u5BB9\uFF0C\u59D4\u6258\u7ED9 enableExperimentsInLevelDat\u3002\n */\nexport async function enableBetaApisInLevelDat(levelDatPath: string): Promise<LevelDatMutateResult> {\n return await enableExperimentsInLevelDat(levelDatPath, [\"gametest\"]);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/level-dat.test.js
CHANGED
|
@@ -5,8 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import test from "node:test";
|
|
6
6
|
import {
|
|
7
7
|
enableBetaApisInLevelDat,
|
|
8
|
+
enableExperimentsInLevelDat,
|
|
9
|
+
KNOWN_EXPERIMENTS,
|
|
8
10
|
parseBedrockLevelDat,
|
|
9
11
|
readLevelDatExperiments,
|
|
12
|
+
resolveExperimentDefinition,
|
|
10
13
|
TAG_BYTE,
|
|
11
14
|
TAG_COMPOUND,
|
|
12
15
|
TAG_END
|
|
@@ -70,14 +73,27 @@ test("readLevelDatExperiments: correctly reads experiments status", () => {
|
|
|
70
73
|
assert.ok(info1);
|
|
71
74
|
assert.equal(info1.hasBetaApis, false);
|
|
72
75
|
assert.equal(info1.experimentsEverUsed, false);
|
|
76
|
+
assert.equal(info1.knownExperiments.gametest, false);
|
|
77
|
+
assert.equal(info1.knownExperiments.upcoming_creator_features, false);
|
|
73
78
|
fs.writeFileSync(file, buildMockLevelDat(true, 1));
|
|
74
79
|
const info2 = readLevelDatExperiments(file);
|
|
75
80
|
assert.ok(info2);
|
|
76
81
|
assert.equal(info2.hasBetaApis, true);
|
|
82
|
+
assert.equal(info2.knownExperiments.gametest, true);
|
|
77
83
|
} finally {
|
|
78
84
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
79
85
|
}
|
|
80
86
|
});
|
|
87
|
+
test("resolveExperimentDefinition: resolves aliases and canonical ids", () => {
|
|
88
|
+
assert.equal(resolveExperimentDefinition("upcoming")?.id, "upcoming_creator_features");
|
|
89
|
+
assert.equal(resolveExperimentDefinition("cameras")?.id, "experimental_creator_cameras");
|
|
90
|
+
assert.equal(resolveExperimentDefinition("voxel")?.id, "voxel_shapes");
|
|
91
|
+
assert.equal(resolveExperimentDefinition("trades")?.id, "villager_trades_rebalance");
|
|
92
|
+
assert.equal(resolveExperimentDefinition("drop3")?.id, "drop_3_2026");
|
|
93
|
+
assert.equal(resolveExperimentDefinition("wilderness_bound")?.id, "drop_3_2026");
|
|
94
|
+
assert.equal(resolveExperimentDefinition("edu")?.id, "education");
|
|
95
|
+
assert.equal(resolveExperimentDefinition("unknown_feature"), null);
|
|
96
|
+
});
|
|
81
97
|
test("enableBetaApisInLevelDat: enables gametest and updates header length", async () => {
|
|
82
98
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
|
|
83
99
|
try {
|
|
@@ -102,12 +118,12 @@ test("enableBetaApisInLevelDat: enables gametest and updates header length", asy
|
|
|
102
118
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
103
119
|
}
|
|
104
120
|
});
|
|
105
|
-
test("
|
|
121
|
+
test("enableExperimentsInLevelDat: enables all known experiments and handles education root tag", async () => {
|
|
106
122
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
|
|
107
123
|
try {
|
|
108
124
|
const file = path.join(tmpDir, "level.dat");
|
|
109
|
-
fs.writeFileSync(file, buildMockLevelDat(
|
|
110
|
-
const res = await
|
|
125
|
+
fs.writeFileSync(file, buildMockLevelDat(true));
|
|
126
|
+
const res = await enableExperimentsInLevelDat(file, "all");
|
|
111
127
|
assert.equal(res.success, true);
|
|
112
128
|
assert.equal(res.changed, true);
|
|
113
129
|
const info = readLevelDatExperiments(file);
|
|
@@ -115,6 +131,37 @@ test("enableBetaApisInLevelDat: creates experiments compound if completely absen
|
|
|
115
131
|
assert.equal(info.hasBetaApis, true);
|
|
116
132
|
assert.equal(info.experimentsEverUsed, true);
|
|
117
133
|
assert.equal(info.savedWithToggledExperiments, true);
|
|
134
|
+
for (const def of KNOWN_EXPERIMENTS) {
|
|
135
|
+
assert.equal(info.knownExperiments[def.id], true, `Experiment ${def.id} should be enabled`);
|
|
136
|
+
}
|
|
137
|
+
const finalBuf = fs.readFileSync(file);
|
|
138
|
+
const parsed = parseBedrockLevelDat(finalBuf);
|
|
139
|
+
assert.equal(parsed.dataLength, finalBuf.length - 8);
|
|
140
|
+
const eduRoot = parsed.rootChildren.get("educationFeaturesEnabled");
|
|
141
|
+
assert.ok(eduRoot);
|
|
142
|
+
assert.equal(eduRoot.value, 1);
|
|
143
|
+
const res2 = await enableExperimentsInLevelDat(file, "all");
|
|
144
|
+
assert.equal(res2.success, true);
|
|
145
|
+
assert.equal(res2.changed, false);
|
|
146
|
+
} finally {
|
|
147
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
test("enableExperimentsInLevelDat: enables specific subset from scratch (no experiments compound)", async () => {
|
|
151
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sfmc-lvl-test-"));
|
|
152
|
+
try {
|
|
153
|
+
const file = path.join(tmpDir, "level.dat");
|
|
154
|
+
fs.writeFileSync(file, buildMockLevelDat(false));
|
|
155
|
+
const res = await enableExperimentsInLevelDat(file, ["upcoming", "voxel", "edu"]);
|
|
156
|
+
assert.equal(res.success, true);
|
|
157
|
+
assert.equal(res.changed, true);
|
|
158
|
+
const info = readLevelDatExperiments(file);
|
|
159
|
+
assert.ok(info);
|
|
160
|
+
assert.equal(info.knownExperiments.upcoming_creator_features, true);
|
|
161
|
+
assert.equal(info.knownExperiments.voxel_shapes, true);
|
|
162
|
+
assert.equal(info.knownExperiments.education, true);
|
|
163
|
+
assert.equal(info.knownExperiments.gametest, false);
|
|
164
|
+
assert.equal(info.knownExperiments.villager_trades_rebalance, false);
|
|
118
165
|
const finalBuf = fs.readFileSync(file);
|
|
119
166
|
const parsed = parseBedrockLevelDat(finalBuf);
|
|
120
167
|
assert.equal(parsed.dataLength, finalBuf.length - 8);
|