@sfmc-bds/bds-tools 0.2.0-beta.17 → 0.2.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/bds-manager.d.ts.map +1 -1
  2. package/dist/bds-manager.js +8 -1
  3. package/dist/bds-manager.js.map +2 -2
  4. package/dist/changelog.d.ts +11 -7
  5. package/dist/changelog.d.ts.map +1 -1
  6. package/dist/changelog.js +60 -34
  7. package/dist/changelog.js.map +2 -2
  8. package/dist/check-update.d.ts.map +1 -1
  9. package/dist/check-update.js +50 -13
  10. package/dist/check-update.js.map +2 -2
  11. package/dist/qq-events-notify.d.ts +1 -1
  12. package/dist/qq-events-notify.d.ts.map +1 -1
  13. package/dist/qq-events-notify.js.map +1 -1
  14. package/dist/qqutil.d.ts.map +1 -1
  15. package/dist/qqutil.js +14 -12
  16. package/dist/qqutil.js.map +2 -2
  17. package/dist/server-properties-i18n.d.ts.map +1 -1
  18. package/dist/server-properties-i18n.js +4 -0
  19. package/dist/server-properties-i18n.js.map +2 -2
  20. package/dist/server-properties.d.ts +3 -0
  21. package/dist/server-properties.d.ts.map +1 -1
  22. package/dist/server-properties.js +15 -0
  23. package/dist/server-properties.js.map +2 -2
  24. package/dist/types.d.ts +1 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/dist/dependency-negotiator.test.js +0 -120
  28. package/dist/dependency-negotiator.test.js.map +0 -7
  29. package/dist/fsx.test.js +0 -60
  30. package/dist/fsx.test.js.map +0 -7
  31. package/dist/level-dat.test.js +0 -172
  32. package/dist/level-dat.test.js.map +0 -7
  33. package/dist/pack-manager-lifecycle.test.js +0 -252
  34. package/dist/pack-manager-lifecycle.test.js.map +0 -7
  35. package/dist/pack-update/config.test.js +0 -75
  36. package/dist/pack-update/config.test.js.map +0 -7
  37. package/dist/pack-update/version-policy.test.js +0 -137
  38. package/dist/pack-update/version-policy.test.js.map +0 -7
  39. package/dist/process-probe.test.js +0 -76
  40. package/dist/process-probe.test.js.map +0 -7
  41. package/dist/server-properties.test.js +0 -127
  42. package/dist/server-properties.test.js.map +0 -7
  43. package/dist/world-packs.test.js +0 -698
  44. package/dist/world-packs.test.js.map +0 -7
  45. package/dist/zipx.test.js +0 -36
  46. package/dist/zipx.test.js.map +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"bds-manager.d.ts","sourceRoot":"","sources":["../src/bds-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,uBAAuB;AACvB,MAAM,WAAW,UAAU;IACzB,mBAAmB;IACnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,qBAAqB;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,uBAAuB;IACvB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,2BAA2B;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,mBAAmB;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,kBAAkB;AAClB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+BD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CA2L5E;AAED,eAAO,MAAM,SAAS,EAAE,YAAiC,CAAC;AAC1D,eAAO,MAAM,iBAAiB,QAAO,IAEpC,CAAC"}
