Network Ownership
Network Ownership
In a Roblox game, all non-anchored parts are “physically simulated” — they can fall, bounce, float in water, etc. All of these things are handled automatically by Roblox so you don’t have to write your own physics engine.
When a Roblox game runs, Articles/Roblox Client Server Model|several computers/devices are involved
. To divide the work of calculating physics, Roblox automatically assigns parts to either the server or to a client. In general, if a part is near an in-game character, its physics will be calculated by that player’s device; otherwise it will be calculated by the server. In either case, the server or client that calculates a part’s physics is called its owner.
Since physics updates must be sent over the network, there’s a small delay between when the owner makes the physical changes and when the other devices see those changes. Normally this delay isn’t too noticeable, but issues may occur when part ownership changes.
Example 1: Projectiles
Imagine a player is shooting an object at another player.
Problem
As the object travels through the air, it will get far from the shooter and ownership will switch to the server. Once the object gets close enough to the target player, it will switch ownership to him or her. Even with a good network connection, there may be a tiny delay and visible “hop” when the object’s owner switches.
Solution
Manually set ownership of the projectile to the server using BasePart/SetNetworkOwner|SetNetworkOwner()
. This ensures that the owner does not change, preventing visible “hops.”
The following example listens for keyboard input from the player and fires a RemoteEvent
to the server. This both creates the projectile and sets its ownership.
-- LocalScript (client) game:GetService("ContextActionService"):BindAction("Throw", function(actionName, state, object) if state == Enum.UserInputState.Begin then game.ReplicatedStorage.RemoteEvent:FireServer() end end, false, "h")
-- Script (server) game.ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function(player) -- Get the direction player is facing local direction = player.Character.Torso.CFrame.lookVector * 5 -- Create projectile and give it velocity in the direction player is facing local projectile = game.ServerStorage.Projectile:Clone() projectile.Parent = game.Workspace projectile:MakeJoints() projectile.PrimaryPart.CFrame = CFrame.new(player.Character.Torso.Position + direction + Vector3.new(0,2,0)) projectile.PrimaryPart.Velocity = direction * 40 + Vector3.new(0,40,0) -- Set the server as the owner of the projectile projectile.PrimaryPart:SetNetworkOwner(nil) end)
BasePart/SetNetworkOwner|SetNetworkOwner()
and its partner function BasePart/GetNetworkOwner|GetNetworkOwner()
must be set on the server side. Clients are not allowed to change ownership settings in a LocalScript
.
Example 2: Vehicles
Consider a vehicle that has a VehicleSeat
for the driver and a Seat
for a passenger.
Problem
With the default ownership rules, if a player hops into the Seat
(passenger) and then another player jumps into the VehicleSeat
(driver), the passenger gets physical ownership of the vehicle. If a passenger has ownership, the driver will have to wait several network cycles before their input is recognized.
Solution
Manually set network ownership of the car to the driver. When a player sits down, VehicleSeat/Occupant
is set to the humanoid that’s sitting on it, so you can listen for the seat’s Instance/Changed
event. At that point, the parent of the humanoid should be the player’s character (unless it’s an NPC) and this can be used to determine which player gets ownership.
When the driver leaves the seat, the vehicle’s network ownership can be set back to automatic (BasePart/SetNetworkOwnershipAuto|SetNetworkOwnershipAuto()
) since it’s not really important who owns an empty vehicle.
-- Script inside of VehicleSeat local VehicleSeat = script.Parent VehicleSeat.Changed:Connect(function(prop) if prop == "Occupant" then local humanoid = VehicleSeat.Occupant if humanoid then local player = game:GetService("Players"):GetPlayerFromCharacter(humanoid.Parent) if player then VehicleSeat:SetNetworkOwner(player) end else VehicleSeat:SetNetworkOwnershipAuto() end end end)
Example 3: Control Lock
In some Roblox games, a player can control an object that isn’t part of their character model. By default, these objects fall under automatic network ownership rules (they will be owned by the closest player character).
Problem
In some cases, the controlling player may not be the character closest to the object. However, the controlling player should still have network ownership so that they get instant feedback when controlling the object.
Consider a ball on the ground that a player can move by pressing certain keys on the keyboard. To get instant feedback, the ball’s BodyForce
is set in a LocalScript
on key input events. However, if another character is close to the ball, the force will not be applied since that other player will gain physical ownership of the ball.
Solution
Set network ownership of the ball to the player who’s controlling it. This way, the LocalScript
changes to the force will be applied instantly.
-- LocalScript (client) local Sphere = game.Workspace.Part local SphereForce = Sphere.BodyForce local ContextActionService = game:GetService("ContextActionService") local function pushBall(actionName, inputState, inputObject) if inputState == Enum.UserInputState.End then SphereForce.force = Vector3.new(0,0,0) else if actionName == "Left" then SphereForce.force = Vector3.new(-100,0,0) end if actionName == "Right" then SphereForce.force = Vector3.new(100,0,0) end end end ContextActionService:BindAction("Left", pushBall, false, "j") ContextActionService:BindAction("Right", pushBall, false, "l")
-- Script (server) game.Players.PlayerAdded:Connect(function(player) game.Workspace.Part:SetNetworkOwner(player) end)
Anchored Parts
Network ownership cannot be set on anchored parts (anchored parts are not physically simulated). Calling BasePart/SetNetworkOwner|SetNetworkOwner()
on an anchored part will cause a script error, but just because you can’t set its owner doesn’t mean that ownership doesn’t come into play.
Setting ownership on an assembly that has no anchored parts anywhere in the mechanism will set the ownership of every part in the mechanism. If the assembly is then anchored, the ownership will remain on the other assemblies in the mechanism that were not anchored. Unanchoring the assembly will return the previously set ownership.
If an assembly is the only thing in the mechanism, anchoring the assembly resets the network ownership on all of the assembly’s parts to “Auto.” If you unanchor the assembly the ownership will follow the automatic rules and will ignore previously settings of ownership.