游戏引擎网络开发者的 64 做与不做 | Part 1 | 客户端方面

摘要:纵观过去 10 年的游戏领域,单机向网络发展已成为一个非常大的趋势。然而,为游戏添加网络支持的过程中往往存在着大量挑战,这里将为大家揭示游戏引擎网络开发者的 64 个做与不做。

【编者按】时下,游戏网络化已势不可逆,因此,对于游戏开发者来说,掌握网络引擎的打造技巧同样不可避免。近日,Research Industrial Systems Engineering GmbH 安全研究员 Sergey Ignatchenko「拥有 20 年以上的工程经验」在 IT Hare 上撰文,深入分享了游戏引擎网络开发的相关经验,由 OneAPM 工程师翻译。

以下为译文:

纵观过去 10 年的游戏领域,单机向网络发展已成为一个非常大的趋势。然而,为游戏添加网络支持的过程中往往存在着大量挑战,而据近几年的工作经验「不仅参与了这一衍变,同样也为大量开发者提供资讯支持」来看,许多游戏开发者甚至都违反了「打造一个优秀网络应用程序」应该坚守的一些基本原则。因此,应用程序往往会面临着「frozen」UIs、莫名的断线「在其他程序互联网访问正常时」、不定期崩溃,以及峰值期间的服务器过载等问题。毫无疑问,这些问题将直接影响到玩家的游戏体验,同时其直接程度也远超管理员和图形开发者的想象。庆幸的是,这些问题处理起来并不复杂,有些甚至是一点就明。

因此,这里将通过一系列博文来布道网络开发的某些理念,其中大部分是游戏引擎开发者不曾留意的。当然对于某些朋友来说,有些观点可能你已经接触到了,但毫无疑问的是,它对大量游戏开发者都是有价值的。因此,对于期望打造出类似游戏或证券交易这类高交互应用程序的开发者,这些建议值得一读。

作为系列的第一篇文章,这里将着重讨论不涉及协议的客户端应用程序网络开发。本系列文章将包括:

  • Protocols and APIs
  • Protocols and APIs (continued)
  • Server-Side (Store-Process-and-Forward Architecture)
  • Server-Side (deployment, optimizations, and testing)
  • Great TCP-vs-UDP Debate
  • UDP
  • TCP
  • Security (TLS/SSL)
  • ……

范围确定

总的来说,游戏引擎网络支持是个非常大的主题,因此本系列博文将圈定一个范围——聚焦拥有客户端应用程序的游戏,而不是那些基于 browser-/AJAX 的游戏,虽然这两种游戏在设计上有着很多共同点,但是其中的区别也足够让讨论分开。本系列博文将尝试覆盖游戏网络层开发的常见理念:

首先,不会只聚焦某种类型的游戏,比如 MMORPGs;毫无疑问, MMORPGs 确实在讨论的范畴中,但是也不乏社交游戏、多玩家战略「包括实时和回合制」、赌博类游戏、证券交易型等等。而出人意料的是,在做网络支持时,这些游戏存在着大量的共同点。「尽管许多取决于时间控制问题,这点将在 Great TCP-vs-UDP Debate 一节详述」。

其次,同样不会限制到某个特定的平台:事实上,这里更推荐开发者写跨平台引擎,其中就包含了网络引擎。在实践中,笔者也曾写过一个网络引擎,它可以在 5 个以上完全不同的平台上运行,这点将在第六条中进行详述。

再次,因为基于游戏引擎开发者的视角,所以这里有个背景是游戏开发者经常需要为他们的游戏开发游戏引擎。在这个情况下,大多数建议都是适用的。

最后,虽然类似「哪个引擎或者网络引擎是最好的?」这样的问题已经超出了讨论的范畴,但是本系列博文同样对回答这个问题有所帮助;毫无疑问,答案取决于游戏的具体需求,因此请详细阅读。换句话说:如果你的游戏引擎或者框架提供了一个支撑网络的方式,这些博文可以作为一个工具对其进行考量,从而弄清其网络实现是否对特定的游戏有益。

OK,在交代完大致的讨论方向后,下面言归正传。

1. 在客户端请使用事件驱动编程模型

