I’ll be teaching you how to move player character movement to the server, formally called server authoritative character movement.
Character movement is usually handled on the client so the player has instant control over what they do for a better gameplay experience. This means they can fly, walk through walls, or just walk super fast. But with this concept, the server becomes the single source of truth and handles things. This could be used for a makeshift anti-cheat system for these exploits.
While this is a great concept, be warned that this code is not a proper implementation, so it should never be used in actual games. It is only meant as a simple proof-of-concept. I actually found this out while doing random things with network ownership.
Why this implementation is bad:
- Very hacky (this shouldn’t even work, why does it even work?)
- No visual feedback for the player or rollback, movement appears delayed
- No control or adjustment possible over how this works
- Many other drawbacks
If you want a proper implementation, you’ll have to look elsewhere. Roblox is also currently working on a native implementation with a new script type named AuroraScript. Unofficial implementations are probably going to become obsolete soon.
With that out of the way, we can get to the bad implementation I was talking about. All you need to do is change the network owner of the BaseParts of the player’s character to the server. This includes any body parts, accessories, etc.
Create a script in ServerScriptService and put this code inside:
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
-- Set the network owner to the server
local function setNetworkOwner(part)
if part:IsA("BasePart") then
part:SetNetworkOwner()
end
end
local function onDescendantAdded(descendant)
setNetworkOwner(descendant)
end
local function onCharacterAdded(character)
-- Allow the character to load into the workspace
RunService.PostSimulation:Wait()
-- Existing descendants
for _, descendant in character:GetDescendants() do
task.spawn(onDescendantAdded, descendant)
end
-- New descendants
character.DescendantAdded:Connect(onDescendantAdded)
end
local function onPlayerAdded(player)
-- Existing character
if player.Character then
onCharacterAdded(player.Character)
end
-- New characters
player.CharacterAdded:Connect(onCharacterAdded)
end
-- Existing players
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end
-- New players
Players.PlayerAdded:Connect(onPlayerAdded)
You can try the implementation in this game.