Files
cocos/web-template/mstest5-cocos-loader.js
2026-06-18 14:07:38 +08:00

1624 lines
58 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 替换 Unity Build/mstest5.loader.js — 保持 createUnityInstance API内部启动 Cocos。
* bundle 文件名从 catalog.json 读取(与 Unity Addressables 一致),路径相对包根目录解析。
*/
(function (global) {
var PRODUCT = '__PRODUCT_NAME__';
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.jsonzip 内路径带 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 <base>,否则会破坏主站 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 后仅确保 resourceslevel-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');
// 不用 HEADdev 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:brfetch 已解压成 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);