同步主站 Cocos 当前工程:关卡资源、运行脚本与打包工具。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user