瞭解多人游戲下的客戶端與伺服器體繫結構

来源:https://www.cnblogs.com/linxiaoxu/archive/2023/08/22/17647493.html
-Advertisement-
Play Games

## 直連 直連模式下,選擇一個玩家充當伺服器(房主)。如果游戲出現不同步,那麼均按房主的世界來,玩家1可以作弊修改其游戲來影響其他玩家的世界 針對兩個玩家來說,直連連接質量更好,延遲小 如果玩家數量很多,不同玩家間的通信則需要靠房主為中介,那通信質量與房主主機配置、網路情況有很大關係 ## 專用服 ...


直連

image-20230821204552758

直連模式下,選擇一個玩家充當伺服器(房主)。如果游戲出現不同步,那麼均按房主的世界來,玩家1可以作弊修改其游戲來影響其他玩家的世界

針對兩個玩家來說,直連連接質量更好,延遲小

如果玩家數量很多,不同玩家間的通信則需要靠房主為中介,那通信質量與房主主機配置、網路情況有很大關係


專用伺服器

image-20230821205140980

所有玩家與專用伺服器通信,專用伺服器通常運行沒有游戲界面的游戲代碼的修改版本,因此可以在配置較低的電腦上運行,還可以檢測作弊,伺服器維護的游戲世界是最權威的


① 客戶端和伺服器

客戶端全聽伺服器的

游戲狀態由伺服器單獨管理,客戶端只把操作(按鍵,指令)發送給服務端,伺服器定期更新游戲狀態,將新的狀態發送給客戶端,客戶端只需要在屏幕上渲染即可

image-20230821232557753

可以預防大範圍作弊

  • 玩家本地修改生命值為99999,伺服器那依舊是10,玩家依舊會收到死亡事件消息
  • 玩家本地修改位置,伺服器處理玩家向右移動一個單位,玩家位置被同步修複

應用舉例:慢節奏游戲,如策略或卡牌


延遲問題

網路數據傳輸要經過大量路由器,還可能遇到網路擁塞情況,遠距離傳輸延遲可能會很高(100~500ms)

玩家發送一個向右移動一格指令,花費了100ms傳到伺服器,伺服器處理狀態,再花費100ms將狀態傳給玩家

在玩家看來,按下了右鍵後有0.2秒的時間游戲沒有任何反應,然後角色才向右移動一格,這是能明顯感知到的卡頓,嚴重時無法玩家無法進行游戲


② 客戶端預測和伺服器對賬

客戶端預測

大部分玩家的輸入都是按預期效果執行的。 輸入 = 操作 = 行為 = 動作 = 滑鼠點擊移動,鍵盤按下釋放等

如果給定游戲狀態和輸入操作,客戶端能完全預測游戲世界的變化(跟伺服器一樣的處理),且只有唯一一種結果(絕對性)

我們可以將輸入操作發送給服務端,先不等服務端返回,在客戶端立即生效預測的結果,這種方式消除了輸入操作和狀態變化之間的延遲,而且客戶端預測的結果大多是正確的,能跟伺服器返回的結果相匹配。

假設有100ms的網路延遲(往返),移動位置的動畫需要100ms,整個操作需要200ms,這是方案①導致的結果

image-20230822000158999

使用方案②後,客戶端檢測到輸入預測玩家位置並更新,動畫播放和網路請求同步進行,原來的200ms現在只需要100ms。伺服器返回的結果與客戶端預測的結果一致。註:伺服器依舊是權威的,維護著所有玩家真實的狀態

image-20230822000419011

同步問題

假設有250ms的網路延遲,玩家按了兩次右鍵,向右移動兩次,客戶端立即模擬,間隔地向伺服器發送兩次"向右移動"操作

兩次移動均完成後過50ms才收到伺服器傳來的第一次“向右移動”操作的結果,此時出現狀態衝突

由於伺服器權威性,客戶端根據“真實的狀態”重新設置了角色位置(角色居然跳回去了),然後又跳回來

image-20230822000815170

伺服器對賬

對賬:會計先做個手工表,每算一會把系統算的表拿出來核對,如果不對就糾正手工表,然後接著算

