Help ~ Kicking a Ball Causes Latency

Hey…

In my game, Mini Cup, there are around 16 players in game. They are divided into 4 teams of 4 and play soccer against each other in turns. Anyone would assume, a major aspect of a soccer (football) game, would be kicking the ball into the oppositions net. And thats what they do in my game.

However it is hard to play with much precisions because the latency makes it so that the opposing players look far away from the ball when they kick it, when in reality, on their end of the game, on their screen, they are already touching the ball. If you play one match in this game you will see the issue yourself:

So now my issue is how do I fix this?
I’ve been trying for months but have come to no solution.
(I’ve changed velocity types, added a cooldown, set network owner to nil and everything I’ve heard of)

This is the current server script which is parented to the ball and is replicated from ServerStorage to the Workspace as needed.

local Players = game:GetService("Players") -- Get the Players service
local Debris = game:GetService("Debris") -- Get the Debris service

local Ball = script.Parent -- Get the ball object
local KickSound = Ball:WaitForChild("Kick") -- Get the kick sound
local bounce = false -- Set a flag to prevent multiple touches from being registered
local gameModule = require(game.ServerStorage.Modules.GameModule)

Ball.Touched:Connect(function(t) -- Connect to the Touched event on the ball
	if t.Name == "PlayerHitBox" then
	local character = t.Parent -- Get the character that touched the ball
	if not character then return end -- If there is no character, return

	local player = Players:GetPlayerFromCharacter(character) -- Get the player associated with the character
	local humanoid = character:FindFirstChildOfClass("Humanoid") -- Get the humanoid of the character
	local rootPart = character:FindFirstChild("HumanoidRootPart") -- Get the root part of the character
	if not player or not humanoid or humanoid.Health <= 0 or not rootPart then return end -- If any of these are missing or the character is not alive, return

	local ballVelocity = Ball.AssemblyLinearVelocity -- Check if the ball already has a velocity

	if bounce == false and not player:GetAttribute("kicked") then -- If the flag is set and no other touches have been registered
		player:SetAttribute("kicked", true)
		print("Bounce is true")
		bounce = true -- Set the bounce flag to prevent further touches from being registered

		local direction = rootPart.CFrame.LookVector -- Get the direction the character is facing
		if direction.Magnitude < 0.001 then return end -- If the direction is very small, return

		Ball:SetNetworkOwner()
		
	
		local kickForce = humanoid.WalkSpeed -- Get the character's walk speed
		
		Ball.AssemblyLinearVelocity = (direction.Unit * kickForce * 1.95) + Vector3.new(0, kickForce * 1.15, 0) -- Set the velocity of the ball based on the direction and walk speed of the character
		
		KickSound:Play() -- Play the kick sound

		-- Set the attributes for the last and second-to-last player to touch the ball
		if Ball:GetAttribute('LastTouch') ~= nil then
			if Ball:GetAttribute('LastTouch') ~= player.Name then
				local previousTouch = Ball:GetAttribute('LastTouch')
				Ball:SetAttribute('LastTouch', player.Name)
				Ball:SetAttribute('SecondTouch', previousTouch)
			else
				-- Do nothing if the current player is the same as the last player to touch the ball
			end
		else
			Ball:SetAttribute('LastTouch', player.Name)
		end
		print("Sending For KickTimer")
		gameModule.kickTimer(player)
		print("Kicktimer Returned")
		task.wait(0.6) -- Wait for 1/2 second
		print("Debounce is false")
		bounce = false -- Reset the bounce flag to allow for future touches to be registered

		-- Increment the touch count for the player's team in the "Standings" folder
		local teamName = player:GetAttribute('TeamName')
		if game.ServerStorage.GameFolder.Standings:FindFirstChild(teamName) ~= nil and game.ServerStorage.GameFolder.Standings:FindFirstChild(teamName):GetAttribute("Touches") ~= nil then
			game.ServerStorage.GameFolder.Standings:FindFirstChild(teamName):SetAttribute('Touches', game.ServerStorage.GameFolder.Standings:FindFirstChild(teamName):GetAttribute('Touches') + 1)
		end
		end
	end
end)

At this point I think it requires an advance solution which I do not have the knowledge to implement. Any ideas or answers would help greatly!

I am also willing to hire an expert who can solve (or at least greatly remedy) the issue as a one time commission.

