Validating a client-sided raycast

How should I validate whether a client-sided raycast hits the target? I want to make sure that if an exploiter tries to fabricate a hit, the server is able to discern whether it is a valid hit or not. What data should I send to the server?

For reference, my current client-sided raycast is below:

local function CastBolt(X, Y, Length)
	--Server validation data
	local Server = {} --What should I send to the server?
	
	--Cast the ray
	local MouseRay = Camera:ScreenPointToRay(X, Y)
	local Params = RaycastParams.new()
	Params.FilterDescendantsInstances = {Tool.Parent}
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	local Result = game.Workspace:Raycast(MouseRay.Origin, MouseRay.Direction*Length, Params)
	
	--Get EndPos based on whether the ray hits
	local StartPos = Character:FindFirstChild("HumanoidRootPart").Position
	local EndPos
	
	if Result then
		EndPos = Result.Position
	else
		EndPos = MouseRay.Origin + MouseRay.Direction*Length
	end
	
	--Draw lightning bolt
	coroutine.wrap(function()
		local Bolt = BoltCreator:Draw(StartPos, EndPos, 10)
		Bolt.Parent = game.Workspace:FindFirstChild("Projectiles")

		for _,Obj in pairs(Bolt:GetChildren()) do
			if Obj:IsA("BasePart") then
				local Effect = Tween:Create(Obj, TweenInfo.new(0.1, Enum.EasingStyle.Sine), {Transparency=1})
				Effect:Play()
				Effect:Destroy()				
			end
		end			
	end)()

	coroutine.wrap(function()
		local Bolt = BoltCreator:Draw(StartPos, EndPos, 10)
		Bolt.Parent = game.Workspace:FindFirstChild("Projectiles")
		
		for _,Obj in pairs(Bolt:GetChildren()) do
			if Obj:IsA("BasePart") then
				local Effect = Tween:Create(Obj, TweenInfo.new(0.1, Enum.EasingStyle.Sine), {Transparency=1})
				Effect:Play()
				Effect:Destroy()				
			end
		end					
	end)()
	
	CreateBoltEvent:FireServer()
end

It is impossible for the server to validate whether a client sided raycast is legit because the parameters that would be told to the server are initially coming from the client.

A basic check you can do is that you send the ray on the client, check what the client hit, then verify if the player could’ve hit that part when they were in the position when they shot the ray, also allowing for some latency to show.

1 Like

So, am I just writing the ray on the client and the server and checking if they hit the same value?

You can 100% validate this information. There are many implemtations you can use for the approach which each have their own pros and cons. Here’s my suggestion for high precision, at larger computation cost:

Send the following to the server:

  1. Position the client cast the ray from.
  2. Direction the client cast the ray.
  3. Distance the client said the target was.
  4. Target the client said the ray hit.

On the server you’d preform the following checks (I would re-order these from lowest cost to highest cost, but will explain them in logical order):

  • Can the server cast a ray from the position the client provided, in the direction the client provided, the distance the cleint said?
  • Is the client close enough to the point they said they were?
  • Can the server cast a ray from the client’s current position, to the position they said they were at?
  • Is the target close to the location the client said it was at (this is the position we just ray cast to)?
  • Can the server cast a ray from the target’s current position to the location the client said it was at?

Please note “close” is variable. Larger range means more acceptance of latency, but higher potential for exploting the server’s good will. Too low and most calls get rejected due to latency.

The idea behind this psudocode is that we need to check if the raycast itself was valid, then check if the player and target were reasonably in the places they said. You can’t trust simply recasting the ray on the server because things may be moved by the time the message reaches the server.

You can change this implementation to be more preformant with lower fideledy checks too (cut out 2 raycasts by just doing range checks for example) but again doing so opens potential for a client to lie and say they are through a thin wall.

You’ll never truely be able to account for 100% of scenarios, but I hope this one idea gives you ideas on how you can implement a solution that works for you.

7 Likes

I’ve seen some very useful replies here. If the delay between firing a RemoteEvent to RayCast on the server is unacceptable, go for them.

In general I’d suggest to only do visual things on the client in something like this. For example, if you make a laser pointer for your gun, if an exploiter decides to mess with it no one will care.

Thank you for the elaborate reply! I will try to utilize this and update you with the results.