UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

1,148 lines (1,112 loc) 43 kB
(function () { // #region 通用变量和方法 let isOPPO = () => { const result = window.qg.getProvider().toLowerCase() === 'oppo' isOPPO = () => result return isOPPO() } let isVivo = () => { const result = window.qg.getProvider().toLowerCase() === 'vivo' isVivo = () => result return isVivo() } let isMi = () => { const result = window.qg.getProvider().toLowerCase().includes('mi') isMi = () => result return isMi() } let isHonor = () => { const result = window.qg.getProvider().toLowerCase() === 'honor' isHonor = () => result return isHonor() } // #endregion // ### 底层函数 Object 相关修改 ### // 保存原始的 Object.defineProperty 方法 var originalDefineProperty = Object.defineProperty // 重定义 Object.defineProperty 方法 Object.defineProperty = function (obj, prop, descriptor) { if (prop === 'HTMLCanvasElement') { try { // 调用原始的 Object.defineProperty 方法 return originalDefineProperty(obj, prop, descriptor) } catch (err) { console.warn(err) return obj } } else { // 调用原始的 Object.defineProperty 方法 return originalDefineProperty(obj, prop, descriptor) } } // 将对象定义为不可更改 const defineReadOnlyPropertyContain = (obj, name, value) => { try { Object.defineProperty(obj, name, { enumerable: true, get () { return value }, set (newValue) { if (newValue !== value) { console.warn(`rewrite the property: ${name}, read only.`, newValue) return } value = newValue } }) } catch (e) { console.warn('不影响正常运行:', e) } } // 将对象定义为不可更改 const defineReadOnlyPropertyCanDefine = (obj, name, value) => { try { Object.defineProperty(obj, name, { enumerable: true, get () { return value }, set (newValue) { if (newValue !== value) { console.warn(`rewrite the property: ${name}`, newValue) } value = newValue } }) } catch (e) { console.warn('不影响正常运行:', e) } } try { // 兼容vivo旧的readFileSync接口的回调接口 const stringPro = String.prototype Object.defineProperty(stringPro, 'data', { get: function () { return this.toString() }, enumerable: false }) Object.defineProperty(stringPro, 'text', { get: function () { return this.toString() }, enumerable: false }) if (!('replaceAll' in stringPro)) { Object.defineProperty(stringPro, 'replaceAll', { get: function (from, to) { from = from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') return this.replace(new RegExp(from, 'g'), to) }, enumerable: false }) } const arrayBufferPro = ArrayBuffer.prototype Object.defineProperty(arrayBufferPro, 'data', { get: function () { return this }, enumerable: false }) Object.defineProperty(arrayBufferPro, 'text', { get: function () { return this }, enumerable: false }) if (!('replaceAll' in stringPro)) { Object.defineProperty(stringPro, 'replaceAll', { get: function (from, to) { from = from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') return this.replace(new RegExp(from, 'g'), to) }, enumerable: false }) } } catch (err) { } // ### 渲染层相关修改 ### let descriptor = Object.getOwnPropertyDescriptor(window, 'TouchEvent') if (window.TouchEvent && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window, 'TouchEvent', window.TouchEvent) } descriptor = Object.getOwnPropertyDescriptor(window, 'Image') if (window.Image && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window, 'Image', window.Image) } descriptor = Object.getOwnPropertyDescriptor(window, 'HTMLImageElement') if (window.HTMLImageElement && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window, 'HTMLImageElement', window.HTMLImageElement) } if (!window.qg.createImage && window.Image) { defineReadOnlyPropertyContain(window.qg, 'createImage', () => { return new window.Image() }) } descriptor = Object.getOwnPropertyDescriptor(window, 'Blob') if (window.Blob && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window, 'Blob', window.Blob) } descriptor = Object.getOwnPropertyDescriptor(window, 'URL') if (window.URL && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window, 'URL', window.URL) } // 兼容 __cccanvas __canvas, cocos低版本会使用到 // OPPO是__canvas,小米是canvas if (!isVivo()) { if (!window.canvas) { // 荣耀平台qg.createCanvas 无法获取gl,需要通过HTMLCanvasElement获取,而且只要调用一次webgl,否则会失败 if (window.HTMLCanvasElement && !window.__canvas && isHonor()) { const canvas = new window.HTMLCanvasElement() if (canvas.getContext && canvas.toDataURL) { window.canvas = canvas } } if (!window.canvas) { window.canvas = window.__canvas ? window.__canvas : window.qg.createCanvas() } } } if (!window.mainCanvas) { window.mainCanvas = window.canvas ? window.canvas : window.qg.createCanvas() } if (!window.__cccanvas) { window.__cccanvas = window.canvas ? window.canvas : window.qg.createCanvas() } if (!window.__canvas) { window.__canvas = window.canvas ? window.canvas : window.qg.createCanvas() } // 针对unity runtime,小米的canvas只能获取2d或者webgl,如果先获取2d会导致webgl获取失败,所以这里先获取webgl,保证后续可以正常渲染上屏 if (window.mainCanvas && window.__miEngineType === 'unity' && isMi()) { window.mainCanvas.getContext('webgl') } descriptor = Object.getOwnPropertyDescriptor(window, 'HTMLCanvasElement') if (window.HTMLCanvasElement && descriptor && descriptor.writable && descriptor.configurable) { const canvas = new window.HTMLCanvasElement() if (canvas.getContext && canvas.toDataURL) { let originCanvas = window.HTMLCanvasElement Object.defineProperty(window, 'HTMLCanvasElement', { enumerable: true, configurable: false, get () { return originCanvas }, set (newValue) { if (newValue !== originCanvas) { console.warn(`rewrite the property: HTMLCanvasElement, read only.`, newValue) return } originCanvas = newValue } }) // 兼容 qg.createCanvas().constructor 会导致荣耀崩溃 if (isHonor()) { descriptor = Object.getOwnPropertyDescriptor(window.qg, 'createCanvas') if (window.qg.createCanvas && descriptor && descriptor.writable && descriptor.configurable) { const originalCreateCanvas = window.qg.createCanvas defineReadOnlyPropertyContain(window.qg, 'createCanvas', (params) => { const canvasObject = originalCreateCanvas.apply(this, params) // Modify the constructor property of the returned object Object.defineProperty(canvasObject, 'constructor', { value: window.HTMLCanvasElement, enumerable: false, writable: true, configurable: true }) return canvasObject }) } } } else { Object.defineProperty(window, 'HTMLCanvasElement', { set: function set (val) { }, get: function get () { return window.__canvas.constructor } }) // 兼容其他平台 getContext只能获取一个,先获取2d会导致webgl获取失败,所以这里先获取webgl,保证后续可以正常渲染上屏 descriptor = Object.getOwnPropertyDescriptor(window.mainCanvas, 'getContext') if (window.mainCanvas.getContext && descriptor && descriptor.writable && descriptor.configurable) { const originalGetContext = window.mainCanvas.getContext defineReadOnlyPropertyContain(window.mainCanvas, 'getContext', (params) => { var contextObject = null try { contextObject = originalGetContext.apply(window.mainCanvas, Array.isArray(params) ? params : [params]) } catch (err) { console.error(err) } if (!contextObject) { try { contextObject = new window.HTMLCanvasElement().getContext(params) } catch (err) { console.error(err) } } return contextObject }) } } } // ##引擎插件适配,联盟暂时不支持引擎插件 try { descriptor = window.requirePlugin && Object.getOwnPropertyDescriptor(window, 'requirePlugin') if (!window.requirePlugin || (descriptor && descriptor.writable && descriptor.configurable)) { const requirePluginOrigin = window.requirePlugin defineReadOnlyPropertyCanDefine(window, 'requirePlugin', (params) => { try { if (requirePluginOrigin && window.qg.getProvider().toLowerCase() === 'vivo') { return requirePluginOrigin.apply(this, params) } } catch (err) { console.error(err) } return window.require(params) }) } } catch (err) { } // ### cocos或laya特殊适配层逻辑转换 ### // 兼容 loadRuntime if (!window.loadRuntime) { window.loadRuntime = () => { return window.qg } } // 兼容 __gl if (window.WebGLRenderingContext && !window.__gl) { window.__gl = window.WebGLRenderingContext.prototype } // 兼容 getCanvasRenderingContext2D if (window.CanvasRenderingContext2D && !window.qg.getCanvasRenderingContext2D) { defineReadOnlyPropertyContain(window.qg, 'getCanvasRenderingContext2D', () => { return window.CanvasRenderingContext2D }) } // 给cocos 游戏使用 兼容适配 window.scriptEngineType = '' if (!window.__getOS) { defineReadOnlyPropertyContain(window, '__getOS', () => 'Android') } if (!window.__getCurrentLanguage) { defineReadOnlyPropertyContain(window, '__getCurrentLanguage', () => 'zh') } if (!window.__getOSVersion) { defineReadOnlyPropertyContain(window, '__getOSVersion', () => 'android') } if (!window.__getPlatform) { defineReadOnlyPropertyContain(window, '__getPlatform', () => 3) // cocos 专用,写死的3 } // 兼容 getUserAgent if (!window.qg.getUserAgent) { defineReadOnlyPropertyContain(window.qg, 'getUserAgent', () => { return 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame ' }) } // 兼容laya引擎获取userAgent判断逻辑修改 if (window.navigator) { window.navigator.userAgent = 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame NetType/WIFI Language/zh_CN' } else { window.navigator = {} window.navigator.userAgent = 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame NetType/WIFI Language/zh_CN' } // 兼容 getCurrentLanguage if (!window.qg.getCurrentLanguage) { defineReadOnlyPropertyContain(window.qg, 'getCurrentLanguage', () => { return 'zh' }) } // 兼容 getOS if (!window.qg.getOS) { defineReadOnlyPropertyContain(window.qg, 'getOS', () => { return 'android' }) } // 兼容jsb接口 const systemInfo = window.qg.getSystemInfoSync() if (!window.jsb) { window.jsb = {} } if (!window.jsb.openURL) { window.jsb.openURL = () => { } } if (!window.jsb.AudioEngine) { window.jsb.AudioEngine = window.qg.createInnerAudioContext() } if (!window.jsb.loadFont) { window.jsb.loadFont = window.qg.loadFont } if (!window.jsb.setPreferredFramesPerSecond) { window.jsb.setPreferredFramesPerSecond = window.qg.setPreferredFramesPerSecond } if (!window.jsb.device) { window.jsb.device = { setMotionInterval: () => { }, setMotionEnabled: () => { }, dispatchDeviceMotionEvent: () => { } } } if (!window.jsb.reflection) { window.jsb.reflection = { callStaticMethod: function () { console.error('不支持jsb.reflection.callStaticMethod这种调用,请尽快删除这种调用') } } } if (!window.jsb.language) { window.jsb.language = systemInfo.language } if (!window.jsb.system) { window.jsb.system = systemInfo.system } if (!window.jsb.platform) { defineReadOnlyPropertyContain(window.jsb, 'platform', 'android') } // 字体相关baseLine适配到web标准,cocos3.6+才支持兼容逻辑 if (!window.qg.getFeature) { defineReadOnlyPropertyContain(window.qg, 'getFeature', (name) => { if (name === 'canvas.context2d.textbaseline.alphabetic' || name === 'canvas.context2d.textbaseline.default') { return 1 } }) } if (!window.qg.setFeature) { defineReadOnlyPropertyContain(window.qg, 'setFeature', () => { }) } // ### API1.0到2.0联盟接口的适配层 ### // ###### Network ###### if (!window.qg.download && window.qg.downloadFile) { defineReadOnlyPropertyContain(window.qg, 'download', window.qg.downloadFile) } // ###### Storage ###### // 兼容 getStorageSync 旧接口 descriptor = Object.getOwnPropertyDescriptor(window.qg, 'getStorageSync') if (window.qg.getStorageSync && descriptor && descriptor.writable && descriptor.configurable) { const getStorageSyncOrigin = window.qg.getStorageSync defineReadOnlyPropertyContain(window.qg, 'getStorageSync', (data) => { if (data && typeof data !== 'string' && data.key) { try { return getStorageSyncOrigin(data.key) } catch (err) { } return '' } return getStorageSyncOrigin(data) }) } // 兼容 deleteStorageSync descriptor = Object.getOwnPropertyDescriptor(window.qg, 'deleteStorageSync') if (window.qg.deleteStorageSync && descriptor && descriptor.writable && descriptor.configurable) { const deleteStorageSyncOrigin = window.qg.deleteStorageSync defineReadOnlyPropertyContain(window.qg, 'deleteStorageSync', (data) => { if (data && typeof data !== 'string' && data.key) { try { return deleteStorageSyncOrigin(data.key) } catch (err) { } return } return deleteStorageSyncOrigin(data) }) } if (window.qg.removeStorageSync && !window.qg.deleteStorageSync && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window.qg, 'deleteStorageSync', (data) => { if (data && typeof data !== 'string' && data.key) { try { return window.qg.removeStorageSync(data.key) } catch (err) { } return } return window.qg.removeStorageSync(data) }) } descriptor = Object.getOwnPropertyDescriptor(window.qg, 'deleteStorage') if (window.qg.removeStorage && !window.qg.deleteStorage && descriptor && descriptor.writable && descriptor.configurable) { defineReadOnlyPropertyContain(window.qg, 'deleteStorage', window.qg.removeStorage) } // 兼容 setStorageSync descriptor = Object.getOwnPropertyDescriptor(window.qg, 'setStorageSync') if (window.qg.setStorageSync && descriptor && descriptor.writable && descriptor.configurable) { const setStorageSyncOrigin = window.qg.setStorageSync defineReadOnlyPropertyContain(window.qg, 'setStorageSync', (...params) => { let data let storageData = {} if (params.length === 1) { data = params[0].value || params[0].data storageData = { key: params[0].key || '', value: data || '' } try { return setStorageSyncOrigin(storageData.key, storageData.value) } catch (err) { } return 'key not define' } else { storageData = { key: params[0] || '', value: params[1] || '' } return setStorageSyncOrigin(storageData.key, storageData.value) } }) } // ###### File ###### var fs = window.qg.getFileSystemManager() var needUserReplace = false if (window.qg.env && window.qg.env.USER_DATA_PATH !== 'internal://files') { console.warn(`qg.env.USER_DATA_PATH is not alliance, ${window.qg.env.USER_DATA_PATH}`) needUserReplace = true } // 兼容 qg 旧的文件管理 if (!window.qg.accessFile) { defineReadOnlyPropertyContain(window.qg, 'accessFile', (data) => { try { fs.accessSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri) return 'true' } catch (err) { } return 'false' }) } if (!window.qg.copyFile) { defineReadOnlyPropertyContain(window.qg, 'copyFile', (data) => { if (data.srcUri) { data.srcPath = data.srcUri } data.srcPath = needUserReplace ? data.srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.srcPath if (data.dstUri) { data.destPath = data.dstUri } data.destPath = needUserReplace ? data.destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.destPath return fs.copyFile(data) }) } if (!window.qg.moveFile) { defineReadOnlyPropertyContain(window.qg, 'moveFile', (data) => { if (data.srcUri) { data.oldPath = data.srcUri } data.oldPath = needUserReplace ? data.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.oldPath if (data.dstUri) { data.newPath = data.dstUri } data.newPath = needUserReplace ? data.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.newPath return fs.rename(data) }) } if (!window.qg.getFileInfo) { defineReadOnlyPropertyContain(window.qg, 'getFileInfo', (data) => { if (data.uri) { data.filePath = data.uri } data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath const suc = data.success data.success = (res) => { res.uri = data.filePath res.length = res.size res.lastModifiedTime = 0 if (suc && typeof suc === 'function') { suc(res) } } return fs.getFileInfo(data) }) } if (!window.qg.listDir) { defineReadOnlyPropertyContain(window.qg, 'listDir', (data) => { if (data.uri) { data.path = data.uri } data.path = needUserReplace ? data.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.path data.recursive = true const suc = data.success data.success = (res) => { var files = [] for (var i = 0; i < res.length; i++) { const file = { uri: res[i].path, length: res[i].stats.size, lastModifiedTime: res[i].stats.lastModifiedTime } files.push(file) } if (suc && typeof suc === 'function') { suc({ fileList: files }) } } return fs.stat(data) }) } if (!window.qg.deleteFile) { defineReadOnlyPropertyContain(window.qg, 'deleteFile', (data) => { if (data.uri) { data.filePath = data.uri } data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath return fs.unlink(data) }) } if (!window.qg.readFile) { defineReadOnlyPropertyContain(window.qg, 'readFile', (data) => { if (data.uri) { data.filePath = data.uri } data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath const suc = data.success data.success = (res) => { res.text = res.data if (suc && typeof suc === 'function') { suc(res) } } return fs.readFile(data) }) } if (!window.qg.readFileSync) { defineReadOnlyPropertyContain(window.qg, 'readFileSync', (data) => { try { let res const filePath = needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri if (data.encoding && data.position && data.length) { res = fs.readFileSync(filePath, data.encoding, data.position, data.length) } else if (data.encoding && data.position) { res = fs.readFileSync(filePath, data.encoding, data.position) } else if (data.encoding) { res = fs.readFileSync(filePath, data.encoding) } else { res = fs.readFileSync(filePath) } if (res) { return { text: res } } } catch (err) { console.error(err) } return 'no such file or directory' }) } if (!window.qg.writeFile) { defineReadOnlyPropertyContain(window.qg, 'writeFile', (data) => { if (data.uri) { data.filePath = data.uri } data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath if (data.text) { data.data = data.text } return fs.writeFile(data) }) } if (!window.qg.writeFileSync) { defineReadOnlyPropertyContain(window.qg, 'writeFileSync', (data) => { try { const filePath = needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri if (data.text && data.encoding) { return fs.writeFileSync(filePath, data.text, data.encoding) } else { return fs.writeFileSync(filePath, data.text) } } catch (err) { console.error(err) } return 'no such file or directory' }) } if (!window.qg.appendFile) { defineReadOnlyPropertyContain(window.qg, 'appendFile', (data) => { if (data.uri) { data.filePath = data.uri } data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath if (data.text) { data.data = data.text } return fs.appendFile(data) }) } if (!window.qg.mkdir) { defineReadOnlyPropertyContain(window.qg, 'mkdir', (data) => { if (data.uri) { data.dirPath = data.uri + '/' data.recursive = true } data.dirPath = needUserReplace ? data.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.dirPath return fs.mkdir(data) }) } if (!window.qg.rmdir) { defineReadOnlyPropertyContain(window.qg, 'rmdir', (data) => { if (data.uri) { data.dirPath = data.uri } data.dirPath = needUserReplace ? data.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.dirPath return fs.rmdir(data) }) } if (!window.qg.isDirectory) { defineReadOnlyPropertyContain(window.qg, 'isDirectory', (data) => { try { const res = fs.statSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri, false) return res.isDirectory() } catch (err) { } return false }) } if (!window.qg.isFile) { defineReadOnlyPropertyContain(window.qg, 'isFile', (data) => { try { const res = fs.statSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri, false) return res.isFile() } catch (err) { } return false }) } if (!window.qg.renameFile) { defineReadOnlyPropertyContain(window.qg, 'renameFile', (data) => { if (data.srcUri) { data.oldPath = data.srcUri } data.oldPath = needUserReplace ? data.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.oldPath if (data.dstUri) { data.newPath = data.dstUri } data.newPath = needUserReplace ? data.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.newPath return fs.rename(data) }) } if (!window.qg.unzipFile) { defineReadOnlyPropertyContain(window.qg, 'unzipFile', (data) => { if (data.srcUri) { data.zipFilePath = data.srcUri } data.zipFilePath = needUserReplace ? data.zipFilePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.zipFilePath if (data.dstUri) { data.targetPath = data.dstUri } data.targetPath = needUserReplace ? data.targetPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.targetPath return fs.unzip(data) }) } // 兼容 FileSystemManager const readInject = fs.readFileSync defineReadOnlyPropertyCanDefine(fs, 'readFileSync', (...params) => { if (params.length === 1 && (params[0].uri || params[0].filePath)) { try { let filePath = params[0].uri || params[0].filePath filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath if (params[0].encoding && params[0].position && params[0].length) { const result = readInject.bind(fs)(filePath, params[0].encoding, params[0].position, params[0].length) return result } else if (params[0].encoding && params[0].position) { const result = readInject.bind(fs)(filePath, params[0].encoding, params[0].position) return result } else if (params[0].encoding) { const result = readInject.bind(fs)(filePath, params[0].encoding) return result } else { const result = readInject.bind(fs)(filePath) return result } } catch (err) { console.error(err) throw (err) } } params[0] = needUserReplace ? params[0].replace('internal://files', window.qg.env.USER_DATA_PATH) : params[0] return readInject.bind(fs)(...params) }) const unlinkInject = fs.unlink defineReadOnlyPropertyCanDefine(fs, 'unlink', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath return unlinkInject.bind(fs)(params) }) const copyFileInject = fs.copyFile defineReadOnlyPropertyCanDefine(fs, 'copyFile', (params) => { params.srcPath = needUserReplace ? params.srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.srcPath params.destPath = needUserReplace ? params.destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.destPath return copyFileInject.bind(fs)(params) }) const writeFileInject = fs.writeFile defineReadOnlyPropertyCanDefine(fs, 'writeFile', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath if (ArrayBuffer.isView(params.data) || params.data instanceof ArrayBuffer) { params.encoding = 'binary' } return writeFileInject.bind(fs)(params) }) const writeFileSyncInject = fs.writeFileSync defineReadOnlyPropertyCanDefine(fs, 'writeFileSync', (path, data, encoding) => { path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) { return writeFileSyncInject.bind(fs)(path, data, 'binary') } return writeFileSyncInject.bind(fs)(path, data, encoding) }) const readFileInject = fs.readFile defineReadOnlyPropertyCanDefine(fs, 'readFile', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath // @note: OPPO 底层在路径不存在时未回调 fail,在此兼容一下,其他渠道保持不变 if (isOPPO()) { try { fs.accessSync(params.filePath) return readFileInject.bind(fs)(params) } catch (err) { if (params.fail && typeof params.fail === 'function') { params.fail(err) } if (params.complete && typeof params.complete === 'function') { params.complete(err) } } } else { return readFileInject.bind(fs)(params) } }) const readdirInject = fs.readdir defineReadOnlyPropertyCanDefine(fs, 'readdir', (params) => { params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath return readdirInject.bind(fs)(params) }) const mkdirSyncInject = fs.mkdirSync defineReadOnlyPropertyCanDefine(fs, 'mkdirSync', (path, recursive) => { path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path return mkdirSyncInject.bind(fs)(path, recursive) }) const rmdirSyncInject = fs.rmdirSync defineReadOnlyPropertyCanDefine(fs, 'rmdirSync', (dirPath, recursive) => { dirPath = needUserReplace ? dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : dirPath return rmdirSyncInject.bind(fs)(dirPath, recursive) }) const accessInject = fs.access defineReadOnlyPropertyCanDefine(fs, 'access', (params) => { params.path = needUserReplace ? params.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.path return accessInject.bind(fs)(params) }) const accessSyncInject = fs.accessSync defineReadOnlyPropertyCanDefine(fs, 'accessSync', (filePath) => { filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath return accessSyncInject.bind(fs)(filePath) }) const unzipInject = fs.unzip defineReadOnlyPropertyCanDefine(fs, 'unzip', (params) => { params.zipFilePath = needUserReplace ? params.zipFilePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.zipFilePath params.targetPath = needUserReplace ? params.targetPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.targetPath return unzipInject.bind(fs)(params) }) if (isHonor()) { const saveFileInject = fs.saveFile defineReadOnlyPropertyCanDefine(fs, 'saveFile', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath return saveFileInject.bind(fs)(params) }) const saveFileSyncInject = fs.saveFileSync defineReadOnlyPropertyCanDefine(fs, 'saveFileSync', (tempFilePath, filePath) => { filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath return saveFileSyncInject.bind(fs)(tempFilePath, filePath) }) const mkdirInject = fs.mkdir defineReadOnlyPropertyCanDefine(fs, 'mkdir', (params) => { params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath return mkdirInject.bind(fs)(params) }) const readdirSyncInject = fs.readdirSync defineReadOnlyPropertyCanDefine(fs, 'readdirSync', (dirPath) => { dirPath = needUserReplace ? dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : dirPath return readdirSyncInject.bind(fs)(dirPath) }) const copyFileSyncInject = fs.copyFileSync defineReadOnlyPropertyCanDefine(fs, 'copyFileSync', (srcPath, destPath) => { srcPath = needUserReplace ? srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : srcPath destPath = needUserReplace ? destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : destPath return copyFileSyncInject.bind(fs)(srcPath, destPath) }) const getFileInfoInject = fs.getFileInfo defineReadOnlyPropertyCanDefine(fs, 'getFileInfo', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath return getFileInfoInject.bind(fs)(params) }) const rmdirInject = fs.rmdir defineReadOnlyPropertyCanDefine(fs, 'rmdir', (params) => { params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath return rmdirInject.bind(fs)(params) }) const unlinkSyncInject = fs.unlinkSync defineReadOnlyPropertyCanDefine(fs, 'unlinkSync', (filePath) => { filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath return unlinkSyncInject.bind(fs)(filePath) }) const appendFileInject = fs.appendFile defineReadOnlyPropertyCanDefine(fs, 'appendFile', (params) => { params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath return appendFileInject.bind(fs)(params) }) const appendFileSyncInject = fs.appendFileSync defineReadOnlyPropertyCanDefine(fs, 'appendFileSync', (filePath, data, encoding) => { filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath return appendFileSyncInject.bind(fs)(filePath, data, encoding) }) const renameInject = fs.rename defineReadOnlyPropertyCanDefine(fs, 'rename', (params) => { params.newPath = needUserReplace ? params.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.newPath params.oldPath = needUserReplace ? params.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.oldPath return renameInject.bind(fs)(params) }) const renameSyncInject = fs.renameSync defineReadOnlyPropertyCanDefine(fs, 'renameSync', (oldPath, newPath) => { oldPath = needUserReplace ? oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : oldPath newPath = needUserReplace ? newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : newPath return renameSyncInject.bind(fs)(oldPath, newPath) }) const statInject = fs.stat defineReadOnlyPropertyCanDefine(fs, 'stat', (params) => { params.path = needUserReplace ? params.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.path return statInject.bind(fs)(params) }) const statSyncInject = fs.statSync defineReadOnlyPropertyCanDefine(fs, 'statSync', (path, recursive) => { path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path return statSyncInject.bind(fs)(path, recursive) }) } // 兼容 subscribeNetworkStatus if (window.qg.onNetworkStatusChange && !window.qg.subscribeNetworkStatus) { defineReadOnlyPropertyContain(window.qg, 'subscribeNetworkStatus', (params = {}) => { const suc = params.success params.success = (data) => { data.type = data.networkType if (suc && typeof suc === 'function') { suc(data) } } return window.qg.onNetworkStatusChange(params.success) }) } // 兼容 loadFont descriptor = Object.getOwnPropertyDescriptor(window.qg, 'loadFont') if (window.qg.loadFont && descriptor && descriptor.writable && descriptor.configurable) { const loadFontOrigin = window.qg.loadFont defineReadOnlyPropertyContain(window.qg, 'loadFont', (...params) => { if (params.length === 1) { return loadFontOrigin(params[0]) } else { if (params[1].startsWith('url(')) { return loadFontOrigin(params[1].slice(5, -2)) } return loadFontOrigin(params[1]) } }) } // 兼容 getDeviceRotation if (!window.qg.getDeviceRotation) { defineReadOnlyPropertyContain(window.qg, 'getDeviceRotation', () => { const data = window.qg.getSystemInfoSync() if (data.deviceOrientation === 'portrait') { return { rotation: 0 } } else { return { rotation: 1 } } }) } // 兼容 subscribeAccelerometer unsubscribeAccelerometer if (window.qg.startAccelerometer && !window.qg.subscribeAccelerometer) { defineReadOnlyPropertyContain(window.qg, 'subscribeAccelerometer', window.qg.startAccelerometer) } if (window.qg.stopAccelerometer && !window.qg.unsubscribeAccelerometer) { defineReadOnlyPropertyContain(window.qg, 'unsubscribeAccelerometer', window.qg.stopAccelerometer) } // #region 网络适配 // @note: 暂时只针对 OPPO,其他渠道按需使用 if (!isVivo()) { const xhrOpenProxy = window.XMLHttpRequest.prototype.open window.XMLHttpRequest.prototype.open = function (method, url, async, user, password) { this.url = url return xhrOpenProxy.apply(this, arguments) } } // #endregion // ### 其他接口适配 ### // touch var _jsb = window.jsb if (!_jsb) { _jsb = {} } var _touches = [] var _getTouchIndex = function _getTouchIndex (touch) { var element for (var index = 0; index < _touches.length; index++) { element = _touches[index] if (touch.identifier === element.identifier) { return index } } return -1 } var _copyObject = function _copyObject (fromObj, toObject) { for (var key in fromObj) { if (fromObj.hasOwnProperty (key)) { toObject[key] = fromObj[key] } } } var _listenerMap = {} _listenerMap.touchstart = [] _listenerMap.touchmove = [] _listenerMap.touchend = [] _listenerMap.touchcancel = [] function _addListener (key, value) { var listenerArr = _listenerMap[key] for (var index = 0, length = listenerArr.length; index < length; index++) { if (value === listenerArr[index]) { return } } listenerArr.push(value) } function _removeListener (key, value) { var listenerArr = _listenerMap[key] || [] var length = listenerArr.length for (var index = 0; index < length; ++index) { if (value === listenerArr[index]) { listenerArr.splice(index, 1) return } } } var _hasDellWith = false var _systemInfo = window.qg.getSystemInfoSync() if (window.innerWidth && _systemInfo.windowWidth !== window.innerWidth) { _hasDellWith = true } var _touchEventHandlerFactory = function _touchEventHandlerFactory (type) { return function (changedTouches) { if (typeof changedTouches === 'function') { _addListener(type, changedTouches) return } var touchEvent = new window.TouchEvent(type) var index if (type === 'touchstart') { changedTouches.forEach(function (touch) { index = _getTouchIndex(touch) if (index >= 0) { _copyObject(touch, _touches[index]) } else { var tmp = {} _copyObject(touch, tmp) _touches.push(tmp) } }) } else if (type === 'touchmove') { changedTouches.forEach(function (element) { index = _getTouchIndex(element) if (index >= 0) { _copyObject(element, _touches[index]) } }) } else if (type === 'touchend' || type === 'touchcancel') { changedTouches.forEach(function (element) { index = _getTouchIndex(element) if (index >= 0) { _touches.splice(index, 1) } }) } var touches = [].concat(_touches) var _changedTouches = [] changedTouches.forEach(function (touch) { var length = touches.length for (var _index = 0; _index < length; ++_index) { var _touch = touches[_index] if (touch.identifier === _touch.identifier) { _changedTouches.push(_touch) return } } _changedTouches.push(touch) }) touchEvent.touches = touches touchEvent.targetTouches = touches touchEvent.changedTouches = _changedTouches if (_hasDellWith) { touches.forEach(function (touch) { touch.clientX /= window.devicePixelRatio touch.clientY /= window.devicePixelRatio touch.pageX /= window.devicePixelRatio touch.pageY /= window.devicePixelRatio }) if (type === 'touchcancel' || type === 'touchend') { _changedTouches.forEach(function (touch) { touch.clientX /= window.devicePixelRatio touch.clientY /= window.devicePixelRatio touch.pageX /= window.devicePixelRatio touch.pageY /= window.devicePixelRatio }) } } var listenerArr = _listenerMap[type] var length = listenerArr.length for (var _index2 = 0; _index2 < length; _index2++) { listenerArr[_index2](touchEvent) } } } if (!window.qg.onTouchStart) { _jsb.onTouchStart = _touchEventHandlerFactory('touchstart') _jsb.offTouchStart = function (callback) { _removeListener('touchstart', callback) } window.qg.onTouchStart = _jsb.onTouchStart.bind(_jsb) window.qg.offTouchStart = _jsb.offTouchStart.bind(_jsb) } if (!window.qg.onTouchMove) { _jsb.onTouchMove = _touchEventHandlerFactory('touchmove') _jsb.offTouchMove = function (callback) { _removeListener('touchmove', callback) } window.qg.onTouchMove = _jsb.onTouchMove.bind(_jsb) window.qg.offTouchMove = _jsb.offTouchMove.bind(_jsb) } if (!window.qg.onTouchCancel) { _jsb.onTouchCancel = _touchEventHandlerFactory('touchcancel') _jsb.offTouchCancel = function (callback) { _removeListener('touchcancel', callback) } window.qg.onTouchCancel = _jsb.onTouchCancel.bind(_jsb) window.qg.offTouchCancel = _jsb.offTouchCancel.bind(_jsb) } if (!window.qg.onTouchEnd) { _jsb.onTouchEnd = _touchEventHandlerFactory('touchend') _jsb.offTouchEnd = function (callback) { _removeListener('touchend', callback) } window.qg.onTouchEnd = _jsb.onTouchEnd.bind(_jsb) window.qg.offTouchEnd = _jsb.offTouchEnd.bind(_jsb) } // 小米适配===================== if (window.TouchEvent) { defineReadOnlyPropertyContain(window, 'TouchEvent', window.TouchEvent) } if (window.Event) { defineReadOnlyPropertyContain(window, 'Event', window.Event) } if (window.mainCanvas) { defineReadOnlyPropertyContain(window, 'mainCanvas', window.mainCanvas) } // #region 游戏引擎特殊逻辑 if (isOPPO()) { // 引擎底层针对 unity 游戏有特殊处理 try { const webglJSON = JSON.parse(fs.readFileSync('./buildUnity/webgl.json', 'utf8')) if (webglJSON.unityVersion) { window.qg.setIsUnityGame(true) } if (window.crypto && !window.crypto.getRandomValues) { window.crypto = null } } catch (ignore) { } } // #endregion })()