同步主站 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

@@ -112,10 +112,19 @@ function shardKey(shard: { file: string }) {
return shard.file;
}
function practiceLevelCount(ids: number[]): number {
let n = 0;
for (const id of ids) {
if (id >= LEVEL_ID_BASE) n += 1;
}
return n;
}
function validateIndex(index: LevelsDbIndex): void {
if (index.total < 1 || index.min < LEVEL_ID_BASE) {
// 允许备用 1400 + 练习 91601+;不能只看 min备用关会把 min 拉到 1
if (index.total < 100 || index.max < LEVEL_ID_BASE) {
throw new Error(
`关卡库索引无效 (${index.total} 关),请重新 package-for-project`,
`关卡库索引过旧 (${index.total} 关),请重新 package-for-project`,
);
}
}
@@ -132,10 +141,10 @@ function validateIngested(): void {
return;
}
const total = sortedIds.length;
const minId = sortedIds[0] ?? 0;
if (total < 1 || minId < LEVEL_ID_BASE) {
const practice = practiceLevelCount(sortedIds);
if (total < 100 || practice < 100) {
throw new Error(
`关卡库无效 (${total} 关),请用主站导出的 levels-database.json`
`关卡库过旧 (${total} 关),请用主站导出的 levels-database.json`
+ '运行 bash tools/sync-level-db.sh 后重新 package-for-project',
);
}
@@ -175,22 +184,38 @@ function indexFetchCandidates(): string[] {
return [...new Set(out)];
}
function looksLikeJsonText(text: string): boolean {
const t = text.replace(/^\uFEFF/, '').trimStart();
return t.startsWith('{') || t.startsWith('[');
}
async function decodePossiblyBrotliJson(ab: ArrayBuffer, expectBrotli: boolean): Promise<unknown> {
const raw = new TextDecoder().decode(ab);
if (!expectBrotli || looksLikeJsonText(raw)) {
return JSON.parse(raw);
}
if (typeof DecompressionStream === 'undefined') {
throw new Error('浏览器不支持 Brotli 解压');
}
const text = await new Response(ab)
.pipeThrough(new DecompressionStream('brotli'))
.text();
return JSON.parse(text);
}
async function fetchJsonWithBrotli(url: string): Promise<unknown> {
const brUrl = url.replace(/\.json(\?.*)?$/i, '.json.br$1');
try {
const brRes = await fetch(brUrl);
if (brRes.ok && typeof DecompressionStream !== 'undefined') {
const text = await new Response(await brRes.arrayBuffer())
.pipeThrough(new DecompressionStream('brotli'))
.text();
return JSON.parse(text);
const brRes = await fetch(brUrl, { credentials: 'same-origin' });
if (brRes.ok) {
return decodePossiblyBrotliJson(await brRes.arrayBuffer(), true);
}
} catch {
/* fallback */
}
const res = await fetch(url, { credentials: 'same-origin' });
if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
return res.json();
return decodePossiblyBrotliJson(await res.arrayBuffer(), false);
}
async function loadIndexFromNetwork(): Promise<void> {