cc.Class({ extends: cc.Component, properties: { manifestUrl: { type: cc.Asset, default: null }, _updating: false, _canRetry: false, _storagePath: '', updateFinish: false, percent: 100 }, checkCb: function (event) { console.log('checkCb ') switch (event.getEventCode()) { case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: console.log('checkCb No local manifest file found, hot update skipped.') this.updateFinish = true break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: console.log('checkCb ERROR_DOWNLOAD_MANIFEST') this.updateFinish = true break case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: console.log('checkCb Fail to download manifest file, hot update skipped.') this.updateFinish = true break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: console.log('checkCb ALREADY_UP_TO_DATE ') this.updateFinish = true break; case jsb.EventAssetsManager.NEW_VERSION_FOUND: this.percent = 0 setTimeout(() => { this.hotUpdate() }, 500); break; default: return; } // this._am.setEventCallback(null); // 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: failed = true; this.updateFinish = true break; case jsb.EventAssetsManager.UPDATE_PROGRESSION: console.log('checkCb getDownloadedFiles/getTotalFiles ', event.getDownloadedFiles(), "/", event.getTotalFiles()) // console.log('checkCb getDownloadedBytes/getTotalBytes >> ', event.getDownloadedBytes(), "/", event.getTotalBytes()) let progress = 0 if (event.getTotalBytes() != 0) { progress = (event.getDownloadedBytes() / event.getTotalBytes()).toFixed(2) } this.percent = Math.floor(progress * 100) break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: this.updateFinish = true failed = true; break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: this.updateFinish = true failed = true; break; case jsb.EventAssetsManager.UPDATE_FINISHED: needRestart = true; break; case jsb.EventAssetsManager.UPDATE_FAILED: this._updating = false; this._canRetry = true; setTimeout(() => { this.checkUpdate() }, 300); break; case jsb.EventAssetsManager.ERROR_UPDATING: break; case jsb.EventAssetsManager.ERROR_DECOMPRESS: break; default: break; } if (failed) { this._am.setEventCallback(null); this._updateListener = null; this._updating = false; } if (needRestart) { this._am.setEventCallback(null); this._updateListener = null; // Prepend the manifest's search path var searchPaths = jsb.fileUtils.getSearchPaths(); console.log('MMM searchPaths ', searchPaths) var newPaths = this._am.getLocalManifest().getSearchPaths(); console.log('MMM newPaths ', newPaths) Array.prototype.unshift.apply(searchPaths, newPaths); console.log('MMM searchPaths new ', searchPaths) // 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; this._am.downloadFailedAssets(); } }, checkUpdate: function () { if (this._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); } console.log("loadLocalManifest url ", url) this._am.loadLocalManifest(url); } if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) { return; } this._am.setEventCallback(this.checkCb.bind(this)); console.log('checkUpdate ') this._am.checkUpdate(); this._updating = true; }, hotUpdate: function () { 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: function () { // Hot update is only available in Native build console.log('HotUpdate is onLoad'); if (!cc.sys.isNative) { return; } this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-remote-asset'); console.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) { console.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) { return true; } else { return true; } }); 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); } this.percent = 100; }, onDestroy: function () { if (this._updateListener) { this._am.setEventCallback(null); this._updateListener = null; } } });