Any tips on minimizing client-server delay?

Greetings developers

I have noticed a very minor delay in my gun framework, in the sense of the time it takes from the player clicking to the target taking damage.
I am aware that it is practically impossible to completely remove the delay, but I am curious to see what I could do to minimize it as much as possible.

Operation path:

  1. ContextActionService based function fires when mb1down
  2. Module handles stuff that’s supposed to be the same on all guns, to minimize lines in each localscript of the gun.
  3. Module fires server event

Same video but in slow-motion:

Local userinput:

local function shoot(actionName, inputState)
	if actionName == "Shoot" then
		if inputState == Enum.UserInputState.Begin then
			if shootType.Value == "Normal" then
				if canFire.Value == true then
					canFire.Value = false
					shootLocalEvent:Fire(script.Name)
					siegeShootTrack:Play()
					mainModule.shoot(script, shootServer, shootHandlerServer, viewmodel, tool, char, mouse.Target, mouse.Hit.Position, muzzleEffect, shootSound, shootType.Value)							
					spawn(function()
						expRecoil(WS.Camera)	
					end)
					task.wait(shootSound.TimeLength-0.3)
					canFire.Value = true
				end	

Module:

function module.shoot(localScript, serverEvent, serverHandlerEvent, viewmodel, tool, char, mouseTarget, mousePosition, muzzleEffect, shootSound, shootType, enableTracer)
	--[[
	local MVvector3 = (mousePosition - muzzleEffect.Position).Magnitude
	local distance = math.abs(MVvector3.X) + math.abs(MVvector3.Y) + math.abs(MVvector3.Z)
	]]
	local mv = localScript:GetAttribute("MuzzleVelocityNum")
	
	local distance = (mousePosition - char.HumanoidRootPart.Position).Magnitude
	local muzzleVelocityWait = 0 --distance / mv
	serverHandlerEvent:FireServer(viewmodel, shootSound, mousePosition, true, shootType, distance, muzzleEffect.Parent.Position)
	spawn(function()
		if enableTracer == true then
			tracerLocal(muzzleEffect, mousePosition, mouseTarget)
		end
		
		muzzleEffect:Emit()
		local shootSoundClone = shootSound:Clone()
		shootSoundClone.Parent = WS.LocalSounds
		shootSoundClone:Play()
	end)
	local targetHum
	local targetChar
	if mouseTarget then
		if mouseTarget.Parent:FindFirstChild("Humanoid") then
			targetChar = mouseTarget.Parent
			targetHum = targetChar:WaitForChild("Humanoid")
		else
			targetHum = nil
		end		
		if targetHum ~= nil and not targetChar:FindFirstChildWhichIsA("ForceField") then
			spawn(function()	
				--task.wait(muzzleVelocityWait)
				serverEvent:FireServer(mouseTarget, targetChar, distance, mouseTarget, mousePosition, shootType)
				studsTween:Cancel()	
				studMeterGuiText.TextTransparency = 0
				--studs to meters formula: studs * 35%
				--distanceStudDisp = math.floor(distance*(35/100))
				spawn(function()
					distanceStudDisp = math.floor(distance)			
					if distanceStudDisp == 1 then metersSpellType = "stud"; else metersSpellType = "studs"; end
					studMeterGuiText.Text = tostring(distanceStudDisp).."  "..metersSpellType					
					task.wait(0.3)
					studsTween:Play()
				end)
			end)
		end
	end
end

Server:

RS.RemoteEvents.GaussCanonServer.OnServerEvent:Connect(function(player, mouseTarget, targetChar, distance, target, mousePos, shootType)	
		if shootType == "Normal" then siegeModeMultiAdd = 0; elseif shootType == "Siege" then siegeModeMultiAdd = 1.2; end
		targetHum = targetChar:FindFirstChild("Humanoid")
		if targetHum.Health == 0 then return end
		if mouseTarget.Name == "Head" then
			multiplier = 2 + siegeModeMultiAdd
		elseif mouseTarget.Name == "Torso" or mouseTarget.Name == "HumanoidRootPart" then
			multiplier = 1.7 + siegeModeMultiAdd
		else
			for _,limb in pairs(limbsTable) do
				if mouseTarget.Name == limb then
					multiplier = 1 + siegeModeMultiAdd
				end
			end
		end
		damage = math.clamp((maxDamage/(math.clamp(distance/maxDmgRange,1,1.5)))*multiplier,minDamage,maxDamage*multiplier)
		targetHum.Health -= damage
		--[[if targetHum.Health <= 0 and targetHum.Parent.Name ~= "Dummy" then
			--[[onKillEvent:FireAllClients(player, targetChar.Name, "GaussCanon", distance, mouseTarget.Name)
			player:WaitForChild("leaderstats").Kills.Value += 1
		end]]
		bloodEvent:Fire(target, targetHum)
end)

If you need to slow the video down for it to be only slightly noticeable, then you shouldn’t worry about it.

You should probably do the raycasting on the server + some lag compensation so an exploiter can’t just kill people by firing a remote event. I personally think it’s a good idea to solidify your game logic early on instead of later when it’s much more difficult. Here’s a pretty interesting article about multiplayer FPS games by Valve that talks about lag compensation;

1 Like

Thanks for the tip. I’ll be spending more time into improving what’s under the hood early on, rather than adding new things that don’t really improve quality.

1 Like