Create smooth and immediate kills from client

With my FPS, if you shoot someone, they “die” on your client, and it sends a remote event to server, to allow for double checking. However, even with this client kill, there’s still such a large delay in getting the tag, and said player actually dying.

When I get the kill, their character kind of freezes, however their waist still keeps moving, and then they “die” (on the server) after 1-2 seconds. Worth noting, I am using a ragdoll system. But even without the ragdoll system, the character just freezes, and then dies 1-2 seconds later. This is causing countless problems for UX, as there’s no fast, fluid reaction to your tag, as the player (who on your screen you tagged) is still moving around for a couple of seconds.

So how can I get the death on your screen instant? I understand exploiters could go around getting kills on their screen, but I am still doing server side checks anyway. There’s countless games on Roblox that have instant kills when you get a tag.

if HitCharacter then
		if not HitCharacter:FindFirstChild("Humanoid") then -- HitPart could be part of gun or hitbox
			HitCharacter = HitCharacter.Parent
			if HitCharacter:FindFirstChild("Humanoid") then -- Character found
				CharacterFound = true
			end
		else -- HitPart was most likely a body part
			CharacterFound = true
		end
		
		if CharacterFound then
			BulletCache:ReturnPart(bullet) -- Return bullet
			
			local HitPlayer = Players:GetPlayerFromCharacter(HitCharacter)
			if HitPlayer then -- Make sure HitPlayer exists
				if cast.UserData.Creator == Player then -- Our local player
					local Teams = GameSettings:GetAttribute("Teams")
					if HitPlayer.Team == Player.Team and Teams then return end -- Can't hit same team member
					
					if HitCharacter:GetAttribute("ForceField") then return end -- Can't tag forcefield player
					
					if HitCharacter.Humanoid.Health <= 0 then return end -- Already dead
					
					if Player.Character.Humanoid.Health <= 0 then return end -- Our player is dead
										
					HitCharacter.Humanoid.Health = 0
					
					TweenHitMarker:Play()
					
					-- Tell server
					PlayerHit:FireServer(
						HitPlayer,
						result.Instance,
						result.Position,
						cast.UserData
					)
					
					HitSound:Play()
					
					Ragdoll.Create(HitPlayer, HitCharacter)
				end
			end
			
			return -- Don't create splatter
		end
	end

Is this lag because of your ping? Or is it because of bad script performance. If it’s because of ping, you can ragdoll them on the client as soon as the client says they killed them.

Most games do this actually. In a sense you’ll send over the needed information to the server after verifying on the client and at the same time you’ll (on the client) register the kill. Exploiters will be able to fake deaths but won’t actually get anything out of them.

Besides ping lag, it could be your scripts that are causing huge delays.

These two are what I’m not sure of. You can try quick debug methods, like so:

local a = tick()
-- ragdoll.create() function / whatever function that youre trying to see how fast it runs
print(tick()-a) -- whatever it prints is how long it takes to complete the function

ff019e88ff9bf292fcf22320645d8792

I played it for a bit and I didn’t notice a huge delay, my internet is pretty good though. Nice game.

local A = tick()
					
HitCharacter.Humanoid.Health = 0
					
TweenHitMarker:Play()
					
-- Tell server
PlayerHit:FireServer(
	HitPlayer,
	result.Instance,
	result.Position,
	cast.UserData
)
					
local B = tick()
print("REMOTE EVENT DELAY " .. B - A)
					
HitSound:Play()
					
Ragdoll.Create(HitPlayer, HitCharacter)
					
print("RAGDOLL DELAY " .. tick() - B)
19:59:42.602  REMOTE EVENT DELAY 2.7179718017578e-05  -  Client - Bullet:140
19:59:42.606  RAGDOLL DELAY 0.0045022964477539  -  Client - Bullet:147

So no real delays with the code?

Cheers :slight_smile:

Go to the script that handles this ^ and put the tick() debug method in it. I should’ve been more clear, sorry.

Perhaps your actual server verification takes too long? If you can’t get it any faster you could make the server store the exact tick the kill remote event is received, and after it verifies the kill it will store that kill as legit, and if the target in that remote event kills his killer AFTER the remote event is received by the server, it will look for the first kill and see if it’s valid, and if it is then it will ignore the target’s kill.

It shouldn’t yield tho? I mean here it is anyway

--// Ray hit
local function Hit(player, playerHit, hitInstance, hitPosition, castData)
	local A = tick()
	
	if not castData then return end -- No castData sent
	
	if type(castData) ~= "table" then return end -- castData wasn't a table
	
	if not hitInstance then return end
	
	local B = tick()
	print("DELAY 1 " .. B - A)
	
	--local Distance = playerHit:DistanceFromCharacter(hitPosition)
	--if Distance > MINIMUM_DISTANCE then return end -- Too far away, fake shot
	
	local Bullet = BulletsFired[castData.Id]
--[[
	TODO
		What if bullet doesn't exist yet?
		If close, clients bullet could hit before the server has time to create the bullet,
		thus causing a legit tag, but not
--]]
	--[[
	if not Bullet then -- Bullet does not exist yet, wait for it to possibly exist??
		wait(1)
		
		Bullet = BulletsFired[castData.Id]
		if not Bullet then return end
	end
	]]
	--print(player.Name, "Bullet found successfully", Bullet, castData.Id)
	if player == playerHit then return end -- Creator can't tag themselves	
	
	local Teams = GameSettings:GetAttribute("Teams")
	if player.Team == playerHit.Team and Teams then return end -- Can't tag your own team
	
	
	local C = tick()
	print("DELAY 2 " .. C - B)
	
	
	if not player.Character:FindFirstChildWhichIsA("Humanoid") then return end
	
	if player.Character.Humanoid.Health <= 0 then return end -- Firing player is dead
	
	if not playerHit.Character then return end
	
	
	local D = tick()
	print("DELAY 3 " .. D - C)
	
	
	if playerHit.Character:GetAttribute("ForceField") then return end -- Can't tag forcefield player
	
	local Humanoid = playerHit.Character:FindFirstChildWhichIsA("Humanoid")
	if not Humanoid then return end
	
	
	local E = tick()
	print("DELAY 4 " .. E - D)
	
	
	-- Create all values associated with Creator
	local CreatorValue = Instance.new("ObjectValue")
	CreatorValue.Name = "Creator"
	CreatorValue.Value = player
	
	-- Player's level
	local LevelValue = Instance.new("NumberValue")
	LevelValue.Name = "Level"
	LevelValue.Value = player.PlayerData.Level.Value
	LevelValue.Parent = CreatorValue
	
	-- Gun used
	local EquippedValue = Instance.new("StringValue")
	EquippedValue.Name = "Equipped"
	EquippedValue.Value = player.PlayerData.Equipped.Value
	EquippedValue.Parent = CreatorValue
	
	-- Get distance
	local Distance = (hitPosition - castData.Origin).Magnitude
	
	if Distance >= 200 then -- Longshot
		local Longshot = Instance.new("Folder")
		Longshot.Name = "Longshot"
		Longshot.Parent = CreatorValue
	end
	
	if hitInstance.Name == "Head" then -- Headshot
		local Headshot = Instance.new("Folder")
		Headshot.Name = "Headshot"
		Headshot.Parent = CreatorValue
	end
	
	CreatorValue.Parent = Humanoid
	
	Humanoid.Health = 0 -- Kill player
end
10:52:10.290  DELAY 1 2.3841857910156e-07  -  Server - RaycastManager:45
10:52:10.290  DELAY 2 0.00019264221191406  -  Server - RaycastManager:73
10:52:10.290  DELAY 3 8.7738037109375e-05  -  Server - RaycastManager:84
10:52:10.290  DELAY 4 8.2969665527344e-05  -  Server - RaycastManager:94

So no serious delays?

Yeah no serious delays.

I think the problem is player ping that causes these non-smooth kills. There are ways to do make it seem smoother, like if it’s verified that it’s a kill on the client, it’ll kill them and show that on the client first, then the server will replicate it.

The only downside to that solution is that if your client can create false positives. What I mean by that is if a client kills another player, but the server rejects it (from lag, failed sanity check, etc), the client will see that they killed them but they won’t actually be dead. Not sure how you’d go about fixing that, but odds are it’ll be a rare occurence. If you do implement this, I suggest heavily testing it first in a developer server.

1 Like