Best way to replicate gun shots?

I am currently working on my game and I have noticed a huge problem

The way the shots replicate in my game goes like this

– local script gets clients camera look vector
– local script fires a shoot remote event to the server
– server script raycasts from camera lookvector
– if the raycast hit a player, do damage.

This works but remote event delay is really pissing me off. If there is a certain amount of people in the game at once the remote events will start to delay and shooting will be annoying as it will be about a half of a second off

If I do it from the client though, It would leave my game extremely vulnerable to exploiters :frowning:

So what do I do?

1 Like

Definitely keep raycasting on the server. How computationally expensive is your raycasting function? A simple ray between two points shouldn’t cause much lag at all, there might be something else you’re doing that’s making the function “heavy.” Are you creating visuals on the server by any chance?

If you really want accuracy and for it to look good too, do this…

  1. have the server keep a list of player positions going back a few seconds
  2. when you fire the gun, go ahead and do the effects on the client (like bullets fired or line drawn etc…)
    2.5) also fire an event to the server with the server time that you fired the gun
  3. server can look in its history, and see where everyone was when the shot took place, and do
    raycasting to see if it was a legit hit
  4. server performs kill and alternatively can let shooter client know it was a kill
1 Like

This is more a network problem than a performance problem.

To OP, you could try simulating the shot on the client for visual feedback and on the server for the actual damage. This has a chance of desync issues where a hit registers on the client but not on the server, but I think it’s a worthy trade off.

The ONLY thing being pased through the remote event is the cameras lookvector. Here’s the code:

Client

function CheckHit()
	spawn(function()

		local Hit, Kill = nil, nil

		local Hit, Kill = game.ReplicatedStorage.RemoteEvents.CheckHit:InvokeServer(game.Workspace.CurrentCamera.CFrame.LookVector.Unit * 100000)

		if Hit ~= nil then

			Player.PlayerGui.Game.Sounds.Hit:Play()

			Crosshair.Image = "http://www.roblox.com/asset/?id=10090085956"

			wait(.25)

			Crosshair.Image = "http://www.roblox.com/asset/?id=409468479"
		end

		if Kill == true then
			game.ReplicatedStorage.RemoteEvents.Kill:Fire(Hit)
		end
	end)
end

Server

game.ReplicatedStorage.RemoteEvents.CheckHit.OnServerInvoke = function(Player,Direction)
	
	local Gun = Player.Character:FindFirstChildWhichIsA("Tool").Name

	local Parameters = RaycastParams.new()

	local ActiveFilter = {}

	table.insert(ActiveFilter,Filter.GetFilter())

	table.insert(ActiveFilter,Player.Character)

	Parameters.FilterDescendantsInstances = ActiveFilter

	Parameters.FilterType = Enum.RaycastFilterType.Blacklist

	local Result = workspace:Raycast(Player.Character.Head.Position, Direction, Parameters)

	local Target = Result.Instance

	if Player.PlayerVariables.InSafeZone.Value == true then return end

	if Target then

		local Model = Target:FindFirstAncestorWhichIsA("Model")

		if not Target:FindFirstAncestorWhichIsA("Tool") then

			if Model then

				if Model:FindFirstChild("Humanoid") and not Model.Name:match("DeathEffect") and Model.Name ~= "Skeleton" then

					if Model.Humanoid.Health ~= 0 then

						local Killed = false

						local CalculatedDamage =  (GunDamage[Gun] * LimbDamage[Target.Name])

						Target.Parent:FindFirstChild("Humanoid").Health -= CalculatedDamage

						if Target.Parent:FindFirstChild("Humanoid").Health == 0 then

							Player.PlayerVariables.Stats.Current.Kills.Value += 1

							Killed = true

							Player.PlayerVariables.Stats.Total.Bottlecaps.Value += 10

							game.ReplicatedStorage.RemoteEvents.GiveCurrency:FireClient(Player,10,false)
						end

						return Target.Parent, Killed
					end
				end
			end
		end
	end
end

Hm, odd. You could try compressing your CFrame parameter and decompressing it in the server function to minimize the amount of data sent on each remote. Something like this:

It might work.

I believe this was the solution. I initially raycasted from the clients camera forward to simulate the actual clients view of the shot and sent the raycasts result to the server. I then made another raycast from the server that went from the clients head to the result of the client. If the server raycasts result and the Client raycasts result were the same, then It’s synced and I don’t believe this can be exploited.