To put it bluntly,
In my football/soccer game, if Player A spawns a ball it sets the network ownership to their Player, but if they spawn it and Player B touches it before Player A does, the ball moves for Player B who kicked it but stays in the same place for Player A who spawned it - when Player A then tries to touch it, the ball bugs out and starts rolling/freezing without any of the properties updating.
The bug is rare but still happens.
The ball attributes DO update when the glitch happens.
Here’s an example of the ball once the Player B has kicked the ball Player A spawned without Player A touching it. (Player A’s perspective)
We’ve attempted to remove the setnetworkowner when the ball spawns but that didn’t fix it.
--// Only Fires once there's a new owner.
BallUpdate.OnServerInvoke = function(Player, Ball, GK, Info)
if not (CS:HasTag(Ball, Tag)) then
return false
end
local Character = Player.Character
if Character ~= nil and Character:FindFirstChild("HumanoidRootPart") ~= nil then
if (Character.HumanoidRootPart.Position - Ball.Position).Magnitude > 30 then
return false
end
else
return false
end
if Ball:GetAttribute("Owner") == Player.UserId then --// Prevents exploits
return false
end
if not GK then
if Ball:GetAttribute("ReactSession") ~= Info[2] then
General:FireAllClients("Debug", "Decline", {
["Player"] = Player,
["Ball"] = Ball,
["Reason"] = "DELAY",
["Position"] = Ball.Position
})
return false
end
local ReactSeconds = workspace:GetServerTimeNow() - Info[1]
local Ping, MaxPing = SecondsToPing(ReactSeconds)
if MaxPing then
General:FireAllClients("Debug", "Decline", {
["Player"] = Player,
["Ball"] = Ball,
["Reason"] = "DELAY",
["Position"] = Ball.Position
})
return false
end
if Player.Ping.Value >= Storage.PingLimit then
General:FireAllClients("Debug", "Decline", {
["Player"] = Player,
["Ball"] = Ball,
["Reason"] = "MAX PING",
["Position"] = Ball.Position
})
return false
end
if (Ball:GetAttribute("Decline") == true) then
General:FireAllClients("Debug", "Decline", {
["Player"] = Player,
["Ball"] = Ball,
["Reason"] = "FIRST TOUCH",
["Position"] = Ball.Position
})
return false
end
Ball:SetNetworkOwner(Player)
Ball:SetAttribute("Owner", Player.UserId)
coroutine.wrap(function()
local PlrsNearBall = GetPlayersNearBall(Ball)
if PlrsNearBall > 4 then
Cooldown = 1.35
else
Cooldown = 0.75
end
FirstTouch(false, Ball, Cooldown, Info[3])
General:FireAllClients("Debug", "NewOwner", {
["NewOwner"] = Player,
["Ball"] = Ball,
["Position"] = Ball.Position
})
end)()
return true
end
end
--// Happens whenever the player touches the ball
function ToolManagement:ApplyVelocity(Ball, Forces, Extra, Curve)
local OldPosition = Ball.Position
local Velocity = Forces["Velocity"]
if Ball:GetAttribute("Owner") ~= player.UserId then
Ball.Anchored = true
Ball.CanCollide = false
coroutine.wrap(function()
ReactUpdate:InvokeServer(
Ball,
false,
{
workspace:GetServerTimeNow(),
Ball:GetAttribute("ReactSession"),
Velocity.Magnitude
}
)
Ball.Anchored = false
Ball.CanCollide = true
end)()
end
end
if Velocity.Magnitude > 0 then
Velocity = (Velocity - (Ball.Position - OldPosition)).Unit * Velocity.Magnitude
end
React:ApplyBodyVelocity(Ball, Velocity, Forces["MForce"], Forces["Debris"], player.Name)