Cocos3.15 js 熱更新實(shí)踐

沒想到我上篇博文還在寫作中阅仔,cocos就出了新版3.15,好像對(duì)音頻播放做了優(yōu)化弧械,挺好的八酒,但是熱更新也修改了點(diǎn),好吧刃唐,其實(shí)我之前修改了它的熱更新機(jī)制丘跌,實(shí)現(xiàn)序列化更新,就是1.0.0 更新到1.0.1然后再更新到1.0.2唁桩。好吧,這個(gè)方法的缺陷就是不能刪除以前不要的文件耸棒。但是cocos自己的新的更新機(jī)制也沒有去刪除不要的文件啊荒澡,-_-,看下面的代碼:

if (diff.type != Manifest::DiffType::DELETED)
{
        std::string path = diff.asset.path;
        DownloadUnit unit;
        unit.customId = it->first;
        unit.srcUrl = packageUrl + path;
        unit.storagePath = _tempStoragePath + path;
        unit.size = diff.asset.size;
        _downloadUnits.emplace(unit.customId, unit);
        _tempManifest->setAssetDownloadState(it->first,Manifest::DownloadState::UNSTARTED);
 }

唯一用到Manifest::DiffType::DELETED的地方。就是如果我們的asset資源如果不是刪除狀態(tài)与殃,那么我們記錄需要下載单山。

3.15下載文件如果需要解壓縮,那么直接解壓縮幅疼,3.14.1還是添加到一個(gè)待解壓縮列表米奸,于是我在3.14.1中做了個(gè)修改,讓他們安裝順序填入map中爽篷,然后依次解壓縮悴晰,后面解壓的覆蓋前面,這樣我們就實(shí)現(xiàn)了跨版本序列化更新≌∠可是3.15的改動(dòng)導(dǎo)致我得重新修改很多代碼漂辐。

cocos的本意是所有的src和res的每個(gè)文件都是單獨(dú)的assets資源,這樣可以改動(dòng)棕硫,或添加的文件就會(huì)下載更新髓涯,但是這樣的話,打包更新包麻煩哈扮,每個(gè)文件都需要單獨(dú)打包纬纪,就算寫腳本處理也覺得不大靠譜,更何況之前遇到的游戲更新滑肉,大家都是一個(gè)版本一個(gè)版本的更新上去的包各,并不是直接跳到某個(gè)版本,亦或是我覺得這樣更新不爽吧赦邻,我覺得還是一個(gè)版本一個(gè)版本更新舒服一點(diǎn)髓棋。哈哈。惶洲。按声。

好,那我們先開始修改源碼恬吕。

找到 AssetsManagerEx.cpp:

改動(dòng)1

添加代碼:

#define MAX_DOWNLOAD_TASK 1
改動(dòng)2
network::DownloaderHints hints =
    {
        static_cast<uint32_t>(_maxConcurrentTaskK),
        DEFAULT_CONNECTION_TIMEOUT,
        ".tmp"
    };

改成

network::DownloaderHints hints =
    {
        static_cast<uint32_t>(/*_maxConcurrentTask*/MAX_DOWNLOAD_TASK),
        DEFAULT_CONNECTION_TIMEOUT,
        ".tmp"
    };
改動(dòng)3
while (_currConcurrentTask < _maxConcurrentTask && _queue.size() > 0)

改成

while (_currConcurrentTask < /*_maxConcurrentTask*/MAX_DOWNLOAD_TASK && _queue.size() > 0)

意思很簡(jiǎn)單签则,我們下載任務(wù)只執(zhí)行一個(gè)!n砹稀渐裂!

c++代碼修改完成。
那我們先下載Tomcat钠惩,安裝完成后柒凉,打開開始菜單的

開始菜單

如果你能看到下面的圖片:

Apache歡迎頁(yè)

說(shuō)明你已經(jīng)安裝成功了。好先放一邊篓跛。膝捞。。

創(chuàng)建一個(gè)cocos js工程愧沟,

  1. 下載安裝cocos3.15蔬咬。
  2. 下載安裝python2.7
  3. 打開cocos3.15環(huán)境沐寺,我的是:


    cocos3.15安裝環(huán)境
  4. 執(zhí)行setup.py林艘,根據(jù)提示,部署環(huán)境混坞。
  5. 運(yùn)行cmd狐援,執(zhí)行腳本cocos new -p com.hangzhou.test -d D:\xxx -l js ,最好事先創(chuàng)建目錄xxx目錄,xxx是你希望項(xiàng)目存放的目錄
  6. 下載安裝webstorm
  7. 用webstorm打開我們的xxx目錄。

