Merge branch 'cocos-main'

# Conflicts:
#	docker-conf/conf.d/nginx.conf
#	nginx.conf
#	scratch-blocks/build.py
#	scratch-blocks/cleanup.sh
#	scratch-blocks/i18n/create_messages.py
#	scratch-blocks/i18n/dedup_json.py
#	scratch-blocks/i18n/js_to_json.py
#	scratch-blocks/i18n/json_to_js.py
#	scratch-blocks/i18n/sync_tx_translations.js
#	scratch-blocks/i18n/xliff_to_json.py
#	scratch-blocks/local_build.sh
#	scratch-blocks/media/icons/control_forever.svg
#	scratch-blocks/media/icons/control_repeat.svg
#	scratch-blocks/media/icons/control_wait.svg
#	scratch-blocks/media/icons/event_broadcast_blue.svg
#	scratch-blocks/media/icons/event_broadcast_coral.svg
#	scratch-blocks/media/icons/event_broadcast_green.svg
#	scratch-blocks/media/icons/event_broadcast_magenta.svg
#	scratch-blocks/media/icons/event_broadcast_orange.svg
#	scratch-blocks/media/icons/event_broadcast_purple.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_blue.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_coral.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_green.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_magenta.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_orange.svg
#	scratch-blocks/media/icons/event_when-broadcast-received_purple.svg
#	scratch-blocks/media/icons/event_whenflagclicked.svg
#	scratch-blocks/media/icons/set-led_blue.svg
#	scratch-blocks/media/icons/set-led_coral.svg
#	scratch-blocks/media/icons/set-led_green.svg
#	scratch-blocks/media/icons/set-led_magenta.svg
#	scratch-blocks/media/icons/set-led_mystery.svg
#	scratch-blocks/media/icons/set-led_orange.svg
#	scratch-blocks/media/icons/set-led_purple.svg
#	scratch-blocks/media/icons/set-led_white.svg
#	scratch-blocks/media/icons/set-led_yellow.svg
#	scratch-blocks/media/icons/wedo_motor-clockwise.svg
#	scratch-blocks/media/icons/wedo_motor-counterclockwise.svg
#	scratch-blocks/media/icons/wedo_when-distance_close.svg
#	scratch-blocks/media/icons/wedo_when-tilt-backward.svg
#	scratch-blocks/media/icons/wedo_when-tilt-forward.svg
#	scratch-blocks/media/icons/wedo_when-tilt-left.svg
#	scratch-blocks/media/icons/wedo_when-tilt-right.svg
#	scratch-blocks/media/icons/wedo_when-tilt.svg
#	scratch-blocks/package-lock.json
#	scratch-blocks/pull_from_blockly.sh
#	scratch-gui/competitio_login/index.html
#	scratch-gui/package-lock.json
#	scratch-gui/package.json
#	scratch-gui/prune-gh-pages.sh
#	scratch-gui/src/components/menu-bar/menu-bar.css
#	scratch-gui/src/components/menu-bar/menu-bar.jsx
#	scratch-gui/src/components/nopermission-modal/NoPermissionModal.jsx
#	scratch-gui/src/components/stage-cpp-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-cpp-unity/stage-unity.jsx
#	scratch-gui/src/components/stage-cpp/cpp-parser.js
#	scratch-gui/src/components/stage-cpp/stage-cpp-clang.jsx
#	scratch-gui/src/components/stage-cpp/stage-cpp.css
#	scratch-gui/src/components/stage-python-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-python-unity/stage-unity.jsx
#	scratch-gui/src/components/stage-python/stage-python.css
#	scratch-gui/src/components/stage-python/stage-python.jsx
#	scratch-gui/src/components/stage-unity/currentLevelChoose.jsx
#	scratch-gui/src/components/stage-unity/stage-unity.jsx
#	scratch-gui/src/lib/libraries/decks/steps/add-effects.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-add-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-jump.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-move.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/animate-char-talk.es.png
#	scratch-gui/src/lib/libraries/decks/steps/change-size.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-move-randomly.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-right-left.es.png
#	scratch-gui/src/lib/libraries/decks/steps/chase-game-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-backdrop.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-collect.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-glide.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-say.es.png
#	scratch-gui/src/lib/libraries/decks/steps/cn-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-01-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-02-animate.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-03-select-different-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-04-use-minus-sign.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-05-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-06-select-another-different-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-07-jump.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-08-change-scenes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-09-glide-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-10-change-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/code-cartoon-11-choose-more-characters.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-choose-character.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-flying-heart.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-keep-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-make-interactive.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-move-scenery.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-object-to-collect.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-select-flyer.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/fly-switch-costume.es.png
#	scratch-gui/src/lib/libraries/decks/steps/glide-around-back-and-forth.es.png
#	scratch-gui/src/lib/libraries/decks/steps/glide-around-point.es.png
#	scratch-gui/src/lib/libraries/decks/steps/hide-show.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-change-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-another-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-another-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-any-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-choose-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-click-green-flag.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-fly-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-glide-to-point.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-left-right.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-type-what-you-want.es.png
#	scratch-gui/src/lib/libraries/decks/steps/imagine-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/move-arrow-keys-left-right.es.png
#	scratch-gui/src/lib/libraries/decks/steps/move-arrow-keys-up-down.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-beat.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-beatbox.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-make-song.es.png
#	scratch-gui/src/lib/libraries/decks/steps/music-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-grow.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/name-spin.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-ball-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-add-code-to-ball.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-bounce-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-choose-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-game-over.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-insert-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-move-the-paddle.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-reset-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pong-select-ball.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-change-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-play-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-random-position.es.png
#	scratch-gui/src/lib/libraries/decks/steps/pop-game-reset-score.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-choose-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-click-record.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-play-your-sound.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-press-record-button.es.png
#	scratch-gui/src/lib/libraries/decks/steps/record-a-sound-sounds-tab.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-change-color.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-grow-shrink.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-move-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-set-voice.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-song.es.png
#	scratch-gui/src/lib/libraries/decks/steps/speech-spin.es.png
#	scratch-gui/src/lib/libraries/decks/steps/spin-point-in-direction.es.png
#	scratch-gui/src/lib/libraries/decks/steps/spin-turn.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-conversation.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-hide-character.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-say-something.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-show-character.es.png
#	scratch-gui/src/lib/libraries/decks/steps/story-switch-backdrop.es.png
#	scratch-gui/src/lib/libraries/decks/steps/switch-costumes.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-10-choose-third-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-12-dance-moves.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-2-choose-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-4-choose-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-6-choose-another-sprite.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-7-move-around.es.png
#	scratch-gui/src/lib/libraries/decks/steps/talking-8-choose-another-backdrop.LTR.png
#	scratch-gui/src/lib/libraries/decks/steps/video-animate.es.png
#	scratch-gui/src/lib/libraries/decks/steps/video-pet.es.png
#	scratch-gui/src/lib/libraries/decks/steps/video-pop.es.png
#	scratch-gui/src/playground/aboutus.ejs
#	scratch-gui/src/playground/checkStatus.js
#	scratch-gui/src/playground/competition.ejs
#	scratch-gui/src/playground/competition.js
#	scratch-gui/src/playground/competition_list.ejs
#	scratch-gui/src/playground/competition_list.js
#	scratch-gui/src/playground/competition_sum.ejs
#	scratch-gui/src/playground/competition_sum.js
#	scratch-gui/src/playground/config.js
#	scratch-gui/src/playground/home.ejs
#	scratch-gui/src/playground/import-check.js
#	scratch-gui/src/playground/leaderboard.ejs
#	scratch-gui/src/playground/login-new.ejs
#	scratch-gui/src/playground/login.ejs
#	scratch-gui/src/playground/login.js
#	scratch-gui/src/playground/matchsum.js
#	scratch-gui/src/playground/programming-learning.ejs
#	scratch-gui/src/playground/programming-learning.js
#	scratch-gui/src/playground/signup-new.ejs
#	scratch-gui/src/playground/third-part-login.js
#	scratch-gui/src/playground/welcome.ejs
#	scratch-gui/src/playground/welcome.js
#	scratch-gui/static/css/welcome-new.css
#	scratch-gui/static/pyodide/pyodide.asm.wasm.br
#	scratch-gui/static/unity/Build/mstest5.data.br
#	scratch-gui/static/unity/Build/mstest5.framework.js.br
#	scratch-gui/static/unity/Build/mstest5.loader.js
#	scratch-gui/static/unity/Build/mstest5.wasm.br
#	scratch-gui/static/unity/StreamingAssets/aa/AddressablesLink/link.xml
#	scratch-gui/static/unity/StreamingAssets/aa/catalog.json
#	scratch-gui/static/unity/StreamingAssets/aa/settings.json
#	scratch-gui/webpack.config.js
#	scratch-l10n/scripts/build-data.js
#	scratch-l10n/scripts/build-i18n-src.js
#	scratch-l10n/scripts/tw-push.js
#	scratch-l10n/scripts/tx-pull-help-articles.js
#	scratch-l10n/scripts/tx-pull-help-names.js
#	scratch-l10n/scripts/tx-pull-locale-articles.js
#	scratch-l10n/scripts/tx-pull-www.js
#	scratch-l10n/scripts/tx-push-help.js
#	scratch-l10n/scripts/tx-push-src.js
#	scratch-l10n/scripts/update-translations.sh
#	scratch-l10n/scripts/validate-www.js
#	scratch-vm/package-lock.json
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3
#	scratch-vm/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3
#	scratch-vm/test/fixtures/sprite.json
This commit is contained in:
Evan
2026-09-07 11:35:22 +08:00
206 changed files with 21799 additions and 437 deletions

