Help with syncing clients

So I’ve made a knife for my newest game, however specifically for the normal knife I have issues getting the clients to line up. What I do is instead of making the knife function on the server I make something happen on the main players client, then to everyone else’s through a server event.

The is the clients don’t line up that well for the normal knife whereas the beam knife is perfect.

ServerEvent

Events.KnifeWeaponFunction.OnServerEvent:Connect(function(p, p1, p2, p3, p4, p5, p6, p7, p8)
	local Char = p.Character
	Events.KnifeWeaponFunction:FireAllClients("N/A", p2, Char, p4, p5, p6, p7, p8)
end)

ClientEvent

RemoteEvents.KnifeWeaponFunction.OnClientEvent:Connect(function(p1, p2, p3, p4, p5, p6, p7, p8)
       if p3 ~= game.Players.LocalPlayer.Character then
             elseif p2 == "Normal" then
			local KnifeTracker = p4
			local Knife = p3.Knife.Handle:Clone()
			Knife.CanCollide = false
			Knife.Anchored = true
			Knife.CFrame = p3.Knife.Handle.CFrame
			Knife.Name = "tKnife"..KnifeTracker
			Knife.Parent = workspace
			local ThrowLoop = Instance.new("Sound")
			ThrowLoop.Name = "ThrowLoop"
			ThrowLoop.Looped = true
			ThrowLoop.Volume = 0.3
			ThrowLoop.SoundId = "http://www.roblox.com/asset?id="..KnifeModule.Sounds.ThrowLoop[1]
			ThrowLoop.Parent = Knife
			ThrowLoop:Play()
			local Pos, KCP, Distance = p5, p6, p7 
			for i = 1, Distance, 6 do
				Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,i-Distance) * CFrame.fromEulerAnglesXYZ(math.rad(i*15),math.rad(180),math.rad(360))
				wait()
			end
			Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
			local KnifeContents = Knife:GetChildren()
		for k = 1, #KnifeContents do
			if KnifeContents[k]:IsA("Sound") then
				KnifeContents[k]:Destroy()
			end
		end
       end
end)

The Normal knife function, DetectHuman is a function that checks if the knife touches a player before the knife reaches the end of the ray

