120 lines
4.0 KiB
JavaScript
120 lines
4.0 KiB
JavaScript
/**
|
||
* 统一运行时包定义 — 本地 static/unity 与 OSS unitycdndir 内容完全一致。
|
||
*
|
||
* 运行时包(唯一真相):
|
||
* Build/
|
||
* StreamingAssets/
|
||
* levels-db-index.json
|
||
* levels-db-index.json.br
|
||
* StreamingAssets/aa/levels-db/ (默认仅 .json.br)
|
||
* 可选: levels-database.json(.br) — KEEP_LEGACY_DB=1
|
||
*
|
||
* 不含 index.html / TemplateData(仅 standalone-player 独立调试页使用)
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const RUNTIME_ROOT_FILES = [
|
||
'levels-database.json',
|
||
'levels-database.json.br',
|
||
'levels-db-index.json',
|
||
'levels-db-index.json.br',
|
||
];
|
||
const RUNTIME_DIRS = ['Build', 'StreamingAssets'];
|
||
|
||
function walkFiles(root, bucket, prefix = '') {
|
||
if (!fs.existsSync(root)) return;
|
||
for (const ent of fs.readdirSync(root, { withFileTypes: true })) {
|
||
if (ent.name === '.DS_Store') continue;
|
||
const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
|
||
const full = path.join(root, ent.name);
|
||
if (ent.isDirectory()) walkFiles(full, bucket, rel);
|
||
else bucket.push({ rel, full, size: fs.statSync(full).size });
|
||
}
|
||
}
|
||
|
||
/** 列出运行时包内所有相对路径(用于 manifest / 校验) */
|
||
function listRuntimeFiles(packDir) {
|
||
const files = [];
|
||
for (const dir of RUNTIME_DIRS) {
|
||
walkFiles(path.join(packDir, dir), files, dir);
|
||
}
|
||
for (const name of RUNTIME_ROOT_FILES) {
|
||
const full = path.join(packDir, name);
|
||
if (fs.existsSync(full)) {
|
||
files.push({ rel: name, full, size: fs.statSync(full).size });
|
||
}
|
||
}
|
||
return files.sort((a, b) => a.rel.localeCompare(b.rel));
|
||
}
|
||
|
||
function existsPlainOrBr(filePath) {
|
||
if (fs.existsSync(filePath) && fs.statSync(filePath).size > 0) return true;
|
||
const br = `${filePath}.br`;
|
||
return fs.existsSync(br) && fs.statSync(br).size > 0;
|
||
}
|
||
|
||
function readJsonPlainOrBr(filePath) {
|
||
if (fs.existsSync(filePath)) {
|
||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||
}
|
||
throw new Error(`缺少 JSON: ${filePath}`);
|
||
}
|
||
|
||
function bundleNamesFromCatalog(catalogPath) {
|
||
if (!fs.existsSync(catalogPath)) return [];
|
||
const catalog = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
|
||
return [...new Set(
|
||
(catalog.m_InternalIds || [])
|
||
.filter((id) => String(id).includes('.bundle'))
|
||
.map((id) => String(id).split('/').pop()),
|
||
)].sort();
|
||
}
|
||
|
||
function assertRuntimePack(packDir, opts) {
|
||
opts = opts || {};
|
||
const loader = path.join(packDir, 'Build/mstest5.loader.js');
|
||
const catalog = path.join(packDir, 'StreamingAssets/aa/catalog.json');
|
||
const levelsManifest = path.join(packDir, 'StreamingAssets/aa/levels-manifest.json');
|
||
if (!fs.existsSync(loader)) {
|
||
throw new Error(`缺少运行时包: ${loader}`);
|
||
}
|
||
if (!existsPlainOrBr(catalog)) {
|
||
throw new Error(`缺少运行时包: ${catalog}(.br)`);
|
||
}
|
||
if (!existsPlainOrBr(path.join(packDir, 'levels-db-index.json'))) {
|
||
throw new Error(`缺少运行时包: ${path.join(packDir, 'levels-db-index.json')}(.br)`);
|
||
}
|
||
const names = fs.existsSync(catalog) ? bundleNamesFromCatalog(catalog) : [];
|
||
if (opts.requireLevelsBundle && !names.some((n) => n.includes('levels_all'))) {
|
||
throw new Error('catalog 缺少 levels_all 分包');
|
||
}
|
||
if (opts.requireLevelsManifest && !existsPlainOrBr(levelsManifest)) {
|
||
throw new Error(`缺少运行时包: ${levelsManifest}(.br)`);
|
||
}
|
||
if (opts.requireLevelsManifest && fs.existsSync(levelsManifest)) {
|
||
const manifest = readJsonPlainOrBr(levelsManifest);
|
||
const count = Object.keys(manifest.levels || {}).length;
|
||
if (count < 1) throw new Error('levels-manifest.json 无关卡条目');
|
||
}
|
||
for (const name of names) {
|
||
const p = path.join(packDir, 'StreamingAssets/aa/WebGL', name);
|
||
if (!existsPlainOrBr(p)) {
|
||
throw new Error(`bundle 无效: ${p}(.br)`);
|
||
}
|
||
const br = `${p}.br`;
|
||
const sz = fs.existsSync(p) ? fs.statSync(p).size : fs.statSync(br).size;
|
||
if (sz < 50) throw new Error(`bundle 过小: ${p}`);
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
RUNTIME_ROOT_FILES,
|
||
RUNTIME_DIRS,
|
||
walkFiles,
|
||
listRuntimeFiles,
|
||
existsPlainOrBr,
|
||
bundleNamesFromCatalog,
|
||
assertRuntimePack,
|
||
};
|