Trong một mô hình quyền kiểm soát của máy chủ, máy chủ là nguồn thông tin chính xác duy nhất cho toàn bộ trạng thái trò chơi, và các client chỉ được tin cậy để báo cáo các đầu vào của chính họ. Kiến trúc này là nền tảng mã mạng cốt lõi của một trò chơi công bằng và cạnh tranh vì nó ngăn chặn các loại gian lận như flyhacks hoặc speedhacks bằng cách không bao giờ tin tưởng vào một client để báo cáo vị trí hoặc trạng thái của chính nó.
Ưu điểm
Trong một hệ thống do máy chủ sở hữu đơn giản, các client sẽ chỉ gửi các đầu vào của mình đến máy chủ và hiển thị kết quả của trò chơi được gửi lại bởi máy chủ. Mặc dù về lý thuyết là đúng, một hệ thống như vậy sẽ phải đối mặt với độ trễ đầu vào đáng kể vì mỗi hành động của người chơi sẽ phải di chuyển đến máy chủ, được xử lý và có kết quả gửi trở lại client trước khi nó có thể được hiển thị. Đối với hầu hết các trò chơi, đặc biệt là những trò chơi có nhịp độ nhanh, độ trễ vòng quay đó sẽ khiến trải nghiệm chơi cảm thấy lag, không phản hồi và không thể chơi được.
Trong mô hình quyền kiểm soát của máy chủ của Roblox, độ trễ được bù đắp bằng cách để các client ngay lập tức dự đoán tác động của các đầu vào của họ ngoài việc gửi chúng đến máy chủ. Ví dụ, khi một người chơi nhấn một phím, client sẽ không chờ máy chủ phản hồi; thay vào đó, nó dự đoán vài khung hình phía trước so với trạng thái máy chủ đã biết cuối cùng. Điều này cho phép client hiển thị kết quả của hành động đầu vào ngay lập tức, hiệu quả là ẩn đi độ trễ mạng và làm cho trò chơi cảm thấy phản hồi nhanh.
Đôi khi client sẽ dự đoán sai (dự đoán sai) và, do độ trễ mạng, client sẽ không biết rằng nó đã mắc sai lầm trong vài khung hình. Ví dụ:
Khi một dự đoán sai được phát hiện, client phải sửa chữa dự đoán của mình dựa trên trạng thái chính danh của máy chủ. Nếu trạng thái chính danh khác với trạng thái được client dự đoán, client phải quay lại và tái mô phỏng các khung hình dự đoán của nó. Hệ thống dự đoán phía client, quay lại và tái mô phỏng này được gọi là "bù đắp độ trễ" và giúp cho các trò chơi nhiều người chơi dựa trên máy chủ trở nên mượt mà và đáp ứng hơn.
Thiết lập
Vì mô hình quyền kiểm soát của máy chủ yêu cầu một số công nghệ động cơ khác để hoạt động chính xác, bạn phải đặt các thuộc tính sau trên đối tượng Workspace trong Explorer:
- Workspace.NextGenerationReplication phải được bật
- Workspace.PlayerScriptsUseInputActionSystem phải được bật
- Workspace.SignalBehavior phải là Deferred
- Workspace.UseFixedSimulation phải được bật
- Workspace.StreamingEnabled phải được bật
- Workspace.AuthorityMode phải là Server (tất cả các thuộc tính trên phải được thiết lập trước)
Khái niệm
Hệ thống quyền kiểm soát của máy chủ hoạt động dựa trên một vài khái niệm cốt lõi như sau.
Dự đoán client
Thông qua dự đoán client, client mô phỏng một vài khung hình phía trước so với trạng thái máy chủ đã biết cuối cùng để dự đoán các tác động của đầu vào người chơi ngay lập tức. Điều này ẩn đi độ trễ đầu vào, nhưng dự đoán có thể sau đó trở thành sai (dự đoán sai của client) và do đó cần được sửa chữa. Client cố gắng mô phỏng đủ xa so với trạng thái máy chủ chính danh cuối cùng để các đầu vào của nó đến máy chủ ở khung hình mong muốn. Số lượng khung hình mà client sẽ dự đoán phía trước trạng thái máy chủ đã biết dựa trên độ trễ giữa client và máy chủ.
Dự đoán sai của client
Khi client nhận được trạng thái chính danh từ máy chủ, nó kiểm tra trạng thái đó so với một hồ sơ lịch sử của những gì nó đã dự đoán tại địa phương cho khung hình đó. Khi có sự khác biệt giữa những gì client dự đoán và những gì máy chủ thực sự đã làm, đây là một dự đoán sai. Các dự đoán sai có thể xảy ra vì một số lý do, bao gồm sự thay đổi trong độ trễ mạng, các người chơi khác hành động theo những cách mà client không dự đoán, trò chơi chạy một số logic hoàn toàn trên máy chủ, v.v.
Nếu trạng thái chính danh khác với trạng thái được client dự đoán, client phải quay lại và tái mô phỏng.
Quay lại và tái mô phỏng
Khi một client phát hiện một dự đoán sai, nó phải đặt lại về trạng thái chính danh của máy chủ và sau đó tái mô phỏng để nhảy lại khung hình dự đoán của nó. Dựa trên độ trễ mạng, client cố gắng mô phỏng đủ xa phía trước so với trạng thái máy chủ chính danh cuối cùng để các đầu vào của nó đến máy chủ ở khung hình mong muốn.
Tóm lại, client:
- Nhận trạng thái chính danh từ máy chủ và so sánh nó với trạng thái dự đoán của chính nó.
- Nếu dự đoán của client là sai:
- Client quay lại trạng thái chính danh cuối cùng đã nhận từ máy chủ.
- Client tái mô phỏng từ trạng thái chính danh đến trạng thái dự đoán của mình, tái áp dụng bất kỳ đầu vào địa phương nào.
Triển khai
Quyền sở hữu mạng và dự đoán
Trong mô hình quyền kiểm soát của máy chủ, bạn có thể giữ các đối tượng gameplay cốt lõi thuộc sở hữu của máy chủ mà không phải chịu chi phí độ trễ đầu vào thường liên quan đến quyền sở hữu máy chủ. Những thứ như xe, nhân vật người chơi hoặc các đối tượng quan trọng khác trong gameplay có thể vẫn thuộc sở hữu của máy chủ, ngay cả khi tương tác với những người chơi khác.
Mặc định, Roblox sẽ tự động dự đoán các thuộc tính với quyền truy cập mô phỏng gần người chơi địa phương Character, nhưng nếu bạn muốn kiểm soát chi tiết hơn, bạn có thể ép buộc dự đoán của một instance bật hoặc tắt bằng RunService:SetPredictionMode().
Đồng bộ mô phỏng
Trong mô hình quyền kiểm soát của máy chủ, client và máy chủ must đều phải chạy mô phỏng cốt lõi, và mô phỏng của client cần có thể quay lại và tái mô phỏng khi xảy ra dự đoán sai. Để kích hoạt điều này, hãy viết logic cốt lõi của bạn bên trong các hàm được liên kết thông qua RunService:BindToSimulation() trong một ModuleScript mà được khởi tạo trên cả client và máy chủ.