修改我們的代碼

在src目錄新建兩個(gè)文件:updateList.js和assetsManagerScene.js文件

文件內(nèi)容是:

updateList.js 用于保存我們的更新js列表
var jsUpdateList = [
    "src/app.js",
    "src/resource.js"
];
assetsManagerScene.js 熱更新場(chǎng)景
/**
 * 熱更新
 */

var failCount = 0;

var maxFailCount = 0;   //最大錯(cuò)誤重試次數(shù)

/**

 * 自動(dòng)更新js和資源

 */

var AssetsManagerLoaderScene = cc.Scene.extend({

    _am: null,

    _progress: null,

    _percent: 0,

    run: function () {

        if (!cc.sys.isNative) {

            this.loadGame();

            return;

        }

        var layer = new cc.Layer();

        this.addChild(layer);

        this._progress = new cc.LabelTTF.create("update____0%", "Arial", 12);

        this._progress.x = cc.winSize.width / 2;

        this._progress.y = cc.winSize.height / 2 + 50;

        layer.addChild(this._progress);

        var storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./");

        cc.log("storagePath is " + storagePath);

        this._am = new jsb.AssetsManager("res/Manifests/project.manifest", storagePath);

        this._am.retain();

        if (!this._am.getLocalManifest().isLoaded()) {

            cc.log("Fail to update assets, step skipped.");
            this.loadGame();

        } else {

            var that = this;

            //cc.EventListenerAssetsManager

            var listener = new jsb.EventListenerAssetsManager(this._am, function (event) {

                switch (event.getEventCode()) {

                    case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:

                        cc.log("assetsManagerScene : ERROR_NO_LOCAL_MANIFEST  " + event.getMessage());
                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                        cc.log("assetsManagerScene : ERROR_DOWNLOAD_MANIFEST  " + event.getMessage());

                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:

                        cc.log("assetsManagerScene : ERROR_PARSE_MANIFEST  " + event.getMessage());

                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                        cc.log("assetsManagerScene : NEW_VERSION_FOUND " + event.getMessage());

                        //我們需要更新咕村。场钉。。
                        break;
                    case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:

                        cc.log("assetsManagerScene : ALREADY_UP_TO_DATE " + event.getMessage());
                        //我們不需要更新懈涛。逛万。。
                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.UPDATE_PROGRESSION://更新進(jìn)度條

                        cc.log("assetsManagerScene : UPDATE_PROGRESSION " + event.getPercent() + "," + event.getMessage());
                        that._percent = event.getPercent();
                        var msg = event.getMessage();
                        if (msg) {
                            cc.log(msg);
                        }
                        that.updateProgress(event.getPercent());

                        break;
                    case jsb.EventAssetsManager.ASSET_UPDATED://資源更新完畢批钠,可能還需要解壓縮
                        cc.log("assetsManagerScene : ASSET_UPDATED " + event.getAssetId() + "," + event.getMessage());
                        break;
                    case jsb.EventAssetsManager.ERROR_UPDATING://更新發(fā)生錯(cuò)誤

                        cc.log("assetsManagerScene : ERROR_UPDATING " + event.getAssetId() + ", " + event.getMessage());

                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.UPDATE_FINISHED://資源更新完畢

                        cc.log("assetsManagerScene : UPDATE_FINISHED " + event.getMessage());

                        that.loadGame();

                        break;
                    case jsb.EventAssetsManager.UPDATE_FAILED://更新失敗

                        cc.log("assetsManagerScene : UPDATE_FAILED " + event.getMessage());

                        failCount++;

                        if (failCount < maxFailCount) {

                            that._am.downloadFailedAssets();
                        }

                        else {

                            cc.log("Reach maximum fail count, exit update process");

                            failCount = 0;

                            that.loadGame();

                        }

                        break;
                    case jsb.EventAssetsManager.ERROR_DECOMPRESS://解壓錯(cuò)誤

                        cc.log("assetsManagerScene : ERROR_DECOMPRESS " + event.getMessage());

                        that.loadGame();

                        break;

                    default:
                        cc.log("assetsManagerScene : unknown Event" + event.getMessage());
                        that.loadGame();
                        break;

                }

            });

            cc.eventManager.addListener(listener, 1);

            this._am.update();

            cc.director.runScene(this);

        }
    },

    loadGame: function () {
        //jsList是jsList.js的變量宇植,記錄全部js。
        if (cc.sys.isNative) {

            cc.loader.loadJs("", ["src/updateList.js"], function (error) {
                if (error)
                    console.log("load js src/updateList.js error=" + error);
                else
                    console.log("load js src/updateList.js success");

                // cc.loader.loadImg() // 圖片放后邊再說(shuō)
                cc.loader.loadJs("", jsUpdateList, function (error) {
                    if (error)
                        console.log("load js error=" + error);
                    else
                        console.log("load js success");

                    cc.director.runScene(new HelloWorldScene());
                });

            });
        }
    },

    updateProgress: function (percent) {
        this._progress.string = "update__" + percent + "__%";
    },

    onExit: function () {

        cc.log("AssetsManager::onExit");

        this._am.release();

        this._super();

    }

});

