(function (global) { 'use strict'; /** * 资源进包(对齐 Cocos 构建规则): * 1) 在 Bundle 目录/config 内 → 该 Bundle * 2) 不在任何 Bundle 内,被若干 Bundle 引用 → 优先级数字最大的 Bundle * 3) 无人引用 → internal * 优先级:config.priority,数字越大越高;无配置则 0。 */ function fmtType(asset) { var t = (asset && asset.constructor && asset.constructor.name) || 'Other'; return String(t).replace(/^cc\./, ''); } function memMB(bytes) { return (bytes || 0) / (1024 * 1024); } function texMemMB(tex) { if (!tex) return 0; var w = tex.width || 0; var h = tex.height || 0; if (!w || !h) { try { if (tex.image && tex.image.width) { w = tex.image.width; h = tex.image.height; } } catch (e) { /* */ } } return w && h ? memMB(w * h * 4 * 1.33) : 0; } function assetMem(asset, type, seenTex) { if (type === 'Texture2D' || type === 'Texture') { var key = String(asset._uuid || asset.uuid || ''); if (key && seenTex[key]) return 0; if (key) seenTex[key] = 1; return texMemMB(asset); } if (type === 'SpriteFrame' || type === 'ImageAsset') return 0; if (type === 'AudioClip' || /Audio/.test(type)) { try { var d = asset.duration || (asset._nativeAsset && asset._nativeAsset.duration) || 0; return d ? memMB(d * 44100 * 2 * 2) : 0.2; } catch (e) { return 0.2; } } try { if (asset._nativeAsset && asset._nativeAsset.byteLength) return memMB(asset._nativeAsset.byteLength); if (asset.data && asset.data.byteLength) return memMB(asset.data.byteLength); } catch (e) { /* */ } return 0; } function frameSize(asset) { var rect = asset.rect || asset._rect; if (rect && rect.width) return { w: rect.width | 0, h: rect.height | 0 }; return { w: asset.width | 0, h: asset.height | 0 }; } function gfxTotals() { var cc = global.cc; var tex = 0; var buf = 0; var heap = 0; try { var st = cc && cc.profiler && cc.profiler.stats; if (st) { if (st.textureMemory && st.textureMemory.counter) tex = st.textureMemory.counter.value || 0; if (st.bufferMemory && st.bufferMemory.counter) buf = st.bufferMemory.counter.value || 0; } } catch (e) { /* */ } try { if (performance.memory && performance.memory.usedJSHeapSize) { heap = performance.memory.usedJSHeapSize / (1024 * 1024); } } catch (e2) { /* */ } return { gfxTextureMB: tex, gfxBufferMB: buf, jsHeapMB: heap }; } /** 优先级:数字越大越高 */ function readPriority(bundle, name) { try { if (bundle && bundle.priority != null && isFinite(bundle.priority)) return Number(bundle.priority); } catch (e) { /* */ } try { var cfg = bundle && bundle._config; if (cfg && cfg.priority != null && isFinite(cfg.priority)) return Number(cfg.priority); } catch (e2) { /* */ } try { if (bundle && bundle._priority != null && isFinite(bundle._priority)) return Number(bundle._priority); } catch (e3) { /* */ } return 0; } /** nativeUrl 里的包名:…/home/import/… → home */ function bundleFromUrl(url) { if (!url) return ''; var m = String(url).match(/\/([A-Za-z0-9_\-]+)\/(?:import|native|js|ohm|chunks)\//i); return m && m[1] ? m[1] : ''; } /** * 建 uuid → bundle 显式索引(构建归属)。 * 顺序:nativeUrl → getAssetInfo → cache → config.paths/packs */ function buildIndex(cc) { var uuidMap = {}; var pathMap = {}; var packOf = {}; var packMembers = {}; var bundleObjs = []; var list = []; cc.assetManager.bundles.forEach(function (bundle, name) { var n = name || (bundle && bundle.name) || ''; if (!n || !bundle) return; bundle.__pwName = n; bundleObjs.push(bundle); list.push({ name: n, assetCount: 0, memMB: 0, texMB: 0, audioMB: 0, otherMB: 0, status: 'loaded', priority: readPriority(bundle, n), }); var put = function (u) { if (u != null && u !== '') uuidMap[String(u)] = n; }; // cache try { var cache = bundle.cache || bundle._cache; if (cache && cache._map) { var keys = Object.keys(cache._map); for (var i = 0; i < keys.length; i++) { if (cache._map[keys[i]]) put(keys[i]); } } if (cache && typeof cache.forEach === 'function') { cache.forEach(function (asset, uuid) { put(uuid); if (asset) put(asset._uuid); }); } } catch (e) { /* */ } // config.paths try { var cfg = bundle._config; var paths = cfg && cfg.paths; if (paths) { for (var p in paths) { if (!Object.prototype.hasOwnProperty.call(paths, p)) continue; var val = paths[p]; var listU = Array.isArray(val) ? val : (val ? [val] : []); for (var j = 0; j < listU.length; j++) { var uu = listU[j]; if (uu && typeof uu === 'object') uu = uu.uuid; if (typeof uu === 'string' && uu) { put(uu); if (!pathMap[uu]) pathMap[uu] = p; } } } } // config.packs(自动图集) var packs = cfg && cfg.packs; if (packs) { for (var packId in packs) { if (!Object.prototype.hasOwnProperty.call(packs, packId)) continue; var members = packs[packId]; if (!members) continue; packMembers[packId] = packMembers[packId] || []; var arr = Array.isArray(members) ? members : [members]; for (var m = 0; m < arr.length; m++) { var um = arr[m]; if (um && typeof um === 'object') um = um.uuid; if (!um) continue; put(um); packOf[String(um)] = packId; packMembers[packId].push(String(um)); } } } } catch (e2) { /* */ } }); return { list: list, uuidMap: uuidMap, pathMap: pathMap, packOf: packOf, packMembers: packMembers, bundleObjs: bundleObjs, }; } /** 对每个资源:getAssetInfo / nativeUrl 再补一轮归属 */ function refineOwnership(cc, bi, rows) { var objs = bi.bundleObjs || []; for (var i = 0; i < rows.length; i++) { var row = rows[i]; var id = row.uuid; if (bi.uuidMap[id]) continue; // nativeUrl var asset = null; try { asset = cc.assetManager.assets.get(id); } catch (e) { asset = null; } var fromUrl = bundleFromUrl(asset && (asset.nativeUrl || asset._nativeUrl)); if (fromUrl) { bi.uuidMap[id] = fromUrl; row.bundle = fromUrl; continue; } // getAssetInfo for (var b = 0; b < objs.length; b++) { var bundle = objs[b]; if (!bundle || typeof bundle.getAssetInfo !== 'function') continue; try { var info = bundle.getAssetInfo(id); if (info && (info.path != null || info.uuid || info.redirect)) { bi.uuidMap[id] = bundle.__pwName || bundle.name; row.bundle = bi.uuidMap[id]; if (info.path && !bi.pathMap[id]) bi.pathMap[id] = info.path; break; } } catch (e2) { /* */ } } } } /** * 依赖进包:不在任何 Bundle 的资源 → 优先级数字最大的引用方。 */ function applyRefPacking(cc, bi, rows) { var depUtil = cc.assetManager && cc.assetManager.dependUtil; var byUuid = {}; for (var i = 0; i < rows.length; i++) byUuid[rows[i].uuid] = rows[i]; var explicit = {}; for (var k in bi.uuidMap) { if (Object.prototype.hasOwnProperty.call(bi.uuidMap, k)) explicit[k] = bi.uuidMap[k]; } var prio = {}; for (var p = 0; p < bi.list.length; p++) { prio[bi.list[p].name] = bi.list[p].priority || 0; } // 引用根:各包内显式归属的资源 var rootsByBundle = {}; for (var r = 0; r < rows.length; r++) { var row = rows[r]; var bb = explicit[row.uuid]; if (!bb) continue; if (!rootsByBundle[bb]) rootsByBundle[bb] = []; rootsByBundle[bb].push(row.uuid); } var getDeps = function (uuid) { if (!depUtil) return []; try { if (depUtil.getDependsRecursively) return depUtil.getDependsRecursively(uuid) || []; } catch (e) { /* */ } try { if (depUtil.getDeps) return depUtil.getDeps(uuid) || []; } catch (e2) { /* */ } return []; }; var refBy = {}; var addRef = function (uuid, bname) { if (!uuid || !bname) return; if (!refBy[uuid]) refBy[uuid] = []; if (refBy[uuid].indexOf(bname) < 0) refBy[uuid].push(bname); }; for (var bn in rootsByBundle) { if (!Object.prototype.hasOwnProperty.call(rootsByBundle, bn)) continue; var roots = rootsByBundle[bn]; for (var i2 = 0; i2 < roots.length; i2++) { var deps = getDeps(roots[i2]); addRef(roots[i2], bn); for (var d = 0; d < deps.length; d++) addRef(String(deps[d]), bn); } } for (var j = 0; j < rows.length; j++) { var row2 = rows[j]; var u2 = row2.uuid; // 已显式归属 if (explicit[u2]) { row2.bundle = explicit[u2]; row2.via = 'direct'; row2.usedBy = refBy[u2] ? refBy[u2].slice() : [explicit[u2]]; continue; } var refs = refBy[u2] || []; if (!refs.length) { row2.bundle = 'internal'; row2.via = 'direct'; row2.usedBy = ['internal']; continue; } // 优先级数字最大的引用方 var best = refs[0]; for (var n = 1; n < refs.length; n++) { var prBest = prio[best] || 0; var prCur = prio[refs[n]] || 0; if (prCur > prBest) best = refs[n]; } row2.bundle = best; row2.via = 'dep'; row2.usedBy = refs.slice(); } } function collectAssets() { var cc = global.cc; if (!cc || !cc.assetManager || !cc.assetManager.assets) { return { bundles: [], rows: [], totals: gfxTotals() }; } var bi = buildIndex(cc); var rows = []; var seenTex = {}; cc.assetManager.assets.forEach(function (asset, uuid) { if (!asset) return; var id = String(uuid || asset._uuid || ''); var type = fmtType(asset); var mem = assetMem(asset, type, seenTex); var bname = bi.uuidMap[id] || bundleFromUrl(asset.nativeUrl || asset._nativeUrl) || ''; var kind = 'other'; if (type === 'Texture2D' || type === 'Texture') kind = 'tex'; else if (type === 'AudioClip' || /Audio/.test(type)) kind = 'audio'; var w = 0; var h = 0; if (type === 'SpriteFrame') { var sz = frameSize(asset); w = sz.w; h = sz.h; } else { w = asset.width | 0; h = asset.height | 0; } var display = asset.name || ''; var pathStr = bi.pathMap[id] || ''; if (!display || /^[0-9a-f-]{8,}$/i.test(display)) { if (pathStr) { var segs = String(pathStr).split('/'); display = segs[segs.length - 1] || pathStr; } if (!display) display = id.slice(0, 8) + '…'; } rows.push({ uuid: id, name: display, type: type, bundle: bname, mem: mem, ref: asset.refCount != null ? asset.refCount : 0, w: w, h: h, kind: kind, pack: bi.packOf[id] || '', texUuid: type === 'SpriteFrame' ? '' : id, path: pathStr, via: bname ? 'direct' : 'direct', }); }); // 补归属 + 依赖进包 refineOwnership(cc, bi, rows); applyRefPacking(cc, bi, rows); // 汇总 bundle var byName = {}; for (var i = 0; i < bi.list.length; i++) { byName[bi.list[i].name] = bi.list[i]; } for (var z = 0; z < bi.list.length; z++) { bi.list[z].assetCount = 0; bi.list[z].memMB = 0; bi.list[z].texMB = 0; bi.list[z].audioMB = 0; bi.list[z].otherMB = 0; } for (var r = 0; r < rows.length; r++) { var row = rows[r]; var agg = byName[row.bundle]; if (!agg) { agg = { name: row.bundle || 'internal', assetCount: 0, memMB: 0, texMB: 0, audioMB: 0, otherMB: 0, status: 'loaded', priority: 0, }; byName[agg.name] = agg; bi.list.push(agg); } agg.assetCount += 1; agg.memMB += row.mem; if (row.kind === 'tex') agg.texMB += row.mem; else if (row.kind === 'audio') agg.audioMB += row.mem; else agg.otherMB += row.mem; } for (var y = 0; y < bi.list.length; y++) { if (bi.list[y].assetCount === 0) bi.list[y].status = 'loaded-empty'; } bi.list.sort(function (a, b) { return (b.memMB - a.memMB) || (b.assetCount - a.assetCount); }); var totals = gfxTotals(); totals.estimateMB = 0; totals.texMB = 0; totals.audioMB = 0; totals.otherMB = 0; for (var t = 0; t < rows.length; t++) { totals.estimateMB += rows[t].mem; if (rows[t].kind === 'tex') totals.texMB += rows[t].mem; else if (rows[t].kind === 'audio') totals.audioMB += rows[t].mem; else totals.otherMB += rows[t].mem; } totals.assetCount = rows.length; totals.bundleCount = bi.list.length; return { bundles: bi.list, rows: rows, totals: totals, packs: bi.packMembers, packOf: bi.packOf, }; } /** 更稳的位图预览:HTMLImage / ImageBitmap / ImageData / nativeUrl */ function imageFromAsset(asset, type) { if (type === 'SpriteFrame') { var tex = asset.texture || (asset.getTexture && asset.getTexture()); return { img: (tex && tex.image) || (tex && tex._image) || null, rect: asset.rect || asset._rect || null, nativeUrl: (tex && tex.image && tex.image.nativeUrl) || (tex && tex.nativeUrl) || asset.nativeUrl || '', }; } if (type === 'ImageAsset') { return { img: asset, rect: null, nativeUrl: asset.nativeUrl || '' }; } var im = asset.image || asset._image || null; return { img: im, rect: null, nativeUrl: (im && im.nativeUrl) || asset.nativeUrl || asset._nativeUrl || '', }; } function buildPreview(asset, type) { try { var pack = imageFromAsset(asset, type); var img = pack.img; var data = img && (img._data || img.data); var rect = pack.rect; // HTMLImageElement if (data && typeof HTMLImageElement !== 'undefined' && data instanceof HTMLImageElement) { if (rect && type === 'SpriteFrame') { var ih = data.height || img.height || 0; var sw = Math.max(1, rect.width | 0); var sh = Math.max(1, rect.height | 0); var cv = document.createElement('canvas'); cv.width = sw; cv.height = sh; cv.getContext('2d').drawImage( data, rect.x | 0, Math.max(0, ih - (rect.y | 0) - sh) | 0, sw, sh, 0, 0, sw, sh ); return { kind: 'image', w: sw, h: sh, url: cv.toDataURL('image/png') }; } return { kind: 'image', w: data.width, h: data.height, url: data.src || '' }; } // ImageBitmap:画到 canvas if (data && typeof ImageBitmap !== 'undefined' && data instanceof ImageBitmap) { var c2 = document.createElement('canvas'); c2.width = data.width; c2.height = data.height; c2.getContext('2d').drawImage(data, 0, 0); return { kind: 'image', w: data.width, h: data.height, url: c2.toDataURL('image/png') }; } // ImageData if (data && data.data && data.width && typeof ImageData !== 'undefined') { var c3 = document.createElement('canvas'); c3.width = data.width; c3.height = data.height; c3.getContext('2d').putImageData(new ImageData(data.data, data.width, data.height), 0, 0); return { kind: 'image', w: data.width, h: data.height, url: c3.toDataURL('image/png') }; } // TypedArray / ArrayBuffer 位图 if (data && data.buffer && data.width && data.height && typeof Uint8Array !== 'undefined') { var c4 = document.createElement('canvas'); c4.width = data.width; c4.height = data.height; var ctx4 = c4.getContext('2d'); try { ctx4.putImageData(new ImageData(new Uint8ClampedArray(data.buffer, data.byteOffset || 0, data.width * data.height * 4), data.width, data.height), 0, 0); return { kind: 'image', w: data.width, h: data.height, url: c4.toDataURL('image/png') }; } catch (e1) { /* */ } } // nativeUrl if (pack.nativeUrl) { return { kind: 'url', url: pack.nativeUrl, w: (img && img.width) || asset.width || 0, h: (img && img.height) || asset.height || 0, }; } if (asset.nativeUrl) { return { kind: 'url', url: asset.nativeUrl, w: asset.width | 0, h: asset.height | 0 }; } } catch (e) { /* */ } return { kind: 'none' }; } /** 反向引用:谁依赖了 uuid(资源/节点名) */ function reverseRefs(uuid, cc) { var deps = []; var depUtil = cc && cc.assetManager && cc.assetManager.dependUtil; var assets = cc && cc.assetManager && cc.assetManager.assets; if (!assets || !assets.forEach) return deps; try { assets.forEach(function (asset, id) { if (!id || String(id) === String(uuid)) return; var list = null; try { if (depUtil && depUtil.getDeps) list = depUtil.getDeps(id); } catch (e) { list = null; } if (!list || !list.length) return; for (var i = 0; i < list.length; i++) { if (String(list[i]) === String(uuid)) { deps.push({ uuid: String(id), name: asset && asset.name ? asset.name : String(id), type: fmtType(asset), }); break; } } }); } catch (e2) { /* */ } // 场景节点名:遍历当前场景找引用(spriteFrame/texture 等) try { if (cc.director && cc.director.getScene && cc.UITransform) { var scene = cc.director.getScene(); var walkN = function (node) { if (!node) return; var comps = node.components || []; for (var c = 0; c < comps.length; c++) { var comp = comps[c]; if (!comp) continue; var vals = ['spriteFrame', 'texture', 'spriteAtlas', 'skeletonData', 'clip', 'audioClip']; for (var v = 0; v < vals.length; v++) { var a = comp[vals[v]]; var au = a && (a._uuid || a.uuid); if (au && String(au) === String(uuid)) { deps.push({ uuid: node.uuid || '', name: (node.name || '?') + ' / ' + (comp.constructor && comp.constructor.name || ''), type: 'Node', }); break; } } } var ch = node.children || []; for (var k = 0; k < ch.length; k++) walkN(ch[k]); }; walkN(scene); } } catch (e3) { /* */ } return deps.slice(0, 40); } /** 详情:预览 + 路径 + 反向引用 */ function assetDetail(uuid) { var cc = global.cc; var asset = null; try { asset = cc.assetManager.assets.get(uuid); } catch (e) { /* */ } if (!asset) { return { kind: 'none', name: '', bundle: '', path: '', uuid: uuid, ref: 0, usedBy: [], refs: [] }; } var type = fmtType(asset); var preview = buildPreview(asset, type); var path = ''; try { cc.assetManager.bundles.forEach(function (bundle) { if (path) return; if (typeof bundle.getAssetInfo === 'function') { var inf = bundle.getAssetInfo(uuid); if (inf && inf.path) path = inf.path; } }); } catch (e2) { /* */ } var bundleName = ''; try { cc.assetManager.bundles.forEach(function (bundle, name) { if (bundleName) return; if (bundle && bundle.cache && bundle.cache.has && bundle.cache.has(uuid)) { bundleName = name || bundle.name || ''; } }); } catch (e3) { /* */ } return { kind: preview.kind, url: preview.url || '', w: preview.w || 0, h: preview.h || 0, name: asset.name || uuid, type: type, bundle: bundleName, path: path, uuid: uuid, ref: asset.refCount != null ? asset.refCount : 0, refs: reverseRefs(uuid, cc), }; } function previewAsset(uuid) { var d = assetDetail(uuid); return { kind: d.kind, url: d.url, w: d.w, h: d.h, }; } if (global.PWAgent) { global.PWAgent.prototype._listAssets = function () { return collectAssets(); }; global.PWAgent.prototype._listAssetsAsync = function (done) { try { done(collectAssets()); } catch (e) { console.warn('[assets]', e); done({ bundles: [], rows: [], totals: gfxTotals() }); } }; global.PWAgent.prototype._previewAsset = function (uuid) { return assetDetail(uuid); }; } global.PWAssetsUtil = { collectAssets: collectAssets, previewAsset: previewAsset, assetDetail: assetDetail, buildPreview: buildPreview, }; })(window);