服务器引擎之艰难困苦的开发历程

发表于2016-07-29
评论0 1.6k浏览
摘要:游戏服务器开发,那些棘手的问题,是如何得到解决的? ProudNet 基于长时间技术积累和众多成功游戏产品和中国及全球范围运营经验 适用于各种复杂网络环境和多种游戏类型,简单易用即使缺少网络游戏/手机游戏服务端开发经验 也可以快速开发出高效稳定游戏服务器的中间件(Middleware)平台。

一、笔者介绍
  裴铉稷.
  开发了游戏服务器&网络引擎‘ProudNet’。已经向200多家游戏开发商提供了ProudNet并持续提供技术支持。在韩国最知名的游戏服务器开发人之一。

二、开始  
  世界上有好多用于游戏开发的服务器&网络库,我把这些统称为服务器引擎。服务器引擎由服务器端的模块和网络客户端模块组成。


  既有数量众多的开源代码,也有商用软件,即需要购买的产品。但大部分用户都使用开放源代码。不仅因为它是免费的,而且其源代码是公开的,所以不受限制。但商用引擎也有它的强项,那就是不同于开源,一旦出现问题不需要用户自己解决所有问题。  
  使用商用引擎,游戏开发人员可以更方便地解决问题,即用钱买时间。 而像我这样提供商用引擎的人却是相反。开发游戏服务器并推出在线游戏的过程中会碰到很多问题。为解决这些问题我们要花费时间。 有些简单的问题5分钟就能解决,但也会出现半个月解决不了的问题,甚至也有过坐飞机到现场查看的情形。 
  解决问题的过程固然艰难,但解决这些问题的过程中可以积累相当多的经验。 我从1995年开始在游戏公司从事游戏开发员工作。 直到2004年为止开发了多款在线游戏并在这13年间积累了相当多的经验。 但从2008年到2016之间通过游戏服务器引擎开发、维护和解决问题获得的经验可能比过去13年的数倍还多。 
  在此我想从目前为止积累的经验中拿出几个可能对大家有所帮助的感悟予以共享。

基于ProudNet的手机RPG游戏 MARVEL FUTURE FIGHT 

三、数据包收发代码的自动生成 
  我从1997年开始了游戏服务器的开发。自夸一下, 我是第一批开拓Windows NT中通过 IOCP开发游戏服务器的人之一。在当时开发游戏服务器的过程中有一个难点,就是对数据包的收发过程需要一一进行编码。 
  对数据包的收发过程一个个编码不仅不方便,也可能会失误。 请看如下代码。


  数据发送端正在发送x,y,z,但接收端只抽出x,z。 这样肯定会出现问题。如果程序很简单,大家不会犯这种错误。 但如果程序变得庞大,又有好多人对其进行调整之后就不一样了。
  怎样解决这个问题? 只要把数据包的收发部分代码交给机器进行编码即可。  
  假设您正在编写针对人物的移动和攻击的收发信息代码,为了让机器代替你编码,需要定义如下函数。


  通过一个程序的执行分析以上函数的定义,然后自动生成发送代码和接收代码,最后生成如下代码。


  大家要做的事情已完成。如果想把数据包发送给其他的计算机,只需调用自动生成代码的函数。在接收端计算机中将调用同名称的函数。 
  这种技术叫做RPC (Remote Procedure Call)。 目前Google protobuf等可以实现这种功能,但我早在2000年就已经实现了这个功能并以此为主题出版了书籍。


  这本书我是用英语编写的,后来也被翻译成中文。
  我研发的服务器引擎ProudNet在2008年面市,其中就包含我自己编写的RPC。