当下,大多数客户端 UI 框架都包含一个所谓的「main thread」,或者叫「main loop」,运行于「main thread」之中,而这个「main thread」本质上会处理一些特定的事件「最原始的是 UI 事件」。这种模型存在所有客户端框架之中,从 Windows GUI、Direct X 和 Cocoa,到 Unity 3D、Android 和 iOS。同时,也确实有一个很好的理由来驱动大家这么做:因为其他的编程模型只能给你带来噩梦。事实上,在实际工作中,笔者也只碰到了一个「出格」的框架,即最初 Java 的 AWT,而在 AWT 中编写 APP 的痛苦也众所周知,有鉴于此,AWT 自始至终也没有流行起来;实际上,谷歌也确实需要为 Android 开发新的 GUI 框架。

那么,在给应用程序添加网络支持后,事件驱动模型究竟应该如何转变?其实,这里并不需要任何改变。实际生产中,所有游戏网络通信逻辑都由消息发送和接收构成;而每一个接收到的网络消息都应该被作为游戏事件驱动逻辑「除去传统的 UI 事件,比如鼠标和键盘输入」的另一个事件。

通常情况下,这个操作可以通过给 main thread 的「message queue」注入一条 message 轻松实现。举个例子,在 Win32 中,这个操作通常由 PostMessage()或者 PostThreadMessage()方法完成。如果你选择的图形框架不支持这个理念,你可能需要通过建立你的队列并进行轮询进行模拟「举个例子,Unity3D2012」。对比在单线程中强制处理所有事件「同时包含 UI 事件和网络消息」,将事件作为数据(win32)还是回调这样的问题并不重要。NB:如果使用 Unity,这个技巧很少会用到,因为 Unity 内置的网络「已经使用了 Unity 的事件处理线程」非常适用于「实时世界模拟real-time world simulator」游戏;然而根据具体游戏特征,使用 Unity 网络做 UDP 传输也并不一定就是最好的途径——特别是那些与实时世界模拟无关的游戏。

在有些用例中,事件处理线程可能与选择框架的「main thread」相去甚远,但是这里需要谨记的是,将所有与逻辑相关事件处理都放到同一个单线程中。然而,纯通信相关「与游戏逻辑完全无关),比如 marshalling、en/decryption 和 (de)compres,尽可能在「main thread」外部处理,在下面的第 3 条中会详细讨论线程隔离问题。

2. 别在事件处理线程之外调用应用程序级别回调

犹记那年,笔者还「很傻很天真」,那时候负责给一个证券交易业务开发网络框架「PS:别问我为什么这么重要的一个任务会交给一个没经验的工程师,笔者同样无解」。开始的时候,新网络库编写的确实比较顺利,但是在这里,笔者同样犯了一个原则错误——在应用程序层面调用了一个回调「它本应该是 1 个回调来响应 sendMessageOverTheNetworkAndCallbackOnReply()-style 函数」。这个蹩脚的错误曾一度给后续使用这个框架的同仁带去了大量麻烦。首先,交互「以及潜在的 races」让使用它的同事难以理解。其次,给 bugs 和 races 追踪带来了大量麻烦。最后,虽然并没有太坏的影响,而且框架总体运行良好,但是如果没有这个回调,开发将变得更加平顺。

数年后,笔者一直为大型多玩家游戏开发网络引擎——同时在线玩家 50 万,日消息数 5 亿条。而在吸取了之前的经验后,避免了类似线程回调,所有的工作都井井有条,同时在多平台切换上也异常平顺。

总结:如果你需要从网络层实现一个回调到应用程序层,首先你需要将事件传递给事件处理线程「通常情况下就是 main thread」,随后通过网络层库调用「发源于事件处理线程」来处理事件,并在必要时调用应用程序级回调。换句话说,下面才是一个完善的途径:

network thread –inter-thread-communication –event-processing thread –network-library-call –application-callback –no-thread-sync-needed

而下面虽然可行,但是不利于他人长期使用:

network thread –network-library-call –application-callback –thread-sync-required

在完善的途径中,回调只存在事件处理线程环境中,这将显著简化应用程序开发。所有应用程序级处理都被严格确定,从而最大程度地减少 races 出现的可能,同时也减少了应用程序级所必要的同步。上面的过程听起来可能比较笨重,操作起来也有些繁琐,但是它可以切实地减少游戏开发者的后续麻烦。

3. 别从事件处理线程调用可能阻塞网络的函数

