当前位置: 首页 > 科技观察

风靡全国,日活8000万,《王者荣耀》后台技术架构进化!

时间:2023-03-21 23:57:08 科技观察

《王者荣耀》能成为当今中国最成功的手游,背后有成熟的技术团队支持。这个曾在PC游戏时代主导构建RTS游戏《霸三国》框架的技术团队,在转型为MOBA手游后为游戏提供了大力支持《王者荣耀》,但过程并非一帆风顺.在今年刚刚结束的腾讯TGDC上,《王者荣耀》的技术总监孙寻在技术环节对这款游戏进行了技术评测,从技术层面向观众讲解了游戏的引擎、整体网络架构和网络.同步方案中的试验和转变。孙逊表示,目前游戏的服务器架构主要由“游戏大厅”和“PvP”两部分组成。在不断探索的过程中,在架构中加入了Proxy传输服务器。解决了“Android、iOS”服务器等出现的一系列问题。此外,他还介绍了《王者荣耀》在网络协议和同步方案上的一些尝试,并一一回顾了这些尝试的优缺点。回答了为什么,最终的游戏会放弃《霸三国》中使用的TCP协议(TransmissionControlProtocol)和Client-Server结构(C/S结构),转而使用UDP协议(UserDatagramProtocol)和帧同步方案。本文是腾讯王者荣耀项目技术总监孙逊带来的《王者荣耀技术架构》主题演讲内容的总结。将分为几个部分介绍王者后台开发过程中的一些内容和思考:包括《王者荣耀》的整体背景介绍、后台架构、上线后的调整、网络同步方案和防作弊方案等。现在有4600多台后端机器《王者荣耀》,我们的产能也有了一定的扩张,进程数4万多。《王者荣耀》游戏背景2012年,我们当时做的客户端游戏《霸三国OL》就是王者的前身。本产品原本是一款RTS向的游戏。后来我们把它改成端游MOBA,再做成手游MOBA,就是现在的《王者荣耀》。2012年到2013年,我们开始做RTS游戏,从多控RTS游戏转向MOBA游戏。2014年,我们开始手游MOBA的预研。然后在2015年2月,我们投入了大量的人力(约100人)投入到《英雄战迹》(《王者荣耀》的前身)的开发中,没过多久。《霸三国》的玩法是玩家可以通过战前排兵布阵形成自己的游戏策略,通过控制多个单位、释放技能、释放兵种特性形成对抗。刚开始做《霸三国》的时候,client引擎是unreal的,但是做完《王者荣耀》的时候就换成unity引擎了。在开发的3到4个月内,产品本身从代码层面上什么都没有。搬到那边去了,所有的代码都需要重写。《霸三国OL》的一些启示做端游的经历《霸三国OL》给我们带来了很多相应的为王的启示,比如策划、编程以及整个团队对MOBA的理解。另外,我们在做客户端游戏《霸三国》的时候,采用了Client-Server模型,但其实在过程中我们参考了一个类似帧同步的概念:比如视野的处理断开和返回时。传统的方式是返回时发送当前图像和其他后续下行通知信息。这种做法会有问题。如果在场景中添加其他模块,根据当前的各种对象和场景中包含的各种状态信息,需要将这些东西打包发送。在后续开发和维护上会很麻烦。我们的做法是把服务器下发的所有序列包都缓存起来,依次重发,这样客户端就可以进行快进性能。它的概念类似于帧同步。还有一点就是要保留设计的灵活性。在最初的RTS中,每个玩家最多可以操作5-8个单位进行对抗。后来改成MOBA游戏,只能操作一个英雄,各种场景,我们自己的技术框架不需要做颠覆性的改变。《王者荣耀》整体架构目前,《王者荣耀》后台的整体架构设计都是从产品需求中推导出来的。如果你玩过《王者荣耀》,你就会知道PvP对抗并不是分服的。微信1区的玩家可以和微信2区的玩家对战,甚至iOS平台的玩家也可以和安卓平台的人对战,但同时一些共享区也保留了分区的概念,比如团队和排行榜基于“区域”的概念。“区”是游戏中的编号,可以理解为玩家新创建的角色身上的标识。当我们最初实现该架构时,服务器相对简单。从原型开始,就只保留了大厅和PvP服务器,两者是分开的。PvP服务器使用类似的CGI调用,可以分配资源的使用,使用后回收,不负责其他事情。从大堂拿你需要的东西,用完还给大堂,让大堂写回DB。我们在大厅和PvP之间做一个直连,后来把直连改为中间转发。在《王者荣耀》中我们称之为Proxy,相当于一个代理服务器,屏蔽了后端很多进程的分布细节。因为游戏本身有很多机器和进程,以及不同的路由规则。一些排行榜或团队根据逻辑区域的数量来确定处理哪台机器或多台机器。一些消息是随机转发或多路广播的,这些消息由代理路由。后来增加了房间服务器,负责在《王者荣耀》内进行匹配、排名等相关功能。如何将实力相近的人组合起来一起玩,就是匹配房服相应的职责,所以才会有匹配到其他服务器队伍的队伍。最后,我们为其添加了一个Adapter,用于利用已部署的大区资源实现跨服匹配功能。在游戏的后端架构中,除了像战队这样的服务器,其他所有模块都可以在线扩展,或者当发现故障导致掉线时,自动从整个架构中屏蔽掉。因为路由方式会限制例如区域1、2、3到本机处理,如果出现故障,只会影响某些逻辑区域的玩家请求的处理,减少故障的范围。《王者荣耀》目前每周可能会发现有多少台机器坏掉,至少有一台机器宕机。在架构中保证模块的自动屏蔽和在线扩展是非常重要的。整体结构更像是MMO的三层结构。MMO在腾讯有典型的三层架构。大厅服务器会根据玩家的区域登录到特定区域的大厅服务器。单个大厅流程可以容纳20,000人,单个PvP可以容纳12,000人。社群登录微信一区或二区时,角色标志会显示在玩家身上。《王者荣耀》目前外网有安卓手机Q、安卓微信、iOS手机Q、iOS微信四大区,还有抢先服务器。我们会使用程序切换的方式,在大版本发布前优先更新抢占服务器。此时无法匹配到官服玩家,因为版本不一致。当全服发布且版本更新一致时,我们会打开开关,预服玩家可以与正式服玩家进行PvP对战。此外,我们还有专门的体验服务器,专为策划和验证而设计。体验服务器保留删除文件的可能性,但在正式环境下绝对不允许这样做。另外,以前的传统手游都是单机的,所以兼容了很多协议,客户端版本也没有更新可以玩。但是,《王者荣耀》的主要玩法是PvP,结合实现方式,不同版本的玩家无法匹配在一起,所以我们没有做多版本协议兼容。上线后调整上线后,《王者荣耀》本身的背景结构整体上并没有太大的变化,因为我们在做端游的时候,对这个结构比较清楚,知道哪里可能会出问题.所以整个结构一直比较稳定。但是我们做了相应的微调,我们做的最多的是网络本身的优化。《王者荣耀》刚上线的时候,市面上对网络时效性要求比较高的即时PvP游戏比较少。我们做了各种尝试,比如在网络上优化CPU性能、延迟、丢包等,而网络本身耗时最多。架构上的微调,就像刚才提到的传送模块,在我们的架构中有很多大厅机器和很多PvP机器。在架构中,每个进程不需要知道详细信息。比如大堂服务器不需要知道后面有多少个房间服务器,只需要知道后面有一个房间服务器,能访问就OK了。如何划分、均衡负载,如何屏蔽后端故障节点,都是代理路由功能的职责。因为大厅和PvP机器太多,我们使用Proxy将整个架构划分为相互不重叠的“树枝”概念。每组Proxy只负责一部分大厅和PvP服务器。这两类服务器在《王者荣耀》服务器中是最多的,但是除了后端通信之外,还建立了Proxies之间的连接,以减少单个Proxy通道的数量,同时保持整个结构的通信。ProxyAdapter是上线后添加的。一开始只有手Q、微信、安卓、iOS四个大区。最早的Android玩家无法破解iOS。当初Android和iOS的分离也是有一些原因的。我们之前假设先更新Android,然后再更新iOS,以保持版本更新的稳定性。但后来我们希望Android和iOS玩家能一起hack,因为有关系链。所以在Android和iOS版本更新频率相同的情况下,我们希望不需要部署太多额外的机器资源和开发,直接利用Android和iOS现有的PvP服务器和区域资源来打通Android和iOS的PvP。安卓玩家登录安卓区会连接到安卓区大厅,iOS登录后会连接到iOS区大厅。当他们需要打开一个黑盒时,我们会通过Adapter桥接传输模块的所有区域,并将它们传递到某个区域。配送方式的选择直接关系到区域资源的占比。网络同步方案之前在做《霸三国》时,采用了Client-Server模式,由服务端判断客户端的性能。那为什么我们在做《王者荣耀》的时候选择了帧同步的方式呢?Client-Server模式的优点是:第一,Safety。因为都是服务端计算的,客户端只负责性能层面的功能,不会影响各种判断的结果。另外,由于Client-Server模式是基于结果的,中途可能会出现丢包,丢包是可以接受和处理的,只要最终结果重发一致即可。帧同步在客户端游戏中被广泛使用。大家熟悉的DotA和《星际争霸》都采用了帧同步技术。帧同步本身对网络的要求比较严格。下发的执行顺序不允许丢包,必须严格保证顺序。如果数据包是12345,则必须是12345。如果数据包丢失,则必须等待丢失的数据包到达。顺序执行。MOBA本身就有很多单元,客户端同屏最多可以有近百个单元。如果一个AOE技能打到20个单位,然后种下一个debuff,Client-Server状态模式需要把这些信息下发,这样可能潜在同步的Status信息就更多了。Client-Server模型本身的另一种发展方式是很难将客户端性能和服务器判断完美匹配。当我们在开发移动MOBA时,我们需要两到三周的时间来开发英雄技能。《王者荣耀》当时的开发周期是三四个月。在这样的时间压力下,我们无法用Client-Server的方式来处理,时间也不够。当时团队还挺紧张的,因为当时市面上还没有这种PvP强、时效性高的手游。帧同步网络的抗抖动能力比较弱,因为它不能丢包。帧同步的基本原理,有兴趣的可以下来自己了解一下。一般都会有区分,是网络模式还是主机模式。这项技术的关键点在于办公室的计算都是基于客户端的计算。10个人中,每个人都会计算自己的份额。它们具有相同的开始,相同的输入,以及完全相同的中间计算逻辑。没有随机过程。时间计算的结果理论上应该是一致的。即使是浮点运算也不应该存在,它有精度问题。包括很多碰撞,动画,基础的数学运算库都是后台自己实现的。需要浮点整形以避免客户端的本地逻辑。这是最容易犯的错误,也是不同步的最常见原因。如果不是很有经验的客户端程序,在写程序的时候用本地代码做相应的逻辑,可能会越跑越远,10个人都是平行世界。整体网络结构大致分为三层:服务端、客户端逻辑层、客户端表现层。服务端主要负责两个功能:收集所有播放器的上游输入,每隔一段时间将其打包成一个输入序列,传递给所有客户端。当客户端发生丢包时,服务器重发;并替换客户端冗余的上行信息。例如,当新输入到达时,旧输入将被丢弃或替换。在中,我们的逻辑是每66毫秒同步15个包,这是必不可少的,因为帧同步不能丢包,数据包必须有严格的执行顺序。客户端逻辑层理解为客户端的本地服务,即所有客户端的结果必须是强一致的,不能有真正的随机性,不能有本地逻辑,不能有浮点计算。给定相同的输入,结果必须是一致的。客户端表示层将逻辑层中的数据进行复制或镜像,然后对表示层进行平滑处理。帧数不同,但不会影响最终的计算结果,只会影响动画和动作表现。PvP刚上线的时候,我们用的是TCP技术。TCP在局域网的情况下表现很好,没有问题,但是在外网出现丢包或者抖动的时候,受限于实现方式。比如由于windows、启动慢等各种原因,你会发现游戏在重连的时候很卡,所以我们就没有使用TCP,改成了UDP。如果丢包,服务器会在应用层补发。UDP受MTU(最大传输单元)大小的限制。如果大于MTU,会出现分包,可能会导致整个包丢失。所以,我们也会有一些比较大的包,在App层会被服务端分包。如果中途有丢包,服务器会补发,碎片包拼成一个包再拆包。比较有价值的是UDP包。如果手机因为信号抖动等原因丢包,通过冗余发送是更有效的解决方案。帧同步的消息比较小。按照每秒15个驱动帧的理论,20分钟的视频录制约10M。但据我们外网统计,一场正常的5V5比赛时长为20分钟,视频大小约为3M。服务器会将玩家的操作存储在纯内存中。当有丢包时,服务器会通过号码快速找到缓存的信息并发送。同时根据丢包的情况,我们会计算发送冗余给这个人的变化量。一开始,每发送一个数据包,都会重复前面3帧的信息。如果丢包严重,我们会尝试冗余更多的信息再发送。客户端拿到后,会尽量压缩逻辑执行过程。帧同步模式比较麻烦的是它不像Client-Server模式,随时进出。崩溃后必须从头开始运行,不能省略中间的计算过程。当然,我们也尝试过其他的方法。比如客户端上传后,不需要服务端定时采集下发,而是直接通过染色帧号进行下发,这样响应更及时,操作反馈更强更快。我们当时做出的结果是手感的改善微乎其微,但带来的负面问题却很大,因为不是固定的每秒15个包的发送,而是发送的包数非常多,完全不同从这个人。操作习惯有关系。一个人可能在一秒钟内产生十多个或二十多个输入,这些输入需要打包并交付给客户端。因为客户端收到很多包,设备会明显发热。我们也和其他部门合作开发类似TCP的技术。大家直观的认为,丢包的话,会在IO层重发。但是实际的结果会发现这个技术是低级的,所以对丢包的控制不是那么灵活,可能的结果还不如TCP本身。传统的帧同步方式会做延时传递,我们也尝试过。如果区间内出现丢包,或者下行时出现网络波动,可以通过延迟下发来平滑抖动和丢包。之所以尝试了这个方案,最后没有做,是因为:《王者荣耀》体验中有些大侠感觉比较偏动作,响应要求比较快。低于我们的要求。另外,在实现Client-Server方式的时候,一般都有套路,客户端会提前执行,根据服务器的性能平滑或者拉取。我们也尝试过这个方案,但最后还是放弃了,因为这个技术会让角色本身的表现有点飘。如果客户端在本地移动,客户端的性能会立即跟随,但根据服务器的下行链路,实际上会进行一些偏移或修正。当出现网络抖动的时候,字符会有点抖动,所以我们放弃了这个方案。帧同步方案,所有的client都进行计算,期望得到一致的结果,但是如果因为bug或者有人使用了modifier,结果就会和别人不一样,当出现不一样的时候,我们说不同步。我们会定期提取一些关键信息做Hash,不同步的人的Hash会和别人不一样。《王者荣耀》上线时不同步率在2%左右,即100回合中可能有2人出现1人或多人,结果和别人不一样。我们现在已经实现了万分之三的不同步率,这只发生在万分之三的回合中。这是如何改进的?如果使用帧同步,肯定会遇到不同步的问题。如果客户端写错了,使用了本地逻辑,浮点数的计算误差可能会达到这样一个临界点,就会产生计算结果。不一致。我们有很多方法:自动化测试,用机器人不断运行,比如在上线一个新英雄之前,有一个脚本测试不断运行,看是否会出现不同步的结果;先测试网络,先暴露问题,再解决问题。另外,在不同步的时候,我们会把整个录音和客户端之间的日志上传保存,这样我们可以根据录音和中间执行的日志顺序,快速定位到问题出在哪里。我们对延迟和单轮质量也有相应的监控。这一轮有没有卡住或者多少次,有没有丢包,丢包多少,最大时延,最大抖动我们都有对应的记录。和统计数据。运营部的同学给我们提供了很多帮助,我们会集成相关的SDK进行网络测速和问题分析。根据我们自己的统计,游戏卡顿的原因主要有以下几个:社区的带宽比较繁忙,很多社区其实都是公共带宽出口。比如有人在下载电影或者看直播,占用带宽很大。会卡。Wi-Fi路由器的延迟相对较高。如果家里的Wi-Fi路由器长时间没有重启,会出现终端过多、信道干扰、大流量应用下载等情况,也会影响你的播放《王者荣耀》。手机信号差,信号抖动,Wi-Fi,4G空口丢包等等,我们在网络优化方面做了很多尝试,比如在丢包的基础上增加冗余,进而优化我们各个方面的效率执行以减少CPU使用率。《王者荣耀》在后台,有两点我们一直在努力,网络优化和匹配机制。我们尝试使用各种方法,后期甚至会尝试使用AI深度学习的方法,来更准确的定位玩家的真实水平,让他能够匹配到更真实的对手和同水平的队友。孙逊,腾讯王者荣耀项目技术总监,2005年加入腾讯,一开始并没有做游戏。2007年之前,他一直在拍拍网工作。2007年加入成都卧龙工作室,即现天美L1工作室。之前参加了《封神记》、《封神记》、《霸三国OL》、后来的《王者荣耀》,现在是这个游戏的技术总监。