What is the BEST way to prevent client-sided exploits?

I am working on a project that has been in my mind for a while, but I am afraid that after it’s release it will just be plagued by exploiters changing their character properties or inserting fly/flinging scripts. Is there a way to prevent these sort of exploits, or is it just up to the game moderators themselves?

Yes, there are many free server-sided (and client-sided (wouldn’t recommend these)) anti-cheats and you can also make some yourself if you have the knowledge to do so. Some server-sided anticheats can detect these flying and flinging functions to a very good degree.

For flying/flinging I recommend having a script detecting movement distance, if a player moves too far too fast teleport them back to their old position

I’ll keep this as brief as possible:
You need to keep strict sanity checks at client-server communication endpoints. Kind of like a border patrol.

If you have remote events, make sure you validate your numbers.

For example, many shooter games have to create sanity checks for bullets. Exploiters can instantiate bullets in bizarre locations that may appear alright on the client. To combat this many games test if the path of the bullet is possible or if the bullet is too far away on the server.

Would I check the player’s speed or would I check the MoveDirection?

Yes, I have already locked down my remote events so they can only do stuff in certain conditions, example: only being able to reload the character if they have died.

1 Like

Check their position/magnitude

here is a script

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(Player)

	Player.CharacterAdded:Connect(function(Character)

		wait(1)

		local Humanoid = Character:WaitForChild("Humanoid")
		local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
		local firstPos = HumanoidRootPart.CFrame

		while wait(1) do

			if(firstPos.p - HumanoidRootPart.CFrame.p).Magnitude >= Humanoid.WalkSpeed + 15 then
				HumanoidRootPart.CFrame = firstPos
			end

			firstPos = HumanoidRootPart.CFrame

		end

	end)
end)

Many exploiters increase the variable fps rate to simulate increased speed, to combat this you could do the following on the client:

local RS = game:GetService("RunService")
local plr = game.Players.LocalPlayer

RS.Heartbeat:Connect(function()
    if workspace:GetRealPhysicsFPS() > 61 then
        -- plr = exploiter
    end
end)

You should also check to see if the humanoid.WalkSpeed or the humanoid.JumpPower is above normal each heartbeat. I would advise against using RenderStepped for detecting exploits.

1 Like

So using that system, I can detect if they are going faster than their assigned walkspeed, correct?
also, what about flyhacks that aren’t moving at a fast rate? would I use a raycast to detect their distance from the ground instead?

I use a combination of server-sided magnitude checks, like the one that @Azul_Litt posted, as well as boundary checks to make sure a player doesn’t go any higher (or lower) than allowed. If your game has flying vehicles or varying altitudes, boundary checks may not work as well.

Raycasting and seeing how far the player is above the ground seems like the best bet.

1 Like

Sorry if this is a late reply but I just found this post again so I just made a prototype of @detourious’s idea of raycasting.

Here’s the code: (put a script in ServerScriptService and paste this inside, change values if needed)

--//Services
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

--//Controls
local maxHeight = 50

--//Tables
local Connections = {}

--//Functions
Players.PlayerAdded:Connect(function(player: Player)
	player.CharacterAdded:Connect(function(character: Model)
		local HumanoidRootPart: BasePart = character:WaitForChild("HumanoidRootPart")
		
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {character}
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		
		local direction = Vector3.new(0, -maxHeight, 0)
		
		Connections[player] = RunService.Stepped:Connect(function()
			if not character then
				Connections[player]:Disconnect()
				
				return
			end
			
			local raycast = workspace:Raycast(HumanoidRootPart.Position, direction, raycastParams)
			
			if not raycast then
				--//Player is 50 studs in the air
				Connections[player]:Disconnect()
				
				--//Reload player
				player:LoadCharacter()
				
				return
			end
		end)
	end)
	
	player.CharacterRemoving:Connect(function()
		Connections[player]:Disconnect()
	end)
end)

Players.PlayerRemoving:Connect(function(player: Player)
	local connection = Connections[player]
	
	if connection then
		connection:Disconnect()
	end
end)

Place file:
Simple Raycast Anti-Cheat (Server-Sided).rbxl (33.9 KB)

1 Like

Thanks, I already made a working prototype, but I will use this to make it even better.