Creator v1.9.3热更新问题已解决

creator热更新问题 继续讨论:

Creator v1.9.3热更新
新版本生成 具体步骤:

  1. 先创建新版本,具体如下图,多了一个新场景NewScene:

  2. 构建项目:

  3. 生成对比文件前要做的准备工作,首先你得先安装一下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 这两个文件一样拷贝到你的服务器更新路径下。

  1. 到这里新版本就做完了,更新服务器怎么搭建就不说了吧。

旧版本生成 具体步骤:

  1. 删掉build/jsb-link文件夹; 删除assets/ 下生成的project.manifest、version.manifest;删除场景NewScene以及对于代码和资源(随便你自己怎么弄,有明显UI变动就可以):
  2. 构建项目,配置不要改动:
  3. 修改批处理代码中版本号 改成1.0.0,比你生成的新版本号低就好,然后执行批处理文件 热更新.bat,成功以后你会发现项目assets/ 下面会有两个对比文件:project.manifest、version.manifest。
  4. 把assets/ project.manifest拖入你UI界面上的ManifestUrl上,注意ManifestUrl的类型是raw-asset而不是官方Dome上的asset:
  5. 保存,然后重新构建一次项目。
  6. 构建完成后先不要编译,还有一个重要步骤。
    修改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();
    }
    }
    });

3赞

本人新手又有点逻辑笨,官方的教程对于我而已着实很难懂。只有慢慢的用2d-x的东西带进去观察问题。希望能帮到更多的新手!

还要学JAVA,弄难,一个C++都够用一辈子了

各种学啊 不学就完了