Prevent Jumping: Can this be avoided by an exploiter?

Hey there!

Sometimes you really wanna disable some features that are by default there. For example: jumping! Some games don’t have it, some do. However, we know that exploiters can easily get around this.

Continuing the saga of me learning and asking you more experienced people how to go about prevent exploits; I’ve found a piece of code that disables the ability of the player to jump. It can only be done in a LocalScript since those sensitive properties aren’t replicated to the server. Fair enough, but I don’t trust it one bit. Take a look at the piece of code below. It works perfectly, just what I wanted, but it can’t be difficult to bypass it, right? Or am I doubting it too much. Either way thanks in advance!

– Happy coding!

-- Services
local userInputService = game:GetService("UserInputService")
local players = game:GetService("Players")

-- Local player
local player = players.LocalPlayer
local character = player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")

-- Fires when the user tries to jump
local function Jump()
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
end

userInputService.JumpRequest:Connect(Jump)
3 Likes

Unfortunately, what you already know is correct, and there is not much you can do about it. Any serious steps that prevent player from jumping burden the server way too much for us to consider implementing them.

There are multiple ways one can prevent jumping. However, there are also a lot of possibilites for player to jump. Since local players control their character model’s physics (simulate them), they can fly, increase their humanoid’s assembly linear velocity (upward movement), simply disable scripts that prevent jumping etc.

(I added two sentences to this paragraph later.)
Your best bet is to not worry about it. You could go to great lengths to prevent jumping, and it will probably work, but is perhaps also not worth it for the reasons explained above. That’s why you should probably stick to general anti-cheat system and prevent regular exploits rather than preventing jumping completely. That also ensures you don’t have too many so called “false-positive” cases.

The code you’ve posted works, albeit introducing the same problem we already mentioned. Exploiters can easily disable this script and stop it’s functioning.

At the same time, it is also not performance friendly, since it activates each time player tries to jump. Instead, take a look at the following options:

1. Disabling jumping state

Security level: relatively secure

Ways to bypass: custom jump function, re-enabling jumping state, flying etc.

local player = game:GetService("Players").LocalPlayer
if (not player.Character) then player.CharacterAdded:Wait() end
local humanoid = player.Character:WaitForChild("Humanoid")

humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)

2. Setting properties client-side

Security level: least secure

Ways to bypass: set values back. Any of the above methods apply as well.

local player = game:GetService("Players").LocalPlayer
if (not player.Character) then player.CharacterAdded:Wait() end
local humanoid = player.Character:WaitForChild("Humanoid")

humanoid.JumpHeight = 0

3. Unbind jump action

Security level: relatively secure

Ways to bypass: custom jump function, flying, binding action back etc.

This might be more tricky for exploiters to discover, although it’s not very complicated to find alternative ways or bind actions back. Main drawback is more demanding reverse action.

local CAS = game:GetService("ContextActionService")
CAS:UnbindAction("jumpAction")

4. Changing properties server-side

Security level: not very secure

Ways to bypass: simply setting values back. All the other methods apply too.

game:GetService("Players").PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		-- Option 1 (only use one option):
		humanoid.JumpHeight = 0
		-- Option 2:
		humanoid.UseJumpPower = true
		humanoid.JumpPower = 0
	end)
end)

As you can see, these are all simple methods that work well, despite the fact than none of them is reliable tool against exploiters. It ultimately comes down to what your initial goal is. If it’s a simple prevention system, use any of the methods above, perhaps preferably options 1, 3, or maybe 2.

If jump prevention plays a key role, you should consider controlling player’s character customly and setting up barries that player’s can’t pass. It’s harder to achieve that, however.

There are definitely more methods to stop jumping, albeit most of them are probably not at all better than the ones above. For instance, we could also disable jump action using PlayerModule, which is arguably worse alternative to disabling action using ContextActionService.

EDIT (2021-04-03)

IMPORTANT! The following is an extension of this post.

7 Likes

Thanks for the very detailed answers Essence! I loved every bit of it.

Though I wish it was possible to prevent exploiters from bypassing any of these methods, it’s simply not gonna happen. Luckily for me, the game I have in mind won’t really take a toll if an exploiter decides to disable the anti-jumping scripts and what not.

After all, players can alert me if they find someone that does and I can simply get them off my game, simple as that. No one gets hurt, they don’t get a giant advantage whatsoever, we are all happy at the end of the day.

