Anti-cheats on Roblox are most of the time actual waste of development time due to most game’s life-cycle being fairly short to justify any Real Anti-cheat development.
As most developers here mention it is very important to perform all necessary logic and checks on server as anything from client can and will be modified by exploiters.
However most people feel very defeated as they think that there is absolutely no way to do good anti-cheat - there actually is.
There have been many clever ways to actually detect anti-cheats and also to prevent their deletion.
One thing to keep in mind is to realize that anti-cheat is not forever, its only a matter of time when it gets broken despite anything you do.
Lets first talk about persistent anticheat.
Persistent anti-cheat can be achieved by having some kind of value which has to be periodically sent to server to verify that anti-cheat is still running.
Issue with that is the fact that exploiters can just find this code right? Not if we obfuscate it.
Obfuscation is very important as it allows for partially securing the code at the cost of performance.
Now most common way to approach this kind of code is to use counter and have math equation (which can be non-sensical as that would help in obfuscation part) and that would generate updated number.
More future-proof and somewhat more telemetry-heavy solution is to generate this number partially from said equation BUT also make it use replicated parts of space in-game so that server can try to generate same number and if it doesn’t match detect the player.
This is a way to create anti-cheat which has to run for player to not get banned.
Now lets get into some juicy detection methods:
Module script require detection:
Re-require detection method.
Whenever script requires module said return table gets saved to cache and whenever there is another require table from cache gets returned.
The way most executors work most of the time is require has to be executed again and cache cannot just be returned, meaning you can use this to your advantage.
By leaving some marker for example of execution and then incrementing it or modifying it in some way if it happens again you can detect re-require of modules.
If you can tie it well to anti-cheat which is persistent in creative way it can be used as detection.
Verifying caller.
You can actually return function in modules instead of a table, which means that you can use debug library.
Debug library is really useful as you can actually get identifier of the script running by doing debug.info(2, "s")
This can be used to check if its valid environment and to trigger a ban or to actually maliciously provide wrong data.
GetLoadedModules() honeypot
Exploiters know that requiring modules can trigger detection therefore they use this function to get loaded modules and then to get to loaded module.
We can exploit this by having honeypot module which uses metatable methods (such as __index __newindex)
And if they search for specific keywords in it or try to modify different values which should be protected we can achieve detection.
Exeuction time profiling
This is one of the less sure-fire way to detect exploiters but concept works on the fact that functions like hookfunction and hookmetamethod are used to wrap function with a very fast function. Even if this function is extremely fast it is possible to measure it against some base-line - it is measurable difference in performance.
You do this by calculating a ratio from empty for i loop and for i loop with something that can be hooked onto.
By checking this ratio it is possible to observe meaningful difference.
Issue with this is that it is possibly inconsistent but this is one of the best protections against hook functions.
Honey-pots are your friend.
Probably best solution is to just do fake and annoying to parse through, very similar to original functions which waste alot of time for exploiters and you can always update them somewhat easily to make it as annoying as possible.
Now there are some detections for script upvalues but debug library is very limited.
Note that most of these depend on how sloppy exploiter is.
“Shadow variable”
Most exploiters with set upvalues to obvious values but you can actually have another value to compare to in some set function and this can lead to detection.
frozen tables
Exploiters might try to change table which is frozen which should give them error, they might then try to change the whole table and you can detect if they replaced it by frozen one or not.
Table value honeypots
While normal upvalues do not keep their keys(variable names) tables still do which means we can either create fake tables which look exactly the same as real one.
This one also depends on how much effort and how skilled exploiter is.
Anti-cheats are very unrewarding because you spend unimaginable amount of time on them and they will get cracked at some point.
It is very important to mention that anti-cheats are best when tailored. When anti-cheat is specifically written for your game you can do specific prevention that will have maybe better effects than general solutions such as those mentioned above.