同步主站 Cocos 当前工程:关卡资源、运行脚本与打包工具。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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) {
|
||||
// 允许备用 1–400 + 练习 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> {
|
||||
|
||||
Reference in New Issue
Block a user