Trong một lần tái mô phỏng, Roblox sẽ chạy lại các hàm đã được liên kết với mô phỏng thông qua BindToSimulation(). Xử lý đầu vào người chơi, tương tác với các đối tượng vật lý đồng bộ và cập nhật trạng thái cốt lõi của trò chơi nên được thực hiện trong các hàm đã được liên kết đó.
ModuleScript có tên Simulation trong ReplicatedStorage:
Simulation
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Simulation = {}
Simulation.Initialize = function()
RunService:BindToSimulation(function(deltaTime)
-- Đọc đầu vào của người chơi
-- Cập nhật trạng thái trò chơi
end)
end
return Simulation
Đồng bộ trạng thái với thuộc tính
Roblox tự động đồng bộ tất cả các thuộc tính với quyền truy cập mô phỏng trên các instance được dự đoán. Đối với dữ liệu tùy chỉnh, thuộc tính là cách chính để đồng bộ hóa các instance được đánh dấu là dự đoán; trên các instance như vậy, bất kỳ sự không khớp nào trong giá trị thuộc tính giữa nguồn thông tin chính xác của máy chủ và dự đoán của client sẽ kích hoạt một quay lại và tái mô phỏng đầy đủ.
Giới hạn thuộc tính
Để được nhân bản, thuộc tính phải đáp ứng tất cả các tiêu chí sau:
- Nó nằm trong số 64 thuộc tính đầu tiên trên Instance của nó.
- Tên của nó chứa tối đa 50 ký tự.
- Nếu một thuộc tính kiểu chuỗi, giá trị của nó chứa tối đa 50 ký tự.
Quyền truy cập mô phỏng
Nhiều thuộc tính và phương thức trong tài liệu API động cơ bao gồm nhãn Quyền Truy Cập Mô Phỏng, ví dụ BasePart.CFrame. Các thuộc tính với nhãn này sẽ được dự đoán bởi hệ thống quyền kiểm soát của máy chủ. Ngoài ra, chỉ các thuộc tính và phương thức có nhãn này mới có thể được truy cập bên trong các hàm được liên kết với RunService:BindToSimulation().
Hành động đầu vào
Trong một trò chơi có quyền kiểm soát của máy chủ, cách chính để một client ảnh hưởng đến trạng thái của trò chơi là thông qua Hệ Thống Hành Động Đầu Vào. Các đầu vào này được gửi đến máy chủ và được phát lại trong quá trình tái mô phỏng trên client. Kết quả là, InputActions nên được sử dụng cho tất cả các đầu vào ảnh hưởng đến mô phỏng cốt lõi và chúng nên được kiểm tra độ hợp lệ trước khi được xử lý.
Lưu ý rằng InputContexts phải là con cháu của một Player để động cơ biết được ai sở hữu InputContext. Một cách tiếp cận là thêm InputContexts của bạn vào một thư mục dưới ReplicatedStorage và sử dụng một Script dưới ServerScriptService để sao chép InputContexts cho mỗi người chơi:

