Anti-cheat without telemetry: server-authoritative state.
Most anti-cheats lean on client signals you can't trust. We moved everything that matters to the server. Here's the architecture.
There's a common pattern in FiveM anti-cheat that we want to argue against: telemetry-driven enforcement. The pattern goes:
- The client reports what it's doing.
- The server inspects the report for things that look suspicious.
- The server bans, kicks, or flags.
This is wrong. Not because it doesn't catch cheaters — it does, sometimes — but because it builds your enforcement on a signal you have no way to trust. The very actor you're trying to police is the one writing the report.
The trap of "looks suspicious"
Every telemetry-based check is a heuristic, and every heuristic is an arms race. You add a check for "moving faster than physics allows." A cheater patches their position deltas to look natural. You add a check for "money increased without an event." A cheater fires the matching event. You add a check for whether the player has a debugger attached. A cheater hides their debugger.
The cheaters are not smarter than you. They are more patient. They have a single, focused problem (defeat your check), and you have a hundred features to ship. They will win the long game every time.
Server-authoritative state
The alternative is to make the cheater's report irrelevant. The server owns the state. The client requests changes. The server validates and applies them. The client gets told what happened. No telemetry needed, because the cheater never had the ability to lie in the first place.
For a concrete example: when a player completes a mechanic job in rb-mechanic, here's what the client cannot do:
- Add money to itself
- Mark the job as complete
- Spawn the reward vehicle
- Set its own reputation level
- Change its job grade
Here's what the client can do:
- Send a
rb_mechanic:requestCompletionevent with the job ID - Receive a
rb_mechanic:jobCompletedevent with the result
The server, on receiving the request:
- Looks up the job by ID from its own table (not the client's claim).
- Confirms the player is the assigned mechanic.
- Confirms the player is at the dropoff location according to its own copy of the player's position.
- Computes the reward from its own pricing config.
- Mutates state. Fires the result event.
A cracked client cannot inject a higher reward. It cannot complete someone else's job. It cannot complete a job from across the map. The server doesn't check for those things — it never gives the client a way to ask.
What you give up
Server-authoritative state is more expensive. Every action round-trips. Every action requires the server to hold canonical state. Every action is rate-limited by network latency, not by the player's local frame time.
We've had to make peace with that trade. The mechanic UI has a "thinking" state on every action that takes 40ms longer than the same action would in a client-trusting design. We considered fudging it — letting the client predict the outcome and rolling back on mismatch — but in practice the rollbacks were rare enough that the prediction wasn't worth the code.
The exceptions
There are exceptions, and being honest about them matters:
- Cosmetic state — what shirt the player is wearing, what color their headlight is — is client-driven and replicated. A cheater who lies about their hat color isn't worth the round-trip.
- Pure UX state — which menu is open, which tab is active — is purely client. Nobody cares.
- Position — strictly speaking, the client is authoritative on its own position. But every consequential action that uses position re-confirms it server-side at the moment of action.
The rule of thumb: if it has a consequence, it lives on the server. If it doesn't, do whatever's cheap.
What this enables
The headline benefit isn't "we catch more cheaters." It's that we don't need to. There's no detection pass, no telemetry pipeline, no manual review of suspicious reports. The behavior we want to prevent is structurally impossible. The behavior we want to allow is the only behavior the server will respond to.
You measure the success of an anti-cheat system not by how many bans it issues but by how many cheaters give up because there's nothing to cheat. Server-authoritative state is how you get to that number being most of them.