cocos2d-x update逻辑合理方案

2015年01月14日 10:45 0 点赞 0 评论 更新于 2025-11-21 14:20

一、方案背景与目标

新项目采用的是Quick - Cocos2d - x,本文将基于Quick - Cocos2d - x - Master( > 2.2.3 rc) 给出更新方案。该方案旨在解决以下几个问题:

  1. 资源、代码在线更新
  2. framework、update模块自更新
  3. 玩家安装新版本后,减少不必要的更新
  4. 更新中断的处理
  5. 解决Quick - Cocos2d - x中遇到的问题

完成上述几点后,整个更新方案应能稳定运行。

二、安装包内容

版本号说明

首先,涉及一个大版本号,它代表C++部分的版本号,只有当C++部分有变动时,这个版本号才会更新,用于提示用户去APPSTORE下载新的版本。其余版本号仅作为显示版本号,可根据游戏内容进行区分。

安装包资源

每一个安装包包含了所有游戏资源,即新版本发布后,玩家无需更新,点开即可玩。安装包内部带有一个文件列表,以LUA文件形式存在,内容如下:

local flist = {
core = 1,
version = "1.0.1",
update_md5 = "xxxxxx",
framework_md5 = "xxxxxx",
files = {
{path="ui/shop/shop_close_btn.png", md5="xxxxxx", size="30"},
{path="ui/army/army_tip.png", md5="xxxxxx", size="20"}
}
}
return flist

使用LUA文件的原因是方便在LUA中使用dofile进行读取。files列表中列出了所有包内的文件,core即为前面提到的大版本号。path是相对于res的路径,包含完整目录和文件后缀。

资源服务器上也有一份相同结构的资源列表。服务器和安装包中的结构如下:

  • res/flist:资源列表
  • res/update.bin:update模块的打包文件
  • res/framework.bin:quick - framework的打包文件
  • res/game.bin:游戏逻辑的打包文件
  • res/.....:其它游戏资源

三、更新流程

  1. 获取服务器版本列表:从服务器取得版本列表(flist)。
  2. 检查update模块:检查update的md5值,若有更新,则下载update.bin,重新载入,并退到main(退出前需清除某些引用),再次重新进入。
  3. 检查framework模块:检查framework的md5值,若有更新,则下载framework.bin,并提示用户重新启动。
  4. 读取本地版本列表:读取本地安装目录的版本列表文件(flist)。
  5. 对比大版本号:比对服务器版本列表和本地安装目录列表中的大版本号,若不一致,则提示用户去APPSTORE上下载。
  6. 处理upd目录:读取upd目录的版本列表文件(flist),若flist文件不存在,或者flist中存放的core与安装目录列表中的不一致(表示用户安装了新版本),则清除整个upd目录,并将本地安装目录的flist内容写入upd目录。
  7. 对比版本号:对比服务器列表与本地列表中的version,若相同,则认为数据无需更新;若不同,则与服务器的flist进行md5差异对比,得到需要更新的文件。
  8. 处理更新中断情况:遍历需要更新的文件列表,若upd目录中存在该文件,则校验其MD5值,若与服务器的相同,则从待更新列表中移除。此步骤用于应对上一次更新过程中玩家中途退出的情况。
  9. 更新文件:逐个更新文件,每个文件更新完毕后,再次校验其MD5码,若校验失败,则重新下载该文件。
  10. 更新flist文件:待所有文件更新完毕,重写upd文件中的flist
  11. 进入游戏

四、资源加载路径设置

为了正确更新资源,可通过路径搜索来实现。代码如下:

-- add update path
CCFileUtils:sharedFileUtils():addSearchPath(device.writablePath.."upd/")
-- add res path for install
CCFileUtils:sharedFileUtils():addSearchPath("res/")

假设device.writablePath的目录是/data/data/com.ooxx.game1/,则第一个目录是/data/data/com.ooxx.game1/upd/,第二个目录是res/

在ANDROID和IOS上,若路径以/开头,则认为是绝对路径,直接与文件名合并生成完整路径;若不是以/开头,在IOS上的工作原理和WINDOWS相同,在ANDROID上,会先检查是否以assets开头,若不是,则会强加上"assets/"并去APK里面搜索。因此,上述两个路径在任何地方都适用。

五、方案来源与效果

以上更新流程是综合了阳光七月、yezehui200、GcvqrNq等人的更新方案得出的,该流程基本解决了本文开头提出的问题。

六、实现过程中遇到的问题及解决方案

1. dofile在ANDROID上的问题

在执行安装目录中的flist时,由于dofile会识别绝对路径,在WINDOWS上正常,但在ANDROID上无法运行,即使硬编码dofile("assets/res/flist")也不行,原因是在ANDROID上,资源是从APK压缩包中读取的。

解决方案是使用CCFileUtils:sharedFileUtils:getFileData(“res/flist”),将得到的数据写入存储卡上,再使用dofile。由于upd目录下没有res文件夹,可确保获取的是安装包下的文件路径,从而解决了dofile在ANDROID上的问题。要获取安装包下的资源,需使用dofile(device.writablePath.."upd/flist")。添加多路径搜索后,使用路径时需格外小心。

2. crypto.md5file在ANDROID上的问题

在测试crypto.md5file时,发现在ANDROID上获取APK中的文件会失败,原因是C++实现里使用了fopen来打开文件,而在ANDROID上无法通过该方式读取APK中文件。好在该需求后续不再需要。

3. 单个文件进度条问题

由于没有使用AssetManager,目前尚未实现单个文件的进度条,后续可能会参考AssetManager的实现。

4. 目录创建问题

直接使用lfs.mkdir(device.writablePath.."upd/ui/shop/)创建目录不会成功,需要一级级向下创建。递归创建目录的方法如下:

  • Windows下:os.execute("mkdir ooxx\\ooxx\\ooxx\\ooxx")
  • iOS、Android、Mac、Linux下:os.execute("mkdir -p ooxx/ooxx/ooxx/ooxx")

七、测试资源服务器的方法

测试资源服务器最简单的方法是网上下载一个nginx,解压后找到html目录,将资源放入该目录。点击nginx.exe启动,在浏览器输入127.0.0.1,若显示welcome nginx,则表示启动成功。输入127.0.0.1/1.png(假设html目录下有该图片),若能在浏览器中看到图片,则说明服务器配置正确。如果是手机测试,最好关闭内网的防火墙,否则可能连接不上。