local Touched = false
		local Knife = Tool.Handle:Clone()
		Knife.CanCollide = false
		Knife.Anchored = true
		Knife.CFrame = Char.Knife.Handle.CFrame
		Knife.Name = "tKnife"..KnifeTracker
		Knife.Parent = workspace
		local ThrowLoop = Instance.new("Sound")
		ThrowLoop.Name = "ThrowLoop"
		ThrowLoop.Looped = true
		ThrowLoop.Volume = 0.3
		ThrowLoop.SoundId = "http://www.roblox.com/asset?id="..Sounds.ThrowLoop[1]
		ThrowLoop.Parent = Knife
		ThrowLoop:Play()
		local NewRay = Ray.new(Knife.CFrame.Position, (MousePos - Knife.CFrame.Position).unit * 1000)
		local Hit, Pos = game.Workspace:FindPartOnRayWithIgnoreList(NewRay, GenerateIgnoreList(Knife))
		local Distance = (Pos - Knife.CFrame.Position).magnitude
		spawn(function()
			local P, K, D = Pos, Knife.CFrame.Position, Distance
			Events.KnifeWeaponFunction:FireServer("N/A", "Normal", "N/A", KnifeTracker, P, K, D)
		end)
		for i = 1, Distance, 6 do
			DetectHuman(Knife, Char)
			Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,i-Distance) * CFrame.fromEulerAnglesXYZ(math.rad(i*15),math.rad(180),math.rad(360))
			wait()
		end
		Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
		local KnifeContents = Knife:GetChildren()
		for k = 1, #KnifeContents do
			if KnifeContents[k]:IsA("Sound") then
				KnifeContents[k]:Destroy()
			end
		end
		if Hit then
			if game.Players:GetPlayerFromCharacter(Hit.Parent) == nil and game.Players:GetPlayerFromCharacter(Hit.Parent.Parent) == nil then
				Knife.Anchored = true
				Events.KnifeWeaponFunction:FireServer("N/A", "WallHit", "N/A", KnifeTracker)
			else
				local char1, char2 = game.Players:GetPlayerFromCharacter(Hit.Parent), game.Players:GetPlayerFromCharacter(Hit.Parent.Parent)
				if char1 ~= nil or char2 ~= nil and char1 ~= Char and char2 ~= Char then
					local GetChar = nil
					if Hit.Parent:FindFirstChild("Humanoid") and not Hit.Parent.Parent:FindFirstChild("Humanoid") then
						GetChar = Hit.Parent
					elseif Hit.Parent.Parent:FindFirstChild("Humanoid") and Hit.Parent:IsA("Accessory") and not Hit.Parent:FindFirstChild("Humanoid") then
						GetChar = Hit.Parent.Parent
					end
					if GetChar ~= nil then
						local GetTorso = GetChar:FindFirstChild("Torso")
						local GetHuman = GetChar:FindFirstChild("Humanoid")
						if GetTorso and GetHuman then
							if Char then
								spawn(function()
									Events.KnifeWeaponFunction:FireServer("N/A", "ThrowHitSound", "N/A", GetTorso)
									Module.PlaySound(Sounds.ThrowHit[1], GetTorso, 0.5)
								end)
								if game.ReplicatedStorage.Content.GameInProgress.Value == true then
									if game.ReplicatedStorage.Content.GameMode.Value == "Wicked Murderer" then
										Events.KnifeDamage:FireServer(GetHuman, 100)
									elseif game.ReplicatedStorage.Content.GameMode.Value == "Freeze Tag"  then
										if GetChar:FindFirstChild("Role") and GetChar.Role.Value == "Bystander" then
											Events.FreezePlayerKnife:FireServer(GetChar)
										end
									else
										-- any other gamemode not specified
										Events.KnifeDamage:FireServer(GetHuman, 100)
									end
								elseif game.ReplicatedStorage.Content.GameInProgress.Value == false then
									Events.KnifeDamage:FireServer(GetHuman, 100)
									-- when a game is not occuring
								end
							end
							Events.KnifeWeaponFunction:FireServer("N/A", "DestroyKnife", "N/A", KnifeTracker)
						end
					end
				end
			end
		end

Not exactly sure what you mean by clients lining up but I think I have an idea.

Does this functionality absolutely need to use a server/client remote event system? Using this system, you cannot guarantee everything will be synced perfectly since each of the clients running the Remote Event code have different devices and different performance capabilities. Some might execute the code faster than others, which could be creating unwanted results for you. The ping of each player could also play a big role here. The time it takes for each player to receive and act on the client event will most definitely vary from player to player.

Personally if I were to tackle this problem I would let the server handle all that code rather than using events, plus it’s more secure as exploiters wouldn’t be able to spam that KnifeWeaponFunction event to their hearts’ content. That’s what I’ve gathered from the few blocks of code you’ve provided. This could, of course, be wrong since I don’t know much about your game, but it’s just an idea.

1 Like

So basically, I’ve handled the whole weapon system on the server prior to doing this, however the server can be extremely slow sometimes and also have a delay when passing the mouse through remote functions, here having 3 way communication makes it smoother since the individual clients get info from the server and try to imitate the client that has the weapon. As for exploits specifically with damage, I have it so the remote events only work if there is a knife in the players character model, so If they wanted to fire the other remotes they can only use it to make someone else throw a weapon at themselves and it won’t kill them. As for clients lining up I mean making it so it’s consistent where things can happen at the exact same time, funny thing is for laser knife, it works well, but when looping the movement of the regular knife flying through the air there’s desync, unless I call the client remote to move the knife on other players screen in the main client, but I assume that would make the movement look laggy since it would just be be spam calling the remote. I mainly use this method to make weapons with similar performance to when filtering enabled wasn’t a thing. This method is extremely good for beams, but for an object looping through the distance of a raycast it’s a bit less reliable.

