Pretty sure StateChanged set by the client won’t replicate, only the physical movement of the character does because they have NetworkOwnership. Having this in the Client isn’t always a bad thing as long as it isn’t your main primary source to getting rid of exploiters.
You shouldn’t be making any exploit detection on the client due to exploiters being able to just simply remove it. If your making it on the client its safe to say that you’ve wasted your time.
Not all exploiters are that smart, as long as you have a good secure server there’s nothing wrong with putting anything on the client to help even more.
This has been discussed through threads and threads so I’m just going to leave it at that.
If I were to do that, I’d probably follow this flow:
--[[
1. Keep checking whether the character is in air through Humanoid.FloorMaterial or Humanoid:GetState()
2. Raycast down the global Y axis and obtain the distance of the ground from the character
3. Apply some old physics to see in how long the character should reach the ground
4. When the time comes, check if the character is still in air (so people don't get detected for jumping
]]
But this is all theoretical and I’m sure there are surer and better ways out there
My goal is to make it more server side but im just trying to get the main elements of the script down so i can make the server sided one nice and clean.
I personally check the floor material every .2 seconds, raycast downward and then i calculate the average height. If their average height is above 100 for example then it will give them a strike. If they get three strikes they get the boot.
One thing you should also check for is if the player is not sitting. Because if someone is flying in a(n) airplane or helicopter, they could get caught flying.
Easily circumventable and can produce false positives based on your game. In terms of circumventing it, you can just fly at 99 studs or lower. In terms of false positives, non-exploiting clients can meet the requirements needed to get flagged. False positives are bad for UX. Don’t kick, place them at an appropriate location.
There are ways to stop fly exploits but this isn’t one. Remember that clients are authoritative of the physics of their characters. You need to take this into account when handling movement-based exploits.
Here’s what I do. Raycast downwards with some random scatter a fixed amount times after every interval of a certain amount of time. If none of the rays hit the floor for a few seconds punish the player.
Be careful about false positives — you should let some exploiters get away with it so you don’t punish laggy players.
Also nothing client sided can be trusted. Humanoid values like jumppower, walkspeed, state, can’t be trusted and some don’t even replicate.
The way I would go about detecting flight is using the Humanoid.FloorMaterial property from the server.
This allows you to tell if the Humanoid is not standing on anything, and if so you can check each RunService.Stepped if their acceleration is decreasing. (Acceleration is the difference in velocity each frame, so each frame you want to make sure their upward acceleration is decreasing)
You can even go a step further and check if the player is following physics by using f = ma (accelerationOfGravity = workspace.Gravity * rootPart:GetMass()). You can then check if the acceleration is too far away from the acceleration of gravity. (E.g. each axis is more than 5 studs away).
Note than when a player jumps their final Y Velocity reaches their JumpPower before they begin falling again.
Not sure if this is what you mean, but the acceleration caused by gravity is always constant. I see you are using the equation f = ma with the code accelerationOfGravity = workspace.Gravity * rootPart:GetMass(). In reality, this code actually represents the equation f=gm which is simply the force caused by gravity; aka normal force.
The actual acceleration is constant; this can be proved with f=ma; a=f/m; a=mg/m; therefore a=g.
If you want to verify if the player is obeying the laws of physics just use a=f/m. During freefall, the players acceleration will simply be workspace.Gravity. To estimate the players acceleration, you need to calculate the derivative of a players velocity.
Pointless. If the client creates it, then the instance is only going to exist client-side. Suggesting checking for these instances (which exploiters don’t need in order to exploit physics) implies client-side checks which are inadequate for this case. It can be bypassed easily as it is.
Any good security model employs the server to either sanity check what the client is doing or grant it permission to take action. Client-side security is if you’re looking for extra measures, despite acknowledging the risk that comes along with adding them.
Knowing this, you could check the Velocity or RotVelocity in the case where it gets dangerously (as in flying) high. You can check this on the server too without having to worry about bypassing your client side anticheat.
The issue with this, is if a player is flung (roblox glitch) there would be no way of knowing if this happens.
Example
--Example
HumanoidRootPart:GetPropertyChangedSignal("Velocity"):Connect(function()
if HumanoidRootPart.Velocity.Magnitude > 1000 then -- i don't think you can compare vectors normally, this is easier than making a function for this
Player:Kick("You MIGHT be exploiting")
end
end)
Client Side
On the contrary, you can go the client sided route:
-- Client-sided example
Character.DescendantAdded:Connect(function(Object)
if Object:IsA("BodyMover") then Player:Kick("Exploiting") end -- this is the safest route
end)
Yep, in my anticheats I tend to do a lot of physics checks since they are pretty straight forward to implement.
I do something very similar to this before all of my other checks:
local limit = 2 -- How much faster they can travel from their actual velocity (helps reduce rubber banding for laggy players without giving too much freedom)
-- Lag can cause their velocity to increase (I think its related to throttling but I'm not 100% sure)
-- When you lag physics is slowed down on the client (excluding characters, however this occasionally seems to weirdly have an effect on their root part's velocity, but I'm not sure why this would be the case)
-- Horizontal checks
local horizMask = Vector3.new(1, 0, 1)
velocity = (HumanoidRootPart.Velocity*horizMask) -- Store their horizontal velocity
if (HumanoidRootPart.Velocity*horizMask).Magnitude > Humanoid.WalkSpeed + limit then
velocity = (HumanoidRootPart.Velocity*horizMask).Unit * (Humanoid.WalkSpeed + limit) -- Clamp their horizontal speed to their current walk speed
end
-- Vertical clamp
velocity = velocity + Vector3.new(0, math.min(HumanoidRootPart.Velocity.Y, Humanoid.JumpPower + limit), 0) -- Clamp their positive vertical velocity to their JumpPower
-- Gravity checks, TP checks, fly checks, etc
It’s also important to make sure you account for vehicles in your game if you have them since the player can then travel much faster. That’s possible to do with Humanoid.SeatPart (and with Attributes now available you can make this process very easy without any extra scripts).