Quick answer: Layer four detection methods: asset hash validation, server-side physics validation, behavioral heuristics, and shadow-banning into isolated lobbies. None is perfect alone; together they raise the cost of cheating enough that most attempts fail. Only use a commercial anti-cheat if the economics justify it.

A player joins your multiplayer game and reports that another player is clearly hacking — moving too fast, hitting from across the map, walking through walls. You check the logs and the server accepted every action. The bug is not in your detection code; you do not have any. Client modification is one of the oldest problems in multiplayer games, and for an indie studio, solving it requires honest thinking about what you can and cannot do.

The Uncomfortable Truth

You cannot fully trust the client. A player who owns the executable can attach a debugger, edit memory, patch the binary, or run the game inside an emulator that fakes whatever your anti-tamper code checks. Commercial anti-cheat solutions raise the cost, but none are perfect.

The realistic goal is not to make cheating impossible — it is to make it expensive enough that most players will not bother, and to catch enough of the ones who do that the honest majority has a good experience.

Layer 1: Server-Side Physics Validation

This is the most important layer and the cheapest to implement. Never trust the client’s position, damage, or state. Validate every action on the server.

On receive player input from client:
    expected_max_distance = player.move_speed * tick_duration
    actual_distance = |input.position - last_position|
    if actual_distance > expected_max_distance * 1.2:
        reject_input(input)
        flag_player("speed_hack")
        return

    if input.attack:
        if input.attack_range > weapon.max_range:
            reject_attack(input)
            flag_player("extended_range")
        if not has_line_of_sight(player, input.target):
            reject_attack(input)
            flag_player("wall_hack")

Every constraint your physics and game rules impose should be re-checked on the server. The 1.2x slop factor accounts for lag and interpolation. Exceeding the slop repeatedly is suspicious; exceeding it once might be a dropped packet.

Layer 2: Asset Hash Validation

Some mods work by editing game assets — replacing texture files to make walls transparent, swapping character models for giant versions, or modifying config files to change weapon stats. Detect these by hashing critical files on the client and comparing to a server-side manifest.

On client connect:
    files = ["data/weapons.json", "shaders/wall.glsl", "config/physics.ini"]
    hashes = {f: sha256(read(f)) for f in files}
    send_to_server(hashes)

On server:
    expected = load_manifest("version_1_2_0_hashes.json")
    for path, hash in client_hashes.items():
        if expected[path] != hash:
            flag_player("asset_tampering", path)

This catches naive mods but not sophisticated ones. A clever cheater can hook the file read to return the expected content while the game uses the modified version. Treat hash validation as a speed bump, not a wall.

Layer 3: Behavioral Heuristics

Some cheats are detectable not by what the client sends but by how the player behaves. Aim-bots produce unnaturally consistent reaction times. Wall-hacks produce unusual pre-fire angles. Speed hacks trigger in specific travel patterns.

Record player metrics on the server and look for outliers:

No single metric proves cheating, but a player who is above the 99.9th percentile on three of them is a suspect worth investigating. Flag, do not ban automatically.

Layer 4: Shadow Banning

When you catch a suspect, resist the urge to kick them. A kicked cheater immediately learns what they got caught for and adjusts. A shadow-banned cheater keeps playing, thinks they are getting away with it, and stops refining their hack.

Shadow banning is just matchmaking segregation. Flagged players get matched only with other flagged players. They see normal games. They do not see that their opponents all also have flags.

On matchmaking request:
    if player.flag_count >= 3:
        pool = "shadow_pool"
    else:
        pool = "normal_pool"
    find_match_in(pool)

Add a manual review queue so a human can confirm the flag before permanent action. False positives are real and banning an innocent player damages your reputation more than one cheater slipping through.

What Not to Do

Do not write your own kernel-level anti-cheat. It is a deep rabbit hole, players hate it, and you will break things. Leave that to EAC and BattlEye if your game justifies them.

Do not punish mods you explicitly allow. Single-player mods, cosmetic mods, and mods in private lobbies are fine. Your detection should scope to ranked or public multiplayer.

Do not publicly list ban criteria. Every specific detection you describe is a tutorial for how to avoid it.

Do not assume cheaters are adults. A lot of them are bored teenagers who will lose interest if cheating is boring. Make cheating boring: no satisfying effect, no visible reaction from opponents, no flash of success.

The Economic Question

For a small indie multiplayer game, server-side validation plus shadow banning is enough. For a competitive game with leaderboards, esports, or monetization, you need more. The rule of thumb: if cheating creates economic value for the cheater (skin drops, ranking, prize money), invest in detection proportional to that value.

For a chill co-op game with no rankings, the honest answer is that cheating barely matters and your time is better spent on features.

“You cannot win the cheat-detection arms race. You can make cheating expensive, tedious, and socially isolating. That is enough to keep your honest players happy — which is the actual goal.”

Related Resources

For broader multiplayer debugging, see how to debug dedicated server crashes. For handling anti-cheat false positives, see how to reduce false positive crash reports from anti-cheat. For dealing with unfair gameplay reports, see how to handle player reports of unfair gameplay.

Never trust the client. Every value the client sends must be sanity-checked on the server against the physical constraints of your game. Everything else is a bonus layer.