天天看點

What Every Programmer Needs To Know About Game NetworkingWhat 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.

換言之,隻有當玩家的角色受到本地玩家輸入的外部因素的影響(用戶端無法預測)時,才需要糾正玩家的位置。當然,如果玩家試圖作弊的話:)

繼續閱讀