前言

  我们的游戏是一款以忍者格斗为题材的ACT游戏,其主打的玩法是PVE推图及PVP 竞技。在剧情模式中,高度还原剧情再次使不少玩家泪目。而竞技场的乐趣,伴随着赛季和各种赛事相继而来,也深受玩家喜爱,从各直播平台几万到几十万的观众可见一斑。然而,在移动端推出实时PK并不是一蹴而就的,本文将向大家介绍游戏的实时PVP相关技术。

技术选型

  实时PK的表现方式,是将N个玩家的行为快速同步给其它玩家展示并保持一致性的过程。这里面涉及到几个要思考的要点:

  • 同步什么?可以是玩家具体操作(如移动操作),也可以是某按键操作(如方向键),这两者是有些微区别的。
  • 怎么同步?可以选择方式多种,传统的C/S模式,或者是P2P形式,或者是帧同步等。
  • 同步方式?载体可以是TCP/UDP。使用哪个比较靠谱?

  基于以上的考量,在游戏中,使用的是基于可靠UDP的帧同步模型作为实时PVP的技术方案。你问为什么不采用TCP,为什么不用C/S,为什么不上P2P,下文分晓。

实现细则

  这里讲述一些重要细节,以解决众多的Why not问题。

使用帧同步模型

  为什么选择帧同步,直接原因是继承之前AI(机甲旋风)经验,对于ACT类型游戏,我们认为帧同步是不错的方案,主要是能够获得以下好处:

  • 高一致性。对于格斗中的技能连招,如果不精确到帧,会出现一些诡异现象。试想某个浮空下落的角色,可能一方客户端看到已经倒地,另一方在未倒地时接上其它技能,会出现两个结果,即使将其拉扯回,同样奇怪。而帧同步的机制,保证了双方客户端的一致性。
  • 服务器逻辑简化。传统的C/S 架构下,在服务器计算及校验,大量的核心逻辑需要客户端及服务器都实现一遍,使用帧同步可以大大简化及保证服务器的稳定性。
  • 低流量消耗。在移动网络中,用户的流量即金钱,如果我们游戏的核心部分耗流量严重,那让45%1左右的非wifi用户情何以堪呢?
  • 反作弊。讲道理来说,帧同步对于反作弊并不友好,但是有一个简单的做法可以快速反作弊,就是双方数据不一致时(上报的校验数据或者战斗结果),即检测到有人作弊。

  那么,帧同步的过程是如何进行的呢?下图演示了两个客户端PlayerA/PlayerB和Server交互的过程。

  对于客户端而言,不断的上报其行为(action)至服务器,并且等待服务器下发的帧包驱动其逻辑。这种方式是帧锁定同步(lockstep)的一种演化 2。
对于服务器来说,固定帧间隔(66ms)将队列中的PlayerA/PlayerB的actions放在一个Frame中,并同步给两个客户端,这似乎和BucketSync类似。

  我常被问到一个问题,对方的卡顿会不会影响我的游戏体验,从以上我们的同步原理中,或许你可以找到答案。

使用UDP代替TCP

  帧同步并对协议层是TCP还是UDP并无要求,但我们打一开始就没考虑TCP直接拥抱UDP,究其原因,若是对TCP的特性稍有了解,大概会背那三字经:“慢启动”,“快重传”,“拥塞避免”(三个字!)。我概括它以下几点不太适合对实时性要求高,对延迟敏感的移动网络游戏:

  • 慢启动算法不适合移动网络的情景,在移动网络环境下信号时好时坏是常态,慢启动会使数据不能及时快速达到对端。
  • 拥塞避免算法不适合移动网络主要原因是其考虑到网络的公平性及收敛性,并且AIMD 算法会使实时性大受影响,延迟明显提升。

  还有TCP协议用于重传的RTO的指数变化及拥塞算法的实现Nagle的缓存等,都是TCP并不太适合高实时性要求的游戏玩法的原因,不再一一列举。

  那么为什么UDP对比TCP更合适呢?多说无益,看一组数据:

  显而易见,在各种网络情形下,UDP的表现(延迟分布)基本上都优于TCP。测试程序在相同的网络环境下,通过设置一定的延迟,丢包率,抖动等,获得TCP/UDP的RTT3 。

  对于P2P的选择,我们放弃的原因是本身UDP打洞并非100%成功,而若打洞失败则仍要走服务器转发,故简化设计考虑,未选择P2P方式去同步。