That’s the downside of using your method unfortunately. The best way to keep everything continuous would be to let the server handle the physics and CFraming rather than doing it on each client all at once, because as I said earlier they will all be processing that remote event at different speeds due to ping and performance differences, leading to desync.

It makes sense why the laser knife wouldn’t have this issue, since it’s simply firing a raycast and each client should be able to handle that particularly quickly and easily. But, as for the regular knife (which flies through the air at a slower speed instead of instantly hitting the destination like the laser knife does), if you make each client handle the CFraming, waits, and other complex physics operations, it’s almost impossible to guarantee that they’ll all process and complete it at the exact same time. This is what is leading to your desync issue.

The only reason I could see the server handling this extremely slowly would be for players with very high ping. For players that live close to the server hosting region and have decent ping, this shouldn’t be much of an issue.

yeah, I understand what you’re saying, but somehow it seems there’s something I could change to make it more even, I notice that when throw a knife in a server with my alt accounts, they seem to get the same result as one another, if ping is high it would probably be glitchy just like how the server spikes for players with high ping. I have an idea and it might work, but I’m just not sure how to go about doing it, but it seems that if I can predict a perfect time to fire it for other players compared to the main I might be able to make the clients run smoothly amongst each other assuming the other players don’t have terrible connections. What I’m saying is across my devices with normal ping, they seem to have the same general delay as eachother, the way I got it to work for laser it making it happen for other players right before the main player, and they share hit detection from the main player, so if there’s just somewhere to make something happen early that might work. a coroutine wouldn’t be much different than the spawn functions I’ve been using, but I need someway to check something before it even happens, it seems pretty complex, I’ve been looking at KAT and it seems that the knives there are client controlled by how smooth they are, I always wondered what fierzaa does bc whatever it is it works great!

I think I may have a solution to your problem.
Instead of using events to replicate your knife, you could use a remote event.
local event = Instance.new(“RemoteEvent”)
event.Name = “KnifeEvent”
event.Parent = game.ReplicatedStorage

On the server side of your script, don’t use events. Use the remote event to send a message to all the clients.
event:FireAllClients(…)

On the client side of your script, use the remote event to receive a message from the server.
event.OnClientEvent:Connect(…)

I do use remoteevents, I take call the remote event to the server, then it goes back to fire every client with the same info as the client with the weapon

Here’s how an example of laser knife

Client1

