在 伺服器權限模型 中,伺服器是整個遊戲狀態的唯一 真相來源,客戶端僅被信任報告自己的輸入。這種架構是公平競技遊戲的核心網路代碼基礎,因為它通過不信任客戶端報告自己的位置或狀態來防止整個類別的作弊行為,例如飛行作弊或加速作弊。
優勢
在一個天真的伺服器擁有系統中,客戶端會簡單地將其輸入發送到伺服器,並顯示伺服器返回的遊戲結果。雖然在技術上是正確的,但這樣的系統會面臨顯著的 輸入延遲,因為每個玩家的行動都必須傳送到伺服器,經過處理後再將結果發送回客戶端,然後才能顯示。對於大多數遊戲,尤其是那些快節奏的遊戲,這種往返延遲會使遊戲感覺延遲、不響應且無法遊玩。
在 Roblox 的 伺服器權限 模型中,延遲通過讓客戶端立即 預測 輸入的效果來補償,並將其發送到伺服器。例如,當玩家按下某個鍵時,客戶端不會等待伺服器的回應;相反,它會 預測 最後已知伺服器狀態的幾幀。這使得客戶端能夠立即顯示輸入行動的結果,有效地隱藏網路延遲,並使遊戲感覺響應迅速。
有時客戶端會錯誤地進行預測(錯誤預測),並且由於網路延遲,客戶端在幾幀內不會知道自己犯了錯。例如:
當檢測到錯誤預測時,客戶端必須根據伺服器的 權威狀態 來修正其預測。如果權威狀態與客戶端的預測狀態不同,客戶端必須 回滾並重新模擬 其預測幀。這種客戶端預測、回滾和重新模擬的系統被稱為「延遲補償」,有助於使伺服器權威的多人遊戲感覺流暢且響應迅速。
設置
伺服器權限模型需要某些其他引擎技術才能正常運行。請確認 Explorer 中 Workspace 對象的以下屬性設置:
- Workspace.AuthorityMode 必須是 Server(設置此項會自動設置以下五項)。
概念
伺服器權限系統基於幾個核心概念運行,如下所示。
客戶端預測
通過 客戶端預測,客戶端在最後已知的伺服器狀態之前模擬幾幀,以立即預測玩家輸入的效果。這隱藏了輸入延遲,但預測可能後來被證明是錯誤的(客戶端錯誤預測),因此需要修正。客戶端嘗試模擬到足夠接近最後已知的權威伺服器狀態,以便其輸入能在預期的幀上到達伺服器。客戶端將預測的幀數基於客戶端和伺服器之間的延遲。
客戶端錯誤預測
當客戶端從伺服器接收到權威狀態時,它會將該狀態與其對該幀的本地預測歷史記錄進行比較。當客戶端的 預測 與伺服器實際執行的內容之間存在差異時,這就是 錯誤預測。錯誤預測可能由多種原因引起,包括網路延遲的變化、其他玩家以客戶端未預期的方式行動、遊戲在伺服器上獨占運行某些邏輯等。
如果權威狀態與客戶端的預測狀態不同,客戶端必須 回滾並重新模擬。
回滾和重新模擬
當客戶端檢測到 錯誤預測 時,它必須重置為伺服器的權威狀態,然後重新模擬以跳回其預測的幀。根據網路延遲,客戶端嘗試模擬到足夠接近最後已知的權威伺服器狀態,以便其輸入能在預期的幀上到達伺服器。
總結來說,客戶端:
- 接收來自伺服器的 權威狀態 並將其與自己的 預測狀態 進行比較。
- 如果客戶端的預測不正確:
- 客戶端回滾到從伺服器接收到的最後已知權威狀態。
- 客戶端從權威狀態重新模擬到其預測狀態,重新應用任何本地輸入。
實現
網路擁有權和預測
在伺服器權限模型中,您可以保持核心遊戲對象由伺服器擁有,而不會產生通常與伺服器擁有相關的輸入延遲成本。像汽車、玩家角色或其他遊戲關鍵對象可以保持由伺服器擁有,即使在與其他玩家互動時也是如此。
默認情況下,Roblox 將自動預測靠近本地玩家 Character 的具有 模擬訪問 的屬性,但如果您想要更細粒度的控制,可以使用 RunService:SetPredictionMode() 明確強制實例的預測開啟或關閉。
自動預測適用於整個物理組件,而不是單個實例。當客戶端預測一個部件時,它還會預測通過焊接、關節或約束與其物理連接的每個部件,以及這些部件的父 Model 和該模型內的任何 Humanoid。例如:
- 一個焊接到預測角色的劍模型成為角色組件的一部分,因此它會自動預測。
要檢查特定實例是否當前被預測,可以從客戶端調用 RunService:GetPredictionStatus()。
模擬同步
在伺服器權限模型中,客戶端和伺服器都必須運行核心模擬,並且客戶端的模擬需要能夠在發生 錯誤預測 時 回滾並重新模擬。為了啟用這一點,請將您的核心邏輯寫在通過 RunService:BindToSimulation() 綁定的函數中,這些函數在客戶端和伺服器上都會初始化。