自己DIY可靠UDP

  上面讲了用什么(UDP)同步什么(帧数据)的问题,有同学要问了,UDP不可靠传输,丢包怎么办?当然,为了数据一致,我们不允许(或许可以允许少量)丢包,TCP的可靠性是由ack/seq+重传去保证的,世面上大多数的可靠UDP实现,也都是类似原理。

  在考察了几个可靠UDP的实现(UDT,ENet等)觉得略显复杂,并且在我们开发时,公司内部的可靠UDP实现未达到可使用阶段,鉴于自己重新造个轮子并不复杂,于是挽起袖子写了起来。

  用于可靠UDP的实现,其UDP协议自定义包头是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//客户端上行包
typedef struct UdpPkgRecvHead
{
    uint16_t seq; //start from 1
    uint16_t ack; //ack server seq
    uint16_t sid; //player uid
    uint8_t  action_len; //pkg actions contain
}UdpPkgRecvHead;
//...
//服务器下行包
typedef struct UdpPkgSendHead
{
    uint16_t seq; //inc when frame id ++
    uint16_t ack; //ack client pkg's seq
    uint8_t frame_len; //pkg frames contain
}UdpPkgSendHead;

  基于以上的定义,可靠性保证的过程大概如图如示:

  客户端在满足以下条件之一后,发包给服务器:

  • 玩家有操作时
  • 发包后间隔(99ms)未收到回包时
  • 收到包一定间隔(99ms)后

  若玩家有操作,则确认信息随着玩家的操作上行包“捎带”至服务器 ; 如果无操作,则固定时间上报确认信息给服务器。客户端的seq每一个操作行为(action)时加1,服务器seq在每一帧数据下发时加1 ,并且双方的RTO 取值不同4。

  对于可靠性的保证,可以采用请求重传,而我们使用的是冗余重传。使用冗余重传的一个好处是,简化了麻烦的时序问题,并且收到的每个包都是完整的顺序的。对于网络拥塞情况下的带宽利用优于TCP,它不足之处是流量略微增加了些。下图是冗余重传的过程:

  图解如下:

  Client发Action1过来,记seq=1,服务器未收到。
  Client又新增了Action2,此时新包将同时包含Action1,Action2,并且seq=2。
  Server确认了上一步骤的包,发给Client的包Ack=2表示确认。
  Client由于某些原因(可能延迟等)尚未收到Server的Ack=2的确认,此时新增Action3,并发包seq=3。
  Client再次发Action4时,发现之前已经Ack=2了,故新包将只带Action3,Action4并且seq=4。

  这里演示了冗余传输的过程,服务器对于收到的包,可以根据seq/ack情况动态去除冗余或者丢弃过期包。可能你会觉得全冗余是否不太合适并且有明显优化空间?在实际现网长期运行中,全冗余的冗余率是100%左右,相比于一些可靠传输的重发最近三帧等方式,这种为可靠性付出的代价是合适的并且也提高了更多实时性。

  小结:在刨除一些优化及细节外,这就是可靠UDP的机制,简单有效,开销极小5。经测试及实际线上运行来看,在弱网络环境下的表现也是不错的。

反作弊策略

  实现的技术细节告一段落,接下来谈谈我们的反作弊策略。有些经验是实践下的真知:)
我们知道帧同步的一切都在客户端运算了,服务器能做的显得很有限。我们不知道玩家当前的位置,不知道玩家的技能使用情况,不知道玩家当前血量,拿什么来反作弊?

