HotUpdate.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. const { ccclass, property } = cc._decorator;
  2. @ccclass
  3. export default class HotUpdate extends cc.Component {
  4. @property({ type: cc.Node })
  5. public txtProgress: cc.Node = null;
  6. @property({ type: cc.Node })
  7. public txtProgressTips: cc.Node = null;
  8. @property({ type: cc.Node })
  9. public gProgress: cc.Node = null;
  10. @property({ type: cc.Node })
  11. public imgRole: cc.Node = null;
  12. @property({ type: cc.Node })
  13. public progress: cc.Node = null;
  14. @property({ type: cc.Sprite })
  15. public progressBar: cc.Sprite = null;
  16. @property({ type: cc.Asset })
  17. public manifestUrl: cc.Asset = null;
  18. _am: any;
  19. _checkListener: null;
  20. _updating: boolean;
  21. _updateListener: null;
  22. _canRetry: boolean;
  23. _failCount: number;
  24. _storagePath: string;
  25. versionCompareHandle: (versionA: any, versionB: any) => number;
  26. private _canEnterGame: boolean;
  27. public checkCb(event: any) {
  28. // cc.log('Code: ' + event.getEventCode());
  29. let isNewVersion = false;
  30. switch (event.getEventCode()) {
  31. case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
  32. console.log("No local manifest file found, hot update skipped.");
  33. break;
  34. case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
  35. case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
  36. console.log("Fail to download manifest file, hot update skipped.");
  37. break;
  38. case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
  39. console.log("Already up to date with the latest remote version.");
  40. this.EnterGame();
  41. break;
  42. case jsb.EventAssetsManager.NEW_VERSION_FOUND:
  43. console.log("New version found, please try to update.(" + this._am.getTotalBytes() + ')');
  44. isNewVersion = true;
  45. break;
  46. default:
  47. return;
  48. }
  49. this._am.setEventCallback(null);
  50. this._checkListener = null;
  51. this._updating = false;
  52. if (isNewVersion) {
  53. //有新版本,显示进度
  54. this.setProgress(0)
  55. this.hotUpdate();
  56. //5秒后判断是否开始下载,如果进度为0.直接进游戏,防止卡死
  57. this.scheduleOnce(() => {
  58. if (this.progressBar.fillRange == 0) {
  59. this.EnterGame()
  60. }
  61. }, 10)
  62. }
  63. }
  64. setProgress(val: number) {
  65. this.gProgress.active = true
  66. this.progressBar.fillRange = val
  67. this.imgRole.x = -this.progress.width / 2 + val * this.progress.width
  68. this.txtProgress.getComponent(cc.Label).string = Math.floor(val * 100) + "%"
  69. this.txtProgressTips.getComponent(cc.Label).string = "资源更新中"
  70. }
  71. public updateCb(event: any) {
  72. var needRestart = false;
  73. var failed = false;
  74. // console.log("updateCb event:"+JSON.stringify(event));
  75. switch (event.getEventCode()) {
  76. case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
  77. // console.log('No local manifest file found, hot update skipped.');
  78. failed = true;
  79. break;
  80. case jsb.EventAssetsManager.UPDATE_PROGRESSION:
  81. // this.panel.byteProgress.progress = event.getPercent();
  82. // this.panel.fileProgress.progress = event.getPercentByFile();
  83. // this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
  84. // this.panel.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes();
  85. // this.pb_hotUpdate.progress = event.getPercent();
  86. // this.lb_progress.string = Math.floor(event.getPercent()*100).toFixed(2) + '%';
  87. this.setProgress(event.getPercent())
  88. // var msg = event.getMessage();
  89. // if (msg) {
  90. // cc.log(event.getPercent()/100 + '% : ' + msg);
  91. // }
  92. break;
  93. case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
  94. case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
  95. // console.log('Fail to download manifest file, hot update skipped.');
  96. failed = true;
  97. break;
  98. case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
  99. // console.log('Already up to date with the latest remote version.');
  100. failed = true;
  101. break;
  102. case jsb.EventAssetsManager.UPDATE_FINISHED:
  103. // console.log('Update finished. ' + event.getMessage());
  104. needRestart = true;
  105. break;
  106. case jsb.EventAssetsManager.UPDATE_FAILED:
  107. // console.log('Update failed. ' + event.getMessage());
  108. break;
  109. case jsb.EventAssetsManager.ERROR_UPDATING:
  110. // console.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
  111. break;
  112. case jsb.EventAssetsManager.ERROR_DECOMPRESS:
  113. // console.log("ERROR_DECOMPRESS:"+event.getMessage());
  114. break;
  115. default:
  116. break;
  117. }
  118. if (failed) {
  119. this._am.setEventCallback(null);
  120. this._updateListener = null;
  121. this._updating = false;
  122. this.EnterGame();
  123. }
  124. if (needRestart) {
  125. this._am.setEventCallback(null);
  126. this._updateListener = null;
  127. // Prepend the manifest's search path
  128. var searchPaths = jsb.fileUtils.getSearchPaths();
  129. var newPaths = this._am.getLocalManifest().getSearchPaths();
  130. // console.log(JSON.stringify(newPaths));
  131. for (var i = 0; i < newPaths.length; i++) {
  132. if (searchPaths.indexOf(newPaths[i]) == -1) {
  133. Array.prototype.unshift.apply(searchPaths, [newPaths[i]]);
  134. }
  135. }
  136. // This value will be retrieved and appended to the default search path during game startup,
  137. // please refer to samples/js-tests/main.js for detailed usage.
  138. // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
  139. cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
  140. jsb.fileUtils.setSearchPaths(searchPaths);
  141. cc.audioEngine.stopAll();
  142. cc.game.restart();
  143. }
  144. }
  145. public retry() {
  146. if (!this._updating && this._canRetry) {
  147. this._canRetry = false;
  148. // console.log('Retry failed Assets...');
  149. this._am.downloadFailedAssets();
  150. }
  151. }
  152. public checkUpdate() {
  153. if (this._updating) {
  154. // console.log('Checking or updating ...');
  155. return;
  156. }
  157. if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  158. // Resolve md5 url
  159. var url = this.manifestUrl.nativeUrl;
  160. if (cc.loader.md5Pipe) {
  161. url = cc.loader.md5Pipe.transformURL(url);
  162. }
  163. this._am.loadLocalManifest(url);
  164. }
  165. if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
  166. console.log('Failed to load local manifest ...');
  167. this.EnterGame();
  168. return;
  169. }
  170. this.getLocalVersion()
  171. this._am.setEventCallback(this.checkCb.bind(this));
  172. this._am.checkUpdate();
  173. this._updating = true;
  174. }
  175. public hotUpdate() {
  176. if (this._am && !this._updating) {
  177. this._am.setEventCallback(this.updateCb.bind(this));
  178. if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  179. // Resolve md5 url
  180. var url = this.manifestUrl.nativeUrl;
  181. if (cc.loader.md5Pipe) {
  182. url = cc.loader.md5Pipe.transformURL(url);
  183. }
  184. this._am.loadLocalManifest(url);
  185. }
  186. this._failCount = 0;
  187. this._am.update();
  188. this._updating = true;
  189. }
  190. }
  191. // use this for initialization
  192. onLoad() {
  193. // Hot update is only available in Native build
  194. if (!cc.sys.isNative) {
  195. // this.EnterGame();
  196. return;
  197. }
  198. this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-remote-asset');
  199. cc.log('Storage path for remote asset : ' + this._storagePath);
  200. // Setup your own version compare handler, versionA and B is versions in string
  201. // if the return value greater than 0, versionA is greater than B,
  202. // if the return value equals 0, versionA equals to B,
  203. // if the return value smaller than 0, versionA is smaller than B.
  204. this.versionCompareHandle = function (versionA, versionB) {
  205. cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
  206. var vA = versionA.split('.');
  207. var vB = versionB.split('.');
  208. for (var i = 0; i < vA.length; ++i) {
  209. var a = parseInt(vA[i]);
  210. var b = parseInt(vB[i] || 0);
  211. if (a === b) {
  212. continue;
  213. }
  214. else {
  215. return a - b;
  216. }
  217. }
  218. if (vB.length > vA.length) {
  219. return -1;
  220. }
  221. else {
  222. return 0;
  223. }
  224. };
  225. // Init with empty manifest url for testing custom manifest
  226. this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle);
  227. // Setup the verification callback, but we don't have md5 check function yet, so only print some message
  228. // Return true if the verification passed, otherwise return false
  229. this._am.setVerifyCallback(function (path, asset) {
  230. // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
  231. var compressed = asset.compressed;
  232. // Retrieve the correct md5 value.
  233. var expectedMD5 = asset.md5;
  234. // asset.path is relative path and path is absolute.
  235. var relativePath = asset.path;
  236. // The size of asset file, but this value could be absent.
  237. var size = asset.size;
  238. if (compressed) {
  239. // console.log("Verification passed : " + relativePath);
  240. return true;
  241. }
  242. else {
  243. // console.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')');
  244. return true;
  245. }
  246. });
  247. // // console.log('Hot update is ready, please check or directly update.');
  248. if (cc.sys.os === cc.sys.OS_ANDROID) {
  249. // Some Android device may slow down the download process when concurrent tasks is too much.
  250. // The value may not be accurate, please do more test and find what's most suitable for your game.
  251. this._am.setMaxConcurrentTask(2);
  252. // console.log("Max concurrent tasks count have been limited to 2");
  253. }
  254. // cc.loader.loadRes('/project.manifest',(err,obj)=>{
  255. // if (!err) {
  256. // // let customManifestStr = JSON.stringify(obj);
  257. // let _nativeAsset = JSON.parse(obj._nativeAsset);
  258. // var customManifestStr = JSON.stringify({
  259. // 'packageUrl': _nativeAsset.packageUrl,
  260. // 'remoteManifestUrl': _nativeAsset.remoteManifestUrl,
  261. // 'remoteVersionUrl': _nativeAsset.remoteManifestUrl,
  262. // 'version': _nativeAsset.version,
  263. // 'assets': _nativeAsset.assert,
  264. // 'searchPaths': _nativeAsset.searchPaths
  265. // });
  266. // if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  267. // var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
  268. // this._am.loadLocalManifest(manifest, this._storagePath);
  269. // }
  270. // }
  271. // });
  272. //开始检测更新
  273. // this.checkUpdate();
  274. }
  275. public EnterGame() {
  276. // 游戏初始化
  277. this.getLocalVersion();
  278. this.scheduleOnce(() => {
  279. cc.director.loadScene("loading")
  280. }, 4)
  281. this._canEnterGame = true
  282. }
  283. //获取本地版本
  284. public getLocalVersion() {
  285. let localVersion = '';
  286. if (this._am && this._am.getLocalManifest() && !this._updating) {
  287. localVersion = this._am.getLocalManifest().getVersion();
  288. console.log("curVersion1:" + localVersion);
  289. import("Config" as any).then((cfgParam) => {
  290. let config = cfgParam.default
  291. config.appVersion = localVersion
  292. console.log("curVersion2:" + localVersion);
  293. })
  294. // console.log("curVersion:"+localVersion);
  295. }
  296. //return localVersion;
  297. }
  298. onDestroy() {
  299. if (this._updateListener) {
  300. this._am.setEventCallback(null);
  301. this._updateListener = null;
  302. }
  303. }
  304. };