關於初學者遊戲中的網路模型

關於初學者遊戲中的網路模型
在過去的兩周里,我一直在為我的遊戲開發線上引擎。 在此之前,我對遊戲中的網路一無所知,因此我閱讀了很多文章並做了很多實驗來理解所有概念並能夠編寫自己的網路引擎。

在本指南中,我想與您分享在編寫自己的遊戲引擎之前需要學習的各種概念,以及學習它們的最佳資源和文章。

一般來說,網路架構主要有兩種類型:對等網路和客戶端-伺服器網路。 在點對點(p2p)架構中,資料在任意一對連接的玩家之間傳輸,而在客戶端-伺服器架構中,資料僅在玩家和伺服器之間傳輸。

儘管在某些遊戲中仍然使用點對點架構,但客戶端-伺服器已成為標準:它更容易實現,需要更小的通道寬度,並且更容易防止作弊。 因此,在本教程中,我們將重點放在客戶端-伺服器架構。

特別是,我們對獨裁伺服器最感興趣:在這樣的系統中,伺服器總是正確的。 例如,如果玩家認為自己在座標 (10, 5),而伺服器告訴他他在 (5, 3),那麼客戶端應該用伺服器報告的位置替換其位置,而不是相反反之亦然。 使用權威伺服器可以更輕鬆地識別作弊者。

網路遊戲系統有三個主要組成部分:

  • 傳輸協定:資料如何在客戶端和伺服器之間傳輸。
  • 應用協定:從客戶端到伺服器以及從伺服器到客戶端傳輸什麼內容以及以什麼格式。
  • 應用程式邏輯:如何使用傳輸的資料來更新客戶端和伺服器的狀態。

了解每個部分的作用以及與之相關的挑戰非常重要。

傳輸協定

第一步是選擇在伺服器和客戶端之間傳輸資料的協定。 為此有兩種網際網路協定: TCP и UDP。 但是您可以基於其中一個建立自己的傳輸協定或使用使用它們的庫。

TCP和UDP的比較

TCP和UDP都是基於 IP。 IP 允許資料包從來源傳輸到接收者,但不保證發送的資料包遲早會到達接收者、至少會到達一次、以及資料包的順序會以正確的方式到達命令。 此外,數據包只能包含有限數量的數據,由值給出 MTU.

UDP 只是 IP 之上的薄層。 因此,它也有同樣的限制。 相比之下,TCP有很多特色。 它透過錯誤檢查在兩個節點之間提供可靠、有序的連接。 因此,TCP 非常方便並且被用於許多其他協議,例如 HTTP, 則fTP и SMTP。 但所有這些功能都是有代價的: 延遲.

要理解為什麼這些函數會導致延遲,我們需要了解 TCP 的工作原理。 當發送節點將資料包發送到接收節點時,它期望收到確認(ACK)。 如果在一段時間後仍未收到資料包(因為資料包或確認遺失,或由於某些其他原因),則它會重新發送資料包。 此外,TCP 保證資料包以正確的順序接收,因此在接收到遺失的資料包之前,所有其他資料包都無法處理,即使它們已經被接收主機接收。

但正如您可能想像的那樣,多人遊戲中的延遲非常重要,尤其是在 FPS 等充滿動作的遊戲類型中。 這就是為什麼許多遊戲都使用 UDP 及其自己的協議。

由於多種原因,基於 UDP 的本機協定可能比 TCP 更有效。 例如,它可以將某些資料包標記為可信,而將其他資料包標記為不可信。 因此,它並不關心不受信任的資料包是否到達接收者。 或者它可以處理多個資料流,以便一個流中遺失的資料包不會減慢其餘流的速度。 例如,可能有一個線程用於玩家輸入,另一個線程用於聊天訊息。 如果不緊急的聊天訊息遺失,也不會減慢緊急的輸入。 或者,專有協定可能會以不同於 TCP 的方式實現可靠性,以便在視訊遊戲環境中更有效率。

