怎么理解游戏热更新中的“母包”和“补丁”

我们玩过游戏的都有过这样的经历,一个游戏安装好之后,啥都不用管,每次点开游戏之后读个条,游戏内容就始终是最新的,可能有新的道具、新的活动,可能某个东西过些日子就找不到了。

当我们学过怎么做游戏之后,我们就知道,哦这玩意叫热更新。那么热更新到底是怎么更新的呢?

在传统的win32或者gnu linux中,我们要热更新一个软件或者游戏是非常方便的,因为它们本质上就是一堆文件堆在那儿,点两下就能运行,哪个有变化就更新哪个,完事。

但是在更多的平台上,我们是不能这么任性的,比如说在Android中,我们一个apk安装好一个App上去,这个App就是把屋顶都拆了也没法自己修改自己,你只能用一个新的apk文件来替代它。

而现代很多系统平台,什么uwp啊,iOS啊,xbox啊,都是采用了类似的软件包的设计形式。

那么就有问题了,照理说,这种机制下,我们的软件没法修改自己,那就没法实现热更新啊,怎么办呢,伟大的劳动人民总是心灵手巧的,人们发现:虽然我没法修改自己,但是在很多系统里面,我都可以申请一块存储空间来读写着玩。那,好像是不是可以曲线救国一下呢?

于是出于这种种原因,现代的很多App啊,游戏之类的,就演变出了这么一种热更新思路,首先,我们可以在概念上,把整个App拆成两个部分:

  • 基础部分:底层的、不会经常变动的部分。
  • 业务部分:基于基础部分之上的,变动频繁的部分。

通常,这个基础部分是无法热更新的,如果要修改这部分,就得重新制作安装包给应用商店。而业务部分,则是可以被热更新的,而通常负责业务部分热更新功能的,正是基础部分。


其实每次说到这个部分的时候,我就会想到网页浏览器。我也很喜欢用网页浏览器来作比喻:这个基础部分就是个浏览器,而业务部分就相当于是我们平时浏览器里的网页。这个浏览器在我们本地可能万年不更新,但是每次刷推特刷新闻都能立即看到最新的内容。

当然,其实这也不完全是比喻,比如PWA和某些“所谓的小程序”,就真的是浏览器和网页。


当我们理解了这个部分之后,我们就来进入正题了,在现在很多游戏领域有两个概念:“母包”和“补丁”。从玩家角度来看,大家可能都知道补丁是个什么东西,但是对于母包这个词就有点陌生了,那么母包和补丁包的关系到底是怎样的呢,我们来举个栗子:

比如说,我们在做一个游戏,做着做着,好像差不多了,欸我们发布给用户玩玩吧,怎么发布呢,就是正常的把游戏编译出一个安装包。就比如说是个apk包吧,我们把这种完整的安装包,就称为母包。为了方便描述,我们把这个母包称为“母包1”,这时候的客户端版本,记作1.0

比方说我们的母包1包含了这么些东西

然后,我们把这个1.0客户端给放在了应用商店,提供给用户下载使用。

但是,虽然我们发布了一个版本,但是开发并没有停止,我们还是不断的在往游戏里加新东西,那么到了第二天,我们改动了这么些东西:

我们在昨天的1.0版本的基础上,修改了一个文件,又增加了一个文件。这时候,我们希望我们的玩家的游戏,都同步到这个新版本来,怎么办呢?

最传统的方法就是我再编译一个安装包,扔给应用商店。但是这样做就有问题了,比如说啊,我一个安装包有10个G,但是我就修改了10kb的内容。你说你要是每天更新一次,每次都让玩家下载10个G的安装包,估计玩家四十米的大刀就收不住了,没过几天玩家全跑了没人玩了。那怎么办呢,这时候,就该补丁包出场了。

我们制作了一个补丁包,把我们今天变动的两个文件单独拧了出来,丢在我们的服务器上。然后玩家的客户端会自动下载这个补丁文件,把这两个改动的文件更新到原来的客户端中。

这样一来,玩家手里的客户端就是今天的最新版本了,为了方便说明,我们把这时候的客户端版本记作1.1 

客户端1.1 = 母包1.0 + 补丁1

好的,我们的故事还在继续,第三天,我们又修改了一些内容,变成了这样:

然后我们现在又想把这个最新版本同步更新给玩家了。在刚刚第二天是时候,我们发现补丁包是个好东西,于是到了第三天这里,我们自然而然的就想到了:欸我们再打个补丁吧,就叫补丁2

思路没错,但是要注意的是,这里我们有两种方法来做这个补丁2

方法1:

我们把相对于1.0版本客户端的所有变动的内容,都放在补丁2中

这时候,比如说有个玩家,第一天下载了我们的游戏,版本是1.0,第二天这个玩家没玩,也就没更新成1.1,这时候当他第三天打开我们的游戏之后,游戏只需要直接下载补丁2,就可以把客户端变成最新版本了(记作1.2版本)

但是对于客户端版本是1.1的玩家,这时候就不爽了:“你这个补丁2里有三分之二的文件,我昨天都已经下载过一遍了,这又让我把整个文件下载一遍什么意思,我流量不要钱啊”

方法一中:版本1.2 = 母包1.0 + 始终最新版本的一个补丁

方法2:

我们在补丁1(版本1.1)的基础上,制作补丁2(版本1.2)

这时候,我们当前版本为1.1的玩家,只需要下载一个相对方法1中体积更小的补丁2,就可以把客户端变成最新的1.2版本了。

而版本在1.0的玩家,就得按照顺序,先下载补丁1,把自己变成1.1版本,再下载补丁2,把自己变成1.2版本。

至于说这两个方法哪个更好,其实没有明确答案。这两个方案各有优缺点,也都是被广泛使用的方法。

方法2中,版本1.2 = 母包1.0 + 补丁1 + 补丁2 (依次)

好的,到这一步,我们大概算是明白,母包和补丁之间的关系了,但是还没完,我们的例子继续进入下一步。

第四天,我们没有修改任何东西,但是我们重新编译了一个安装包,记作母包2

欸,我们发现了,这个母包2中的内容,和之前母包1加两补丁的1.2版本的内容是一模一样的。

如果这时候,我们把母包2放在应用商店中给新来的用户下载,那么下载了母包2的用户打开就直接能玩了,不需要再下载补丁来更新客户端。而与此同时,之前下载了我们母包1,并且通过补丁更新到1.2版本的玩家,也不需要重新去下载一个母包2,就可以获得一致的游戏内容。

也就是说:

  1. 母包、补丁包和客户端版本之间的关系并不是一一对应的。
  2. 补丁包是依托于某个特定的母包版本的。不能说我母包2去下载个母包1的补丁,那就乱套了。

(当然,也有那种补丁包不依赖于特定母包版本的骚操作存在,但那就是另一个故事了)


题外话,我们一开始把游戏分为基础部分和业务部分两个部分。对于很多游戏来说,会把javascript、lua、python这些语言代码文件放在业务部分,这样一来,游戏中的绝大部分业务内容就都可以实时更新了。

讲到这一步,对于我们大部分中小团队来说,就完全够用了,那么,看完之后要不要试试看活学活用,自己动手给自己的App/游戏实现一下热更新的功能呢。

yomunsam

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

推荐ヾ(•ω•`)o