Safe way to detect if player is climbing?

Hey, this is a just a quick question.

So I’m working on a anticheat system for for my game, which works perfectly besides climbing. Currently when a player is climbing, the anti-cheat detects that they’re flying, and kills them. My original idea was to cast a raycast infront of the player, but a player could just be easily facing a wall.

What is an efficient way for the server to check if a player is actually climbing?

1 Like
if Humanoid:GetState() == Enum.HumanoidStateType.Climbing then
	--do stuff
end

This inside a CharacterAdded event will do fine.

Humanoid state replicates from client to server so that would be easily overcome by hackers

I think your original idea of ray casting infront of the player would be ideal for this, as far as I know the only way to tell if a player is climbing is what Forummer suggested, which can be as you pointed out unfortunately spoofed by the client.

If you ray casted infront of the player to check if they were climbing they would have to be facing a wall, i.e. stood with their characters face less than a meter away from a wall every time they wanted to fly, which should be discouraging enough. I think that’s all you can do really, as with most dev created anti-cheat in Roblox there is not a perfect solution

1 Like

Hey, thanks!
I’ve come up with an idea which adds on to the original idea of casting another ray infront of the player. Seen as how my anticheat is server sided, I can use raycast.Instance and check if the instance is a truss, or name is _truss, and also check the distance away. This isn’t a perfect solution but it gets the job done.

Awesome! Yeah good thinking I’m sure that’s as best of a solution as you can get, that means the exploiter would have to stood close to + facing a truss part in order to fly… at which point they may as well just be climbing the truss, I don’t think you could get any better than that :+1:

1 Like

If You just want to detect Climbing you can use this:

game.Players.PlayerAdded:Connect(function(Plr)
	local Char = Plr.CharacterAdded:Connect(function(Char)
		local Hum:Humanoid = Char:WaitForChild("Humanoid",5)
		local HRP:BasePart = Char:WaitForChild("HumanoidRootPart",5)
		if not Hum then
			return
		end
		if not HRP then
			return
		end
		Hum.Climbing:Connect(function(speed) -- check for if the humanoid started climbing or changed climbing speed
			print(Plr.Name .. " Is Currently Climbing at a speed of: " .. speed .. " SPS") -- print climbing speed
			while Hum:GetState() == Enum.HumanoidStateType.Climbing do -- loop that runs while the humanoid is climbing.
				print(Plr.Name .. " Is Currently Climbing.")
				task.wait() -- Slight Wait so you dont crash studio like I accidentally did by forgetting this.
			end
		end)
	end)
end)

And If You want to detect what the Player is climbing on, you can use this:

game.Players.PlayerAdded:Connect(function(Plr)
	local Char = Plr.CharacterAdded:Connect(function(Char)
		local Hum:Humanoid = Char:WaitForChild("Humanoid",5)
		local HRP:BasePart = Char:WaitForChild("HumanoidRootPart",5)
		if not Hum then
			return
		end
		if not HRP then
			return
		end
		Hum.Climbing:Connect(function(speed) -- check for if the humanoid started climbing or changed climbing speed
			print("Climbing at a speed of: " .. speed .. " SPS") -- print climbing speed
			while Hum:GetState() == Enum.HumanoidStateType.Climbing do -- loop that runs while the humanoid is climbing.
				local RCP = RaycastParams.new() -- RaycastParams
				RCP.RespectCanCollide = true -- Most likely cant climb on CanCollide False Objects so ignore them.
				RCP.FilterDescendantsInstances = {Char} -- Ignore the Character
				RCP.FilterType = Enum.RaycastFilterType.Exclude -- Set RCP to Ignore
				RCP.IgnoreWater = true -- Ignore water as well, you probably cant climb on it.
				--local CastPos = HRP:GetPivot().Position + Vector3.new(0,0,.5)*(-HRP:GetPivot().LookVector) -- Move the cast slightly backwards if you want
				--local NewCFrame = CFrame.new(CastPos) * HRP:GetPivot().Rotation -- Turns CastPos into a CFrame.
				--[[
				Quick Note: 
				
				The Part Size I decided On And Recommend Is Vector3.new(.1,2,0).
				 - I set X to .1 In Order To get Thin Ladders And not the Objects Or walls that May Surround them.
				 - I set Y to 2 Because that is the Size of the HumanoidRootPart and also A good Size That can Get The Ladder the Player is Climbing On Without Getting Floors Or Ceilings Jutting Out Above And Below
				 - I set Z to 0 In Order To make sure the Blockcast doesn't start on the other side of the ladder.
				--]]
				local Cast = workspace:Blockcast(HRP:GetPivot(),Vector3.new(.1,2,0),HRP:GetPivot().LookVector*2,RCP) -- Do The Blockcast
				if Cast then -- Make Sure Cast Exists. You could also do "if not Cast then continue end"
					local ClimbingObject = Cast.Instance -- Object You Are Probably Climbing on. Curved or Jutting Out Objects Around the Ladder May Interfere.
					print("Currently Climbing On: " , ClimbingObject) -- Print the Object.
				end
				task.wait() -- Slight Wait so you don't crash studio like I accidentally did by forgetting this.
			end
		end)
	end)
end)

Also, Sorry Im Late, And ye I know this is solved I just thought this might be a bit more help. Also, Raycasts Are probably not the best option for this because they may pass through a ladder or object a player is climbing on, which is why I used a Blockcast in order to hopefully have a better shot at hitting the ladder.

Edit: If this doesn’t work, feel free to let me know, I have not tested it fully yet, but i did test it before. I Changed it because I used an odd way of getting the Humanoid Before.