文章新地址

原理

  每次登陆游戏利用cocos的assetManager从服务器拉去当前最新的两个文件。 一个是version.mainifest,一个project.mainifest. 这两个文件都是xml的描述文件。一个包含了版本信息,第二个包含了游戏所有资源的MD5码。首先通过version文件对比本地的版本是否相同,如果不相同,再通过跟本地的project文件对比MD5码来判断哪些文件需要重新下载,替换资源。

步骤:

  1. 有一个文件下载的热更新服务器,将最新项目资源(res/ src/ 目录)放入热更新服务器中,添加版本信息母文件(version_info.json)和python脚本文件eneateManifest.py(生成project.manifest、version.manifest文件)。

2.version_info.json文件: 主要用来配置信息

{"packageUrl" : "http://ip:port/update/MyProj/assets/","remoteManifestUrl" : "http://ip:port/update/MyProj/version/project.manifest","remoteVersionUrl" : "http://ip:port/update/MyProj/version/version.manifest","engineVersion" : "3.3","update_channel" : "Android","bundle" : "2018111701","version" : "1.0.0",
}

3.eneateManifest.py文件: 这个文件是一个python。目的是生成对应的version和project文件。project文件可以帮你给每个资源生成独一无二的MD5码,相当于每个资源的标记。下面是一段python文件的代码。

#coding:utf-8import os
import sys
import json
import hashlib
import subprocess
import getpassusername = getpass.getuser()
# 改变当前工作目录
#os.chdir('/Users/' + username + '/Documents/client/MyProj/')assetsDir = {#MyProj文件夹下需要进行热跟的文件夹"searchDir" : ["src", "res"],#需要忽略的文件夹"ignorDir" : ["cocos", "framework", ".svn"],#需要忽略的文件"ignorFile":[".DS_Store"],
}versionConfigFile   = "version/version_info.json"  #版本信息的配置文件路径
versionManifestPath = "version/version.manifest"    #由此脚本生成的version.manifest文件路径
projectManifestPath = "version/project.manifest"    #由此脚本生成的project.manifest文件路径
# projectManifestPath = "/Users/ximi/Documents/client/MyProj/res/version/project.manifest"    #由此脚本生成的project.manifest文件路径(mac机)class SearchFile:def __init__(self):self.fileList = []for k in assetsDir:if (k == "searchDir"):for searchdire in assetsDir[k]:                 self.recursiveDir(searchdire)def recursiveDir(self, srcPath):''' 递归指定目录下的所有文件'''dirList = []    #所有文件夹  files = os.listdir(srcPath) #返回指定目录下的所有文件,及目录(不含子目录)for f in files:         #目录的处理if (os.path.isdir(srcPath + '/' + f)):              if (f[0] == '.' or (f in assetsDir["ignorDir"])):#排除隐藏文件夹和忽略的目录passelse:#添加非需要的文件夹                                  dirList.append(f)#文件的处理elif (os.path.isfile(srcPath + '/' + f)) and (f not in assetsDir["ignorFile"]):               self.fileList.append(srcPath + '/' + f) #添加文件#遍历所有子目录,并递归for dire in dirList:        #递归目录下的文件self.recursiveDir(srcPath + '/' + dire)def getAllFile(self):''' get all file path'''return tuple(self.fileList)def CalcMD5(filepath):"""generate a md5 code by a file path"""with open(filepath,'rb') as f:md5obj = hashlib.md5()md5obj.update(f.read())return md5obj.hexdigest()def getVersionInfo():'''get version config data'''configFile = open(versionConfigFile,"r")json_data = json.load(configFile)configFile.close()# json_data["version"] = json_data["version"] + '.' + str(GetSvnCurrentVersion())json_data["version"] = json_data["version"]return json_datadef GenerateVersionManifestFile():''' 生成大版本的version.manifest'''json_str = json.dumps(getVersionInfo(), indent = 2)fo = open(versionManifestPath,"w")  fo.write(json_str)  fo.close()def GenerateProjectManifestFile():searchfile = SearchFile()fileList = list(searchfile.getAllFile())project_str = {}project_str.update(getVersionInfo())dataDic = {}for f in fileList:      dataDic[f] = {"md5" : CalcMD5(f)}print fproject_str.update({"assets":dataDic})json_str = json.dumps(project_str, sort_keys = True, indent = 2)fo = open(projectManifestPath,"w")  fo.write(json_str)  fo.close()if __name__ == "__main__":GenerateVersionManifestFile()GenerateProjectManifestFile()