elseif KnifePower == "Laser Knife" then
		local Touched = false
		spawn(function()
			Events.KnifeWeaponFunction:FireServer("N/A", "CreateLaser", "N/A", KnifeTracker)
		end)
		local Knife = Tool.Handle:Clone()
		Knife.CanCollide = false
		Knife.Anchored = true
		Knife.CFrame = Tool.Handle.CFrame
		Knife.Name = "tKnife"..KnifeTracker
		Knife.Parent = workspace
		local NewRay = Ray.new(Knife.CFrame.Position, (MousePos - Knife.CFrame.Position).unit * 1000)
		local Hit, Pos = workspace:FindPartOnRay(NewRay, Char, false, true)
		local Distance = (Pos - Knife.CFrame.Position).magnitude
		spawn(function()
			Module.PlaySound(Sounds.Laser[1], Char.Torso, 0.5)
			local P, D, K = Pos, Distance, Knife.CFrame.Position
			Events.KnifeWeaponFunction:FireServer("N/A", "Laser", "N/A", KnifeTracker, P, D, K)
		end)
		local LaserPart = Instance.new("Part")
		LaserPart.Name = "Laser"..KnifeTracker
		LaserPart.CanQuery = false
		LaserPart.Anchored = true
		LaserPart.CanCollide = false
		LaserPart.Material = "Neon"
		LaserPart.Color = Color3.new(1, 0, 0)
		LaserPart.TopSurface = Enum.SurfaceType.Smooth
		LaserPart.BottomSurface = Enum.SurfaceType.Smooth
		LaserPart.Size = Vector3.new(0.2, 0.2, Distance)
		LaserPart.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0, 0, (-Distance/2) + 2)
		LaserPart.Parent = workspace
		for i = 10000, Distance do
			Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,i-Distance) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
		end
		Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
		local TweenProperies = {
			Size = Vector3.new(0.02, 0.02, Distance);
			Transparency = 0.5
		}
		local FadeLaser = game.TweenService:Create(LaserPart, TweenInfo.new(0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.In), TweenProperies)
		FadeLaser:Play()
		delay(0.11, function()
			LaserPart:Destroy()
		end)
		if Hit then
			if game.Players:GetPlayerFromCharacter(Hit.Parent) ~= nil or game.Players:GetPlayerFromCharacter(Hit.Parent.Parent) ~= nil then
				local CChar = nil 
				if Hit.Parent:FindFirstChild("Humanoid") and not Hit.Parent.Parent:FindFirstChild("Humanoid") then
					CChar = Hit.Parent
				elseif Hit.Parent:IsA("Accessory") and Hit.Parent.Parent:FindFirstChild("Humanoid") then
					CChar = Hit.Parent.Parent
				end
				if CChar ~= nil and CChar ~= Char then
					local GetTorso = CChar:FindFirstChild("Torso")
					local GetHuman = CChar:FindFirstChild("Humanoid")
					if GetTorso and GetHuman then
						spawn(function()
							Events.KnifeWeaponFunction:FireServer("N/A", "ThrowHitSound", "N/A", GetTorso)
							Module.PlaySound(Sounds.ThrowHit[1], GetTorso, 0.5)
						end)
						if game.ReplicatedStorage.Content.GameInProgress.Value == true then
							if game.ReplicatedStorage.Content.GameMode.Value == "Wicked Murderer" then
								Events.KnifeDamage:FireServer(GetHuman, 100)
							elseif game.ReplicatedStorage.Content.GameMode.Value == "Freeze Tag"  then
								if CChar:FindFirstChild("Role") and CChar.Role.Value == "Bystander" then
									Events.FreezePlayerKnife:FireServer(CChar)
								end
							else
								-- any other gamemode not specified
								Events.KnifeDamage:FireServer(GetHuman, 100)
							end
						elseif game.ReplicatedStorage.Content.GameInProgress.Value == false then
							Events.KnifeDamage:FireServer(GetHuman, 100)
							-- when a game is not occuring
						end
						Events.KnifeWeaponFunction:FireServer("N/A", "DestroyKnife", "N/A", KnifeTracker)
					end
				end
			else
				Events.KnifeWeaponFunction:FireServer("N/A", "SecondaryWallHit", "N/A", KnifeTracker)
			end
		end
        end

Other Clients