InputSetup
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
Sử dụng mẫu này, bạn có thể đọc InputActions cho tất cả các người chơi trong RunService:BindToSimulation() trên cả client và server để nhận cùng dữ liệu cho một khung hình nhất định và ghi lại đầu vào của khung hình trước đó trong một thuộc tính, ví dụ để kích hoạt một nhân vật chạy khi một hành động đầu vào RunAction được kích hoạt.
Lưu ý rằng nếu bạn cần ghi dữ liệu tùy chỉnh vào Hệ Thống Hành Động Đầu Vào, bạn nên sử dụng một hàm được kết nối với RunService.RenderStepped hoặc RunService:BindToRenderStep(), không phải RunService:BindToSimulation(). Việc kích hoạt một InputAction trong BindToSimulation() có thể gây ra kết quả không chính xác cho các client trong quá trình tái mô phỏng.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
RunService:BindToRenderStep("CameraInput", Enum.RenderPriority.Last.Value, function()
-- Gửi vector hướng đi của camera từ client đến máy chủ thông qua một InputAction
local cameraForwardInputAction = Players.LocalPlayer.PlayContext.CameraForward
local cameraForwardVector = Workspace.CurrentCamera.CFrame.LookVector
cameraForwardInputAction:Fire(cameraForwardVector)
end)
Sự kiện từ xa
Các sự kiện từ xa vẫn có thể được sử dụng trong mô hình quyền kiểm soát của máy chủ để tạo điều kiện cho việc truyền thông tin đơn lẻ giữa client và server. Ví dụ, các máy chủ có thể sử dụng các sự kiện từ xa để phát dữ liệu về việc người chơi ghi điểm hoặc nhặt đồ vật, và các client có thể sử dụng các sự kiện từ xa như một API thay thế để gửi đầu vào đến máy chủ, chẳng hạn như cho các lần nhấn nút hoặc chạm vào các đối tượng trong thế giới 3D.
Hoạt ảnh, âm thanh và hiệu ứng
Các hiệu ứng phía client như hoạt ảnh và âm thanh phải được viết với nhận thức rằng mô phỏng client chỉ là một dự đoán của trạng thái chính danh của máy chủ. BindToSimulation() hạn chế các thuộc tính và phương thức có thể được gọi từ bên trong các hàm đã được liên kết để giúp hướng dẫn bạn viết chỉ cho trạng thái mô phỏng đã được đồng bộ hóa. Việc hiển thị kết quả của mô phỏng này, kích hoạt các hiệu ứng và âm thanh, v.v. nên được thực hiện trong một hàm riêng kết nối với RenderStepped mà đọc kết quả của mô phỏng và kích hoạt các hiệu ứng mong muốn.
Hướng dẫn thêm xung quanh việc hiển thị một mô phỏng được dự đoán được phủ sóng trong hướng dẫn các kỹ thuật nâng cao.
Dự án mẫu
Ngoài tài liệu này, các mẫu sau có thể giúp bạn bắt đầu:


