Terrible Latency With Weapons

My game has swords, Pretty neat right?
Its a very original idea, IK

However, there seems to be an issue with latency.
Here is the situation:

  1. 2 Players (Both have around equal ping)
  2. One is chasing the other, the other is running
  3. On the chaser’s screen he is kinda close, but not close enough to hit the player
    a. But he attacks anyways
  4. However on the runner’s screen the chaser is definitely not close enough to hit him.
  5. But damage is still calculated?

Architecture:

Client:
image

  • Located in Replicated Storage
  • Required by Weapons on client

Server:
image

  • Located in ServerStorage
  • When the weapon is placed in the player, the server script requires the module, and the module places SDMG in (Plus other unimportant stuff)

Code:

Only the important stuff actually…

SDMG
Slash.OnServerEvent:Connect(function(plar, hit, InSlash)
	local char = hit.Parent --this is the character of the enemy (Supposedly)
	local plr = game.Players:GetPlayerFromCharacter(char); --this is the plr of the enemy (Supposedly)
	
	local CHP = hit:FindFirstChild('HP') or hit.Parent:FindFirstChild('HP') --if this is a wall then its HP
	
	if plr and plr ~= player and AttackDebounce then --if we have a plr that isnt ourselves
		if plr.Team ~= player.Team then --not same team lol
			AttackDebounce = false
			
			local hrp1 = char:WaitForChild('HumanoidRootPart'); --the enemies root part
			local char2 = player.Character; --our character
			
			if not char2 then return end --if we dont exist then, no...
			local hrp2 = char2:WaitForChild('HumanoidRootPart'); --our root part
			
			if (hrp2.Position - hrp1.Position).magnitude > DamDist then print('over distance') return end -- 10 is the max distance in studs
			if InSlash then --if we are attacking
				local hum = char:WaitForChild('Humanoid')
				
				if hum.Health > 0 then
					if (hum.Health - PlayerDamage.Value) <= 0 then
					--	print('killed', '(117)')
						UpdateEvent:Fire('addStat', player, 'Kills', plr)
					end
				end
				
				hum:TakeDamage(PlayerDamage.Value)
			else --if they just touched our blade
				local hum = char:WaitForChild('Humanoid')
				local dmg = ((PlayerDamage.Value/100) * 35)
				
				if hum.Health > 0 then
					if (hum.Health - dmg) <= 0 then
						UpdateEvent:Fire('addStat', player, 'Kills', plr)
					--	print('killed', '(130)')
					end
				end
				
				hum:TakeDamage(dmg)
			end
			
			wait(CDtime.Value)
			
			AttackDebounce = true
		end
	elseif CHP and AttackDebounce and InSlash then --wall damage
		
			AttackDebounce = false
			CHP.Value = CHP.Value - WallDamage.Value
			
			wait(CDtime.Value)
			
			AttackDebounce = true
	end
end)
ClientWeapon
function client:startup(tool)
	local CDtime = tool:WaitForChild('Default'):WaitForChild('CoolDown');
	local Animations = tool:WaitForChild('Animations')
	local Slash = tool:WaitForChild('Slash')
	local Sword = tool
	local Remote = tool:WaitForChild('Player')
	local plr = game.Players.LocalPlayer
		
	local Debounce = true
	local SlashDebounce = true
	local slashing = false
	
	for _, p in pairs(tool:GetChildren()) do
		if p:IsA('BasePart') or p:IsA('MeshPart') or p:IsA('UnionOperation') then
			p.Touched:Connect(function(hit)
				if (hit.Position - p.Position).Magnitude < 10 then
					Slash:FireServer(hit, slashing)
				end
			end)
		end
	end
	
	function anim()
		local char = plr.Character or plr.CharacterAdded:Wait()
		local anim = Animations:GetChildren()[math.random(1, #Animations:GetChildren())]
		local slashAnim = char:WaitForChild('Humanoid'):LoadAnimation(anim)
		
		slashAnim:Play()
	end
	
	Sword.Activated:Connect(function() 
		if not Debounce then return end
		Debounce = false;
		local success, message = pcall(anim)

		slashing = true
		local ct = wait(CDtime.Value);
		slashing = false

		Debounce = true;
	end)
	
	while wait(1) do
		Remote:FireServer()
	end
end

Attempts:
So how did I attempt fix this crap?

Originally the code was set up to only check distance when the client sent that the player was slashing (Or clicking to attack their weapon) and then the the distance was checked on the server. I tried lowering the distance acceptable but that didn’t change much.

From there I made the scripts like it is now, any time the client detects that the something has touched the sword it sends that through the remote event and then the server double checks the distance and there we are. However, now that I think about it, and as the evidence points out, It didnt change anything.

So, how do I fix this?

From the clients POV, the engine makes every other character lag behind so that it can interpolate their movements smoothly. You can try broadcasting the CFrame of each character and replicating it yourself, but this will result in some jitteryness. I think you should just let this go…

The most fair thing you can do is let the players slash whenever they want, and have the server do the distance check to determine if it landed. You can’t really fix the latency, only make it fair (equally bad) for all participants. The only time things will feel correct is when both players have been standing still for at least as long as the round-trip latency of the player with the worst connection. Only then do both clients have a consistent view.

You could make players move really slowly, that would improve hit accuracy proportionately. Melee battles with really slow-moving, heavily armoured knights with slow weapon swing speeds would definitely feel like hits were registering more accurately than if you try to make some high-speed parkour ninja sword battle.