RemoteEvents.KnifeWeaponFunction.OnClientEvent:Connect(function(p1, p2, p3, p4, p5, p6, p7, p8)
         if p2 == "Laser" then
			local KnifeTracker = p4
			local Knife = p3.Knife.Handle:Clone()
			Knife.CanCollide = false
			Knife.Anchored = true
			Knife.CFrame = p3.Knife.Handle.CFrame
			Knife.Name = "tKnife"..KnifeTracker
			Knife.Parent = workspace
			KnifeModule.PlaySound(KnifeModule.Sounds.Laser[1], p3.Torso, 0.5)
			local Pos, Distance, KCP = p5, p6, Knife.CFrame.Position
			local LaserPart = Instance.new("Part")
			LaserPart.Name = "Laser"..KnifeTracker
			LaserPart.CanQuery = false
			LaserPart.Anchored = true
			LaserPart.CanCollide = false
			LaserPart.Material = "Neon"
			LaserPart.Color = Color3.new(1, 0, 0)
			LaserPart.TopSurface = Enum.SurfaceType.Smooth
			LaserPart.BottomSurface = Enum.SurfaceType.Smooth
			LaserPart.Size = Vector3.new(0.2, 0.2, Distance)
			LaserPart.CFrame = CFrame.new(Pos, KCP) * CFrame.new(0, 0, (-Distance/2) + 2)
			LaserPart.Parent = workspace
			for i = 10000, Distance do
				Knife.CFrame = CFrame.new(Pos, KCP) * CFrame.new(0,0,i-Distance) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
			end
			Knife.CFrame = CFrame.new(Pos, KCP) * CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
			coroutine.resume(coroutine.create(function()
				local Landed = Instance.new("ObjectValue", Knife)
				Landed.Name = "KnifeLanded"
			end))
			local TweenProperies = {
				Size = Vector3.new(0.02, 0.02, Distance);
				Transparency = 0.5
			}
			local FadeLaser = game.TweenService:Create(LaserPart, TweenInfo.new(0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.In), TweenProperies)
			FadeLaser:Play()
			delay(0.11, function()
				LaserPart:Destroy()
			end)
		elseif p2 == "HyperBeam" then
			local KnifeTracker = p4
			local Knife = p3.Knife.Handle:Clone()
			Knife.CanCollide = false
			Knife.Anchored = true
			Knife.CFrame = p3.Knife.Handle.CFrame
			Knife.Name = "tKnife"..KnifeTracker
			Knife.Parent = workspace
			KnifeModule.PlaySound(KnifeModule.Sounds.HyperBeam[1], p3.Torso, 0.5)
			local Pos, Distance, KCP = p5, p6, Knife.CFrame.Position
			local BeamPart = Instance.new("Part")
			BeamPart.Name = "Beam"..KnifeTracker
			BeamPart.CanQuery = false
			BeamPart.Anchored = true
			BeamPart.CanCollide = false
			BeamPart.Material = "Neon"
			BeamPart.Color = Color3.new(1, 0, 1)
			BeamPart.TopSurface = Enum.SurfaceType.Smooth
			BeamPart.BottomSurface = Enum.SurfaceType.Smooth
			BeamPart.Size = Vector3.new(0.2, 0.2, Distance)
			BeamPart.CFrame = CFrame.new(Pos, KCP) * CFrame.new(0, 0, (-Distance/2) + 2)
			BeamPart.Parent = workspace	
			for i = 10000, Distance do
				Knife.CFrame = CFrame.new(Pos, Knife.CFrame.Position) * CFrame.new(0,0,i-Distance) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
			end
			Knife.CFrame = CFrame.new(Pos, KCP) * CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(math.rad(90),math.rad(180),math.rad(360))
			local Tween1P = {
				Size = Vector3.new(0.15, 0.15, Distance);
				Color = Color3.new(0.333333, 1, 1)
			}
			local Tween2P = {
				Size = Vector3.new(0.1, 0.1, Distance);
				Color = Color3.new(0.333333, 1, 0)
			}
			local Tween3P = {
				Size = Vector3.new(0.05, 0.05, Distance);
				Color = Color3.new(1, 1, 0)
			}
			local Tweens = {Tween1P, Tween2P, Tween3P} 
			for t = 1, 3 do
				if t > 1 then
					wait(0.1)
				end
				local FadeBeam = game.TweenService:Create(BeamPart, TweenInfo.new(0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.In), Tweens[t])
				FadeBeam:Play()
			end
			delay(0.01, function()
				BeamPart:Destroy()
			end)
		end
	end
end)

Server

Events.KnifeDamage.OnServerEvent:Connect(function(player, Human, Damage, GlobalChar)
	if player.Character:FindFirstChild("Knife") or player.Backpack:FindFirstChild("Knife") then
		Human:TakeDamage(Damage)
	else
		if GlobalChar ~= nil and GlobalChar:FindFirstChild("Knife") or game.Players:GetPlayerFromCharacter(GlobalChar).Backpack:FindFirstChild("Knife") then
			Human:TakeDamage(Damage)
		end
	end
end)

Events.KnifeWeaponFunction.OnServerEvent:Connect(function(p, p1, p2, p3, p4, p5, p6, p7, p8)
	local Char = p.Character
	Events.KnifeWeaponFunction:FireAllClients(p1, p2, Char, p4, p5, p6, p7, p8)
end)

Alright, I think what your talking about is client sided rendering so, what I would do is the knife has a variable that store the knifes position on the server but the knife is rendered on all clients using FireAllClients() method.

You can also try something like this: Is replicating some movement with remote event a bad thing?