HotUpdate.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. const {ccclass, property} = cc._decorator;
  2. /**
  3. * 热更新
  4. */
  5. @ccclass
  6. export default class HotUpdate extends cc.Component {
  7. @property(cc.Asset)
  8. public manifestUrl:cc.Asset = null;
  9. @property(cc.Node)
  10. public gxNode:cc.Node = null;//更新load
  11. // @property(cc.Label)
  12. // public mInfo:cc.Label = null;
  13. @property(cc.Node)
  14. public mMyNode:cc.Node = null;
  15. @property(cc.Label)
  16. public mV1:cc.Label = null;
  17. @property(cc.Label)
  18. public mV2:cc.Label = null;
  19. @property(cc.Label)
  20. public mLoadBfb:cc.Label = null;
  21. @property(cc.Label)
  22. public fileCount:cc.Label = null;//文件数量
  23. @property(cc.ProgressBar)
  24. public byteProgress:cc.ProgressBar = null;//下载进度
  25. // @property(cc.Node)
  26. // mLogContent:cc.Node = null;//更新日志
  27. private _updating:boolean = false;
  28. private _am:jsb.AssetsManager = null;//jsb.AssetsManager
  29. private _canRetry:boolean = false;//是否可以重新下载
  30. private _storagePath:string;//存储路径
  31. private versionCompareHandle: (versionA: string, versionB: string) => number = null!;
  32. public login(){
  33. if(cc.sys.isNative){
  34. // cc.director.loadScene('anysdkLogin');
  35. cc.director.loadScene('main');
  36. }else{
  37. cc.director.loadScene('main');
  38. }
  39. }
  40. public exitGame(){
  41. cc.game.end();
  42. }
  43. public onLoad(){
  44. let proto = cc.ProgressBar.prototype;
  45. let updateBarStatus = proto['_updateBarStatus'];
  46. proto['_updateBarStatus'] = function () {
  47. if (this.progress == 0) {
  48. this.progress = 0.001;
  49. }
  50. updateBarStatus.bind(this)();
  51. }
  52. this.mV1.node.active = false
  53. this.mV2.node.active = false
  54. cc.game.setFrameRate(35);
  55. this.gxNode.active = false;
  56. if (!cc.sys.isNative) {
  57. this.login();
  58. return;
  59. }
  60. this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'mmtg-remote-asset');
  61. cc.log('Storage path for remote asset : ' + this._storagePath);
  62. let myself = this;
  63. this.versionCompareHandle = function (versionA: string, versionB: string) {
  64. console.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
  65. myself.mV1.node.active = true
  66. myself.mV2.node.active = true
  67. myself.mV1.string = '客户端版本:'+versionA
  68. myself.mV2.string = '服务器版本:'+versionB
  69. var vA = versionA.split('.');
  70. var vB = versionB.split('.');
  71. for (var i = 0; i < vA.length; ++i) {
  72. var a = parseInt(vA[i]);
  73. var b = parseInt(vB[i] || '0');
  74. if (a === b) {
  75. continue;
  76. }
  77. else {
  78. return a - b;
  79. }
  80. }
  81. if (vB.length > vA.length) {
  82. return -1;
  83. }
  84. else {
  85. return 0;
  86. }
  87. };
  88. // Init with empty manifest url for testing custom manifest
  89. this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle);
  90. // Setup the verification callback, but we don't have md5 check function yet, so only print some message
  91. // Return true if the verification passed, otherwise return false
  92. this._am.setVerifyCallback((path, asset)=> {
  93. // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
  94. var compressed = asset.compressed;
  95. // Retrieve the correct md5 value.
  96. var expectedMD5 = asset.md5;
  97. // asset.path is relative path and path is absolute.
  98. var relativePath = asset.path;
  99. // The size of asset file, but this value could be absent.
  100. var size = asset.size;
  101. if (compressed) {
  102. // this.mInfo.string = "Verification passed : " + relativePath;
  103. return true;
  104. }else {
  105. // this.mInfo.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
  106. // if(!jsb.fileUtils.isFileExist(path)){
  107. // return false;
  108. // }
  109. // let dataBinary = jsb.fileUtils.getDataFromFile(path);
  110. // let buffer = nodeJsBufferTool.from(dataBinary);
  111. // let md5Code = crypto.createHash('md5').update(buffer).digest('hex');
  112. // if(md5Code !== asset.md5){
  113. // cc.log('不一致文件:',path);
  114. // cc.log('md5Code = ',md5Code);
  115. // cc.log('asset = ',asset.md5);
  116. // // cc.log('dataBinary : ',dataBinary);
  117. // // cc.log('strData : ',strData);
  118. // return false;
  119. // }
  120. // cc.log('一致文件 :',path);
  121. // return true;
  122. // this.mInfo.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
  123. if(!jsb.fileUtils.isFileExist(path)){
  124. return false;
  125. }
  126. let fileSize = jsb.fileUtils.getFileSize(path);
  127. if(fileSize != asset.size){
  128. return false;
  129. }
  130. return true;
  131. }
  132. });
  133. // this.mInfo.string = 'Hot update is ready, please check or directly update.';
  134. if (cc.sys.os === cc.sys.OS_ANDROID) {
  135. // Some Android device may slow down the download process when concurrent tasks is too much.
  136. // The value may not be accurate, please do more test and find what's most suitable for your game.
  137. this._am.setMaxConcurrentTask(10);
  138. // this.mInfo.string = "Max concurrent tasks count have been limited to 2";
  139. }else{
  140. this._am.setMaxConcurrentTask(15);
  141. }
  142. this.fileCount.string = '';
  143. this.mLoadBfb.string = '';
  144. this.byteProgress.progress = 0;
  145. //判断是否需要下载新包更新
  146. //1.获取app版本
  147. //2.获取服务器最新版本
  148. //3.打开下载页
  149. // if (cc.sys.os === cc.sys.OS_ANDROID) {
  150. // this.getServerAppVersion((v1)=>{
  151. // if(v1){
  152. // let v2 = this.getAppVersion();
  153. // let state = this.versionCompareHandle(v1,v2);
  154. // if(state > 0){
  155. // //如果有新版本
  156. // this.updateBtn.active = true;
  157. // }else{
  158. // this.checkUpdate();
  159. // }
  160. // }else{
  161. // this.checkUpdate();
  162. // }
  163. // });
  164. // }else{
  165. // this.checkUpdate();
  166. // }
  167. this.checkUpdate();
  168. }
  169. public checkUpdate() {
  170. if (this._updating) {
  171. // cc.log('Checking or updating ...');
  172. this.pushLog('Checking or updating ...');
  173. return;
  174. }
  175. if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  176. var url = this.manifestUrl.nativeUrl;
  177. this._am.loadLocalManifest(url);
  178. }
  179. if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
  180. // cc.log('Failed to load local manifest ...');
  181. this.pushLog('Failed to load local manifest ...');
  182. return;
  183. }
  184. this._am.setEventCallback(this.checkCb.bind(this));
  185. this._am.checkUpdate();
  186. this._updating = true;
  187. }
  188. public hotUpdate () {
  189. this.gxNode.active = true;
  190. this.fileCount.string = 'loading...';
  191. if (this._am && !this._updating) {
  192. this._am.setEventCallback(this.updateCb.bind(this));
  193. if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  194. // Resolve md5 url
  195. var url = this.manifestUrl;
  196. if (cc.loader.md5Pipe) {
  197. url = cc.loader.md5Pipe.transformURL(url);
  198. }
  199. this._am.loadLocalManifest(url);
  200. }
  201. this._am.update();
  202. this._updating = true;
  203. }
  204. }
  205. public checkCb(event){
  206. cc.log('Code: ' + event.getEventCode());
  207. switch (event.getEventCode())
  208. {
  209. case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
  210. cc.log("No local manifest file found, hot update skipped.");
  211. this.login();
  212. break;
  213. case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
  214. case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
  215. cc.log("Fail to download manifest file, hot update skipped.");
  216. this.login();
  217. break;
  218. case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
  219. cc.log("Already up to date with the latest remote version.");
  220. this.login();
  221. break;
  222. case jsb.EventAssetsManager.NEW_VERSION_FOUND:
  223. cc.log("New version found, please try to update.");
  224. // this.panel.fileProgress.progress = 0;
  225. // this.panel.byteProgress.progress = 0;
  226. // this.updateBtn.active = true;
  227. // cc.log('getTotalBytes : ',event.getTotalBytes());
  228. this._am.setEventCallback(null);
  229. this._updating = false;
  230. this.hotUpdate();
  231. break;
  232. default:
  233. return;
  234. }
  235. }
  236. public updateCb(event) {
  237. var needRestart = false;
  238. var failed = false;
  239. switch (event.getEventCode())
  240. {
  241. case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
  242. cc.log('No local manifest file found, hot update skipped.');
  243. failed = true;
  244. break;
  245. case jsb.EventAssetsManager.UPDATE_PROGRESSION:
  246. // this.byteProgress.progress = event.getPercent();
  247. let percentByFile = event.getPercentByFile();
  248. if(!percentByFile){
  249. percentByFile = 0;
  250. }else{
  251. if(event.getDownloadedFiles() < event.getTotalFiles()-1){
  252. this.fileCount.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
  253. }else{
  254. this.fileCount.string = '文件解压中...';
  255. }
  256. }
  257. this.byteProgress.progress = percentByFile;
  258. this.mMyNode.x = 915*percentByFile
  259. // if(event.getPercent()){
  260. // this.mLoadBfb.string = Math.floor(event.getPercentByFile()*100)+'%';
  261. // }
  262. // this.mLoadBfb.string = Math.floor(percentByFile*100)+'%';
  263. // var msg = event.getMessage();
  264. // if (msg) {
  265. // // this.panel.info.string = 'Updated file: ' + msg;
  266. // cc.log(event.getPercent()/100 + '% : ' + msg);
  267. // }
  268. break;
  269. case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
  270. case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
  271. cc.log('Fail to download manifest file, hot update skipped.');
  272. failed = true;
  273. break;
  274. case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
  275. cc.log('Already up to date with the latest remote version.');
  276. failed = true;
  277. break;
  278. case jsb.EventAssetsManager.UPDATE_FINISHED:
  279. this.pushLog('Update finished. ' + event.getMessage());
  280. needRestart = true;
  281. break;
  282. case jsb.EventAssetsManager.UPDATE_FAILED:
  283. this.pushLog('Update failed. ' + event.getMessage());
  284. // this.mInfo.string = '更新失败,是否重试?';
  285. this._updating = false;
  286. this._canRetry = true;
  287. this.retry();
  288. // this.updateBtn.active = true;
  289. // needRestart = true;
  290. // this.hotUpdate();
  291. break;
  292. case jsb.EventAssetsManager.ERROR_UPDATING:
  293. this.pushLog('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
  294. // needRestart = true;
  295. // this.hotUpdate();
  296. break;
  297. case jsb.EventAssetsManager.ERROR_DECOMPRESS:
  298. cc.log(event.getMessage());
  299. needRestart = true;
  300. break;
  301. default:
  302. break;
  303. }
  304. if (failed) {
  305. this._am.setEventCallback(null);
  306. this._updating = false;
  307. this.login();
  308. }
  309. if (needRestart) {
  310. this._am.setEventCallback(null);
  311. // Prepend the manifest's search path
  312. var searchPaths = jsb.fileUtils.getSearchPaths();
  313. var newPaths = this._am.getLocalManifest().getSearchPaths();
  314. cc.log(JSON.stringify(newPaths));
  315. Array.prototype.unshift.apply(searchPaths, newPaths);
  316. // This value will be retrieved and appended to the default search path during game startup,
  317. // please refer to samples/js-tests/main.js for detailed usage.
  318. // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
  319. cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
  320. jsb.fileUtils.setSearchPaths(searchPaths);
  321. cc.audioEngine.stopAll();
  322. setTimeout(() => {
  323. cc.game.restart();
  324. }, 1000)
  325. }
  326. }
  327. // public loadCustomManifest() {
  328. // if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
  329. // var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
  330. // this._am.loadLocalManifest(manifest, this._storagePath);
  331. // this.panel.info.string = 'Using custom manifest';
  332. // }
  333. // }
  334. public retry(){
  335. if (!this._updating && this._canRetry) {
  336. this._canRetry = false;
  337. // cc.log('Retry failed Assets...');
  338. this.pushLog('Retry failed Assets...');
  339. this._am.downloadFailedAssets();
  340. }
  341. }
  342. public onDestroy () {
  343. if (this._am) {
  344. this._am.setEventCallback(null);
  345. }
  346. }
  347. public pushLog(msg){
  348. // let node = new cc.Node();
  349. // node.anchorX = 0;
  350. // node.height = 32;
  351. // node.color = cc.color(0,0,0);
  352. // let log = node.addComponent(cc.Label)
  353. // log.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
  354. // log.string = msg;
  355. // log.node.parent = this.mLogContent;
  356. }
  357. public onclickCancel(){
  358. this.checkUpdate();
  359. }
  360. }