这是网络开发者所有可以提交中影响最大的错误之一。如上文所述,你需要在一个单独的线程中处理所有事件。这种操作得当且方便,但麻烦也因此产生,比如:在一个事件处理器中做一个简单如 gethostbyname() 的调用,这个操作在小范围中不会存在任何问题,但是在有些情况中现实世界的玩家却会因此阻塞数分钟之久!如果你在 GUI 线程中调用这样一个函数,对于用户来说,在函数阻塞时,GUI 一直都处于 frozen 或者 hanged 状态,这从用户体验的角度是绝对不允许的。

因此,通过 GUI 来做网络交互时所有函数都应该是非阻塞的,或者位于不同的线程中。在这种情况下,你需要让事件状态机更加复杂,你可以效率地取得类似「waiting for DNS resolution」这样的状态,同时它还需要可以避免「frozen」GUI ,并且可以让你处理网络延时,包括:

  • 在需要时通知用户。举个例子,在等待了 1 秒或者 5 秒后,你知道这里出现了问题,用户同样需要知道这个事情。因此,你最好让用户了解到你已经发现了这个问题,并着手处理。
  • 在需要时终止操作并重试。
  • 允许用户优雅地终止请求或应用程序,而不是逼迫他们去使用任务管理器。

需要注意的是,这点看起来似乎与第一条和第二条相违背,但事实上并不是这样。对于「hey, so should I do it single-threaded or multi-threaded?」这样的问题,答案是:系统级别网络调用要么是非阻塞的,要么是来自非事件处理线程;同时,所有事件处理必须在事件处理线程中完成。这就意味着,如果使用多线程,你需要在一个非事件处理的网络处理线程中调用类似阻塞 recv() 的函数,随后将调用的结果转换为一个事件,并通过队列的形式「如上文 1 中介绍」将这个事件传递给事件处理线程。严格来讲,decryption/decompression 就要进行这样的处理,虽然需要去做避免事件处理线程成为一个瓶颈的流程,但它通常比只将 encryption/compression 扔到网络处理线程中来得更有性价比。

网络线程的另一个替代是 non-blocking IO,这里同样存在一些需要注意的地方,包括 gethostbyname() 和 getaddrinfo() 在主流平台中并不存在 non-blocking IO 版本,同时笔者也不认为在客户端使用 non-blocking 带来的麻烦会更少。服务器端将是另一种情景,详情会在系列博文的第三部分服务器端讨论。

4. 不要将用户作为免费的错误处理程序

在游戏引擎开发中,很多开发者使用了一个异常简单的网络错误处理途径。也就是,他们简单的将错误抛到用户面前,只留下一句「服务器存在一点问题,请重试」。这个做法是非常讨厌的,并且不会带来任何效果「当然轻松了开发者,但是损害了用户」。除下开发人员太懒,不存在任何理由不将问题在内部解决。在问题产生并给用户提示后,没理由不自动重试而要求用户再次操作。为了通知这个问题,你可以在屏幕的显著位置进行显示,或者是弹出一个对话框「没有ok这个按钮,只有关闭」,同时将在问题解决后自动消失。这样一来,在问题产生用户离开后,如果你能短时间解决问题,你不会对用户体验产生任何影响。

有人可能争论不停重试会造成网络阻塞,但是作为一名开发者,你有责任让用户体验变得简单。当然,你也可以设置一个临界值,比如 5 分钟来关闭重试,并提示「对不起,我们已经尽力了,但问题在短时间内无法得到修复」。

综合上面的 1-3 条,你通常需要在网络处理线程中检测问题,并将它转换成 1 个事件,并在事件处理线程中处理事件,比如显示一个对话框。

5. 为用户提供有价值的错误消息

从终端用户的角度来看,「网络不可用」、「连接拒绝」以及「连接终止」没有任何区别;如果可能的话,你可能是想告诉他们网线未插入或者是服务器故障或者是两者之间的一些问题,但是仅仅因为一些专业用语让用户无法确定问题真相是完全不可取的。更糟糕的做法是,试图将技术细节隐藏于一些模棱两可的话语之间,比如「服务器有一点问题」和「你丢失了连接」。

总之,切记将错误消息从你能理解的语句转换到用户能理解的提示,而不是让用户无法辨别各种提示间的区别——让所有消息看起来完全相同。

