#!/usr/bin/env node /** * 将 levels-database.json 拆为 index + 分片,首屏只下 index(~几 KB)。 * * 输出: * /levels-db-index.json (+ .br) * /StreamingAssets/aa/levels-db/-.json (+ .br) */ const fs = require('fs'); const path = require('path'); const { brotliCompressFile, formatBytes } = require('./package-optimize'); const DEFAULT_SHARD_SIZE = 100; function mkdirp(p) { fs.mkdirSync(p, { recursive: true }); } /** * @param {string} srcPath assets/level-data/levels-database.json * @param {string} outDir 运行时包根目录 * @param {{ shardSize?: number }} opts */ function splitLevelsDatabase(srcPath, outDir, opts = {}) { const shardSize = opts.shardSize || DEFAULT_SHARD_SIZE; const raw = JSON.parse(fs.readFileSync(srcPath, 'utf8')); const levels = raw.levels || {}; const ids = Object.keys(levels) .map((k) => parseInt(k, 10)) .filter((n) => !Number.isNaN(n)) .sort((a, b) => a - b); if (ids.length < 1) { throw new Error(`关卡库为空: ${srcPath}`); } const min = ids[0]; const max = ids[ids.length - 1]; const shardDir = path.join(outDir, 'StreamingAssets', 'aa', 'levels-db'); mkdirp(shardDir); const shards = []; for (let start = min; start <= max; start += shardSize) { const end = Math.min(start + shardSize - 1, max); const slice = {}; for (const id of ids) { if (id >= start && id <= end) { slice[String(id)] = levels[String(id)]; } } if (!Object.keys(slice).length) continue; const fileName = `${start}-${end}.json`; const relFile = `levels-db/${fileName}`; const absPath = path.join(shardDir, fileName); fs.writeFileSync(absPath, JSON.stringify({ levels: slice }), 'utf8'); const br = brotliCompressFile(absPath, absPath + '.br'); console.log(`>>> levels-db/${fileName}: ${formatBytes(br.raw)} → .br ${formatBytes(br.br)}`); shards.push({ min: start, max: end, file: relFile, count: Object.keys(slice).length }); } const index = { version: 2, mode: 'sharded', shardSize, levelIdBase: raw.levelIdBase, generatedAt: raw.generatedAt || new Date().toISOString(), source: raw.source || 'split-levels-database', total: ids.length, min, max, stats: raw.stats, shards, }; const indexPath = path.join(outDir, 'levels-db-index.json'); fs.writeFileSync(indexPath, JSON.stringify(index), 'utf8'); const indexBr = brotliCompressFile(indexPath, indexPath + '.br'); console.log(`>>> levels-db-index.json: ${formatBytes(indexBr.raw)} → .br ${formatBytes(indexBr.br)}`); console.log(`>>> 关卡库分片: ${shards.length} 片, ${ids.length} 关 (${min}–${max})`); return { index, shards, total: ids.length }; } module.exports = { splitLevelsDatabase, DEFAULT_SHARD_SIZE }; if (require.main === module) { const src = path.resolve(process.argv[2]); const out = path.resolve(process.argv[3]); if (!src || !out) { console.error('Usage: split-levels-database.js '); process.exit(1); } splitLevelsDatabase(src, out); }