Thank you again!

1 Like

@EssenceExplorer

You’re wrong here, jumping can be prevented quite easily. Capture the player’s previous position and check if the current position delta Y is greater than their jump height / power is capable of. To prevent false positives (player is falling), check if their current position’s Y axis isn’t lower than the previous one. Also make sure to check if the time in air is high enough.

To counter for players with high lag, you can just increase the threshold. This is what I do in my anti cheat:

if lastPosition then
	local maxJumpHeight = humanoid.JumpHeight
    local dp = hrp.Position * Vector3.new(0, 1, 0) - lastPosition * Vector3.new(0, 1, 0)
	local length = math.floor(dp.Magnitude)

	if length > maxJumpHeight + (Settings.strictMode and 0 or Settings.jumpThreshold)
	 	 -- Make sure player is not falling:
	 	 and math.floor(hrp.Position.Y) >= math.floor((lastPosition or hrp.Position).Y)	
    then 		
		-- Player jumped higher than their jump power is capable of / in sky 
		table.insert(exploitData.detections.speed, "Jumped higher than their walk power / in sky")
		exploitData.flags += 1

		if Settings.strictMode and exploitData.flags > Settings.flagsBeforeKick then
			 player:Kick("[BoboDefender]: Attempted to fly")

		elseif not Settings.strictMode then
			if os.time - (exploitData.inAir or os.time()) > Settings.maxSecondsInAir then
			    	exploitData.recentActivity = true
			        PunishPlayer(character, lastPosition, networkOwnerChange)
				    exploitData.recentActivity = false
			 end 
		end
	end
end
2 Likes

My phrasing might not be ideal here. Please excuse me for that. I’m not wrong, and I explicitely stated that one can make effort and write a good anti-jump system. The issue is burdening of the server, because any such rapid checks drain resources. At the end of the day, it’s most likely not worth preventing jumping completely server-side, but rather having a general anti-cheat system that prevents general flight and teleportation exploits etc. Preventing any jumping seems too expensive.

Barriers, such as maximal y-axis delta in a period of time, or physical barriers. To make a decent system, it takes a little more time, so it’s thus harder to achieve than simple jump prevention.

I’ve assembled together a quick alternative, about which I’m not sure whether it’s more performant or not, but requires less calculations by user and requires raycasting, which has it’s own advantages and disadvantages. It’s easier to detect when player is in air, but at the same time activates raycasting in the background. This method still performs very well.

I haven’t done much of a testing, but the idea is understandable I believe.

Basic anti-high-jump code with raycasting
local rootParts = {}
local lastMagnitude = {}
local jumpAttempts = {}

local ALLOWED_JUMP_HEIGHT = 5
local HRP_CENTER_HEIGHT = 3

-- Set-up and clean-up ----------------------------------------------------------
game:GetService("Players").PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		rootParts[player.UserId] = character:WaitForChild("HumanoidRootPart")
		jumpAttempts[player.UserId] = 0
	end)
end)
game:GetService("Players").PlayerRemoving:Connect(function(player)
	rootParts[player.UserId] = nil; lastMagnitude[player.UserId] = nil
	jumpAttempts[player.UserId] = nil
end)
---------------------------------------------------------------------------------

while (true) do
	for userId, root in pairs(rootParts) do		
		local raycastResult = workspace:Raycast(
			root.Position,
			Vector3.new(0,-ALLOWED_JUMP_HEIGHT - HRP_CENTER_HEIGHT,0)
		)
		if (not raycastResult) then
			if (root.Position.Y > lastMagnitude[userId]) then
				-- It's on developer to decide what to do from here on.
				-- I teleport player to the ground just as an example.
				local floorRay = workspace:Raycast(root.Position, Vector3.new(0,-200,0))
				if (floorRay) then
					root.CFrame = CFrame.new(floorRay.Position)
				else
					root.Parent:Destroy()
				end
			end
		end
		lastMagnitude[userId] = root.Position.Y
	end
	-- More frequent checks are more accurate, but much more demanding as well.
	wait(.4)
end

Your script seems pretty solid, but there are a couple of small yet crucial corrections you may want to do.

Exploiters can easily change this value any time and modifications will replicate (just like humanoid.JumpPower property), meaning they can easily bypass any further detection. They can attach a metatable, returning the same value over and over again.