修改我們的main.js文件:

    cc.LoaderScene.preload(g_resources, function () {
        cc.director.runScene(new HelloWorldScene());

    }, this);

改成:

    // cc.LoaderScene.preload(g_resources, function () {
    //     cc.director.runScene(new HelloWorldScene());
    //
    // }, this);

    var scene = new AssetsManagerLoaderScene();

    scene.run();

修改project.json埋心,改成這樣

"jsList" : [
        "src/assetsManagerScene.js"
    ]

在res/Manifests目錄下添加一個(gè)版本文件:project.manifest

{
    "packageUrl": "http://192.168.1.148:8080/cocos/test/",
    "remoteManifestUrl": "http://192.168.1.148:8080/cocos/test/project.manifest",
    "remoteVersionUrl": "http://192.168.1.148:8080/cocos/test/version.manifest",
    "engineVersion": "3.15",
    "version": "1.0.0",

    "assets": {
    },
    "searchPaths": [
        "update"
    ]
}

最終webstorm的目錄結(jié)構(gòu)是:

目錄結(jié)構(gòu)

打開vs項(xiàng)目


vs解決方案

運(yùn)行項(xiàng)目指郁,我們可以看到我們的更新場(chǎng)景:

更新場(chǎng)景

大約4秒后會(huì)呈現(xiàn)我們的游戲場(chǎng)景

游戲場(chǎng)景

由于我們還沒配置我們的熱更新服務(wù)器,所以有4秒的超時(shí)等待時(shí)間拷呆。

下面配置我們的熱更新服務(wù)器:
打開Apache安裝目錄闲坎,我的是在C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps,新建cocos目錄茬斧,再在里面創(chuàng)建test目錄腰懂。
得到這樣一個(gè)目錄:C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\cocos\test
創(chuàng)建目錄update,和project.manifest文件和version.manifest文件
結(jié)果圖:

test目錄

project.manifest就是我上面講的文件项秉,內(nèi)容一樣的绣溜。
version.manifest內(nèi)容如下:

{
    "forceUpdate": true
    
    "packageUrl": "http://192.168.1.148:8080/cocos/test/",
    "remoteManifestUrl": "http://192.168.1.148:8080/cocos/test/project.manifest",
    "remoteVersionUrl": "http://192.168.1.148:8080/cocos/test/version.manifest",
    "engineVersion": "3.15",
    "version": "1.0.0"
}

再運(yùn)行我們的程序,發(fā)現(xiàn)我們很快就從更新場(chǎng)景到游戲場(chǎng)景了BΠ2烙鳌!

下面進(jìn)行我們的服務(wù)器配置熱更新

拷貝一份app.js到test岁诉,修改一行代碼:

var helloLabel = new cc.LabelTTF("Hello World", "Arial", 38);

改成

var helloLabel = new cc.LabelTTF("Hello World 1", "Arial", 38);

新建src目錄拷貝app.js到里面锚沸,然后添加到壓縮包。起名app_js_1.0.1.zip:

app_js_1.0.1.zip

修改我們?cè)趖est目錄的project.manifest和version.manifest:

project.manifest
version.manifest

再次運(yùn)行我們的程序涕癣,OK哗蜈,見到我們的Hello World 1了!J艋!

Hello World 1

下面候生,我們重復(fù)操作同眯,把上面那段顯示文字改成Hello World 2,Hello World 3,每個(gè)都創(chuàng)建一個(gè)壓縮包:

資源更新包

壓縮包都一樣都是包含一個(gè)src文件夾,里面存一個(gè)app.js文件唯鸭。
繼續(xù)修改我們的project.manifest和version.manifest文件:

project.manifest
version.manifest

重新運(yùn)行我們的程序须蜗!如愿以償看到我們的Hello World 3

Hello World 3

最后要說(shuō)的話

這里cocos有一個(gè)坑,

