Client side projectiles

What do you want to achieve?

Hello, I’ve been working the entire day on client sided projectiles, since they’re smoother and put less strain on the server.
My main issue is that the current system is very vulnerable to exploiters, and sometimes not accurate.

It works like this:

Server side

local bullet = flameFolder["Gun Bullet"]:Clone()
bullet.Anchored = true
bullet.Position = model.Gun["Tip Attachment"].WorldPosition
bullet.Parent = workspace.Effects
game.Debris:AddItem(bullet, 1.5)
local mouseInfo = game.ReplicatedStorage.Relations.GetMouseInfoRemoteFunction:InvokeClient(Player)
bullet.CFrame = CFrame.new(bullet.Position, mouseInfo.Hit.p)
local DetectionArguments = {}
DetectionArguments.Speed = 200
DetectionArguments.Character = Player.Character
local Arguments = {}
Arguments.Player = Player
Arguments.MouseHit = mouseInfo.Hit
local finalPosition = detection.ClientSideProjectile(bullet, Arguments, DetectionArguments)
repeat wait() until finalPosition if not finalPosition then return end

The detection.ClientSideProjectile function, still on the server:

function detection.ClientSideProjectile(Projectile, Arguments, DetectionArguments)
	local returnValue = nil
	local clientsAnswers = {}
	local projectilePosition = Projectile.Position
	DetectionArguments.Part = Projectile
	local PlayersFiredTo = game.Players:GetPlayers()
	for _,v in pairs(PlayersFiredTo) do
		spawn(function()
			local returningValue
			returningValue = clientFunctions:InvokeClient(v, "ProjectileFire", DetectionArguments)
			table.insert(clientsAnswers, returningValue)
		end)
	end

Then, it compares every position recieved from the clients it reached to determine which one would be the most accurate, but this won’t work if the player is alone on the server, since it would have nothing to compare the position recieved to.
At the end, I use raycast to detect if the part did actually go in a straight line, the way it’s intended:

--//ProjectilePosition refers to the position the projectile first had before sending it to clients
local rayInfo = RaycastParams.new()
rayInfo.FilterType = Enum.RaycastFilterType.Whitelist
rayInfo.FilterDescendantsInstances = {bullet}
local distance = (projectilePosition - returnValue).Magnitude
if not Arguments.Velocity then
	ray = workspace:Raycast(projectilePosition, Projectile.CFrame.lookVector*distance, rayInfo)
else
	ray = workspace:Raycast(projectilePosition, Arguments.Velocity*distance, rayInfo)
end
if ray then
	return returnValue
else
    print("Object not found")
	return nil
end

The raycast at the end does work most of the time, but I don’t know how to make it always successful.