You should also replace os.time() for time(), because os.time() only returns seconds. Any smaller jump time intervals won’t be detected.

Everything else seems alright, but you can reduce number of calculation. For example:

local delta = hrp.Position * Vector3.new(0,1,0) - lastPosition * Vector3.new(0,1,0)
local length = math.floor(delta.Magnitude)
-- replace for simple:
local delta = hrp.Position.Y - lastPosition.Y

I think you can also leave out all the math flooring, since it’s not needed, and consumes resources.

@FactorOfTheThird if I were you, I’d use these advices only in case jump prevention is crucial. Otherwise, rather implement a general anti-cheat. It will not only reduce the number of “false-positives” even more, but also represent less of a burden for your server.

You also shouldn’t kick players unless cheating is obvious. Non-cheaters may get “flung” because of terrain bug, or have very weak internet connection, all of which may potentially kick them and eventually discourage them from playing your game.

EDIT (for curious ones)
Magnitude checks use dot product and square root calculations, which are relatively expensive:

For instance:

print(hrp.Position.Magnitude)
print(math.sqrt(hrp.Position:Dot(hrp.Position)))

Second option is twice as fast in terms of performance on micro-level, but both are relatively demanding.

2 Likes

The issue is burdening of the server,

I forgot to mention that once the exploiter is detected, I take away their network owner ship for around 5 seconds which means they can’t no clip, fly or speed hack, and the anti cheat won’t do further detections again if they don’t have network owner ship.

nd of the day, it’s most likely not worth preventing jumping completely server-side, but rather having a general anti-cheat system that prevents general flight and teleportation exploits etc. Preventing any jumping seems too expensive.

My anti chest protects against all common exploits, so this point is just irrelevant, please make points they you are sure are correct; as it not only saves time and prevents mid information.

I’m not wrong, and I explicitely stated that one can make effort and write a good anti-jump system .

I understand that you’re trying to contribute here, but you shouldn’t give out points which an exploiter can easily go through.

Exploiters can easily change this value any time and modifications will replicate (just like humanoid.JumpPower property), meaning they can easily bypass any further detection. They can attach a metatable, returning the same value over and over again.

This is incorrect, due to filtering enabled, exploiter modifying the value will not replicate to the server. The exploiter in no possibly way can bypass the anti cheat.

I think you can also leave out all the math flooring, since it’s not needed, and consumes resources.

Please don’t make subjective statements, math.floor is highly needed here to get off the decimal to prevent inaccurate reading.

For instance:print(hrp.Position.Magnitude)print(math.sqrt(hrp.Position:Dot(hrp.Position)))
Second option is twice as fast in terms of performance on micro-level, but both are relatively demanding.

Both of them aren’t demanding, the first time you access .Magnitude, it’s cached. Therefore using it again will return the cached value, it won’t have to calculate it again. To mention that taking a square root isn’t expensive at all, don’t provide such false claims.

You should also replace os.time() for time() , because os.time() only returns seconds. Any smaller jump time intervals won’t be detected.

It’s not needed at the moment, but I plan to change it to either os.clock, as it is much better than time, since it has high precision.

local raycastResult = workspace:Raycast(
			root.Position,
			Vector3.new(0,-ALLOWED_JUMP_HEIGHT - HRP_CENTER_HEIGHT,0)
		)

There is also a flaw within your “high jump anti cheat code”, you aren’t accounting for the exploiters jump height / power, and I don’t recommend having constant values since they won’t get you reliable results as expected and are one of the most inefficient ways you can go about making a jump anti cheat.

You are also detecting in a single thread, which will get you quite inaccurate results, since if a exploiter suddenly exploits and goes far away from the ground for a few seconds, the code may not detect that, which is not what you want.

local floorRay = workspace:Raycast(root.Position, Vector3.new(0,-200,0))

Instead of ray casting unnecessarily, capture their last position and teleport them to that last position.

Everything else seems alright, but you can reduce number of calculation. For example:

That is just a micro optimization, no need for that. Optimization isn’t needed and only should be used if your code is performing heavy tasks frequently.

You also shouldn’t kick players unless cheating is obvious. Non-cheaters may get “flung” because of terrain bug, or have very weak internet connection, all of which may potentially kick them and eventually discourage them from playing your game.

You can just increase the threshold and use the delta time to account for lag, if a exploiter is flinged, then you can account for it by disabling collisions.