实时的检测,做了两点:

  客户端固定间隔上报双方血量及技能使用情况,服务器进行记录
  单局结束,上报胜负关系

  基于这两点,我们可知道某一帧玩家的血量是多少,每个人都上报自己的及对方(们)的,双向校验可看出有有无作弊行为。困扰而不得其解的是,当只有两人时,判断谁是作弊者比较麻烦。当两人以上时,可以仲裁。

  我们做了一点容错,当胜负结果异常时,才去进一步检查上报的记录以判断作弊者,判输。而对于上报数据并不一致但是胜负关系一致的情况,记录日志来诊断(容易发生在版本变更时)问题。

  通过实时的检测,基本可以检测到单局中作弊行为(加速,无限CD,锁血等),因为他们最终都导致双方数据不一致而结算不一致或上报血量不一致。

非实时的统计学反作弊方案

  当有一些漏洞可被利用但是一时无法定位的时候,统计学上的反作弊会比较有用。这里说的漏洞是指通过某种行为使对方掉线或者不发包等未知原因导致游戏异常结束的行为。

  我们在游戏结算时,非正常获胜的(掉线等)都会记录下来,并且作用于一个惩罚机制。

  每天通过非正常获胜的次数有上限,达到上限后,其非正常获胜都将不计。
  非正常获胜的次数作用于实时检测逻辑,如果双方数据不一致,非正常获胜次数多的玩家失败。
  非正常获胜次数影响玩家进入匹配,次数越高需要等越久才能开始匹配。

  这个方案在线上发挥过作用,有效阻挡了少部分非正常玩家利用漏洞获益,减少了其影响面。

后话

  上文介绍了游戏的实时PVP的技术实现,这里配一个架构图,看看其外围。

  有两点需要说明:

  • TGW的多通接入支持UDP四层,UDP 服务需要监听所有tunel的ip/port。
  • 帧同步的原理,要求我们必须精细化匹配。游戏中是二进制版本+资源版本一致才相互匹配,可以做到更精细化的根据出战忍者双方客户端数据hash值是否一致进行匹配。

  本文介绍了实时PVP的基础技术,一些优化手段及线上遇到的疑难问题的诊断,篇幅所限,且听下回分解。

转载于:https://www.cnblogs.com/WangBoBlog/p/7904031.html

