บทความ › พัฒนาเกม

Roblox Server Authority: กันโกงด้วย client prediction และ rollback

Roblox เปิด Server Authority ให้ทุกเกมแล้ว เซิร์ฟเวอร์เป็นเจ้าของสถานะเกม เครื่องผู้เล่นทายผลล่วงหน้าแล้วย้อนแก้เมื่อทายผิด วิธีเปิด วิธีเขียนโค้ด และข้อจำกัด

กลาง อ่าน 10 นาที 23 ตุลาคม 2569

อ่านจบแล้วจะรู้
  • Server Authority ให้เซิร์ฟเวอร์เป็นเจ้าของสถานะเกม เครื่องผู้เล่นส่งแค่ input จึงกัน speedhack และ flyhack ได้ตั้งแต่โครงสร้าง
  • เครื่องผู้เล่นทายผลล่วงหน้าหลายเฟรม เมื่อเซิร์ฟเวอร์ตอบไม่ตรง จะย้อนกลับไปสถานะจริงแล้วจำลองเฟรมที่เหลือใหม่
  • เปิดด้วย Workspace.AuthorityMode = Server แล้วย้าย logic หลักไป RunService:BindToSimulation และอ่านปุ่มผ่าน InputAction
  • ใช้ time() ไม่ใช่ tick() หรือ os.clock() เพราะต้องย้อนกลับได้ตอนจำลองใหม่

ถ้าเกม Roblox ของคุณให้เครื่องผู้เล่นคุมตัวละครตัวเอง โปรแกรมโกงก็คุมได้เหมือนกัน วิ่งเร็วเกิน บินได้ ทะลุกำแพง เพราะเซิร์ฟเวอร์เชื่อตำแหน่งที่เครื่องผู้เล่นส่งมา Server Authority ที่ Roblox เปิดให้ทุกเกมเมื่อ 9 กรกฎาคม 2026 กลับด้านความเชื่อนี้ เซิร์ฟเวอร์เป็นเจ้าของสถานะ เครื่องผู้เล่นส่งแค่ input และใช้ client prediction ซ่อนความหน่วง บทความนี้อธิบายกลไก วิธีเปิด วิธีเขียนโค้ดให้เข้ากับมัน และกับดัก

ข้อมูลตรวจกับเอกสาร Server Authority บน Creator Hub, ประกาศ Full Release บน DevForum (9 ก.ค. 2026) และประกาศ BindToSimulation (29 เม.ย. 2026) ณ ตุลาคม 2026

ถ้ายังไม่คุ้นว่าโค้ดฝั่งไหนรันที่ไหน อ่าน Roblox EP3 เซิร์ฟเวอร์กับเครื่องผู้เล่น ก่อน บทนี้ต่อยอดจากตรงนั้น

เดิมเป็นยังไง และทำไมโกงได้

แบบเดิมของ Roblox ตัวละครผู้เล่นมี network ownership อยู่ที่เครื่องผู้เล่น เครื่องผู้เล่นคำนวณฟิสิกส์ของตัวเองแล้วส่งผลขึ้นเซิร์ฟเวอร์ ข้อดีคือกดแล้วตัวละครขยับทันที ข้อเสียคือเซิร์ฟเวอร์ต้อง “เชื่อ” ผลนั้น ทางกันโกงแบบเก่าจึงเป็นการเขียนโค้ดตรวจย้อนหลัง เช่น วัดระยะที่ขยับต่อวินาทีแล้วดึงกลับ ซึ่งเขียนยากและพลาดง่าย

Server Authority เปลี่ยนเป็น เซิร์ฟเวอร์คือความจริงเดียว เครื่องผู้เล่นส่งแค่ input (กดเดินหน้า กดกระโดด) เซิร์ฟเวอร์จำลองผลเอง โปรแกรมโกงที่แก้ความเร็วบนเครื่องตัวเองจึงไม่มีผลกับสถานะจริง

เครื่องผู้เล่นทายผลล่วงหน้าตอบสนองทันทีสถานะจริงจากเซิร์ฟเวอร์มาถึงเทียบที่เฟรมเดียวกันไม่ตรง: ย้อนกลับrollbackจำลองเฟรมที่เหลือใหม่จนถึงตอนนี้
เครื่องผู้เล่นทายได้ แต่ความจริงจากเซิร์ฟเวอร์ชนะเสมอ

