25

苹果开发中文网站网游丨一月一更新,一更更一月,如何实现热更新?

 5 years ago
source link: http://www.cocoachina.com/programmer/20190529/27018.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

网游丨一月一更新,一更更一月,如何实现热更新?

suiling· 2019-05-29

一款游戏的生命周期可以被分为两个阶段:开发期和运营期,尤其是网络游戏,运营期的工作甚至比开发期重要,运营的好坏直接决定了一款游戏的生命周期,在运营期,一款游戏最主要的工作就是更新,更新又分两种:大版本更新和小更新,大版本更新包含游戏比较大的内容改变和功能的添加,甚至包含底层性能的优化,一般手机网络游戏的大版本更新周期是一个月左右,而小更新包含已经开发好的功能的上线,运营活动的开启和关闭,某些新关卡的添加,新人物或者卡牌的上线,每次更新都是一件令项目组头疼的事,并不是因为开发任务繁重,而是提交平台和审核,一般的手机网络游戏都要提交各大应用平台,每次更新都要重新打一遍平台包并提交,提交审核流程又是一件耗时耗力的工作,因为一般平台的审核都要经过人力的测试,尤其像苹果APP Store的审核周期甚至达到半个月到一个月,所以往往造成“一月一更新,更新更一月”的尴尬局面,大大降低了游戏的体验和玩家对于游戏的粘性,如何快速的更新游戏的版本成为了摆在各个项目组面前的课题。

热更新技术在一定程度上解决了这个问题,为游戏运行时动态更新资源而设计的,其中的资源可以是贴图,动画,音乐音效甚至是脚本。在游戏漫长的运营周期中,可以上传新的资源到服务器,在游戏客户端登录时同步远程服务器上的修改,自动下载新的资源到用户的设备上,从而绕过各个渠道平台的审核机制,达到快速更新迭代产品的目的,一般的功能更新,数值修改,角色添加及bug修改都可以通过热更新完成,需要说明的是,更新的代码只限于实时运行的脚本代码,这也是为什么很多游戏公司把大量的游戏逻辑放在脚本中开发的原因,这篇文章介绍Cocos Creator中的热更新解决方案。

本质上,Cocos Creator中的热更新机制是依赖与Cocos2D-x的AssetManager,这个功能在Cocos2D-x 3.0时发布,同时发布了Cocos2D-JS 3.0版本中也含有这个功能,之后在Cocos2D-x升级了这个功能,加入了多线程并发实现,之后又在Cocos Creator 1.4.0版本和Cocos2d-x v3.15中经过一次重大重构,系统性解决了热更新过程中的问题。

Cocos2D-JS的设计主要是针对Web平台发布游戏,所以需要考虑浏览器的缓存机制,服务器端保存了完整的一个Web页面内容,浏览器请求一个网页后,就会在浏览器缓存这个网页的资源,当浏览器重新请求这个网页时会查询服务器版本最后的修改时间或者是唯一标识,如果不同则下载新的文件来更新缓存,如果相同就使用缓存的文件。

对于市面上的手游,目前比较常有的热更新机制是补丁包机制,即每个版本对应上一个版本生成一个补丁包,每个版本对应一个版本号,在客户端保存一个当前资源的版本号,每次检测热更的时候首先和服务器通讯,对比版本号,如果版本号落后于服务器版本号,就开始热更流程,否则正常进入游戏,更新流程就是按顺序下载版本差之间的一个或多个资源包,这样更新的问题是资源包如果由人工整理,很难确保不会出错,而有些项目也尝试使用git上的工具,动态对比资源文件夹下的资源,从而生成不同版本之间的虚拟资源文件包,然而由于不具有足够的灵活性和资源间的可拆解性,这种版本差异“打包”的方式终究不是一个完美的解决方案。

Cocos Creator所采用的更新基本流程是为每个文件制定一个版本号,这样当对比更新文件时,会以每个文件为单位判断是否需要更新,增加了灵活性。

它的更新的基本流程是:

客户端每次启动时发送请求和服务端版本进行比对获得差异列表。

如果差异列表为空,则直接进入游戏,否则从服务端下载所有新版本中有改动的资源文件。

用新下载的资源覆盖旧缓存以及应用包内的文件。

在这种设计思路下,所有资源文件以离散的方式保存在服务器端,更新时会以文件为单位进行更新检查和文件下载。

AssetManager以包内的一个配置文件-manifest配置文件为检查更新的标准,manifest是一个json的配置文件,它的基本格式见代码清单1

代码清单1

//manifest文件
{
"packageUrl" :          远程资源的本地缓存根路径
"remoteVersionUrl" :    [可选项] 远程版本文件的路径,用来判断服务器端是否有新版本的资源
"remoteManifestUrl" :   远程资源 Manifest 文件的路径,包含版本信息以及所有资源信息
"version" :             资源的版本
"engineVersion" :       引擎版本
"assets" :              所有资源列表
"key" :             资源的相对路径(相对于资源根目录)
"md5" :             md5 值代表资源文件的版本信息
"compressed" :      [可选项] 如果值为 true,文件被下载后会自动被解压,目前仅支持 zip 压缩格式
"size" :            [可选项] 文件的字节尺寸,用于快速获取进度信息
 "searchPaths" :         需要添加到 FileUtils 中的搜索路径列表
}复制代码