1 Like

Look, I don’t want to argue. If you don’t want to accept suggestions for improvements, which stand on solid grounds, you don’t have to. I pointed out some aspects that can be changed and argumented them.

Not, it is correct. Player is in control of their character (network owners), so they simulate their physics. They can access movement properties such as jumping power/height and speed, which consequentially results in such exploits existing. Go to server test in studio and try changing those values while observing changes server side.

Subjective statements? They are not subjective at all. In fact, math.flooring rounds numbers, resulting in less accurate number. I don’t see a reason why we need relatively expensive flooring. Why would you need to floor delta on regular basis? How can reading be unaccurate without it? It isn’t.

How can it be cached? Caching such values would consume a lot of memory. At the same time, when the player is moving, values are constantly changing, and magnitude is being calculated as a parallel calculation in your case.

What are you trying to tell me? That square rooting isn’t relatively expensive? It is, but depends on point of view. It is definitely a good practice to avoid it if we can. The less calculations, the better.

Not at all inefficient. I told you that the code I pasted is sample code to give everyone here a basic idea. They can always set jump height value customly, but not by taking the value from humanoid, because again, it can be changed by cheaters.

The loop finishes way faster than in a few seconds. If we have for example 60 and more players in a single server, it might be a good idea to implement threads. That’s true. Results are still very correct when it comes to jumps higher than normal. It will effortlessly detect players that jump for multiple seconds. Detection frequence may also be increased.

You don’t have to accept the suggestions. But you are rejecting to reduce a big number of calculations. The performance difference isn’t big, but logically speaking it’s much better to reduce the code length, readability, as well as improve performance to some extent. That’s what programming is about.

1 Like

Boldly providing false information is correct? Your points are invalid and I have clearly gave enough info on how you are incorrect.

No, it is completely incorrect, Only the physics will replicate if they have network owner ship, not their values.

The length usually returns numbers in decimals, like 7.4 and 7.2, assuming the humanoid height is 7.2, flooring the length is highly needed to prevent false positives just because of a decimal number increase.

This is subjective, these calculations are not expensive and removing them is just micro optimisation.

I explained why it is inefficient above the point above.

I don’t know how you want this elaborated to you, but these calculations are not expensive and they aren’t a “big” number of calculations.

If you are going to argue pointlessly and provide false information with it, I or someone may have a reason to flag your post.

Oh jeez, what a back and forth argument’s we are having here haha. This is interesting. Didn’t really mean this post to become something like this. I feel like both of you have your own points. Obviously I can’t fact-check most or any of these as I still have ways to go in my scripting journey, but one of you needs to become a bigger person and drop it haha. It’s obvious we aren’t really getting anywhere with the arguments :slight_smile:

2 Likes

@SilentsReplacement you obviously have a big problem with accepting reasonable corrections and suggestions. I’m trying to help here, and you are constantly aggressive against anyone who proposes a potentially better way to do something. I’ve seen your responses on other posts by other people too. It’s not a behaviour that is welcome on this forum.

I haven’t written a single post trying to criticize you in a negative way or be rude. You don’t have to accept the suggestions, which by the way have argumented points you are constantly trying to neglect.

What you are doing borders to hominem attacks, and you never act in a polite manner. As far as correct informations go, you didn’t even bother to give what you are saying a test. Let’s take a look at the following statement as an example:

Your own posts say it all.

@FactorOfTheThird you are completely right. My intention is and was not at all to argue here but to help you, while providing everything you can fact-check using Roblox studio, Roblox Dev Hub, and this forum. I hope you learned something new from this.

@SilentsReplacement I flagged your post for the reasons explained above.

1 Like

You’re absolutely fine Essence. I very much appreciate your effort in explaining each point in this post both by me and the other person.

Thanks for your time and patience to write all of the details up above! I defo learned some things and it’s giving me a good view on how I can approach exploiters in the future! :slight_smile:

1 Like

You like to argue pointlessly and spread false info, which led me to flag all your posts. Whenever someone corrects you, you argue in a very harsh way and when you get proven wrong, you instead try to blame the harshness on the other person and accuse them, I haven’t being rude to you or replied to you in a harsh way. I am getting sick of you boldly accusing me and spreading false information, and having a constructive argument with you is useless, as you are just going to argue again when you get proven wrong. The aggressiveness you reply with is something I don’t like and not tolerated in the developer forum, you are just acting immature at this point.

