王者荣耀实现原理学习笔记

原作来自腾讯游戏学院

以下是原文部分节选

一、服务器架构

“房间模式”

房间类玩法和MMORPG有很大不同:

1.广播单元的不确定性

2.广播数量很小

3.需要匹配一台房间服务器让少数人进入同一个服务器

这一类游戏最重要的是其“游戏大厅”的承载量,每个“游戏房间”受逻辑所限,需要维持和广播的玩家数据是有限的,但“游戏大厅”需要维持相当高的在线用户数,所以仍然需要“分服”。

大厅:“自动匹配”玩家进入一个“游戏房间”,这需要对所有在线玩家做搜索和过滤,以及为了更好的体验,会对玩家分地区匹配,以方便获得更快速的同步。

大体流程:

登录------>大厅服务器------>选择组队游戏功能------>服务器通知参与的所有客户端------>新开一条连接------>连接到房间服务器------>用户在房间服务器里进行交互

二、通信方式

1.http:每次通信完成以后都会断开(这种方式对于需要频繁交互的双方来说效率太低)

2.socket:适用于实时性要求较高的游戏

(socket通信有TCP和UDP两种,其区别如下,王者荣耀采用的是UDP)

王者荣耀实现原理学习笔记_第1张图片

为什么采用UDP?

(1)tcp保证数据可靠是有代价的

 TCP发送一个包,等待一段时间,直到检测到数据包丢失,如果没有接收到它的ACK(Acknowledgement——确认字符,表示发来的数据已准确无误地接收),接下来就会重新发送丢失的数据包到目标计算机。重复的数据包将被丢弃在接收端,乱序的数据包将被重新排序。以此来保证包的可靠性和有序性。TCP:无论什么情况,只要数据包出错,就必须等待数据包重发,即使最新的数据已经到达,但还是不能访问这些数据包,新的数据会被放在一个队列中,需要等待丢失的包重新发送过来之后,所有数据没有丢失才可以访问。(当出现一个数据包丢失的情况,所有事情都需要停下来等待这个数据包重发,客户端会出现等待接收数据、操作出现卡顿和响应不及时的现象。)

(2)udp的可靠性 —— 手动DIY组装

UDP不能保证包的顺序,比如第100个收到的数据包不一定是第100个发出的数据包,同时也无法保证不丢包,期间有一个包丢失,udp是不会去校验的。

解决UDP可靠性的关键:顺序、丢包

大体解决思路:

一、为每个数据包增加序列号,每发一次包,增加本地序列号。

二、每个数据包增加一段位域,用来容纳多个确认字符。确认字符的数目由应用发包速率来决定,速率越高,确认字符的数量也相应越多。

三、每次收到包,把收到的包上的序列号变为确认字符,发送包的时候带上这些确认字符。

四、如果从确认字符里面发现某个数据包有丢失,把它留给应用程序来编写一个包含丢失数据的新的数据包,必要的话,这个包还会用一个新的序列号发送。

五、多次收到同一包的时候可以放弃它。

三、同步方案

状态同步:采用c/s架构,所有状态由服务器来控制,安全性比较高,但流量比较大。(大型MMORPG)

帧同步:采用囚徒模式,所有客户端强制采用一个逻辑帧率,从而保证输出一致,其特点是流量小,安全性较差。

1.帧率

2.Lockstep ----- 帧同步

将一个游戏看成一个巨大的状态机,所有参与者都采用同一个逻辑帧率来不断向前推进


王者荣耀实现原理学习笔记_第2张图片
王者荣耀实现原理学习笔记_第3张图片

A B C 分别表示三个玩家的时间轴,这个时间轴不是电脑上的本地时间,而是A、B、C联机时定义的一个时间轴。

虚线分割出来的时间片称为“turn”,可以理解成一“帧”。箭头表示玩家将自己的操作指令广播给其他玩家。

一盘游戏可以看成一个大型的状态机,因为是同一个游戏,因此初始状态S0和帧率F是相同的。在第1个“turn”结束时,所有玩家都接收到完全一样的输入 I(I指的是当前游戏中所有玩家的操作指令集);由于帧率 F 和 S0 以及 I 是固定的,所以在 t1 时刻每个玩家电脑上计算出的下一个状态 S1 一定是相同的。

由上面可以知道:

1.游戏的过程就是每一个“turn”不断向前推进的过程,每一个玩家的“turn”推进速度一致。(这里的“turn”借鉴了“帧”的概念,与游戏的渲染帧率不是同一个概念)

2.每一帧只有当服务器集齐了所有玩家的操作指令,也就是输入I确定了之后才可以进行计算,进入下一个“turn”,否则就要等待最慢的玩家。所有玩家的操作指令收集齐后再广播给所有的玩家,如此才能保证帧同步。

3.LockStep的游戏是严格“turn”向前推进的,如果有人延迟比较高,其他玩家必须等待该玩家跟上之后再继续计算,不存在某个玩家领先或者落后其他玩家若干个“turn”的情况。使用LockStep同步机制的游戏中,每个玩家的延迟都等于延迟最高的那个人的延迟。

4.由于大家的“turn”一致,以及输入固定,所以每一个“turn”各个客户端的计算结果都是一致的。

具体执行流程:


王者荣耀实现原理学习笔记_第4张图片

这种囚徒模式的帧同步,在第2个“turn”的时候,因为玩家1有延迟,而导致第二帧的同步时间发生延迟,从而导致所有玩家都在等待,出现卡顿现象。

四、乐观锁和断线重连

上面说到囚徒模式的帧同步有一个致命的缺陷:若联网玩家有一个网速慢了,势必影响其他玩家的游戏体验,因为服务器要等待所有玩家的输入到达后再同步到各个客户端。另外如果中途有人掉线了,游戏就会无法继续或者掉线玩家无法重连,因为在严格的帧同步的情况下,中途加入游戏从技术上来讲是非常困难的。因为你重新进来之后,你的初始状态和大家不一致,而且你的状态信息都是丢失状态的,比如你的等级,随机种子,角色的属性信息等。(王者荣耀服务器会保存当场游戏的游戏指令以及状态信息)

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

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

服务器不会等到收集完所有用户的输入后进行下一帧,而是按照固定频率来同步玩家的输入信息到客户端,如果有玩家网络延迟,服务器的帧步进是不会等待的。

通俗理解:网络延迟,指令“迟到”,服务器帮你发送了个空白指令或预设指令。

五、技能同步

“伪随机”

N台电脑采用相同的随机种子,第N次随机的结果是一致的。

游戏开始前,服务器为每个玩家分配一个随机种子,同步给各个客户端,如此每个客户端在计算每个角色的技能时,能保证伤害一致。

帧同步的两种实现形式:

1.服务器要求接收到每一个客户端的帧执行完成消息时才发送执行下一帧的命令(等待每个人)

2.服务器如同一个节拍器,只关心发送时机,不会等待。如果有个客户端没有赶上某一帧的操作收集,那就当他是没有操作处理了。(收作业的时候不交就当你没交了2333333)

MOBA类的帧同步:服务器只是负责一个通知作用,并不会管哪个客户端卡了还是出状况等问题。如果是状态同步的话服务器负责计算结果,客户端负责展现。

如何判断挂机?

服务器在规定时间内没有收到玩家的操作指令。(有些情况是判断攻击指令以防止泉水挂机的现象)

你可能感兴趣的:(王者荣耀实现原理学习笔记)