Skip to content
Lesson 15.2Defensive Design and Anti-Cheat

Server Plugins, Counters, and False Flags

Read Macrodox and other SourceMod designs as measurement systems, separating button edges, weighted history, and enforcement.

Lesson 15.2
Read or listenPrint chapter or book
About 10 minutes

Keyboard shortcuts

?
Open this guide
Alt + B
Show or hide the lesson list
Alt + O
Show or fold the lesson outline
Alt + N
Open or close notes
Alt + H
Toggle semantic colours
Alt + D
Toggle page brightness
Alt + C
Toggle code brightness
← →
Move through a focused code trace
Home End
First or final step in a focused trace
Escape
Close this guide or an open panel

Reader shortcuts pause while you type in a text field.

Cheatsheet for Lesson 15.2Key ideas, reference and questions

A one-page summary of “Server Plugins, Counters, and False Flags” you can keep beside your notes. Plain Markdown.

Lesson 15.2 — Server Plugins, Counters, and False Flags: cheatsheet

  • Remember: A recent-history array, a weighted score, and a consecutive counter are different measurements. Define which one a rule uses.

Key terms

  • rising edge: A rising edge is that transition from zero to one.
  • usize: Each bool sample is released (false) or held (true), and previous starts released; usize is the returned count.
  • threshold: A threshold is a chosen cutoff for a decision.

Review

  • Q: A weighted measurement uses (old times 9 plus sample) divided by 10. Starting from 0.5, a new sample of 1 gives which value? A: 0.55, preserving nine-tenths of the previous measurement
  • Q: What does a SourceMod server plugin observe directly? A: Commands and events reported by the engine
Download cheatsheet (.md)

SourceMod lets a Source server run plugins that observe game events and player commands. A plugin is code loaded through the server’s supported extension system. It sees the engine’s reported state and the inputs reaching that server. It does not gain a trustworthy view of all client software.

The command boundary from Lesson 9.10 explains both its power and its limits. A plugin can compare movement requests with accepted state. It cannot conclude which program created a request from its timing alone.

The inspected Macrodox version is 1.9, from the source supplied for this book revision. Its original forum discussion describes a detector built for Counter-Strike: Source bunnyhop servers. Bunnyhopping chains jumps to maintain movement speed under a game’s movement rules.

Two sources, the same five commandsEach command carries the jump button’s state. Nothing in it names the program that produced it.
  1. 1 A player and a script send the same bits

    A player pressing the key

    0cmd 1
    1cmd 2
    1cmd 3
    0cmd 4
    1cmd 5

    An automation script

    0cmd 1
    1cmd 2
    1cmd 3
    0cmd 4
    1cmd 5

    Two key presses and a scripted schedule can both produce 0, 1, 1, 0, 1.

  2. 2 What the server can compute

    New press? (0 → 1)
    0cmd 1
    +1cmd 2
    0cmd 3
    0cmd 4
    +1cmd 5
    Known about the source
    fresh presses: 2
    which program: unknown

    Two rising edges for either source: an observation to explain, not the name of a program.

Each command carries the jump button’s state. Nothing in it names the program that produced it.

Macrodox remembers whether a player’s jump button was already held. A new press increments its counter only when the input changes from released to held. Repeated commands with the same held button do not each become a new press.

For the five toy samples 0, 1, 1, 0, 1, zero means released and one means held. The first 0 → 1 adds one press. The second consecutive one adds none. The later release allows the final 0 → 1 to add another. There are two fresh presses, because there are two rising edges.

Watch it happenCount presses rather than held commands

Five samples produce 2 fresh presses; 3 samples are held.

Read the steps
  1. Start released. The five incoming samples are 0, 1, 1, 0, 1.
  2. A released sample stores 0 as the previous state. It adds no press.
  3. A transition from 0 to 1 adds one fresh press. Count becomes 1.
  4. The next sample is still 1. The held input leaves the count unchanged.
  5. A released sample stores 0 as the previous state. It adds no press.
  6. A transition from 0 to 1 adds one fresh press. Count becomes 2.
  7. 2 transitions from 0 to 1 produce 2 fresh presses. Counting all held samples instead gives 3; these counts answer different questions.