1
+ {"version":3,"file":"bds-manager.d.ts","sourceRoot":"","sources":["../src/bds-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,uBAAuB;AACvB,MAAM,WAAW,UAAU;IACzB,mBAAmB;IACnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,qBAAqB;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,uBAAuB;IACvB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,2BAA2B;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,mBAAmB;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,kBAAkB;AAClB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+BD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAkM5E;AAED,eAAO,MAAM,SAAS,EAAE,YAAiC,CAAC;AAC1D,eAAO,MAAM,iBAAiB,QAAO,IAEpC,CAAC"}
@@ -7,7 +7,7 @@ import { isMainModule } from "./is-main.js";
7
7
  import { loadConfig, ROOT_DIR } from "./paths.js";
8
8
  import { log } from "./log.js";
9
9
  import { postBdsLifecycleEvent } from "./qq-events-notify.js";
10
- import { ensureEmitServerTelemetry, localizeServerProperties } from "./server-properties.js";
10
+ import { ensureEmitServerTelemetry, ensureNetherNetTransport, localizeServerProperties } from "./server-properties.js";
11
11
  import {
12
12
  clearBdsPidFile,
13
13
  findBedrockServerPids,
@@ -67,10 +67,12 @@ function createBdsManager(options = {}) {
67
67
  clearBdsPidFile(ROOT_DIR);
68
68
  p.process = null;
69
69
  log.info("BDS \u5DF2\u505C\u6B62");
70
+ void postBdsLifecycleEvent("stop");
70
71
  return;
71
72
  }
72
73
  log.info("BDS \u672A\u8FD0\u884C");
73
74
  clearBdsPidFile(ROOT_DIR);
75
+ p.isManualStop = false;
74
76
  return;
75
77
  }
76
78
  if (p.process && p.process.pid === pid && p.process.stdin) {
@@ -103,6 +105,7 @@ function createBdsManager(options = {}) {
103
105
  } else {
104
106
  log.info("BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...");
105
107
  await killBedrockServerByImage();
108
+ void postBdsLifecycleEvent("stop");
106
109
  }
107
110
  clearBdsPidFile(ROOT_DIR);
108
111
  p.process = null;
@@ -120,9 +123,11 @@ function createBdsManager(options = {}) {
120
123
  throw new Error(`BDS \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728: ${p.exePath}`);
121
124
  }
122
125
  ensureEmitServerTelemetry(p.bdsPath, log);
126
+ ensureNetherNetTransport(p.bdsPath, log);
123
127
  localizeServerProperties(p.bdsPath, { logger: log, rootDir: ROOT_DIR });
124
128
  ensureBdsExecutable(p.exePath);
125
129
  log.info("\u6B63\u5728\u542F\u52A8 BDS...");
130
+ p.isManualStop = false;
126
131
  const child = spawn(p.exePath, [], {
127
132
  cwd: p.bdsPath,
128
133
  env: { ...process.env, ...bdsSpawnEnvExtra(p.bdsPath) },
@@ -152,6 +157,8 @@ function createBdsManager(options = {}) {
152
157
  const wasManual = p.isManualStop;
153
158
  if (!wasManual) {
154
159
  void postBdsLifecycleEvent("crash", `code=${code ?? "?"}`);
160
+ } else {
161
+ void postBdsLifecycleEvent("stop");
155
162
  }
156
163
  if (!wasManual && p.crashRestart && isMain()) {
157
164
  log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1e3}s \u540E\u81EA\u52A8\u91CD\u542F...`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/bds-manager.ts"],
4
- "sourcesContent": ["/**\n * bds-manager.ts \u2014 Bedrock Dedicated Server (BDS) \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u7BA1\u7406\u5668\n *\n * \u6838\u5FC3\u804C\u8D23\uFF1A\n * - \u4F18\u96C5\u505C\u670D\uFF1A\u5411\u6807\u51C6\u8F93\u5165\u53D1\u9001 `stop` \u547D\u4EE4\u7B49\u5F85\u81EA\u7136\u9000\u51FA\uFF0C\u8D85\u65F6\u540E\u9010\u6B65\u5347\u7EA7\u81F3 SIGTERM \u4E0E SIGKILL\n * - \u5B88\u62A4\u76D1\u89C6\uFF08watch\uFF09\uFF1A\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\u4E8B\u4EF6\u5E76\u5728\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u5EF6\u65F6\u91CD\u542F\u6062\u590D\n * - \u751F\u547D\u5468\u671F\u4E8B\u4EF6\u5E7F\u64AD\uFF1A\u5206\u53D1\u542F\u52A8\u3001\u505C\u6B62\u3001\u5D29\u6E83\u7B49\u4E8B\u4EF6\uFF0C\u89E6\u53D1\u8FD0\u7EF4\u544A\u8B66\u4E0E\u72B6\u6001\u540C\u6B65\n * - \u7BA1\u9053\u4EA4\u4E92\u4E0E\u63A2\u6D3B\uFF1A\u68C0\u6D4B\u8FDB\u7A0B\u5B58\u6D3B\u72B6\u6001\u5E76\u5411 BDS stdin \u6295\u9012\u63A7\u5236\u53F0\u6307\u4EE4\n */\n\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { EventEmitter } from \"node:events\";\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"./host-platform.js\";\nimport { isMainModule } from \"./is-main.js\";\nimport { loadConfig, ROOT_DIR } from \"./paths.js\";\nimport { log } from \"./log.js\";\nimport { postBdsLifecycleEvent } from \"./qq-events-notify.js\";\nimport { ensureEmitServerTelemetry, localizeServerProperties } from \"./server-properties.js\";\nimport {\n clearBdsPidFile,\n findBedrockServerPids,\n isProcessAlive,\n isProcessAliveSync,\n killBedrockServerByImage,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"./process-probe.js\";\n\n/** BDS \u8FDB\u7A0B\u7BA1\u7406\u5668\u7EDF\u4E00\u64CD\u4F5C\u63A5\u53E3\u3002 */\nexport interface BdsManager {\n /** \u542F\u52A8 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n start(): Promise<void>;\n /** \u4F18\u96C5\u505C\u6B62 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n stop(): Promise<void>;\n /** \u67E5\u8BE2 BDS \u8FDB\u7A0B\u5F53\u524D\u662F\u5426\u5904\u4E8E\u8FD0\u884C\u72B6\u6001\u3002 */\n status(): Promise<boolean>;\n /** \u5411 BDS \u63A7\u5236\u53F0\u7BA1\u9053\u53D1\u9001\u6307\u4EE4\u3002 */\n sendCommand(cmd: string): boolean;\n /** \u5F00\u542F\u5B88\u62A4\u76D1\u89C6\u6A21\u5F0F\uFF08\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u62C9\u8D77\uFF09\u3002 */\n watch(): Promise<void>;\n /** \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u4E8B\u4EF6\u53D1\u5C04\u5668\u3002 */\n events: EventEmitter;\n /** \u5F53\u524D\u662F\u5426\u5C5E\u4E8E\u4E3B\u52A8\u8C03\u7528\u505C\u670D\u3002 */\n isManualStop: boolean;\n /** \u83B7\u53D6\u5F53\u524D\u8FD0\u884C\u4E2D BDS \u8FDB\u7A0B\u7684 PID\u3002 */\n getPid(): number;\n}\n\n/** BDS \u8FDB\u7A0B\u542F\u52A8\u9009\u9879\u3002 */\nexport interface BdsManagerOptions {\n detached?: boolean;\n}\n\n\ninterface CachedProc {\n process: ReturnType<typeof spawn> | null;\n isManualStop: boolean;\n crashRestart: boolean;\n crashDelayMs: number;\n exePath: string;\n bdsPath: string;\n}\n\nlet cached: CachedProc | null = null;\n\nfunction ensureProc(): CachedProc {\n if (cached) return cached;\n const cfg = loadConfig();\n const bds_path = path.resolve(cfg.bds_path || process.cwd());\n const exePath = bdsExePath(bds_path);\n\n cached = {\n process: null,\n isManualStop: false,\n crashRestart: cfg.crash_restart !== false,\n crashDelayMs: (cfg.crash_restart_delay ?? 5) * 1000,\n exePath,\n bdsPath: bds_path,\n };\n return cached;\n}\n\nexport function createBdsManager(options: BdsManagerOptions = {}): BdsManager {\n const events = new EventEmitter();\n events.setMaxListeners(100);\n\n const sendCommand = (cmd: string): boolean => {\n const p = ensureProc();\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !isProcessAliveSync(pid)) {\n log.warn(\"BDS \u672A\u8FD0\u884C\");\n return false;\n }\n if (p.process && p.process.pid === pid && p.process.stdin) {\n try {\n p.process.stdin.write(cmd + \"\\n\");\n log.info(`\u5DF2\u53D1\u9001\u547D\u4EE4: ${cmd}`);\n return true;\n } catch {\n return false;\n }\n }\n log.warn(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u547D\u4EE4\u5230 stdin\");\n return false;\n };\n\n const stop = async (): Promise<void> => {\n const p = ensureProc();\n // \u65E0\u8BBA\u81EA\u7BA1 / \u5916\u90E8\u8FDB\u7A0B\uFF0C\u624B\u52A8 stop \u90FD\u4E0D\u62A5 crash\n p.isManualStop = true;\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n const externalPids = await findBedrockServerPids();\n if (externalPids.length > 0) {\n log.info(\"\u68C0\u6D4B\u5230\u5916\u90E8\u8FD0\u884C\u7684 BDS \u8FDB\u7A0B\uFF0C\u4F7F\u7528 taskkill \u7EC8\u6B62...\");\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n return;\n }\n log.info(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n return;\n }\n\n if (p.process && p.process.pid === pid && p.process.stdin) {\n log.info(\"\u6B63\u5728\u5173\u95ED BDS...\");\n try {\n p.process.stdin.write(\"stop\\n\");\n } catch (e) {\n log.warn(`\u53D1\u9001 stop \u547D\u4EE4\u5931\u8D25: ${(e as Error).message}`);\n }\n\n await Promise.race([\n new Promise<void>((resolve) => p.process?.once(\"exit\", () => resolve())),\n new Promise<void>((resolve) =>\n setTimeout(() => {\n log.warn(\"30s \u8D85\u65F6\uFF0C\u5F3A\u5236\u7EC8\u6B62...\");\n try {\n p.process?.kill(\"SIGTERM\");\n } catch {}\n setTimeout(resolve, 5000);\n }, 30_000)\n ),\n ]);\n\n if (p.process && p.process.exitCode === null) {\n log.warn(\"\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B...\");\n try {\n p.process.kill(\"SIGKILL\");\n } catch {}\n }\n } else {\n // \u5916\u90E8\u542F\u52A8\u7684 BDS \u2014 fallback \u4F7F\u7528 taskkill / pkill\n log.info(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...\");\n await killBedrockServerByImage();\n }\n\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n };\n\n const start = async (): Promise<void> => {\n const p = ensureProc();\n const existing = readBdsPidFile(ROOT_DIR);\n if (existing && (await isProcessAlive(existing))) {\n log.info(\"BDS \u5DF2\u5728\u8FD0\u884C\u4E2D\");\n return;\n }\n\n if (!fs.existsSync(p.exePath)) {\n log.error(`\u672A\u627E\u5230 ${p.exePath}`);\n throw new Error(`BDS \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728: ${p.exePath}`);\n }\n\n // \u542F\u52A8\u524D\u5E42\u7B49\u786E\u4FDD\u9065\u6D4B\u5F00\u5173\u4E0E\u914D\u7F6E\u6CE8\u91CA\u672C\u5730\u5316\uFF08\u5B89\u88C5\u9636\u6BB5\u4E5F\u4F1A\u5199\uFF1B\u65E7\u76EE\u5F55\u9996\u6B21\u542F\u52A8\u65F6\u8865\u4E0A\uFF09\n ensureEmitServerTelemetry(p.bdsPath, log);\n localizeServerProperties(p.bdsPath, { logger: log, rootDir: ROOT_DIR });\n ensureBdsExecutable(p.exePath);\n\n log.info(\"\u6B63\u5728\u542F\u52A8 BDS...\");\n const child = spawn(p.exePath, [], {\n cwd: p.bdsPath,\n env: { ...process.env, ...bdsSpawnEnvExtra(p.bdsPath) },\n stdio: options.detached ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"],\n detached: options.detached,\n windowsHide: true,\n });\n\n if (options.detached) child.unref();\n\n p.process = child;\n writeBdsPidFile(child.pid ?? 0, ROOT_DIR);\n log.info(`BDS \u5DF2\u542F\u52A8 (PID: ${child.pid})`);\n void postBdsLifecycleEvent(\"start\", `pid=${child.pid ?? 0}`);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n events.emit(\"output\", text);\n if (isMain()) process.stdout.write(text);\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = `[STDERR] ${chunk.toString()}`;\n events.emit(\"output\", text);\n if (isMain()) process.stderr.write(text);\n });\n\n child.on(\"exit\", (code) => {\n log.info(`BDS \u5DF2\u9000\u51FA (code: ${code})`);\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n const wasManual = p.isManualStop;\n if (!wasManual) {\n void postBdsLifecycleEvent(\"crash\", `code=${code ?? \"?\"}`);\n }\n if (!wasManual && p.crashRestart && isMain()) {\n log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1000}s \u540E\u81EA\u52A8\u91CD\u542F...`);\n setTimeout(() => {\n start().catch((e) => log.error(`\u81EA\u52A8\u91CD\u542F\u5931\u8D25: ${e.message}`));\n }, p.crashDelayMs);\n }\n p.isManualStop = false;\n });\n };\n\n const status = async (): Promise<boolean> => {\n const pid = readBdsPidFile(ROOT_DIR);\n const alive = pid > 0 && (await isProcessAlive(pid));\n if (alive) {\n console.log(`BDS \u8FD0\u884C\u4E2D (PID: ${pid})`);\n } else {\n console.log(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n }\n return alive;\n };\n\n const watch = async (): Promise<void> => {\n log.info(\"\u542F\u52A8\u76D1\u62A4\u6A21\u5F0F...\");\n while (true) {\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n try {\n await start();\n } catch (e) {\n log.error(`\u81EA\u52A8\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n await new Promise((r) => setTimeout(r, 5_000));\n }\n };\n\n const getPid = (): number => {\n const p = ensureProc();\n return p.process?.pid ?? readBdsPidFile(ROOT_DIR);\n };\n\n return {\n start,\n stop,\n status,\n sendCommand,\n watch,\n events,\n isManualStop: false,\n getPid,\n };\n}\n\nexport const bdsEvents: EventEmitter = new EventEmitter();\nexport const bdsEvents_enabled = (): void => {\n bdsEvents.setMaxListeners(100);\n};\nbdsEvents_enabled();\n\n/** \u5224\u65AD\u5F53\u524D\u662F\u5426\u4F5C\u4E3A CLI \u4E3B\u5165\u53E3\u8FD0\u884C\uFF08\u88AB check-update \u7B49 import \u65F6\u4E3A false\uFF09 */\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\uFF08\u4E0E check-update \u5BF9\u79F0\uFF09\n if (process.env.SFMC_SERVICE === \"manager\") return true;\n return isMainModule(import.meta.url);\n}\n\n// CLI \u5165\u53E3\nif (isMain()) {\n const cmd = process.argv[2];\n const bds = createBdsManager();\n const args = process.argv.slice(3);\n\n switch (cmd) {\n case \"start\":\n bds.start().catch((e) => log.error(`\u542F\u52A8\u5931\u8D25: ${e.message}`));\n break;\n case \"stop\":\n bds.stop().catch((e) => log.error(`\u505C\u6B62\u5931\u8D25: ${e.message}`));\n break;\n case \"restart\":\n bds\n .stop()\n .then(() => bds.start())\n .catch((e) => log.error(`\u91CD\u542F\u5931\u8D25: ${e.message}`));\n break;\n case \"status\":\n bds.status();\n break;\n case \"send\":\n if (args[0]) bds.sendCommand(args.join(\" \"));\n else console.log(\"\u7528\u6CD5: node bds-manager.js send <command>\");\n break;\n case \"watch\":\n bds.watch().catch((e) => log.error(`\u76D1\u62A4\u5F02\u5E38: ${e.message}`));\n break;\n default:\n console.log(`\u7528\u6CD5:\n start \u542F\u52A8 BDS\n stop \u505C\u6B62\n restart \u91CD\u542F\n status \u68C0\u67E5\u72B6\u6001\n send <cmd> \u53D1\u9001\u547D\u4EE4\n watch \u76D1\u62A4\u6A21\u5F0F\uFF08\u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF09`);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * bds-manager.ts \u2014 Bedrock Dedicated Server (BDS) \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u7BA1\u7406\u5668\n *\n * \u6838\u5FC3\u804C\u8D23\uFF1A\n * - \u4F18\u96C5\u505C\u670D\uFF1A\u5411\u6807\u51C6\u8F93\u5165\u53D1\u9001 `stop` \u547D\u4EE4\u7B49\u5F85\u81EA\u7136\u9000\u51FA\uFF0C\u8D85\u65F6\u540E\u9010\u6B65\u5347\u7EA7\u81F3 SIGTERM \u4E0E SIGKILL\n * - \u5B88\u62A4\u76D1\u89C6\uFF08watch\uFF09\uFF1A\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\u4E8B\u4EF6\u5E76\u5728\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u5EF6\u65F6\u91CD\u542F\u6062\u590D\n * - \u751F\u547D\u5468\u671F\u4E8B\u4EF6\u5E7F\u64AD\uFF1A\u5206\u53D1\u542F\u52A8\u3001\u505C\u6B62\u3001\u5D29\u6E83\u7B49\u4E8B\u4EF6\uFF0C\u89E6\u53D1\u8FD0\u7EF4\u544A\u8B66\u4E0E\u72B6\u6001\u540C\u6B65\n * - \u7BA1\u9053\u4EA4\u4E92\u4E0E\u63A2\u6D3B\uFF1A\u68C0\u6D4B\u8FDB\u7A0B\u5B58\u6D3B\u72B6\u6001\u5E76\u5411 BDS stdin \u6295\u9012\u63A7\u5236\u53F0\u6307\u4EE4\n */\n\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { EventEmitter } from \"node:events\";\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"./host-platform.js\";\nimport { isMainModule } from \"./is-main.js\";\nimport { loadConfig, ROOT_DIR } from \"./paths.js\";\nimport { log } from \"./log.js\";\nimport { postBdsLifecycleEvent } from \"./qq-events-notify.js\";\nimport { ensureEmitServerTelemetry, ensureNetherNetTransport, localizeServerProperties } from \"./server-properties.js\";\nimport {\n clearBdsPidFile,\n findBedrockServerPids,\n isProcessAlive,\n isProcessAliveSync,\n killBedrockServerByImage,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"./process-probe.js\";\n\n/** BDS \u8FDB\u7A0B\u7BA1\u7406\u5668\u7EDF\u4E00\u64CD\u4F5C\u63A5\u53E3\u3002 */\nexport interface BdsManager {\n /** \u542F\u52A8 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n start(): Promise<void>;\n /** \u4F18\u96C5\u505C\u6B62 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n stop(): Promise<void>;\n /** \u67E5\u8BE2 BDS \u8FDB\u7A0B\u5F53\u524D\u662F\u5426\u5904\u4E8E\u8FD0\u884C\u72B6\u6001\u3002 */\n status(): Promise<boolean>;\n /** \u5411 BDS \u63A7\u5236\u53F0\u7BA1\u9053\u53D1\u9001\u6307\u4EE4\u3002 */\n sendCommand(cmd: string): boolean;\n /** \u5F00\u542F\u5B88\u62A4\u76D1\u89C6\u6A21\u5F0F\uFF08\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u62C9\u8D77\uFF09\u3002 */\n watch(): Promise<void>;\n /** \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u4E8B\u4EF6\u53D1\u5C04\u5668\u3002 */\n events: EventEmitter;\n /** \u5F53\u524D\u662F\u5426\u5C5E\u4E8E\u4E3B\u52A8\u8C03\u7528\u505C\u670D\u3002 */\n isManualStop: boolean;\n /** \u83B7\u53D6\u5F53\u524D\u8FD0\u884C\u4E2D BDS \u8FDB\u7A0B\u7684 PID\u3002 */\n getPid(): number;\n}\n\n/** BDS \u8FDB\u7A0B\u542F\u52A8\u9009\u9879\u3002 */\nexport interface BdsManagerOptions {\n detached?: boolean;\n}\n\n\ninterface CachedProc {\n process: ReturnType<typeof spawn> | null;\n isManualStop: boolean;\n crashRestart: boolean;\n crashDelayMs: number;\n exePath: string;\n bdsPath: string;\n}\n\nlet cached: CachedProc | null = null;\n\nfunction ensureProc(): CachedProc {\n if (cached) return cached;\n const cfg = loadConfig();\n const bds_path = path.resolve(cfg.bds_path || process.cwd());\n const exePath = bdsExePath(bds_path);\n\n cached = {\n process: null,\n isManualStop: false,\n crashRestart: cfg.crash_restart !== false,\n crashDelayMs: (cfg.crash_restart_delay ?? 5) * 1000,\n exePath,\n bdsPath: bds_path,\n };\n return cached;\n}\n\nexport function createBdsManager(options: BdsManagerOptions = {}): BdsManager {\n const events = new EventEmitter();\n events.setMaxListeners(100);\n\n const sendCommand = (cmd: string): boolean => {\n const p = ensureProc();\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !isProcessAliveSync(pid)) {\n log.warn(\"BDS \u672A\u8FD0\u884C\");\n return false;\n }\n if (p.process && p.process.pid === pid && p.process.stdin) {\n try {\n p.process.stdin.write(cmd + \"\\n\");\n log.info(`\u5DF2\u53D1\u9001\u547D\u4EE4: ${cmd}`);\n return true;\n } catch {\n return false;\n }\n }\n log.warn(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u547D\u4EE4\u5230 stdin\");\n return false;\n };\n\n const stop = async (): Promise<void> => {\n const p = ensureProc();\n // \u65E0\u8BBA\u81EA\u7BA1 / \u5916\u90E8\u8FDB\u7A0B\uFF0C\u624B\u52A8 stop \u90FD\u4E0D\u62A5 crash\n p.isManualStop = true;\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n const externalPids = await findBedrockServerPids();\n if (externalPids.length > 0) {\n log.info(\"\u68C0\u6D4B\u5230\u5916\u90E8\u8FD0\u884C\u7684 BDS \u8FDB\u7A0B\uFF0C\u4F7F\u7528 taskkill \u7EC8\u6B62...\");\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n void postBdsLifecycleEvent(\"stop\");\n return;\n }\n log.info(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n p.isManualStop = false;\n return;\n }\n\n if (p.process && p.process.pid === pid && p.process.stdin) {\n log.info(\"\u6B63\u5728\u5173\u95ED BDS...\");\n try {\n p.process.stdin.write(\"stop\\n\");\n } catch (e) {\n log.warn(`\u53D1\u9001 stop \u547D\u4EE4\u5931\u8D25: ${(e as Error).message}`);\n }\n\n await Promise.race([\n new Promise<void>((resolve) => p.process?.once(\"exit\", () => resolve())),\n new Promise<void>((resolve) =>\n setTimeout(() => {\n log.warn(\"30s \u8D85\u65F6\uFF0C\u5F3A\u5236\u7EC8\u6B62...\");\n try {\n p.process?.kill(\"SIGTERM\");\n } catch {}\n setTimeout(resolve, 5000);\n }, 30_000)\n ),\n ]);\n\n if (p.process && p.process.exitCode === null) {\n log.warn(\"\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B...\");\n try {\n p.process.kill(\"SIGKILL\");\n } catch {}\n }\n } else {\n // \u5916\u90E8\u542F\u52A8\u7684 BDS \u2014 fallback \u4F7F\u7528 taskkill / pkill\n log.info(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...\");\n await killBedrockServerByImage();\n void postBdsLifecycleEvent(\"stop\");\n }\n\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n };\n\n const start = async (): Promise<void> => {\n const p = ensureProc();\n const existing = readBdsPidFile(ROOT_DIR);\n if (existing && (await isProcessAlive(existing))) {\n log.info(\"BDS \u5DF2\u5728\u8FD0\u884C\u4E2D\");\n return;\n }\n\n if (!fs.existsSync(p.exePath)) {\n log.error(`\u672A\u627E\u5230 ${p.exePath}`);\n throw new Error(`BDS \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728: ${p.exePath}`);\n }\n\n // \u542F\u52A8\u524D\u8865\u9F50\u5FC5\u8981\u914D\u7F6E\u5E76\u672C\u5730\u5316\u6CE8\u91CA\uFF08\u5B89\u88C5\u9636\u6BB5\u4E5F\u4F1A\u5199\uFF1B\u65E7\u76EE\u5F55\u9996\u6B21\u542F\u52A8\u65F6\u8865\u4E0A\uFF09\n ensureEmitServerTelemetry(p.bdsPath, log);\n ensureNetherNetTransport(p.bdsPath, log);\n localizeServerProperties(p.bdsPath, { logger: log, rootDir: ROOT_DIR });\n ensureBdsExecutable(p.exePath);\n\n log.info(\"\u6B63\u5728\u542F\u52A8 BDS...\");\n p.isManualStop = false;\n const child = spawn(p.exePath, [], {\n cwd: p.bdsPath,\n env: { ...process.env, ...bdsSpawnEnvExtra(p.bdsPath) },\n stdio: options.detached ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"],\n detached: options.detached,\n windowsHide: true,\n });\n\n if (options.detached) child.unref();\n\n p.process = child;\n writeBdsPidFile(child.pid ?? 0, ROOT_DIR);\n log.info(`BDS \u5DF2\u542F\u52A8 (PID: ${child.pid})`);\n void postBdsLifecycleEvent(\"start\", `pid=${child.pid ?? 0}`);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n events.emit(\"output\", text);\n if (isMain()) process.stdout.write(text);\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = `[STDERR] ${chunk.toString()}`;\n events.emit(\"output\", text);\n if (isMain()) process.stderr.write(text);\n });\n\n child.on(\"exit\", (code) => {\n log.info(`BDS \u5DF2\u9000\u51FA (code: ${code})`);\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n const wasManual = p.isManualStop;\n if (!wasManual) {\n void postBdsLifecycleEvent(\"crash\", `code=${code ?? \"?\"}`);\n } else {\n void postBdsLifecycleEvent(\"stop\");\n }\n if (!wasManual && p.crashRestart && isMain()) {\n log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1000}s \u540E\u81EA\u52A8\u91CD\u542F...`);\n setTimeout(() => {\n start().catch((e) => log.error(`\u81EA\u52A8\u91CD\u542F\u5931\u8D25: ${e.message}`));\n }, p.crashDelayMs);\n }\n p.isManualStop = false;\n });\n };\n\n const status = async (): Promise<boolean> => {\n const pid = readBdsPidFile(ROOT_DIR);\n const alive = pid > 0 && (await isProcessAlive(pid));\n if (alive) {\n console.log(`BDS \u8FD0\u884C\u4E2D (PID: ${pid})`);\n } else {\n console.log(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n }\n return alive;\n };\n\n const watch = async (): Promise<void> => {\n log.info(\"\u542F\u52A8\u76D1\u62A4\u6A21\u5F0F...\");\n while (true) {\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n try {\n await start();\n } catch (e) {\n log.error(`\u81EA\u52A8\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n await new Promise((r) => setTimeout(r, 5_000));\n }\n };\n\n const getPid = (): number => {\n const p = ensureProc();\n return p.process?.pid ?? readBdsPidFile(ROOT_DIR);\n };\n\n return {\n start,\n stop,\n status,\n sendCommand,\n watch,\n events,\n isManualStop: false,\n getPid,\n };\n}\n\nexport const bdsEvents: EventEmitter = new EventEmitter();\nexport const bdsEvents_enabled = (): void => {\n bdsEvents.setMaxListeners(100);\n};\nbdsEvents_enabled();\n\n/** \u5224\u65AD\u5F53\u524D\u662F\u5426\u4F5C\u4E3A CLI \u4E3B\u5165\u53E3\u8FD0\u884C\uFF08\u88AB check-update \u7B49 import \u65F6\u4E3A false\uFF09 */\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\uFF08\u4E0E check-update \u5BF9\u79F0\uFF09\n if (process.env.SFMC_SERVICE === \"manager\") return true;\n return isMainModule(import.meta.url);\n}\n\n// CLI \u5165\u53E3\nif (isMain()) {\n const cmd = process.argv[2];\n const bds = createBdsManager();\n const args = process.argv.slice(3);\n\n switch (cmd) {\n case \"start\":\n bds.start().catch((e) => log.error(`\u542F\u52A8\u5931\u8D25: ${e.message}`));\n break;\n case \"stop\":\n bds.stop().catch((e) => log.error(`\u505C\u6B62\u5931\u8D25: ${e.message}`));\n break;\n case \"restart\":\n bds\n .stop()\n .then(() => bds.start())\n .catch((e) => log.error(`\u91CD\u542F\u5931\u8D25: ${e.message}`));\n break;\n case \"status\":\n bds.status();\n break;\n case \"send\":\n if (args[0]) bds.sendCommand(args.join(\" \"));\n else console.log(\"\u7528\u6CD5: node bds-manager.js send <command>\");\n break;\n case \"watch\":\n bds.watch().catch((e) => log.error(`\u76D1\u62A4\u5F02\u5E38: ${e.message}`));\n break;\n default:\n console.log(`\u7528\u6CD5:\n start \u542F\u52A8 BDS\n stop \u505C\u6B62\n restart \u91CD\u542F\n status \u68C0\u67E5\u72B6\u6001\n send <cmd> \u53D1\u9001\u547D\u4EE4\n watch \u76D1\u62A4\u6A21\u5F0F\uFF08\u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF09`);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,13 @@
1
- /**
2
- * changelog.ts — 更新日志抓取 (官方页面)
3
- */
1
+ /** changelog.ts — 按 BDS 版本查找 Minecraft 官方更新日志。 */
4
2
  import type { ChangelogPayload } from "./types.js";
5
- declare const CHANGELOG_BASE = "https://feedback.minecraft.net/hc/en-us/sections/360001186971";
6
- /** 抓取当前版本的更新说明 (文本 + 首张图 base64) */
7
- export declare function fetchChangelog(channel: string): Promise<ChangelogPayload | null>;
8
- export { CHANGELOG_BASE };
3
+ interface FeedbackArticle {
4
+ title: string;
5
+ html_url: string;
6
+ body: string;
7
+ }
8
+ export declare function changelogSectionUrl(channel: string): string;
9
+ export declare function findMatchingChangelogArticle(articles: FeedbackArticle[], channel: string, version: string): FeedbackArticle | null;
10
+ /** 只返回与目标版本匹配的文章;找不到时由调用方给出对应频道的目录。 */
11
+ export declare function fetchChangelog(channel: string, version: string): Promise<ChangelogPayload | null>;
12
+ export {};
9
13
  //# sourceMappingURL=changelog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,QAAA,MAAM,cAAc,kEAAkE,CAAC;AAUvF,oCAAoC;AACpC,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAwCtF;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAKlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG3D;AAuBD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,eAAe,GAAG,IAAI,CAYxB;AAED,uCAAuC;AACvC,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoCvG"}
package/dist/changelog.js CHANGED
@@ -1,48 +1,74 @@
1
1
  import { parse as parseHtml } from "node-html-parser";
2
- import { fetchJsonWithFallback, httpGetText } from "./http.js";
2
+ import { httpGetJson, httpRequest } from "./http.js";
3
3
  import { log } from "./log.js";
4
- const CHANGELOG_BASE = "https://feedback.minecraft.net/hc/en-us/sections/360001186971";
5
- function firstElement(root, selector) {
6
- return root.querySelector(selector);
4
+ const FEEDBACK_BASE = "https://feedback.minecraft.net";
5
+ const RELEASE_SECTION = "360001186971";
6
+ const PREVIEW_SECTION = "360001185332";
7
+ function changelogSectionUrl(channel) {
8
+ const section = channel === "preview" ? PREVIEW_SECTION : RELEASE_SECTION;
9
+ return `${FEEDBACK_BASE}/hc/en-us/sections/${section}`;
7
10
  }
8
- async function fetchChangelog(channel) {
11
+ function versionCandidates(version, channel) {
12
+ const parts = version.split(".");
13
+ if (parts[0] === "1" && Number(parts[1]) >= 26) parts.shift();
14
+ const full = parts.join(".");
15
+ if (channel === "preview") return [full];
16
+ return [full, parts.slice(0, 3).join("."), parts.slice(0, 2).join(".")];
17
+ }
18
+ function articleVersions(title) {
19
+ const found = [];
20
+ for (const match of title.matchAll(/\b(?:1\.)?\d+\.\d+(?:\.\d+)?(?:\/\d+)*\b/g)) {
21
+ const [first, ...rest] = match[0].split("/");
22
+ found.push(first);
23
+ const prefix = first.slice(0, first.lastIndexOf(".") + 1);
24
+ for (const suffix of rest) found.push(prefix + suffix);
25
+ }
26
+ return found;
27
+ }
28
+ function findMatchingChangelogArticle(articles, channel, version) {
29
+ const candidates = versionCandidates(version, channel);
30
+ return articles.find((article) => {
31
+ const title = article.title.toLowerCase();
32
+ const expectedChannel = channel === "preview" ? /\b(beta|preview)\b/.test(title) : /\bbedrock\b/.test(title) && !/\b(beta|preview)\b/.test(title);
33
+ return expectedChannel && articleVersions(title).some((v) => candidates.includes(v));
34
+ }) ?? null;
35
+ }
36
+ async function fetchChangelog(channel, version) {
37
+ const section = channel === "preview" ? PREVIEW_SECTION : RELEASE_SECTION;
9
38
  try {
10
- const url = `https://www.minecraft.net/en-us/article/${channel === "preview" ? "bedrock-beta" : "bedrock"}-update`;
11
- const html = await httpGetText(url);
12
- const root = parseHtml(html);
13
- const article = firstElement(root, "article") ?? firstElement(root, ".article-content") ?? firstElement(root, '[class*="content"]');
14
- if (!article) return null;
15
- const paragraphs = [];
16
- for (const el of article.querySelectorAll("p, h2, h3, li")) {
17
- const t = el.text.trim();
18
- if (t) paragraphs.push(t);
19
- }
20
- const text = paragraphs.join("\n").slice(0, 2e3);
21
- let imageBase64 = null;
22
- const firstImg = article.querySelector("img");
23
- if (firstImg) {
24
- const imgSrc = firstImg.getAttribute("src") ?? firstImg.getAttribute("data-src") ?? void 0;
25
- if (imgSrc) {
26
- try {
27
- const imgUrl = imgSrc.startsWith("http") ? imgSrc : `https://www.minecraft.net${imgSrc}`;
28
- const res = await fetchJsonWithFallback([], 15e3).catch(() => null);
29
- void res;
30
- const { httpRequest } = await import("./http.js");
31
- const img = await httpRequest(imgUrl);
32
- imageBase64 = img.body.toString("base64");
33
- } catch (e) {
34
- log.warn(`\u83B7\u53D6\u56FE\u7247\u5931\u8D25: ${e.message}`);
39
+ for (let page = 1; page <= 5; page++) {
40
+ const url = `${FEEDBACK_BASE}/api/v2/help_center/en-us/sections/${section}/articles.json?per_page=10&page=${page}`;
41
+ const result = await httpGetJson(url, { totalTimeoutMs: 15e3 });
42
+ const article = findMatchingChangelogArticle(result.articles, channel, version);
43
+ if (article) {
44
+ const root = parseHtml(article.body);
45
+ const paragraphs = root.querySelectorAll("p, h2, h3, li").map((el) => el.text.trim()).filter(Boolean);
46
+ const text = paragraphs.join("\n").slice(0, 2e3);
47
+ if (!text) return null;
48
+ let imageBase64 = null;
49
+ const imgSrc = root.querySelector("img")?.getAttribute("src");
50
+ if (imgSrc) {
51
+ try {
52
+ const imgUrl = new URL(imgSrc, article.html_url).href;
53
+ const image = await httpRequest(imgUrl, { totalTimeoutMs: 15e3 });
54
+ imageBase64 = image.body.toString("base64");
55
+ } catch (e) {
56
+ log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u56FE\u7247\u5931\u8D25: ${e.message}`);
57
+ }
35
58
  }
59
+ return { text, imageBase64, url: article.html_url };
36
60
  }
61
+ if (!result.next_page) break;
37
62
  }
38
- return { text: text.slice(0, 2e3), imageBase64 };
63
+ log.warn(`\u672A\u627E\u5230 BDS ${version} (${channel}) \u5BF9\u5E94\u7684\u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7`);
39
64
  } catch (e) {
40
65
  log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u5931\u8D25: ${e.message}`);
41
- return null;
42
66
  }
67
+ return null;
43
68
  }
44
69
  export {
45
- CHANGELOG_BASE,
46
- fetchChangelog
70
+ changelogSectionUrl,
71
+ fetchChangelog,
72
+ findMatchingChangelogArticle
47
73
  };
48
74
  //# sourceMappingURL=changelog.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/changelog.ts"],
4
- "sourcesContent": ["/**\n * changelog.ts \u2014 \u66F4\u65B0\u65E5\u5FD7\u6293\u53D6 (\u5B98\u65B9\u9875\u9762)\n */\n\nimport { parse as parseHtml, HTMLElement } from \"node-html-parser\";\nimport { fetchJsonWithFallback, httpGetText } from \"./http.js\";\nimport type { ChangelogPayload } from \"./types.js\";\nimport { log } from \"./log.js\";\n\nconst CHANGELOG_BASE = \"https://feedback.minecraft.net/hc/en-us/sections/360001186971\";\n\n/**\n * node-html-parser \u7684 API \u4E0E cheerio \u7565\u4E0D\u540C:\u5B83\u6CA1\u6709 first/length \u94FE\u5F0F\u5224\u65AD,\n * \u800C\u662F\u7528 querySelector + \u8282\u70B9\u5C5E\u6027\u3002\u8FD9\u91CC\u5305\u4E00\u5C42\u5C0F\u5DE5\u5177\u8BA9\u9009\u62E9\u5B50\u8BED\u4E49\u96C6\u4E2D\u3002\n */\nfunction firstElement(root: HTMLElement, selector: string): HTMLElement | null {\n return root.querySelector(selector);\n}\n\n/** \u6293\u53D6\u5F53\u524D\u7248\u672C\u7684\u66F4\u65B0\u8BF4\u660E (\u6587\u672C + \u9996\u5F20\u56FE base64) */\nexport async function fetchChangelog(channel: string): Promise<ChangelogPayload | null> {\n try {\n const url = `https://www.minecraft.net/en-us/article/${channel === \"preview\" ? \"bedrock-beta\" : \"bedrock\"}-update`;\n const html = await httpGetText(url);\n const root = parseHtml(html);\n const article = firstElement(root, \"article\")\n ?? firstElement(root, \".article-content\")\n ?? firstElement(root, '[class*=\"content\"]');\n if (!article) return null;\n\n const paragraphs: string[] = [];\n for (const el of article.querySelectorAll(\"p, h2, h3, li\")) {\n const t = el.text.trim();\n if (t) paragraphs.push(t);\n }\n const text = paragraphs.join(\"\\n\").slice(0, 2000);\n\n let imageBase64: string | null = null;\n const firstImg = article.querySelector(\"img\");\n if (firstImg) {\n const imgSrc: string | undefined = firstImg.getAttribute(\"src\") ?? firstImg.getAttribute(\"data-src\") ?? undefined;\n if (imgSrc) {\n try {\n const imgUrl = imgSrc.startsWith(\"http\") ? imgSrc : `https://www.minecraft.net${imgSrc}`;\n const res = await fetchJsonWithFallback([], 15_000).catch(() => null);\n void res;\n const { httpRequest } = await import(\"./http.js\");\n const img = await httpRequest(imgUrl);\n imageBase64 = img.body.toString(\"base64\");\n } catch (e) {\n log.warn(`\u83B7\u53D6\u56FE\u7247\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n }\n\n return { text: text.slice(0, 2000), imageBase64 };\n } catch (e) {\n log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u5931\u8D25: ${(e as Error).message}`);\n return null;\n }\n}\n\nexport { CHANGELOG_BASE };\n"],
5
- "mappings": "AAIA,SAAS,SAAS,iBAA8B;AAChD,SAAS,uBAAuB,mBAAmB;AAEnD,SAAS,WAAW;AAEpB,MAAM,iBAAiB;AAMvB,SAAS,aAAa,MAAmB,UAAsC;AAC7E,SAAO,KAAK,cAAc,QAAQ;AACpC;AAGA,eAAsB,eAAe,SAAmD;AACtF,MAAI;AACF,UAAM,MAAM,2CAA2C,YAAY,YAAY,iBAAiB,SAAS;AACzG,UAAM,OAAO,MAAM,YAAY,GAAG;AAClC,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,UAAU,aAAa,MAAM,SAAS,KACvC,aAAa,MAAM,kBAAkB,KACrC,aAAa,MAAM,oBAAoB;AAC5C,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,aAAuB,CAAC;AAC9B,eAAW,MAAM,QAAQ,iBAAiB,eAAe,GAAG;AAC1D,YAAM,IAAI,GAAG,KAAK,KAAK;AACvB,UAAI,EAAG,YAAW,KAAK,CAAC;AAAA,IAC1B;AACA,UAAM,OAAO,WAAW,KAAK,IAAI,EAAE,MAAM,GAAG,GAAI;AAEhD,QAAI,cAA6B;AACjC,UAAM,WAAW,QAAQ,cAAc,KAAK;AAC5C,QAAI,UAAU;AACZ,YAAM,SAA6B,SAAS,aAAa,KAAK,KAAK,SAAS,aAAa,UAAU,KAAK;AACxG,UAAI,QAAQ;AACV,YAAI;AACF,gBAAM,SAAS,OAAO,WAAW,MAAM,IAAI,SAAS,4BAA4B,MAAM;AACtF,gBAAM,MAAM,MAAM,sBAAsB,CAAC,GAAG,IAAM,EAAE,MAAM,MAAM,IAAI;AACpE,eAAK;AACL,gBAAM,EAAE,YAAY,IAAI,MAAM,OAAO,WAAW;AAChD,gBAAM,MAAM,MAAM,YAAY,MAAM;AACpC,wBAAc,IAAI,KAAK,SAAS,QAAQ;AAAA,QAC1C,SAAS,GAAG;AACV,cAAI,KAAK,yCAAY,EAAY,OAAO,EAAE;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,KAAK,MAAM,GAAG,GAAI,GAAG,YAAY;AAAA,EAClD,SAAS,GAAG;AACV,QAAI,KAAK,qDAAc,EAAY,OAAO,EAAE;AAC5C,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["/** changelog.ts \u2014 \u6309 BDS \u7248\u672C\u67E5\u627E Minecraft \u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7\u3002 */\n\nimport { parse as parseHtml } from \"node-html-parser\";\nimport { httpGetJson, httpRequest } from \"./http.js\";\nimport { log } from \"./log.js\";\nimport type { ChangelogPayload } from \"./types.js\";\n\nconst FEEDBACK_BASE = \"https://feedback.minecraft.net\";\nconst RELEASE_SECTION = \"360001186971\";\nconst PREVIEW_SECTION = \"360001185332\";\n\ninterface FeedbackArticle {\n title: string;\n html_url: string;\n body: string;\n}\n\ninterface FeedbackPage {\n articles: FeedbackArticle[];\n next_page: string | null;\n}\n\nexport function changelogSectionUrl(channel: string): string {\n const section = channel === \"preview\" ? PREVIEW_SECTION : RELEASE_SECTION;\n return `${FEEDBACK_BASE}/hc/en-us/sections/${section}`;\n}\n\n/** BDS \u7248\u672C\u591A\u4E00\u4E2A\u6784\u5EFA\u53F7\uFF1B\u6B63\u5F0F\u7248\u6587\u7AE0\u901A\u5E38\u53EA\u5199\u524D\u4E09\u6BB5\uFF0C\u9884\u89C8\u7248\u9700\u4FDD\u7559\u6784\u5EFA\u53F7\u3002 */\nfunction versionCandidates(version: string, channel: string): string[] {\n const parts = version.split(\".\");\n if (parts[0] === \"1\" && Number(parts[1]) >= 26) parts.shift();\n const full = parts.join(\".\");\n if (channel === \"preview\") return [full];\n return [full, parts.slice(0, 3).join(\".\"), parts.slice(0, 2).join(\".\")];\n}\n\n/** \u517C\u5BB9 26.44/45 \u548C 1.21.93/94 \u8FD9\u7C7B\u5408\u5E76\u53D1\u5E03\u7684\u5B98\u65B9\u6807\u9898\u3002 */\nfunction articleVersions(title: string): string[] {\n const found: string[] = [];\n for (const match of title.matchAll(/\\b(?:1\\.)?\\d+\\.\\d+(?:\\.\\d+)?(?:\\/\\d+)*\\b/g)) {\n const [first, ...rest] = match[0].split(\"/\");\n found.push(first!);\n const prefix = first!.slice(0, first!.lastIndexOf(\".\") + 1);\n for (const suffix of rest) found.push(prefix + suffix);\n }\n return found;\n}\n\nexport function findMatchingChangelogArticle(\n articles: FeedbackArticle[],\n channel: string,\n version: string\n): FeedbackArticle | null {\n const candidates = versionCandidates(version, channel);\n return (\n articles.find((article) => {\n const title = article.title.toLowerCase();\n const expectedChannel =\n channel === \"preview\"\n ? /\\b(beta|preview)\\b/.test(title)\n : /\\bbedrock\\b/.test(title) && !/\\b(beta|preview)\\b/.test(title);\n return expectedChannel && articleVersions(title).some((v) => candidates.includes(v));\n }) ?? null\n );\n}\n\n/** \u53EA\u8FD4\u56DE\u4E0E\u76EE\u6807\u7248\u672C\u5339\u914D\u7684\u6587\u7AE0\uFF1B\u627E\u4E0D\u5230\u65F6\u7531\u8C03\u7528\u65B9\u7ED9\u51FA\u5BF9\u5E94\u9891\u9053\u7684\u76EE\u5F55\u3002 */\nexport async function fetchChangelog(channel: string, version: string): Promise<ChangelogPayload | null> {\n const section = channel === \"preview\" ? PREVIEW_SECTION : RELEASE_SECTION;\n try {\n for (let page = 1; page <= 5; page++) {\n const url = `${FEEDBACK_BASE}/api/v2/help_center/en-us/sections/${section}/articles.json?per_page=10&page=${page}`;\n const result = await httpGetJson<FeedbackPage>(url, { totalTimeoutMs: 15_000 });\n const article = findMatchingChangelogArticle(result.articles, channel, version);\n if (article) {\n const root = parseHtml(article.body);\n const paragraphs = root\n .querySelectorAll(\"p, h2, h3, li\")\n .map((el) => el.text.trim())\n .filter(Boolean);\n const text = paragraphs.join(\"\\n\").slice(0, 2000);\n if (!text) return null;\n\n let imageBase64: string | null = null;\n const imgSrc = root.querySelector(\"img\")?.getAttribute(\"src\");\n if (imgSrc) {\n try {\n const imgUrl = new URL(imgSrc, article.html_url).href;\n const image = await httpRequest(imgUrl, { totalTimeoutMs: 15_000 });\n imageBase64 = image.body.toString(\"base64\");\n } catch (e) {\n log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u56FE\u7247\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n return { text, imageBase64, url: article.html_url };\n }\n if (!result.next_page) break;\n }\n log.warn(`\u672A\u627E\u5230 BDS ${version} (${channel}) \u5BF9\u5E94\u7684\u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7`);\n } catch (e) {\n log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u5931\u8D25: ${(e as Error).message}`);\n }\n return null;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"check-update.d.ts","sourceRoot":"","sources":["../src/check-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwJH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAwRjD"}
1
+ {"version":3,"file":"check-update.d.ts","sourceRoot":"","sources":["../src/check-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkKH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CA6SjD"}
@@ -1,18 +1,15 @@
1
- import {
2
- bindByteProgressToBar,
3
- createNodeServiceLogger,
4
- createTerminalProgress
5
- } from "@sfmc-bds/sdk/logs";
1
+ import { bindByteProgressToBar, createNodeServiceLogger, createTerminalProgress } from "@sfmc-bds/sdk/logs";
6
2
  import fs from "node:fs";
7
3
  import os from "node:os";
8
4
  import path from "node:path";
9
5
  import { createBdsManager } from "./bds-manager.js";
10
- import { CHANGELOG_BASE, fetchChangelog } from "./changelog.js";
6
+ import { changelogSectionUrl, fetchChangelog } from "./changelog.js";
11
7
  import { copyDirSync, copyFileSyncSafe, emptyDirSync, hashFileAsync, rmSafe } from "./fsx.js";
12
8
  import { bdsExePath, bdsInstallRequiredFiles, ensureBdsExecutable } from "./host-platform.js";
13
9
  import { httpDownload } from "./http.js";
14
10
  import { isMainModule } from "./is-main.js";
15
11
  import { loadConfig, LOG_PATH, resolvePaths, ROOT_DIR } from "./paths.js";
12
+ import { probeBdsStatus } from "./process-probe.js";
16
13
  import { sendText, sendWithImage } from "./qqutil.js";
17
14
  import {
18
15
  clearRollbackMarker,
@@ -21,7 +18,7 @@ import {
21
18
  verifyBdsInstall,
22
19
  writeRollbackMarker
23
20
  } from "./rollback.js";
24
- import { ensureEmitServerTelemetry, localizeServerProperties } from "./server-properties.js";
21
+ import { ensureEmitServerTelemetry, ensureNetherNetTransport, localizeServerProperties } from "./server-properties.js";
25
22
  import { clearTaskbarProgress, isTaskbarSupported, setTaskbarProgress } from "./taskbar.js";
26
23
  import { emitUpdateKv, emitUpdateResult } from "./update-result.js";
27
24
  import {
@@ -99,6 +96,17 @@ async function restorePreserves(bdsPath, backupDir, preserve) {
99
96
  }
100
97
  }
101
98
  }
99
+ async function waitForBdsStopped(timeoutMs = 1e4) {
100
+ const deadline = Date.now() + timeoutMs;
101
+ let status = await probeBdsStatus({ rootDir: ROOT_DIR });
102
+ while (status.state !== "stopped" && Date.now() < deadline) {
103
+ await new Promise((resolve) => setTimeout(resolve, 250));
104
+ status = await probeBdsStatus({ rootDir: ROOT_DIR });
105
+ }
106
+ if (status.state !== "stopped") {
107
+ throw new Error(`BDS \u8FDB\u7A0B ${status.pid} \u4ECD\u5728\u8FD0\u884C\uFF0C\u5DF2\u53D6\u6D88\u6587\u4EF6\u8986\u76D6`);
108
+ }
109
+ }
102
110
  async function extractZipToBds(zipPath, destDir) {
103
111
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "bds-extract-"));
104
112
  try {
@@ -195,17 +203,17 @@ async function runUpdate() {
195
203
 
196
204
  \u670D\u52A1\u5668\u5373\u5C06\u5F00\u59CB\u66F4\u65B0~ \u8BF7\u8010\u5FC3\u7B49\u5F85^(*\uFFE3(oo)\uFFE3)^`
197
205
  );
198
- const cl = await fetchChangelog(channel);
206
+ const cl = await fetchChangelog(channel, latestVer);
199
207
  if (cl) {
200
- const text = `\u{1F4CB} \u66F4\u65B0\u5185\u5BB9\u6982\u8981
208
+ const text = `\u{1F4CB} ${latestVer} \u66F4\u65B0\u5185\u5BB9\u6982\u8981
201
209
 
202
210
  ${cl.text.slice(0, 1500)}
203
211
 
204
- \u5B8C\u6574\u65E5\u5FD7: ${CHANGELOG_BASE}`;
212
+ \u5B8C\u6574\u65E5\u5FD7: ${cl.url}`;
205
213
  if (cl.imageBase64) await sendWithImage(text, cl.imageBase64);
206
214
  else await sendText(text);
207
215
  } else {
208
- await sendText(`\u{1F4CB} \u66F4\u65B0\u65E5\u5FD7: ${CHANGELOG_BASE}`);
216
+ await sendText(`\u{1F4CB} ${latestVer} \u66F4\u65B0\u65E5\u5FD7\u76EE\u5F55: ${changelogSectionUrl(channel)}`);
209
217
  }
210
218
  }
211
219
  let backupInfo;
@@ -237,9 +245,16 @@ ${cl.text.slice(0, 1500)}
237
245
  try {
238
246
  log.info("\u505C\u6B62 BDS \u670D\u52A1...");
239
247
  await bds.stop();
248
+ await waitForBdsStopped();
240
249
  log.info("BDS \u5DF2\u505C\u6B62");
241
250
  } catch (e) {
242
- log.warn(`BDS \u505C\u6B62\u5F02\u5E38: ${e.message}`);
251
+ log.error(`BDS \u505C\u6B62\u5931\u8D25: ${e.message}`);
252
+ await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25
253
+
254
+ \u505C\u670D\u5931\u8D25: ${e.message}
255
+ \u64CD\u4F5C\u5DF2\u4E2D\u6B62`);
256
+ clearRollbackMarker();
257
+ return 1;
243
258
  }
244
259
  const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), "bds-update-"));
245
260
  const zipPath = path.join(stagingDir, `bedrock-server-${latestVer}.zip`);
@@ -255,7 +270,7 @@ ${cl.text.slice(0, 1500)}
255
270
  for (const url of downloadUrls) {
256
271
  const progressBar = createTerminalProgress({
257
272
  stream: process.stderr,
258
- logger: (msg) => log.info(msg.startsWith("\u8FDB\u5EA6") ? `\u4E0B\u8F7D${msg.slice(2)}` : msg),
273
+ logger: (msg) => log.info(msg),
259
274
  format: "\u4E0B\u8F7D\u8FDB\u5EA6 | {bar} | {percentage}% | {value}/{total} MB | \u901F\u5EA6: {speed}"
260
275
  });
261
276
  const onByteProgress = bindByteProgressToBar(progressBar, {
@@ -312,6 +327,18 @@ BDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);
312
327
  rmSafe(stagingDir);
313
328
  return 1;
314
329
  }
330
+ try {
331
+ await waitForBdsStopped();
332
+ } catch (e) {
333
+ log.error(`\u90E8\u7F72\u5DF2\u4E2D\u6B62: ${e.message}`);
334
+ await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25
335
+
336
+ ${e.message}
337
+ \u5C1A\u672A\u8986\u76D6\u5B89\u88C5\u76EE\u5F55`);
338
+ rmSafe(stagingDir);
339
+ clearRollbackMarker();
340
+ return 1;
341
+ }
315
342
  try {
316
343
  log.info("\u89E3\u538B\u4E2D...");
317
344
  emptyDirSync(bdsPath);
@@ -320,6 +347,15 @@ BDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);
320
347
  log.info("\u89E3\u538B\u5B8C\u6210");
321
348
  } catch (e) {
322
349
  log.error(`\u89E3\u538B\u5931\u8D25: ${e.message}`);
350
+ const status = await probeBdsStatus({ rootDir: ROOT_DIR });
351
+ if (status.state !== "stopped") {
352
+ await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25
353
+
354
+ \u89E3\u538B\u5931\u8D25: ${e.message}
355
+ BDS \u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u670D\u5E76\u624B\u52A8\u6062\u590D`);
356
+ rmSafe(stagingDir);
357
+ return 1;
358
+ }
323
359
  await sendText(`\u274C BDS \u66F4\u65B0\u5931\u8D25
324
360
 
325
361
  \u89E3\u538B\u5931\u8D25: ${e.message}
@@ -344,6 +380,7 @@ BDS \u5DF2\u505C\u6B62\uFF0C\u8BF7\u624B\u52A8\u6062\u590D`);
344
380
  }
345
381
  await restorePreserves(bdsPath, backupInfo.path, preserve);
346
382
  ensureEmitServerTelemetry(bdsPath, log);
383
+ ensureNetherNetTransport(bdsPath, log);
347
384
  localizeServerProperties(bdsPath, { logger: log, rootDir: ROOT_DIR });
348
385
  const newExeHash = await hashFileAsync(exePath, "sha256").catch(() => "");
349
386
  if (newExeHash) {