1 Like

The issue is that you are using Touched and you are using it on the server.

The way I’d fix it is using raycasting or GetPartsInPart to look for the player character on the client and applying the velocity on the client after using SetNetworkOwner(localPlayer)

that way, there would be little to no latency.

To apply any special value changes, for example player:SetAttribute("kicked",true), that would be communicated to the server via remoteEvent/remoteFunction.

It would probably require you to rearrange your code, but it is not too hard to accomplish.

1 Like

Wouldn’t that cause a “jump” to appear as if the ball is teleporting from one player to another when it switches from one players network to anothers?

The issue you’re describing is a common one in multiplayer games where latency can cause discrepancies between what different players see on their screens. To address this, you can use techniques like client-side prediction and server reconciliation.

Client-side prediction involves predicting the movement of game objects on the client before receiving updates from the server. This can help reduce the perceived latency and make the game feel more responsive. Server reconciliation involves checking the state of game objects on the server and reconciling any discrepancies with the client’s predicted state. This helps ensure that all players are seeing the same game state and can help prevent cheating.

In your specific case, it looks like you are already setting the network owner to nil, which can help reduce perceived latency. However, you may want to consider implementing more advanced techniques like client-side prediction and server reconciliation to further reduce latency and ensure consistent game state across all clients.

If you’re not familiar with these techniques or don’t have the experience to implement them yourself, you may want to consider hiring an experienced game developer to help you. You can find game developers on freelance platforms like Upwork or Fiverr, or through game development communities like the Unity or Unreal Engine communities.

1 Like

Could you plug this follow up into OpenAi since you are already there:

How to make client side prediction in Roblox?

1 Like

Sure lmao…
Client-side prediction is a technique used in games to reduce the effects of network latency and improve the responsiveness of player actions. In Roblox, client-side prediction can be implemented using RemoteFunctions and RemoteEvents.

Here’s a basic example of how you can implement client-side prediction for a player’s movement:

  1. Create a RemoteFunction in a Script located in ServerScriptService. This RemoteFunction will be used to receive the player’s input data from the client and return the predicted result of the movement.
local function onPlayerInput(player, input)
  -- calculate the predicted result of the movement based on the input
  local predictedResult = calculatePredictedResult(input)

  -- return the predicted result to the client
  return predictedResult
end

game.ReplicatedStorage.RemoteFunction.OnServerInvoke = onPlayerInput
  1. In a LocalScript located in StarterPlayerScripts, use UserInputService to capture the player’s input and send it to the server via the RemoteFunction created in step 1.
local function sendPlayerInput(input)
  -- send the input data to the server and receive the predicted result
  local predictedResult = game.ReplicatedStorage.RemoteFunction:InvokeServer(input)

  -- apply the predicted result to the player's character
  applyPredictedResult(predictedResult)
end

game:GetService("UserInputService").InputBegan:Connect(function(input)
  -- capture the player's input and send it to the server
  sendPlayerInput(input)
end)
  1. On the server, calculate the predicted result of the player’s movement based on their input data. This could involve predicting the player’s position based on their velocity and acceleration, or predicting the result of a player’s attack animation based on their input and animation state.
  2. On the client, apply the predicted result to the player’s character. This could involve updating the player’s position, orientation, or animation state.

It’s important to note that client-side prediction can introduce inaccuracies and inconsistencies in the game state. To minimize these issues, it’s recommended to use server-side validation to verify the results of the player’s actions and correct any discrepancies.

1 Like

this issue will never really go away because of clients ping state and just server connections the best way i can think of is to kick the ball from local side to clear that off then fire remote event and kick the ball on the server side or you can refire the event to AllClients that way they kick the ball on their screen the best way to defeat lags is to not use server side since that would take more then just firing all clients that will also make more holes for cheaters but shoot your shot who knows you may get it

– edit
or you can keep the server side script and instead calculate the kick and send the informations to allclients so it moves on thier screen first in the end the cframe is always getting tracked from client or server side

1 Like

I tried this but whenever I sent the remote event from the server script to the local script, it would FireAllClients but never be received by the client and the ball wouldn’t move.

And how would you calculate the predicted result? On a soccer ball midkick per say?

can i see the script it very wierd for it to do so and unexpected if the connection to the remoteevent is done with local script then it should work or you can just loop through players and fire the remote event to each player