客户端每次启动时发送请求和服务端版本进行比对获得差异列表remote信息(包括 packageUrl、remoteVersionUrl、remoteManifestUrl是该manifest所指向远程包信息

如果差异列表为空,则直接进入游戏,否则从服务端下载所有新版本中有改动的资源文件。另外,md5信息可以是某个版本号,完全由用户决定的,本地和远程manifest对比时,只要md5 信息不同,就可以认为文件版本有改动。

需要说明的是,编辑器的asset文件夹并不等于资源文件夹,资源热更新是更新构建出来的资源,在使用构建面板构建原生版本时,在构建目录下找到res和src文件夹,这两个文件夹内保存的才是真正让游戏运行起来的游戏包内资源。其中src包含所有脚本,res包含所有资源。

开始更新的流程如图1所示。

image.png

图1开始更新流程

尤其启动后,首先检查包内有没有manifest配置文件,然后创建临时的缓存文件夹,同时开始启动请求服务器的manifest信息,和本地的信息对比。

移动游戏作为一个独立的本地包安装到手机设备上时,它是以iOS的ipa或者Android的apk形式存在的,包在安装后,它是以整体存在的,它其中的内容是无法被修改或者添加的,包内的任何资源都会一直存在。因此在热更新机制中,只能更新本地缓存到手机的可写目录下(应用存储空间或者SD卡指定目录),并通过Cocos2D-X的FileUtils的搜索路径机制完成本地缓存对包内资源的覆盖。在更新过程中会首先将新版本资源放到一个临时文件夹中,只有当本次更新正常完成,才会替换到本地缓存文件夹内。如果中途中断更新或者更新失败,此时的失败版本都不会污染现有的本地缓存,这样可以确保更新不会因为中途中断而造成出现不可修复的bug,如图15-3所示,为更新过程文件临时文件夹和缓存文件夹的转换问题。

   下载的全部逻辑如图2所示。

image.png

 

图2   下载文件流程

首先检查文件列表,是否有下载的文件,如果有可以下载的文件,就开始一个一个下载文件,下载完成后拷贝整个内容到缓存文件夹,然后替换新的manifest配置文件,然后重启游戏并设置缓存路径为最高的搜索优先级目录,之所以要重启游戏,有两个原因,首先是更新下来的脚本需要干净的Javascript环境才能正常运行。因为在热更新完成后,游戏中的所有脚本实际上已经执行过,所有的类、组件、对象都已经存在Javascript上下文环境中了,此时如果不重启直接加载脚本,同名的类和对象的确会被覆盖,但是已经用旧的类创建的对象是一直存在的,而被直接覆盖的全局对象在运行过程中修改的状态也全部丢失了。另外一个原因是场景配置,AssetsLibrary中的配置都需要更新到最新才能够正常加载场景和资源,Cocos Creator的资源也依赖于配置,场景依赖于settings.js中的场景列表,而raw assets依赖于settings.js中的raw assets列表。如果settings.js没有重新执行,并被main.js和AssetsLibrary重新读取,那么游戏中是加载不到新的场景和资源的。

如果传输过程中有任何的中断,都会通过断点续传机制重新开始下载,那么如何开始断点续传呢?manifest配置文件中会标识每个文件的状态,包括:未开始、下载中、下载成功,在更新的过程中,每个文件下载完成后都会被标识为完成,同时会被写入到内存中,每当下载的文件数量完成到某一个进度节点时都会将内存中的数据写入到临时文件夹中的manifest文件中,再次启动时,会优先检查临时文件夹中的文件,如果有未下载完成的文件则继续进行临时文件夹中的下载。

热更新流程中很重要的步骤是比较客户端和服务端的版本,默认情况下只有当服务端主版本比客户端主版本更新时才会去更新。引擎中实现了一个版本对比的函数,它的最初版本使用了最简单的字符串,对于某些版本号的情况会出现错误的结果,比如出现“1.9 > 1.10”。在Cocos Creator 来到1.4版本之后,引擎的版本对比函数升级为支持 “x.x.x.x”序列版本的对比函数,不符合这种版本号模式的情况下会继续使用字符串比较函数。

下载过程中可能会由于一些原因导致文件下载的错误,所以下载完成后比较安全的状态需要检查文件是否正确,一般情况下时对比md5码,校验的代码见代码清单2。

代码清单2   校验文件

//校验文件
assetsManager.setVerifyCallback(function (filePath, asset) {
    var md5 = calculateMD5(filePath);
    if (md5 === asset.md5)
        return true;
    else
        return false;
});复制代码

当用户环境中已经包含一个本地缓存版本时,热更新管理器会比较缓存版本和应用包内版本,使用较新的版本作为本地版本。如果在游戏运行时服务器的版本有更新,热更新管理器在更新过程中,按照正常流程会使用临时文件夹来下载服务器版本。当服务器版本更新成功后,临时文件夹的内容会被复制到本地缓存文件夹中,如果有同名文件则被覆盖,最后删除临时文件夹。需要注意的是,这个过程中并不会删除本地缓存中的原始文件,因为这些文件仍然可能是有效的,只是它们没有在这次版本中被修改。

版本更新的方式有两种,在设计小版本热更新的时候,也要考虑如何进行大版本的更新,当每次大版本更新的时候,需要将热更的文件清理,否则就会出现新文件被旧文件覆盖的情况,清理文件操作见代码清单3。

代码清单3   清理文件

//清理更新文件
//之前版本号
var previousVersion =
parseFloat(cc.sys.localStorage.getItem('currentVersion'));
//当之前版本号比现有版本号大的时候
if (previousVersion < game.currentVersion) {
     //清理热更新的储存路径
     jsb.fileUtils.removeDirectory(storagePath);
}复制代码

推荐阅读:

Cocos Creator游戏开发实战

image.png

作者:满硕泉 著 

内容简介“

(1)Cocos引擎创始人、Cocos引擎技术总监、触控科技西南区总经理等Cocos官方核心专家高度评价并推荐

(2)基于Cocos Creator 2.x版本,全面介绍引擎的功能作用、工作原理,以及游戏开发的方法、优秀实践和性能优化


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK