【游戏技术群】959392658  【游戏出海群】12067810
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2436

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 1118|回复: 13

[士郎] Unity手游开发札记——基于累积差异的Patch系统实现

[复制链接]  [移动端链接]
排名
1
昨日变化

7406

主题

7948

帖子

3万

积分

Rank: 16

UID
1231
好友
185
蛮牛币
9245
威望
30
注册时间
2013-7-29
在线时间
3779 小时
最后登录
2019-3-18

社区QQ达人活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

发表于 2018-12-7 14:42:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
凑巧近期因为代理方的要求做了一个Patch系统的功能增强,添加了回退版本的功能,对于项目当前的Patch系统又进行了一些深入的讨论和回顾。同时也发现即便是团队内的同事对于这套Patch系统的实现细节还有很多模糊的地方,就想趁这个机会对这套Patch系统进行一些梳理和总结,一方面让更多的老司机看看还有什么改进的点和缺陷,另外也给对于这块不够了解的朋友带来新的思路和想法。

当前这个心态下选择这个题目来讲有一点“自暴自弃”的意思。Patch系统是一个非常基础的系统,从本质上说它几乎没有什么技术含量,在游戏开发中这个系统也是属于在背后默默干“脏活累活”的角色。然而,在实际的开发中,这个系统中又有很多设计理念需要去遵守,有太多实现细节需要去注意,才能够保证它可以稳定地满足各种项目的实际需求。所以,在这个系统中几乎没有“实验室方法”,而是以“工厂经验”为主,沉淀在各个项目组里。我想,这也是为什么当前检索不到太多详细描述这一系统实现的博客的原因之一吧。

今天,我就借着对于项目在用的这套基于累积差异的Patch系统的回顾和整理,来聊一聊基于Unity引擎实现这一系统的一些细节。

1. 为什么要有Patch系统
在游戏开发中,所谓Patch系统,是让游戏可以在不重新下载安装包的情况下进行更新的功能。你可以用它更新资源、代码,甚至可以把玩家下载的A游戏更新成一个全新的B游戏。目前常见的“小包”策略也是基于这一系统实现玩家只需要下载一个很小的包体,然后通过更新来显示/隐式地让玩家下载到完整的游戏。


市场需要更快的更新速度,运维需要更方便的bug修复手段,这些都是程序要实现Patch系统的原因。虽然对于大部分商业游戏来说Patch系统不可或缺,但我在这里抛出这个问题只是想让大家在自己的项目中引入这个系统之前,对于需求有一个思考的过程——不是所有的成功游戏都有Patch系统,也不是有了完善的Patch的游戏都会成功,所以,在时间、人力、技术能力有限的情况下,任何系统都存在舍弃的可能,即便是Patch系统。

当然,独立游戏和主机游戏开发者们,不用往下看了,这篇文章基本在浪费你的时间。
2. Patch系统基本结构
Patch系统的原理非常简单——从网络下载新的资源“覆盖”掉本地的老资源,达到更新资源的目的。基本的数据流转和网络结构图如下所示:
1.jpg
Patch系统网络结构图
这里可能会有不同的实现细节,在我们使用的Patch系统中,首先通过打包服务进行打包并且生成Patch文件,借助ftp服务将Patch文件上传到Patch服务器,然后分发到CDN服务器上。玩家的设备通过http(s)服务获取到自己需要的Patch文件,“覆盖”本地之前的资源就可以了。

这里的“覆盖”之所以一直加引号是因为在不同的方式里有不同的做法,一种是真的把老的文件覆盖掉,而另外一种在手游里比较常用的是区分原始的资源目录和Patch目录,然后优先读取Patch目录的资源来从逻辑上“更改”之前的资源。
整个Patch系统的结构也非常简单:
2.jpg Patch系统的模块结构图
从整体上可以划分为Patch生成、Patch服务器和Patch更新三大模块。其中Patch的生成模块基于打包产生的资源,同时可能需要文件压缩等其他模块的功能;Patch服务器通常需要ftp服务、云存储服务以及CDN服务等功能;Patch更新模块我们使用了Unity的WebRequest模块来做http(s)的访问,同时也需要文件解压等功能的支持。

所以,从整体上来看,Patch系统的复杂程度似乎并不高,模块的划分也简单明了,彼此之间的耦合比较低,但是在项目应用中,要实现一套正确且稳定的Patch系统还需要处理很多细节。接下来的部分将从这三个模块来分别讨论那些恼人的实现细节。

3. Patch生成模块
Patch生成模块的主要功能就是产生每次打包生成的资源和基准资源之间的差异文件,这基于一个非常简单的公式:
当前资源 = 基准资源 + 差异资源

所谓基准资源,就是玩家可以下载到的安装文件里包含的所有资源。这个安装包所能支持的更新,大部分情况下都是只能往比它更新的版本进行更新。而生成差异的方式主要有两种。


3.1 两种不同的差异生成方式
这两种不同的差异生成方式中,一种是端游时代常用的基于逐次差异的更新方式,其资源之间的差异生成方式如下图所示:
3.jpg
逐次差异的Patch文件生成方式
可以看出,这种差异的生成过程是每次都和上次的版本进行差异的对比,从而生成这两个版本之间的Patch。相应的,Patch的下载过程也是依次下载当前客户端版本和目标要升级到的版本之间的所有Patch文件,然后再逐个进行“覆盖”安装。即比如要从版本1升级到版本3,需要下载版本1和版本2之间的差异Patch,然后再下载版本2和版本3之间的差异Patch。


这中做法的优势主要有:

  • Patch的生成、管理以及更新过程简单明了,出问题的概率较小;
  • 每一个差异文件都是完整地被下载更新的,因此可以压缩成一个文件上传到Patch服务器,这样每次的Patch文件也较小。
在手游中,这样的更新方式也被很多项目继续沿用。但是对于手游玩家来说,尤其是前几年网速不够好的时候,对于流量和下载Patch的等待时间更加敏感,这种更新方式就有一个不够友好的地方——在累积了几次更新同时更新的情况下,一些被频繁更新的文件会被冗余地下载多次。

简单来说,A数据在Patch1中有修改,Patch2中也有修改,其实玩家从基准资源更新到版本2资源的时候,A数据被下载多次(虽然被压缩了),其中除了最后一次,其它的下载都是无效的。

另外一个问题是在手游时代会有各种各样的渠道需求,这里可能涉及到不同渠道的包有不同的维护节奏,因为要么选择完全分开多条线进行各自的Patch维护,要么选择单条Patch更新线进行比较密集的维护。前者增加管理成本,容易出错,后者放大了上面提到的冗余下载问题,都不是特别理想。

有问题的地方就会有改变,也有不少项目组采用了基于文件差异的方式进行Patch系统的构建。Patch的核心思想是将本地的所有文件都更新成Patch服务器上要求的新版本,那么一种简单粗暴的思路是把当前资源包含的所有内容都丢到Patch服务器上去,Patch更新的时候根据和本地的差异按需下载不同的文件即可。这种方式虽然简单粗暴,但是也非常有效,甚至可以用来完整修复被损坏的包体。

当然,它的问题就是当文件很多的时候(比如我们项目,ab的数量大约接近1w个了……),这个对比过程还是比较耗时的,而且对于Patch服务器的压力也会相对较大,几百M甚至上G的文件要上传到Web服务器上去,还会有些慢。

基于这个思路,演化出了一种基于累积差异的Patch生成方式,也是我们项目目前在用的方式。
4.jpg
基于累积差异的Patch生成方式
在这种方式下,每个版本对应的Patch都是和基准资源进行差异对比,随着资源增加和修改的过程,差异Patch也会越来越多。Patch更新的功能是获取本地资源和目标Patch文件之间的差异,然后下载差异内容即可。

这两种方式各有利弊,也有各自的适用场景,不存在一种一定比另外一种好的情况,各个团队大都根据自己的经验和喜好自己选择。而本文主要来描述基于第二种方式进行Patch系统构建的细节。

3.2 差异文件的细节
在这种Patch系统中,Patch对比的最小单元是单个文件,对于使用AssetBundle来进行打包的Unity引擎来说,就是单个AssetBundle文件。针对文件,所能够进行的操作只有三种:

  • 增加文件,基准资源中没有但是新的资源版本里存在的文件;
  • 修改文件,和基准资源中的文件哈希值不同的文件;
  • 删除文件,基准资源中存在,而新的资源版本里不再存在的文件。

对于增加和修改的文件,Patch文件要包含它们;而对于删除的文件,由于Patch本身设计上是不会删除文件的,因此无需处理——因为在包体里的文件,本身就无法进行删除操作,对于已经下载过的Patch文件,因为不会再引用到了,不删除也没有关系。

这个过程看上去很简单,但是当要引入另外一个需求的时候,事情就变得复杂了——非强制整包更新的支持。

在游戏运营的过程中,我们通常每隔一段时间会为玩家提供非强制的整包更新,一来可以修复一些并不严重的bug或者进行一些性能优化,二来减少后续的玩家下载完整包之后的Patch大小。当要应对这一需求的时候,之前非常简单的Patch系统就变得稍微复杂一下。

这里来讲一个在项目开发中遇到的真正问题来解释一下要支持非强制整包更新需要进行的额外改动。我们项目在开发期很早就部署了Patch系统,这也是推荐给其他团队的做法——因为作为对于稳定性要求较高的系统,经历越多的测试,能够发现的问题越多,那么在项目研发期就应用正式的Patch系统,并对这块的异常情况保持关注,可以避免很多在游戏上线之后才会发现的问题。当时发现某个安装包经过更新之后,提示资源缺失的错误信息,这在理论上是不应该出现的情况。
经过逐个文件的对比和分析,发现了类似如下这样一个例子的错误:
5.jpg
一次模拟错误的更新过程
我们来看一下图中这样一个模拟错误的更新过程:

  • 基准资源,会出一个强制更新的整包,同时对应的Patch内容是空的;
  • 然后第一次更新,增加了a文件、修改了b文件,删除了c文件,对应的patch是只有a和b两个文件的;
  • 第二次更新,修改了d文件,这时有一个非强制的整包更新,也就是部分玩家会下载到一个新的整包,注意,这个整包里是没c文件,因为它已经被删除了。这时候因为依然要支持从基准包更新上来的玩家,因此Patch中会包含a、b、d三个文件;
  • 第三次更新中,美术把c文件又添加回来了,而且和基准资源是一样的,同时d文件也进行了回退操作,修改成和基准资源相同,这样Patch文件里,就只包含了a和b两个文件。

使用基准资源对应的整包的玩家不会存在问题,而当一个使用版本2的资源对应的整包的玩家,在进行Patch更新的时候,会发现自己没有什么需要下载的内容,因为a和b两个文件在保内都是最新的。这时候,玩家的资源内容和目标的内容就不再正确——首先是c文件缺失,然后是d文件和想要的并不一致。

导致这一错误的原因是按照之前的原理,差异只和基准资源来做,那么可以保证的是从基准资源对应的包更新上来是不会存在问题的,然而从中间处的非强制更新包,其资源内容和基准资源肯定是有差异的,那些被回退修改的文件就很容易出现前面提到的丢失或者并不是目标版本的错误。解决这一问题的方法也很简单——
在每次差异文件制作的过程中,记录和维护一个历次被删除和修改的文件列表,当发现这个文件又回退操作(即曾经修改或者删除过,但是哈希值又和基准资源中的变得一致了),就强制将其放置到需要差异列表中。
这种修复方式比较简单粗暴,不需要关注是否曾经出过非强制更新包,代价是差异文件中会存在一些冗余的并不一定要被更新的文件,但是因为下载过程会进行差异对比来避免冗余文件的下载,因此并不会导致玩家下载冗余的资源,只是差异文件会偏多一些。

3.3 文件压缩
前文已经提到了,基于逐次差异的更新方式中,可以对每次的patch进行压缩,因为它们是一定会被完整下载的。而基于累积差异的Patch系统,并不是很方便对于所有资源进行压缩——因为不同的版本需要下载的文件各不相同,需要可以按照文件名称获取制定的文件内容。

当然,针对单个文件我们可以逐个的压缩,由于我们在unity的assetbundle打包过程中使用的是压缩率不高但是读取友好的lz4的方式,文件会相对lzma的压缩方式较大,为了减少玩家下载更新的流量消耗和等待时间,我们针对每一个文件使用大量7z的格式进行压缩,可以减少大约40%-50%左右的文件大小(更要压缩率可以减少更小,但是相应的解压时间也会长一些,这个可以自己测试)。

Unity端没有选择自己集成sevenZip的库,而是使用了这个插件进行解压(剥离了不少我们不需要的功能,只保留了7zip、zip等几个模块)。
6.jpg

使用的3.4 小节
Patch生成的过程相对简单,但是依然有不少内容需要关注。要验证Patch差异生成的完备性,我们可以从这个三个方面进行保证:

  • 设计上的完备性。这个是在分析和设计一套Patch系统之前要通过逻辑的分析和计算来对实现方案的完备性进行分析和讨论,它是否可以兼容各种复杂情况,是否存在像前文所述的那样的更新漏洞;
  • 实现验证的完备性。在系统开发完成之后,需要通过白盒和黑盒测试来确保实现功能上的完备性,这部分工作通常由程序和QA一起来完成,通过构建各种测试用例和场景,来对系统进行测试;
  • 数学方式的完备性验证。通常业务系统的完备性验证都不需要上升到这个层次,经过细心的白盒和黑盒测试就已经可以做到很完备了。我们项目目前也没有做到这一步,但是从经历的各种问题的处理过程来说,如果可以利用一些数学的方式进行模拟和测试,是可以从理论上保证能够模拟出所有更新情况的。毕竟对于文件的操作也就增删改这三种,整个Patch的制作过程也不复杂,所有更新结果可以生成一张可达图或者一棵可达树,利用Petri网等理论是可以证明这个图中所有最终节点的正确性的。

4. Patch服务器
patch服务器作为开发团队和玩家之间同步Patch文件的传输者而存在,对于开发团队来说,ftp服务的上传和管理是比较方便的,而对于玩家来说,通常提供http(s)服务来进行更新的支持。
4.1 文件正确性验证
由于文件在任何拷贝、网络传输过程中都存在文件错误的可能性,即使概率很低,但是一旦发生就是不能够被接受的。所以我们在Patch的流程中会尽量遵守这样的约定:

  • 最后下载完毕后会验证文件的哈希值,确保和打包产出的文件一致;
  • 尽量减少patch文件的拷贝和传输次数,如无必要,不做额外操作;
  • 在操作过程中,关键节点尽量对文件哈希值进行校验。
对于Patch服务器来说,增加一个文件哈希值的验证过程是比较理想的,确保可以控制的最靠近玩家的内容的正确性。
4.2 Patch服务器文件结构
Patch服务器上的文件和文件夹统一使用小写,尽量规避一些大小写不统一造成的问题。Patch服务器上的文件组织结构也尽量保持简介明了,我们使用的结构大致如下图所示(并非完整的真实目录结构,示例用):
7.jpg

Patch服务器文件组织结构示例
首先在项目的patch目录下,区分安卓和iOS两个大的目录,避免Patch文件管理上的错乱。以ios为例,该目录下存放各个版本的Patch文件,这些文件夹可以用svn版本号命名,以确保唯一性。在patch目录内,按照patch需要的文件夹结构存放文件内容,同时要包含包括差异文件哈希值在内的一些对于该Patch内容的描述文件(不同做法对于这些文件的内容要求不同,因此这里不深究细节)。

在ios目录的根目录下,存放用于描述当前最新的patch信息的文件——patch_ios,同时配置一个对应的带有.pre后缀的文件用于预先测试。这部分稍后做详细解释,这里先针对为何这样设计做一个解释:

  • 由于CDN更新有延迟的问题,因此所有的Patch文件的提交都是以新的目录来提交,不使用覆盖的方式,避免玩家因为CDN更新不及时而获取到旧的资源。这是为何推荐使用版本号来作为patch父目录的一个原因。
  • 使用单独的文件针对当前要求的Patch版本进行定义,这样可以通过简单地控制这个单一文件的外放时机来影响玩家什么时候开始可以更新到新的Patch内容。在这一文件被更新出去之前,要确保的是对应的Patch存放目录已经被完整地提交到Patch服务器上。
  • 使用单独的文件控制Patch的另外一个好处是,通过Patch url的重定向逻辑,可以方便地让不同的包使用不同的Patch文件,同时在进行更新的时候,又可以让不同的包使用同一个Patch目录。比如我们要为应用宝做单独的Patch版本和服务器版本维护,这里只需要添加一个patch_version_yyb的文件即可,它里面单独定义了应用宝渠道对应的patch文件。这样当需要单独更新应用宝的时候,只修改这个文件中的内容就可以,不会对别的渠道产生影响。

在这样的结构下,patch_xxx这个文件就像一个指针,可以方便的在各个版本之间跳转,将玩家导向到不同的Patch目录去。

4.3 Patch维护的流程
在我们项目中,当周版本的时候,新的开发内容验证完毕,会建立分支,并且进行打包,产生需要更新的Patch文件,接下来等到维护阶段就需要进行Patch的外放操作。这里描述下这个外放操作的过程:

  • Patch内部测试成功之后,上传到Patch服务器,等到传输和同步完成;
  • 更新预测试用的patch_xxx.pre文件,将Patch导向到新上传的Patch目录;
  • 客户端使用特殊的“技巧”,启动预更新流程,测试Patch更新成功;
  • 在服务器维护阶段,停止服务器,确保玩家数据回存完毕;
  • 更新正式的patch_xxx文件,使用正式的客户端测试Patch更新;(此时玩家可以开始更新Patch了,但是服务器无法进入)
  • 更新维护并开启服务器,ip等白名单限制,内网测试;
  • 服务器开放外网访问权限,维护完毕。

这个维护流程的步骤不能更改,是一步一步互相关联的,否则,比如patch提前外放了,服务器还未停服,抑或服务器已经维护完毕开服了,patch还未外放成功,都可能导致服务器和客户端的版本不一致,从而造成问题。

从这个流程可以看出,.pre文件的重要性,就是用于对Patch文件进行预测试,在正式服务器还未更新的情况下,就可以进行Patch的更新,让内部的测试人员进行正式Patch文件的提前测试。

4.4 小节
Patch服务器的维护也是游戏运维过程中非常重要的部分,在我们项目中,正式的Patch是手动进行维护的,而内网dev版本的patch文件,则是脚本自动在打包的时候进行维护的。正式的Patch上传也可以使用脚本,但是对于patch_xxx这个文件的维护,一定要手动进行,因为它和整个版本的更新过程又很大的关联性。

5. Patch更新模块
Patch更新模块我们选择在C#部分进行实现,目的是简化Patch更新之后的Reload过程。它的基本流程简单来说就是先获取Patch服务器上的patch_[platform]这个文件,然后根据这个文件来判断当前的版本是否可以更新,是否需要更新,以及要更新到哪个版本去,进而去Patch服务器对应的目录获取目标差异文件的列表,进行更新检查,并下载需要更新的文件。

下面这张流程图描述了一次Patch更新的过程中需要经历的步骤,这里只包含了这篇文章想要讨论的部分细节,而不是真正实现的全部。
8.jpg

Patch更新流程图
接下来选择这个过程中的几个细节点进行详细的说明。

5.1 闪屏界面
闪屏界面看似和Patch毫无关系,为什么要列出来说呢?原因很简单,大部分流程里,只有这部分是在Patch之前,如果你想在提前测试的时候做一些手脚,这是一个比较好的时机。

而至于要做什么和怎样做,为了避免我们的后门外漏,就不描述细节了,提两个点:
  • 最好方便操作,比如一些特殊的手势等;
  • 要做到即使后门的触发方式被外部玩家知道了,也无法正常使用。
5.2 新安装版本检查
当判定一个包是一个新的安装版本的时候,需要清除Documents目录,避免一些Patch文件的遗留导致使用到错误的资源,不同项目也会根据自己的需求做一些特定的操作。清除的过程不赘述,这里想讨论的问题是如何判定当前的包是覆盖安装的新包?

比较常规的做法是拿包内的资源版本号和Patch目录的资源版本号做对比,如果发现包内的版本号较大,就认为是新的安装包,执行安装新包的清理逻辑。这可以处理较多的情况,但是依然会有一些极限问可能无法处理:

  • 玩家安装一个老的完整包的情况。因为有非强制更新包的情况存在,所以外部玩家是可能主动/被动安装一个老版本的包的情况出现,虽然大部分情况下这部分Patch会继续可用,但是遇到一些需要紧急回退Patch版本的情况下,就可能存在问题。
  • 内部测试容易出现误导性的错误。在内部进行测试的时候,经常会对Documents目录进行修改等操作,有时候Document目录清理不及时,会出现一些奇怪的报错,导致不需要的额外排查时间,而内部覆盖安装老的包等情况非常常见。

为了保险起见,我们选择一种比较极端的方式来做新安装包的检查——只要安装包和之前的版本不一致,就认为是一个新包的覆盖安装操作,执行清理逻辑。具体操作如下:

  • 打包的时候,在包的资源里放置一个额外文件,包含一个全局唯一标识(简化来说也可以用一个时间戳来标识);
  • 新包安装的流程里,将这个文件拷贝到Document特定目录,用于标识外部资源对应的新安装包版本;
  • 每次游戏启动的时候,检查包内的这个标识和Document里的是否一致,如果一致,则认为不是新安装包,如果不一致,则判定为新安装包,执行Document的清理逻辑,并重新拷贝这个新的标识文件。

这样的代价是减少了一些Patch文件的复用可能,但是确保了不同的包无论哪个版本更新,都可以进行完备的清理操作。
5.3 预处理
如果说闪屏是手动操作的后门,这个预处理,应该算是一个对于Patch的正式版本的后门。它核心要面对和处理的问题有两个:
  • 通过预设一些开关和设置选项,让我们有机会修复一些Patch系统中的问题。我们知道,Patch系统本身也可能存在一些bug,一方面要做完备的测试来提前发现它们,另外一方面是希望通过一些预设的配置来修复遗漏的问题。如果Patch系统是在脚本层的,那么Patch本身是可以修复自己的大部分bug的,只是需要重启才能够生效,但即便是如此,也可能会出现一些极端情况——某个项目在对外测试之前应营销的需求出了一个关闭Patch的版本,没有恢复回来直接上架了渠道,这对于客户端来说意味着失去了对于玩家手动客户端的控制,属于最为严重的一级事故。而因为Patch已经关闭了,也就么有办法通过Patch修复它自身,那么这个客户端版本就永远无法开启Patch了,只能强制重新提包来解决。预处理就是为了让这种傻X的情况可以有一个挽回的方式而存在的。。。
  • 应对Apple Store和安卓的评审。我们知道,Apple Store名义上是太愿意看到应用有自主更新的过程的,因此对于评审的包关闭Patch过程是一个比较稳妥的做法。而评审的包通过之后就会直接让玩家下载,这样就需要同一个包可以动态开关Patch。同时,这样一个包在评审的过程中应该使用评审服务器,而同时也会有外部玩家在玩这个游戏,看到的应该是正式的玩家服务器。这也需要预处理流程来进行干预。

预处理过程通常就是从远端下载一个配置文件,甚至可以包括一些脚本代码,对本地的配置和代码逻辑产生影响。这里注意两点:

  • 配置文件注意加密,避免被劫持产生一些风险;
  • 为了应对评审需求,需要通过版本号进行一些配置应用控制。
5.4 Patch地址重定向
Patch地址的获取对于大部分正式的包来说都是一样的正式地址,只有平台的区别。但是在游戏开发过程中,总会需要提供各种各样的包来为渠道评审、版号评审等服务器,这时候就需要Patch地址的重定向功能。

其实说白了,重定向就是修改Patch信息的获取地址,就比如前文提到的应用宝的例子,对于应用宝的包,可以把patch地址从patch_android修改为patch_android_yyb。这个重定向的标识符通常会在打包的时候塞在包里,让这个包明确知道自己是一个做什么目的的包,而在Patch文件生成的过程中,这个信息是不会存在的,也就是说我们的Patch只有版本的概念,而没有针对某些特定的渠道或者评审需求的逻辑,任何包都可以被导向到这个Patch目录去进行下载和更新。这一点对于Patch的可维护性非常重要。

影响Patch重定向的因素可能有很多,我们使用的有:
  • 包标示,即前面所说的打包时放入到包内的特殊标示;
  • 预处理标示,在预处理流程中定义的特殊标示;
  • 服务器标示,用于区分先行服和正式服的标示,方便做后续的根据服务器要求的版本切换Patch的功能。

