Inconsistent frames between sever and client

Hello forum! I am having issues while scripting my volleyball game. In my game, there is a server ball and a client ball. The server uses Heartbeat to move the ball, while the client uses Renderstepped. The problem is that the server and the client have different deltatimes, meaning that the frames of the lerp are not synchronized. This may not seem like a big issue, but there are instances where the client ball goes over the net but the server ball hits the net and vice versa. Currently, I am using a raycast to detect if the net is in front of the ball, but due to frame inconsistencies, this method isn’t very reliable.
I have thought about making a part that follows the path the ball will take and detect if the part touches the net, but I’m not sure how I would do that.
Any possible solutions are appreciated! Thank you!

2 Likes

Hi Kilp,

These issues are common, and I believe the most adopted solution is giving authority to either the client, or the server.

There will almost always be a gap in the server heartbeat and client framerate, it’s a lot of factors like hardware, running software, and game optimization.

Therefore, giving one side (usually the server) the authority and right to decide the outcome of any inconsistencies is usually the go to.

You might’ve already experienced this in fighting games, when you see a move that you should’ve landed but didn’t, or landed a move when they were nowhere near you. This is a combination of latency, and server authority.

So in this case, I would just trust the server, and have it replicate the accurate position of the ball to all the clients. It might look a bit funky on the client, but it’ll be more fair and function better that way.

2 Likes

Could we see how you are doing the “movement” both in the server and the client?

Are you moving the ball in the server, but doing the rendering on the client?

I would like to make it as smooth as possible on the client though

Are you incorporating the deltatime into the lerp?

Yes I am (texttextetexttexttetxtetxtetxt)

Im moving it on both the server and the client

1 Like

May I see how you incorporate it?

How does that work exactly – could we see code? (like what @guy56890 said)

Sure

Server script:

con = runs.Heartbeat:Connect(function(dt)

		stillupd = true
		i +=dt/data.increaseval		local suc, err = pcall(function()
			if not data.ball or data.ball:GetAttribute("CanUpdateLerp") == false then  con:Disconnect() end
		end)
		if not suc then
			print(err)

			con:Disconnect()

		end

		if data.ball:GetAttribute("LerpID") ~= lerpid then print("discon");con:Disconnect() return end
		if (data.ball.Position - data.c2.p).Magnitude < 3 then con:Disconnect() return end

		local rayparams = RaycastParams.new()
		rayparams.CollisionGroup = "ServerBall"
		rayparams.IgnoreWater = true
		local rayres = workspace:Raycast(data.ball.Position, data.ball.CFrame.LookVector * 3,rayparams )
		if rayres and rayres.Instance then
			if rayres.Instance.Name == "Net" then

				if not netcon then
					c1 = data.ball.CFrame
					netcon = runs.Heartbeat:Connect(function()
						local succ, err = pcall(function()
							if data.ball == nil or data.ball:GetAttribute("CanUpdateLerp")== false then netcon:Disconnect(); con:Disconnect() return end
						end)
						if not succ then
							netcon:Disconnect()
						end
						if data.ball:GetAttribute("LerpId") ~= lerpid then netcon:Disconnect(); con:Disconnect() return end
					

						if (Vector3.new(data.ball.Position.X, data.ball.Position.Y, 0) - Vector3.new(c2.X, c2.Y, 0) ).Magnitude < 3 then netcon:Disconnect(); con:Disconnect() return end







						data.ball.CFrame = CFrame.new(c1:Lerp(c2, i * 2).X, c1:Lerp(c2, i *2).Y, rayres.Instance.Position.Z - char.HumanoidRootPart.CFrame.LookVector.Z * 2)


					end)
					return
				end


			end

		end
		if not netcon then


			data.ball.CFrame = CFrame.lookAlong(data.c1:Lerp(data.c2, i *5 ).p, data.ball:GetAttribute("LastLV"))


		end


	end)

Client:

con = runs.Heartbeat:Connect(function(dt)

			stillupd = true
			i += dt/increase 

			if ball.Parent.Parent == nil or ball.Parent:GetAttribute("CanUpdateLerp") == false or ball:GetAttribute("LerpID") ~= lerpid then  con:Disconnect(); print(ball.Parent:GetAttribute("CanUpdateLerp")) return end
			if (ball.Position - c2.p).Magnitude < 3 then ball:SetAttribute("LerpID", nil);con:Disconnect(); return end

			local rayparams = RaycastParams.new()
			rayparams.CollisionGroup = "ServerBall"
			rayparams.IgnoreWater = true
			local rayres = workspace:Raycast(ball.Position, ball.CFrame.LookVector * 3,rayparams ) or workspace:Raycast(char.HumanoidRootPart.Position, frontraycast, rayparams)

			if (rayres and rayres.Instance)   then
				if   rayres.Instance.Name == "Net"  then
					
					if not netcon then
						c1 = ball.CFrame
						netcon = runs.RenderStepped:Connect(function()
							if ball.Parent:GetAttribute("CanUpdateLerp")== false then netcon:Disconnect() return end




							if (Vector3.new(ball.Position.X, ball.Position.Y, 0) - Vector3.new(c2.X, c2.Y, 0) ).Magnitude < 3 then netcon:Disconnect() return end






						
							ball.CFrame = CFrame.new(c1:Lerp(c2, i *2).X, c1:Lerp(c2, i*2).Y, rayres.Instance.Position.Z - char.HumanoidRootPart.CFrame.LookVector.Z * 2)


						end)
					end


				end
				return
			end
			if not netcon then


				ball.CFrame = CFrame.lookAlong(c1:Lerp(c2, i * 5).p, lv)


			end


		end)

Also heres a video of the issue:
https://gyazo.com/7ec1769eaef355e80a92e30ec07db609

Could you explain to me what I’m looking at here?

Basically the server ball is detected on the client, the client hits the client ball, then the server ball is sent to the server, the server hits the ball, and finally the server ball is sent to all the other clients and they hit the client ball. Th server ball is the parent to the client ball, buit every client has a different client ball.

So what are all the uh, different colors of balls?

The red one is the server ball and the fully visible one is the client ball

This seems also to maybe be an issue with the raycast. It could be that one frame, they raycast doesn’t hit anything, however the next frame it’ll have skipped over it during the speed, and therefore the raycast wouldve been ineffective.

It could also be that you have the client calculations done on Heartbeat, instead of RenderStepped. This results in the object being moved after all the physics calculations have been done, and perhaps moving it to RenderStepped would provide a more desirable outcome.

I dont think thats the problem because I only just changed it to heartbeat to see if it would fix my problem, which it didnt

I also think thats what the issue is

1 Like

I ended up fixing it by raycasting on the client right before the lerp starts and if nothing is detected on that raycast, the server takes over completely. I also made it so that you cant hit the ball if it is behind you, even if it is in the hitbox. There are still some minor issues, but the main ones are gone for the most part.