那麼,如果 TCP 如此糟糕,那麼我們將基於 UDP 創建自己的傳輸協定嗎?

情況有點複雜。 儘管 TCP 對於遊戲網路系統幾乎不是最佳選擇,但它可以很好地適合您的特定遊戲並節省您寶貴的時間。 例如,對於回合製遊戲或只能在 LAN 網路上玩的遊戲來說,延遲可能不是問題,因為 LAN 網路上的延遲和丟包率比 Internet 低得多。

許多成功的遊戲,包括《魔獸世界》、《我的世界》和《泰拉瑞亞》,都使用 TCP。 然而,大多數 FPS 使用自己的基於 UDP 的協議,因此我們將在下面詳細討論它們。

如果您決定使用 TCP,請確保將其停用 內格爾演算法,因為它在發送之前緩衝資料包,這意味著它會增加延遲。

要了解有關多人遊戲中 UDP 和 TCP 之間的差異的更多信息,您可以閱讀 Glenn Fiedler 的文章 UDP 對比傳輸控制協定.

自己的協議

您想創建自己的傳輸協議,但不知道從哪裡開始? 你很幸運,因為 Glenn Fiedler 寫了兩篇關於此的精彩文章。 你會發現其中有很多聰明的想法。

第一篇文章 遊戲程式設計師的網絡 2008年,比第二年容易, 建構遊戲網路協議 2016年。 我建議您從舊的開始。

請注意,Glenn Fiedler 是使用基於 UDP 的自訂協定的大力支持者。 讀完他的文章後,您可能會採納他的觀點,即 TCP 在視頻遊戲中存在嚴重缺陷,並且您會想要實現自己的協議。

但如果您是網路新手,請幫自己一個忙,使用 TCP 或函式庫。 要成功實現自己的傳輸協議,您需要事先學習很多知識。

網路圖書館

如果您需要比 TCP 更有效率的協議,但又不想經歷實現自己的協定和深入了解大量細節的麻煩,您可以使用網路庫。 其中有很多:

我沒有全部嘗試過,但我更喜歡 ENet,因為它易於使用且可靠。 此外,它還為初學者提供了清晰的文件和教程。

傳輸協定:結論

總結一下:有兩種主要的傳輸協定:TCP 和 UDP。 TCP 有許多有用的功能:可靠性、封包順序保存、錯誤偵測。 UDP 不具備這一切,但 TCP 本質上增加了延遲,這對某些遊戲來說是不可接受的。 也就是說,為了確保低延遲,您可以基於 UDP 創建自己的協議,或者使用在 UDP 上實現傳輸協議並適用於多人視頻遊戲的庫。

TCP、UDP 和庫之間的選擇取決於幾個因素。 首先,從遊戲的需求來看:是否需要低延遲? 其次,從應用協定要求來看:是否需要一個可靠的協定? 正如我們將在下一部分中看到的,可以創建一個非常適合不可信協議的應用程式協定。 最後,還需要考慮網路引擎開發人員的經驗。

我有兩個建議:

  • 盡可能從應用程式的其餘部分中抽像出傳輸協議,以便可以輕鬆替換它,而無需重寫所有程式碼。
  • 不要過度優化。 如果您不是網路專家且不確定是否需要自訂的基於 UDP 的傳輸協議,則可以從 TCP 或提供可靠性的庫開始,然後測試和測量效能。 如果出現問題並且您確信原因是傳輸協議,那麼可能是時候創建您自己的傳輸協議了。

在本部分的最後,我建議您閱讀 多人遊戲程式設計簡介 作者:Brian Hook,涵蓋了此處討論的許多主題。

應用協定

現在我們可以在客戶端和伺服器之間交換數據,我們需要決定要傳輸哪些數據以及以什麼格式。

經典的方案是客戶端向伺服器發送輸入或動作,伺服器將當前遊戲狀態傳送給客戶端。

