Ball lagging on NetworkOwnership change

  1. What do you want to achieve? Make the networkownership change seamless, moving it to server isn’t an option as that has its own problems that we can’t accept

  2. What is the issue? The ball is having a slight delay until it is being replicated to other clients making it seem like the ball is teleporting

  3. What solutions have you tried so far? Searched through the devforum, asked the MPS (probs one of the most important games in the soccer community) owner about the problem, tried changing RemoteEvents to RemoteFunctions, added cooldowns

It’s important to note that there are soccer games that work on the same principle ( Setting networkownership and adding forces to the ball on the client ), and the problem isn’t present there


local function IsBallInProximity(player : Player, Ball : BasePart, maxProximity : number) : boolean
	local distance = player:DistanceFromCharacter(Ball.Position)
	if player.Character and distance <= maxProximity then
		return true
		return false

Shoot.OnServerEvent:Connect(function(player : Player, Ball : BasePart)
	if Ball:GetAttribute("Ignore") == true then return end
	if IsBallInProximity(player, Ball, SoccerConfiguration.MaxDetectionRange) then
		local oldOwner = Ball:GetNetworkOwner()

		if oldOwner and oldOwner ~= player then
			Ball:SetAttribute("Ignore", true)
			task.delay(0.3, function()
				Ball:SetAttribute("Ignore", false)

		Ball:SetAttribute("LastPlayerHit", player.DisplayName)

		local Sound = Ball:FindFirstChild("HitSound")
		if Sound then


		ApplyForces = function(Ball : BasePart, Power : number, Angle : number, direction : Vector3 | nil, timeToDestroyForce : number | nil)
			local Character = Player.Character
			if not Character then return end


			Ball.AssemblyLinearVelocity =
			Ball.AssemblyAngularVelocity =

			ForceApplierInstance:ApplyForce(Ball, "BallReact", (((direction or Character.HumanoidRootPart.CFrame.lookVector) * Power) + (Character.HumanoidRootPart.CFrame.UpVector * Angle)), timeToDestroyForce or 0)

ApplyForce Module:

local function OnForceAdded(forceObject, desiredForce : Vector3)
	forceObject.Velocity = desiredForce
	forceObject.MaxForce =,math.huge,math.huge)

function ForceApplier:ApplyForce(Object : BasePart, ForceName : string, desiredForceValue : Vector3, destroyTime : number)
	if destroyTime == 0 then
		local desiredForceObject = Object:FindFirstChild(ForceName)
		if not desiredForceObject then return end
		if not self.Forces[Object] then
			self.Forces[Object] = {}

		local oldForce = self.Forces[Object][desiredForceObject]
		if oldForce then
			self:UnApplyForce(Object, desiredForceObject.Name)
		self.Forces[Object][desiredForceObject] = tick() + destroyTime
		OnForceAdded(desiredForceObject, desiredForceValue)

PS. The ball teleports more and more depending on the ball’s velocity, it isn’t so obvious on the video, but when you’re fighting for the ball with another player then it’s really frustrating

Did you find a solution? i have the same issue

Hi! No solution, sadly - but i managed to minimize the lag by adding a better denounce system for the ball, long story short - every time the player kicks the ball, other players can’t kick it for about 0.5 seconds, this minimized the problem to the point where the game became playable, also it introduced a interesting functionality

I hope Roblox will improve on their netcode and NetworkOwnership will change faster

Oh, also - lowering the ball’s gravity kinda helped with the lag

I did the debounce of around 0.5 - 0.6 seconds as well and it did help relative to the lag.

1 Like

And lowered the gravity like you as welll. Its playable now too

1 Like

Hey i think i found a fix that makes it playable at 200+ ping. Make a courtine.wrap or a task.spawn or something, then put a Runservice.Stepped inside, then set the ball network owner to server there. Since its setting it before the physics is registereed it should be faster. In my own testing it seems to fixed alot of thte ping issue. Hope this helps if you see it. I dont think the coroutine is necessary if you put it in its own script but thats what i did.

I’m pretty sure the runservice loops don’t yield the code automatically, thanks for the tip though - i’ll check it out and mark the answer as correct if it works well enough

Did that actually work smoothly ?