@honor-minigame/cli
Version:
honor minigame pack cli
93 lines (85 loc) • 4.33 kB
JavaScript
/**
* 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);
}
})();