动作手游实时PVP技术揭密(服务器篇)相关推荐

  1. 动作手游实时PVP帧同步方案(客户端)

    1.概述 1.1.基于UDP的帧同步方案 在技术选型方面,之所以选择帧同步方案,在Kevin的一篇介绍PVP帧同步后台实现的文章中已经做了详细叙述,这里简单摘要如下: 高一致性.如果每一帧的输入都同步 ...

  2. 神位纷争服务器维护,《神位纷争》沉寂了一年的硬核动作手游从PVP到PVE的转型...

    由微笑科技推出的第三人称动作手游<神位纷争>在沉寂了一年之后,终于是在此前正式登陆了安卓平台. 经过一系列的调整和更新,此次的正式版本以「神位纷争 2.0」的全新面貌重新展现在玩家面前. ...

  3. 手游后台PVP系统网络同步方案总结

    http://www.gameres.com/476063.html 概述 PVP系统俨然成为现在新手游的上线标配,手游Pvp系统体验是否优秀,很大程度上决定了游戏的品质.从最近半年上线的新手游来看, ...

  4. 手游实时语音,你所不知道的那些事

    随着手机性能的提高,手游开始向复杂化发展.不再是以消消乐这种简单的小游戏为主,RPG.实时对战类游戏开始变火.这种游戏,由于有玩家间的交互,有团队作战.配合,保证玩家能够进行实时语音通话,成为极大的需 ...

  5. 《火柴人联盟》制作人鬼人谈如做好动作手游

    <火柴人联盟>制作人鬼人谈如做好动作手游 发布者: justsolo | 发布时间: 2015-1-21 00:20| 评论数: 19 游戏类型:手游游戏/平板游戏  设计类型:[系统/框 ...

  6. 问道手游服务器维护,问道手游3月14日服务器维护内容一览

    为了保证服务器的运行稳定和服务质量,问道手游于3月14日早上06:00分左右进行停服维护,本次维护主要是针对游戏中玩家反映的一些BUG和异常进行优化修复.下面,就随小编一起来看看具体的维护内容. 维护 ...

  7. 《鬼泣-巅峰之战》制作人:预约800万的动作手游是怎么做出来的?

    凭借<鬼泣>IP的影响力,由CAPCOM授权.云畅游戏开发的<鬼泣-巅峰之战>手游,在公布之后受到许多玩家的关注,目前游戏全渠道预约人数超过了800万.葡萄君此前也写过这款手游 ...

  8. 剑灵认证服务器系统出错,剑灵手游程序错误怎么办 和服务器断开官方解决

    最近剑灵手游放出,想必大家也是期待已久,新作内测会遇到一些bug,最近就有不少玩家游戏的时候发现登录不了,今天小编为大家带来了剑灵手游程序错误怎么办 和服务器断开官方解决,感兴趣的朋友们可以跟着小编去 ...

  9. 圣斗士星矢服务器维护时间,圣斗士星矢手游8月2日服务器内BUG说明公告

    圣斗士星矢手游8月2日出现了服务器内BUG,官方发布了服务器内BUG说明公告,感兴趣的小伙伴们快来一起了解一下吧! 圣斗士星矢手游8月2日服务器内BUG说明公告 亲爱的圣斗士们: 圣域感谢玩家们的热情 ...

最新文章

  1. 负起责任、审慎对待论文创新和性能指数,ACM Fellow罗杰波教授分享他的审稿经验...
  2. 分布式事务:分布式事务原理概述
  3. NLP快速入门:手把手教你用HanLP做中文分词
  4. CABasicAnimation动画
  5. 10 计算机组成原理第六章 总线 总线的概念与分类 总线性能指标 总线仲裁 总线操作和定时 总线标准
  6. ftp+linux+使用webdav,群晖-win/mac/nfs ftp tftp webdav文件服务的概念及设置
  7. 吸血鬼 java_吸血鬼数
  8. SandyMandy ,绝世好BABY http://angel.mingox.com
  9. JAVA开发面试常问问题总结2
  10. 某大型银行深化系统技术方案之十四:服务层之服务调度机制
  11. 复习Javascript专题(三):面向对象(对象的创建与继承,原型及原型链)
  12. Matlab排序函数sort()和sortrows()
  13. python声纹识别_声纹识别(说话人识别)
  14. Android:自动点击屏幕
  15. 通通锁接口调用<Response [400]>报错及python示例代码
  16. 中华黄金·金生态合伙人颁奖典礼在珠海站开幕完美收官!!
  17. C++入门到精通。(五、C++的运算符。)
  18. crontab报错,但本地执行正常
  19. state和status的区别
  20. java网页截图_java-selenium 实现网页截图

热门文章

  1. android 小米手机打不开摄像头,小米手机相机故障无法连接到相机怎么办【故障解决】...
  2. java获取时间下周几的时间
  3. 如何解决hbase中数据热点问题
  4. 第001讲:我和Python的第一次亲密接触 | 课后测试题及答案(小甲鱼)
  5. php微信小程序毕业设计 php后台驾校考试小程序毕业设计开题报告功能参考
  6. 用求阶乘的函数fact(),编程计算1!+2!+3!......+n!的值
  7. 福州大学计算机学院印佳丽,福州大学数学与计算机科学学院导师介绍:曾有栋...
  8. k8s部署vue项目
  9. 这样设置,让你的 iPhone 用起来更加得心应手
  10. 路由桥接 新增路由 扩大wify 一个网线两个wify