美文网首页cocos2dcocos2d-Lua
9.Quick-cocos2dx热更新客户端实现

9.Quick-cocos2dx热更新客户端实现

作者: 会写诗的翩翩少年 | 来源:发表于2017-07-15 11:37 被阅读52次

    0.前言
      本文介绍的是热更新客户端实现,本文的阅读可结合上文,热更新的客户端实现大致分为以下几个步骤:
      1.修改资源搜索路径,使热更之后,游戏能优先加载热更资源
      2.加载本地资源清单Reslist
      3.请求远程资源清单替换本地资源清单
      4.对照资源清单检查本地资源,有的则在清单中标记,没有的则删除
      5.请求资源清单中未标记的资源
      6.加载资源清单中的代码资源
      7.热更结束,回调主逻辑

    1.使用方式

    local CCHotFix = require("app.scenes.CCHotFix")
    CCHotFix.new(handler(self, function(self)
        end))
    

    2.代码阅读
      代码很简单,注释很清晰,阅读建议就是将代码拷贝至sublime(个人感觉看着舒服一些),逐行阅读,开始吧!

    require("lfs")
    local CCHotFix = class("CCHotFix")
    
    --游戏版本号
    VERSION_C_GAME = 1
    --资源清单名
    local NAME_C_RES_LIST = "ResList"
    --服务器路径
    local PATH_C_SERVER_URL = "ftp://localhost/"
    --资源路径
    local PATH_C_HOTFIX_RES = device.writablePath .. "upd/Res/"
    --临时资源路径
    local PATH_C_HOTFIX_RES_TMP = device.writablePath .. "upd/ResTmp/"
    --资源清单路径
    local PATH_C_HOTFIX_RES_LIST = device.writablePath .. "upd/" .. NAME_C_RES_LIST
    
    function CCHotFix:ctor(onComplete)
        --热更结束的回调
        self.onComplete = onComplete
    
        --资源文件夹创建
        cc.FileUtils:getInstance():createDirectory(PATH_C_HOTFIX_RES)
        cc.FileUtils:getInstance():createDirectory(PATH_C_HOTFIX_RES_TMP)
    
        --修改资源搜索路径
        cc.FileUtils:getInstance():addSearchPath(PATH_C_HOTFIX_RES)
        cc.FileUtils:getInstance():addSearchPath("res/")
    
        --检测是否存在资源清单
        if cc.FileUtils:getInstance():isFileExist(PATH_C_HOTFIX_LIST) then 
            self.ResList = dofile(PATH_C_HOTFIX_RES_LIST)
            VERSION_C_GAME = self.ResList.ver
        end
    
        --请求服务器上的资源清单
        self:requestResList()
    end
    
    --从服务器请求文件
    function CCHotFix:requestFileFromServer(url, succ, err)
        local url = PATH_C_SERVER_URL .. url
        local request = network.createHTTPRequest(function(event)
            if event.name == "completed" then           
                local rscode = event.request:getResponseStatusCode()            
                if rscode == 226 then               
                    succ(event.request:getResponseData())               
                else
                    err()
                end
            elseif event.name == "progress" then
            else
                err()
            end
        end, url, "GET")
        request:setTimeout(15)
        request:start()
    end
    
    --遍历路径
    function CCHotFix:traversePath(rootpath, func)
        for entry in lfs.dir(rootpath) do
            if entry ~= '.' and entry ~= '..' then
                local path = rootpath .. '\\' .. entry
                local attr = lfs.attributes(path)
                assert(type(attr) == 'table')               
                if attr.mode == 'directory' then
                    self:traversePath(path)
                else
                    func(path)
                end
            end
        end
    end
    
    --请求资源列表
    function CCHotFix:requestResList()
        self:requestFileFromServer(NAME_C_RES_LIST,
            handler(self, function(self, data)
                --请求成功后,将其写入本地路径,并加载
                io.writefile(PATH_C_HOTFIX_RES_LIST, data, "wb+")
                self.ResList = dofile(PATH_C_HOTFIX_RES_LIST)
    
                --根据资源清单对本地资源进行检测
                self:checkUpdRes()
            end),
            handler(self, function(self)
                --本地和服务器都无资源清单
                if not self.ResList then
                    self.onComplete()
                else  --本地有,远程请求失败的情况
                    self:checkUpdRes()
                end         
            end))
    end
    
    function CCHotFix:checkUpdRes()
        --遍历本地资源路径
        self:traversePath(PATH_C_HOTFIX_RES, handler(self, function(self, path)
            local keep = false
            local md5 = string.lower(crypto.md5file(path))
            
            --检测该文件的md5是否存在于资源清单
            for i, v in ipairs(self.ResList.stage) do
                if string.lower(v.code) == md5 then
                    local arr = string.split(path, "/")
                    local name = table.remove(arr, #arr)
                    local path = table.concat(arr, "/") .. "/"
                    if name ~= v.name then  --与资源清单上的名字不同则重命名
                        cc.FileUtils:getInstance():renameFile(path, name, v.name)
                    end
    
                    --存在则标记资源清单
                    v.fileCheck = true
                    keep = true
                    break
                end
            end
    
            --该文件不存在于资源清单,则删除该文件
            if not keep then
                cc.FileUtils:getInstance():removeFile(path)
            end
        end))
    
        --请求资源清单上未标记的资源
        self:requestUncheckRes()
    end
    
    function CCHotFix:requestUncheckRes()
        --将未标记资源写入对象存储
        self.uncheckRes = {}
        for i, v in ipairs(self.ResList.stage) do
            if not v.fileCheck then
                table.insert(self.uncheckRes, v.code)
            end
        end
        
        --一次下载一个资源文件
        self.downloadNext = function(self)
            local path = table.remove(self.uncheckRes)
            if path then
                self:requestFileFromServer(path,
                    handler(self, function(self, data)
                        --请求成功后,写入tmp路径
                        io.writefile(PATH_C_HOTFIX_RES_TMP .. path, data, "wb+")
                        self:downloadNext()
                    end),
                    handler(self, function(self)
                        print("请求资源失败,热更新停止")
                        cc.FileUtils:getInstance():removeDirectory(PATH_C_HOTFIX_RES_TMP)
                        self.onComplete()
                    end))
            else
                print("下载完毕")
                self:movTmpRes()
            end
        end
        self:downloadNext()
    end
    
    --将tmp路径下的资源写入正式路径下
    function CCHotFix:movTmpRes()
        for i, v in ipairs(self.ResList.stage) do
            if not v.fileCheck then
                v.fileCheck = true
                os.rename(PATH_C_HOTFIX_RES_TMP .. v.code, PATH_C_HOTFIX_RES .. v.name)
            end
        end
        self:loadZip()
    end
    
    --加载热更下来的代码资源
    function CCHotFix:loadZip()
        for i, v in ipairs(self.ResList.stage) do
            if v.act == "load" then
                cc.LuaLoadChunksFromZIP(PATH_C_HOTFIX_RES .. v.name)
            end
        end
    
        VERSION_C_GAME = self.ResList.ver
        self.onComplete()
    end
    
    return CCHotFix
    

    相关文章

      网友评论

      • 最怕认真:你的cocos版本是多少,为什么不用AssetsManagerEx呢
        最怕认真:@会写诗的翩翩少年 这应该是你们自己封装的吧~,官方的热更新,偷偷告诉你!TM的有BUG,根本不支持断点续传,干TM的,我尼玛把源码硬是看了一遍,这哪是做游戏,这是做引擎,而且去官方找说法,有人回答是不存在的,官方装死,本事一流!
        会写诗的翩翩少年:@最怕认真 用的是quick3.6.3,公司用的是这个:joy:

      本文标题:9.Quick-cocos2dx热更新客户端实现

      本文链接:https://www.haomeiwen.com/subject/hsvahxtx.html