伺服器發送的不是完整狀態,而是包含位於玩家附近的實體的過濾狀態。 他這樣做有三個原因。 首先,完整狀態可能太大而無法高頻傳輸。 其次,客戶端主要對視覺和音訊資料感興趣,因為大部分的遊戲邏輯都是在遊戲伺服器上模擬。 第三,在某些遊戲中,玩家不需要知道某些數據,例如敵人在地圖另一側的位置,否則他可以嗅探資料包並確切地知道該移動到哪裡來殺死他。

序列化

第一步是將我們要傳送的資料(輸入或遊戲狀態)轉換為適合傳輸的格式。 這個過程稱為 序列化.

我立即想到的想法是使用人類可讀的格式,例如 JSON 或 XML。 但這將完全無效,並且會浪費大部分通道。

建議使用二進位格式,這樣更加緊湊。 也就是說,資料包僅包含幾個位元組。 這裡有一個問題需要考慮 位元組順序,在不同的計算機上可能會有所不同。

要序列化數據,您可以使用庫,例如:

只需確保庫創建可移植檔案並關心字節序即可。

另一種解決方案是自己實現;這並不是特別困難,特別是如果您對程式碼使用以資料為中心的方法。 此外,它還允許您執行使用該庫時並不總是可行的最佳化。

Glenn Fiedler 寫了兩篇關於序列化的文章: 讀寫資料包 и 連載策略.

壓縮

客戶端和伺服器之間傳輸的資料量受到通道頻寬的限制。 數據壓縮將允許您在每個快照中傳輸更多數據,提高更新頻率,或只是降低通道要求。

位元封裝

第一種技術是位打包。 它包括準確使用描述所需值所需的位數。 例如,如果您有一個可以有 16 個不同值的枚舉,那麼您可以只使用 8 位,而不是整個位元組(4 位)。

Glenn Fiedler 在文章的第二部分解釋瞭如何實現這一點 讀寫資料包.

位元打包對於採樣特別有效,這將是下一節的主題。

採樣

採樣 是一種有損壓縮技術,僅使用可能值的子集來對值進行編碼。 實現離散化最簡單的方法是將浮點數進行捨入。

Glenn Fiedler(再次!)在他的文章中展示瞭如何將採樣付諸實踐 快照壓縮.

壓縮演算法

下一個技術將是無損壓縮演算法。

在我看來,以下是您需要了解的三種最有趣的演算法:

  • 霍夫曼編碼 使用預先計算的程式碼,速度非常快並且可以產生良好的結果。 它用於壓縮 Quake3 網路引擎中的資料包。
  • ZLIB 是一種通用壓縮演算法,不會增加資料量。 你怎麼能看到 這裡,它已被用於各種應用。 對於更新狀態來說可能是多餘的。 但如果您需要從伺服器向客戶端發送資產、長文字或地形,它會很有用。
  • 複製運行長度 - 這可能是最簡單的壓縮演算法,但它對於某些類型的資料非常有效,並且可以用作 zlib 之前的預處理步驟。 它特別適合壓縮由重複許多相鄰元素的圖塊或體素組成的地形。

增量壓縮

最後一種壓縮技術是增量壓縮。 它的事實在於,僅傳輸當前遊戲狀態與客戶端接收到的最後狀態之間的差異。

它首先用於 Quake3 網路引擎。 這裡有兩篇文章解釋瞭如何使用它:

格倫·費德勒(Glenn Fiedler)也在他的文章的第二部分中使用了它 快照壓縮.

加密

此外,您可能需要對客戶端和伺服器之間的資訊傳輸進行加密。 有幾個原因:

  • 隱私/保密:訊息只能由收件者閱讀,嗅探網路的其他人無法閱讀它們。
  • 身份驗證:想要扮演玩家角色的人必須知道他的金鑰。
  • 作弊預防:惡意玩家創建自己的作弊包將更加困難,他們必須重現加密方案並找到密鑰(每次連接都會改變)。