生成version.manifest如下

{"packageUrl": "http://ip:port/update/MyProj/assets/", "engineVersion": "3.3", "version": "1.0.0", "remoteVersionUrl": "http://ip:port/update/MyProj/version/version.manifest", "remoteManifestUrl": "http://ip:port/update/MyProj/version/project.manifest"
}

生成project.manifest如下

{"assets": {"src/packages/mvc/init.lua": {"md5": "6b9173481a1300c5e737ad5885ebef00"}, "src/protobuf.lua": {"md5": "f790fe35eb179a4341ff41d94e488a5d"}...}, "packageUrl": "http://ip:port/update/MyProj/assets/", "engineVersion": "3.3", "version": "1.0.0", "remoteVersionUrl": "http://ip:port/update/MyProj/version/version.manifest", "remoteManifestUrl": "http://ip:port/update/MyProj/version/project.manifest"
}

4.游戏客户端: 利用cocos assetManager来从服务器获取文件并且进行资源的替换(这里所谓的替换并不是真正的替换,利用了Fileutils->searchPath() 设置资源文件读取的优先级。也就是老资源和代码并没有删除,而是舍弃不用。

--region *.lua
--Datelocal AssetsManager = class("AssetsManager",function ()return cc.LayerColor:create(cc.c4b(20, 20, 20, 220))
end)function AssetsManager:ctor()self:onNodeEvent("exit", handler(self, self.onExitCallback))self:initUI()self:setAssetsManage()
endfunction AssetsManager:onExitCallback()self.assetsManagerEx:release()
endfunction AssetsManager:initUI()local hintLabel = cc.Label:createWithTTF("正在更新...", CONFIG.TTF_FONT_2, 20):addTo(self):move(600, 80)local progressBg = display.newSprite("sprites/hyd_progress_bg.png")    :addTo(self):move(600, 40)self.progress = cc.ProgressTimer:create(display.newSprite("sprites/hyd_progress.png")):addTo(progressBg):move(380, 19)self.progress:setType(cc.PROGRESS_TIMER_TYPE_BAR)self.progress:setBarChangeRate(cc.p(1, 0))self.progress:setMidpoint(cc.p(0.0, 0.5))self.progress:setPercentage(0) --触摸吞噬self.listener = cc.EventListenerTouchOneByOne:create()self.listener:setSwallowTouches(true)local onTouchBegan = function (touch, event)return trueendself.listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener, self)
endfunction AssetsManager:setAssetsManage()--创建可写目录与设置搜索路径local storagePath = cc.FileUtils:getInstance():getWritablePath() .. "NewRes/" local resPath = storagePath.. '/res/'local srcPath = storagePath.. '/src/'if not (cc.FileUtils:getInstance():isDirectoryExist(storagePath)) then         cc.FileUtils:getInstance():createDirectory(storagePath)cc.FileUtils:getInstance():createDirectory(resPath)cc.FileUtils:getInstance():createDirectory(srcPath)endlocal searchPaths = cc.FileUtils:getInstance():getSearchPaths() table.insert(searchPaths, 1, storagePath)  table.insert(searchPaths, 2, resPath)table.insert(searchPaths, 3, srcPath)cc.FileUtils:getInstance():setSearchPaths(searchPaths)self.assetsManagerEx = cc.AssetsManagerEx:create("version/project.manifest", storagePath)    self.assetsManagerEx:retain()local eventListenerAssetsManagerEx = cc.EventListenerAssetsManagerEx:create(self.assetsManagerEx, function (event)self:handleAssetsManagerEvent(event)end)local dispatcher = cc.Director:getInstance():getEventDispatcher()dispatcher:addEventListenerWithFixedPriority(eventListenerAssetsManagerEx, 1)--检查版本并升级self.assetsManagerEx:update()
endfunction AssetsManager:handleAssetsManagerEvent(event)    local eventCodeList = cc.EventAssetsManagerEx.EventCode    local eventCodeHand = {[eventCodeList.ERROR_NO_LOCAL_MANIFEST] = function ()print("发生错误:本地资源清单文件未找到")end,[eventCodeList.ERROR_DOWNLOAD_MANIFEST] = function ()print("发生错误:远程资源清单文件下载失败")  --资源服务器没有打开,self:downloadManifestError()end,[eventCodeList.ERROR_PARSE_MANIFEST] = function ()print("发生错误:资源清单文件解析失败")end,[eventCodeList.NEW_VERSION_FOUND] = function ()print("发现找到新版本")end,[eventCodeList.ALREADY_UP_TO_DATE] = function ()print("已经更新到服务器最新版本")            self:updateFinished()end,[eventCodeList.UPDATE_PROGRESSION]= function ()print("更新过程的进度事件")self.progress:setPercentage(event:getPercentByFile())end,[eventCodeList.ASSET_UPDATED] = function ()print("单个资源被更新事件")end,[eventCodeList.ERROR_UPDATING] = function ()print("发生错误:更新过程中遇到错误")end,[eventCodeList.UPDATE_FINISHED] = function ()print("更新成功事件")self:updateFinished()end,[eventCodeList.UPDATE_FAILED] = function ()print("更新失败事件")end,[eventCodeList.ERROR_DECOMPRESS] = function ()print("解压缩失败")end}local eventCode = event:getEventCode()    if eventCodeHand[eventCode] ~= nil theneventCodeHand[eventCode]()end
endfunction AssetsManager:updateFinished()self:setVisible(false)self.listener:setEnabled(false)
endfunction AssetsManager:downloadManifestError()self:setVisible(false)self.listener:setEnabled(false)
endreturn AssetsManager--endregion

