0.2.5-dev: official battlefield scenes on the 3D board — a local fetch / extract step for the season scene bundles, per-field scene rendering with the metal board as fallback, the full bundle can include the extracted scenes and setup can fetch them (--scenes); nothing official is stored in the repository (PR #533 by @andrew05060414, ported; the owner's decision of 2026-10-12); golden: unchanged
This commit is contained in:
1 parent
7fd0478dc0
commit
d482fbb8ba
19 files changed
+1659
-22
No files matched your search
@@ -0,0 +1,135 @@
|
||||
// Official battlefield scenes in the 0.2.5 asset pack (PR #533, GitHub #532).
|
||||
// The repository never stores the bundles or the extracted files. The full release zip includes
|
||||
// public/assets/local/scene when the maintainer extracted it; a lite zip and a source install do not,
|
||||
// and setup may fetch + extract on that machine. Missing extractor dependencies keep the metal board.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { artPlan, formatSummary, isPlayerFile, isRefused } from '../tools/package.mjs';
|
||||
import { localGaps, sceneInstallPlan } from '../tools/setup.mjs';
|
||||
|
||||
const ROOT = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
|
||||
|
||||
test('sceneInstallPlan: fetch only with a client and extractor deps; otherwise the metal board', () => {
|
||||
const present = sceneInstallPlan({ hasScenes: true, client: false });
|
||||
assert.equal(present.run, false);
|
||||
assert.equal(present.state, 'ok');
|
||||
assert.match(present.detail, /金属地块/);
|
||||
|
||||
const check = sceneInstallPlan({ hasScenes: false, check: true, client: true, python: true });
|
||||
assert.equal(check.run, false);
|
||||
assert.equal(check.state, 'skip');
|
||||
assert.match(check.detail, /--scenes/);
|
||||
assert.match(check.detail, /金属地块/);
|
||||
|
||||
const noClient = sceneInstallPlan({ hasScenes: false, client: false, python: true });
|
||||
assert.equal(noClient.run, false);
|
||||
assert.equal(noClient.state, 'skip');
|
||||
assert.match(noClient.detail, /完整包/);
|
||||
assert.match(noClient.detail, /金属地块/);
|
||||
|
||||
const incomplete = sceneInstallPlan({ hasScenes: false, client: 'incomplete', python: true, force: true });
|
||||
assert.equal(incomplete.run, false);
|
||||
assert.equal(incomplete.state, 'warn');
|
||||
assert.match(incomplete.detail, /金属地块/);
|
||||
|
||||
const noPy = sceneInstallPlan({ hasScenes: false, client: true, python: false });
|
||||
assert.equal(noPy.run, false);
|
||||
assert.equal(noPy.state, 'warn');
|
||||
assert.match(noPy.detail, /Python/);
|
||||
assert.match(noPy.detail, /金属地块/);
|
||||
|
||||
const noDeps = sceneInstallPlan({ hasScenes: false, client: true, python: true, deps: false });
|
||||
assert.equal(noDeps.run, false);
|
||||
assert.equal(noDeps.state, 'warn');
|
||||
assert.match(noDeps.detail, /UnityPy/);
|
||||
assert.match(noDeps.detail, /金属地块/);
|
||||
|
||||
const declined = sceneInstallPlan({ hasScenes: false, client: true, python: true, declined: true });
|
||||
assert.equal(declined.run, false);
|
||||
assert.equal(declined.state, 'skip');
|
||||
|
||||
const ask = sceneInstallPlan({ hasScenes: false, client: true, python: true });
|
||||
assert.equal(ask.run, false);
|
||||
assert.equal(ask.state, 'ask');
|
||||
assert.match(ask.detail, /金属地块/);
|
||||
|
||||
for (const extra of [{ force: true, declined: true }, { yes: true }]) {
|
||||
const go = sceneInstallPlan({ hasScenes: false, client: true, python: true, ...extra });
|
||||
assert.equal(go.run, true, JSON.stringify(extra));
|
||||
assert.match(go.detail, /金属地块/);
|
||||
}
|
||||
});
|
||||
|
||||
test('an older extraction is told it lacks the battlefield scenes', () => {
|
||||
assert.equal(localGaps({ enemySpines: true, tokenSpines: true }), '');
|
||||
assert.match(localGaps({ enemySpines: true, tokenSpines: true, scenes: false }), /各战场官方场景/);
|
||||
assert.match(localGaps({ enemySpines: false, tokenSpines: false, scenes: false }), /各战场官方场景/);
|
||||
});
|
||||
|
||||
test('official scene bundles and extracted scenes are not git files; the extractor scripts are', () => {
|
||||
const gi = fs.readFileSync(path.join(ROOT, '.gitignore'), 'utf8');
|
||||
assert.match(gi, /^public\/assets\/$/m);
|
||||
assert.match(gi, /^data\/local-assets\.json$/m);
|
||||
assert.match(gi, /^\.cache\/$/m);
|
||||
assert.match(gi, /official-scenes/);
|
||||
assert.equal(isRefused('.cache/official-scenes/26-04-13-12-31-42_e9b560/scenes/a.ab'), true);
|
||||
assert.equal(isRefused('public/assets/local/scene/act2autochess_m02/scene.bin'), false);
|
||||
assert.equal(isPlayerFile('public/assets/local/scene/act2autochess_m02/scene.bin'), false);
|
||||
assert.equal(isPlayerFile('tools/local-extract/scenes.py'), true);
|
||||
assert.equal(isPlayerFile('tools/local-extract/fetch_scenes.py'), true);
|
||||
});
|
||||
|
||||
test('the full asset plan ships extracted scenes; the lite plan ships none', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-scenes-'));
|
||||
try {
|
||||
const sceneDir = path.join(dir, 'public/assets/local/scene/act2autochess_m02');
|
||||
const texDir = path.join(dir, 'public/assets/local/scene/tex');
|
||||
fs.mkdirSync(sceneDir, { recursive: true });
|
||||
fs.mkdirSync(texDir, { recursive: true });
|
||||
fs.mkdirSync(path.join(dir, 'data'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, 'data/assets.json'), '{}\n');
|
||||
fs.writeFileSync(path.join(sceneDir, 'scene.bin'), 'mesh-bytes');
|
||||
fs.writeFileSync(path.join(sceneDir, 'scene.json'), '{}\n');
|
||||
fs.writeFileSync(path.join(texDir, 'TX_A.webp'), 'webp');
|
||||
fs.writeFileSync(path.join(dir, 'data/local-assets.json'), JSON.stringify({
|
||||
version: 1,
|
||||
groups: {
|
||||
'scene/act2autochess_m02': {
|
||||
scene: { path: '/assets/local/scene/act2autochess_m02/scene.json' },
|
||||
bin: { path: '/assets/local/scene/act2autochess_m02/scene.bin' },
|
||||
},
|
||||
'scene/tex': { TX_A: { path: '/assets/local/scene/tex/TX_A.webp' } },
|
||||
},
|
||||
}));
|
||||
const full = artPlan(dir, { lite: false });
|
||||
for (const f of [
|
||||
'public/assets/local/scene/act2autochess_m02/scene.bin',
|
||||
'public/assets/local/scene/act2autochess_m02/scene.json',
|
||||
'public/assets/local/scene/tex/TX_A.webp',
|
||||
'data/local-assets.json',
|
||||
]) assert.ok(full.files.includes(f), f);
|
||||
assert.ok(full.scenes.includes('public/assets/local/scene/act2autochess_m02/scene.bin'));
|
||||
assert.equal(full.scenes.length, 3);
|
||||
const lite = artPlan(dir, { lite: true });
|
||||
assert.deepEqual(lite.files, []);
|
||||
assert.deepEqual(lite.scenes, []);
|
||||
const summary = formatSummary({
|
||||
lite: false, version: '0.2.5-dev', tracked: [], art: [], localArt: true, generated: [], dropped: {}, orphans: [], held: [],
|
||||
scanned: 0, names: 0, problems: [], scenes: { files: full.scenes, bytes: 9 + 3 + 4 },
|
||||
bytes: { tracked: 0, art: 16, modules: 0, vendor: 0, total: 16, leftOut: 0, orphans: 0 },
|
||||
});
|
||||
assert.match(summary, /battlefield scenes: 3 files/);
|
||||
const empty = formatSummary({
|
||||
lite: false, version: '0.2.5-dev', tracked: [], art: [], localArt: true, generated: [], dropped: {}, orphans: [], held: [],
|
||||
scanned: 0, names: 0, problems: [], scenes: { files: [], bytes: 0 },
|
||||
bytes: { tracked: 0, art: 0, modules: 0, vendor: 0, total: 0, leftOut: 0, orphans: 0 },
|
||||
});
|
||||
assert.match(empty, /battlefield scenes: none/);
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user