3

Unity手游实战:从0开始SLG——本地化篇(五)服务器下发配置

 3 years ago
source link: https://zhuanlan.zhihu.com/p/343294105
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.

Unity手游实战:从0开始SLG——本地化篇(五)服务器下发配置

腾讯 U3D开发工程师

前面几篇文章已经聊过了本地化和多语言的一些基本处理方法。文本、字体、图片、以及语音翻译等等。文本、字体已经讲完,图片因为TMP可以自己产生艺术字效果,所以归于文本一类。基于以上的实现之后,项目已经能够胜任大部分的本地化策略了,但是也仅限于在所有资源都已经预置了的情况下,那么这篇我们就来聊一下基于热更的实现。让多语言文本能够在上线后,也能在服务器的控制下解决多语言的显示问题。

200+篇教程总入口,欢迎收藏:

1 Sproto协议

在之前的客户端技术选型的文章里我们就介绍过,服务器使用的是SkyNet。
Unity手游实战:从0开始SLG——客户端技术选型

Sproto 是谷歌Proto的一个变种,是云风基于SkyNet实现的一种优化格式。之所以选择这个格式是因为SkyNet对Proto的支持只到2.x,并且已经不再维护了。而Sproto更符合目前服务器在Lua端的性能表现。为了配合服务器的通信协议格式,客户端也选择使用Sproto。

详细的内容以及扩展阅读可以参照技术选型文章里的Sproto部分。

2 Sproto 表格

客户端关注两个部分的数据,一部分来自运行时服务器的下发,一部分来自静态的策划配置。既然和服务器通信采用的是Sproto格式,那就没理由再整一套额外的数据格式来处理策划数据。

但,策划大部分时候都是用Excel编辑数据,所以我们需要制作一个导表工具,用来将表格数据转化为以Sproto格式序列化的Byte文件,就像Proto一样。

这里的转换过程有点复杂,一言难尽,有空我们讲导表工具的时候再进行详细阐述。总而言之,就是客户端将服务器的通信协议和策划的表格配置统一成了Sproto格式读取和处理。这带来了很多好处,比如要联调服务器数据的时候,我们完全可以使用表格伪造数据进行联调,或者将服务器下发的数据持久化存储,变为静态数据的一部分。同时这样也可以让策划把一部分敏感数据配置在服务器,登陆之后再由服务器下发存储到安全的地方,防止反编译。

当然这些特性其实我们都没有做,只是用到了服务器数据格式和策划数据格式一致的特性,进行了部分表格的增改。当然这里就包括对多语言的更改。

3 增改结构

客户端对于不同的数据源也是分开存储的,策划数据汇总于DataManager,而服务器数据汇总于NetDataManager。二者之间基本不会有交互,只有一种情况下除外。服务器在下发了登陆成功协议之后,紧跟着会下发策划数据表的热更新Bytes。

这里需要和传统的客户端资源热更新形成区别。客户端的热更新是静态的,走的是CDN,它是一种常规的管线功能,更新的资源体量大,种类多。但是只会在游戏登陆之前(一般是刚启动)。而我们这种功能实现是为了应急或者修补BUG而应用的备份策略。服务器的流量本来就很贵,不应该用来下发大量的静态/表格数据。同时作为应急功能,它不能被过渡使用从而导致过多过于复杂的逻辑。

我们会在一个专门的导表目录里放置需要热更的表格数据,导出之后,服务器会识别到该目录下的所有文件,根据文件名建立名字-数据索引,然后存储在内存中,客户端连接上之后,以专门的协议下发,客户端收到之后,和缓存中的数据进行比较,没有的增补,已有的覆盖。

这个功能对于服务器来说是热重载的,对于那些已经在线的客户端,也可以通过运营工具进行广播,从而完成服务器不停机,客户端不重启,便对数据或者BUG进行修复。

4 屏蔽词实现

屏蔽词有些麻烦。为了兼顾查找和替换效率,我们是使用了 trie Tree的方式进行的。如果有不清楚的,可以返回看这篇文章。
Unity手游实战:从0开始SLG——独立功能扩展(二)使用DFA处理屏蔽字

因为屏蔽词的条目非常之多(我们过审的版本有52万条,文本文件就接近10M),所以必须提前将屏蔽词进行离线处理,然后运行时进行加载(不然每次登陆你可能要等10几秒)。但这也带来了额外的问题,因为它不像其他表格数据一样是离散的,所以不能使用上述方法进行增量下发。

而由于屏蔽词的树是提前遍历所有词组,然后离线生成的,它也不具备动态插入功能(重新生成整棵树代价很大)。所以针对屏蔽词我们使用了另一套规则。既然不能合在一起,干脆就完全独立为两棵树。数据格式是一样的,只是数据源不一样,那么我们就创建两个版本,Server版本和Client版本。Client版本多且大,离线生成。服务器版本小且动态生成。当需要查询屏蔽词的时候,优先查询服务器版本,没有再查Client。

而这个时候,数据下发依然可以通过上述的框架结构进行数据增补,每次服务器有屏蔽词下发,就重建一次服务器屏蔽词树,因为服务器增补的条目数一般都很少,几乎不会造成性能问题(即使有也是一瞬间)。

我们在版署过审中,使用该方案减少了多次打回出包的问题。

5 复合配置热修复

所谓复合配置就是有些问题单张数据表无法修复的。比如系统邮件,如果本身邮件的数据表就缺少数据模板,这个时候我们不仅仅要下发语言相关的内容,还需要下发邮件的增补数据。同理如果邮件还涉及到新的奖励物品,也可以通过下发Item表进行增补。当然这些使用到的都是基于上述的增补框架。

6 运营介入

这部分涉及到的是运营的各种公告和通知。比如紧急停机,突发状况等等,这些都是无法预料的,自然也无法事先将语言或者数据配置在表格中。而上述我们所处理的都是游戏功能,属于策划层面的BUG修复。但是到运营这个层面就不同了,首选运营编辑公告或者处理不同地区服务器的时候不应该都经过策划或者开发组。事实上游戏运营者可能和开发者完全是不同的公司。如果是在不同的国家的当地发行,甚至语言都不通。

这个时候就需要一套完整的工具链来帮助运营直接使用该功能。前面我们聊了这么多也知道,服务器和客户端之间进行数据增补是完全没有问题的,现在需要在这条链路里增加一个控制方。确切的说应该是变更一个控制方。因为现有的链路服务器和客户端交互是在某个特殊的目录下,存有特殊的byte文件。而这个文件是策划导表生成的。服务器读取文件,转化为二进制,通过协议下发。

而我们稍微转变一下思路,即提供一个运营工具,让他们可以将需要的配置通过工具生成一个指定的文件或者二进制缓存,传递到服务器,然后就可以按照正常流程进行数据增补了。

本个小节聊的比较浅,但大致思路都讲清楚了。接下来我们就最后聊一下翻译和语音。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK