How to prevent movement exploits without server-authoritative movement?

Hello! I’d really appreciate some advice on how to handle this.

Basically, I’m making a +1 speed game. At the beginning of development, I was thinking about using server authority, so I wouldn’t have to worry too much about additional sanity checks.

For example, at the end of each stage, there’s a win pad with a .Touched event that gives the player a win. There are also some basic checks for checkpoints, etc.

However, server-authoritative movement turned out to be really laggy.

So now I’m not really sure how to prevent movement-based exploits, or whether it’s even worth trying to prevent them completely for this type of game.

Obviously, I want to keep the game competitive and avoid situations where exploiters can simply fly/teleport to the end of the obby, get a huge number of wins, and repeat the process to get onto the leaderboard.

How would you approach preventing movement-based exploits in this kind of game? I’m not really sure how to actually detect or prevent things like flying, teleporting, speed hacks, etc.

I’d especially appreciate advice from people who have dealt with similar movement-based exploits in Roblox games.

My opinion on this is fairly simple, the probably fix would be to force a reset or reload characters if anything in loaded in without server check

In a +1 speed game, your main risk is indeed the auto-farming by teleports/speed.

A good solution is to analyze the movement on the server before awarding the player with benefits. You don’t even have to constantly try to rollback the player, you can eg calculate how much horizontal distance the player made in a rolling window.

If the player is much faster than expected → teleport back to start without rewards
If the player didn’t move at all on server → silently ignore

When a player complains something is broken, ask them for a recording of a run. If your implementation is correct, they’ll probably ghost you when they realize there’s no way around.

I changed my mind.. Sera Eri’s reply is the obvious fix here. One mega win and you’re outta here.. :rofl:

[On The Server]

At the start of the race, get your goal position and the player position, then subtract them to get a delta. Using the length / magnitude of this ‘delta’, you have a distance, and from there, you can divide that by their WalkSpeed to get the time it should take them to reach the end.

On finish, compare that with your estimated time, give it a bit of wiggle room and flag anything that finishes quicker.

local delta = ( goalPart.Position - humanoidRoot.Position )
local distance = delta.Magnitude
local estimate = distance / humanoid.WalkSpeed

function OnFinish( player, finishTime )
    -- Anything faster than 75% estimate is wacky
    if ( finishTime < estimate * 0.75 ) then
        print( `{player} is 1337 H4X0R` )
    end
end
1 Like

Do you think anything within the game itself is making server authority really laggy? Like visuals that take place on the server and do the whole replication stuff?

So it really depends on the type of exploit they’re using.

Flying exploits I just have a script inserted into their character that checks their humanoid.Floor and I set a variable to count u[ by +1 for every second that a player is in the air. So 3 seconds or so is a bit of time to stay in the air (unless you have a high JumpHeight then it could take 3+ seconds to fall back down to the ground.)

So I check for FloorMaterial like this:

--This assumes script.Parent.Parent is the character then fetch the humanoid
local airTime = 0
local humanoid = script.Parent.Parent:FindFirstChildOfClass("Humanoid")

--0 is the quickest number a loop can go, you could also just do 1 for every second.
while true do task.wait(0)
    if humanoid.FloorMaterial == Enum.Material.Air then
        if airTime >= 3 then
            print("Player has been in the air for 3 full seconds. Likely hacking.")
        elseif airTime < 3 then
            airTime += 1
            task.wait(1)
        end
    else
        airTime = 0
    end
end

Note: The anove script uses a normal loop. I only did this as an example, and it’s really not best practice. Anyone could jump and the loop might not register that they touched the floor within that amount of time, and it could potentially give a false-positive of them being a hacker.

For best use please use os.Time. The above script was a quick example I wrote into this DevForum comment.

Also another way I would prevent exploiting is if you’re checking for a movement-based exploit such as running and your game, as you’ve stated, gives +1 speed every second or so, then that’s actually what you should be checking.

If you give players +1 speed every second and you want to check to see if they’ve exploiting the game, store their total play-time somewhere. Even if it’s just a value in the leaderstats.

You can check this by doing something similar to the script above like this:

--This assumes script.Parent.Parent is the character then fetch the humanoid
local airTime = 0
local humanoid = script.Parent.Parent:FindFirstChildOfClass("Humanoid")

--0 is the quickest number a loop can go, you could also just do 1 for every second.

--Gets the player's character
local playerCharacter = script.Parent.Parent
local players = game:GetService("Players")
--Gets the player from the referenced character
local player = players:GetPlayerFromCharacter(playerCharacter)

--fetches your leaderstats
local leaderstats = player:FindFirstChild("leaderstats")
local playTime = player:FindFirstChild("PlayTime")
local defaultSpeed = 16

--Calculates the speed. If playTime is 0 then speed will be 16. If playtime is 1 then speed will be 17. Continues.
local calculatedSpeed = playTime.Value+defaultSpeed

while true do task.wait(0)
    if humanoid.Walkspeed > calculatedSpeed then
        print("They are likely hacking. Their speed is greater than the speed collected from time.")
    else
        --It would be less than or equal if you give players the option to set their speed.
        print("Their speed is <= calculatedSpeed.")
    end
end

Note: Keep in mind that if you use conveyors, they are known to glitch. Sometimes conveyors that move the characters can accidentally fling a player across the map or something, and so this gives a false-positive and makes the system think the player is exploiting.

^ Not that you’re using conveyors or anything, I mean some games do but I just wanted to note that here.

(That also depends on your system and if you decide to use a script that checks the playerCharacter’s position.)

I hope this helps, let me know! I noticed someone had an estimated time and if you decide to use my speed approach then you wouldn’t have to estimate how quickly they should finish the race. You can just literally compare it to how long they’ve been in-game for and check the speed they’re allowed.

Also I noticed the answer that @TheFaeTwins gave, and this is a very good approach to this. One thing I would challenge this with is this:

  1. What’s stopping the game from accidently flinging the player across the map and then giving them a punishment?
  2. Also if collisions are on, faster players could potentially help push a slower player (maybe their friend) across the map by pushing against them.

Please note that I’m not attacking your comment, I’m just thinking about it in a very “what could go wrong, or what unexpected issues or outcomes can occur?” way.

You could even combine these two checks.

  1. On race completion check to see if it’s suspicious.
  2. If it’s suspicious then check the walkspeed.
  3. If the walkspeed is totally normal then it was likely a glitch, fling, accident, or a potential teleport exploit. but totally if their walkspeed is greater than what’s actually allowed for them then it’s way likely a hack.

Nope. You can test server authority yourself:

  • Set the speed to something like 100 — you’ll start seeing random fallbacks.
  • Try hitting a wall at high speed — the character starts randomly changing CFrames and behaving inconsistently.
  • On a high-ping server, movement becomes delayed. For example, when a player presses W, they have to wait for the server response before seeing the movement.

After running into many more issues like these, I switched back to client. For my game, server authority just isn’t ideal due to these limitations.

Thank you, I really appreciate your response! That sounds good, and I’ll definitely consider it.

I also think combining it with Sera Eris’s additional checks from this post would be a good approach.

Since the player’s speed increases with their level, and we’ve built the stages around the recommended speed for each level, players most likely won’t be able to complete certain stages unless they have reached the required level. So my idea is to also check the player’s level when they finish a stage and simply not give them the reward if they finish significantly faster than expected or don’t have the required level.

Obviously, I’ll keep some tolerance to avoid false positives.

Let me know what you think about it!

1 Like

Your test can have many false positives targeting the air time. This way exploiters can use fling on players and it will get them banned or likely flagged as an exploiter themselves. I suggest you improve that method. This would only happens IF collision are on. In simple addition, the humanoid walkspeed can tweak depending on certain animation or bundle which can make it faster if glitches get made

That was more of an example, special use cases such as out of bounds, etc. would be handled different, if someone is out of the map you can handle that any way you’d like.

I choose to do that way for a multiple of reasons, but if someone is out of bounds i.e. past a certain point, outside of allowed areas, touched a part and triggered an out of bounds event they otherwise weren’t supposed to trigger, then you handle each case accordingly.

I don’t know how OP’s map is setup so I can’t give a definitive answer, I can only help guide them with a potential solution. They should choose what works for them in the end.

Edit-1: About the humanoid.walkspeed, you can easily set R6 only, set certain animations, make custom characters, etc. This doesn’t inherently mean that it’s automatically a wrong solution.

It’s a solution like any other, some might just require additional steps, however it’s not guaranteed. Again, a lot of this can be tied to special use cases, etc.

Edit-2:
I just remembered in a previous comment I made I stated this:

  1. Also if collisions are on, faster players could potentially help push a slower player (maybe their friend) across the map by pushing against them.

This obviously doesn’t just cover if a player pushes another player, but your example as well. Sure many of these can have false positives, but so can a lot of other things. That’s why game development requires thinking in those areas as well.