I’m unsure whether this is a bug or not but here goes.
I have a ball which when kicked, sets the network owner to the player and applies the force. Occasionally, the ball acts as if the networkowner isn’t the player and acts like this:
https://medal.tv/games/roblox-studio/clips/xBSsR8GxrwfWR/d1337e6zDsqW?invite=cr-MSxOWE0sMjQxNTAxMTEs
As you can tell from the clip, the NetworkOwner IS me, but the physics are still jittery. I’m directly setting the ball’s velocity to move it and do not use any bodymovers etc.
Here’s how it’s normally supposed to work:
https://medal.tv/games/roblox-studio/clips/xBTxfuaS8ZOO4/d1337f0NYYz3?invite=cr-MSxFZW4sMjQxNTAxMTEs
From this clip, the ball reacts accordingly and moves smoothly.
Code which applies the force:
local function ApplyForce(ball: BasePart, limb: BasePart, shottype, camera: CFrame) -- applies all forces (spin, linearvelocity) to the ball
kicked = true
local isjumping = humanoid.Jump
local ballpos = ball.Position
local limbpos = limb.Position
local playerpos = humanoidrootpart.Position
local direction = humanoidrootpart.CFrame.LookVector * Vector3.new(1, 0, 1)
local lookvector = camera.LookVector
local rightvector = humanoidrootpart.CFrame.RightVector
local totalspeed = math.clamp(ball.AssemblyLinearVelocity.Magnitude, 0, 120)
local curveforce = GetCurveForce(ball, limb) -- done before delay to ensure that curve isn't affected by latency
local topspinforce
task.spawn(function()
topspinforce = GetTopSpinForce(ball, ballpos, limb, limbpos, isjumping) -- make sure that directional info are above this because they're delayed.
end)
UpdateGuide(false)
direction += Vector3.new(0, yvector, 0)
if playerpos.Y - ballpos.Y > 1.5 and shottype == "Chest" then return end
if ball:GetAttribute("Owner") ~= player.Name then -- waits until the server confirms the touch and transfers network ownership.
ball.TouchOnClient:FireServer(ticks.Value, limb, ball.CFrame, kickaction) -- remotefunction was too slow, so using remoteevent instead.
local start = tick()
repeat task.wait() until ball:GetAttribute("Owner") == player.Name or tick() - start > 2
if ball:GetAttribute("Owner") ~= player.Name then return end
else
ball.TouchOnClient:FireServer(ticks.Value, limb, ball.CFrame, kickaction)
if shottype == "LDribble" or shottype == "RDribble" then
task.wait(0.1) -- delay for more dribbling opportunities
end
end
if ball:GetAttribute("Owner") ~= player.Name then return end
if player:GetAttribute("Thrower") and player:GetAttribute("HasBall") then
local start = tick()
repeat task.wait() until not player:GetAttribute("HasBall")
end
if shottype == "LDribble" or shottype == "RDribble" then
local isright = shottype == "RDribble"
if sprinting then
direction = humanoid.MoveDirection
end
if lookvector.Y <= -0.8 then
direction *= Vector3.new(1, 0, 1)
direction += Vector3.new(0, 1, 0)
power = humanoid.WalkSpeed + 5
local ObjectSpace = humanoidrootpart.CFrame:PointToObjectSpace(ball.Position)
if ObjectSpace.Z >= 0 then
GetCorrectAnimation("Dribble", isright):Stop()
GetCorrectAnimation("RainbowFlick", isright):Play()
end
else
direction *= Vector3.new(1, 0, 1)
if sprinting then
power = humanoid.WalkSpeed + 8
if (humanoid.MoveDirection:Dot(humanoidrootpart.CFrame.RightVector) > 0.5) then
GetCorrectAnimation("SharpTurn", true):Play()
elseif (humanoid.MoveDirection:Dot(humanoidrootpart.CFrame.RightVector) < -0.5) then
GetCorrectAnimation("SharpTurn", false):Play()
elseif humanoid.MoveDirection:Dot(humanoidrootpart.CFrame.LookVector) < -0.5 then
GetCorrectAnimation("DragBack", isright):Play()
else
GetCorrectAnimation("Dribble", isright):Play()
end
else
power = humanoid.WalkSpeed + 12
GetCorrectAnimation("Dribble", isright):Play()
end
end
local dribbleforce = direction * power
ball.AssemblyLinearVelocity = dribbleforce
elseif shottype == "Dive" then
local direction = CFrame.new(ballpos, playerpos).LookVector
local direction2 = humanoidrootpart.AssemblyLinearVelocity.Unit
local reboundpower = math.max(50, totalspeed / 2)
ball.AssemblyLinearVelocity = -direction * reboundpower
elseif shottype == "Header" then
direction -= Vector3.new(0, 0.1, 0)
if direction.Y <= 0.1 then
direction = Vector3.new(direction.X, 0, direction.Z)
end
ball.AssemblyLinearVelocity = totalspeed * direction
elseif shottype == "Chest" then
direction = humanoidrootpart.CFrame.LookVector
local dribbleforce = direction * (humanoid.WalkSpeed + 5)
ball.AssemblyLinearVelocity = dribbleforce
elseif shottype == "Tackle" then
if linearvelocity.Enabled then
direction = linearvelocity.VectorVelocity.Unit
end
local spin = direction:Cross(Vector3.new(0, 1, 0))
ball.AssemblyLinearVelocity = direction * (power * 0.5)
ball.AssemblyAngularVelocity = -(spin * (power * 0.5) * Vector3.new(1, 0, 1))
elseif shottype == "Throw" then
ball.AssemblyLinearVelocity = direction * (power / 3)
elseif shottype == "RShoot" or shottype == "LShoot" then
ball.AssemblyLinearVelocity = direction * power * 0.7
if not topspinforce then
repeat task.wait() until topspinforce
end
if yvector == 0 then
topspinforce = 0.8 * power
end
ball.AssemblyAngularVelocity = Vector3.new(0, curveforce, 0) + -(rightvector * topspinforce * Vector3.new(1, 0, 1))
end
end
On the server, it sets the ball’s network ownership to the player and you can see from the first clip, it does it successfully.
If this IS a bug, I don’t have permission to post yet and would appreciate if someone would do it for me.
Thanks for reading.