"assets": {
        "update3": {
            "path": "update/app.js_1.0.3.zip",
            "md5": "9521D6B2176D667E22DC4F8345AE96E5",
            "compressed": true
        },
        "update2": {
            "path": "update/app.js_1.0.2.zip",
            "md5": "940002A15EA29516E3AC45AE073BE66C",
            "compressed": true
        },
        "update1": {
            "path": "update/app.js_1.0.1.zip",
            "md5": "88AA0BC4F6D9FE04A8532859674DE321",
            "compressed": true
        }
        
    }

我們所有的assets更新文件必須是倒序的,因?yàn)閏ocos執(zhí)行下載的時(shí)候是倒著取的明肮,

while (_currConcurrentTask < /*_maxConcurrentTask*/MAX_DOWNLOAD_TASK && _queue.size() > 0)
    {
        std::string key = _queue.back();//這里從back取下載的key菱农,也就是我們的update1,update2柿估,update3
        _queue.pop_back();
        
        _currConcurrentTask++;
        DownloadUnit& unit = _downloadUnits[key];
        _fileUtils->createDirectory(basename(unit.storagePath));
        _downloader->createDownloadFileTask(unit.srcUrl, unit.storagePath, unit.customId);
        
        _tempManifest->setAssetDownloadState(key, Manifest::DownloadState::DOWNLOADING);
    }

限制單個(gè)下載也是這個(gè)原因循未,我們?nèi)绻鄠€(gè)任務(wù)同時(shí)下載,可能會(huì)導(dǎo)致覆蓋文件出現(xiàn)問(wèn)題秫舌,可能導(dǎo)致我們的Hello World 3不能顯示的妖,可能最后只顯示Hello World 1硫戈。

后續(xù)問(wèn)題

后面發(fā)現(xiàn)文件多了泥彤,更新的下來(lái)的zip包也是多線程解壓縮的,责静,然后可能會(huì)存在舊的壓縮包解壓出來(lái)的東西替換最新的壓縮包解壓出來(lái)的墨缘,星虹,尷尬。镊讼。宽涌。可能還是只能支持整包更新吧狠毯。护糖。〗浪桑看了官方github上提供的manifest生成工具嫡良,它的思路就是把你所有的文件都記錄在案,沒有的献酗,自己去下載寝受。可能這樣是目前最好的一種方法了罕偎。附上官方的manifest生成工具,使用之前需要安裝nodejs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末很澄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颜及,更是在濱河造成了極大的恐慌甩苛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俏站,死亡現(xiàn)場(chǎng)離奇詭異讯蒲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)肄扎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門墨林,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赁酝,“玉大人,你說(shuō)我怎么就攤上這事旭等∽么簦” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵搔耕,是天一觀的道長(zhǎng)隙袁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)度迂,這世上最難降的妖魔是什么藤乙? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮惭墓,結(jié)果婚禮上坛梁,老公的妹妹穿的比我還像新娘。我一直安慰自己腊凶,他們只是感情好划咐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钧萍,像睡著了一般褐缠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上风瘦,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天队魏,我揣著相機(jī)與錄音,去河邊找鬼万搔。 笑死胡桨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞬雹。 我是一名探鬼主播昧谊,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼酗捌!你這毒婦竟也來(lái)了呢诬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胖缤,失蹤者是張志新(化名)和其女友劉穎尚镰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哪廓,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狗唉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撩独。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敞曹。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖综膀,靈堂內(nèi)的尸體忽然破棺而出澳迫,到底是詐尸還是另有隱情,我是刑警寧澤剧劝,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布橄登,位于F島的核電站,受9級(jí)特大地震影響讥此,放射性物質(zhì)發(fā)生泄漏拢锹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一萄喳、第九天 我趴在偏房一處隱蔽的房頂上張望卒稳。 院中可真熱鬧,春花似錦他巨、人聲如沸充坑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捻爷。三九已至,卻和暖如春份企,著一層夾襖步出監(jiān)牢的瞬間也榄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工司志, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甜紫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓俐芯,卻偏偏與公主長(zhǎng)得像棵介,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吧史,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理邮辽,服務(wù)發(fā)現(xiàn),斷路器贸营,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 有點(diǎn)懶得把圖片傳上來(lái)了吨述,請(qǐng)移步這里看 Cocos官方論壇-wiki CocoaChina論壇帖子 上面兩個(gè)是一樣的...
    椒鹽老蟶閱讀 5,257評(píng)論 1 6
  • 官方有提供demo。demo鏈接:https://github.com/cocos-creator/tutoria...
    紫荊逸閱讀 4,850評(píng)論 7 8
  • 第二個(gè)專題是樹钞脂,今天才刷完揣云,上周被實(shí)習(xí)郁悶崩了TAT104. Maximum Depth of Binary Tr...
    codingXue閱讀 195評(píng)論 0 0