5.5 是否引擎版本过低判定
在游戏开发过程中,可能会有强制更新的需求,虽然我们不愿这样做,但是在大的资料片更新,或者的确遇到非常严重的bug修复修复的时候,可能会走强制更新的流程。虽然现在渠道的评审速度相对快了不少,但每次强制更新都依然意味着一定比例玩家的流失。

在需要强制更新的情况下,我们需要限制老包的玩家进入,我们使用了一个叫做最低引擎版本号的数字来进行限定,放置在patch_[platform]这个文件中,客户端检查自己包内的引擎版本号是否大约远端下载要求的版本号,如果小于,则提示玩家包无法使用,需要进行更新。

在Unity中,我们使用C#代码的部分的svn版本号来作为这个标示,在打包过程中,这个数据会从基准资源中读取,只有在重建创建基准资源的过程中,才会更新这个最低引擎版本号。

这个做法也未必是最好的方式,仅供参考。
5.6 小节
Patch流程后面的部分在流程图中都已经有基本的说明了,应该也好理解,对于一些细节,出于安全性的考虑,也不方便说得太细,所以还请读者见谅。

6. 总结
这篇博客从下笔到现在中间断断续续写了两周,一来游戏上线的事情实在是太多,二来Patch系统中的内容也太过繁杂,一边写一边纠结——对于切身做过这块,并且深入思考过的朋友,可能对于某些问题的处理心有戚戚焉,而没有真正做过这块的读者,可能不太好体会这些设计和做法的原因。我也是经历过两个完整的项目之后,才对于这套东西有了自认为非常清晰的认识和理解。

即便如此,在应对“可降版本”的开发需求的时候,依然和两位同事进行了多次深入的讨论和思考,才敲定最终的实现方案。(这部分的实现并不复杂,核心理念是加入对于已经下载的文件删除的功能,具体细节如果真的有项目组需求而且有疑惑可以私聊讨论。)

终归来说,想用一句话来总结我对于Patch系统的态度——
Patch系统无小事。
在游戏上线之后,Patch系统的维护成本非常高,而可能会造成的影响也非常大,包括无法正常游戏(比如脚本资源损坏)和一些很难定位问题的bug(因为拿不到线程),所以在设计、实现以及后续修改Patch系统的时候要额外小心,在开发过程中对于Patch系统表现出来的“偶现”错误也要足够的重视,尽量排查出根本的原因。

出于安全性的考虑,我用抽象的流程在规避一些实现细节的同时期望可以尽量表达清楚我想表达的观点和方法。我想这也是目前关于Patch系统的文章比较少的第二个原因吧。也希望这篇文章可以起到抛砖引玉的作用,让更多有经验的朋友分享一下他们的经验和踩过的坑,从而让大家可以构建出更加稳定、健壮、灵活的Patch系统。
知乎@Funny David




跟我念“站长妹纸萌萌哒!”我说站长,你说YO!爱你们么么哒~
回复

使用道具 举报

排名
26963
昨日变化
9

0

主题

23

帖子

40

积分

Rank: 1

UID
304200
好友
1
蛮牛币
110
威望
0
注册时间
2018-11-11
在线时间
3 小时
最后登录
2018-12-7
发表于 2018-12-7 15:31:04 | 显示全部楼层
基于累积差异的Patch系统实现

回复 支持 反对

使用道具 举报

排名
17798
昨日变化
6

0

主题

7

帖子

67

积分

Rank: 2Rank: 2

UID
291793
好友
0
蛮牛币
73
威望
0
注册时间
2018-7-30
在线时间
24 小时
最后登录
2019-1-4
发表于 2018-12-7 18:18:36 | 显示全部楼层
666666666666666666

回复 支持 反对

使用道具 举报

7日久生情
2195/5000
排名
1402
昨日变化
5

0

主题

681

帖子

2195

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
135463
好友
0
蛮牛币
290
威望
0
注册时间
2016-1-23
在线时间
628 小时
最后登录
2019-3-19
发表于 2018-12-7 21:45:59 | 显示全部楼层
哦i活泼i和平【9哦i金牌