All I did was correct your invalid points, and I stand by all my points, I recommend you learn more about exploiting and filtering enabled before making such false claims. If you really think you are correct then you can either test it out your self or visit the hub.

you obviously have a big problem with accepting reasonable corrections and suggestions.

Me or you? Why would I accept a invalid correction?

Values don’t replicate to the server when changed via client, it’s not my job to give you proof, you are to test it your self.

Well, this is the first time you actually argumented what you are claiming. As it turns out, what you said about JumpPower and JumpHeight values is correct. Changes don’t replicate, with certain exceptions we can read about in the following image.


(Roblox Client-Server Model, Roblox Dev Hub, 2021-04-03)

However, even though server doesn’t detect any changes, their effect actually replicates, hence such exploits exist. There is also nothing wrong with using constants, so I don’t see a reason why you called that inefficient. It’s better, but not inefficient.

Players can change JumpPower/JumpHeight, velocity, assembly velocity, humanoid state and so on.

As far as flagging goes, that was pretty immature from you. There is a big difference between politely showing someone that what they said is incorrect, and accusing them of spreading misinformation, attacking their character, and thinking you are always right.

You said you corrected my “invalid posts”. I made a mistake when I said value replicates, because only its effect does, but that doesn’t at all mean any of the other suggestions I’ve given are incorrect. time() suggestion (os.clock() is originally intended for benchmarking), reducing number of calculations…

Square root is relatively expensive, compared to multiplication, division, subtraction etc. Much more instructions to CPU are needed to find the approximate result, so it can be considered expensive compared to all other operations except math.pow(power), which is even worse in terms of execution time. On modern computers, difference is negligible, but that’s not the point. The point is in simplicity and readability, and I don’t see a reason why write:

local delta = hrp.Position * Vector3.new(0, 1, 0) - lastPosition * Vector3.new(0, 1, 0)
local length = delta.Magnitude

When the following cleaner line returns exactly the same result about 7 times faster on my computer:

local delta = hrp.Position.Y - lastPosition.Y

The goal is to keep it simple while using the least amount of operations possible and consuming less memory, considering those operations may run up to 30 times per second.

Magnitude checks are alright, but Unity documentation, for example, suggests avoiding it. Difference in performance is not big, but why use it if it’s not needed. I don’t know what you meant with “magnitude is cached”, but it seems like execution time is the same no matter if it’s cached or not (I believe it’s calculated).

This is my last answer to you in this topic. I made one mistake here, but you did too. You have no right as well as no solid grounds to accuse in such manner.

@FactorOfTheThird I wish you all the success with your future projects. At least you’ve learned something, which was the initial goal. :blush:

EDIT

Their last position might be in air.

1 Like

I see that you made some fair points.

That’s the point, if an exploiter jumps higher than their jump height is capable of with some addition of leeway to account for lag, they will be teleported back to their previous position. Using constants are not very reliable and will not get you accurate results necessary. Consider the scenario:

  • Player has a large avatar / is extremely tall, distance from their primary part and the ground they are standing on is high

You get the idea, that’s why your method is relatively inefficient. What I do is cast a ray from the players right and left foot and check if it hit something, the reason I cast 2 rays is only if one didn’t hit. This is to account when a player is standing on a edge with one of their foot not on the ground, this is one of the most efficient ways you can check if a player is on ground.

As far as optimisations go, square root is not an expensive operation as I tested it my self, will send the code later since I’m on my phone.

Yes, because you want to have the outmost precision when working with time related anti exploits, hence why os.clock is better. Especially if you aren’t detecting often. That’s the reason os.clock is intended for benchmarking but that doesn’t mean you should only use it for benchmarks only.

These operations aren’t performed frequently in my anti chest nor it is needed, but I tested this my self and saw no differences in performance at all. If you have a fairly decent CPU, you should be good. Recently, these operations have been optimised in the new Luau VM.

Their last position might be in air.

Then only update the position when on ground, this is something I do in my anti cheat.

don’t know what you meant with “magnitude is cached”, but it seems like execution time is the same no matter if it’s cached or not (I believe it’s calculated)

Indexing magnitude of the same vector will cache the result, so it won’t have to calculate it again unnecessarily. Also, these are all implemented in C, meaning the performance difference will be even lower since program in C runs faster than compared to other languages.

1 Like