A popular executor just pushed a massive update, and it’s got me rethinking my game’s security architecture. I know the golden rule is “never trust the client,” but the tools exploiters have access to are getting incredibly advanced.
Here are the specific features they just rolled out that I am worried about:
In-house Luau Decompiler & Script Viewer: They can now easily read and decompile LocalScripts and ModuleScripts.
Game Explorer & Save Instance: They can map out the entire client-side hierarchy and steal place files (maps, client UI, etc.).
Luau Environment Improvements: Better environment hooks, which likely means stronger bypasses for client-side anti-cheats.
Since client-side anti-cheats are essentially a losing battle against these tools, I want to make sure my server-side architecture is as bulletproof as possible.
I’d love to hear how you guys are handling this:
ModuleScripts: How do you structure your ModuleScripts so that even if they are decompiled, the exploiter gains no leverage over the server? (Yes, i do know obfuscation can be the solution to this, but what about the saveinstance?)
Stolen Assets: Since “Save Instance” is back in full swing, do you just accept that client UI and maps will be stolen, or do you have any clever tricks to make stolen assets useless?
Any advice, resources, or structural tips would be hugely appreciated. Thanks!
As far as I’m aware, what gets sent to client can always be accessed by the client, meaning any successful exploit injection will always be able to access the assets within your game.
In short, you’re at the mercy of whether Roblox improves their built-in anti-cheat. There was a short period for around a year where they did some big update and 90% of executors were killed off, but one rule in game design is that every anti cheat you will ever design will be bypassed eventually.
Stolen assets don’t really mean much to me personally - it’s just a side effect of developing on this platform. For as long as your game is active and maintained, stolen re-uploads will always be the obviously stolen one that nobody bothers to play. Roblox’s restrictions on audience reach also make this reuploading process far more difficult for bad actors.
There’s a LOT of my work on leaking sites as we speak that’s been downloaded many times over, and it’s just how the cookie crumbles here.
I’m not an anti-cheat expert, but I do know that client-side anti cheat has generally always always been considered bad practice, and if you ABSOLUTELY need it, just obfuscate it.
On the bright side, server authority will probably erase a lot of notable cheats from existence (speed hacks, flying, etc) and make your life easier.
One thing you can do is store as many assets as possible in ServerStorage. They can all still be stolen, as they are streamed to the client. But if you only stream assets to the client as needed rather than keeping everything in ReplicatedStorage, it may give them a harder time to get everything.
Removing print() statements that might give them a clue as to what a function or variable is could help slow them down. But a professional would probably still decode it, and there’s nothing you can do. A smart enough exploiter (although rare), can just sit there and analyze your code until you’re cooked. Unless you’re spending 5 hours per system making it explot-proof, not too much you can do.
And yeah, assets are basically open-sourced and also nothing we can do about it.
Yeah honestly, fair point. Fighting the client is a losing battle anyway. If they want to copy the map and UI, let them, it’s just an empty shell without the server logic. Appreciate the reality check, definitely gonna just focus on making the backend bulletproof.
Solid points. Scrubbing the print statements is an easy win just to add some friction, even if it won’t stop the dedicated ones.
I really like the idea of keeping things locked in ServerStorage and only serving what’s needed rather than dumping it all in Replicated. How do you usually handle that approach for 3D models specifically? Are you just having the server clone them into the Workspace when a player triggers an event/enters a zone, or are you handling the streaming differently?
I mentioned the ServerStorage one because its technically an option but for me I practically only use it for any entities and vfx that arent spawned into the map at the start of the game. For normal mobs and npcs that are always spawned in, I dont think the method would work super well because youd want those entities to be rendered from a good distance and writing code to spawn them conditionally is tedious, so sadly the method isnt that practical or easy-to-do. Even with inventory items, if you want to have ViewportFrames and have no delays you need to keep your item models in replicatedstorage. You could maybe keep them in ServerStorage and then Clone them into their Viewports from the server when their inventory is loaded or an item is acquired. But also tedious.
I mean all of this stuff has existed for as long as FE compliant exploiting has. None of this is new, synapse could do it all circa 2018.
Obfuscation is useless here. The decompiling works via reading memory and scripts are abstracted pretty heavily to save on space. The scripts already will have no variable names, function names, etc. it is basically just reconstructing the logic of the script. Also how would they have leverage over the server? They can’t call modulescripts from the server, if you have a server specific modulescript just make it not accessible to the client (e.g. in server script service).
You have to accept it. In order to block save instance you would need to somehow not store the map/UI memory on the client. Most executors iirc are level 6 meaning they have access to literally all the memory the client has. If you are loading an asset on the client assume it is possible to be stolen.
Client anticheat gets too bad of a wrap as well. 99% of exploiters are children with zero understanding of game dev who are just running other people’s scripts. A competent exploiter can easily bypass client anticheat but you will still catch a lot of people who are running one size fits all scripts (speed hacks, flying, etc.) that aren’t specifically made for your game and don’t prevent client anticheat from triggering.
After an internal review conducted by the TNLO Central Optimization Council, the following determination has been reached.
We do not.
Client-side anti-cheats remain outside the scope of the TNLO Optimization Framework due to an unfavorable performance-to-effectiveness ratio.
The Council has therefore classified excessive client-side anti-cheat logic as non-essential computational overhead.
Pursuant to TNLO Code §5.7.B — Runtime Efficiency Standards, the introduction of measurable runtime overhead without corresponding measurable security guarantees constitutes an optimization violation.
People who swear by the “never trust the client” mantra are people who’ve never had a game with over 50 CCU. They think their perfect theoretical knowledge compensates for their lack of practical experience (it doesn’t).
Here is a practical scenario. You’ve perfected your server-side detections, they’re flawless, although heavily limited, because there is only so much they can detect. Part of your game’s core loop is not knowing where other players are. What do you do against ESPers? Manual moderation is an option, but in reality, not many people bother reporting someone whose cheating is not explicit, but more subtle. Maybe they are just that good? Why bother.
So in this case, why not make a simple client-side detection? The so-called “overhead computations” are overstated by a mile. The ratio of skids to people who are actually trying to reverse engineer your game is 100 to 1. And even for the tryhards it is an obstacle that slows them down. I recommend looking into LogService as a tool for detections. I won’t go into much detail, but its possibilities are only limited by your imagination.
Fair point on all of this not being strictly “new,” I think the recent executor updates just brought the paranoia back to the surface for me lol.
You’re absolutely right about ServerScriptService. As long as sensitive modules stay out of ReplicatedStorage, decompiling client ones doesn’t give them a server backdoor. And yeah, it sucks accepting that client UI and maps will inevitably be stolen via SaveInstance, but there’s really no practical way around it without ruining the player experience.
I really like your point about client anti-cheat. I was ready to scrap mine entirely, but you’re totally right, it’s worth keeping just to catch the 99% of kids running generic speed/fly hacks who don’t know how to bypass it.
I’ll have to file an appeal with the Council on this one. If a client-side anti-cheat is causing measurable runtime overhead, that sounds like an engineering issue. A basic check using LogService costs next to nothing but wipes out the vast majority of skids. Assuming the client is compromised is standard practice, but giving up on the low-hanging fruit to appease a rigid framework just leaves the door wide open for no reason.
This is a fantastic point. ESP is exactly the kind of server blind spot I was worried about when trying to make everything 100% server-authoritative.
You’re totally right about the 100 to 1 ratio. It makes zero sense to let 99 skids run rampant with generic scripts just because 1 dedicated reverse-engineer might bypass the client check anyway. Treating client AC as a filter rather than an impenetrable wall completely changes how I look at this. I’m definitely going to start experimenting with LogService to see what kind of honeypots I can set up. Appreciate the reality check!