View File

@@ -0,0 +1,119 @@
import CONFIG from '../playground/config.js';
const FILES = {
dataUrl: 'mstest5.data.br',
frameworkUrl: 'mstest5.framework.js.br',
codeUrl: 'mstest5.wasm.br'
};
// 压缩后 wasm 约 4.4MB;用户侧曾出现只下到 ~800KB 的截断包
const MIN_BYTES = {
dataUrl: 1024 * 1024,
frameworkUrl: 20 * 1024,
codeUrl: 2 * 1024 * 1024
};
const WASM_MAGIC = [0x00, 0x61, 0x73, 0x6d];
function withVersion(url) {
const version = CONFIG.version || Date.now();
return `${url}${url.includes('?') ? '&' : '?'}v=${encodeURIComponent(version)}`;
}
function buildCandidates() {
const local = '/unity/Build';
const cdn = CONFIG.unitycdnbuilddir;
if (CONFIG.usecdn) {
return [cdn, local].filter(Boolean);
}
return [local, cdn].filter(Boolean);
}
function isUncompressedWasm(buffer) {
const bytes = new Uint8Array(buffer);
return WASM_MAGIC.every((value, index) => bytes[index] === value);
}
function looksLikeJavascript(buffer) {
const head = new TextDecoder().decode(buffer.slice(0, 80));
return /function|unityFramework|^\s*var |^\s*const |^\s*!function/.test(head);
}
function looksLikeUnityData(buffer) {
const head = new TextDecoder().decode(buffer.slice(0, 16));
return head.startsWith('UnityWebData1.0');
}
function toTrustedUrl(fileName, buffer, sourceUrl) {
if (fileName.endsWith('.wasm.br') && isUncompressedWasm(buffer)) {
return URL.createObjectURL(new Blob([buffer], { type: 'application/wasm' }));
}
if (fileName.endsWith('.js.br') && looksLikeJavascript(buffer)) {
return URL.createObjectURL(new Blob([buffer], { type: 'application/javascript' }));
}
if (fileName.endsWith('.data.br') && looksLikeUnityData(buffer)) {
return URL.createObjectURL(new Blob([buffer]));
}
return sourceUrl;
}
async function fetchCompleteAsset(fileName, minBytes) {
let lastError = null;
for (const base of buildCandidates()) {
const url = withVersion(`${base}/${fileName}`);
try {
const response = await fetch(url, { cache: 'no-store' });
if (!response.ok) {
lastError = new Error(`${url} HTTP ${response.status}`);
continue;
}
const buffer = await response.arrayBuffer();
if (buffer.byteLength < minBytes) {
console.warn(`[Unity] ${fileName} 被截断: ${buffer.byteLength} bytes来自 ${url}),期望至少 ${minBytes}`);
lastError = new Error(`${fileName} truncated: ${buffer.byteLength}`);
continue;
}
console.log(`[Unity] ${fileName} 下载完整: ${buffer.byteLength} bytes from ${url}`);
return toTrustedUrl(fileName, buffer, url);
} catch (error) {
console.warn(`[Unity] 下载失败 ${url}`, error);
lastError = error;
}
}
throw lastError || new Error(`无法完整下载 ${fileName}`);
}
export async function getUnityPlayerConfig() {
const [dataUrl, frameworkUrl, codeUrl] = await Promise.all([
fetchCompleteAsset(FILES.dataUrl, MIN_BYTES.dataUrl),
fetchCompleteAsset(FILES.frameworkUrl, MIN_BYTES.frameworkUrl),
fetchCompleteAsset(FILES.codeUrl, MIN_BYTES.codeUrl)
]);
const streamingAssetsUrl = (CONFIG.usecdn && CONFIG.unitycdndir)
? `${CONFIG.unitycdndir}/StreamingAssets`
: '/unity/StreamingAssets';
return {
dataUrl,
frameworkUrl,
codeUrl,
streamingAssetsUrl,
companyName: 'DefaultCompany',
productName: 'Web A',
productVersion: String(CONFIG.version || '1.0')
};
}
export function loadUnityLoaderScript() {
if (typeof window.createUnityInstance === 'function') {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = withVersion('/unity/Build/mstest5.loader.js');
script.onload = () => resolve();
script.onerror = () => reject(new Error('Unity loader 加载失败'));
document.body.appendChild(script);
});
}