/** * 替换 Unity Build/mstest5.loader.js — 保持 createUnityInstance API,内部启动 Cocos。 * bundle 文件名从 catalog.json 读取(与 Unity Addressables 一致),路径相对包根目录解析。 */ (function (global) { var PRODUCT = 'mstest5'; var files = new Map(); var origFetch = global.fetch.bind(global); var packageRoot = ''; var streamingBase = ''; var bundleManifest = null; var levelsManifest = null; var levelsManifestPromise = null; var assetsCoreLoaded = false; var assetsCorePromise = null; var loadedLevelDbShards = new Set(); var levelDbShardPromises = new Map(); var levelPackPromises = new Map(); var loadedLevelIds = new Set(); var levelPrefabsBundlePromise = null; function primeLevelPrefabsBundleScripts() { if (!levelsShellReady()) return; var idxHit = files.get('assets/level-prefabs/index.js'); if (idxHit && !global.__tfrhLevelPrefabsIndexLoaded) { injectScript(bytesToText(idxHit)); global.__tfrhLevelPrefabsIndexLoaded = true; } } function primeResourcesBundleScripts() { var idxHit = files.get('assets/resources/index.js'); if (idxHit && !global.__tfrhResourcesIndexLoaded) { injectScript(bytesToText(idxHit)); global.__tfrhResourcesIndexLoaded = true; } } /** 拆分包模式下须先 loadBundle('resources'),level-prefabs 预制体依赖 resources */ function ensureResourcesBundleOnCc(cc, origLoadBundle) { var am = cc && cc.assetManager; if (!am) return Promise.reject(new Error('assetManager unavailable')); var existing = am.getBundle('resources'); if (existing) return Promise.resolve(existing); primeResourcesBundleScripts(); return new Promise(function (resolve, reject) { origLoadBundle('resources', null, function (err, bundle) { if (err || !bundle) reject(err || new Error('resources bundle unavailable')); else resolve(bundle); }); }); } /** 串行注册 level-prefabs,避免 ensureCocos + LevelPrefabLoader 并发 loadBundle 竞态 */ function loadLevelPrefabsBundleOnce(cc, origLoadBundle, onComplete) { var am = cc && cc.assetManager; if (!am) { if (onComplete) onComplete(new Error('assetManager unavailable'), null); return; } var existing = am.getBundle('level-prefabs'); if (existing && typeof am.removeBundle === 'function') { try { am.removeBundle(existing); } catch (e) { /* ignore */ } } if (levelPrefabsBundlePromise) { levelPrefabsBundlePromise.then(function (bundle) { if (onComplete) onComplete(null, bundle); }).catch(function (e) { if (onComplete) onComplete(e, null); }); return; } levelPrefabsBundlePromise = ensureResourcesBundleOnCc(cc, origLoadBundle).then(function () { if (!levelsShellReady()) { return Promise.reject(new Error('level-prefabs shell 不可用(config/index 应在 assets_all)')); } primeLevelPrefabsBundleScripts(); return new Promise(function (resolve, reject) { var bundleRoot = joinUrl(packageRoot || getPackageRoot(), 'assets/level-prefabs/'); origLoadBundle('level-prefabs', { baseUrl: bundleRoot }, function (err, bundle) { if (err || !bundle) { levelPrefabsBundlePromise = null; reject(err || new Error('level-prefabs bundle unavailable')); return; } resolve(bundle); }); }); }); levelPrefabsBundlePromise.then(function (bundle) { if (onComplete) onComplete(null, bundle); }).catch(function (e) { if (onComplete) onComplete(e, null); }); } function getPackageRoot() { var scripts = document.getElementsByTagName('script'); var i; for (i = scripts.length - 1; i >= 0; i--) { var src = scripts[i].getAttribute('src') || scripts[i].src || ''; if (src.indexOf('loader.js') >= 0) { var u = new URL(src, global.location.href); return u.href.replace(/Build\/[^/?#]+\.loader\.js.*$/i, ''); } } return new URL('./', global.location.href).href; } function resolveStreamingBase(config) { packageRoot = getPackageRoot(); var sa = (config && config.streamingAssetsUrl) || 'StreamingAssets'; // 与 Unity 一致:/unity/StreamingAssets 相对站点根目录,不能相对 packageRoot(否则会 /unity/unity/...) if (/^https?:\/\//i.test(sa)) { streamingBase = sa; } else if (sa.charAt(0) === '/') { streamingBase = new URL(sa, global.location.href).href; } else { streamingBase = new URL(sa, packageRoot).href; } if (!/\/$/.test(streamingBase)) streamingBase += '/'; return streamingBase; } function joinUrl(base, rel) { return new URL(String(rel || '').replace(/^\//, ''), base).href; } function toPath(url) { try { var u = new URL(url, global.location.href); return decodeURIComponent(u.pathname.replace(/^\/+/, '')); } catch (e) { return String(url || ''); } } /** Cocos bundle 常请求 import/NN/uuid.json,zip 内路径带 assets/.../import/ 前缀(NN 为 uuid 前 2 位 hex) */ var IMPORT_SHARD = '[0-9a-fA-F]{2}'; function resolveImportFileKey(pathname) { if (!pathname) return null; var p = String(pathname).replace(/\\/g, '/').replace(/^\/+/, ''); var shardRe = new RegExp('^unity/assets/(level-prefabs|resources|main|internal)/(' + IMPORT_SHARD + ')/(.+)$', 'i'); p = p.replace(shardRe, function (_, bundle, dir, file) { return 'assets/' + bundle + '/import/' + dir + '/' + file; }); if (files.has(p)) return p; var bundleImport = new RegExp('^assets/(level-prefabs|resources|main|internal)/(' + IMPORT_SHARD + ')/([^/]+)$', 'i').exec(p); if (bundleImport) { var withImport = 'assets/' + bundleImport[1] + '/import/' + bundleImport[2] + '/' + bundleImport[3]; if (files.has(withImport)) return withImport; } var short = new RegExp('^(' + IMPORT_SHARD + ')/(.+)$', 'i').exec(p); if (short) { var bases = [ 'assets/level-prefabs/import/', 'assets/resources/import/', 'assets/main/import/', 'assets/internal/import/', 'assets/resources/native/', 'assets/main/native/', 'assets/internal/native/', ]; for (var i = 0; i < bases.length; i++) { var key = bases[i] + short[1] + '/' + short[2]; if (files.has(key)) return key; } } return null; } function lookupFile(pathname) { var keys = [pathname, pathname.replace(/^\.?\//, '')]; var parts = pathname.split('/'); var n; for (n = 1; n <= 6 && n < parts.length; n++) { keys.push(parts.slice(-n).join('/')); } for (var i = 0; i < keys.length; i++) { var k = keys[i]; if (files.has(k)) return files.get(k); var importKey = resolveImportFileKey(k); if (importKey && files.has(importKey)) return files.get(importKey); var idx = k.indexOf('StreamingAssets/aa/WebGL/'); if (idx >= 0 && files.has(k.slice(idx + 'StreamingAssets/aa/WebGL/'.length))) { return files.get(k.slice(idx + 'StreamingAssets/aa/WebGL/'.length)); } var ai = k.indexOf('/assets/'); if (ai >= 0 && files.has('assets/' + k.slice(ai + 8))) { return files.get('assets/' + k.slice(ai + 8)); } if (k.indexOf('assets/') === 0 && files.has(k)) return files.get(k); } var fallbackKey = resolveImportFileKey(pathname); if (fallbackKey && files.has(fallbackKey)) return files.get(fallbackKey); return null; } function mimeForPath(pathname) { if (/\.(m?js)$/i.test(pathname)) return 'application/javascript'; if (/\.json$/i.test(pathname)) return 'application/json'; if (/\.wasm$/i.test(pathname)) return 'application/wasm'; return 'application/octet-stream'; } function getRootPathname() { var rootPath = new URL(packageRoot || getPackageRoot(), global.location.href).pathname; return /\/$/.test(rootPath) ? rootPath : rootPath + '/'; } function needsUnityPrefix(pathname) { return /^\/(src|assets|cocos-js)\//.test(pathname) || pathname === '/src/settings.json'; } function relPathFromUrlPath(pathname) { var root = getRootPathname(); if (pathname.indexOf(root) === 0) { return pathname.slice(root.length).replace(/^\/+/, ''); } if (needsUnityPrefix(pathname) || /^\/(assets|src)\//.test(pathname)) { return pathname.replace(/^\//, ''); } return ''; } function normalizeBundleRelPath(raw) { var s = String(raw || '').replace(/\\/g, '/').trim(); if (!s) return ''; if (s.indexOf('blob:') === 0) { s = s.slice(5).replace(/^https?:\/?\/?/i, ''); if (s.charAt(0) === '/') s = s.slice(1); } var parts = s.split('/'); var out = []; for (var i = 0; i < parts.length; i++) { var part = parts[i]; if (!part || part === '.') continue; if (part === '..') { if (out.length) out.pop(); continue; } out.push(part); } return out.join('/'); } function resolveBundleVirtualUrl(raw) { if (!files.size || !raw) return null; var norm = normalizeBundleRelPath(raw); if (!norm) return null; var virt = virtualUrlForBundleFile(norm); if (virt) return virt; if (norm.indexOf('src/chunks/bundle.js') >= 0) return virtualUrlForBundleFile('src/chunks/bundle.js'); if (norm.indexOf('src/effect.bin') >= 0) return virtualUrlForBundleFile('src/effect.bin'); return null; } function fixEmbeddedUrl(raw) { if (!raw) return raw; var virtEarly = resolveBundleVirtualUrl(raw); if (virtEarly) return virtEarly; try { var u = new URL(String(raw), global.location.href); var p = u.pathname; if (files.size > 0) { var importKey = resolveImportFileKey(p) || resolveImportFileKey(p.replace(/^\/+/, '')); if (importKey) { var importVirt = virtualUrlForBundleFile(importKey); if (importVirt) return importVirt; } var rel = relPathFromUrlPath(p); if (rel) { var virt = virtualUrlForBundleFile(rel); if (virt) return virt; importKey = resolveImportFileKey(rel); if (importKey) { importVirt = virtualUrlForBundleFile(importKey); if (importVirt) return importVirt; } } } if (p.indexOf(getRootPathname()) === 0) return u.href; if (needsUnityPrefix(p)) { return joinUrl(packageRoot || getPackageRoot(), p.replace(/^\//, '')); } } catch (e) { /* ignore */ } return raw; } function rewriteEmbeddedUrl(input) { var raw = typeof input === 'string' ? input : (input && input.url) || ''; var fixed = fixEmbeddedUrl(raw); return fixed === raw ? input : fixed; } function installEmbeddedPathFix() { if (global.__unityEmbeddedPathFixed) return; global.__unityEmbeddedPathFixed = true; global.fetch = function (input, init) { return origFetch(rewriteEmbeddedUrl(input), init); }; var OrigXHR = global.XMLHttpRequest; if (OrigXHR && !OrigXHR.__unityPathFixed) { global.XMLHttpRequest = function () { var xhr = new OrigXHR(); var origOpen = xhr.open; xhr.open = function (method, url) { return origOpen.call(xhr, method, fixEmbeddedUrl(url)); }; return xhr; }; global.XMLHttpRequest.__unityPathFixed = true; } patchElementUrlLoading(HTMLScriptElement, ['src']); patchElementUrlLoading(HTMLImageElement, ['src']); patchElementUrlLoading(HTMLLinkElement, ['href']); patchElementUrlLoading(HTMLVideoElement, ['src', 'poster']); patchElementUrlLoading(HTMLAudioElement, ['src']); patchElementUrlLoading(HTMLSourceElement, ['src']); } function patchElementUrlLoading(Ctor, props) { if (!Ctor || !Ctor.prototype || Ctor.prototype.__unityUrlLoadingFixed) return; var proto = Ctor.prototype; var origSetAttr = proto.setAttribute; proto.setAttribute = function (name, value) { if (value && props.indexOf(name) >= 0) value = fixEmbeddedUrl(value); return origSetAttr.call(this, name, value); }; for (var i = 0; i < props.length; i++) { patchUrlProperty(proto, props[i]); } proto.__unityUrlLoadingFixed = true; } function patchUrlProperty(proto, prop) { var key = '__unity' + prop + 'Fixed'; if (proto[key]) return; var desc = Object.getOwnPropertyDescriptor(proto, prop); if (!desc || !desc.set) return; Object.defineProperty(proto, prop, { configurable: true, enumerable: desc.enumerable, get: desc.get, set: function (v) { desc.set.call(this, fixEmbeddedUrl(v)); }, }); proto[key] = true; } var origScriptSrcDesc = null; function resolveEmbeddedAssetUrl(raw) { if (!raw) return raw; var fixed = fixEmbeddedUrl(raw); var p = toPath(typeof fixed === 'string' ? fixed : String(raw || '')); var rel = relPathFromUrlPath(p) || p.replace(/^\.?\//, ''); if (lookupFile(p) || (rel && lookupFile(rel))) { return virtualUrlForBundleFile(rel) || fixed; } return fixed; } function assignScriptSrc(el, raw) { var url = resolveEmbeddedAssetUrl(raw); if (origScriptSrcDesc && origScriptSrcDesc.set) { origScriptSrcDesc.set.call(el, url); } else { el.setAttribute('src', url); } } function ensureLevelPackForPath(pathname) { var lid = levelIdFromPrefabPath(pathname); if (!lid) return Promise.resolve(); return ensureLevelPackLoaded(lid); } function assignLevelPrefabScriptSrc(el, value) { var apply = function () { assignScriptSrc(el, value); }; if (levelsShellReady() && fileHit(value)) { apply(); return; } ensureLevelPackForPath(value).then(apply).catch(apply); } function patchLevelPrefabsScriptLoading() { if (!HTMLScriptElement || HTMLScriptElement.prototype.__tfrhLevelPrefabScript) return; var proto = HTMLScriptElement.prototype; origScriptSrcDesc = Object.getOwnPropertyDescriptor(proto, 'src'); var origSetAttr = proto.setAttribute; proto.setAttribute = function (name, value) { if (name === 'src' && needsLevelPrefabs(value)) { assignLevelPrefabScriptSrc(this, value); return; } if (value && name === 'src') value = resolveEmbeddedAssetUrl(value); return origSetAttr.call(this, name, value); }; var desc = origScriptSrcDesc; if (desc && desc.set) { Object.defineProperty(proto, 'src', { configurable: true, enumerable: desc.enumerable, get: desc.get, set: function (v) { if (needsLevelPrefabs(v)) { assignLevelPrefabScriptSrc(this, v); return; } desc.set.call(this, resolveEmbeddedAssetUrl(v)); }, }); } proto.__tfrhLevelPrefabScript = true; } function needsAssetsCore(pathname) { var p = String(pathname || ''); if (needsLevelPrefabs(p)) return false; return p.indexOf('assets/') >= 0 || p.indexOf('/assets/') >= 0; } function installFetchShim() { global.fetch = function (input, init) { input = rewriteEmbeddedUrl(input); var p = toPath(typeof input === 'string' ? input : (input && input.url) || ''); var hit = lookupFile(p); if (hit) { var body = /\.(m?js|json)$/i.test(p) ? bytesToText(hit) : hit; return Promise.resolve(new Response(body, { status: 200, headers: { 'Content-Type': mimeForPath(p) }, })); } if (needsAssetsCore(p)) { return ensureAssetsCoreLoaded().then(function () { var hit2 = lookupFile(p); if (hit2) { var body2 = /\.(m?js|json)$/i.test(p) ? bytesToText(hit2) : hit2; return new Response(body2, { status: 200, headers: { 'Content-Type': mimeForPath(p) }, }); } return origFetch(input, init); }); } if (needsLevelPrefabs(p)) { return ensureLevelPackForPath(p).then(function () { var hit2 = lookupFile(p); if (hit2) { var body2 = /\.(m?js|json)$/i.test(p) ? bytesToText(hit2) : hit2; return new Response(body2, { status: 200, headers: { 'Content-Type': mimeForPath(p) }, }); } return origFetch(input, init); }); } return origFetch(input, init); }; var OrigXHR = global.XMLHttpRequest; if (OrigXHR) { global.XMLHttpRequest = function () { var xhr = new OrigXHR(); var reqUrl = ''; var origOpen = xhr.open; xhr.open = function (method, url) { reqUrl = fixEmbeddedUrl(url); return origOpen.call(xhr, method, reqUrl); }; var origSend = xhr.send; xhr.send = function () { var hit = lookupFile(toPath(reqUrl)); if (!hit && needsAssetsCore(reqUrl)) { ensureAssetsCoreLoaded().then(function () { hit = lookupFile(toPath(reqUrl)); if (!hit) return origSend.apply(xhr, arguments); var isText = /\.(m?js|json|txt|xml|atlas|tmx|tsx|vsh|fsh|fnt|plist)$/i.test(toPath(reqUrl)) || xhr.responseType === 'json' || xhr.responseType === 'text' || !xhr.responseType; deliverXhrHit(xhr, isText ? bytesToText(hit) : null, isText ? null : hit); }).catch(function () { origSend.apply(xhr, arguments); }); return; } if (!hit && needsLevelPrefabs(reqUrl)) { ensureLevelPackForPath(reqUrl).then(function () { hit = lookupFile(toPath(reqUrl)); if (!hit) return origSend.apply(xhr, arguments); var isText = /\.(m?js|json|txt|xml|atlas|tmx|tsx|vsh|fsh|fnt|plist)$/i.test(toPath(reqUrl)) || xhr.responseType === 'json' || xhr.responseType === 'text' || !xhr.responseType; deliverXhrHit(xhr, isText ? bytesToText(hit) : null, isText ? null : hit); }).catch(function () { origSend.apply(xhr, arguments); }); return; } if (!hit) return origSend.apply(xhr, arguments); var isText = /\.(m?js|json|txt|xml|atlas|tmx|tsx|vsh|fsh|fnt|plist)$/i.test(toPath(reqUrl)) || xhr.responseType === 'json' || xhr.responseType === 'text' || !xhr.responseType; deliverXhrHit(xhr, isText ? bytesToText(hit) : null, isText ? null : hit); }; return xhr; }; } patchLevelPrefabsScriptLoading(); } function deliverXhrHit(xhr, text, rawBytes) { var responseType = xhr.responseType || ''; setTimeout(function () { try { var response = text; if (responseType === 'json') { try { response = typeof text === 'string' ? JSON.parse(text) : text; } catch (parseErr) { console.warn('[mstest5] XHR json 解析失败', parseErr); } } else if (responseType === 'arraybuffer' && rawBytes) { response = rawBytes.buffer.slice(rawBytes.byteOffset, rawBytes.byteOffset + rawBytes.byteLength); } Object.defineProperty(xhr, 'readyState', { value: 4 }); Object.defineProperty(xhr, 'status', { value: 200 }); Object.defineProperty(xhr, 'responseText', { value: typeof text === 'string' ? text : '' }); Object.defineProperty(xhr, 'response', { value: response }); if (xhr.onreadystatechange) xhr.onreadystatechange(); if (xhr.onload) xhr.onload(); } catch (e) { /* ignore */ } }, 0); } function needsLevelPrefabs(pathname) { var p = String(pathname || ''); return p.indexOf('level-prefabs') >= 0 || p.indexOf('assets/level-prefabs') >= 0; } function levelsShellReady() { return files.has('assets/level-prefabs/config.json') && files.has('assets/level-prefabs/index.js'); } function levelIdFromPrefabPath(pathname) { var m = /Level(\d+)/.exec(String(pathname || '')); if (m) return m[1]; if (!levelsManifest || !levelsManifest.levels) return null; var norm = String(pathname || '').replace(/\\/g, '/'); var base = norm.split('/').pop() || norm; var id; for (id in levelsManifest.levels) { if (!Object.prototype.hasOwnProperty.call(levelsManifest.levels, id)) continue; var entry = levelsManifest.levels[id]; var filesList = entry && entry.files; if (!filesList || !filesList.length) continue; for (var i = 0; i < filesList.length; i++) { var rel = String(filesList[i] || ''); if (norm.indexOf(rel) >= 0 || base === rel.split('/').pop()) return id; } } return null; } function dispatchLevelsBundleProgress(progress) { if (typeof global.__tfrhOnLevelsBundleProgress === 'function') { try { global.__tfrhOnLevelsBundleProgress(progress); } catch (e) { /* ignore */ } } try { global.dispatchEvent(new CustomEvent('tfrh-levels-bundle-progress', { detail: { progress: progress }, })); } catch (e2) { /* ignore */ } try { global.dispatchEvent(new CustomEvent('tfrh-level-pack-progress', { detail: { progress: progress }, })); } catch (e3) { /* ignore */ } } function ensureLevelPackLoaded(levelId, onProgress) { levelId = String(levelId); if (loadedLevelIds.has(levelId)) return Promise.resolve(); if (levelPackPromises.has(levelId)) { var pending = levelPackPromises.get(levelId); if (onProgress) pending.then(function () { onProgress(1); }).catch(function () {}); return pending; } var promise = ensureAssetsCoreLoaded().then(function () { return ensureLevelsManifestLoaded(); }).then(function () { if (!levelsManifest || !levelsManifest.levels || !levelsManifest.levels[levelId]) { return Promise.reject(new Error('关卡包未在 manifest 中: Level' + levelId)); } if (!streamingBase) return Promise.reject(new Error('streamingBase 未初始化')); var entry = levelsManifest.levels[levelId]; var url = joinUrl(streamingBase, 'aa/WebGL/' + entry.bundle); dispatchLevelsBundleProgress(0); return fetchBinaryWithProgress( url, function (frac) { dispatchLevelsBundleProgress(frac); if (onProgress) onProgress(frac); }, 0, 1, ).then(function (buf) { mergeZipIntoFiles(unzip(buf)); var ok = (entry.files || []).every(function (rel) { return files.has(rel); }); if (!ok) { throw new Error('关卡包解压不完整: Level' + levelId); } loadedLevelIds.add(levelId); return invalidateLevelPrefabsBundle(); }).then(function () { dispatchLevelsBundleProgress(1); if (onProgress) onProgress(1); }); }).catch(function (e) { levelPackPromises.delete(levelId); throw e; }); levelPackPromises.set(levelId, promise); return promise; } /** 按关解压 import 后须丢弃旧 level-prefabs bundle,否则 bundle.load 仍用壳 config */ function invalidateLevelPrefabsBundle() { levelPrefabsBundlePromise = null; global.__tfrhLevelPrefabsIndexLoaded = false; return withCocosEngine(function (cc) { var am = cc && cc.assetManager; if (!am) return; var bundle = am.getBundle('level-prefabs'); if (bundle && typeof am.removeBundle === 'function') { try { am.removeBundle(bundle); } catch (e) { /* ignore */ } } }); } global.__tfrhEnsureLevelPack = function (levelId, onProgress) { return ensureLevelPackLoaded(levelId, onProgress); }; global.__tfrhInvalidateLevelPrefabsBundle = invalidateLevelPrefabsBundle; /** 兼容旧路径:按路径推断 levelId 再下载 */ function ensureLevelsBundleLoaded(onProgress) { return Promise.resolve(); } function prefetchLevelsBundleBackground() {} function bytesToText(body) { return new TextDecoder().decode(body); } function injectScript(code, type) { var s = document.createElement('script'); if (type) s.type = type; s.charset = 'utf-8'; s.text = code; document.body.appendChild(s); } function fileHit(src) { var base = String(src || '').replace(/^\.\//, '').replace(/^\//, ''); return lookupFile(base) || lookupFile(src) || files.get(base) || files.get(base.split('/').pop()); } function loadScriptSrc(src, type) { return new Promise(function (resolve, reject) { var root = packageRoot || getPackageRoot(); var rel = String(src || '').replace(/^\.\//, ''); var url = joinUrl(root, rel); var s = document.createElement('script'); if (type) s.type = type; s.charset = 'utf-8'; s.src = url; s.onload = function () { resolve(); }; s.onerror = function () { reject(new Error('Failed to load ' + url)); }; document.body.appendChild(s); }); } function loadScript(src, type) { var hit = fileHit(src); if (hit && files.size > 0) { return Promise.resolve(injectScript(bytesToText(hit), type)); } var root = packageRoot || getPackageRoot(); var urls = [ src, joinUrl(root, src), joinUrl(root, src.replace(/^\.\//, '')), '/unity/' + String(src).replace(/^\//, ''), ]; var baseName = String(src).split('/').pop(); if (baseName === 'cocos-bridge.js') { urls.push('/cocos-bridge.js', '/unity/cocos-bridge.js'); } var i = 0; function tryNext() { if (i >= urls.length) { return Promise.reject(new Error( 'Failed to load ' + src + ' (bundle 内无此文件;若 usecdn=true 请上传新版 StreamingAssets/aa/WebGL/*.bundle 到 CDN)', )); } var url = urls[i++]; return origFetch(url).then(function (res) { if (!res.ok) return tryNext(); return res.text(); }).then(function (code) { if (!code) return tryNext(); injectScript(code, type); }).catch(function () { return tryNext(); }); } return tryNext(); } function ensureSystemBase() { var root = packageRoot || getPackageRoot(); try { if (global.System && typeof global.System.config === 'function') { global.System.config({ baseURL: root }); } } catch (e) { /* ignore */ } // 禁止修改 document ,否则会破坏主站 sw.js、manifest 等相对路径 } function virtualUrlForBundleFile(relPath) { var hit = lookupFile(relPath) || files.get(relPath); if (!hit) return null; var cacheKey = '__tfrhBlobUrl:' + relPath; if (!global[cacheKey]) { global[cacheKey] = URL.createObjectURL(new Blob([hit], { type: mimeForPath(relPath) })); } return global[cacheKey]; } function patchImportMapCc(map, useLocalDisk) { if (!map.imports || !map.imports.cc) return map; // bundle 模式:cc.js 仅在 zip 解压后的内存中,不能用磁盘路径 if (!useLocalDisk) { var virtual = virtualUrlForBundleFile('cocos-js/cc.js'); if (virtual) { map.imports.cc = virtual; return map; } } var rootHref = packageRoot || getPackageRoot(); var basePath = new URL(rootHref, global.location.href).pathname; if (!/\/$/.test(basePath)) basePath += '/'; map.imports.cc = basePath + 'cocos-js/cc.js'; return map; } function markCocosImportMapReady() { global.__tfrhCocosImportMapReady = true; try { if (global.System && typeof global.System.prepareImport === 'function') { return Promise.resolve(global.System.prepareImport()); } } catch (e) { /* ignore */ } return Promise.resolve(); } function bindCocosEngine(cc) { if (cc) { global.__tfrhCocosEngine = cc; patchAssetManagerLoadBundle(cc); } return cc; } /** loadBundle('level-prefabs') 前先注入 index.js,并保证 levels zip 已解压 */ function patchAssetManagerLoadBundle(cc) { var am = cc && cc.assetManager; if (!am || am.__tfrhLoadBundlePatched) return; var origLoadBundle = am.loadBundle.bind(am); am.__tfrhOrigLoadBundle = origLoadBundle; am.loadBundle = function (name, options, onComplete) { if (typeof options === 'function') { onComplete = options; options = null; } var bundleName = String(name || ''); if (bundleName.indexOf('level-prefabs') < 0) { return origLoadBundle(name, options, onComplete); } loadLevelPrefabsBundleOnce(cc, origLoadBundle, onComplete); }; am.__tfrhLoadBundlePatched = true; } /** 覆盖 LevelPrefabLoader:内置 c() 缓存与 bundle 壳不同步,改走已验证的 loadBundle+load 路径 */ function loadLevelPrefabViaBundle(cc, path) { var raw = String(path || '').trim(); var m = /Level(\d+)/.exec(raw); var lid = m ? parseInt(m[1], 10) : NaN; var loadPath = raw.indexOf('level-prefabs/') === 0 ? raw : ('level-prefabs/' + raw.replace(/^level-prefabs\//, '')); var prep = (typeof global.__tfrhEnsureLevelPack === 'function' && !Number.isNaN(lid)) ? global.__tfrhEnsureLevelPack(lid) : Promise.resolve(); var Prefab = cc.Prefab; return prep.then(function () { return invalidateLevelPrefabsBundle(); }).then(function () { return ensureLevelsManifestLoaded(); }).then(function (manifest) { var entry = manifest && manifest.levels && manifest.levels[String(lid)]; var uuid = entry && entry.uuid; return new Promise(function (resolve, reject) { function finish(err, asset) { if (!err && asset) resolve(asset); else reject(err || new Error('missing prefab: ' + loadPath)); } function tryBundleLoad() { cc.assetManager.loadBundle('level-prefabs', function (err, bundle) { if (err || !bundle) { reject(err || new Error('level-prefabs bundle unavailable')); return; } bundle.load(loadPath, Prefab, finish); }); } if (uuid) { cc.assetManager.loadAny({ uuid: uuid, type: Prefab }, function (err, asset) { if (!err && asset) { resolve(asset); return; } tryBundleLoad(); }); return; } tryBundleLoad(); }); }); } function installLevelPrefabLoadFix(cc) { if (!cc) return Promise.resolve(); global.__tfrhLoadLevelPrefab = function (path) { return loadLevelPrefabViaBundle(cc, path); }; if (!global.System) return Promise.resolve(); return global.System.import('chunks:///_virtual/LevelPrefabLoader.ts').then(function (mod) { if (!mod) return; mod.loadLevelPrefab = global.__tfrhLoadLevelPrefab; mod.__tfrhLoadPatched = true; }).catch(function () { /* module not ready */ }); } /** cc.game.init 后仅确保 resources;level-prefabs 进关时 loadLevelPrefabsBundleOnce 再下 levels_all */ function ensureCocosResourcesBundle(cc) { if (!cc || !cc.assetManager) return Promise.resolve(); var origLoadBundle = cc.assetManager.__tfrhOrigLoadBundle; if (!origLoadBundle) return Promise.resolve(); return ensureResourcesBundleOnCc(cc, origLoadBundle); } function ensureCocosGameRunning(cc) { if (!cc || !cc.game) return Promise.resolve(); if (typeof global.__tfrhSyncEmbeddedCanvasNow === 'function') { global.__tfrhSyncEmbeddedCanvasNow(); } if (cc.game.inited) { syncEmbeddedCamerasFromLoader(cc); return installLevelPrefabLoadFix(cc); } return cc.game.init({ debugMode: cc.DebugMode.ERROR, settingsPath: 'src/settings.json', }).then(function () { if (typeof global.__tfrhSyncEmbeddedCanvasNow === 'function') { global.__tfrhSyncEmbeddedCanvasNow(); } return ensureCocosResourcesBundle(cc); }).then(function () { return installLevelPrefabLoadFix(cc); }).then(function () { return cc.game.run(); }).then(function () { syncEmbeddedCamerasFromLoader(cc); if (typeof global.__tfrhSyncEmbeddedCanvas === 'function') { global.__tfrhSyncEmbeddedCanvas(); } }); } function withCocosEngine(fn) { if (!global.__tfrhCocosImportMapReady) return Promise.resolve(); var cached = global.__tfrhCocosEngine; if (cached) { fn(cached); return Promise.resolve(); } if (!global.System) return Promise.resolve(); return global.System.import('cc').then(function (cc) { bindCocosEngine(cc); fn(cc); }).catch(function () { /* cc not ready */ }); } function loadImportMapFromDisk() { var root = packageRoot || getPackageRoot(); return origFetch(joinUrl(root, 'src/import-map.json')).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status + ' for import-map.json'); return res.json(); }).then(function (map) { var s = document.createElement('script'); s.type = 'systemjs-importmap'; s.charset = 'utf-8'; s.textContent = JSON.stringify(patchImportMapCc(map, true)); document.head.appendChild(s); return markCocosImportMapReady(); }); } function loadImportMap() { var hit = fileHit('src/import-map.json'); var mapPromise; if (hit && files.size > 0) { try { mapPromise = Promise.resolve(JSON.parse(bytesToText(hit))); } catch (e) { mapPromise = Promise.reject(e); } } else { var root = packageRoot || getPackageRoot(); mapPromise = origFetch(joinUrl(root, 'src/import-map.json')).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status + ' for import-map.json'); return res.json(); }); } return mapPromise.then(function (map) { var s = document.createElement('script'); s.type = 'systemjs-importmap'; s.charset = 'utf-8'; s.textContent = JSON.stringify(patchImportMapCc(map, false)); document.head.appendChild(s); return markCocosImportMapReady(); }); } function assertCocosRuntimeLoaded() { if (files.has('index.js') && (files.has('cocos-bridge.js') || files.has('application.js'))) return; throw new Error( 'scenes bundle 不是 Cocos 运行时包(可能 CDN 仍是旧 Unity bundle)。' + ' 请执行 deploy-to-001code.sh 并上传 StreamingAssets/aa/WebGL/defaultlocalgroup_scenes_all_*.bundle', ); } function ensureCocosDom(canvas) { if (!canvas) return; canvas.id = 'GameCanvas'; canvas.tabIndex = 99; var inner = document.getElementById('Cocos3dGameContainer'); if (!inner) { inner = document.createElement('div'); inner.id = 'Cocos3dGameContainer'; canvas.parentNode.insertBefore(inner, canvas); inner.appendChild(canvas); } inner.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;overflow:hidden;'; canvas.style.cssText = 'display:block;width:100%;height:100%;'; installEmbeddedStageLayout(canvas); } function applyEmbeddedResolution(cc) { if (!cc || !cc.view) return; var view = cc.view; var RES = cc.ResolutionPolicy; view.resizeWithBrowserSize(true); view.setDesignResolutionSize(960, 600, RES.FIXED_WIDTH); } function syncEmbeddedCamerasFromLoader(cc) { if (!cc || !cc.view || !cc.director) return; applyEmbeddedResolution(cc); var vis = cc.view.getVisibleSize(); var halfH = (vis.height > 0 ? vis.height : 600) / 2; var scene = cc.director.getScene(); if (!scene) return; var find = cc.find; ['UICamera', 'BgCamera', 'Main Camera'].forEach(function (name) { var node = find(name, scene); if (!node) return; var cam = node.getComponent(cc.Camera); if (cam) cam.orthoHeight = halfH; }); var mainNode = find('Main Camera', scene); if (mainNode) mainNode.setPosition(0, 0, mainNode.position.z); } function installEmbeddedStageLayout(canvas) { if (!canvas || canvas.__tfrhEmbeddedLayout) return; canvas.__tfrhEmbeddedLayout = true; var stageRoot = canvas.parentElement; while (stageRoot && stageRoot !== document.body) { if (stageRoot.classList && stageRoot.className.indexOf('unity-stage-root') >= 0) break; stageRoot = stageRoot.parentElement; } if (!stageRoot || stageRoot === document.body) { stageRoot = canvas.parentElement; while (stageRoot && stageRoot !== document.body) { if (stageRoot.clientWidth > 0 && stageRoot.clientHeight > 0) break; stageRoot = stageRoot.parentElement; } if (!stageRoot || stageRoot === document.body) { stageRoot = canvas.parentElement; } } var canvasResizeHooked = false; var syncRaf = 0; var syncing = false; function afterCanvasResize(cc) { syncEmbeddedCamerasFromLoader(cc); if (typeof global.__tfrhSyncHudOrtho === 'function') global.__tfrhSyncHudOrtho(); } function setFrameSizeIfChanged(cc, w, h) { if (w <= 0 || h <= 0) return false; var frame = cc.view.getFrameSize(); if (Math.floor(frame.width) === w && Math.floor(frame.height) === h) return false; cc.view.setFrameSize(w, h); return true; } function ensureCanvasResizeHook(cc) { if (canvasResizeHooked) return; canvasResizeHooked = true; cc.view.on('canvas-resize', function () { // Never call setFrameSize here — it re-triggers canvas-resize and overflows the stack. afterCanvasResize(cc); }); } function runSyncLayout() { var w = Math.floor((stageRoot && stageRoot.clientWidth) || 0); var h = Math.floor((stageRoot && stageRoot.clientHeight) || 0); if (w <= 0 || h <= 0) return; var container = document.getElementById('Cocos3dGameContainer'); if (container) { container.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;overflow:hidden;'; } canvas.style.cssText = 'display:block;width:100%;height:100%;'; if (stageRoot) { stageRoot.style.position = 'relative'; stageRoot.style.minHeight = '0'; if (!stageRoot.classList || stageRoot.className.indexOf('unity-stage-root') < 0) { stageRoot.style.width = '100%'; stageRoot.style.height = '100%'; } stageRoot.style.backgroundColor = '#010101'; } if (!global.__tfrhCocosImportMapReady) return; if (syncing) return; syncing = true; withCocosEngine(function (cc) { ensureCanvasResizeHook(cc); cc.view.resizeWithBrowserSize(true); if (setFrameSizeIfChanged(cc, w, h)) { /* afterCanvasResize runs via canvas-resize listener */ } else { afterCanvasResize(cc); } }).finally(function () { syncing = false; }); } function syncLayout() { if (syncRaf) return; syncRaf = requestAnimationFrame(function () { syncRaf = 0; runSyncLayout(); }); } global.__tfrhSyncEmbeddedCanvas = syncLayout; global.__tfrhSyncEmbeddedCanvasNow = runSyncLayout; runSyncLayout(); syncLayout(); if (typeof ResizeObserver !== 'undefined' && stageRoot) { var ro = new ResizeObserver(syncLayout); ro.observe(stageRoot); } window.addEventListener('resize', syncLayout); } function waitInstance(maxMs) { maxMs = maxMs || 120000; return new Promise(function (resolve, reject) { var t0 = Date.now(); (function tick() { var ins = global.unityInstance || global.cocosIns; if (ins && global.__tfrhCocosReady) return resolve(ins); if (Date.now() - t0 > maxMs) return reject(new Error('Cocos instance timeout')); requestAnimationFrame(tick); })(); }); } function probeLocalExtracted() { var root = packageRoot || getPackageRoot(); var url = joinUrl(root, 'index.js'); // 不用 HEAD:dev server 可能对缺失文件仍返回 200/HTML,误判为本地包 return origFetch(url, { cache: 'no-cache' }).then(function (res) { if (!res.ok) return false; return res.text().then(function (txt) { if (!txt || /<\s*html/i.test(txt)) return false; return /System\.register|application\.js/i.test(txt); }); }).catch(function () { return false; }); } function systemImportEntry(useLocalDisk) { if (!useLocalDisk) { var hit = fileHit('index.js'); if (hit) { injectScript(bytesToText(hit)); return Promise.resolve(); } } var root = packageRoot || getPackageRoot(); // 主站嵌入在 /editor.html,不能用相对 ./index.js(会解析到站点根目录) var entryUrl = joinUrl(root, 'index.js'); if (useLocalDisk) { entryUrl += '?v=' + Date.now(); } return global.System.import(entryUrl); } function unzip(buf) { var view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); var out = new Map(); var pos = 0; while (pos + 4 < view.byteLength) { if (view.getUint32(pos, true) !== 0x04034b50) break; var comp = view.getUint16(pos + 8, true); var compSize = view.getUint32(pos + 18, true); var nameLen = view.getUint16(pos + 26, true); var extraLen = view.getUint16(pos + 28, true); var name = new TextDecoder().decode(new Uint8Array(buf.buffer, buf.byteOffset + pos + 30, nameLen)); var dataStart = pos + 30 + nameLen + extraLen; var raw = new Uint8Array(buf.buffer, buf.byteOffset + dataStart, compSize); var body; if (comp === 0) body = raw; else throw new Error('Unsupported zip compression method ' + comp + ' (pack with zip -0)'); var norm = name.replace(/\\/g, '/').replace(/^\/+/, ''); out.set(norm, body); pos = dataStart + compSize; } return out; } function isZipBytes(bytes) { return bytes && bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b; } function isJsonText(text) { if (!text) return false; var c = text.charCodeAt(0); return c === 0x7b || c === 0x5b; } function decodeBrotliToBytes(ab) { if (typeof DecompressionStream !== 'undefined') { return new Response(ab).pipeThrough(new DecompressionStream('brotli')) .arrayBuffer() .then(function (buf) { return new Uint8Array(buf); }); } return Promise.reject(new Error('浏览器不支持 Brotli 解压')); } /** OSS 若对 .br 文件再设 Content-Encoding:br,fetch 已解压成 zip/json,勿二次 Brotli */ function normalizeBinaryPayload(bytes, expectBrotli) { if (!expectBrotli || isZipBytes(bytes)) { return Promise.resolve(bytes); } var slice = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); return decodeBrotliToBytes(slice).catch(function () { if (isZipBytes(bytes)) return bytes; return Promise.reject(new Error('Brotli 解压失败')); }); } function fetchBinary(url, onProgress) { return fetchBinaryWithProgress(url, onProgress, 0, 1); } function decodeBrotliToText(ab) { if (typeof DecompressionStream !== 'undefined') { return new Response(ab).pipeThrough(new DecompressionStream('brotli')) .arrayBuffer() .then(function (buf) { return new TextDecoder().decode(buf); }); } return Promise.reject(new Error('浏览器不支持 Brotli 解压')); } function normalizeJsonPayload(ab, expectBrotli) { var text = new TextDecoder().decode(ab); if (!expectBrotli || isJsonText(text)) return Promise.resolve(text); return decodeBrotliToText(ab).catch(function () { text = new TextDecoder().decode(ab); if (isJsonText(text)) return text; return Promise.reject(new Error('Brotli JSON 解压失败')); }); } function fetchBinaryWithProgress(url, onProgress, weightStart, weightEnd) { weightStart = weightStart == null ? 0 : weightStart; weightEnd = weightEnd == null ? 1 : weightEnd; var brUrl = String(url).replace(/\.bundle(\?.*)?$/i, '.bundle.br$1'); if (!/\.br(\?|$)/i.test(brUrl) && brUrl.indexOf('.bundle') >= 0) { /* brUrl already set */ } else if (/\.json(\?|$)/i.test(url)) { brUrl = url.replace(/\.json(\?.*)?$/i, '.json.br$1'); } else if (!/\.br(\?|$)/i.test(url)) { brUrl = url + '.br'; } else { brUrl = url; } function readResponse(res, isBrotli) { if (!res.ok) throw new Error('HTTP ' + res.status + ' for ' + url); if (!res.body || !res.body.getReader) { return res.arrayBuffer().then(function (ab) { var bytes = new Uint8Array(ab); return normalizeBinaryPayload(bytes, isBrotli).then(function (out) { if (onProgress) onProgress(weightEnd); return out; }); }); } var reader = res.body.getReader(); var chunks = []; var received = 0; var total = Number(res.headers.get('content-length')) || 0; function pump() { return reader.read().then(function (result) { if (result.done) { var out = new Uint8Array(received); var off = 0; for (var i = 0; i < chunks.length; i++) { out.set(chunks[i], off); off += chunks[i].length; } if (isBrotli) { return normalizeBinaryPayload(out, true).then(function (decoded) { if (onProgress) onProgress(weightEnd); return decoded; }); } if (onProgress) onProgress(weightEnd); return out; } chunks.push(result.value); received += result.value.length; if (onProgress && total > 0) { var frac = received / total; onProgress(weightStart + (weightEnd - weightStart) * frac); } return pump(); }); } return pump(); } return origFetch(brUrl).then(function (res) { if (res.ok) return readResponse(res, true); return origFetch(url).then(function (plain) { return readResponse(plain, false); }); }).catch(function () { return origFetch(url).then(function (plain) { return readResponse(plain, false); }); }); } function ensureAssetsCoreLoaded(onProgress) { if (assetsCoreLoaded) return Promise.resolve(); if (assetsCorePromise) return assetsCorePromise; if (!bundleManifest || !bundleManifest.assetsUrl) { return Promise.reject(new Error('assets_all manifest 未就绪')); } assetsCorePromise = fetchBinaryWithProgress( bundleManifest.assetsUrl, onProgress || function () {}, 0, 1, ).then(function (buf) { mergeZipIntoFiles(unzip(buf)); patchSettingsScriptPackages(); assetsCoreLoaded = true; }).catch(function (e) { assetsCorePromise = null; throw e; }); return assetsCorePromise; } global.__tfrhEnsureAssetsCore = function (onProgress) { return ensureAssetsCoreLoaded(onProgress); }; function loadStartupBundles(manifest, onProgress) { onProgress(0.05); /* internal/main 等引擎启动即需,assets_all 须与 scenes 并行首屏加载 */ var scenesP = fetchBinaryWithProgress(manifest.scenesUrl, onProgress, 0.05, 0.42); var assetsP = fetchBinaryWithProgress(manifest.assetsUrl, onProgress, 0.42, 0.58); return Promise.all([scenesP, assetsP]).then(function (bufs) { mergeZipIntoFiles(unzip(bufs[0])); assertCocosRuntimeLoaded(); mergeZipIntoFiles(unzip(bufs[1])); patchSettingsScriptPackages(); assetsCoreLoaded = true; onProgress(0.6); }); } function mergeZipIntoFiles(zipMap) { zipMap.forEach(function (v, k) { files.set(k, v); }); } function patchSettingsScriptPackages() { var hit = files.get('src/settings.json'); if (!hit) return; try { var settings = JSON.parse(bytesToText(hit)); if (settings.scripting && Array.isArray(settings.scripting.scriptPackages)) { settings.scripting.scriptPackages = settings.scripting.scriptPackages.map(function (pkg) { return resolveBundleVirtualUrl(pkg) || pkg; }); } if (settings.rendering && settings.rendering.effectSettingsPath) { var effectVirt = resolveBundleVirtualUrl(settings.rendering.effectSettingsPath); if (effectVirt) settings.rendering.effectSettingsPath = effectVirt; } files.set('src/settings.json', new TextEncoder().encode(JSON.stringify(settings))); } catch (e) { console.warn('[mstest5] patch settings failed', e); } } function parseCatalogBundles(catalog) { var ids = catalog && catalog.m_InternalIds; if (!ids || !ids.length) throw new Error('catalog.json missing m_InternalIds'); var names = []; var i; for (i = 0; i < ids.length; i++) { var id = String(ids[i] || ''); if (id.indexOf('.bundle') < 0) continue; names.push(id.split('/').pop()); } if (!names.length) throw new Error('catalog.json has no .bundle entries'); var scenes = null; var assets = null; var levels = null; for (i = 0; i < names.length; i++) { if (names[i].indexOf('scenes_all') >= 0) scenes = names[i]; if (names[i].indexOf('assets_all') >= 0) assets = names[i]; if (names[i].indexOf('levels_all') >= 0) levels = names[i]; } if (!scenes || !assets) { throw new Error('catalog.json missing scenes_all/assets_all bundles: ' + names.join(', ')); } if (!levels) { /* levels_all 已废弃,改用 levels-manifest.json 按关分包 */ } return { scenes: scenes, assets: assets, levels: levels, all: names }; } function loadLevelsManifest(base) { var manifestUrl = joinUrl(base, 'aa/levels-manifest.json'); return origFetch(manifestUrl, { cache: 'no-cache' }).then(function (res) { if (!res.ok) { console.warn('[mstest5] 缺少 levels-manifest.json,将无法按关下载'); return null; } return res.json(); }).catch(function (e) { console.warn('[mstest5] levels-manifest.json 读取失败', e); return null; }); } function ensureLevelsManifestLoaded() { if (levelsManifest) return Promise.resolve(levelsManifest); if (levelsManifestPromise) return levelsManifestPromise; if (!streamingBase) return Promise.reject(new Error('streamingBase 未初始化')); levelsManifestPromise = loadLevelsManifest(streamingBase).then(function (lm) { levelsManifest = lm; return lm; }); return levelsManifestPromise; } function findLevelDbShard(index, levelId) { if (!index || !index.shards) return null; var id = Number(levelId); for (var i = 0; i < index.shards.length; i++) { var s = index.shards[i]; if (id >= s.min && id <= s.max) return s; } return null; } function ensureLevelDbShardLoaded(levelId) { var index = global.__tfrhLevelsDbIndex; if (!index || index.mode !== 'sharded') return Promise.resolve(); var shard = findLevelDbShard(index, levelId); if (!shard) return Promise.resolve(); var key = shard.file; if (loadedLevelDbShards.has(key)) return Promise.resolve(); if (levelDbShardPromises.has(key)) return levelDbShardPromises.get(key); if (!streamingBase) return Promise.reject(new Error('streamingBase 未初始化')); var url = joinUrl(streamingBase, 'aa/' + key.replace(/^\/+/, '')); var promise = fetchLevelsDatabaseJson(url).then(function (json) { loadedLevelDbShards.add(key); if (!global.__tfrhLevelsDatabaseJson) { global.__tfrhLevelsDatabaseJson = { version: index.version || 2, levels: {} }; } var merged = global.__tfrhLevelsDatabaseJson.levels || {}; var incoming = (json && json.levels) || {}; Object.keys(incoming).forEach(function (k) { merged[k] = incoming[k]; }); global.__tfrhLevelsDatabaseJson.levels = merged; }).catch(function (e) { levelDbShardPromises.delete(key); throw e; }); levelDbShardPromises.set(key, promise); return promise; } global.__tfrhEnsureLevelDbShard = function (levelId) { return ensureLevelDbShardLoaded(levelId); }; function loadBundleManifest(config) { var base = resolveStreamingBase(config); var catalogUrl = joinUrl(base, 'aa/catalog.json'); return origFetch(catalogUrl).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status + ' for ' + catalogUrl); return res.json(); }).then(function (catalog) { var bundles = parseCatalogBundles(catalog); var manifest = { scenesUrl: joinUrl(base, 'aa/WebGL/' + bundles.scenes), assetsUrl: joinUrl(base, 'aa/WebGL/' + bundles.assets), bundleNames: bundles, }; bundleManifest = manifest; return manifest; }); } function bootstrapCocos(onProgress, useLocalDisk) { onProgress(0.65); installEmbeddedPathFix(); if (!useLocalDisk) installFetchShim(); var canvas = document.getElementById('unity-canvas') || document.getElementById('GameCanvas'); var load = useLocalDisk ? loadScriptSrc : loadScript; var mapStep = useLocalDisk ? loadImportMapFromDisk : loadImportMap; return load('cocos-bridge.js') .then(function () { onProgress(0.7); return load('src/polyfills.bundle.js'); }) .then(function () { onProgress(0.75); return load('src/system.bundle.js'); }) .then(function () { ensureSystemBase(); onProgress(0.8); return mapStep(); }) .then(function () { ensureCocosDom(canvas); onProgress(0.85); return systemImportEntry(useLocalDisk); }) .then(function () { return global.System.import('cc').then(function (cc) { bindCocosEngine(cc); return ensureCocosGameRunning(cc); }); }) .then(function () { onProgress(0.92); return waitInstance(); }) .then(function (ins) { onProgress(1); if (!ins.SetFullscreen) { ins.SetFullscreen = function () { var el = document.getElementById('unity-container') || document.documentElement; if (el.requestFullscreen) el.requestFullscreen(); }; } global.unityInstance = ins; prefetchLevelsBundleBackground(); var sync = global.__tfrhSyncEmbeddedCanvas; if (typeof sync === 'function') { sync(); setTimeout(sync, 0); setTimeout(sync, 120); setTimeout(sync, 400); setTimeout(sync, 1000); setTimeout(sync, 2000); } withCocosEngine(function (cc) { syncEmbeddedCamerasFromLoader(cc); }); return ins; }); } // loader 解析时即安装路径修复(Cocos 后续动态 script 需要) packageRoot = getPackageRoot(); installEmbeddedPathFix(); function resolveLevelsDbBase(config) { var sa = config && config.streamingAssetsUrl; if (sa && /^https?:\/\//i.test(sa)) { return String(sa).replace(/\/StreamingAssets\/?$/i, ''); } return packageRoot || getPackageRoot(); } function resolveLevelsDatabaseUrl(config) { if (global.__tfrhLevelsDatabaseUrl) return global.__tfrhLevelsDatabaseUrl; return joinUrl(resolveLevelsDbBase(config), 'levels-database.json'); } function resolveLevelsDbIndexUrl(config) { if (global.__tfrhLevelsDbIndexUrl) return global.__tfrhLevelsDbIndexUrl; return joinUrl(resolveLevelsDbBase(config), 'levels-db-index.json'); } function fetchLevelsDatabaseJson(url) { var brUrl = url.replace(/\.json(\?.*)?$/i, '.json.br$1'); return origFetch(brUrl).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res.arrayBuffer().then(function (ab) { return normalizeJsonPayload(ab, true).then(function (text) { return JSON.parse(text); }); }); }).catch(function () { return origFetch(url).then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json(); }); }); } function prefetchLevelDbIndex(config) { var indexUrl = resolveLevelsDbIndexUrl(config); var legacyUrl = resolveLevelsDatabaseUrl(config); global.__tfrhLevelsDbIndexUrl = indexUrl; global.__tfrhLevelsDatabaseUrl = legacyUrl; if (global.__tfrhLevelsDbIndex) return Promise.resolve(); return fetchLevelsDatabaseJson(indexUrl).then(function (json) { if (json && json.mode === 'sharded') { global.__tfrhLevelsDbIndex = json; return; } throw new Error('非分片索引'); }).catch(function (e) { console.warn('[mstest5] 关卡库索引预取失败,启动时将由 LevelDatabase 回退', indexUrl, e); }); } function isRemoteCdnConfig(config) { var sa = config && config.streamingAssetsUrl; return !!(sa && /^https?:\/\//i.test(String(sa))); } function shouldUseBundlePackFlow(config) { if (isRemoteCdnConfig(config)) { return Promise.resolve(true); } var base = resolveStreamingBase(config); var catalogUrl = joinUrl(base, 'aa/catalog.json'); return origFetch(catalogUrl, { cache: 'no-cache' }).then(function (res) { return res.ok; }).catch(function () { return false; }); } global.createUnityInstance = function (canvas, config, onProgress) { onProgress = onProgress || function () {}; onProgress(0.02); resolveStreamingBase(config); return prefetchLevelDbIndex(config).then(function () { return shouldUseBundlePackFlow(config); }).then(function (useBundles) { if (!useBundles) { return probeLocalExtracted().then(function (useLocalDisk) { if (useLocalDisk) { onProgress(0.35); return bootstrapCocos(onProgress, true); } return loadBundleManifest(config).then(function (manifest) { return loadStartupBundles(manifest, onProgress); }).then(function () { prefetchLevelsBundleBackground(); return bootstrapCocos(onProgress, false); }); }); } return loadBundleManifest(config).then(function (manifest) { return loadStartupBundles(manifest, onProgress); }).then(function () { prefetchLevelsBundleBackground(); return bootstrapCocos(onProgress, false); }); }); }; })(typeof window !== 'undefined' ? window : globalThis);