Android apk 安装后在手机中还是以apk存在,apk 不可写入和删除,所以热更新下载的最新资源都存在缓存中,并添加缓存目录为最高优先级搜索目录,加载资源时从最高优先级目录中加载从而起到替换更新的作用。

cocos2dx中有一个热更新类AssetsManagerEx,用这个类实现热更功能时需要有两个文件,project.manifest以及version.manifest。这里主要是project.manifest文件

Cocos自身也封装了热更新的模块AssetsManager、AssetsManagerEx。

AssetsManager采用的是升级包的管理方式,首先进行版本号对比,然后根据URL获取对应的升级包,解压升级包,设置资源加载路径,通过加载writepath目录下最新文件的方式来实现更新。问题是当涉及跳版本更新,或只有一个文件被改动时,用户就要下载前面全部的升级内容,升级包会越来越大。

AssetsManagerEx是AssetsManager的加强版,不同的是不再使用升级包的方式,而是采用单个文件拉取的方式。首先获取本地更新配置,之后与服务器的更新配置比对,得出差异文件,之后单个拉取差异文件。当本地版本大于服务器版本时,会清理掉本地更新缓存。AssetsManagerEx也有尚未解决的问题,例如多个更新序列无法并行,只能顺序启动。另外版本后期随着项目庞大配置文件几乎包含了所有的文件信息,对比文件时间的耗时会越来越长。

参考:https://blog.csdn.net/u012278016/article/details/84298942