Prediction, misprediction, rollback

ถ้ารอเซิร์ฟเวอร์ตอบทุกครั้ง ผู้เล่นปิง 100 ms จะรู้สึกหน่วงทุกปุ่ม Roblox จึงให้เครื่องผู้เล่นทายผลล่วงหน้า

  1. Prediction เครื่องผู้เล่นจำลองล่วงหน้าไปหลายเฟรมจากสถานะล่าสุดที่เซิร์ฟเวอร์ยืนยัน ใช้ input ของตัวเอง จำนวนเฟรมขึ้นกับความหน่วง
  2. เทียบผล เมื่อสถานะจริงของเฟรมหนึ่งมาถึง เครื่องผู้เล่นเทียบกับที่ตัวเองทายไว้ของเฟรมเดียวกัน
  3. Misprediction ถ้าไม่ตรง (ปิงเปลี่ยน ผู้เล่นอื่นชน หรือมี logic ที่รันบนเซิร์ฟเวอร์อย่างเดียว)
  4. Rollback + resimulation ย้อนกลับไปใช้สถานะของเซิร์ฟเวอร์ แล้วจำลองเฟรมที่เหลือใหม่จนถึงเฟรมปัจจุบัน โดยใส่ input ของตัวเองซ้ำ

ตัวอย่างในเอกสาร: เครื่องผู้เล่นนำอยู่สองเฟรม ทายเฟรม 3 ผิด ก็จำลองเฟรม 4 ถึง 6 ใหม่ก่อนไปเฟรม 7 ผู้เล่นอาจเห็นตัวละครกระตุกไปตำแหน่งที่ถูก

เครื่องผู้เล่นทายได้ แต่ไม่มีสิทธิ์ตัดสิน ทุกครั้งที่ทายผิด ความจริงจากเซิร์ฟเวอร์ชนะ

ตัวอย่างคำนวณ: rollback กินเวลาเท่าไร

ตัวเลขเฟรมที่นำและเวลาต่อ step ด้านล่างเป็นการประมาณเพื่อให้เห็นภาพ เอกสารไม่ได้ระบุสูตร

  • BindToSimulation ที่ Enum.StepFrequency.Hz60 → หนึ่ง step = 1000 ÷ 60 ≈ 16.7 ms
  • ผู้เล่นปิง (round trip) 100 ms → เครื่องผู้เล่นนำราว 100 ÷ 16.7 ≈ 6 เฟรม
  • สมมติ callback ของเราใช้ 1 ms ต่อ step
  • ทายผิดหนึ่งครั้ง → จำลองใหม่ 6 step × 1 ms = 6 ms ในเฟรมเดียว จากงบ 16.7 ms

ถ้า callback ใช้ 3 ms ก็กลายเป็น 18 ms เกินงบหนึ่งเฟรมไปแล้ว ผู้เล่นเครื่องช้าเห็นเฟรมตก บทเรียนคือ ทุกอย่างใน BindToSimulation จะถูกรันซ้ำ เก็บไว้เฉพาะ logic ที่กำหนดผลเกม ของตกแต่ง (เสียง เอฟเฟกต์ แอนิเมชันประกอบ) ย้ายไป RenderStepped

เปิด Server Authority

ใน Studio เปิด Properties ของ Workspace หมวด Server Authority ตั้ง AuthorityMode = Server ค่าอื่นจะถูกตั้งตามให้เอง

propertyค่าที่ถูกตั้งผลที่ต้องรู้
NextGenerationReplicationEnabledระบบ replicate แบบใหม่
PlayerScriptsUseInputActionSystemEnabledPlayerScripts ใช้ InputAction
SignalBehaviorDeferredevent ไม่ยิงทันที แต่ถูกเลื่อนไปจุดถัดไป
UseFixedSimulationEnabledระบบภายใน (Humanoid, animation, ControllerManager) เดินด้วยความถี่คงที่
StreamingEnabledEnabledส่วนที่ไกลจากผู้เล่นอาจยังไม่โหลดบนเครื่อง

