同步主站 Cocos 当前工程:关卡资源、运行脚本与打包工具。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-12 14:40:34 +08:00
parent ae75fce80b
commit fac515a669
5609 changed files with 27744634 additions and 5653 deletions

View File

@@ -2,9 +2,10 @@
/**
* 将 build/assets/level-prefabs 拆为:
* - shell: config.json + index.js进 assets_all 首屏)
* - 每关一包: assets/level-prefabs/import/.../*.json
* - 每 15 关一包: assets/level-prefabs/import/.../*.json
*
* 输出 levels-manifest.json 供 loader 按 levelId 按需下载。
* 输出 levels-manifest.json 供 loader 按 levelId 按需下载(同包关卡共享 bundle
* LEVELS_PER_PACK 可覆盖每包关卡数,默认 15。
*/
const crypto = require('crypto');
const fs = require('fs');
@@ -12,6 +13,8 @@ const path = require('path');
const { execSync } = require('child_process');
const { formatBytes } = require('./package-optimize');
const DEFAULT_LEVELS_PER_PACK = 15;
function mkdirp(p) { fs.mkdirSync(p, { recursive: true }); }
function hashFileMd5(filePath) {
@@ -35,6 +38,11 @@ function walkJsonFiles(dir, out = []) {
return out;
}
function levelsPerPack() {
const n = parseInt(process.env.LEVELS_PER_PACK || String(DEFAULT_LEVELS_PER_PACK), 10);
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LEVELS_PER_PACK;
}
/** 扫描 import/*.json从预制体名 Level{id} 建立 levelId → 相对路径 */
function indexImportFiles(levelPrefabsDir) {
const importRoot = path.join(levelPrefabsDir, 'import');
@@ -80,52 +88,68 @@ function splitLevelBundles(levelPrefabsDir, webglDir, manifestOutPath, packTmpDi
throw new Error(`缺少 level-prefabs/config.json: ${levelPrefabsDir}`);
}
const packSize = levelsPerPack();
const configLevels = readConfigLevels(levelPrefabsDir);
const importByLevel = indexImportFiles(levelPrefabsDir);
mkdirp(webglDir);
const stageBase = packTmpDir || path.join(webglDir, '..', '.level-pack-tmp');
mkdirp(stageBase);
const manifest = {
version: 1,
shell: ['assets/level-prefabs/config.json', 'assets/level-prefabs/index.js'],
levels: {},
};
let packed = 0;
let totalBytes = 0;
const ready = [];
const missing = [];
for (const [levelId, meta] of configLevels) {
const importRel = importByLevel.get(levelId);
if (!importRel) {
missing.push(levelId);
continue;
}
const stageRoot = path.join(stageBase, levelId);
const assetRoot = path.join(stageRoot, 'assets', 'level-prefabs');
mkdirp(path.dirname(path.join(assetRoot, importRel)));
fs.copyFileSync(
path.join(levelPrefabsDir, importRel),
path.join(assetRoot, importRel),
);
ready.push({ levelId, meta, importRel });
}
ready.sort((a, b) => Number(a.levelId) - Number(b.levelId));
const zipTmp = path.join(stageBase, `${levelId}.zip`);
const manifest = {
version: 2,
packSize,
shell: ['assets/level-prefabs/config.json', 'assets/level-prefabs/index.js'],
levels: {},
};
let packed = 0;
let packs = 0;
let totalBytes = 0;
for (let i = 0; i < ready.length; i += packSize) {
const group = ready.slice(i, i + packSize);
const firstId = group[0].levelId;
const lastId = group[group.length - 1].levelId;
const stageRoot = path.join(stageBase, `pack-${firstId}-${lastId}`);
const assetRoot = path.join(stageRoot, 'assets', 'level-prefabs');
for (const item of group) {
mkdirp(path.dirname(path.join(assetRoot, item.importRel)));
fs.copyFileSync(
path.join(levelPrefabsDir, item.importRel),
path.join(assetRoot, item.importRel),
);
}
const zipTmp = path.join(stageBase, `pack-${firstId}-${lastId}.zip`);
zipDir(stageRoot, zipTmp);
const hash = hashFileMd5(zipTmp);
const bundleName = `defaultlocalgroup_level_${levelId}_${hash}.bundle`;
const bundleName = `defaultlocalgroup_levels_${firstId}_${lastId}_${hash}.bundle`;
fs.copyFileSync(zipTmp, path.join(webglDir, bundleName));
const size = fs.statSync(zipTmp).size;
totalBytes += size;
packs += 1;
for (const item of group) {
manifest.levels[item.levelId] = {
bundle: bundleName,
uuid: item.meta.uuid,
};
packed += 1;
}
manifest.levels[levelId] = {
bundle: bundleName,
path: meta.path,
uuid: meta.uuid,
files: [`assets/level-prefabs/${importRel}`],
bytes: size,
};
packed += 1;
fs.rmSync(stageRoot, { recursive: true, force: true });
fs.unlinkSync(zipTmp);
}
@@ -134,14 +158,15 @@ function splitLevelBundles(levelPrefabsDir, webglDir, manifestOutPath, packTmpDi
fs.writeFileSync(manifestOutPath, JSON.stringify(manifest), 'utf8');
console.log(`>>> 关卡分包: ${packed} 关, 合计 ${formatBytes(totalBytes)}, 均 ${formatBytes(Math.round(totalBytes / Math.max(packed, 1)))}/`);
console.log(`>>> 关卡分包: ${packed} / ${packs} 包 (${packSize} 关/包), 合计 ${formatBytes(totalBytes)}, 均 ${formatBytes(Math.round(totalBytes / Math.max(packs, 1)))}/`);
if (missing.length) {
console.warn(`>>> 警告: ${missing.length} 关在 config 中无 import 文件 (例: ${missing.slice(0, 5).join(', ')})`);
}
return { manifest, packed, missing, totalBytes };
return { manifest, packed, packs, packSize, missing, totalBytes };
}
module.exports = {
DEFAULT_LEVELS_PER_PACK,
splitLevelBundles,
indexImportFiles,
readConfigLevels,