What Every Programmer Needs To Know About Game Networking

What Every Programmer Needs To Know About Game Networking

A short history of game networking techniques

Posted by Glenn Fiedler on Wednesday,February 24,2010

Introduction

Hi, I’m Glenn Fiedler and welcome to Networking for Game Programmers.

你有没有想过多人游戏是如何运作的?

从外部看,这似乎很神奇:两个或两个以上的玩家在网络上共享一致的体验,就像他们在同一个虚拟世界中一起存在一样。

但是作为程序员,我们知道下面实际发生的事情和你看到的情况是完全不同的。这都是幻觉。一个巨大的诡计。你所感知到的共享只是从你自己的角度和位置看到的和时间的近似。

Peer-to-Peer Lockstep

最初的游戏是网络化的点对点的,每台计算机在完全连接的网状拓扑中彼此交换信息。你现在仍然可以在即时战略游戏中看到这种模式的存在,有趣的是,出于某种原因,也许这是第一种方式——这仍然是大多数人认为游戏网络是如何工作的。

其基本思想是将游戏抽象为一系列回合,并在每一回合开始时处理一组命令消息,指导游戏状态的演变。例如:移动单位,攻击单位,建造建筑物。网络所需要的就是运行完全相同的命令集,并从一个公共初始状态启动每个玩家的机器。

当然,这是一个过于简单的解释,掩盖了许多微妙的点,但它概括了如何为RTS游戏联网的基本概念。你可以在这里阅读更多关于这个网络模型的信息: 1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond.

它看起来如此简单和优雅,但不幸的是有几个限制。

首先,要确保一个游戏完全具有确定性是非常困难的;每一回合在每台机器上的表现都是相同的。例如,一个单位可以在两台机器上采取稍微不同的路线,更快地到达战场,在一台机器上节省一天的时间,而在另一台机器和稍晚到达不节省时间。就像蝴蝶拍打着翅膀,在世界的另一边引发飓风一样,一个微小的差异会导致一段时间内的完全失步。

下一个限制是,为了确保游戏在所有机器上的播放完全相同,在模拟该回合之前,必须等待所有玩家的该回合命令被接收。这意味着游戏中的每个玩家的延迟等于最慢的玩家。RTS游戏通常通过立即提供音频反馈和/或播放过场动画来隐藏这一点,但最终任何真正影响游戏的动作只可能在延迟过后发生。

最后的限制是由于游戏同步的方式,只发送命令消息来改变状态。为了使这一点起作用,所有玩家必须从相同的初始状态开始。一般来说,这意味着每个玩家必须在开始游戏前加入大厅,尽管技术上可能支持延迟加入,但这并不常见,因为在实况游戏中捕捉和传输完全确定的起点非常困难。

尽管有这些限制,这种模式自然适合即时战略游戏,它仍然生活在今天的游戏,如“指挥和征服”,“帝国时代”和“星际争霸”。原因是在RTS游戏中,游戏状态由数千个单位组成,而且太大了,无法在玩家之间进行交换。这些游戏别无选择,只能交换驱动游戏状态演变的命令。

但是其水平在不断提高。这就是确定性p2p帧同步网络模型。现在让我们看看动作游戏的演变,从Doom、Quake和Unreal开始。

Client/Server

在动作游戏时代,p2p帧同步的局限性在Doom中变得明显,尽管它在局域网上表现出色,但在互联网用户上却表现得非常糟糕

Although it is possible to connect two DOOM machines together across the Internet using a modem link, the resulting game will be slow, ranging from the unplayable (e.g. a 14.4Kbps PPP connection) to the marginally playable (e.g. a 28.8Kbps modem running a Compressed SLIP driver). Since these sorts of connections are of only marginal utility, this document will focus only on direct net connections.

当然,问题是Doom是专为局域网上的网络设计的,并且使用了前面为RTS游戏描述的p2p帧同步模式。每个回合的玩家输入(按键等)都与其他玩家交换,在任何玩家可以模拟帧之前,其他玩家的所有按键都需要被接收。

换句话说,在你可以转动、移动或射击之前,你必须等待来自最滞后的玩家的输入。试想一下,对于那些“只有边际效用”的互联网用户来说,这会导致他们痛哭流涕,咬牙切齿。:)

为了超越局域网和大学网络大公司中连接良好的精英,有必要改变这种模式。1996年,这正是约翰·卡马克和他的团队在发布Quake时所做的使用客户机/服务器而不是p2p。

现在,不同于每个玩家运行相同的游戏代码并直接进行通信,每个玩家现在都是一个“客户端”,他们只与一台名为“服务器”的计算机进行通信。游戏不再需要在所有机器上都具有确定性,因为该游戏实际上只存在于服务器上。每个客户端都有效地充当了一个哑终端,显示服务器上运行的游戏的近似值。

在纯客户端/服务器模型中,您不在本地运行游戏代码,而是将输入(如按键、鼠标移动、单击)发送到服务器。作为回应,服务器会更新你的角色在世界上的状态,并用一个包含你的角色和你附近的其他玩家状态的包进行回复。客户端所要做的就是在这些更新之间插入,以提供平滑移动的假象。