我強烈建議為此使用庫。 我建議使用 ,因為它特別簡單並且有很棒的教程。 特別有趣的是關於的教程 密鑰交換,它允許您為每個新連接產生新密鑰。

應用協議:結論

我們的應用程式協議到此結束。 我相信壓縮是完全可選的,使用它的決定僅取決於遊戲和所需的頻寬。 在我看來,加密是強制性的,但在第一個原型中你可以不加密。

應用邏輯

我們現在可以更新客戶端中的狀態,但可能會遇到延遲問題。 玩家完成輸入後,需要等待伺服器更新遊戲狀態,看看它對世界產生了什麼影響。

此外,在兩次狀態更新之間,世界是完全靜態的。 如果狀態更新率較低,則運動會非常不穩定。

有多種技術可以減少此問題的影響,我將在下一節中介紹它們。

延遲平滑技術

本節中描述的所有技術都將在本系列中詳細討論 快節奏多人遊戲 加布里埃爾·甘貝塔。 我強烈推薦閱讀這個優秀的系列文章。 它還包括一個互動式演示,讓您了解這些技術如何在實踐中發揮作用。

第一種技術是直接應用輸入結果,而不等待伺服器的回應。 它被稱為 客戶端預測。 但是,當客戶端收到來自伺服器的更新時,它必須驗證其預測是否正確。 如果不是這種情況,那麼他只需要根據從伺服器收到的資訊來改變他的狀態,因為伺服器是專制的。 這項技術首先在《Quake》中使用。 您可以在文章中閱讀更多相關信息 雷神之鎚引擎程式碼審查 法比安桑格拉斯 [翻譯 關於哈布雷]。

第二組技術用於平滑其他實體在兩個狀態更新之間的移動。 解決這個問題有兩種方法:內插法和外推法。 在插值的情況下,採用最後兩個狀態並顯示從一種狀態到另一種狀態的轉換。 它的缺點是會導致少量延遲,因為客戶端總是看到過去發生的事情。 外推是根據客戶端收到的最後狀態來預測實體現在應該在哪裡。 其缺點是,如果實體完全改變運動方向,那麼預測位置與實際位置之間就會有較大誤差。

僅在 FPS 中有用的最新、最先進的技術是 滯後補償。 使用延遲補償時,伺服器會考慮客戶端射擊目標時的延遲。 例如,如果玩家在螢幕上進行了爆頭,但實際上由於延遲而導致目標位於不同的位置,那麼由於延遲而剝奪玩家殺人的權利是不公平的。 因此,伺服器會將時間倒回玩家開槍的那一刻,以模擬玩家在螢幕上看到的內容並檢查射擊與目標之間的碰撞。

Glenn Fiedler(一如既往!)在 2004 年寫了一篇文章 網路物理(2004),其中他為伺服器和客戶端之間的實體模擬同步奠定了基礎。 2014年他寫了一系列新文章 網路物理,其中描述了同步物理模擬的其他技術。

Valve wiki 上還有兩篇文章, 來源多人網絡 и 客戶端/伺服器遊戲內協定設計與最佳化中的延遲補償方法 其中考慮了延誤補償。

防止作弊

防止作弊有兩種主要技術。

第一:讓作弊者更難發送惡意資料包。 如上所述,實現這一點的一個好方法是加密。

第二:獨裁伺服器應該只接收命令/輸入/操作。 除了發送輸入之外,客戶端不應該能夠更改伺服器上的狀態。 然後,伺服器每次接收輸入時,都必須在使用之前檢查它是否有效。

應用邏輯:結論

我建議您實施一種模擬高延遲和低刷新率的方法,以便您可以在惡劣條件下測試遊戲的行為,即使客戶端和伺服器在同一台電腦上運行也是如此。 這將大大簡化延遲平滑技術的實現。

其他有用的資源

如果您想探索有關網路模型的其他資源,可以在這裡找到它們:

來源: www.habr.com

添加評論