cocos2dx lua 热更新
文章新地址
原理
每次登陆游戏利用cocos的assetManager从服务器拉去当前最新的两个文件。 一个是version.mainifest,一个project.mainifest. 这两个文件都是xml的描述文件。一个包含了版本信息,第二个包含了游戏所有资源的MD5码。首先通过version文件对比本地的版本是否相同,如果不相同,再通过跟本地的project文件对比MD5码来判断哪些文件需要重新下载,替换资源。
步骤:
- 有一个文件下载的热更新服务器,将最新项目资源(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 热更新相关推荐
- 【步兵 cocos2dx】热更新(下)
[步兵 cocos2dx]热更新(下) By EOS. 上一篇把基本的理论都讲了一遍,接下来讲一下代码部分. (ps:上下两篇讲完刚好,如果再来个上中下...岂不成三集篇了 =.=?) 热更否? 我这 ...
- 【步兵 cocos2dx】热更新(上)
[步兵 cocos2dx]lua的热更新 By EOS. 之前写好的热更,基本可以在项目中使用,接下来拿出来跟大家分享一下. 话不多说,直接进入正题...总感觉两行长度差太多不舒服,现在好了. lua ...
- uLua,一个Unity+Lua热更新解决方案!
原文:http://game.ceeger.com/forum/read.php?tid=16483&fid=16 看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后 ...
- uLua最新的Unity+Lua热更新解决方案!!!
看了坛子上同学用Kopilua,以为真的跨平台没问题,就实验了安卓手机,然后就开始铺游戏框架,干了一星期到昨晚想起来到ipad上跑跑,然后我跟我的小Demo一起崩溃了.今天搜索luajit,终于在u3 ...
- java lua热更新_lua热更新学习
什么是热更新,对于它的理解,正如云风所说的那样,热更新更多的用途是做不停机的 bug 修复,不应用于常规的版本更新.对于热更新的博客,网上看了不少,包括云风写的一篇 热更文章.也仔细看了 snax 的 ...
- cocos2dx版本热更新梳理
文章转载自:http://blog.csdn.net/itol925/article/details/45968039 cocos热更新实现了项目资源和脚本文件的动态更新.当工程有新的改动时,用户无需 ...
- Lua热更新学习 lua与C#的互相调用
在 热跟新中 只需要 写好 解析Lua 脚本的代码 然后c#代码 不需要变动 ,只需要修改 Lua脚本就好了 通过Lua 脚本控制 游戏逻辑 Lua 和C#中的类型 的对应 几个重要的 lua ...
- SIKI学院lua热更新技术
Program p=new Program(); //lua.RegisterFunction("CSharpToLuaMethod",p, p.GetType().GetMeth ...
- 腾讯开源手游热更新方案,Unity3D下的Lua编程
写在前面 \\ xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能.易用性.扩展性而广受好评.现在,腾讯已经将xLua开源到GitHub. ...
最新文章
- java课堂测试样卷-----简易学籍管理系统
- ubuntu 更新mysql后无法登陆_更新ubuntu之后无法登陆mysql
- c语言程序的实质,C语言_printf中的%p的实质
- python入门代码示例-Python入门简单的静态网页爬虫3.0 (爬虫的示例代码)
- [转].NET 数字格式化:忽略末尾零
- Java-String类型的参数传递问题
- 20220209-CTF MISC -normal_png-stegsolve分析图片--Winhex修改图片宽高
- c语言也能用模板方法模式?
- idea 新增html页面或者是修改html文件后,target不会同步更新
- 使用XShell连接Cygwin
- [编程] C语言变量和数据类型总结练习题
- 研究学习时用到的软件
- linux ftp查看列表命令,linux查询ftp命令
- oeasy教您玩转vim - 38 - # 配合移动
- 时钟系统(NTP子母钟系统)如何为高铁系统保驾护航
- 从前端角度分析浏览器响应时间慢等情况
- IO、文件、NIO【三】
- (uniapp-快速入门)运行到小程序模拟器微信开发工具
- 测绘资质在线处理资质问题
- python中setattr(),getattr(),hasattr()函数的使用