要修複同步問題需要知道伺服器可能沒有處理完客戶端的所有輸入,客戶端的狀態是現在時,而伺服器傳來的狀態是過去某一時刻的,兩者有一定的時間差

可以為客戶端的每個輸入添加一個數字標記,按照輸入的順序遞增這個標記。客戶端發送的兩個輸入分別被標記了#1、#2,且保存了各自的副本。下麵的例子演示了一個客戶端和一個服務端的網路交互

伺服器每發來一個真實狀態,客戶端就重新模擬一次:當t=250ms,客戶端接收到伺服器對#1輸入的結果時,客戶端丟棄#1輸入的副本,併在#1的結果上根據#2副本重新計算當前游戲狀態。計算後與當前狀態對比,如果有差異則重新設置

image-20230822002124542

當t=350ms,客戶端接收到伺服器對#2輸入的結果時,丟棄#2輸入的副本,此時因為輸入副本隊列為空,不需要重新模擬。只需要將結果與當前狀態對比,如果有差異則重新設置

應用:在回合制戰鬥中,玩家A攻擊另一個角色B時,可以優先顯示血液效果和造成傷害的數字,但不應該在伺服器返回結果前更新角色健康狀態(不是絕對的,可能有多種結果,如這一擊是否把角色B打斷了腿,或者是角色B使用醫療包的事件比攻擊早)

不容易逆轉:如果A可以預測並更新健康狀態、角色B打醫療包的事件又在A攻擊之前,此時A客戶端還沒有受到角色B健康狀態的更新,A客戶端預測B生命值降至0,觸發角色死亡事件(可能銷毀了這個實體),而實際上角色還在伺服器那活得好好的,那還原前一步就十分複雜了

客戶端預測的結果與伺服器預測的結果不匹配問題,在多個客戶端情況下經常發生


ANTI WEB SPIDER BOT www.cnblogs.com/linxiaoxu

③ 實體插值(平滑插值)

伺服器時間步長

考慮方案②,當大量客戶端接入一個伺服器時,伺服器將收到大量的操作(按鍵、滑鼠)輸入,每接收一個輸入就要更新當前世界並廣播游戲狀態,需要消耗大量的CPU和帶寬

最好的辦法是整個游戲世界以低頻率周期性地更新,例如每秒10次,每次更新延遲為100ms,稱為時間步長

把接收到的輸入全部放入隊列中,不做處理(個人認為接收到輸入直接處理也行)。每100ms,處理隊列,將更新後的狀態廣播給每個客戶端

在這種情況下,游戲世界以可預測的速率獨立於客戶端輸入的存在、輸入數量進行更新


航位推算

航位推算是通過使用先前確定的位置或定位,並結合對速度、航向(或方向或航向)和經過時間的估計,來計算移動物體的當前位置的過程。

多人模式下,在實體方向和速度都會立即改變的情況下,航位推算的結果是不准確的。比如多人賽車,伺服器每100ms將其他車輛的位置、速度、方向狀態傳遞給客戶端,客戶端僅能根據這些信息來模擬賽車運動100ms,最後模擬得到的位置跟伺服器下一次發來的位置有較大的差別,位置糾正,汽車瞬移。

詳細的說(省略了對賬過程):

  • 有玩家A(0,0)跟玩家B(0,0),伺服器C
  • C告訴A:B正在往左移動,速度100m/s,位置是(0,0)
  • C告訴B:A現在沒移動,位置是(0,0)
  • 50ms後中B開始(往右移動,速度瞬間變成1000m/s);這兩個事件被加入了C的隊列
  • 又過了50ms,到現在 t = 100ms
  • B在A上渲染的位置(-10,0)
  • B自己渲染的位置(-5+50,0)
  • C處理隊列,將狀態發送給A跟B
  • C告訴A:B正在往右移動,速度1000m/s,位置是(45,0)
  • C告訴B:A現在沒移動,位置是(0,0)
  • A發現伺服器給的B位置與自己預測的位置有出路
  • A修複B的位置,B被瞬移了

玩家自身跟伺服器通信是不會出現這種問題的,因為玩家操作的實體是實時的,沒有延遲;其他玩家不是實時的,同步數據都是伺服器給的,即其他實體相關信息的一種稀疏性


實體插值