แล้ว Save และ Publish ถ้าเกมเดิมไม่ได้เปิด StreamingEnabled นี่คือการเปลี่ยนที่ใหญ่สุด โค้ดที่ workspace.Map.Door ตรง ๆ บนเครื่องผู้เล่นต้องเปลี่ยนเป็นรอโหลด

UseFixedSimulation คือแนวเดียวกับ game loop แบบ fixed timestep ให้ทุกเครื่องเดินเวลาเท่ากัน ผลคำนวณจึงเทียบกันได้

เขียนโค้ดให้เข้ากับ simulation

หลักจากเอกสาร: logic หลักอยู่ใน ModuleScript ที่เริ่มทำงานทั้งฝั่งเซิร์ฟเวอร์และเครื่องผู้เล่น อ่านปุ่มผ่าน InputAction เก็บค่าของเราเองใน attribute และเขียน attribute เฉพาะใน BindToSimulation

โครงด้านล่างทำปุ่ม Boost ที่กิน stamina (สร้าง InputContext ชื่อ GameplayContext มี InputAction แบบ Bool ชื่อ Boost ไว้ใน ReplicatedStorage)

-- ServerScriptService/GiveInputContext (Script)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

Players.PlayerAdded:Connect(function(player)
	-- InputContext ต้องอยู่ใต้ Player
	ReplicatedStorage.GameplayContext:Clone().Parent = player
end)
-- ReplicatedStorage/Simulation (ModuleScript)
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local MAX_STAMINA = 100
local DRAIN_PER_SEC = 40
local Simulation = {}

function Simulation.Initialize()
	RunService:BindToSimulation(function(dt)
		-- เซิร์ฟเวอร์คำนวณทุกคน เครื่องผู้เล่นทายเฉพาะตัวเอง (ตัวละครที่ถูก predict)
		local list = if RunService:IsServer() then Players:GetPlayers() else { Players.LocalPlayer }
		for _, player in list do
			local character = player.Character
			local context = player:FindFirstChild("GameplayContext")
			if not (character and context) then continue end

			local boosting = context.Boost:GetState() == true
			local stamina = character:GetAttribute("Stamina") or MAX_STAMINA
			if boosting and stamina > 0 then
				stamina = math.max(0, stamina - DRAIN_PER_SEC * dt) -- ตรวจขอบเขตเสมอ
			end
			character:SetAttribute("Stamina", stamina)
		end
	end, Enum.StepFrequency.Hz60)
end

return Simulation

เรียก require(ReplicatedStorage.Simulation).Initialize() จากทั้ง Script ใน ServerScriptService และ LocalScript ใน StarterPlayerScripts เครื่องผู้เล่นได้ผลทันที เซิร์ฟเวอร์ได้ค่าจริง ถ้า attribute สองฝั่งไม่ตรง ระบบ rollback ให้เอง

นี่คือโครงตามรูปแบบในเอกสาร ก่อนใช้จริงเทียบกับ template Racing, Soccer หรือ Laser Tag ที่ Roblox เตรียมไว้

กฎที่ต้องจำ

  • เวลา: ใช้ time() ซึ่งเป็นเวลา simulation แบบ fixed step ย้อนและเล่นซ้ำได้ tick(), os.time(), os.clock() ไม่ sync และไม่ย้อน
  • Input: ทุก input ที่มีผลกับ simulation ใช้ InputAction ห้ามใช้ UserInputService.InputBegan และตรวจค่าก่อนใช้
  • Attribute: replicate เฉพาะ 64 ตัวแรกต่อ instance ชื่อไม่เกิน 50 ตัวอักษร ค่าสตริงไม่เกิน 50 ตัวอักษร
  • API ใน simulation: เรียกได้เฉพาะ property/method ที่ติดป้าย “Simulation Access” task.spawn() จาก callback ยังติดข้อจำกัดนี้ task.defer() หลุดออกไป
  • กล้อง: Camera InputAction เลิก sync แล้ว ใช้ Player:GetCameraState() ที่คืน CFrame, FieldOfView, ViewportSize

