const { ccclass, property } = cc._decorator; @ccclass export default class HotUpdate extends cc.Component { @property({ type: cc.Node }) public txtProgress: cc.Node = null; @property({ type: cc.Node }) public txtProgressTips: cc.Node = null; @property({ type: cc.Node }) public gProgress: cc.Node = null; @property({ type: cc.Node }) public imgRole: cc.Node = null; @property({ type: cc.Node }) public progress: cc.Node = null; @property({ type: cc.Sprite }) public progressBar: cc.Sprite = null; @property({ type: cc.Asset }) public manifestUrl: cc.Asset = null; _am: any; _checkListener: null; _updating: boolean; _updateListener: null; _canRetry: boolean; _failCount: number; _storagePath: string; versionCompareHandle: (versionA: any, versionB: any) => number; private _canEnterGame: boolean; public checkCb(event: any) { // cc.log('Code: ' + event.getEventCode()); let isNewVersion = false; switch (event.getEventCode()) { case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: console.log("No local manifest file found, hot update skipped."); break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: console.log("Fail to download manifest file, hot update skipped."); break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: console.log("Already up to date with the latest remote version."); this.EnterGame(); break; case jsb.EventAssetsManager.NEW_VERSION_FOUND: console.log("New version found, please try to update.(" + this._am.getTotalBytes() + ')'); isNewVersion = true; break; default: return; } this._am.setEventCallback(null); this._checkListener = null; this._updating = false; if (isNewVersion) { //有新版本,显示进度 this.setProgress(0) this.hotUpdate(); //5秒后判断是否开始下载,如果进度为0.直接进游戏,防止卡死 this.scheduleOnce(() => { if (this.progressBar.fillRange == 0) { this.EnterGame() } }, 10) } } setProgress(val: number) { this.gProgress.active = true this.progressBar.fillRange = val this.imgRole.x = -this.progress.width / 2 + val * this.progress.width this.txtProgress.getComponent(cc.Label).string = Math.floor(val * 100) + "%" this.txtProgressTips.getComponent(cc.Label).string = "资源更新中" } public updateCb(event: any) { var needRestart = false; var failed = false; // console.log("updateCb event:"+JSON.stringify(event)); switch (event.getEventCode()) { case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: // console.log('No local manifest file found, hot update skipped.'); failed = true; break; case jsb.EventAssetsManager.UPDATE_PROGRESSION: // this.panel.byteProgress.progress = event.getPercent(); // this.panel.fileProgress.progress = event.getPercentByFile(); // this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles(); // this.panel.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes(); // this.pb_hotUpdate.progress = event.getPercent(); // this.lb_progress.string = Math.floor(event.getPercent()*100).toFixed(2) + '%'; this.setProgress(event.getPercent()) // var msg = event.getMessage(); // if (msg) { // cc.log(event.getPercent()/100 + '% : ' + msg); // } break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: // console.log('Fail to download manifest file, hot update skipped.'); failed = true; break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: // console.log('Already up to date with the latest remote version.'); failed = true; break; case jsb.EventAssetsManager.UPDATE_FINISHED: // console.log('Update finished. ' + event.getMessage()); needRestart = true; break; case jsb.EventAssetsManager.UPDATE_FAILED: // console.log('Update failed. ' + event.getMessage()); break; case jsb.EventAssetsManager.ERROR_UPDATING: // console.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage()); break; case jsb.EventAssetsManager.ERROR_DECOMPRESS: // console.log("ERROR_DECOMPRESS:"+event.getMessage()); break; default: break; } if (failed) { this._am.setEventCallback(null); this._updateListener = null; this._updating = false; this.EnterGame(); } if (needRestart) { this._am.setEventCallback(null); this._updateListener = null; // Prepend the manifest's search path var searchPaths = jsb.fileUtils.getSearchPaths(); var newPaths = this._am.getLocalManifest().getSearchPaths(); // console.log(JSON.stringify(newPaths)); for (var i = 0; i < newPaths.length; i++) { if (searchPaths.indexOf(newPaths[i]) == -1) { Array.prototype.unshift.apply(searchPaths, [newPaths[i]]); } } // 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(); } } public retry() { if (!this._updating && this._canRetry) { this._canRetry = false; // console.log('Retry failed Assets...'); this._am.downloadFailedAssets(); } } public checkUpdate() { if (this._updating) { // console.log('Checking or updating ...'); return; } if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { // Resolve md5 url var url = this.manifestUrl.nativeUrl; if (cc.loader.md5Pipe) { url = cc.loader.md5Pipe.transformURL(url); } this._am.loadLocalManifest(url); } if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) { console.log('Failed to load local manifest ...'); this.EnterGame(); return; } this.getLocalVersion() this._am.setEventCallback(this.checkCb.bind(this)); this._am.checkUpdate(); this._updating = true; } public hotUpdate() { if (this._am && !this._updating) { this._am.setEventCallback(this.updateCb.bind(this)); if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { // Resolve md5 url var url = this.manifestUrl.nativeUrl; if (cc.loader.md5Pipe) { url = cc.loader.md5Pipe.transformURL(url); } this._am.loadLocalManifest(url); } this._failCount = 0; this._am.update(); this._updating = true; } } // use this for initialization onLoad() { // Hot update is only available in Native build if (!cc.sys.isNative) { // this.EnterGame(); return; } this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-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); // 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) { // console.log("Verification passed : " + relativePath); return true; } else { // console.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')'); return true; } }); // // console.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); // console.log("Max concurrent tasks count have been limited to 2"); } // cc.loader.loadRes('/project.manifest',(err,obj)=>{ // if (!err) { // // let customManifestStr = JSON.stringify(obj); // let _nativeAsset = JSON.parse(obj._nativeAsset); // var customManifestStr = JSON.stringify({ // 'packageUrl': _nativeAsset.packageUrl, // 'remoteManifestUrl': _nativeAsset.remoteManifestUrl, // 'remoteVersionUrl': _nativeAsset.remoteManifestUrl, // 'version': _nativeAsset.version, // 'assets': _nativeAsset.assert, // 'searchPaths': _nativeAsset.searchPaths // }); // if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { // var manifest = new jsb.Manifest(customManifestStr, this._storagePath); // this._am.loadLocalManifest(manifest, this._storagePath); // } // } // }); //开始检测更新 // this.checkUpdate(); } public EnterGame() { // 游戏初始化 this.getLocalVersion(); this.scheduleOnce(() => { cc.director.loadScene("loading") }, 4) this._canEnterGame = true } //获取本地版本 public getLocalVersion() { let localVersion = ''; if (this._am && this._am.getLocalManifest() && !this._updating) { localVersion = this._am.getLocalManifest().getVersion(); console.log("curVersion1:" + localVersion); import("Config" as any).then((cfgParam) => { let config = cfgParam.default config.appVersion = localVersion console.log("curVersion2:" + localVersion); }) // console.log("curVersion:"+localVersion); } //return localVersion; } onDestroy() { if (this._updateListener) { this._am.setEventCallback(null); this._updateListener = null; } } };