由於玩家的方向和速度都會立即改變,航位推算無法應用。如FPS,玩家通常以非常高的速度奔跑、蹲比和轉彎,這使得航位推算毫無用處,因為無法再根據之前的狀態準確地預測位置和速度。

為了給玩家帶來連續性和流暢移動的錯覺,採用一種巧妙的做法

每個玩家本身是現在時,而其他玩家都是過去式:玩家自己是實時的,而看到的其他玩家都是他們過去某一時刻的狀態。

將伺服器最新發來的狀態記P1,前一個時間步長的狀態記P2(舊狀態)客戶端在本地,那接下來一個時間步長內整個世界狀態將線性從P2變為P1

也就是說,你比其他所有人都快了一個時間步長,其他人比你都慢一拍

下圖很好解釋了線性插值,v=(11.75,10)意味著客戶端2在收到P1後已經過了75ms(時間步長100ms,線性插值的步長也是100ms),當時間過了100ms,客戶端2的世界狀態將完全變為P1,此時可能已經有了新的P1或者還沒收到P1,我們可以根據網路延遲動態修改線性插值的步長

image-20230822020146078

目前的功能

  • 客戶端在本地發送輸入並模擬效果
  • 伺服器從所有客戶端獲取帶有時間戳的輸入
  • 伺服器處理輸入並更新世界狀態
  • 伺服器向所有客戶端發送伺服器世界狀態的快照
  • 客戶端接收伺服器發來的世界狀態更新
    • 根據這個狀態與沒被伺服器處理的輸入 重新模擬
    • 對其他實體狀態進行線性插值

④ 延遲補償

對時間和空間敏感的事件來說,比如射擊事件,當玩家向另一名玩家射擊時,由於其他玩家都是過去的玩家,所以你的瞄準延遲為100毫秒,你在對100毫秒延遲之前的敵人射擊。

通用解決方法 伺服器根據射擊事件的時間戳,重建該時間戳時的世界狀態,可以準確地知道你開槍的那一刻準星瞄準的實體

但由於是過去式,在敵人看來,100ms之後可能已經移動到掩體之後,卻依舊被爆頭了,不過這個解決方案已經很不錯了


⑤ 幀同步

幀同步

該部分還沒講全,未來某天補上代碼

狀態同步講完,接下來講主流同步方式的另外一種:幀同步。通常用於實時戰略和FPS

幀同步通過同步玩家的動作,確保每個人都能獲得相同的輸入,併在每一幀上執行相同的邏輯,最終獲得一致的性能和結果

相同的輸入 + 相同的時序 = 相同的輸出

如何確保同一時間點

等待所有玩家載入完成,由於載入完成後還會有一系列初始化操作,可以播個開場動畫,做到所有玩家都在同一時間點開始游戲

同步設備時間

客戶端訪問伺服器,伺服器返回一個ping值,乘以2加上伺服器返回的時間就是準確的當前伺服器時間。游戲期間後續同步中根據較小的ping值修改時間

同步種子

游戲里經常會使用隨機數,同步隨機數種子可以保證各個客戶端模擬的一致性

命令同步

伺服器每幀收集所有玩家操作,然後將其廣播給所有玩家,沒有玩家操作就廣播一個空指令,向前推動游戲幀

核心邏輯-命令隊列

命令隊列的設計可以輕鬆實現戰鬥回放。創建兩種偵聽器,分別是本地模式和網路模式

  • 本地模式下偵聽玩家的操作並將操作填充到隊列中
  • 網路模式下偵聽玩家的操作併發送給伺服器,同時監視伺服器發來的數據並將操作填充到隊列中

核心邏輯-游戲主迴圈識別

幀同步需要我們嚴格控制整個游戲的執行順序,通常情況下,不能直接使用引擎更新,需要把一切掌握在自己手中。首先需要控制的是幀速率

  • 以特定的幀率來運行游戲,如每秒60幀
  • 跟蹤幀進度並控制,如果當前設備幀索引落後過多,加快它的幀率

對象的更新應當是按特定順序執行的,需要進行排序

網路延遲

添加幀緩衝區和前滾動畫,用UDP取代底層TCP如KCP

由於TCP超時重傳機制。沒有收到一幀的數據包時,游戲的邏輯無法正常執行,直到數據包被重新發送