这是向前迈出的一大步。游戏体验的质量现在取决于客户端和服务器之间的连接,而不是游戏中最落后的一方。玩家也有可能在游戏中间来来往往,随着客户端/服务器减少了每个玩家所需的平均带宽,玩家数量也随之增加。

但是纯客户机/服务器模型仍然存在问题:

While I can remember and justify all of my decisions about networking from DOOM through Quake, the bottom line is that I was working with the wrong basic assumptions for doing a good internet game. My original design was targeted at < 200ms connection latencies. People that have a digital connection to the internet through a good provider get a pretty good game experience. Unfortunately, 99% of the world gets on with a slip or ppp connection over a modem, often through a crappy overcrowded ISP. This gives 300+ ms latencies, minimum. Client. User’s modem. ISP’s modem. Server. ISP’s modem. User’s modem. Client. God, that sucks. Ok, I made a bad call. I have a T1 to my house, so I just wasn’t familliar with PPP life. I’m addressing it now.

问题当然是延迟。

接下来发生的事情将永远改变这个行业。

Client-Side Prediction

在最初的Quake中,你感觉的到计算机和服务器之间的延迟。按Forward键,在实际开始移动之前,您将等待数据包传输到服务器并返回给您所花的时间。按下Fire键,在射击前等待同样的延迟。

如果你玩过类似现代FPS的使命召唤:现代战争,你知道这不再会发生了。那么,现代的fps游戏到底是如何消除多人游戏中你自己行动的延迟的呢?

约翰·卡马克在写关于即将发布的《QuakeWorld》的计划时说:

I am now allowing the client to guess at the results of the users movement until the authoritative response from the server comes through. This is a biiiig architectural change. The client now needs to know about solidity of objects, friction, gravity, etc. I am sad to see the elegant client-as-terminal setup go away, but I am practical above idealistic.

所以现在为了消除延迟,客户机运行的代码比以前多。它不再是一个向服务器发送输入并在发送回的状态之间插值的哑终端。相反,它能够在本地并根据输入立即预测角色的移动,在客户机上运行玩家角色的游戏代码子集。

现在,只要您按下前进,就不会等待客户端和服务器之间的往返行程——您的角色会立即开始前进。

这种方法的困难不在预测中,因为预测和正常的游戏代码一样工作——根据玩家的输入及时地向前模拟游戏角色的状态。难点在于如何应用服务器修正,当服务器和客户端的角色的位置和行为不同时。

现在,你可能会想。嘿,如果你在客户端上运行代码——为什么不让客户端对他们的玩家角色具有权威性呢?客户端可以为自己的角色运行模拟代码,并在每次发送数据包时简单地告诉服务器它们在哪里。问题在于,如果每个玩家都能简单地告诉服务器“这是我当前的位置”,那么黑客攻击客户端就非常容易,这样一个骗子就可以立即躲开即将击中他们的RPG,或者立即在你身后传送,在你背后开枪。

因此,在fps游戏中,尽管每个玩家都在本地预测自己角色的运动以隐藏延迟,但服务器绝对有必要对每个玩家角色的状态进行权威控制。正如蒂姆·斯威尼在《 The Unreal Networking Architecture》中所写:“服务器就是主宰”。

这里是有趣的地方。如果客户端不一致,客户端必须接受服务器对位置的更新,但由于客户端和服务器之间的延迟,这种更正一定是延迟的。例如,如果从客户端到服务器需要100毫秒的时间,而从服务器到服务器需要100毫秒的时间,那么对玩家角色位置的任何服务器更正都是200毫秒之后的,与客户端预测自己移动的时间相比。

如果客户端只是逐字应用服务器修正更新,它会及时地将客户端拉回来,完全撤销任何客户端的预测。那么,如何在允许客户提前预测的情况下解决这个问题呢?

解决方案是在客户端上为本地播放器保留一个过去角色的状态和输入的循环缓冲区,然后当客户端从服务器接收到更正时,它首先从服务器丢弃任何早于更正状态的缓冲状态,并使用存储在循环缓冲区中的玩家输入从服务器更正开始重播状态。将客户端的状态更正回当前的“预测”时间。实际上,客户端在保持世界其他地方不变的同时,无形地“倒带和重放”本地玩家角色移动的最后N帧。

这样,玩家似乎可以控制自己的角色而不产生任何延迟,只要客户机和服务器角色模拟代码是合理的,对客户机和服务器上的相同输入给出大致相同的结果,就很少进行纠正。正如蒂姆·斯威尼所描述的:

… the best of both worlds: In all cases, the server remains completely authoritative. Nearly all the time, the client movement simulation exactly mirrors the client movement carried out by the server, so the client’s position is seldom corrected. Only in the rare case, such as a player getting hit by a rocket, or bumping into an enemy, will the client’s location need to be corrected.

换言之,只有当玩家的角色受到本地玩家输入的外部因素的影响(客户端无法预测)时,才需要纠正玩家的位置。当然,如果玩家试图作弊的话:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值