回复 支持 反对

使用道具 举报

7日久生情
2195/5000
排名
1402
昨日变化
5

0

主题

681

帖子

2195

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
135463
好友
0
蛮牛币
290
威望
0
注册时间
2016-1-23
在线时间
628 小时
最后登录
2019-3-19
发表于 2018-12-8 09:25:55 | 显示全部楼层
hgvkuhjgvkuhkuh

回复 支持 反对

使用道具 举报

7日久生情
3029/5000
排名
230
昨日变化

3

主题

181

帖子

3029

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
40098
好友
0
蛮牛币
13906
威望
0
注册时间
2014-8-15
在线时间
781 小时
最后登录
2019-3-19
QQ
发表于 2018-12-9 10:31:25 | 显示全部楼层
Mark Mark Mark
[发帖际遇]: 一个袋子砸在了 mytool 头上,mytool 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

5熟悉之中
716/1000
排名
4122
昨日变化
2

1

主题

222

帖子

716

积分

Rank: 5Rank: 5

UID
245227
好友
0
蛮牛币
943
威望
0
注册时间
2017-9-21
在线时间
135 小时
最后登录
2019-3-15
发表于 2018-12-10 09:44:45 | 显示全部楼层
6666666666666666666666666666

回复 支持 反对

使用道具 举报

7日久生情
1868/5000
排名
909
昨日变化

0

主题

118

帖子

1868

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
35660
好友
0
蛮牛币
2762
威望
0
注册时间
2014-7-22
在线时间
626 小时
最后登录
2019-3-13
发表于 2018-12-10 12:17:39 | 显示全部楼层
总结的挺好的,确实维护的成本挺高的

回复 支持 反对

使用道具 举报

5熟悉之中
872/1000
排名
5633
昨日变化
2

0

主题

448

帖子

872

积分

Rank: 5Rank: 5

UID
146677
好友
9
蛮牛币
2782
威望
0
注册时间
2016-4-25
在线时间
175 小时
最后登录
2019-3-4
QQ
发表于 2018-12-10 14:26:37 | 显示全部楼层
这个 有点意思了,谢谢分享

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1119/1500
排名
2346
昨日变化
3

1

主题

156

帖子

1119

积分

Rank: 6Rank: 6Rank: 6

UID
236305
好友
1
蛮牛币
1554
威望
0
注册时间
2017-8-7
在线时间
358 小时
最后登录
2019-3-19
发表于 2018-12-11 09:58:06 | 显示全部楼层
虽然在做独立游戏 还是学习一下

回复 支持 反对

使用道具 举报

7日久生情
1543/5000
排名
1408
昨日变化
53

2

主题

183

帖子

1543

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
22820
好友
2
蛮牛币
1972
威望
0
注册时间
2014-4-25
在线时间
478 小时
最后登录
2019-3-18
发表于 2018-12-11 17:28:02 | 显示全部楼层
很厉害的样子!!!!!!!!

成为游戏蛮牛
回复

使用道具 举报

5熟悉之中
629/1000
排名
5318
昨日变化
37

2

主题

192

帖子

629

积分

Rank: 5Rank: 5

UID
208267
好友
0
蛮牛币
1316
威望
0
注册时间
2017-2-24
在线时间
165 小时
最后登录
2019-3-19
发表于 2018-12-13 09:53:55 | 显示全部楼层
我仿佛看到她在那里等候

回复 支持 反对

使用道具 举报

7日久生情
1878/5000
排名
2196
昨日变化
4

12

主题

879

帖子

1878

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
218409
好友
3
蛮牛币
5127
威望
0
注册时间
2017-4-19
在线时间
363 小时
最后登录
2019-3-13
发表于 2018-12-13 11:32:39 | 显示全部楼层
66666666666666666666
[发帖际遇]: jiangjhq681211 被钱袋砸中进医院,看病花了 2 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

7日久生情
1878/5000
排名
2196
昨日变化
4

12

主题

879

帖子

1878

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
218409
好友
3
蛮牛币
5127
威望
0
注册时间
2017-4-19
在线时间
363 小时
最后登录
2019-3-13
发表于 2018-12-13 11:33:59 | 显示全部楼层
666666666666666666

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表