在 服务器权限模型 中,服务器是整个游戏状态的唯一 真相来源,客户端只被信任报告自己的输入。这种架构是公平竞争游戏的核心网络代码基础,因为它通过绝不信任客户端报告其自己的位置或状态,防止了整个类别的作弊行为,如飞行作弊或加速作弊。
优势
在一个简单的服务器拥有系统中,客户端会将其输入发送到服务器,并显示服务器返回的游戏结果。尽管在技术上是正确的,这种系统会面临显著的 输入延迟,因为每个玩家的操作都必须旅行到服务器,经过处理,然后将结果发送回客户端才能显示。对于大多数游戏,尤其是快速的游戏,这种往返延迟会导致游戏感觉滞后、不响应且不可玩。
在 Roblox 的 服务器权限 模型中,延迟通过让客户端即时 预测 输入的效果来补偿,同时将它们发送到服务器。例如,当玩家按下一个键时,客户端不会等待服务器的响应;相反,它会在最后已知的服务器状态之前预测几个帧。这允许客户端立即显示输入操作的结果,从而有效隐藏网络延迟,使游戏感觉响应迅速。
有时,客户端会预测错误 (misprediction),并且由于网络延迟,客户端在几个帧内不会知道它犯了错误。例如:
当检测到错误预测时,客户端必须根据服务器的 权威状态 来修正其预测。如果权威状态与客户端的预测状态不同,客户端必须 回滚并重新模拟 预测的帧。此客户端侧预测、回滚和重新模拟的系统称为“延迟补偿”,有助于使服务器权限的多玩家游戏感觉平滑且响应迅速。
设置
服务器权限模型需要某些其他引擎技术才能正确运行。在 资源管理器 中确认 Workspace 对象上的以下属性设置:
- Workspace.AuthorityMode 必须是 Server(设置此项会自动设置以下五个)。
概念
服务器权限系统基于几个核心概念,如下所述。
客户端预测
通过 客户端预测,客户端在最后已知的服务器状态之前预测几帧,以立即预测玩家输入的效果。这隐藏了输入延迟,但该预测可能会后来证明是错误的 (client misprediction),因此需要纠正。客户端试图在最后一个已知的权威服务器状态之前模拟足够远的距离,以便它的输入能够在预定帧到达服务器。客户端将预测的帧数基于客户端和服务器之间的延迟。
客户端错误预测
当客户端接收到来自服务器的权威状态时,它会将该状态与其对该帧的历史记录进行比较。当客户端 预测 的内容与服务器实际的情况之间存在差异时,这就是 错误预测。错误预测可能由于多种原因发生,包括网络延迟的变化、其他玩家采取客户端未预料到的行动、游戏在服务器上独占运行某些逻辑等。
如果权威状态与客户端的预测状态不同,客户端必须 回滚并重新模拟。
回滚和重新模拟
当客户端检测到 错误预测 时,它必须重置为服务器的权威状态,然后重新模拟,以跳回其预测的帧。基于网络延迟,客户端试图在最后一个已知的权威服务器状态之前模拟足够远的距离,以便它的输入能够在预定帧到达服务器。
总之,客户端:
- 从服务器接收 权威状态 并与自己的 预测状态 进行比较。
- 如果客户端的预测不正确:
- 客户端回滚到从服务器接收到的最后已知权威状态。
- 客户端从权威状态重新模拟到其预测状态,重新应用任何本地输入。
实现
网络拥有权和预测
在服务器权限模型中,您可以保持核心游戏对象服务器拥有,而不必承担通常与服务器拥有相关的输入延迟成本。诸如汽车、玩家角色或其他关键游戏对象可以保持服务器拥有,甚至在与其他玩家交互时。
默认情况下,Roblox 将自动预测靠近本地玩家 Character 的具有 模拟访问 的属性,但如果您想要更细粒度的控制,可以通过 RunService:SetPredictionMode() 明确强制某个实例的预测开启或关闭。
模拟同步
在服务器权限模型中,客户端和服务器必须同时运行核心模拟,客户端的模拟需要能够在发生 错误预测 时进行 回滚和重新模拟。要启用此功能,请将核心逻辑写入通过 RunService:BindToSimulation() 绑定的函数中,这些函数在客户端和服务器上都初始化的 ModuleScript 中。

在重新模拟期间,Roblox 将重新运行通过 BindToSimulation() 绑定到模拟的函数。处理玩家输入、与同步的物理对象交互以及更新核心游戏状态应放在这些绑定的函数中。
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() 绑定的函数内部访问。
输入动作
在一个服务器权威的游戏中,客户端影响游戏状态的主要方式是通过 输入动作系统。这些输入被发送到服务器,并在客户端的重新模拟过程中重放。因此,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() 限制了可以在绑定函数内调用的属性和方法,以帮助您仅对同步的模拟状态进行写入。呈现此模拟的结果、触发效果和声音等操作应在与 RenderStepped 连接的单独函数中进行,该函数读取模拟结果并触发所需的效果。
关于呈现预测模拟的进一步指导在 高级技术 指南中有介绍。
示例项目
除了本文档外,以下模板可以帮助您入门:


