从 creator热更新问题 继续讨论:
Creator v1.9.3热更新
新版本生成 具体步骤:
-
先创建新版本,具体如下图,多了一个新场景NewScene:
-
构建项目:
-
生成对比文件前要做的准备工作,首先你得先安装一下nodejs工具,我这里自己写了个批处理文件:
批处理代码:
version_generator.js 自己去官方的dome里面拿一个放在自己自己项目下面;
-v 1.7.0 这里是新版本的版本号,反正必须要比你的旧版本高;
-u http://112.74.42.214:8011/remote-assets-hotupdatetest/ 是你的热更服务器地址;
-s build/jsb-link/ 构建生成的项目路径;
-d assets/ 存放生成对比文件的路径。
------------------------------------------------------------------------------
4. 执行批处理文件 热更新.bat,成功以后你会发现项目assets/ 下面会有两个对比文件:project.manifest、version.manifest。
5. 把assets/ project.manifest拖入你UI界面上的ManifestUrl上,注意ManifestUrl的类型是raw-asset而不是官方Dome上的asset:
6. 保存,然后重新构建一次项目。
7. 构建完成后:
a) 把 build/jsb-link/ 路径下生成的 res 和 src 拷贝到你服务器(我这里就是http://112.74.42.214:8011/remote-assets-hotupdatetest/)更新路径下;
b) 把 assets/ 下生成的project.manifest、version.manifest 这两个文件一样拷贝到你的服务器更新路径下。
- 到这里新版本就做完了,更新服务器怎么搭建就不说了吧。
旧版本生成 具体步骤:
- 删掉build/jsb-link文件夹; 删除assets/ 下生成的project.manifest、version.manifest;删除场景NewScene以及对于代码和资源(随便你自己怎么弄,有明显UI变动就可以):
- 构建项目,配置不要改动:
- 修改批处理代码中版本号 改成1.0.0,比你生成的新版本号低就好,然后执行批处理文件 热更新.bat,成功以后你会发现项目assets/ 下面会有两个对比文件:project.manifest、version.manifest。
- 把assets/ project.manifest拖入你UI界面上的ManifestUrl上,注意ManifestUrl的类型是raw-asset而不是官方Dome上的asset:
- 保存,然后重新构建一次项目。
- 构建完成后先不要编译,还有一个重要步骤。
修改main.js,添加本地热更新路径:
修改完保存。
7. 然后再编译(这里就不需要再去构建了,直接编译)
8. 编译没错误就OK了,我这里是android平台debug模式下:
更新前:
点击检查更新按钮:
发现有新版本了,然后点击开始更新:
发现正在下载更新文件。
更新完成重启后:
新版本这里我把更新界面的LOGO也换掉了的,
点击检查更新按钮:
发现没有新版本了,切换场景按钮就显示出来了,点击按钮跳转到NewScene,
更新代码:
HotUpdate.js
/**
-
负责热更新逻辑的组件
*/
cc.Class({
extends: cc.Component,properties: {
manifestUrl: cc.RawAsset, //本地project.manifest资源清单文件
txtTips: cc.Label,
txtFileProgress:cc.Label,
txtByteProgress:cc.Label,
progressFile: cc.ProgressBar,
progressByte: cc.ProgressBar,btnChangeScene: cc.Node, _updating: false, _canRetry: false, _storagePath: ''},
changeSceneCb: function (event) {
var searchPaths = jsb.fileUtils.getSearchPaths();
console.log(“搜索路径:”+searchPaths)
//进入登录场景
this.scheduleOnce(function(){
cc.director.loadScene(“NewScene”);
}, 1);
},checkCb: function (event) {
cc.log('Code: ’ + event.getEventCode());
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
cc.log(“No local manifest file found, hot update skipped.”);
this.txtTips.string = “本地manifest没有被发现”;
break;
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
cc.log(“Fail to download manifest file, hot update skipped.”);
this.txtTips.string = “下载或者解析manifest出错”;
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
cc.log(“Already up to date with the latest remote version.”);
this.txtTips.string = “已经更新到最新版本”;this.btnChangeScene.active = true; break; case jsb.EventAssetsManager.NEW_VERSION_FOUND: cc.log('New version found, please try to update.'); let localVersion = this._am.getLocalManifest().getVersion(); let remoteVersion = this._am.getRemoteManifest().getVersion(); this.txtTips.string = "检查到新版本,本地版本号:"+localVersion+" 远程版本号:"+remoteVersion+", 可以更新!"; this.progressFile.progress = 0; this.progressByte.progress = 0; break; default: return; } cc.eventManager.removeListener(this._checkListener); this._checkListener = null; this._updating = false;},
updateCb: function (event) {
var needRestart = false;
var failed = false;
switch (event.getEventCode()) {
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
cc.log(‘No local manifest file found, hot update skipped…’);
failed = true;
this.txtTips.string = “updateCb:没有发现本地manifest”;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
cc.log(event.getPercent());
cc.log(event.getPercentByFile());
cc.log(event.getDownloadedFiles() + ’ / ’ + event.getTotalFiles());
cc.log(event.getDownloadedBytes() + ’ / ’ + event.getTotalBytes());var msg = event.getMessage(); if (msg) { cc.log('Updated file: ' + msg); this.txtTips.string = "updateCb:正在更新文件,"+msg; } else { this.txtTips.string = "updateCb:正在更新文件,null"; } this.progressByte.progress = event.getPercent(); this.progressFile.progress = event.getPercentByFile(); if (this.txtFileProgress.node.active == false && this.txtByteProgress.node.active == false) { this.txtFileProgress.node.active = true; this.txtByteProgress.node.active = true; } this.txtFileProgress.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles(); this.txtByteProgress.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes(); break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: cc.log('Fail to download manifest file, hot update skipped.'); failed = true; this.txtTips.string = "updateCb:下载远程manifest出错"; break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: cc.log('Already up to date with the latest remote version.'); failed = true; this.txtTips.string = "updateCb:已经更新到最新版本"; break; case jsb.EventAssetsManager.UPDATE_FINISHED: cc.log('Update finished. ' + event.getMessage()); needRestart = true; this.txtTips.string = "updateCb:更新完成"; break; case jsb.EventAssetsManager.UPDATE_FAILED: cc.log('Update failed. ' + event.getMessage()); this._updating = false; this._canRetry = true; this.txtTips.string = "updateCb:更新文件【"+ event.getMessage()+"】失败"; break; case jsb.EventAssetsManager.ERROR_UPDATING: cc.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage()); this.txtTips.string = "updateCb:更新中出错,"+event.getAssetId()+', '+event.getMessage(); break; case jsb.EventAssetsManager.ERROR_DECOMPRESS: cc.log(event.getMessage()); this.txtTips.string = "updateCb:解压失败,"+event.getMessage(); break; default: break; } if (failed) { cc.eventManager.removeListener(this._updateListener); this._updateListener = null; this._updating = false; } if (needRestart) { cc.eventManager.removeListener(this._updateListener); this._updateListener = null; // Prepend the manifest's search path var searchPaths = jsb.fileUtils.getSearchPaths(); var newPaths = this._am.getLocalManifest().getSearchPaths(); cc.log(JSON.stringify(newPaths)); Array.prototype.unshift(searchPaths, newPaths); // This value will be retrieved and appended to the default search path during game startup, // please refer to samples/js-tests/main.js for detailed usage. // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect. cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths)); jsb.fileUtils.setSearchPaths(searchPaths); cc.audioEngine.stopAll(); cc.game.restart(); }},
retry: function () {
if (!this._updating && this._canRetry) {
this._canRetry = false;cc.log('Retry failed Assets...'); this._am.downloadFailedAssets(); }},
checkForUpdate: function () {
cc.log(“start checking…”);
if (this._updating) {
cc.log(‘Checking or updating …’);
return;
}
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
this._am.loadLocalManifest(this.manifestUrl);
cc.log(“loadLocalManifest函数参数(本地校验文件路径):”+this.manifestUrl);
}
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
cc.log(‘Failed to load local manifest …’);
return;
}
this._checkListener = new jsb.EventListenerAssetsManager(this._am, this.checkCb.bind(this));
cc.eventManager.addListener(this._checkListener, 1);this._am.checkUpdate(); this._updating = true;},
hotUpdate: function () {
if (this._am && !this._updating) {
this._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this));
cc.eventManager.addListener(this._updateListener, 1);if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { this._am.loadLocalManifest(this.manifestUrl); } this._failCount = 0; this._am.update(); this._updating = true; }},
show: function () {
// if (this.updateUI.active === false) {
// this.updateUI.active = true;
// }
},// use this for initialization
onLoad: function () {
this.progressFile.progress = 0;
this.progressByte.progress = 0;
this.txtFileProgress.node.active = false;
this.txtByteProgress.node.active = false;
this.txtFileProgress.string = “”;
this.txtByteProgress.string = “”;
this.btnChangeScene.active = false;// Hot update is only available in Native build if (!cc.sys.isNative) { this.btnChangeScene.active = true; return; } this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'xiaoming-remote-asset'); cc.log('Storage path for remote asset : ' + this._storagePath); // Setup your own version compare handler, versionA and B is versions in string // if the return value greater than 0, versionA is greater than B, // if the return value equals 0, versionA equals to B, // if the return value smaller than 0, versionA is smaller than B. this.versionCompareHandle = function (versionA, versionB) { cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB); var vA = versionA.split('.'); var vB = versionB.split('.'); for (var i = 0; i < vA.length; ++i) { var a = parseInt(vA[i]); var b = parseInt(vB[i] || 0); if (a === b) { continue; } else { return a - b; } } if (vB.length > vA.length) { return -1; } else { return 0; } }; // Init with empty manifest url for testing custom manifest this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle); if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) { this._am.retain(); } // Setup the verification callback, but we don't have md5 check function yet, so only print some message // Return true if the verification passed, otherwise return false this._am.setVerifyCallback(function (path, asset) { // When asset is compressed, we don't need to check its md5, because zip file have been deleted. var compressed = asset.compressed; // Retrieve the correct md5 value. var expectedMD5 = asset.md5; // asset.path is relative path and path is absolute. var relativePath = asset.path; // The size of asset file, but this value could be absent. var size = asset.size; if (compressed) { cc.log("Verification passed : " + relativePath); return true; } else { cc.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')'); return true; } }); cc.log("Hot update is ready, please check or directly update."); if (cc.sys.os === cc.sys.OS_ANDROID) { // Some Android device may slow down the download process when concurrent tasks is too much. // The value may not be accurate, please do more test and find what's most suitable for your game. this._am.setMaxConcurrentTask(2); cc.log("Max concurrent tasks count have been limited to 2"); } // this.checkUpdate();},
onDestroy: function () {
if (this._updateListener) {
cc.eventManager.removeListener(this._updateListener);
this._updateListener = null;
}
if (this._am && !cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
this._am.release();
}
}
});
