四、服务器引擎的简单的使用方法 
  ProudNet中包含客户端和服务器之间的网络功能和客户端之间的P2P网络通信功能。能够方便易捷地使用这些功能是ProudNet的特点之一。 
  简单看一下ProudNet的使用方法。 
  需要启动游戏服务器时只需创建NetServer实例并调用Start函数即可,这是全部。


  客户端接入服务器时也同样建立NetClient实例并调用Connect 函数。这时服务器将回调(callback)OnClientJoin 函数。 大家可以创建OnClientJoin函数。 当客户端连接到服务器之后就会回调OnJoinServerComplete函数。同样大家只需实现这个函数的功能即可。 
  客户端和服务器之间的网络连接可以使用PRC。
  如果想从客户端向服务器发送Knight_Move信息,首先在客户端调用Knight_Move 函数,然后在服务器生成Knight_Move函数就可以。它的处理速度理所当然地符合实时多人游戏所需的要求。 
  如果从服务器向客户端55发送Knight_ShowMove 消息,只需在服务器调用Knight_ShowMove,这时第一个输入的参数为客户端ID55。


  如果要实现客户端55,56,57之间的P2P通信,就要把它们绑定为P2P组。 P2P组可以看做是QQ聊天窗之类的概念。在同一个聊天窗的人之间可以相互聊天,而未被邀请的人无法发言的特性相同。
  首先在服务器调用CreateP2PGroup,而作为参数输入55,56,57。假设出现了ID90的P2P组。 
  客户端很快知道自己已经加入了P2P组 90。 也会知道在此组中除了自己还有其他客户端55,56,57。 
  当客户端55向客户端56发送Knight_Attack时,需要调用Knight_Attack并输入56作为参数。这时客户端56就会立即执行Knight_Attack函数。 而客户端57调用Knight_Attack时把P2P组90作为参数输入,那么客户端55,56,57都会回调 Knight_Attack。


  利用这一点可以降低在线游戏服务器的运行负荷,缩短玩家之间交换位置信息的时间。例如在MMORPG中把大部分数据传输通过客户端和服务器之间的通信来处理,而表示玩家移动的信息可以同时使用P2P和C/S通信。


  在大规模多人在线游戏,即MMO游戏中玩家的移动信息不会传递给所有其他玩家。比如玩家55的移动只传递给能够看到玩家55的其他玩家。ProudNet对这项功能的实现很成熟。 以“能够看到玩家55”为原则建立P2P组,如果能够看到玩家55的其他玩家 57进入视野时就把玩家57加入到P2P组,反过来,如果玩家58离开了视野,就把玩家58从P2P组中移除。实现这些功能只需调用NetServer的 JoinP2PGroup, LeaveP2PGroup即可。 ProudNet的稳定性保障了对这个功能的频繁调用。


  如果想让开发人员自由地运用C/S网络和P2P网络,那么服务器引擎就要在底层稳定顺畅的提供支撑。在多种网络环境中也要无故障运行。

五、无等待时间的P2P连接过程 
  开发人员在开发P2P网络游戏或应用时对服务器引擎的运行有如下的愿望。 
  “当调用客户端之间P2P连接的函数时,能够立即无条件实现通信。” 
  进行P2P通信就需要经过NAT Hole punching的过程。此过程就要0.1到2秒的时间。 有时会发生无法进行hole puching的情形。 等待hole punching或另外编写替代代码是件很繁琐的事情。  
  我为了解决这个问题选择了如下的方法。  
  “hole punching未到之前进行中继通信。这期间在后台尝试hole punching。 Hole punching成功之后再进行P2P通信。 
  实现这个功能不是很难,但问题在于P2P之间的可靠(reliable)消息传递。可靠信息传递类似于TCP要保证 “我发的内容对方一定收到”。 TCP的可靠信息传递的机制是发送方窗口未确认到对方已接受就会重新发送。 在客户端之间进行P2P中继通信的过程中,一旦hole punching成功, 发送方窗口也要向hole punching的通道转移。第一次开发ProudNet时 这部分算是我的第一个挑战,但我还是非常好的解决了这个问题。

基于ProudNet的格斗游戏街头霸王5

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论