These five samples are a toy trace, not a ban threshold.

A rising edge is that transition from zero to one. Separating levels from edges matters in ordinary game input too. A held key can span many commands while describing one physical press.

Optional: practise counting rising edges with this toy function. Each bool sample is released (false) or held (true), and previous starts released; usize is the returned count.

fn fresh_presses(samples: &[bool]) -> usize {
let mut previous = false;
let mut presses = 0;
for &held in samples {
if held && !previous {
presses += 1;
}
previous = held;
}
presses
}

The five samples [false, true, true, false, true] return 2. Keeping a button held does not add another press.

At a game-reported jump event, Macrodox records the counted presses and horizontal speed. It retains a circular array of recent jump counts. A circular array reuses its oldest slot when the write position reaches the end. The inspected declaration reserves 30 slots; that is a chosen storage capacity, not a universal detection rule.

Other measurements use a weighted average, which gives older and newer observations different influence. The source updates these averages with (old × 9 + sample) ÷ 10. Dividing each term by ten shows the weights: 0.9 × old + 0.1 × sample.

For an illustrative old value of 0.5 and a new sample of 1, the result is (0.5 × 9 + 1) ÷ 10 = 5.5 ÷ 10 = 0.55. The older value still contributes. This is not a ratio computed over exactly the last fifteen jumps.

The perfect-jump measurement samples fresh presses using the previous and current ground state. A press on the transition to grounded contributes one; a press after remaining grounded contributes zero. Other command states do not automatically contribute another sample. The circular log and this weighted measurement therefore have different meanings.

Context can change an apparently perfect pattern

Section titled “Context can change an apparently perfect pattern”

The Macrodox discussion warns against combining it with automatic-jump plugins. Such a plugin changes legitimate input behavior. Map conditions and timing also affect what the detector observes. A goal of eliminating false positives does not establish that the goal was achieved.

The inspected code queues flagged identities in a file and applies actions on configured map cycles. Persisting an identity helps a server restart retain a decision. It does not strengthen the underlying measurement. Before an irreversible action, preserve the relevant trace, map, settings, and detector version for review.

This simulation needs JavaScript. The lesson text around it works through the same numbers.

Other plugins expose the same maintenance problem

Section titled “Other plugins expose the same maintenance problem”

SourceMod Anti-Cheat, or SMAC, divides checks into modules. An inspected SMAC fork’s February 2025 changelog removed a console-variable check after a game update because the older rule could falsely ban players. A detector must follow the rules of the game version it is protecting.

Little Anti-Cheat, also called Lilac, documents a case where scroll-wheel attack input can trigger an autoshoot heuristic. Its original repository was archived in September 2024. An archived detector can teach a design, but it does not promise maintenance for later game updates.

The supplied CowAntiCheat case combines command counters, view changes, server state, and client-reported settings. A client report must never be allowed to disable a server check. A missing or unusual report calls for separate handling. It does not prove either innocence or cheating.

A threshold is a chosen cutoff for a decision. Test thresholds against legitimate trace data, including permitted devices and server settings. Permanent bans and public accusations need more support than a heuristic crossing its cutoff. These case studies explain measurements and their failure modes; their implementation and tuning recipes are not reproduced.

Knowledge check

Read the measurement correctly

A weighted measurement uses (old times 9 plus sample) divided by 10. Starting from 0.5, a new sample of 1 gives which value?

Finished Lesson 15.2?

Mark it done and the lesson list shows a beside it. A chapter whose lessons are all done is marked done too. Progress is saved in this browser only.

Chapter 15: 10 lessons

This book grew out of the original Game Hacking Academy by attilathedud and has been rewritten as a beginner-first course. Like the original repository, its source is published under the Apache License 2.0.