cocos2dcocos2d-Lua

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

2017-07-15  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读