6. 支持多平台

纵观当下游戏领域,单平台游戏已经不再有吸引力。即使引擎只为一款游戏打造,但是你又真的能确定游戏未来不会过渡到其他平台?实践中,让网络代码跨平台并不是一件难事,因此你没理由不做多平台的准备。笔者个人的网络库就覆盖了 Windows、Linux、Mac OS X、FreeBSD、iOS 等引擎。

6a. 在客户端使用 Berkeley Sockets

如果你的游戏引擎是基于 C 或者 C++,并且将应用程序定义为只 Windows 平台,那么就可能尝试一些 Windows 特有的函数「那些以 WSA*()为前缀的」来通信。请不要这么做,转而使用 Berkeley sockets「那些 socket()/connect()/send()/recv()函数」进行取代;关于使用细节,请自行 Google。对于其他提供了跨平台 APIs 的编程语言,选择一个合适的网络库通常不会有太多问题。

7. 提供一个自动升级 APP 的途径

通常情况下,自动升级并不会考虑为游戏引擎的一部分。然而,个人觉得将自动升级纳入网络层会有一些相应的好处,其原因是:

  • 用户可能期望多一些选择「从主题到 DLCs」。
  • 如果可以边玩边下载,那么他们会很开心。
  • 如果下载干涉到娱乐,那他们肯定会不开心。
  • 在你的网络层提供可选下载,你可以优先考虑流量,将下载对游戏的影响降到最低「在本系列博文的 Part IIb 一节将讨论更多技巧」。因为 QoS 并不适用于互联网,两个并行的连接很可能产生相互影响。
  • 如果你支持可选下载,他们同样需要自动化更新,因此结合自动下载和自动更新是件不错的事情。
  • 因此,在网络引擎实现整个自动更新功能是个不错的事情。
  • 此外,用户可以边玩边下载,从而最大化了娱乐时间。
  • 上面的推理并不是在任何条件下都成立的,但是我看到了类似系统的实现,同时也取得了非常好的效果。

注意:尽管与网络库集成,你同样需要在 HTTP「而不是基于你的协议」上实现初始自动更新「会在游戏应用启动前启动」;这么操作并不会带来太多的复杂性,但是很可能会彻底地修改你的协议。

其他在启动更新上的操作是非常复杂的,因此会单独开一篇文章来表述、

To Be Continued……

原文链接:64 Network DO’s and DON’Ts for Game Engine Developers. Part I: Client Side

本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 17:29:54

游戏引擎网络开发者的 64 做与不做 | Part 1 | 客户端方面的相关文章

最受欢迎的游戏引擎集结号:跨平台篇

移动应用分类五花八门,面对众多对手,移动游戏突破重围,成了大家日常生活中的基础娱乐活动之一.也因此,越来越多的开发者开始投身移动游戏的开发中.不过,面对多样化的目标平台,要知道选择一个适合自己的跨平台游戏引擎是一件非常重要的事.所以,本文中例举了除了以前常常提到的cocos2d-x和虚幻引擎之外的,其他一些在跨平台方面比较优越,在开发者中很受欢迎的游戏引擎. 1. Unity3D 说到跨平台游戏引擎,开发者最先想到的就是Unity3D.他是让开发者可以轻松创建诸如三维视频游戏.建筑可视化.实时三

千百万Java开发者的福音:跨平台Cocos2d-Java游戏引擎诞生 .

跨平台Cocos2d-Java游戏引擎以及配套的CocosEditor2.0游戏开发工具终于诞生了.使用Java语言来开发Cocos2d跨平台游戏, 和-lua,-js 的风格相近API,毋庸置疑,这确实是Cocos2d新的里程碑.欢迎来的Cocos2d-Java的世界: 全世界有多少 Java 开发者? Oracle 说世界上有900万 Java 程序员 ,Wikipedia说是1000万,而 NumberOf.net 的哥们儿说的很精确:世界上有九百万七千三百四十六个 Java 程序员. 为

第1部分: 游戏引擎介绍, 渲染和构造3D世界

原文作者:Jake Simpson译者: 向海Email:[email protected] ------------------------------------------------------------第1部分: 游戏引擎介绍, 渲染和构造3D世界 介绍 自Doom游戏时代以来我们已经走了很远. DOOM不只是一款伟大的游戏,它同时也开创了一种新的游戏编程模式: 游戏 "引擎". 这种模块化,可伸缩和扩展的设计观念可以让游戏玩家和程序设计者深入到游戏核心,用新的模型,场景和