在重新模擬期間,Roblox 將重新運行通過 BindToSimulation() 綁定到模擬的函數。處理玩家輸入、與同步的物理對象互動以及更新核心遊戲狀態應該在這些綁定函數內進行。
在綁定的模擬函數內,使用 time() 進行任何基於時間的邏輯,例如驅動動畫速度或隨時間波動的值。當 Workspace.AuthorityMode 為 Enum.AuthorityMode.Server 時,time() 在客戶端和伺服器之間是同步的,並使用固定步長模擬時間。在 重新模擬 期間,它會回到權威幀並在客戶端重播幀時向前推進,因此每次模擬幀時都會返回一致的值。
ModuleScript 名為 Simulation 在 ReplicatedStorage 中:
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Simulation = {}
Simulation.Initialize = function()
RunService:BindToSimulation(function(deltaTime)
-- 讀取玩家輸入
-- 更新遊戲狀態
end)
end
return Simulation使用屬性進行狀態同步
Roblox 自動同步所有具有 模擬訪問 的預測實例上的所有屬性。對於自定義數據,屬性 是同步標記為預測的實例的主要方式;在這些實例上,伺服器的真相來源和客戶端的預測之間的任何屬性值不匹配都將觸發完整的 回滾和重新模擬。
屬性限制
為了能夠被複製,屬性必須滿足以下所有標準:
- 它是其 Instance 上的前 64 個屬性之一。
- 它的名稱最多包含 50 個字符。
- 如果是字符串類型屬性,其值最多包含 50 個字符。
模擬訪問
引擎 API 參考中的許多屬性和方法都包含 模擬訪問 標籤,例如 BasePart.CFrame。具有此標籤的屬性將由伺服器權限系統進行預測。此外,只有具有此標籤的屬性和方法才能在通過 RunService:BindToSimulation() 綁定的函數內訪問。
創建和刪除實例
在通過 BindToSimulation() 綁定的函數內,您可以使用 Instance.new()、Instance:Clone() 或 Instance.fromExisting() 創建實例。客戶端立即創建實例,當伺服器的權威副本到達時,Roblox 將其與伺服器的權威副本合併。詳細信息,請參見 預測實例創建。
刪除實例的方式不同。Instance:Destroy() 沒有 模擬訪問,並且您無法更改已在 DataModel 中的實例的 Parent,因此在綁定函數內調用任一操作會導致錯誤。相反,請在模擬內的 屬性 中記錄刪除實例的意圖,然後在模擬外部刪除實例,例如在伺服器的 PostSimulation 連接中。
local RunService = game:GetService("RunService")
local character = script.Parent
RunService.PostSimulation:Connect(function()
-- "SwordEquipped" 是當玩家卸下劍時由模擬寫入的
local sword = character:FindFirstChild("Sword")
if sword and not character:GetAttribute("SwordEquipped") then
sword:Destroy()
end
end)輸入行動
在伺服器權威遊戲中,客戶端影響遊戲狀態的主要方式是通過 輸入行動系統。這些輸入被發送到伺服器,並在客戶端的重新模擬期間重播。因此,應該對 所有影響核心模擬的輸入 使用 InputActions,並且在處理之前應檢查其有效性。
請注意,InputContexts 必須是 Player 的子項,以便引擎知道誰擁有 InputContext。一種方法是將您的 InputContexts 添加到 ReplicatedStorage 下的文件夾中,並使用 Script 在 ServerScriptService 下為每個玩家克隆 InputContexts:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local InputsFolder = ReplicatedStorage:WaitForChild("Inputs")
local function onPlayerAdded(player)
local clone = InputsFolder:Clone()
clone.Parent = player
end
Players.PlayerAdded:Connect(onPlayerAdded)
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end使用此模式,您可以在客戶端和伺服器的 RunService:BindToSimulation() 中讀取 InputActions,以接收給定幀的相同數據,並在 屬性 中記錄前一幀的輸入,例如在觸發 RunAction 輸入行動時觸發角色運行。
遠程事件
遠程事件 仍然可以在伺服器權限模型中使用,以促進客戶端和伺服器之間的離散通信。例如,伺服器可以使用遠程事件廣播有關玩家得分或撿起物體的數據,客戶端可以使用遠程事件作為發送輸入到伺服器的替代 API,例如按鈕按下或在 3D 世界中點擊物體。
動畫
影響遊戲玩法的動畫,例如角色移動或攻擊動畫,應在通過 BindToSimulation() 綁定的函數內控制,這些函數在客戶端和伺服器上運行。核心 Animator 和 AnimationTrack 成員,例如 LoadAnimation()、Play()、Stop()、AdjustSpeed() 和 TimePosition 具有 模擬訪問。
動畫狀態是預測模擬的一部分。當動畫期間發生 回滾 時,客戶端會將動畫回滾到權威狀態,並與模擬的其餘部分一起重播,因此客戶端和伺服器的動畫保持同步。由於客戶端可能在回滾期間替換動畫軌道,因此請勿在幀之間緩存 AnimationTrack 引用。相反,請使用 Animator:GetTrackByAnimationId() 在每個幀查找實時軌道。完整示例,請參見 編寫動畫代碼。
聲音和效果
客戶端效果,例如聲音和粒子,必須在知道客戶端模擬僅僅是權威伺服器狀態的預測的情況下編寫。BindToSimulation() 限制了可以在綁定函數內調用的屬性和方法,以幫助指導您僅寫入同步的模擬狀態。顯示此模擬的結果、觸發效果和聲音等應在連接到 RenderStepped 的單獨函數中進行,該函數讀取模擬的結果並觸發所需的效果。
由於客戶端可能預測伺服器上從未發生的事件,因此您的渲染代碼應能夠撤消或隱藏錯誤預測的效果。關於渲染預測模擬的進一步指導涵蓋在 播放聲音和視覺效果 中。
示例項目
除了這份文檔,以下模板可以幫助您入門:


