"use strict"; /** * @en Registration method for the main process of Extension * @zh 为扩展的主进程的注册方法 */ /** * // 打开panel * Editor.Panel.open(`${插件名}.${panel名}`); * // 调用普通事件 * Editor.Message.request(插件名, 消息名, ...args); * // 调用场景方法 * Editor.Message.request('scene', 'execute-scene-script', { * //插件名 * name: string, * //方法名 * method: string, * //参数列表 * args: any[] * }); * */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.unload = exports.load = exports.methods = void 0; // path.join不能正确处理'db://'结构,会把'//'变成'/' const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const utils_1 = require("./utils"); const electron = require('electron'); const adminFolderName = 'app-admin'; const controllerFolderName = 'app-controller'; const managerFolderName = 'app-manager'; const modelFolderName = 'app-model'; const soundFolderName = 'app-sound'; const viewFolderName = 'app-view'; const builtinFolderName = 'app-builtin'; const bundleFolderName = 'app-bundle'; const pkgFolderUrl = 'db://pkg/'; const pkgFolderPath = utils_1.convertUrlToPath(pkgFolderUrl); const builtinFolderUrl = 'db://assets/' + builtinFolderName; const builtinFolderPath = utils_1.convertUrlToPath(builtinFolderUrl); const bundleFolderUrl = 'db://assets/' + bundleFolderName; const bundleFolderPath = utils_1.convertUrlToPath(bundleFolderUrl); const adminFolderUrl = builtinFolderUrl + '/' + adminFolderName; const adminFolderPath = builtinFolderPath + '/' + adminFolderName; const controllerFolderUrl = builtinFolderUrl + '/' + controllerFolderName; const controllerFolderPath = builtinFolderPath + '/' + controllerFolderName; const managerFolderUrl = builtinFolderUrl + '/' + managerFolderName; const managerFolderPath = builtinFolderPath + '/' + managerFolderName; const modelFolderUrl = builtinFolderUrl + '/' + modelFolderName; const modelFolderPath = builtinFolderPath + '/' + modelFolderName; const soundFolderUrl = bundleFolderUrl + '/' + soundFolderName; const soundFolderPath = bundleFolderPath + '/' + soundFolderName; const viewFolderUrl = bundleFolderUrl + '/' + viewFolderName; const viewFolderPath = bundleFolderPath + '/' + viewFolderName; const executorFileUrl = adminFolderUrl + '/executor.ts'; const executorFilePath = adminFolderPath + '/executor.ts'; function isExecutor(info, strict = true) { if (!strict) { if (info.path.endsWith('Controller') && info.type === 'cc.Script') return true; if (info.path.endsWith('Manager') && (info.type === 'cc.Script' || info.type === 'cc.Prefab')) return true; if ((info.name.startsWith('data.') || info.name.startsWith('config.') || info.name.startsWith('store.')) && info.type === 'cc.Script') return true; if ((info.name.startsWith('Page') || info.name.startsWith('Paper') || info.name.startsWith('Pop') || info.name.startsWith('Top')) && (info.type === 'cc.Script' || info.type === 'cc.Prefab' || info.type === 'cc.Scene' || info.type === 'cc.SceneAsset')) return true; if (info.type === 'cc.AudioClip') return true; return false; } if (info.path === builtinFolderUrl) return true; if (info.path === bundleFolderUrl) return true; if (info.path === managerFolderUrl) return true; if (info.path === controllerFolderUrl) return true; if (info.path === modelFolderUrl) return true; if (info.path === soundFolderUrl) return true; if (info.path === viewFolderUrl) return true; if (info.path.startsWith(controllerFolderUrl)) { return info.path.endsWith('Controller') && info.type === 'cc.Script'; } if (info.path.startsWith(managerFolderUrl)) { return info.path.endsWith('Manager') && (info.type === 'cc.Script' || info.type === 'cc.Prefab'); } if (info.path.startsWith(modelFolderUrl)) { return (info.name.startsWith('data.') || info.name.startsWith('config.') || info.name.startsWith('store.')) && info.type === 'cc.Script'; } if (info.path.startsWith(viewFolderUrl)) { return (info.name.startsWith('Page') || info.name.startsWith('Paper') || info.name.startsWith('Pop') || info.name.startsWith('Top')) && (info.type === 'cc.Script' || info.type === 'cc.Prefab' || info.type === 'cc.Scene' || info.type === 'cc.SceneAsset'); } if (info.path.startsWith(soundFolderUrl)) { return info.type === 'cc.AudioClip'; } } function compareStr(str1, str2) { if (str1 === str2) { return 0; } const len = Math.max(str1.length, str2.length); for (let i = 0, code1 = 0, code2 = 0; i < len; i++) { if (str1.length <= i) { return -1; } else if (str2.length <= i) { return 1; } else { code1 = str1.charCodeAt(i); code2 = str2.charCodeAt(i); if (code1 > code2) { return 1; } else if (code1 < code2) { return -1; } } } return 0; } const viewSelect = ['Page', 'Paper', 'Pop', 'Top']; const viewRegExp = RegExp(`^(${viewSelect.join('|')})`); function readFileSyncByPath(url) { const filepath = utils_1.convertUrlToPath(url); return fs_1.existsSync(filepath) ? fs_1.readFileSync(filepath, 'utf8') : ''; } function isTSDefault(value) { // const varname = value[0]; const filename = value[1]; const dirname = value[2]; const extname = value[3]; if (extname.endsWith('js')) { return false; } const filepath = path_1.default.join(utils_1.convertUrlToPath(dirname), filename + '.ts'); const js = fs_1.readFileSync(filepath, 'utf8'); return js.search(/export\s+default/) >= 0; } const keyWords = [ 'lib', 'manager', 'Manager', 'controller', 'Controller', 'data', 'config', 'store', 'IViewName', 'IViewNames', 'IMiniViewName', 'IMiniViewNames', 'IMusicName', 'IMusicNames', 'IEffectName', 'IEffectNames', 'ViewName', 'MiniViewName', 'MusicName', 'EffectName' ]; async function clearExecutor() { if (!fs_1.existsSync(executorFilePath)) return; let result = '/* eslint-disable */\n' + 'import { Component } from \'cc\';\n' + 'import { app } from \'../../app/app\';\n' + 'import { EDITOR,EDITOR_NOT_IN_PREVIEW } from \'cc/env\';\n\n'; result += 'export type IReadOnly = { readonly [P in keyof T]: T[P] extends Function ? T[P] : (T[P] extends Object ? IReadOnly : T[P]); };\n\n'; result += 'export type IViewName = "never"\n'; result += 'export type IViewNames = IViewName[]\n'; result += 'export type IMiniViewName = "never"\n'; result += 'export type IMiniViewNames = IMiniViewName[]\n'; result += 'export type IMusicName = "never"\n'; result += 'export type IMusicNames = IMusicName[]\n'; result += 'export type IEffectName = "never"\n'; result += 'export type IEffectNames = IEffectName[]\n\n'; result += 'export type IApp = {\n'; result += ' Controller: {},\n'; result += ' controller: {},\n'; result += ' Manager: {},\n'; result += ' manager: {},\n'; result += ' data: {},\n'; result += ' config: {}\n'; result += ' store: {}\n'; result += '}\n'; // config result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.config, {})\n'; // data result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.data, {})\n'; // store result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.store, {})\n\n'; // controller result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.Controller, {})\n'; result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.controller, {})\n\n'; // 修正windows系统中的\为/ result = result.replace(/\\/g, '/'); // save if (readFileSyncByPath(executorFileUrl) !== result) { await Editor.Message.request('asset-db', 'create-asset', executorFileUrl, result, { overwrite: true }); } } async function updateExecutor() { // app-builtin文件夹不存在, 创建 if (!fs_1.existsSync(builtinFolderPath)) await utils_1.createFolderByUrl(builtinFolderUrl, { readme: utils_1.getResReadme(builtinFolderName) }); // app-admin文件夹不存在, 创建 if (!fs_1.existsSync(adminFolderPath)) await utils_1.createFolderByUrl(adminFolderUrl, { meta: utils_1.getResMeta(adminFolderName), readme: utils_1.getResReadme(adminFolderName) }); const mgrList = []; const ctrList = []; const dataList = []; const confList = []; const storeList = []; const viewScene = {}; const miniViewKeys = {}; const musicKeys = {}; const effectKeys = {}; // app-controller app-manager app-model const result1 = await Editor.Message.request('asset-db', 'query-assets', { pattern: builtinFolderUrl + '/{app-controller,app-manager/*,app-model}/*.ts' }) .then(res => { return res.sort((a, b) => compareStr(a.name, b.name)); }) .catch(() => []); // app-sound const result2 = await Editor.Message.request('asset-db', 'query-assets', { pattern: soundFolderUrl + '/{music,effect}/**/*.*' }) .then(res => { return res.sort((a, b) => compareStr(a.name, b.name)); }) .catch(() => []); // app-view const result3 = await Editor.Message.request('asset-db', 'query-assets', { pattern: viewFolderUrl + '/{page,pop,top,paper/*}/*/native/*.{prefab,scene}' }) .then(res => { return res.sort((a, b) => compareStr(a.name, b.name)); }) .catch(() => []); // manager const result4 = await Editor.Message.request('asset-db', 'query-assets', { pattern: 'db://app/manager/**/*.ts' }) .then(res => { return res.sort((a, b) => compareStr(a.name, b.name)); }) .catch(() => []); // 集合 const results = result1.slice().concat(result2).concat(result3).concat(result4); for (let index = 0; index < results.length; index++) { const result = results[index]; const fileUrl = result.url; // 文件名.扩展名 const basename = path_1.default.basename(result.url || '') || ''; // 扩展名 const extname = path_1.default.extname(result.url || '') || ''; // 文件名 const filename = basename.slice(0, -extname.length); // 文件目录名 const dirname = path_1.default.dirname(result.url || '') || ''; if (!basename) continue; if (!extname) continue; if (!filename) continue; if (!dirname) continue; if (extname === '.ts') { // 变量名 const varname = filename.replace(/[.-]/g, '_'); if (keyWords.indexOf(varname) >= 0) { console.log(`[跳过此文件] [${filename}] 原因: ${varname}与关键字中(${JSON.stringify(keyWords)})的一个重复`); } else if (fileUrl.startsWith(controllerFolderUrl)) { // 用户controller if (filename.endsWith('Controller')) { ctrList.push([varname, filename, dirname, extname]); } } else if (fileUrl.startsWith(managerFolderUrl)) { // 用户manager if (filename.endsWith('Manager') && dirname.endsWith(utils_1.stringCaseNegate(filename.slice(0, -7)))) { mgrList.push([varname, filename, dirname, extname]); } } else if (fileUrl.startsWith('db://app/manager/')) { // 系统manager(系统Mgr的文件夹命名为了美观没有那么规范,所以和用户Mgr的逻辑有区别) if (filename.endsWith('Manager') && dirname.endsWith(filename.slice(0, -7).toLowerCase())) { mgrList.push([varname, filename, dirname, extname]); } } else if (fileUrl.startsWith(modelFolderUrl)) { // model if (filename.startsWith('data.')) { dataList.push([varname, filename, dirname, extname]); } else if (filename.startsWith('config.')) { confList.push([varname, filename, dirname, extname]); } else if (filename.startsWith('store.')) { storeList.push([varname, filename, dirname, extname]); } } } else if (extname === '.prefab' || extname === '.scene') { if (fileUrl.startsWith(viewFolderUrl) && viewRegExp.test(filename)) { const dirArray = dirname.split('/'); const index = dirArray.indexOf(viewFolderName); const viewDirArray = dirArray.slice(index + 1); if (['page', 'paper', 'pop', 'top'].indexOf(viewDirArray[0].toLowerCase()) >= 0) { // 主界面 if (filename === `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`) { viewScene[filename] = extname === '.scene'; } // 子界面 else if (filename === `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}`) { miniViewKeys[filename] = `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`; } } else { // 主界面 if (filename === `${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}`) { viewScene[filename] = extname === '.scene'; } // 子界面 else if (filename === `${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}${utils_1.stringCase(viewDirArray[3], false)}`) { miniViewKeys[filename] = `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`; } } } } else if (fileUrl.startsWith(soundFolderUrl)) { const dir = path_1.default.join(dirname.split(soundFolderName + '/').pop(), filename); if (dir.startsWith('music')) { // musicKeys musicKeys[dir] = dir; } else { // effectKeys effectKeys[dir] = dir; } } } // const pkgNames: string[] = []; // if (existsSync(pkgFolderPath)) { // readdirSync(pkgFolderPath).forEach(function (item) { // const item_path = path.join(pkgFolderPath, item); // const item_stat = statSync(item_path); // if (!item_stat.isDirectory()) return; // const item_name = path.basename(item_path); // if (item_name.startsWith('@')) { // readdirSync(item_path).forEach(function (sub) { // const sub_path = path.join(item_path, sub); // const sub_stat = statSync(sub_path); // if (!sub_stat.isDirectory()) return; // const sub_name = path.basename(sub_path); // pkgNames.push(item_name + '/' + sub_name); // }); // } else { // pkgNames.push(item_name); // } // }); // } let result = '/* eslint-disable */\n' + 'import { Component,director,Director } from \'cc\';\n' + 'import { app } from \'../../app/app\';\n' + 'import { EDITOR,EDITOR_NOT_IN_PREVIEW } from \'cc/env\';\n\n'; result += 'export type IReadOnly = { readonly [P in keyof T]: T[P] extends Function ? T[P] : (T[P] extends Object ? IReadOnly : T[P]); };\n\n'; result += `export type IViewName = ${Object.keys(viewScene).map(str => `"${str}"`).join('|') || '"never"'}\n`; result += 'export type IViewNames = IViewName[]\n'; result += `export type IMiniViewName = ${Object.keys(miniViewKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`; result += 'export type IMiniViewNames = IMiniViewName[]\n'; result += `export type IMusicName = ${Object.keys(musicKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`; result += 'export type IMusicNames = IMusicName[]\n'; result += `export type IEffectName = ${Object.keys(effectKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`; result += 'export type IEffectNames = IEffectName[]\n\n'; // pkgNames.forEach(name => result += `import 'db://pkg/${name}'\n`); const writeImport = function writeImport(arr, module) { return arr.forEach(function (value) { const varname = value[0]; const filename = value[1]; const dirname = value[2]; if (isTSDefault(value)) { result += `import ${varname} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`; } else if (module) { result += `import {${varname}} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`; } else { result += `import * as ${varname} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`; } }); }; writeImport(confList, false); writeImport(dataList, false); writeImport(storeList, false); writeImport(ctrList, true); writeImport(mgrList, true); // controller let ctrStr = ''; let CtrStr = ''; ctrList.forEach(function ([varname], index, array) { CtrStr += `${varname.slice(0, -10)}:typeof ${varname}`; ctrStr += `${varname.slice(0, -10).toLowerCase()}:IReadOnly<${varname}>`; if (index < array.length - 1) { CtrStr += ','; ctrStr += ','; } }); // manager let mgrStr = ''; let MgrStr = ''; mgrList.forEach(function ([varname], index, array) { MgrStr += `${varname.slice(0, -7)}:Omit`; if (varname === 'UIManager') { mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname},keyof Component>`; } else if (varname === 'SoundManager') { mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname},keyof Component>`; } else { mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname},keyof Component>`; } if (index < array.length - 1) { MgrStr += ','; mgrStr += ','; } }); result += 'export type IApp = {\n'; result += ` Controller: {${CtrStr}},\n`; result += ` controller: {${ctrStr}},\n`; result += ` Manager: {${MgrStr}},\n`; result += ` manager: {${mgrStr}},\n`; result += ` data: {${dataList.map(([varname]) => `${varname.slice(5)}:${varname}`).join(',')}},\n`; result += ` config: {${confList.map(([varname]) => `${varname.slice(7)}:IReadOnly<${varname}>`).join(',')}}\n`; result += ` store: {${storeList.map(([varname]) => `${varname.slice(6)}:IReadOnly<${varname}>`).join(',')}}\n`; result += '}\n\n'; result += 'function init(){\n'; // config result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.config, {${confList.map(([varname]) => `${varname.slice(7)}:new ${varname}()`).join(',')}})\n`; // data result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.data, {${dataList.map(([varname]) => `${varname.slice(5)}:new ${varname}()`).join(',')}})\n`; // store result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.store, {${storeList.map(([varname]) => `${varname.slice(6)}:new ${varname}()`).join(',')}})\n\n`; // controller result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.Controller, {${ctrList.map(([varname]) => `${varname.slice(0, -10)}:${varname}`).join(',')}})\n`; result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.controller, {${ctrList.map(([varname]) => `${varname.slice(0, -10).toLowerCase()}:new ${varname}()`).join(',')}})\n`; result += '}\n'; result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) director.on(Director.EVENT_RESET,init)\n'; result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) init()\n'; // 修正windows系统中的\为/ result = result.replace(/\\/g, '/'); // save if (readFileSyncByPath(executorFileUrl) !== result) { await Editor.Message.request('asset-db', 'create-asset', executorFileUrl, result, { overwrite: true }); } } let timer = null; function callUpdateExecutor(clear = false) { if (timer) return; if (clear) { clearExecutor(); callUpdateExecutor(false); } else { timer = setTimeout(() => { updateExecutor().finally(() => { timer = null; }); }, 500); } } // 获得Creator主窗口 function getMainWebContents() { const windows = electron.BrowserWindow.getAllWindows(); for (let i = 0; i < windows.length; i++) { const win = windows[i]; if (win.webContents.getURL().includes('windows/main.html') || (win.title && win.title.includes('Cocos Creator'))) { return win.webContents; } } return; } function updateMark() { const webContents = getMainWebContents(); if (webContents) { const hackCode = fs_1.readFileSync(path_1.default.join(__dirname, '../res/mark.js'), 'utf-8'); webContents.executeJavaScript(hackCode); } } exports.methods = { ['open-panel']() { Editor.Panel.open('app.open-panel'); }, ['open-wiki']() { const url = 'https://gitee.com/cocos2d-zp/xforge/wikis/pages'; Editor.Message.send('program', 'open-url', url); }, ['open-issues']() { const url = 'https://gitee.com/cocos2d-zp/xforge/issues'; Editor.Message.send('program', 'open-url', url); }, ['open-github']() { const url = 'https://github.com/a1076559139/XForge'; Editor.Message.send('program', 'open-url', url); }, ['open-store']() { const url = 'https://store.cocos.com/app/search?name=xforge'; Editor.Message.send('program', 'open-url', url); }, ['refresh-executor']() { // 点击更新 callUpdateExecutor(); console.log('[executor.ts] 刷新成功'); }, ['scene:ready']() { // }, ['asset-db:ready']() { updateExecutor(); updateMark(); }, ['asset-db:asset-add'](uuid, info) { if (!isExecutor(info)) return; callUpdateExecutor(); }, ['asset-db:asset-change'](uuid, info) { if (!isExecutor(info, false)) return; callUpdateExecutor(); }, ['asset-db:asset-delete'](uuid, info) { if (!isExecutor(info)) return; callUpdateExecutor(true); } }; /** * @en Hooks triggered after extension loading is complete * @zh 扩展加载完成后触发的钩子 */ function load() { Editor.Message.request('asset-db', 'query-ready').then(ready => { if (!ready) return; updateExecutor(); }); } exports.load = load; /** * @en Hooks triggered after extension uninstallation is complete * @zh 扩展卸载完成后触发的钩子 */ function unload() { } exports.unload = unload;