cocos2dx lua 热更新相关推荐

  1. 【步兵 cocos2dx】热更新(下)

    [步兵 cocos2dx]热更新(下) By EOS. 上一篇把基本的理论都讲了一遍,接下来讲一下代码部分. (ps:上下两篇讲完刚好,如果再来个上中下...岂不成三集篇了 =.=?) 热更否? 我这 ...

  2. 【步兵 cocos2dx】热更新(上)

    [步兵 cocos2dx]lua的热更新 By EOS. 之前写好的热更,基本可以在项目中使用,接下来拿出来跟大家分享一下. 话不多说,直接进入正题...总感觉两行长度差太多不舒服,现在好了. lua ...

  3. uLua,一个Unity+Lua热更新解决方案!

    原文:http://game.ceeger.com/forum/read.php?tid=16483&fid=16 看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后 ...

  4. uLua最新的Unity+Lua热更新解决方案!!!

    看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后就开始铺游戏框架,干了一星期到昨晚想起来到ipad上跑跑,然后我跟我的小Demo一起崩溃了.今天搜索luajit,终于在u3 ...

  5. java lua热更新_lua热更新学习

    什么是热更新,对于它的理解,正如云风所说的那样,热更新更多的用途是做不停机的 bug 修复,不应用于常规的版本更新.对于热更新的博客,网上看了不少,包括云风写的一篇 热更文章.也仔细看了 snax 的 ...

  6. cocos2dx版本热更新梳理

    文章转载自:http://blog.csdn.net/itol925/article/details/45968039 cocos热更新实现了项目资源和脚本文件的动态更新.当工程有新的改动时,用户无需 ...

  7. Lua热更新学习 lua与C#的互相调用

    在 热跟新中 只需要 写好  解析Lua 脚本的代码 然后c#代码 不需要变动 ,只需要修改 Lua脚本就好了 通过Lua 脚本控制 游戏逻辑 Lua 和C#中的类型 的对应 几个重要的 lua    ...

  8. SIKI学院lua热更新技术

    Program p=new Program(); //lua.RegisterFunction("CSharpToLuaMethod",p, p.GetType().GetMeth ...

  9. 腾讯开源手游热更新方案,Unity3D下的Lua编程

    写在前面 \\ xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能.易用性.扩展性而广受好评.现在,腾讯已经将xLua开源到GitHub. ...

最新文章

  1. java课堂测试样卷-----简易学籍管理系统
  2. ubuntu 更新mysql后无法登陆_更新ubuntu之后无法登陆mysql
  3. c语言程序的实质,C语言_printf中的%p的实质
  4. python入门代码示例-Python入门简单的静态网页爬虫3.0 (爬虫的示例代码)
  5. [转].NET 数字格式化:忽略末尾零
  6. Java-String类型的参数传递问题
  7. 20220209-CTF MISC -normal_png-stegsolve分析图片--Winhex修改图片宽高
  8. c语言也能用模板方法模式?
  9. idea 新增html页面或者是修改html文件后,target不会同步更新
  10. 使用XShell连接Cygwin
  11. [编程] C语言变量和数据类型总结练习题
  12. 研究学习时用到的软件
  13. linux ftp查看列表命令,linux查询ftp命令
  14. oeasy教您玩转vim - 38 - # 配合移动
  15. 时钟系统(NTP子母钟系统)如何为高铁系统保驾护航
  16. 从前端角度分析浏览器响应时间慢等情况
  17. IO、文件、NIO【三】
  18. (uniapp-快速入门)运行到小程序模拟器微信开发工具
  19. 测绘资质在线处理资质问题
  20. python中setattr(),getattr(),hasattr()函数的使用

热门文章

  1. 现在开始 -- Steve Pavlina
  2. 前苏联奥数题之12个乒乓球问题解答
  3. Google收购摩托罗拉将导致多个拐点
  4. android studio 内部存储(将数据储存到文件中)
  5. 大动作!中国研发“超级高铁” 理论时速达1000公里
  6. 免费网络营销与推广的几种方法
  7. Redis | Redis 通用命令
  8. 下载-MS SQL Server 2005(大全版)含开发人员版、企业版、标准版
  9. 51单片机 时间控制的不同时段交通灯控制设计(简单版)
  10. 计算机网络安装调试费用,计算机网络实验 网络设备及其安装调试new