UNPKG

@honor-minigame/cli

Version:

honor minigame pack cli

93 lines (85 loc) 4.33 kB
/** * image-load-fix.js —— 图片加载 ERROR 4930 适配(internal://files/ 协议头改写) * * 问题: * ERROR 4930 ... Arguments: internal://files/gamecaches/.../res/native/eb/xxx.png * at Image.o (cocos2d-runtime.js) // 模块42 loadImage 的 error 回调 o() * at 42.e.exports (cocos2d-runtime.js) // new Image + img.src = url * at Object.parse (cocos2d-runtime.js) // cc.assetManager.parser.parse 派发 * * 根因(已确认,非 PNG 损坏): * Cocos 2.4.x 下载远程 bundle 的图片后缓存到快游戏本地目录,路径形如 * internal://files/gamecaches/<id>/res/native/eb/<uuid>.png。该路径作为 url * 传给模块42 loadImage(new Image() + img.src = url)解码,但快游戏 rt-assets * 的 Image 兼容层【不支持 internal://files/ 协议头】,set src 时同步解码失败 * 并触发 error 事件,loadImage 抛 new Error(getError(4930, url)),沿资源管线 * 上抛导致纹理/场景加载失败、卡死黑屏。 * * 真正修复:把图片地址的 internal://files/ 协议头替换为快游戏支持的 rt-user:/ * (二者指向同一物理目录): * internal://files/gamecaches/.../xxx.png -> rt-user:/gamecaches/.../xxx.png * * 修复方式(不改引擎文件,仅运行时 monkey-patch,双路): * 路线1(主修复,确定生效):改写 cc.assetManager.parser.parse——图片类型的 * data 即为图片 url,加载前把 internal://files/ 改写为 rt-user:/。parser 是 * 普通 JS 对象,可安全替换。 * 路线2(尽力而为,扩大覆盖):改写全局 Image.prototype.src setter,使所有 * new Image().src = internal://files/... 的加载(含 spine/dragonbones 等第三 * 方图集加载)都自动改写。原生 setter 不可配置时跳过,不影响路线1。 * * 非 internal://files/ 的地址、非 4930 的成功/失败完全走原逻辑,行为零差异。 */ (function () { 'use strict'; var INTERNAL_FILES = 'internal://files/'; // internal://files/<path> -> rt-user:/<path> function rewriteUrl(url) { if (typeof url !== 'string' || url.indexOf(INTERNAL_FILES) !== 0) return url; return 'rt-user:/' + url.slice(INTERNAL_FILES.length); } // 路线2:改写全局 Image.prototype.src,覆盖所有 new Image().src=... 的加载 function patchImageSrc() { if (typeof Image === 'undefined' || !Image.prototype) return; var proto = Image.prototype; if (proto.__rtImgSrcFix) return; var desc; try { desc = Object.getOwnPropertyDescriptor(proto, 'src'); } catch (e) { return; } // 仅 accessor 且可配置时才能包裹;原生 setter 不可配置则跳过,交由路线1兜底 if (!desc || typeof desc.set !== 'function' || !desc.configurable) return; var origSet = desc.set; try { Object.defineProperty(proto, 'src', { configurable: true, enumerable: !!desc.enumerable, get: desc.get, set: function (v) { return origSet.call(this, rewriteUrl(v)); } }); proto.__rtImgSrcFix = true; } catch (e) { /* 改写失败,路线1仍生效 */ } } // 路线1:改写 cc.assetManager.parser.parse(主修复) function patchParser() { if (typeof cc === 'undefined' || !cc.assetManager || !cc.assetManager.parser) return; var parser = cc.assetManager.parser; var origParse = parser.parse; if (!origParse || parser.__imgLoadFix) return; // 防重复 patch parser.__imgLoadFix = true; parser.parse = function (id, data, type, options, onComplete) { // 图片类型的 data 即为图片 url,加载前改写协议头(非 internal://files/ // 的字符串或非字符串 data 不受影响) var rewritten = rewriteUrl(data); return origParse.call(this, id, rewritten, type, options, onComplete); }; } function apply() { patchImageSrc(); patchParser(); } // 本文件在 boot.js 中于 qgame-adapter.js、cocos2d-runtime.js 之后 require, // 故 Image 与 cc.assetManager.parser 此时均已可用;EVENT_GAME_INITED 再补一次 // 确保一定 patch 成功。 apply(); if (typeof cc !== 'undefined' && cc.game && cc.game.once) { cc.game.once(cc.game.EVENT_GAME_INITED, apply); } })();