或者直接幀鎖定,直到有數據來,以超快的幀率同步

不用幀鎖定,客戶端請求伺服器狀態副本,實現回滾跟重試然後恢復

重新連接

如果是一個小的重新連接,只丟失了幾幀數據,會用這幾幀的數據進行補充。如果是一個大型重新連接,伺服器序列化的數據此時將緩存5秒。如果在這段時間內重覆斷開連接並重新連接,伺服器將重用這些緩存的數據。

優點

使用幀同步可以節省消息量,狀態同步需要伺服器對每個客戶端發送大量狀態信息(大量實體,每個實體各自維護大量欄位),幀同步只需要發送操作指令和幀索引

由於消息量得到了節省,在網路情況不佳的情況下,也能實現實時戰鬥游戲的同步問題

我們可以輕鬆實現回放,伺服器記錄所有操作,客戶端請求回放文件執行每一幀


參考資料

Client-Server Game Architecture - Gabriel Gambetta

Networking (part 2) · GitBook (rvagamejams.com)

Game server synchronization of large amounts of data in a battle (monstar-lab.com)

Tutorial: Technical Implementation Details of Frame Synchronization in Games


ENet

ENet是LOVE使用的一個第三方網路庫,採用UDP協議,在運輸層幫我們完成了各種事情,包括消息確認

帶心跳檢測功能,當有一方不回覆超過5~30秒時則認為其disconnect


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ##### 7 選擇器優先順序 所謂CSS優先順序,即是指CSS樣式在瀏覽器中被解析的先後順序。樣式表中的特殊性描述了不同規則的相對權重。 ``` /* !important > 行內樣式>ID選擇器 > 類選擇器 > 標簽 > 通配符 > 繼承 > 瀏覽器預設屬性 1 內聯樣式表的權值最高 style ...
  • ##### 6 樣式繼承 CSS的樣式表繼承指的是,特定的CSS屬性向下傳遞到子孫元素。總的來說,一個HTML文檔就是一個家族,然後html元素有兩個子元素,相當於它的兒子,分別是head和body,然後body和head各自還會有自己的兒子,最終形成了一張以下的家族譜。 ![image](http ...
  • ##### 9 閉包 我們都知道,函數里是可以訪問函數外的全局變數,而函數外不能訪問函數內的局部變數,如下: ```js // 函數外定義a,在函數內可以訪問 var a = "測試"; function fn() { console.log(a); } fn(); ``` 執行結果: ![imag ...
  • ##### 5 偽類選擇器 anchor偽類:專用於控制鏈接的顯示效果 |More Actions[:link](https://www.w3school.com.cn/cssref/selector_link.asp) |a:link |選擇所有未被訪問的鏈接。 | | | | | |[:visi ...
  • # 通過 NVM 安裝、管理Node.js 版本(Windows) ## 介紹 NVM 可以讓我們通過指令安裝指定版本,並且可以切換當前Node.js 版本,不用花時間在解決升版和降版。 ## 安裝 鏈接: https://github.com/coreybutler/nvm-windows 選擇最 ...
  • ##### 4 屬性選擇器 屬性選擇器是通過元素的屬性及屬性值來選擇元素的。下麵介紹屬性選擇器的用法。 1. 第一種用法 ``` 作用:選擇含有指定屬性的元素。 語法:[屬性名]{} ``` 示例如下: ```html 屬性選擇器 用戶名: 密 碼: 數據量: ``` 運行結果: ![image]( ...
  • ##### 8 JavaScript函數 在JS中聲明函數和python差不多. 也要有一個關鍵字頂在前面. python是`def`, 到了JS里換成了`function`, 只不過在JS中沒有像python那麼死板, 必須`def`後面必須跟上函數名. 這也為我們未來做逆向提供了第一個超大的伏筆 ...
  • 在當今快節奏的社會中,技術變化日新月異。作為一名技術博客站長,我深切感受到了學習和傳播知識的重要性。為了更好地滿足讀者的需求,我決定採用VuePress搭建一個功能強大且易於維護的知識庫平臺,名為LearnData。本文將介紹我如何利用VuePress構建LearnData,並展示一些相關的代碼示例... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...