UNPKG

@honor-minigame/cli

Version:

honor minigame pack cli

202 lines (186 loc) 7.76 kB
/** * GameGlobal polyfill —— 荣耀快游戏平台适配 * * 背景: * Unity 构建产物 buildUnity/webgl.wasm.framework.unityweb.js 中被注入了 * 一段"纹理压缩预下载"处理代码(_glCompressedTexImage2D / * _glCompressedTexSubImage2D 内的 getMatchId / renderTexture 等),该代码 * 依赖微信小游戏的全局对象 GameGlobal: * - GameGlobal.USED_TEXTURE_COMPRESSION * - GameGlobal.TextureCompressedFormat * - GameGlobal.NoneLimitSupportedTexture * - GameGlobal.DownloadedTextures * 并在 matchId 命中时调用 window.wasmsdk.QGDownloadTexture 预下载纹理。 * * 问题: * 荣耀快游戏环境不存在 GameGlobal 全局对象,且本项目并未实现上述纹理预下载 * 机制(无 wasmsdk、未填充 DownloadedTextures)。framework 在 getMatchId() * 内执行 `isMiniProgram && GameGlobal.USED_TEXTURE_COMPRESSION` 时,因访问 * 未声明的 GameGlobal 直接抛出: * ReferenceError: GameGlobal is not defined * 导致游戏崩溃。 * * 修复: * 本文件由 main.js 第一行 require,早于 UnityLoader.instantiate 加载 * framework.unityweb.js。在此创建一个空的 GameGlobal,并显式将 * USED_TEXTURE_COMPRESSION 置为 false。这样 getMatchId() 恒返回 * [-1, "8x8", false],_glCompressedTexImage2D / _glCompressedTexSubImage2D * 走标准 WebGL compressedTexImage2D 路径,不再访问 GameGlobal 上的任何纹理 * 缓存,也不再调用未实现的 QGDownloadTexture。 */ (function () { // 兼容不同运行时,定位全局对象 var globalObj = (typeof globalThis !== "undefined") ? globalThis : (typeof window !== "undefined") ? window : this; if (!globalObj.GameGlobal) { globalObj.GameGlobal = {}; } var G = globalObj.GameGlobal; // 关闭纹理压缩"预下载"机制,改走标准 WebGL 上传路径 G.USED_TEXTURE_COMPRESSION = false; // 以下三项在 USED_TEXTURE_COMPRESSION=false 时不会被访问到,仅作占位以保证健壮性 G.TextureCompressedFormat = ""; G.NoneLimitSupportedTexture = ""; G.DownloadedTextures = {}; // Image适配 const _Image = window.Image // 缓存代理构造器:getter 始终返回同一引用,保证 Image === Image 为 true, // 避免某些第三方库对 Image 做身份比较时失败;命名为 Image 也让堆栈更友好。 const ImageProxy = function Image(width, height) { return qg.createImage(width, height) } Object.defineProperty(window, 'Image', { set() { }, get() { return ImageProxy } }); const data = [ ["internal://tmp/", "rt-temp:/"], ["internal://cache/", "rt-user:/"], ["internal://files/", "rt-user:/"] ] const regCache = {}; const transformPath = (data, path) => { for (let i = 0; i < data.length; i++) { regCache[data[i][0]] = regCache[data[i][0]] || new RegExp(data[i][0], 'i'); if (regCache[data[i][0]].test(path)) { return path.replace(regCache[data[i][0]], data[i][1]) } } return null }; const formatPath = (path) => { return transformPath(data, path) || path } Object.defineProperty(window.qg, 'createImage', { enumerable: true, configurable: true, value: () => { const img = new _Image() // 在原型链上查找src属性描述符 let proto = Object.getPrototypeOf(img) let descriptor while (proto && !descriptor) { descriptor = Object.getOwnPropertyDescriptor(proto, 'src') proto = Object.getPrototypeOf(proto) } if (!descriptor) { console.error('Cannot find src property descriptor') descriptor = img } const originalSetter = descriptor.set const originalGetter = descriptor.get Object.defineProperty(img, 'src', { configurable: true, enumerable: true, get: function () { return this._src || originalGetter.call(this) }, set: function (value) { this._src = value const transformedPath = formatPath(value) return originalSetter.call(this, transformedPath) } }); return img } }) let subpackageRoots = [] try { let manifest = qg.getFileSystemManager().readFileSync('manifest.json', 'utf-8') if (manifest) { let manifestInfo = JSON.parse(manifest) if (manifestInfo.subpackages && manifestInfo.subpackages.length > 0) { // 存储分包 root 列表,用于 require 时匹配调用者所在分包 subpackageRoots = manifestInfo.subpackages.map(item => { let root = item.root.endsWith('/') ? item.root : `${item.root}/` return root }) } } } catch (error) { console.info('manifest.json解析异常', error) } // require适配 let _require = require /** * 通过文件路径获取所在目录路径 * @param {string} path 文件路径 * @return {string} 目录路径 */ function getFileDirPath(path) { if (!path) return "" const idx = path.lastIndexOf("/") return idx !== -1 ? path.substring(0, idx) : "" } function getStackTraceAt(index = 0) { const error = new Error() const stackLines = error.stack?.split('\n') || [] // 跳过两行:Error 消息行 + getStackTraceAt 自身所在的帧 const targetLine = stackLines[index + 2] if (!targetLine) return null // 支持多种 stack 帧格式: // 1. Chrome/V8 正常: " at func (path:line:col)" 或 " at func (path)" // 2. Firefox/Safari: "func@path:line:col" 或 "@path" // 3. V8 顶层匿名调用: " at /path:line:col" (路径直接跟在 "at " 之后,无函数名) // 4. 异常格式: " at <anonymous> (path)" // 注意:路径中可能含 ":"(Windows "C:\..."、URL "http://..."、file://、Android 内部路径等), // 因此路径用 .+? 非贪婪匹配,:行号:列号 用可选匹配以兼容无行号的精简格式。 let match = targetLine.match(/\((.+?)(?::\d+:\d+)?\)/) if (match) return match[1] match = targetLine.match(/@(.+?)(?::\d+:\d+)?\s*$/) if (match) return match[1] // 顶层匿名调用:路径直接跟在 "at " 之后,整行无括号无 @ match = targetLine.match(/^\s*at\s+(.+?)(?::\d+:\d+)?\s*$/) return match ? match[1].trim() : null } const newRequire = function (cls) { // 当 require("./game.js") 且存在分包时,根据调用者路径判断所在分包,拼接分包 root if (cls === './game.js' && subpackageRoots && subpackageRoots.length > 0) { const callerPath = getStackTraceAt(1) if (callerPath) { const callerDir = getFileDirPath(callerPath) // 查找调用者所在的分包 root const matchedRoot = qg.subpackageRoots.find(root => { // 统一去掉前导 ./、/、\ 以及结尾的 /,保证 callerDir.includes 能正确匹配 const normalizedRoot = root .replace(/\\/g, '/') .replace(/^\.?\/?/, '') .replace(/\/$/, '') return callerDir.includes(normalizedRoot) }) if (matchedRoot) { // 拼接分包路径:./分包root/game.js const cleanRoot = matchedRoot.replace(/\/$/, '') console.log('加载的分包路径:', `./${cleanRoot}/game.js`) return _require(`./${cleanRoot}/game.js`) } } } return _require(cls) } require = newRequire })();