王者荣耀服务端

时间:2024-04-07 12:45:10

分享要点

  1. 服务器框架
  2. 通信方式
  3. 同步方案
  4. 暴击同步

服务器框架

基本的框架:

王者荣耀服务端

框架描述:

  • PVP对战不分服,信息分区。微信1区可与微信2区一起PVP,ios平台可与Android平台的玩家一起PVP。保留

分区的感念,英雄、信息、排位榜、站队等是基于区的概念。

  • 房间对战系统,在线广播单元不确定性、广播数量很少,需要匹配一台房间服务器让10个玩家进入一个服务器。
  • 一般的逻辑就是玩家登陆“大厅服务器”,然后选择组队或匹配游戏的功能,服务器会通知参与的所有游戏客户端创建一条新的连接到房间服务上,这样就能在一个匹配房间服务器里进行游戏交互。

通信方式

通信方式一般有http和socket两种方式,http每次通信完成后断开连接,无法满足频繁交互的应用,所以游戏一般采用socket方式来通信。

socket通信:TCP、UDP

TCP与UDP的优劣:

王者荣耀服务端

基于游戏的功能与场景不同,采用不同的socket通信,王者荣耀是UDP的。

同步方案

王者荣耀使用的是:Lockstep 帧同s

  • Lockstep基本原理

帧同步可以说是通过帧率延申过来的,一个游戏看作一个巨大的状态机,所有游戏玩家都采用同一个逻辑帧率来不断向前推进。

如下两图:

王者荣耀服务端

 

王者荣耀服务端

 

图中是A、B、C三个玩家的时间轴,这个时间轴不是电脑上的本地时间,而是A、B、C联机时定义的一个时间轴。虚线分隔出来时间片称为turn,可以理解成一帧。箭头表示该玩家将自己的操作指令广播给其他玩家。

我们把一盘游戏看成一个大型的状态机,因为大家玩的是同一款的游戏,因此F是相同的,初始状态S0也是相同的。在第一个turn结束时,所有玩家都接收到了完全一样的输入I,注意这里的I不是一个值,而是包含了当前游戏中所有玩家的操作指令集合。t1时刻所有玩家的电脑自行计算结果。由于F、S0和I是固定的,所以每个玩家电脑上计算出的下一个状态S1一定是相同的。

举个例子,假设A、B、C是游戏中3个互相敌对的单位,攻击力都为100。在某一个turn内,A和B都右键点击了C(warcraft这类游戏好像都是右键普攻),C右键点击了A,这些操作指令都广播到了其他玩家电脑上,则该turn的输入为“A攻击C、B攻击C、C攻击A”。那么该turn结束后,每个人的电脑都开始计算,且计算结果是相同的,即“A损失100生命值,B不变,C损失200生命值”。

通过上面可以知道:

  1. 游戏的前进分为一帧帧,与游戏的渲染帧率不一样,只是借鉴帧的原理,自定义的帧turn,游戏的同步实现是每一个turn不断向前推进的结果,每个游戏玩家的turn推进速度一致。
  2. 每一帧只有当服务器集齐了所有玩家的操作指令,也就是输入确定了之后,才可以进行计算,进入下一个turn,否则就要等待最慢的玩家。之后再广播给所有的玩家。如此才能保证帧一致。
  3. Lockstep的游戏是严格按照turn向前推进的,如果有人延迟比较高,其他玩家必须等待该玩家跟上之后再继续计算,不存在某个玩家领先或落后其他玩家若干个turn的情况。使用Lockstep同步机制的游戏中,每个玩家的延迟都等于延迟最高的那个人。
  4. 由于大家的turn一致,以及输入固定,所以每一步所有客户端的计算结果都一致的。

 

  • Lockstep执行流程

王者荣耀服务端

 上图中我们可以明显看到,这种囚徒模式的帧同步,在第二帧的时候,因为玩家1有延迟,而导致第二帧的同步时间发生延迟,从而导致所有玩家都在等待,出现卡顿现象。

乐观锁&断线重连:

囚徒模式的帧同步,有一个致命的缺陷就是,若联网的玩家有一个网速慢了,势必会影响其他玩家的体验,因为服务器要等待所有输入达到之后再同步到所有的c端。

另外如果中途有人掉线了,游戏就会无法继续或者掉线玩家无法重连,因为在严格的帧同步的情况下,中途加入游戏是从技术上来讲是非常困难的。因为你重新进来之后,你的初始状态和大家不一致,而且你的状态信息都是丢失状态的,比如,你的等级,随机种子,角色的属性信息等。

比如玩过早期的冰封王座都知道,一旦掉线基本这局就废了,需要重开,至于为何没有卡顿的现象,因为那时都是解决方案都是采用局域网的方式,所以基本是没有延迟问题的。

后期为了解决这个问题,如今包括王者荣耀,服务器会保存玩家当场游戏的游戏指令以及状态信息,在玩家断线重连的时候,能够恢复到断线前的状态。

不过这个还是无法解决帧同步的问题,因为严格的帧同步,是要等到所有玩家都输入之后,再去通知广播client更新,如果A服务器一直没有输入同步过来,大家是要等着的,那么如何解决这个问题?

采用“定时不等待”的乐观方式在每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新。如此帧率的时钟在由服务器控制,当客户端有操作的时候及时的发送服务器,然后服务端每秒钟20-50次向所有客户端发送更新消息。如下图:

王者荣耀服务端

 上图中,我们看到服务器不会再等到搜集完所有用户输入再进行下一帧,而是按照固定频率来同步玩家的输入信息到每一个c端,如果有玩家网络延迟,服务器的帧步进是不会等待的,比如上图中,在第二帧的时候,玩家A的网速慢,那么他这个时候,会被网速快的玩家给秒了(其他游戏也差不多)。但是网速慢的玩家不会卡到快的玩家,只会感觉自己操作延迟而已。

暴击同步

我们都知道,王者荣耀中有许多问题是与概率相关的,比如整点时野怪是随机刷新的,出了无尽之后是有概率暴击的。那么按照Lockstep同步机制,计算都是在每个玩家自己手机上完成的,那么在有概率存在的情况下,怎么可能保证每台手机的计算结果一致呢?!

这时使用到伪随机数,我们来看下面这个Java程序

王者荣耀服务端

大部分编程语言内置库里的随机数都是利用线性同余发生器产生的,如果不指定随机种子(Random Seed),默认以当前系统时间戳作为随机种子。一旦指定了随机种子,那么产生的随机数序列就是确定的。就是说两台电脑采用相同的随机种子,第N次随机的结果是一致的。

所以在游戏开始前,服务器为每个玩家分配一个随机种子,然后同步给client,如此每个client在计算每个角色的技能时候,就能保证伤害是一致的。这也是多数帧同步游戏采用的方案,王者荣耀实现就如此。

例如一个英雄的暴击率为30%,对某个目标持续普攻,如果随机数序列为12 32 90 25,小于等于30判定暴击,大于30判定不暴击,那么每个玩家电脑的计算结果都是暴击 不暴击 不暴击 暴击。(这里只是举例说明原理,游戏中的实现细节不一定是这样)

 

注:此文是笔记,写博客方便自己查阅,若不喜,勿喷!