'use strict'; const check = (ok, message) => { if (!ok) throw Error(message); }; const integer = (n, min = 0) => Number.isSafeInteger(n) && n >= min && n <= 100000; const digest = h => typeof h === 'string' && /^[a-f0-9]{64}$/.test(h); const levelFile = (g, id, e) => g + '/levels/' + id + '.' + e.hash + '.json'; const packFile = (g, key, e) => g + '/packs/' + key + '.' + e.hash + '.zip'; function entry(e) { check(e && digest(e.hash) && Number.isSafeInteger(e.size) && e.size > 0, '资源条目无效'); return e; } function level(data) { check(data && Array.isArray(data.LEVEL_INFO) && data.LEVEL_INFO.length > 0 && data.LEVEL_INFO[0] && typeof data.LEVEL_INFO[0] === 'object' && Array.isArray(data.BLOCK_INFO) && Array.isArray(data.BLOCK_INFO[0]), '关卡配置无效'); return data; } function manifest(m, version, environment) { check(m && m.schemaVersion === 1 && m.contentVersion === version && m.environment === environment && typeof m.release === 'string' && /^[a-zA-Z0-9_-]+$/.test(m.release) && m.groups, '关卡清单版本或环境不匹配'); for (const g of ['A', 'B']) { const group = m.groups[g]; check(group && group.levels && group.packs && integer(group.resourceCount, g === 'A' ? 100 : 0), '关卡资源范围无效'); check(Object.keys(group.levels).length === group.resourceCount, '关卡文件数量不符'); for (let id = 1; id <= group.resourceCount; id++) entry(group.levels[id]); const keys = []; for (let start = 1; start <= group.resourceCount; start += 100) keys.push(String(start)); if (g === 'A') keys.push('all'); check(Object.keys(group.packs).length === keys.length, '关卡包数量不符'); for (const key of keys) { const p = entry(group.packs[key]), start = key === 'all' ? 1 : Number(key); check(p.start === start && p.end === (key === 'all' ? group.resourceCount : Math.min(start + 99, group.resourceCount)) && digest(p.indexHash), '关卡包范围无效'); } } check(!m.simulatedB || environment === 'test', '模拟B不能发布到正式环境'); return m; } function builtin(b) { check(b && b.schemaVersion === 1 && integer(b.contentVersion, 1) && b.groups && b.groups.A && b.groups.B, '内置关卡清单无效'); for (const g of ['A', 'B']) for (const id of Object.keys(b.groups[g])) { check(integer(Number(id), 1), '内置关卡编号无效'); const e = entry(b.groups[g][id]); check(e.file === 'levels-local/' + g + '/' + id + '.' + e.hash + '.json', '内置关卡路径无效'); } return b; } module.exports = { check, integer, digest, entry, level, manifest, builtin, levelFile, packFile };