国内自主研发的游戏引擎一览

国内自主研发的引擎究竟有多少.因为我在某论坛看到某些人说.只有完美和剑侠3 才是称的上是国产,不是自己的引擎开发的游戏不叫国产 我汗. 不太赞同. 还有国内自主的真的只有这两个么 我也不认同. 有人说 那是抄的有人说那是完全自主的   这个我不知道,,,下面来..(有不对的 有少的 希望大家增加或者指正) 国内自主研发的引擎知多少.哈.我在网上搜了下.除了大家熟知的完美时空和金山剑侠3的引擎以外,还有这些,,不管他们是好是坏 是新是旧,是只放消息即将推出~   是完全自主还是什么的?还是它们的游

游戏引擎剖析

原文作者:Jake Simpson 译者: 向海 Email:[email protected] 第1部分: 游戏引擎介绍, 渲染和构造3D世界 介绍 自Doom游戏时代以来我们已经走了很远. DOOM不只是一款伟大的游戏,它同时也开创了一种新的游戏编程模式: 游戏 "引擎". 这种模块化,可伸缩和扩展的设计观念可以让游戏玩家和程序设计者深入到游戏核心,用新的模型,场景和声音创造新的游戏, 或向已有的游戏素材中添加新的东西.大量的新游戏根据已经存在的游戏引擎开发出来,而大多数都以ID公

JAVASCRIPT开发HTML5游戏--斗地主(网络对战PART4)

继之前用游戏引擎(青瓷引擎)做了斗地主单机版游戏之后,这里分享下使用socket.io来实现网络对战,代码可已放到github上,在此谈谈自己整个的开发思路吧. 客户端代码 服务端代码 (点击图片进入游戏体验) 前文链接: javascript开发HTML5游戏--斗地主(单机模式part1) javascript开发HTML5游戏--斗地主(单机模式part2) javascirpt开发HTML5游戏--斗地主(单机模式part3) 本文章为网络对战第一部分内容.主要内容如下: 简介 服务端项

UWP简单示例(三):快速开发2D游戏引擎

准备 IDE:VisualStudio 2015 Language:VB.NET/C# 图形API:Win2D MSDN教程:UWP游戏开发 写在前面的话 没有什么比重复造轮子更让人心碎的事情了. (如果有,那就是造了两遍) 是否有必要开发游戏引擎? 现在市面上有很多成熟的跨平台游戏引擎,对个人开发者也较为友好 若你是一名C#开发者,可以选择CocosSharp或Unity3D 尤其Unity3D,用它开发2D和3D游戏一样出色 当然,亲自编写一款简单的2D游戏引擎也是一件让人自信感满满的事情

2016年 最火的 15 款 HTML5 游戏引擎

HTML5游戏从2014年Egret引擎开发的神经猫引爆朋友圈之后,就开始一发不可收拾,今年<传奇世界>更是突破流水2000万!从两年多的发展来看,游戏开发变得越来越复杂,需要制作各种炫丽的效果,还要制作各种基于 2D 或者 3D 的场景.作为一名开发者,分析了当下最火爆,最热门的HTML5游戏引擎供大家参考,希望大家也能找到属于自己的那款游戏开发引擎. 我在github上面收集了四十多款的HTML5开源游戏引擎,从里面star.fork等等参数分析其流行度,最后综合各方面元素,筛选出靠前的十

转:高层游戏引擎——基于OGRE所实现的高层游戏引擎框架

高层游戏引擎——基于OGRE所实现的高层游戏引擎框架 这是意念自己的毕业论文,在一个具体的实践之中,意念主要负责的是物件和GUI之外的其他游戏系统.意念才学疏陋,望众位前辈不吝赐教.由于代码质量不高.环境很难于配置.资源包过大等问题,意念暂先不提供代码和程序,未来有时间的时候组织一下这些曾经的代码,再向外发布. 文过三月,也有些新的想法,以后会慢慢跟大家聊的,欢迎拍砖哦^_^. 关键字与术语: 游戏. 游戏引擎 .高层引擎.规则 .场景.物件.Terrain(地形).解释器 .Applicati