"use strict"; window.packageRoot = 'packages://hot-update-tools/'; var fs = require('fire-fs'); var path = require('fire-path'); var Electron = require('electron'); var { remote } = require('electron'); var CfgUtil = Editor.require('packages://hot-update-tools/core/CfgUtil.js'); var FileUtil = Editor.require('packages://hot-update-tools/core/FileUtil.js'); var Mail = Editor.require('packages://hot-update-tools/mail/Mail.js'); var OSS = Editor.require('packages://hot-update-tools/node_modules/ali-oss'); var CO = Editor.require('packages://hot-update-tools/node_modules/co'); Editor.Panel.extend({ style: fs.readFileSync(Editor.url('packages://hot-update-tools/panel/index.css', 'utf8')) + "", template: fs.readFileSync(Editor.url('packages://hot-update-tools/panel/index.html', 'utf8')) + "", $: { logTextArea: '#logTextArea', hotAddressSelect: '#hotAddressSelect', testEnvSelect: '#testEnvSelect', }, ready() { let logCtrl = this.$logTextArea; let logListScrollToBottom = function () { setTimeout(function () { logCtrl.scrollTop = logCtrl.scrollHeight; }, 10); }; let hotAddressSelectCtrl = this.$hotAddressSelect; window.hotAddressSelectCtrl = hotAddressSelectCtrl; function selectionLast(index) { setTimeout(function () { hotAddressSelectCtrl.selectedIndex = index; }, 10); } function getSelectIp() { let ip = hotAddressSelectCtrl.$select.value; console.log(ip); return ip; } // 初始化vue面板 window.plugin = new window.Vue({ el: this.shadowRoot, created: function () { this._initPluginCfg(); }, init: function () { }, data: { srcDirPath: "", resDirPath: "", projManifestPath: "", verManifestPath: "", version: "", remoteServerVersion: "",// 远程服务器版本 isShowRemoteServerVersion: false,// 是否显示远程服务器版本号 genManifestDir: "", serverRootDir: "", resourceRootDir: "", localServerPath: "", logView: "", copyProgress: 0, totalNum: 0,// 操作文件总数 curNum: 0,// 当前操作的次数 serverVersion: "-",// 服务器版本 serverPackageUrl: "", localGameVersion: "-",//游戏版本号 localGamePackageUrl: "", localGameProjectManifest: "", localGameVersionManifest: "", localGameProjectManifestUrl: "",//assets的url localGameVersionManifestUrl: "",// // 测试环境逻辑变量 testEnvLocal: true, testEnvALi: false, testEnvEmail: false,// 发送邮件界面 testEnvSelect: 0, // 热更资源服务器配置历史记录 isShowUseAddrBtn: false, isShowDelAddrBtn: false, hotAddressArray: [], // 邮件逻辑变量 emailContent: "邮件内容!", addMailPeople: "", emailPeopleArray: [ "xu_yanfeng@126.com", ], }, computed: {}, methods: { //////////////////////////////////阿里云环境///////////////////////////////////////////////////// onBtnClickAliTest() { let client = new OSS({ region: 'oss-cn-beijing', //云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,部署在服务端使用RAM子账号或STS,部署在客户端使用STS。 accessKeyId: 'LTAIOxxDqJpJbzfy', accessKeySecret: 'kZRbbX3nNtxWlx5XWsR8uRrJzj4X5C', bucket: 'happycars' }); CO(function* () { client.useBucket('happycars'); // let ret = yield client.list(); // yield client.get(''); // for (let i = 0; i < ret.objects.length; i++) { // let item = ret.objects[i]; // console.log(i + ": " + item.url); // } // console.log(ret); function* listDir(dir) { let list = yield client.list({ prefix: dir, delimiter: '/' }); list.prefixes.forEach(function (subDir) { console.log("目录: " + subDir); }); list.objects.forEach(function (obj) { console.log("文件: " + obj.name); }); } yield listDir('hot'); }).catch(function (err) { console.log(err); }) }, //////////////////////////////////发送邮件///////////////////////////////////////////////////// onBtnClickSendMail() { // Editor.Ipc.sendToMain('hot-update-tools:test', 'Hello, this is simple panel'); Mail.sendMail(this.remoteServerVersion, this.emailContent, null, function () { this._addLog("发送邮件完毕!"); }.bind(this)); }, onInputMailPeopleOver() { if (this.isPeopleExist() === false) { this.emailPeopleArray.push(this.addMailPeople); } }, isPeopleExist() { console.log("isPeopleExist"); if (this.addMailPeople === null || this.addMailPeople === "") { return false; } for (let i = 0; i < this.emailPeopleArray.length; i++) { let itemPeople = this.emailPeopleArray[i]; if (itemPeople === this.addMailPeople) { return true; } } return false; }, /////////////////////////////////////////////////////////////////////////////////////// // 测试 onTest() { }, onBtnClickPackVersion() { this._packageVersion(); }, // 打包目录 _packageDir(rootPath, zip) { let dir = fs.readdirSync(rootPath); for (let i = 0; i < dir.length; i++) { let itemDir = dir[i]; let itemFullPath = path.join(rootPath, itemDir); let stat = fs.statSync(itemFullPath); if (stat.isFile()) { zip.file(itemDir, fs.readFileSync(itemFullPath)); } else if (stat.isDirectory()) { this._packageDir(itemFullPath, zip.folder(itemDir)); } } }, // 将当前版本打包 _packageVersion() { this._addLog("[Pack] 开始打包版本 ..."); let JSZip = Editor.require("packages://hot-update-tools/node_modules/jszip"); let zip = new JSZip(); // 打包manifest文件 let version = path.join(this.genManifestDir, "version.manifest"); zip.file(`version.${this.version}.manifest`, fs.readFileSync(version)); let project = path.join(this.genManifestDir, "project.manifest"); zip.file("project.manifest", fs.readFileSync(project)); // 要打包的资源 let srcPath = path.join(this.resourceRootDir, "src"); this._packageDir(srcPath, zip.folder("src")); let resPath = path.join(this.resourceRootDir, "remote"); this._packageDir(resPath, zip.folder("remote")); // 打包的文件名 let versionData = fs.readFileSync(version, 'utf-8'); let versionJson = JSON.parse(versionData); let versionStr = versionJson.version;// 版本 this._addLog("[Pack] 打包版本:" + versionStr); if (versionStr !== this.version) { this._addLog("[Pack] 打包版本和当前填写的版本不一致,出现异常,停止打包!"); return; } // 打包到目录,生成zip versionStr = versionStr.replace('.', '_'); let zipName = "ver_" + versionStr + ".zip"; let zipDir = CfgUtil.getPackZipDir(); if (!fs.existsSync(zipDir)) { fs.mkdirSync(zipDir); } let zipFilePath = path.join(zipDir, zipName); if (fs.existsSync(zipFilePath)) {// 存在该版本的zip fs.unlinkSync(zipFilePath); this._addLog("[Pack] 发现该版本的zip, 已经删除!"); } else { } zip.generateNodeStream({ type: 'nodebuffer', streamFiles: true }) .pipe(fs.createWriteStream(zipFilePath)) .on('finish', function () { this._addLog("[Pack] 打包成功: " + zipFilePath); }.bind(this)) .on('error', function (event) { this._addLog("[Pack] 打包失败:" + event.message); }.bind(this)); }, onBuildFinished(time) { // 当构建完成的时候,genTime和buildTime是一致的 console.log("hot - onBuildFinished"); CfgUtil.updateBuildTime(time); }, onChangeSelectHotAddress(event) { console.log("change"); this.isShowUseAddrBtn = true; this.isShowDelAddrBtn = true; this._updateShowUseAddrBtn(); }, _updateShowUseAddrBtn() { let selectURL = window.hotAddressSelectCtrl.value; if (this.serverRootDir === selectURL) { this.isShowUseAddrBtn = false; } }, // 增加热更历史地址 _addHotAddress(addr) { let isAddIn = true; for (let i = 0; i < this.hotAddressArray.length; i++) { let item = this.hotAddressArray[i]; if (item === addr) { isAddIn = false; break; } } if (isAddIn) { this.hotAddressArray.push(addr); this._addLog("[HotAddress]历史记录添加成功:" + addr); } else { // this._addLog("[HotAddress]历史记录已经存在该地址: " + addr); } }, // 删除热更历史地址 onBtnClickDelSelectedHotAddress() { let address = window.hotAddressSelectCtrl.value; if (this.hotAddressArray.length > 0) { let isDel = false; for (let i = 0; i < this.hotAddressArray.length;) { let item = this.hotAddressArray[i]; if (item === address) { this.hotAddressArray.splice(i, 1); isDel = true; this._addLog("删除历史地址成功: " + item); } else { i++; } } if (isDel) { this.isShowDelAddrBtn = false; this.isShowUseAddrBtn = false; this._saveConfig(); } } else { this._addLog("历史地址已经为空"); } }, onBtnClickUseSelectedHotAddress() { let address = window.hotAddressSelectCtrl.value; this.serverRootDir = address; this.onInPutUrlOver(); this._updateShowUseAddrBtn(); }, _addLog(str) { let time = new Date(); // this.logView = "[" + time.toLocaleString() + "]: " + str + "\n" + this.logView; this.logView += "[" + time.toLocaleString() + "]: " + str + "\n"; logListScrollToBottom(); }, _getFileIsExist(path) { try { fs.accessSync(path, fs.F_OK); } catch (e) { return false; } return true; }, onCleanAPPCfg() { CfgUtil.cleanConfig(); }, _saveConfig() { let data = { version: this.version, serverRootDir: this.serverRootDir, resourceRootDir: this.resourceRootDir, genManifestDir: CfgUtil.getMainFestDir(), localServerPath: this.localServerPath, hotAddressArray: this.hotAddressArray, }; CfgUtil.saveConfig(data); }, _initPluginCfg() { console.log("init cfg"); // manifest输出目录 this.genManifestDir = CfgUtil.getMainFestDir(); if (FileUtil.isFileExit(this.genManifestDir) === false) { FileUtil.mkDir(this.genManifestDir); } // 用户自定义配置 CfgUtil.initCfg(function (data) { if (data) { this.version = data.version; this.serverRootDir = data.serverRootDir; this.resourceRootDir = data.resourceRootDir; this.localServerPath = data.localServerPath; this.hotAddressArray = data.hotAddressArray || []; this._updateServerVersion(); this._getRemoteServerVersion(); } else { this._saveConfig(); } this._initResourceBuild(); this.initLocalGameVersion(); }.bind(this)); }, // 导入生成的manifest到游戏项目中 importManifestToGame() { let projectFile = path.join(this.genManifestDir, "project.manifest"); let versionFile = path.join(this.genManifestDir, "version.manifest") // let strArr = this.localGameProjectManifestUrl.split("project.manifest"); // let dir = strArr[0]; let dir = "db://assets"; Editor.assetdb.import([projectFile, versionFile], dir, function (err, results) { results.forEach(function (result) { console.log(result.path); // result.uuid // result.parentUuid // result.url // result.path // result.type }); }.bind(this)); this.initLocalGameVersion(); // let spawn = require('child_process').spawn; // let free = spawn('cmd', []); // // 捕获标准输出并将其打印到控制台 // free.stdout.on('data', function (data) { // console.log('standard output:\n' + data); // }); // // // 捕获标准错误输出并将其打印到控制台 // free.stderr.on('data', function (data) { // console.log('standard error output:\n' + data); // }); // // // 注册子进程关闭事件 // free.on('exit', function (code, signal) { // console.log('child process eixt ,exit:' + code); // }); }, initLocalGameVersion() { Editor.assetdb.queryAssets('db://assets/**\/*', "raw-asset", function (err, results) { let versionCfg = ""; let projectCfg = ""; results.forEach(function (result) { if (result.path.indexOf("version.manifest") !== -1) { versionCfg = result.path; this.localGameVersionManifestUrl = result.url; } else if (result.path.indexOf("project.manifest") !== -1) { projectCfg = result.path; this.localGameProjectManifestUrl = result.url; } }.bind(this)); console.log("version: " + versionCfg); console.log("project: " + projectCfg); if (versionCfg.length === 0) { this._addLog("项目中没有配置文件: version.manifest"); return; } if (projectCfg.length === 0) { this._addLog("项目中没有配置文件: project.manifest"); return; } this.localGameVersionManifest = versionCfg; this.localGameProjectManifest = projectCfg; let verVersion = ""; let projVersion = ""; fs.readFile(versionCfg, 'utf-8', function (err, data) { if (!err) { let verData = JSON.parse(data); verVersion = verData.version; fs.readFile(projectCfg, 'utf-8', function (err, data) { if (!err) { let projectData = JSON.parse(data); projVersion = projectData.version; if (projVersion === verVersion) { this.localGameVersion = projVersion; this.localGamePackageUrl = projectData.packageUrl; } else { this._addLog("游戏中的 project.manifest 和 version.manifest 中的version字段值不一致,请检查配置文件"); } } else { // this.localGameVersion = "没有在项目中发现热更新文件"; this._addLog("读取项目中的配置文件失败: " + projectCfg); } }.bind(this)) } else { this._addLog("读取项目中的配置文件失败: " + versionCfg); } }.bind(this)) }.bind(this)); /* Editor.assetdb.deepQuery(function (err, results) { results.forEach(function (result) { if (result.type !== "folder" && result.type !== "texture" && result.type !== "sprite-frame" && result.type !== "javascript" && result.type !== "dragonbones-atlas" && result.type !== "prefab" && result.type !== "audio-clip" && result.type !== "animation-clip" && result.type !== "scene" && result.type !== "dragonbones" && result.type !== "particle" && result.type !== "label-atlas" && result.type !== "text" && result.type !== "" && result.type !== "" ) { let fullName = result.name + result.extname; console.log(result.type + " : " + fullName); } }); }); */ }, // build目录 _initResourceBuild() { if (this.resourceRootDir.length === 0) { // Editor.Profile.load('profile://local/builder.json', (err, profile) => { // if (!err) { // console.log(profile); // } // }); // 没有填写resource目录,自动提示 let projectDir = path.join(Editor.assetdb.library, "../"); let buildCfg = path.join(projectDir, "local/builder.json"); if (FileUtil.isFileExit(buildCfg)) { fs.readFile(buildCfg, 'utf-8', function (err, data) { if (!err) { let buildData = JSON.parse(data); let buildDir = buildData.buildPath; let buildFullDir = path.join(projectDir, buildDir); let jsbDir = path.join(buildFullDir, "jsb-default"); this._checkResourceRootDir(jsbDir); } }.bind(this)) } else { this._addLog("发现没有构建项目, 使用前请先构建项目!"); } } }, _checkResourceRootDir(jsbDir) { if (FileUtil.isFileExit(jsbDir)) { let srcPath = path.join(jsbDir, "src"); let resPath = path.join(jsbDir, "remote"); if (FileUtil.isFileExit(srcPath) === false) { this._addLog("没有发现 " + srcPath + ", 请先构建项目."); return false; } if (FileUtil.isFileExit(resPath) === false) { this._addLog("没有发现 " + resPath + ", 请先构建项目."); return false; } this.resourceRootDir = jsbDir; return true; } else { this._addLog("没有发现 " + jsbDir + ", 请先构建项目."); return false; } }, onClickGenCfg(event) { // 检查是否需要构建项目 let times = CfgUtil.getBuildTimeGenTime(); let genTime = times.genTime; let buildTime = times.buildTime; if (genTime === buildTime) {// 构建完版本之后没有生成manifest文件 CfgUtil.updateGenTime(new Date().getTime(), this.version);// 更新生成时间 } else { this._addLog("[生成] 你需要重新构建项目,因为上次构建已经和版本关联:" + CfgUtil.cfgData.genVersion); return; } if (!this.version || this.version.length <= 0) { this._addLog("[生成] 版本号未填写"); return; } if (!this.serverRootDir || this.serverRootDir.length <= 0) { this._addLog("[生成] 服务器地址未填写"); return; } // 检查resource目录 if (this.resourceRootDir.length === 0) { this._addLog("[生成] 请先指定 "); return; } if (this._checkResourceRootDir(this.resourceRootDir) === false) { return; } if (!this.genManifestDir || this.genManifestDir.length <= 0) { this._addLog("[生成] manifest文件生成地址未填写"); return; } if (!fs.existsSync(this.genManifestDir)) { this._addLog("[生成] manifest存储目录不存在: " + this.genManifestDir); return; } this._saveConfig(); this._genVersion(this.version, this.serverRootDir, this.resourceRootDir, this.genManifestDir); }, // serverUrl 必须以/结尾 // genManifestDir 建议在assets目录下 // buildResourceDir 默认为 build/jsb-default/ // -v 10.1.1 -u http://192.168.191.1//cocos/remote-assets/ -s build/jsb-default/ -d assets _genVersion(version, serverUrl, buildResourceDir, genManifestDir) { this._addLog("[Build] 开始生成manifest配置文件...."); let projectFile = "project.manifest"; let versionFile = "version.manifest"; let manifest = { version: version, packageUrl: serverUrl, remoteManifestUrl: "", remoteVersionUrl: "", assets: {}, searchPaths: [] }; if (serverUrl[serverUrl.length - 1] === "/") { manifest.remoteManifestUrl = serverUrl + projectFile; manifest.remoteVersionUrl = serverUrl + versionFile; } else { manifest.remoteManifestUrl = serverUrl + "/" + projectFile; manifest.remoteVersionUrl = serverUrl + "/" + versionFile; } let dest = genManifestDir; let src = buildResourceDir; let readDir = function (dir, obj) { let stat = fs.statSync(dir); if (!stat.isDirectory()) { return; } let subpaths = fs.readdirSync(dir), subpath, size, md5, compressed, relative; for (let i = 0; i < subpaths.length; ++i) { if (subpaths[i][0] === '.') { continue; } subpath = path.join(dir, subpaths[i]); stat = fs.statSync(subpath); if (stat.isDirectory()) { readDir(subpath, obj); } else if (stat.isFile()) { // Size in Bytes size = stat['size']; // let crypto = require('crypto'); md5 = require('crypto').createHash('md5').update(fs.readFileSync(subpath, 'binary')).digest('hex'); compressed = path.extname(subpath).toLowerCase() === '.zip'; relative = path.relative(src, subpath); relative = relative.replace(/\\/g, '/'); relative = encodeURI(relative); obj[relative] = { 'size': size, 'md5': md5 }; if (compressed) { obj[relative].compressed = true; } } } }; let mkdirSync = function (path) { try { fs.mkdirSync(path); } catch (e) { if (e.code !== 'EEXIST') throw e; } }; // Iterate res and src folder readDir(path.join(src, 'src'), manifest.assets); readDir(path.join(src, 'remote'), manifest.assets); let destManifest = path.join(dest, 'project.manifest'); let destVersion = path.join(dest, 'version.manifest'); mkdirSync(dest); // 生成project.manifest fs.writeFileSync(destManifest, JSON.stringify(manifest)); this._addLog("[Build] 生成 project.manifest成功"); // 生成version.manifest delete manifest.assets; delete manifest.searchPaths; fs.writeFileSync(destVersion, JSON.stringify(manifest)); this._addLog("[Build] 生成 version.manifest成功"); this._packageVersion(); }, // 选择物理server路径 onSelectLocalServerPath(event) { let res = Editor.Dialog.openFile({ title: "选择本地测试服务器目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], }); if (res !== -1) { this.localServerPath = res[0]; this._saveConfig(); this._updateServerVersion(); } }, // 拷贝文件到测试服务器 onCopyFileToLocalServer() { // 检查要拷贝的目录情况 if (!fs.existsSync(this.localServerPath)) { this._addLog("本地测试服务器目录不存在:" + this.localServerPath); return; } // 检查资源目录 let srcPath = path.join(this.resourceRootDir, "src"); let resPath = path.join(this.resourceRootDir, "remote"); if (!fs.existsSync(this.resourceRootDir)) { this._addLog("资源目录不存在: " + this.resourceRootDir + ", 请先构建项目"); return; } else { if (!fs.existsSync(srcPath)) { this._addLog(this.resourceRootDir + "不存在src目录, 无法拷贝文件"); return; } if (!fs.existsSync(resPath)) { this._addLog(this.resourceRootDir + "不存在res目录, 无法拷贝文件"); return; } } // 检查manifest文件 let project = path.join(this.genManifestDir, "project.manifest"); let version = path.join(this.genManifestDir, "version.manifest"); if (!this.genManifestDir || this.genManifestDir.length <= 0) { this._addLog("manifest文件生成地址未填写"); return; } else { if (!this._getFileIsExist(project)) { this._addLog(project + "不存在, 请点击生成配置"); return; } if (!this._getFileIsExist(version)) { this._addLog(version + "不存在, 请点击生成配置"); return; } } this._addLog("[部署] 开始拷贝文件到:" + this.localServerPath); this.curNum = 0; this.copyProgress = 0; //删除老文件 this._addLog("[部署] 删除目录路径: " + this.localServerPath); let delNum = this._getFileNum(this.localServerPath); this._addLog("[部署] 删除文件个数:" + delNum); this._delDir(this.localServerPath); this.totalNum = this._getTotalCopyFileNum(); this._addLog("[部署] 复制文件个数:" + this.totalNum); this._copySourceDirToDesDir(srcPath, path.join(this.localServerPath, "src")); this._copySourceDirToDesDir(resPath, path.join(this.localServerPath, "remote")); this._copyFileToDesDir(project, this.localServerPath); this._copyFileToDesDir(version, this.localServerPath); }, // 获取要操作的文件总数量 _getTotalCopyFileNum() { // let delNum = this._getFileNum(this.localServerPath); let srcNum = this._getFileNum(path.join(this.resourceRootDir, "src")); let resNum = this._getFileNum(path.join(this.resourceRootDir, "remote")); return srcNum + resNum + 2 + 2;// 2个manifest,2个目录(src, res) }, addProgress() { this.curNum++; let p = this.curNum / this.totalNum; p = p ? p : 0; // console.log("进度: " + p * 100); this.copyProgress = p * 100; if (p >= 1) { this._addLog("[部署] 部署到指定目录成功:" + this.localServerPath); this._updateServerVersion(); } }, // 刷新服务器版本号 refreshLocalServerVersion() { this._updateServerVersion(); }, _updateServerVersion() { if (this.localServerPath.length > 0) { let path = require("fire-path"); let fs = require("fire-fs"); let versionCfg = path.join(this.localServerPath, "version.manifest"); fs.readFile(versionCfg, 'utf-8', function (err, data) { if (!err) { let cfg = JSON.parse(data); this.serverVersion = cfg.version; this.serverPackageUrl = cfg.packageUrl; } else { let projectCfg = path.join(this.localServerPath, "project.manifest"); fs.readFile(projectCfg, 'utf-8', function (err, data) { if (!err) { let projectCfg = JSON.parse(data); this.serverVersion = projectCfg.version; this.serverPackageUrl = projectCfg.packageUrl; } else { this._addLog("无法获取到本地测试服务器版本号"); } }.bind(this)); } }.bind(this)); } else { this._addLog("请选择本机server物理路径"); } }, // 获取文件个数 _getFileNum(url) { let i = 0; let lookDir = function (fileUrl) { let files = fs.readdirSync(fileUrl);//读取该文件夹 for (let k in files) { i++; let filePath = path.join(fileUrl, files[k]); let stats = fs.statSync(filePath); if (stats.isDirectory()) { lookDir(filePath); } } }; lookDir(url); return i; }, _delDir(rootFile) { let self = this; //删除所有的文件(将所有文件夹置空) let emptyDir = function (fileUrl) { let files = fs.readdirSync(fileUrl);//读取该文件夹 for (let k in files) { let filePath = path.join(fileUrl, files[k]); let stats = fs.statSync(filePath); if (stats.isDirectory()) { emptyDir(filePath); } else { fs.unlinkSync(filePath); // self.addProgress(); // console.log("删除文件:" + filePath); } } }; //删除所有的空文件夹 let rmEmptyDir = function (fileUrl) { let files = fs.readdirSync(fileUrl); if (files.length > 0) { for (let k in files) { let rmDir = path.join(fileUrl, files[k]); rmEmptyDir(rmDir); } if (fileUrl !== rootFile) {// 不删除根目录 fs.rmdirSync(fileUrl); // self.addProgress(); // console.log('删除空文件夹' + fileUrl); } } else { if (fileUrl !== rootFile) {// 不删除根目录 fs.rmdirSync(fileUrl); // self.addProgress(); // console.log('删除空文件夹' + fileUrl); } } }; emptyDir(rootFile); rmEmptyDir(rootFile); }, // 拷贝文件到目录 _copyFileToDesDir(file, desDir) { if (this._getFileIsExist(file)) { let readable = fs.createReadStream(file);// 创建读取流 let copyFileName = path.basename(file); let fileName = path.join(desDir, copyFileName); let writable = fs.createWriteStream(fileName);// 创建写入流 readable.pipe(writable);// 通过管道来传输流 this.addProgress(); } }, // 拷贝文件夹 _copySourceDirToDesDir(source, des) { let self = this; // 在复制目录前需要判断该目录是否存在,不存在需要先创建目录 let exists = function (src, dst, callback) { fs.exists(dst, function (exists) { // 已存在 if (exists) { callback(src, dst); } // 不存在 else { fs.mkdir(dst, function () { self.addProgress(); callback(src, dst); }); } }); }; /* * 复制目录中的所有文件包括子目录 * @param{ String } 需要复制的目录 * @param{ String } 复制到指定的目录 */ let copy = function (src, dst) { // 读取目录中的所有文件/目录 fs.readdir(src, function (err, paths) { if (err) { throw err; } paths.forEach(function (path) { let _src = src + '/' + path, _dst = dst + '/' + path, readable, writable; fs.stat(_src, function (err, st) { if (err) { throw err; } if (st.isFile()) {// 判断是否为文件 readable = fs.createReadStream(_src);// 创建读取流 writable = fs.createWriteStream(_dst);// 创建写入流 readable.pipe(writable);// 通过管道来传输流 // console.log("拷贝文件:" + _dst); self.addProgress(); } else if (st.isDirectory()) {// 如果是目录则递归调用自身 exists(_src, _dst, copy); } }); }); }); }; // 复制目录 exists(source, des, copy); }, _isVersionPass(newVersion, baseVersion) { if (newVersion === undefined || newVersion === null || baseVersion === undefined || baseVersion === null) { return false; } let arrayA = newVersion.split('.'); let arrayB = baseVersion.split('.'); let len = arrayA.length > arrayB.length ? arrayA.length : arrayB.length; for (let i = 0; i < len; i++) { let itemA = arrayA[i]; let itemB = arrayB[i]; // 新版本1.2 老版本 1.2.3 if (itemA === undefined && itemB !== undefined) { return false; } // 新版本1.2.1, 老版本1.2 if (itemA !== undefined && itemB === undefined) { return true; } if (itemA && itemB && parseInt(itemA) > parseInt(itemB)) { return true; } } return false; }, onInputVersionOver() { let buildVersion = CfgUtil.cfgData.genVersion; let buildTime = CfgUtil.cfgData.buildTime; let genTime = CfgUtil.cfgData.genTime;// 生成manifest时间 let remoteVersion = this.remoteServerVersion; if (remoteVersion !== null && remoteVersion !== undefined) {// 存在远程版本 if (this._isVersionPass(this.version, remoteVersion)) { this._addLog("上次构建时版本号: " + buildVersion); if (this._isVersionPass(this.version, buildVersion)) {// 上次构建版本和远程版本一致 this._addLog("版本通过验证!"); } else { this._addLog("[Warning] 要构建的版本低于上次构建版本: " + this.version + "<=" + buildVersion); } } else { this._addLog("[Warning] version 填写的版本低于远程版本"); } } else {// 未发现远程版本 } // let nowTime = new Date().getTime(); // if (nowTime !== buildTime) { // if (genVersion === this.version) { // this._addLog("版本一致,请构建项目"); // } else { // } // } this._saveConfig(); }, onInPutUrlOver(event) { let url = this.serverRootDir; let index1 = url.indexOf("http://"); let index2 = url.indexOf("https://"); if (index1 === -1 && index2 === -1) { let reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/; if (!reg.test(url)) { this._addLog(url + " 不是以http://https://开头,或者不是网址, 已经自动修改"); this.serverRootDir = "http://" + this.serverRootDir; this._getRemoteServerVersion(); } } else { // 更新服务器版本 this._getRemoteServerVersion(); } this._addHotAddress(this.serverRootDir); this._updateShowUseAddrBtn(); this._saveConfig(); }, // 获取远程资源服务器的版本号 _getRemoteServerVersion() { if (this.serverRootDir.length <= 0) { console.log("远程资源服务器URL错误: " + this.serverRootDir); return; } // TODO 浏览器缓存会导致版本号获取失败 this.isShowRemoteServerVersion = false; this.remoteServerVersion = null; let versionUrl = this.serverRootDir + "/version.manifest"; let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && ((xhr.status >= 200 && xhr.status < 400))) { let text = xhr.responseText; // console.log("版本文件内容\n" + versionUrl + "\n" + text); let json = null; try { json = JSON.parse(text); } catch (e) { console.log(e); this._addLog("获取远程版本号失败!"); return; } this.isShowRemoteServerVersion = true; this.remoteServerVersion = json.version; } else if (xhr.status === 404) { // console.log("404"); } }.bind(this); xhr.open('get', versionUrl, true); xhr.setRequestHeader("If-Modified-Since", "0");// 不缓存 xhr.send(); }, onTestUrl() { let url = this.serverRootDir; if (url.length > 0) { this._isUrlAvilable(url, function (code) { this._addLog(url + " 响应: " + code); }.bind(this)); } else { this._addLog("请填写 [资源服务器url] "); } }, _isUrlAvilable(url, callback) { let http = require("http"); try { http.get(url, function (res) { if (callback) { callback(res.statusCode); } //let text; //res.setEncoding('utf8'); //res.on('data', function (chunk) { // text += chunk; // console.log(text); //}); }.bind(this)); } catch (e) { callback(-1); } }, // 访问测试网站 onOpenUrl(event) { let url = this.serverRootDir; if (url.length > 0) { Electron.shell.openExternal(url); } else { this._addLog("未填写参数"); } }, userLocalIP() { let ip = ""; let os = require('os'); let network = os.networkInterfaces(); for (let i = 0; i < network.WLAN.length; i++) { let json = network.WLAN[i]; if (json.family === 'IPv4') { ip = json.address; } } console.log(ip); if (ip.length > 0) { this.serverRootDir = "http://" + ip; this.onInPutUrlOver(null); } }, // 选择资源文件目录 onSelectSrcDir(event) { let res = Editor.Dialog.openFile({ title: "选择Src目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], callback: function (fileNames) { }, }); if (res !== -1) { this.srcDirPath = res[0]; this._saveConfig(); } }, onSelectResDir() { let res = Editor.Dialog.openFile({ title: "选择Res目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], }); if (res !== -1) { this.resDirPath = res[0]; this._saveConfig(); } }, // 选择projManifest文件 onOpenResourceDir() { if (!fs.existsSync(this.resourceRootDir)) { this._addLog("目录不存在:" + this.resourceRootDir); return; } Electron.shell.showItemInFolder(this.resourceRootDir); Electron.shell.beep(); }, onOpenManifestDir() { if (!fs.existsSync(this.genManifestDir)) { this._addLog("目录不存在:" + this.genManifestDir); return; } Electron.shell.showItemInFolder(this.genManifestDir); Electron.shell.beep(); }, onOpenLocalServer() { if (!fs.existsSync(this.genManifestDir)) { this._addLog("目录不存在:" + this.localServerPath); return; } Electron.shell.showItemInFolder(this.localServerPath); Electron.shell.beep(); }, // 选择生成Manifest的目录 onSelectGenManifestDir() { let res = Editor.Dialog.openFile({ title: "选择生成Manifest目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], }); if (res !== -1) { this.genManifestDir = res[0]; this._saveConfig(); } }, onSelectGenServerRootDir() { let res = Editor.Dialog.openFile({ title: "选择部署的服务器根目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], }); if (res !== -1) { this.serverRootDir = res[0]; this._saveConfig(); } }, onSelectResourceRootDir() { let res = Editor.Dialog.openFile({ title: "选择构建后的根目录", defaultPath: Editor.projectInfo.path, properties: ['openDirectory'], }); if (res !== -1) { let dir = res[0]; if (this._checkResourceRootDir(dir)) { this.resourceRootDir = dir; this._saveConfig(); } } }, onCleanSimRemoteRes() { let path = require("fire-path"); let simPath = path.join(__dirname, "../cocos2d-x/simulator/win32"); let remoteAsset = path.join(simPath, "remote-asset"); if (!fs.existsSync(remoteAsset)) { console.log(remoteAsset); this._addLog("[清理热更缓存] 目录不存在: " + remoteAsset); } else { FileUtil.emptyDir(remoteAsset); this._addLog("[清理热更缓存] 清空目录 " + remoteAsset + " 成功."); } }, onOpenLocalGameManifestDir() { let strArr = this.localGameProjectManifest.split("project.manifest"); let dir = strArr[0]; if (!fs.existsSync(dir)) { this._addLog("目录不存在:" + dir); return; } Electron.shell.showItemInFolder(dir); Electron.shell.beep(); }, onTestZip() { let zip = require("node-native-zip"); }, // onTestSelect() { let ip = Math.random() * 100; this.hotAddressArray.push(ip.toFixed(2)); selectionLast(this.hotAddressArray.length - 1); }, onIpSelectChange(event) { console.log("index:%d, text:%s, value:%s ", event.detail.index, event.detail.text, event.detail.value); }, onLogIp() { // getSelectIp(); Editor.Ipc.sendToMain('hot-update-tools:test', 'Hello, this is simple panel'); // let relativePath = path.relative(__dirname, path.join(Editor.projectInfo.path, 'assets')); // console.log(relativePath); }, onTestEnvChange(event) { // let children = event.target.$select.children; // for (let i = 0; i < children.length; i++) { // let item = children[i]; // if (item.value === "0") { // item.selected = true; // } else { // item.selected = false; // } // } let selectValue = event.target.value; this.testEnvALi = false; this.testEnvLocal = false; this.testEnvEmail = false; if (selectValue === "0") {// 本地测试环境 this.testEnvLocal = true; } else if (selectValue === "1") {//阿里云测试环境 this.testEnvALi = true; } else if (selectValue === "2") { this.testEnvEmail = true; } }, } }); }, messages: { 'hot-update-tools:onBuildFinished'(event, time) { window.plugin.onBuildFinished(time); }, } });