ข้อผิดพลาดที่เจอบ่อย

  • เล่นเสียงหรือเอฟเฟกต์ใน BindToSimulation โดน rollback แล้วเล่นซ้ำ ย้ายไป RenderStepped
  • ใช้ os.clock() จับเวลาคูลดาวน์ ตอนจำลองใหม่เวลาไม่ย้อน คูลดาวน์เพี้ยน ใช้ time()
  • คิดว่า RemoteEvent อยู่ใน timeline เดียวกัน ลำดับของ RemoteEvent เทียบกับ property/attribute ไม่รับประกัน และมีรายงานในกระทู้ว่าคลาดราว 40-50 ms ใช้กับเหตุการณ์แยกครั้งเท่านั้น
  • เปิดแล้ว publish เลย ทดสอบด้วย Network Simulation ใน Studio ตั้งปิงสูงและ packet loss ก่อน
  • ลืมข้อจำกัดช่วงแรก ประกาศ Full Release ระบุว่า Animator เล่นพร้อมกันได้ 8 track และการเปลี่ยน Tool ระหว่าง misprediction อาจลบ Weld ของ Tool บนเครื่องผู้เล่น ตรวจรายการ known limitations ล่าสุดก่อนเปิด

แหล่งอ้างอิง

คำถามที่พบบ่อย

เปิด Server Authority แล้วเกมเก่าพังไหม

อาจพัง เพราะการเปิดจะบังคับ StreamingEnabled, SignalBehavior แบบ Deferred และ input แบบ InputAction ไปพร้อมกัน โค้ดที่พึ่ง UserInputService หรือคิดว่าทุกส่วนของแผนที่โหลดอยู่ตลอด ต้องแก้ ลองใน place สำเนาก่อนเสมอ

ยังต้องตรวจ RemoteEvent เองไหม

ต้อง Server Authority คุมเฉพาะสิ่งที่อยู่ใน simulation RemoteEvent สำหรับการกระทำแยกครั้ง เช่น ซื้อของหรือกดปุ่ม ยังต้องตรวจสิทธิ์และค่าที่ส่งมาบนเซิร์ฟเวอร์เหมือนเดิม และไม่ได้เรียงลำดับร่วมกับ timeline ของ simulation

เซิร์ฟเวอร์จะหนักขึ้นไหม

Roblox บอกตรง ๆ ว่าการใช้ทรัพยากรเซิร์ฟเวอร์จะเพิ่มขึ้น มากน้อยขึ้นกับว่าเกมใกล้เพดานอยู่แล้วแค่ไหน วัดใน Studio ก่อนเปิดจริง

คำสั่งและค่าตั้งในบทความทดสอบบนระบบตัวอย่าง ลองบนเครื่องทดสอบก่อนใช้กับระบบจริงเสมอ

อ่านต่อ

#เกมกลาง

Roblox EP5 เกมลาวาขึ้น: สร้างทั้งด่านด้วยโค้ด และทดสอบกฎเกมนอก Studio

เกมที่สองของซีรีส์ เกมเป็นรอบที่ลาวาค่อย ๆ ขึ้น ใครถึงยอดหอคอยได้แต้ม ทั้งด่านสร้างจากโค้ดด้วย Rojo และทดสอบกฎว่ากระโดดถึงทุกขั้นด้วย Lune

อ่าน 13 นาที
#เกมเริ่มต้น

Roblox EP1 เริ่มทำเกมแรก: ติดตั้ง Roblox Studio และสร้างฉากแรกที่กด Play ได้

ทำเกม Roblox ตั้งแต่ศูนย์ ติดตั้ง Roblox Studio รู้จักหน้าจอ Explorer และ Properties วางพื้น ทำบันได กด Play ทดสอบ และเข้าใจว่าเกมหนึ่งเกมประกอบด้วยอะไรบ้าง

อ่าน 6 นาที
#เกมเริ่มต้น

Roblox EP4 ประกอบ obby ให้ครบเกม: จุดเซฟ กระดานด่าน ลาวาทั้งเกม และเส้นชัย

รวมทุกอย่างเป็นเกม obby ที่เล่นได้จริง จุดเซฟด้วย SpawnLocation กระดานคะแนน leaderstats ลาวาทั้งเกมคุมด้วย Script เดียวผ่าน Tag และเส้นชัยที่นับครั้งชนะ พร้อมโค้ดเต็ม

อ่าน 13 นาที