PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience

Network Ownership

Network Ownership

Aug 10 2018, 2:20 PM PST 10 min

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)

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.

Tags:
  • physics
  • coding
  • performance