Brightness, reading palette, text size, comments and reading sound.Adds page background, hover cards, button corners, code colours, width and layout.Everything, including diagram styling, motion, inline code, depth and fine details.
Reading sound Off
Sound starts off. Play when you want it.
Original sounds, released into the public domain under CC0.
Margin comments
Hide author and reader comments without deleting them.
Page brightness
Lesson text size
Reading palette
More options
Reader finish
Modern adds semantic colours and refined controls. Soft depth adds frost and shadows.
Page background
Hover cards
Definitions, examples, tips, and lesson summaries that open over words and links. Off also removes the underlines.
Chat button
Which corner the button for asking questions about the book sits in. Top corners stop short of the lesson list and contents; at the bottom right it sits under the Notes button. It takes the colour of the chapter you are reading.
Notes button
Where the 📝 Notes button sits on lessons. Hidden leaves a thin tab on the edge; Alt + N brings it back.
Settings panel
Popover opens under the button. Side sheet slides in from the right with a dimmed page behind it.
Sidebar and header contrast
Original keeps the dark lesson list and header. Unified tints them to match the page and palette.
Reading width
How wide the lesson text column is. Standard is about 70 characters a line.
Table of contents
Light or dark for the “On this page” panel on the right, whatever the page is.
Lesson spacing
Code brightness
Syntax colours
Moving effects
System respects reduced motion while still advancing steps. On Play enables moving effects when you start a diagram. Off keeps manual steps.
Fine-tuning
Depth
Flat is the original. Soft adds faint shadows to diagram boxes, cards and buttons and lifts them slightly on hover.
Inline code
Classic keeps the badge look. Soft quiets the background and border for paragraphs full of keywords.
Floating buttons
Docked shows the Notes and chat buttons normally. Minimal makes them small icons that fade until you point at them.
Gradients
Use flat colours for headings, cards and diagram backgrounds.
Diagram background
Changes diagram surfaces; labels and role colours remain readable.
Diagram boxes
Heading rectangles
Diagram label backgrounds
Diagram frames
Diagram size
Page grid
Time per step
Semantic colours
Ligatures
What the colours mean
Terms, inputs, and data
Code and processing
Results and tips
Cautions
These colours follow your theme; their meanings stay the same. Data tables and diagram explanations use the information colour; code panels use processing. The lesson header and navigation identify its chapter. Correct answers are green; errors are red.
Alt + T palette · Alt + D page · Alt + C code · Alt + S syntax · Alt + H semantic · Alt + L ligatures · Alt + N lesson list · Alt + O on this page
15. Defensive Design and Anti-Cheat — print chapter
A multiplayer game asks several computers to agree on one world. Each
player controls a client, the program that sends their input and draws
their view. The player also controls the machine running it. That machine
cannot be the sole authority for shared positions, scores, and rewards.
Anti-cheat combines controls that prevent, detect, and respond to
unauthorized advantages in the shared game. It includes game design, server validation,
client protection, investigation, and enforcement. A scanner alone cannot
repair a game that accepts any score a client sends.
An authority boundary separates a request from the party allowed to
decide its result. In Lesson 9.10, a Source client predicts
movement, while the server owns the accepted simulation.
The server refuses a jump of 800The server knows the player was at x = 100 and allows at most 5 units per tick.
ClientServer: x = 100
1
Requestmove to x = 900
Step 1: Client to Server: x = 100
A request, not a new position.
2
Alone900 − 100 = 800 > 5: refuse
Step 2: Server: x = 100 (alone)
The step is measured from the server’s own position.
3
Replyyou are at x = 100
Step 3: Server: x = 100 to Client
The client’s view snaps back.
All 3 steps shown
The server knows the player was at x = 100 and allows at most 5 units per tick.
The server accepts an ordinary stepThe same check passes a request that fits the rules.
ClientServer: x = 100
1
Requestmove to x = 104
Step 1: Client to Server: x = 100
A small step.
2
Alone104 − 100 = 4 ≤ 5: accept
Step 2: Server: x = 100 (alone)
The server applies it to its own state.
3
Replyyou are at x = 104
Step 3: Server: x = 100 to Client
The server, not the client, decided that.
All 3 steps shown
The same check passes a request that fits the rules.
Consider a toy game with three points already earned. One uncollected coin
is worth one point. A client claims that its score is now 99. The server
ignores that claimed total. It checks whether the player’s simulated
position reaches the available coin. A valid pickup removes the coin and
adds its reward: 3 + 1 = 4.
Watch it happenRequests do not own the score
Change the simulation
Change the claimed score or pickup conditions. Watch the claim stop at the boundary; only an available coin reached in this toy world adds a point.
The claimed 99 is ignored. One accepted pickup adds one point. Score ends at 4.
Read the steps
The server starts with score 3. The client can request an action, but cannot replace that score.
A claimed score of 99 travels toward the boundary. The server does not use it.
The claim stops at the authority boundary. Score remains 3.
The server simulates the player reaching the coin position.
The valid pickup consumes the available coin and sends its one-point reward to the score update.
The final score is 4: 3 + 1. The claimed 99 never becomes the server's score.
This toy world starts with 3 points; an available, reached coin is worth one point.
This simulation needs JavaScript. The lesson text around it works through the same numbers.
The boundary prevents this score manipulation even if the client display
has been changed. Detection can still help explain the attempted request,
but protecting the result does not have to wait for an account ban.
An information advantage exposes something the player should not know,
such as an opponent behind a wall. An input advantage automates decisions
or timing that the game requires the player to control. A state advantage changes
movement, damage, inventory, or another accepted result.
Other problems involve message order and volume, account abuse, or the
game’s economy. A reward request arriving twice may be a normal retry after
packet loss. The game must prevent duplicate rewards before deciding
whether the repetition was malicious.
These categories overlap. They describe changes to available information,
control of actions, or accepted game state. An accessibility device and an automation tool
may both produce unusual inputs. Policy, supported controls, and the
actual advantage matter when interpreting them.
Prevention stops an invalid effect. A server rejects movement through
a solid wall or refuses a purchase without sufficient currency.
Detection identifies an observation worth investigating. A sequence
of inconsistent movement commands may trigger a recorded flag.
Deterrence makes abuse less attractive through clear enforcement policies
and credible consequences. Mitigation reduces harm while a case is being resolved.
These jobs need different evidence. A malformed request can be rejected
immediately. An irreversible accusation needs stronger support than an
unusual mouse movement. Publicly accusing a player also exposes them to
harassment, so a defender should separate internal investigation from
public messaging.
A false positive is an honest case incorrectly marked suspicious.
A false negative is cheating that the detector misses. Neither rate
can be understood by looking only at players already banned.
Telemetry is recorded information about a running system. Useful
telemetry includes the version of the check, server state, command timing, and
the reason a check failed. It lets an investigator reproduce a decision
and notice when a game update changes legitimate behavior.
An enforcement system also needs corrections and appeals. Keep the
evidence needed to review a case, limit who can access it, and stop
collecting data that does not help protect the game. This chapter studies
defenses in owned offline examples. It does not require testing a live
service or another player’s machine.
Lesson 15.2
Server Plugins, Counters, and False Flags
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.
1A 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.
2What 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.
All 2 steps shown
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
Change the simulation
Choose five held/released samples and the preceding state. Replay moves each actual sample into the comparison and updates the fresh-press count.
Five samples produce 2 fresh presses; 3 samples are held.
Read the steps
Start released. The five incoming samples are 0, 1, 1, 0, 1.
A released sample stores 0 as the previous state. It adds no press.
A transition from 0 to 1 adds one fresh press. Count becomes 1.
The next sample is still 1. The held input leaves the count unchanged.
A released sample stores 0 as the previous state. It adds no press.
A transition from 0 to 1 adds one fresh press. Count becomes 2.
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.
fnfresh_presses(samples: &[bool]) -> usize {
letmutprevious = false;
letmutpresses = 0;
for &heldinsamples {
ifheld && !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.
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.
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.
Lesson 15.3
What Client Protection Can See
Server validation protects accepted results. It cannot directly inspect
every change inside a player’s local process. Client protection adds
observations on that machine, including file integrity, loaded code, and
access to the game process.
User mode is the operating system’s restricted execution environment
for ordinary applications. A local protection component can compare game
files with trusted hashes, inspect loaded modules, or record permitted
process-access events. Its permissions and operating-system interfaces
limit what it can observe.
Each check sees one part of the machineA user-mode component works within the permissions Windows gives it.
1Files: the bytes on disk
game.exe
SHA-256 9F2C…41AB
=
trusted 9F2C…41ABmatch
The hash covers game.exe as read from disk, not later changes in memory.
2Modules: what is loaded in the game
Loaded
game.exe
hud.dllsupported overlay
helper.dllunknown
An unknown module is worth a look; it is not proof of a cheat.
3Access: who opened the game
Handles to game.exe
tool.exeread rights
crash reporterread rights
The same read rights, different purposes: check provenance and the game’s supported tools.
All 3 steps shown
A user-mode component works within the permissions Windows gives it.
A handle grants access to an operating-system object, such as a process.
An unexpected process with game-memory access can be relevant to an
investigation. A handle’s existence alone does not establish a cheat.
Debuggers, crash reporters, and permitted overlays can also interact with
processes. Check the access rights, provenance, and game’s supported tools.
Lesson 14.7 separates integrity checks from the trust in
the checking code. If a local checker and its reference are both under an
adversary’s control, its successful report cannot establish an independent
truth. Lesson 14.8 makes the same distinction for debugger
signals: an observation and an enforcement decision are different jobs.
Kernel privilege adds visibility and responsibility
The kernel is the operating system’s privileged core. A kernel driver
is code loaded into that environment to perform operating-system services.
It can observe or enforce some boundaries unavailable to an ordinary
application. See Lesson 12.9 for this trust boundary.
That privilege also increases the consequences of a defect. A bad memory
access can crash the system rather than just the game. A vulnerable driver
can expose dangerous operations to other software. Signing identifies an
approved publisher and protects the signed bytes against alteration; it
does not prove that their implementation is free of security bugs.
Microsoft’s vulnerable-driver blocklist illustrates an additional loading
policy. A known vulnerable signed driver can be refused even though its
signature is valid. An allowed driver still requires maintenance and
compatibility testing.
A signature and a vulnerability decision differ. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
read the selected inputs
evaluate each stated condition
combine the required conditions
perform the effect only when accepted
otherwise retain the old state
report the result and its limits
Result: this image passes the illustrated entry gate. That is not a proof of bug-free code.
A vulnerable signed driver stops while an allowed driver enters the kernel.
Read the steps
Both driver fixtures have valid signatures. One is also known to be vulnerable.
Signature verification alone would accept the vulnerable driver. The loading policy also checks the blocklist.
A matching blocked identity stops at the boundary. Its signature does not override that policy.
The other driver satisfies the fixture policy. Compatibility and update testing still matter.
Only the allowed driver enters this kernel. A blocklist helps reduce risk; it is not proof that every remaining driver is safe.
A synthetic loading policy illustrates signature and vulnerable-driver checks.
Trend Micro documented ransomware abuse of a vulnerable Genshin Impact
anti-cheat driver. The lesson is broader than one game: a privileged
security component can become a security liability if its exposed
operations are unsafe. Driver revocation, updates, constrained interfaces,
and operating-system protections all matter.
If an anti-cheat and an interfering tool both run in user mode, both are
subject to process handles, access rights, and the other Windows permission
checks. Running at the same processor privilege does not itself grant either
process access to the other. It does limit what either can establish about
system-wide events and privileged components from inside its own process.
A kernel component changes that balance. Running in ring 0 lets it use
documented Windows facilities that user-mode code cannot reach:
process and image-load notifications, so it learns about every new
process and every DLL or driver as it loads, instead of scanning for them
afterward;
handle-operation callbacks, which run while another process opens a handle
to the game and may remove rights from the request. A tool that asks for read
and write rights can receive a handle without them, and its
ReadProcessMemory then fails at the handle-rights check that
Lesson 12.4
followed;
a view of loaded drivers and system state that user-mode code cannot quietly
alter.
The costs described above fall on players. Anti-cheat code in the kernel has
the same authority as the rest of the kernel, so its bugs become bug checks on
players’ machines, and a vulnerability in it becomes a vulnerability in every
machine it is installed on. It asks players to trust a game company with ring-0
code. Kernel placement also creates another boundary to protect: which drivers
may load and what authority they expose. Windows uses signing rules, vulnerable
driver block policies, and memory integrity to reduce that risk.
Boot trust and attestation answer narrower questions
Loading protection at boot can help it observe the environment before the
game starts. Loading it with the game limits its lifetime. Neither timing
choice makes privileged code infallible. A product may support different
modes on systems with different security guarantees.
Secure Boot checks that the boot chain uses components authorized by
the configured signing policy. A Trusted Platform Module, or TPM,
can retain protected keys and record boot measurements. A measurement is a
record of what was loaded, commonly represented by cryptographic hashes.
Attestation is a verifiable report about measured system state. A
verifier checks the signing identity, expected measurements, and a fresh
challenge so an old report cannot be reused as a current one. The protocol
must also bind the report to the intended session. The hash and signature
distinction from Lesson 10.8 applies here.
Such a report can support a claim about a measured boot environment. It
does not prove that every later action is fair, or that all possible
software and hardware behavior was measured. Server-owned rules remain
necessary.
Protection should document its lifetime, privileges, collected information,
retention, and uninstall behavior. Limit collected data to its stated
purpose. Test updates against supported hardware and software, with a
recovery path for failures.
Privacy and reliability are engineering constraints. A protection system
that repeatedly crashes honest players or cannot explain its data use
weakens trust in the game it is trying to protect.
Lesson 15.4
How Detections Work
A detector turns observations into a signal. To assess that signal, ask
what was observed, what rule matched, and how often it makes mistakes in
the population where it runs. Its output is not automatically a verdict.
A signature detector matches a known pattern. A byte signature can
identify a known code sequence, as in Lesson 5.7. A file hash
can identify exact bytes. Matching a pattern is only as meaningful as its
uniqueness and the trust in the observation.
A heuristic is a rule based on an expected pattern rather than an exact
identity. Repeated input timing or an unlikely view change might trigger
one. Legitimate devices, accessibility controls, or gameplay changes can
also produce that pattern.
A behavioral detector studies actions over time. It may compare input
edges, aiming transitions, or movement consistency. A statistical model
describes the distribution of these observations. A machine-learning
model learns relationships from labeled examples. Both need evaluation
on legitimate cases outside the data used to build the model.
An integrity signal compares observed state with an expected reference.
Hardware and account signals describe another part of the environment or
its history. A new account or private profile is not itself proof of an
unfair game action. Combining signals requires attention to what each
actually measures.
The base rate is how common a condition is in the population being
tested. Use a hypothetical population of 100,000 players. Assume one in
1,000 cheats. Then 100,000 ÷ 1,000 = 100 cheat, and
100,000 − 100 = 99,900 are honest.
Now assume the detector catches 99 percent of cheaters and wrongly flags
1 percent of honest players. These are chosen teaching assumptions, not
claims about a real anti-cheat product.
Among the 100 cheaters, 0.99 × 100 = 99 are caught. The remaining
100 − 99 = 1 is missed. Among the 99,900 honest players,
0.01 × 99,900 = 999 are wrongly flagged. The remaining
99,900 − 999 = 98,901 are correctly left unflagged.
The flagged group therefore contains 99 + 999 = 1,098 people. Only
99 ÷ 1,098 ≈ 0.0902, or about 9 percent, are cheating.
Flagged players: how many are really cheating?Each dot stands for several players. Filled dots are flagged cheaters; open dots are flagged honest players.
Dots and the share of real flags
Each dot is 9 players
Counts
Flagged players
99 cheating11 filled dots
999 honest111 open dots
Chance that a flag is real
Cheating among all flagged
9.0%99 of 1098
99 of 1098 flagged players are cheating: 9.0%. With 9 players per dot that is 11 filled and 111 open dots.
Watch it happenCount the people behind a flag
Most flagged players are honest in this hypothetical rare-cheating population.
Read the steps
One in a thousand of 100,000 players gives 100 cheaters and 99,900 honest players.
Catching 99 percent of 100 gives 99. Wrongly flagging 1 percent of 99,900 gives 999.
The first eleven dots represent 99 caught cheaters: eleven times nine.
Another 111 dots represent 999 honest players: 111 times nine. They join the same flagged population.
There are 122 dots, representing 1,098 flags. Only eleven of those dots represent cheaters.
99 divided by 1,098 is about 9 percent. A flag needs further evidence before an accusation.
The rates are chosen assumptions, not estimates for a real game. The lesson derives every count.
The picture groups nine flagged people into each dot. Nine is chosen
because it divides both flagged counts exactly: 99 ÷ 9 = 11 dots for cheating,
and 999 ÷ 9 = 111 dots for honest players. Together they make 122 dots. It depicts
the flagged group, rather than every person in the original population.
This simulation needs JavaScript. The lesson text around it works through the same numbers.
Detection precision is the fraction of flags that are correct. In this example
it is about 9 percent, even though the detector catches almost all cheaters.
Detection recall is the fraction of cheaters caught: here 99 ÷ 100 = 99%.
The two numbers answer different questions.
A hard rule violation, such as an impossible purchase against server-owned
inventory, can be prevented immediately. A weak behavioral flag can send a
trace for review without publicly naming the player.
Rechecking with another observation can help, but its errors may be
correlated with the first check. Two models trained on the same mislabeled
examples are not two independent witnesses. Measure the combined decision
on real legitimate cases instead of multiplying error rates by assumption.
Delaying enforcement can allow further validation and group related cases.
It also leaves time for harm, so prevention and mitigation must continue.
Human reviewers need the trace and its context, clear decision criteria,
and a way to correct mistakes. A human verdict is not automatically
error-free either.
Lesson 15.5
Layers, Trust, and Review
A match can have several protection layers. The game validates actions against its authoritative state.
A platform provides account and service signals. A league may add its own
client requirements and review process. A community server may run plugins
with local policies. These layers need not see the same facts or impose
the same consequences.
The game server knows its accepted inventory and simulation. Client
protection sees parts of the local software environment. An account service
can know historical enforcement records. A replay reviewer sees whatever
the recording retained. Each observation has a time, scope, and source.
Provenance records where information came from and how it was obtained.
Keep the originating event identifier, game version, server state, and
detector version when forwarding a signal. Otherwise, a later service may
misinterpret a copied report as a new measurement.
Label a toy event A. The game logs it, a plugin forwards it, and a player
report references it. That creates three notifications. Their common
origin means they still describe one event.
Watch it happenMerge reports about the same event
Change the simulation
Change which event each delivery describes. Matching identities merge; a new identity adds a record. Record count alone cannot establish independence or guilt.
Three deliveries describe 1 event identities; a later B leaves 2 total. Distinct identities alone do not establish independence or guilt.
Read the steps
The system observes one event, labelled A.
Game, plugin and report deliveries carry A, A, A. Three notifications can describe one or several event identities.
Merge by event identity: A: 1 event. Repeated copies do not create more events.
A later delivery carries B. That identity is new; keep its own provenance and context.
The ledger now holds A, B: 2 events. Separate records still do not establish statistical independence or guilt.
A, B and C are chosen event identifiers, not confidence scores.
This simulation needs JavaScript. The lesson text around it works through the same numbers.
A later event B adds another observation. Now there are two distinct
events. Distinct does not automatically mean statistically independent:
both might have been affected by the same game bug or network condition.
Three notifications can describe one eventCounting reports is not counting events. Distinct events still need their own evidence.
1Event A happens once; three reports describe it
Reports of event A
game log
plugin forward
player reference
What was counted
notifications: 3
events: 1
The game log, a plugin forward and a player reference each mention the one event A.
2A later distinct event B makes two
Events
event A
event Bevents: 2
Two distinct events is a different count, but it does not prove they are statistically independent.
All 2 steps shown
Counting reports is not counting events. Distinct events still need their own evidence.
Correlation means observations vary together. When several detectors
share inputs or assumptions, their mistakes can occur together too. Count
unique events and evaluate the combined policy on legitimate cases. Do
not turn a copied signal into extra confidence merely by routing it
through additional services.
Suppose twenty observed cases contain four flagged only by signal A, two
flagged by both A and B, three flagged only by B, and eleven flagged by
neither. A flags six cases and B flags five. Their intersection is the
two cases shared by both sets. Their union is the nine distinct cases
flagged by at least one: six plus five minus the two counted twice.
This simulation needs JavaScript. The lesson text around it works through the same numbers.
P(B given A) asks a narrower question: among the six cases already
flagged by A, how many were also flagged by B? Here it is two out of six,
not two out of twenty. Changing the overlap changes that answer. These
counts describe an invented sample; they do not prove that any case is
cheating. Multiplying P(A) by P(B) gives the intersection only when the
events are independent, an assumption shared detector inputs can violate.
Matchmaking risk is different from a cheating verdict
A trust score summarizes risk or expected match quality for a policy.
It may guide matchmaking or prioritize review. It is not necessarily an
assertion that the account cheated in a particular match.
Valve’s published CS:GO Trust Factor explanation illustrates that
distinction: it describes matchmaking using account and play observations,
while leaving changing details undisclosed. An account score does not
replace an explanation of which check failed in an enforcement decision.
New accounts, shared hardware, changing devices, and unusual but permitted
play can complicate such signals. Keep the scope of a consequence tied to
the evidence and documented enforcement policy. A community server’s local removal is
different from a platform-wide sanction.
A console proves what software it booted with a signed report from its
security processor, the remote attestation that
Lesson 13.6
follows. A console running modified software cannot produce a valid report,
because the modified software cannot reach the signing key and cannot make the
boot measurements match software it is not.
Why ban the console rather than just ignore the report? A modified console can
run cheats in matches with other people, run pirated games, and send the
service messages that genuine software never would, so nothing it says can be
trusted. Banning it by its unique ID, and sometimes the account, protects other
players and the store. The ban is the service’s decision under the terms of
service the owner accepted, not a technical malfunction.
Attestation is one layer, not the whole defence. Well-designed online games
still check every action on the server, as Chapter 9 describes,
because no check on the client is perfect. A server that believes a console’s
claim about where a player is standing has the same weakness as one that
believes a PC’s.
A ban wave applies accumulated enforcement decisions in a batch.
Grouping cases can give investigators time to validate them and make
immediate detector feedback less revealing. The delay also allows further
harm unless other controls prevent or mitigate it.
Use durable case records and preserve the version of the check that led to each
decision. If an update causes a false-positive cluster, pause enforcement based
on the affected check, investigate its shared cause, and correct affected cases.
A general recovery process should exist before a large batch is applied.
Review queues need clear prioritization and limited data access. A report
can be valuable without being sufficient evidence for a ban. Repeated
reports from the same source or the same incident should not automatically
increase the penalty. The purpose of layering is to add useful coverage
and context, not to hide responsibility behind a majority of flags.
Lesson 15.6
Cheat Categories from the Defender's Side
A defender benefits from knowing where an unfair advantage originates.
That location affects which observations and protections are available.
It does not change who should own the game’s accepted results.
An external tool runs outside the game process. An internal tool
runs code inside it. Depending on permissions and operating-system
interfaces, a defender may observe process access, loaded modules, or
unexpected integrity changes. Legitimate development and accessibility
software can create some of the same observations.
Where the tool runs changes what a defender can observeBoth tools read the same health value; only the route differs.
1External: a separate process
game.exe
game.exemodule
health 100in memory
tool.exe
handleread rights
health 100its copy
tool.exe opens a handle and asks Windows to copy bytes. A debugger leaves the same kind of handle.
2Internal: code inside the game
game.exe
game.exemodule
helper.dlladded
A defender may see
a new module
changed code bytes
No handle is needed after loading, but the added module, and any bytes it patched, are inside the game. A supported overlay also loads a DLL.
All 2 steps shown
Both tools read the same health value; only the route differs.
Cheat Engine is a development and modding tool with memory inspection,
debugging, and scripting capabilities. Those capabilities can be useful in
an owned offline program, like the memory examples earlier in this book.
The presence of a general-purpose tool is different from proving an unfair
action in a protected match. Nor does changing client memory automatically
change the server’s inventory or simulation.
Do not assume that a project having publicly visible source settles every
license or distribution question. Check the actual components and current
terms before redistributing software. This lesson studies tool categories;
it does not ask you to install or use them against protected games.
Kernel-assisted abuse uses privileged operating-system code. It expands
the local access problem described in Lesson 15.3. Defenders can combine
driver policy, integrity protections, updates, and server validation.
These controls cover different parts of the problem.
Direct memory access, or DMA, lets hardware devices transfer memory
without having the CPU copy every byte. Lesson 13.1 explains
why device access needs isolation. An IOMMU, an input-output memory
management unit, restricts which memory regions a device can access under
the configured policy. Firmware and operating-system configuration affect
whether that protection is actually in place.
Neither greater software privilege nor device access creates a hidden
server value that was never sent. In the picture, the server sends the
player’s own health but excludes an unrelated hidden enemy. Observing the
client’s received memory yields only the available information.
Local inspection can reveal only delivered state. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
server keeps health and hidden position
select the outgoing fields
deliver only those fields
client stores its received copy
inspect the local fields
report available and absent information
Result: this client knows its own health and has no hidden-position field.
Final values: message = limited; sent_fields = health; health = 73; hidden_position = absent.
Read every step of the lesson’s example
Step 1, line 1. Start with the selected inputs and follow how the next operation changes the state.
message = limited.
Step 2, line 2. The server chooses which fields leave its process.
message = limited; sent_fields = health.
Step 3, line 3. The message carries no implicit copy of the rest of server memory.
message = limited; sent_fields = health.
Step 4, line 4. The local copy contains only the delivered fields.
The client receives its own health and no hidden enemy position.
Read the steps
The server stores both permitted local information and a hidden enemy state.
The outgoing update selects the player's own health. The hidden enemy is excluded.
Only that selected value crosses the network boundary.
Client memory gains the received health. It has no enemy position from this update.
Different observers of client memory can inspect available data. Greater local privilege does not create information never delivered.
Server-side disclosure rules complement local protection. The server must still account for information revealed in other updates, sounds, or timing.
The example compares information availability across one message boundary.
This is why local protection and server disclosure rules complement one
another. Audit other channels too. Sounds, event messages, timing, or team
updates can reveal information that one position filter withheld.
Screen-based automation changes the observation problem
Computer vision extracts information from images. A vision-based
automation system can act on the ordinary game view and emulate input.
It may not need access to the game’s internal memory.
The server still sees resulting commands and their timing. Behavioral
signals, supported-input policy, and review can contribute evidence.
Unusual input consistency also occurs with some permitted devices, so a
pattern alone needs legitimate-device testing and context.
Network abuse can involve invalid messages, repeated requests, excessive
work, or inconsistent session claims. Parse bounded input, authenticate
the session, validate actions against current state, and define retry
behavior. A cryptographically protected connection does not make the
authenticated player’s requests truthful.
An asset is stored game content such as a model, texture, or sound.
Unauthorized replacement can change the information or visibility a game
intends to provide. A server can require an approved asset set where the
rules call for it. Use signed manifests and trusted references rather than
letting the client select both the content and the expected checksum.
See Lesson 10.8.
Permitted mods have a separate policy and supported loading path. They
should not be silently treated as cheating merely because their bytes
differ from the stock installation. Define the protected mode, authorized
extensions, and review procedure before interpreting a local mismatch.
Lesson 15.7
Control Gaps in Toy Defenses
The earlier chapters showed how a value, hook, or object layout can
change while a game still appears to run normally. The next question is
whether a check guards the effect it claims to guard. A perfect module
inventory cannot rescue a rule that checks the wrong path. Three deliberately
weak rules in a small program make that gap visible before we examine game
logic controls and further trust-boundary failures.
Research the failure without targeting a real defense
This lesson uses a tiny program with no Windows APIs, drivers, processes,
networking, persistence, or security-product interaction. The “attacker” is a
test function calling an intentionally bad policy. That lets us study the
computer-science shape of an evasion while keeping the recipe useless against
a real anti-cheat or endpoint defense. 🧪
Ask the weak policy about WriteMemory; it says no.
Ask about ApplyPatch; it says yes.
Observe that the second spelling reaches the same kind of state-changing
effect the policy claimed to prevent.
A read-only policy that checks names instead of effectsThe toy policy blocks WriteMemory by name. ApplyPatch changes state just as much, and the name check lets it through.
1The policy blocks one name
Policy by name
WriteMemory
name: blocked
changes state
ApplyPatch
name: allowed
changes state
It lists WriteMemory as forbidden. ApplyPatch is not on the list, so the policy allows it.
2Both commands change state
Policy by name: one gap
WriteMemory
name: blocked
changes state
ApplyPatch
name: allowed (wrong)
changes state
The invariant says no state changes, so the name check missed one of them.
3Repair: classify the effect
Repaired: policy by effect
WriteMemory
effect: blocked
changes state
ApplyPatch
effect: blocked
changes state
The repaired invariant looks at what a command does, so any state-changing command is refused.
All 3 steps shown
The toy policy blocks WriteMemory by name. ApplyPatch changes state just as much, and the name check lets it through.
The weakness is coverage by spelling. Adding more forbidden names would
become an endless denylist. Classify the effect instead:
Look at what changed about the question being asked. The weak version asked
“which command is this?”, which has as many answers as there are names. The
enum asks “what does this command do to state?”, and that has exactly two
answers. A question with a fixed number of answers can be answered completely;
a question about names cannot.
effect_of is now the single place that judgement lives. Adding a command to
the lab means classifying it there, and the match makes that unavoidable —
leave the new variant out and the code does not compile. The denylist that
would have needed extending forever has become a decision the compiler insists
you make once.
The original policy blocks only the command named WriteMemory; every other name passes, even when writes are disabled. The final if is the call-site fragment inside the lab’s command handler: it shows the shared execution boundary where that decision is used.
command != LabCommand::WriteMemory// Ignores the setting and other write commands.
}
ifpolicy(command, settings.writes_allowed) {
execute_at_shared_boundary(command)?;
}
Classify the effect of each command once, then apply the setting to every state-changing effect. The enum and exhaustive match keep new command variants from silently falling outside the rule.
In the supplied test hook, replace the build ID with 8.
Observe that the weak function still writes 0xCC because it uses an old
decision.
This is a time-of-check/time-of-use gap. The repair validates the build and
expected old byte immediately beside the side effect:
iftarget.build_id != expected_build {
returnErr("wrong build");
}
iftarget.byte != expected_byte {
returnErr("state changed");
}
target.byte = replacement;
For a real game process, Windows can still change the target between
two calls. You cannot make another process freeze by wishing. Keep the interval
small, compare expected bytes, report mismatch as a normal refusal, and make
the operation reversible.
This interactive lab needs JavaScript. The lesson text around it still explains the same idea.
Recipe 3: test a state-changing branch the logger misses
A reviewer looking only at the log cannot distinguish “nothing happened”
from “a prohibited request was blocked.”
This is a telemetry gap, not a control bypass. It still matters because a
denial that leaves no event hides repeated mistakes and makes the control
difficult to test. The fix computes the decision first and then logs it,
whichever way it went:
Do not log secrets, raw memory, authentication keys, or giant payloads. A useful
event records time, operation category, verified target identity, allow/deny
decision, and a bounded reason code.
Published evasion write-ups are usually operational recipes. Underneath the
commands, each one describes the same parts, and this lab’s bypass contains
every one of them:
Part
In this lab
The promise the control made
Read-only mode prevents state change
What the control actually checked
One command name
The equivalent effect it missed
Another command reached the same sink
The observable result
The weak policy returned true for ApplyPatch
Where the invariant belongs
At the shared state-changing boundary
The regression test that keeps it fixed
Every ChangesState variant is denied in read-only mode
Described by its parts, a bypass is a gap between what a control promised and
what it checked. That is something a defender can close, and nothing in the
description is a step that could be aimed at someone else’s machine.
No program can directly read a person’s intent. A detector measures observable
signals—for example a debug flag, an unusual timing gap, a changed byte, an
unexpected handle, or a forbidden state transition—and applies a rule. That
rule can be wrong in two directions:
Outcome
Plain-English meaning
True positive
The measured signal and prohibited condition are both present
False positive
A harmless tool or environment produces the same signal
True negative
Normal behavior is accepted
False negative
The prohibited effect occurs without the expected signal
This is why a strong repair protects the important effect boundary instead
of trusting one environmental clue. In the toy lab, “state must not change in
read-only mode” is an invariant. A process name, timing threshold, or single
flag is only a signal that often accompanies a violation; it is not the
invariant itself.
The four-outcome table above is called a confusion matrix. Testing a rule
against a controlled fixture fills it in one condition at a time: attached and
detached runs, slow and fast machines, and the diagnostic tools a legitimate
user is expected to run. A rule that fires on a legitimate debugger or profiler
lands in the false-positive row, and that is a compatibility bug worth fixing
rather than proof that every observer is hostile.
Use signal families as a checklist for honest tests and false-positive review,
not as a list of concealment tricks. The goal is to understand what the detector
actually measures and then protect the real invariant at the shared boundary.
A name can change, an earlier validation can go stale, and a denied decision
can disappear from the record. Authorize the effect at its shared boundary,
record both allowed and denied outcomes, and keep the failed case as a
regression test. The toy boundary makes each failure observable without
conflating it with the behavior of a real game’s defenses.
Lesson 15.8
Control Gaps in Game Logic
The next three examples each fit in one small state model. For each, identify
the invariant and canonical state, then compare a checksum or environment
signal with the decision telemetry and the effect. A paired regression test
should show both the control gap and the legitimate behavior a repair must
retain.
Every control in this lesson makes a promise, and all three break it the same
way: each one checks something near the thing it cares about instead of the
thing itself.
A greyed-out button stands in for “this player is allowed to do that.” A
checksum over one field stands in for “this record is intact.” A debugger flag
stands in for “this request is honest.” Every stand-in is easier to check than
the real property, which is precisely why someone reached for it — and every one
of them can be true while the real property is false.
Once you see that shape, the repair stops being a guess. It is never “add more
stand-ins.” It is to move the check to the place where the effect actually
happens, and demote the stand-in to a clue that helps explain what went wrong.
The engine checks an award at the effect boundary. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
ui_unlocked = true; bonus = 0
read engine-owned permission
candidate = bonus + amount
require permission and candidate <= 100
apply only the accepted candidate
emit the decision and final bonus
Result: EnginePolicyDenied, bonus=0. The same unlocked button appears beside both accepted and rejected effects.
Presentation state answers “should the button look available?” It does not
answer “may the simulation perform this effect?” Multiple inputs, scripts, and
restored UI state can reach the same engine operation. The weak control places
authority in a copy used for presentation.
The button is greyed out, the command still runsThe engine policy says no awards. The weak award_bonus handler never reads it.
ScriptHandlerUI flagEngine
1
Requestaward_bonus(25)
Step 1: Script to Handler
A script calls the command directly, without the button.
2
Controlread ui_button_unlocked: true
Step 2: Handler to UI flag
Restored UI state left the presentation flag true.
3
Databonus 0 → 25
Step 3: Handler to Engine
The effect happens while the policy is false.
All 3 steps shown
The engine policy says no awards. The weak award_bonus handler never reads it.
The handler checks the state it is about to changeThe same call meets the engine policy at the effect boundary.
ScriptHandlerUI flagEngine
1
Requestaward_bonus(25)
Step 1: Script to Handler
The same direct call arrives.
2
Controlread engine policy: no awards
Step 2: Handler to Engine
The UI flag is ignored; the engine’s own policy decides.
3
ReplyEnginePolicyDenied, bonus stays 0
Step 3: Handler to Script
Refused with a reason, and nothing changes.
All 3 steps shown
The same call meets the engine policy at the effect boundary.
The repaired method ignores presentation state at the effect boundary. It
checks the engine policy, uses checked addition, enforces the maximum, and
emits one of EnginePolicyDenied, ValueOutOfRange, or Allowed.
The regression test keeps the UI flag true and proves the repaired method
denies the operation without changing bonus. A second test proves a valid
engine policy still permits a bounded award.
Recipe 2: a checksum covers only the convenient field
StatsRecord contains player ID, health, maximum health, generation, and a
checksum. The weak checksum covers only health:
constfnweak_checksum(health: u16) -> u32 {
u32::from(health).wrapping_mul(31)
}
The promised invariant is: the identity and generation match the same record,
the health range is valid, and every field used by that relation is covered.
The checksum answers only, “Did the health field change?” Changing
maximum_health can make the relation impossible while the stored weak
checksum still matches.
player_id7
health80
max_health100
generation3
checksumweak_checksum(80)
The weak checksum reads only health. Changing max_health from 100 to 60 breaks health <= max_health, but the stored checksum still matches — it never looked at max_health.
The repaired relation covers player ID, health, maximum health, and generation,
then separately checks health <= maximum_health. Emit a specific failure such
as ChecksumMismatch or OutOfRange; do not compress every failure into one
boolean in production code.
The regression suite mutates every covered field individually and asserts that
the complete relation fails. It also keeps a valid record to prove verification
is not permanently closed.
This checksum detects example-data corruption; it is not authentication. If the
system needs to defend against a party who can recompute an unkeyed checksum,
the design needs an authenticated owner and key lifecycle, not a more obscure
formula.
Recipe 3: one environment signal substitutes for command validation
The flag describes the environment, not the command. Even a perfectly measured
observer signal cannot prove that an unrelated health value is in range. The
weak rule therefore has a large ordinary path where invalid data is accepted.
Begin with health 80 and a maximum of 100. The weak function sees an ordinary
environment signal, accepts a request for 500, and leaves the game in an
invalid state. Both assertions should pass even though the invariant fails.
The repaired method creates two independent events:
an environment-observation event records whether the flag was present;
a command-decision event validates value <= maximum_health.
An observer flag can still help diagnose a run, but it neither grants nor
removes the health invariant. Tests cover all four combinations of ordinary or
flagged environment with valid or invalid values.
All three mistakes confuse a proxy with the property that actually matters.
The repair is not “add more proxy checks.” It is to enforce the invariant where
the effect becomes real, then use proxy signals as supporting telemetry.
Proxy: an observable value used as indirect evidence for another
property.
Coverage bypass: a meaningful change outside the data examined by a
control.
Effect boundary: the shared point where a requested state change becomes
canonical.
Negative regression test: proves the previously failing case is refused.
Positive regression test: proves valid behavior still works after repair.
Contradiction: evidence that a control decision and resulting state cannot
both satisfy the promised invariant.
The three toy failures differ in their proxies, but each lets an effect happen
without checking the promised invariant at the shared boundary. Each repair
keeps both sides of the evidence: the formerly accepted invalid case is
rejected, and valid behavior still works. Lesson 12.10 applies the same rule to
eight more failure modes.
Lesson 15.9
Anti-Cheat in Modern Games
This comparison was checked on October 5, 2026. Products change their
requirements, supported games, and detection systems. The examples show
documented architecture, rather than a ranking or a guarantee that any
particular match is free of cheating.
Riot describes Vanguard as a game-time client component and a kernel
driver. Its traditional mode starts the driver at boot so protection can
observe the environment before the game client starts.
On June 24, 2026, Riot announced optional on-demand sessions for
sufficiently secured PCs. In this mode, protection starts with a Riot
game and runs while it is being played. The published prerequisites include
an appropriate Windows version, Secure Boot, a TPM, virtualization-based
security, code-integrity protection, and an IOMMU. Boot-managed operation
remains a different mode; it is inaccurate to describe every Vanguard
installation as always starting at boot.
VALORANT also illustrates why client protection cannot replace server
design. Its server-side visibility policy limits hidden information sent
to a client. Local privilege and server disclosure are separate controls.
A kernel component’s privilege describes what it may access. Its lifetime
describes when it runs. The two properties are separate. The diagram
compares illustrative boot-managed and session-managed lifetimes. Its
animation time is slowed for viewing, rather than measuring a real boot.
When a component runs defines its observation window. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
boot-start component begins at boot
game-session component begins with the game
choose before, during or after the game
ask which selected component is active
stop the session component with the game
report the observation window
Result: selected component active at this moment. Its data access still needs a separate capability explanation.
Final values: lifetime = session; moment = during; starts = game start; active = true; stops = game exit.
Read every step of the lesson’s example
Step 1, line 1. Start with the selected inputs and follow how the next operation changes the state.
lifetime = session; moment = during.
Step 2, line 2. The two components have different start and stop boundaries.
lifetime = session; moment = during; starts = game start.
Step 3, line 3. Inspect the selected point in time.
lifetime = session; moment = during; starts = game start.
Step 4, line 4. Being active is separate from having permission to read a particular source.
lifetime = session; moment = during; starts = game start; active = true.
Step 5, line 5. The session ends with the game; the boot-long component remains in this schedule.
lifetime = session; moment = during; starts = game start; active = true; stops = game exit.
Step 6, line 6. Result: selected component active at this moment. Its data access still needs a separate capability explanation.
lifetime = session; moment = during; starts = game start; active = true; stops = game exit.
Watch it happenCompare protection lifetimes
One monitor starts at boot; another starts and stops with the game.
Read the steps
At boot, the boot-managed monitor is active. The game and session-managed monitor are not yet running.
Starting the game activates the session-managed monitor too.
During play, both examples are active. Their bars record when each service ran.
The game stops. The session-managed monitor stops, while the boot-managed example remains active.
The ongoing boot bar and ended session bar explain why protection lifetime and privilege are separate choices.
Illustrative lifetimes, not measured startup durations. Products can offer different modes.
The bars preserve the period each component was active. Ending a
session-managed monitor when the game stops does not mean it had lower
privilege while the game was running.
When protection is active, relative to the gameIllustrative spans, not measured product timelines: one protection starts at boot, the other with the game.
1Boot-managed protection starts at boot
Observation window
bootend of window
before the game
game running
after the game
Boot-managed protection
active from boot, past the game’s end, to the window’s edge
The game runs for part of the window. This protection is active before it starts and after it ends, up to the window’s edge.
2Session-managed protection follows the game
Observation window
bootend of window
before the game
game running
after the game
Session-managed protection
active only while the game runs
It starts and stops with the game. Both may have kernel privilege while active: they differ in when, not in how privileged.
All 2 steps shown
Illustrative spans, not measured product timelines: one protection starts at boot, the other with the game.
VAC, Valve Anti-Cheat, contributes client-side detection and server
enforcement of VAC status. The game still needs its own validation and
review systems. The public material does not provide a complete recipe for
VAC or VAC Live detections, so do not infer one from a product label.
Valve’s August 19, 2024 update described initial limited-match testing of
VacNet 3.0 and asked for details of incorrectly cancelled matches. That
dated announcement does not establish a universal current rollout or a
measured detection rate.
Valve’s April 25, 2024 Counter-Strike 2 notes added an Overwatch system
for match-demo review by trusted partners. This is a dated description of
review support, not a promise that every account can access the old CS:GO
review interface.
The earlier CS:GO Trust Factor documentation describes matchmaking
using account and play observations, with changing factors left
undisclosed. Matchmaking risk, detection, and a reviewed sanction are
different decisions. A historical FAQ cannot establish today’s internal
CS2 scoring formula.
Activision’s current description combines server-side systems and
investigation with a PC kernel driver. It states that the driver operates
only while a protected Call of Duty title runs, and monitors software
interacting with those titles.
Server-side protections can also affect console players through shared
services and cross-play. The presence of a PC driver does not imply that
every participant uses the same local component.
Epic’s Easy Anti-Cheat integration through Epic Online Services supports
client-server and peer-to-peer architectures. In client-server mode, game
network code carries opaque messages between the client and server
protection modules. Opaque means the game transports the data without
interpreting its internal format. The developer still owns game-rule
validation and must integrate the required session and message lifecycle.
BattlEye describes user-mode and kernel-mode protection, proactive
prevention, and detection. These descriptions establish parts of its
design, rather than an independent measurement of how well it protects
every supported title.
A useful comparison also includes supported platforms, privacy, reliability,
appeals, and the game’s own authority model. Public ban counts lack the
undetected cases and legitimate population needed to calculate precision
or recall. Lesson 15.4 explains those missing denominators.
Lesson 15.10
Designing a Game That Is Hard to Cheat In
The strongest defense often removes an opportunity before a detector is
needed. Decide who owns each result, what a client may learn, and what
must happen when a request is repeated or delayed.
An invariant is a condition that must remain true across valid
operations. Examples include an item being consumed at most once, a
balance remaining within its permitted range, or a purchase granting only
the reward paid for.
Check these conditions beside the state they protect. A user-interface check can
improve feedback, but it cannot replace the server’s purchase validation.
An authenticated session establishes the requester; it does not establish
that every requested action is permitted.
Check object ownership and current state when applying an action. A valid
earlier snapshot can become stale before the request arrives. A pickup
observed by two players still represents one available world object.
Idempotence means repeating an operation has the same effect as applying
it once. Network retries are common when a reply is lost. Reward operations
need an identity and a committed result so the same request cannot earn
another reward.
The toy server owns one coin, labelled C, and begins at score zero. The
first permitted pickup consumes C and adds one point: 0 + 1 = 1. A later
delivery repeats that pickup. The server finds its committed result and
returns it without adding another point.
A repeated pickup returns the stored resultThe first pickup commits consumption and score 1 together. A retry carrying the same identity gets that result back, so the score stays 1.
ClientServer (score 0, coin C)
1
RequestPick up coin C
Step 1: Client to Server (score 0, coin C)
The request carries an identity for this pickup.
2
ReplyCommit C and score 1; reply is lost
Step 2: Server (score 0, coin C) to Client
Consuming the coin and raising the score are committed together. The reply is lost or delayed, so the client cannot tell whether it worked.
3
RequestSame pickup again (retry)
Step 3: Client to Server (score 0, coin C)
The client sends the same request with the same identity.
4
ReplyStored result: score 1, no second reward
Step 4: Server (score 0, coin C) to Client
The server recognises the identity and returns the committed result instead of paying again. The score stays 1.
All 4 steps shown
The first pickup commits consumption and score 1 together. A retry carrying the same identity gets that result back, so the score stays 1.
Check and consume a coin as one accepted action. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
coin exists; reward total = 0
receive two requests for the same coin
check current coin state at acceptance
consume the coin with the accepted reward
later request sees the consumed state
report effects, not only successful checks
Result: 5 reward. Validation must stay coupled to the state change; two valid earlier checks can still duplicate one effect.
Two requests for one coin produce only one reward.
Read the steps
The server owns one coin, identified as C, and starts with score 0.
A valid pickup request reaches the server. It checks availability and the player's right to collect C.
Commit the reward and object consumption together. Zero plus one coin gives score 1.
A repeated request arrives, perhaps because a reply was lost. Repetition alone is not proof of cheating.
The server recognizes the already committed pickup. It returns the existing result without another reward.
Two deliveries have caused one accepted action. Score stays 1 and the coin remains consumed.
C is a chosen object identifier. The toy score begins at zero.
Commit the reward and consumption together. If another request can run
between the availability check and consumption, both might observe an
available coin. Use the appropriate transaction or synchronization for
the owning system. Remember that a transaction’s scope must include all
state needed to preserve the invariant.
Repeated delivery here explains a normal network condition. Handling it
safely is better evidence of sound design than treating every retry as a
reason to ban its sender.
Construct each client’s permitted observation on the server. Include
information needed for responsive presentation, but exclude unrelated
hidden state. A label hidden on screen is not information removed from
client memory.
Bound parsing, queued work, history, and requests per client. Use explicit
overflow and disconnect policies. Test normal delays, packet duplication,
lost replies, and concurrent actions in a synthetic offline harness.
Those cases expose trust and lifetime errors without requiring a real
cheater or a live service.
Keep authoritative records distinct from predictions and presentation.
That distinction lets a reviewer understand whether a mismatch changed the
shared world or merely corrected a local view.
Record the reason, version of the check or policy, and context for a decision. Minimize
personal data and restrict access to case records. Preserve enough
information for a correction without retaining unrelated private content.
Start new heuristic detectors in an observation mode against representative
legitimate play. Inspect error clusters after game and device updates.
Have a way to stop enforcement from a faulty detector and correct its consequences.
Deploying more privileged protection does not remove that responsibility.
Keep learning experiments inside programs, captures, servers, and mods
you own or have explicit permission to test. Written scope should identify
the systems, allowed data, permitted actions, and when testing stops.
Do not collect other players’ private data to make a lesson example.
Technical access does not settle copyright, license, contract, privacy,
or computer-access questions. These depend on the applicable rules and
permissions. Verify the actual license and terms for the work you intend
to distribute; seek appropriate advice for consequential legal decisions.
The defensive designs here do not require claims about the outcome of a
particular lawsuit or permission to interfere with an online match.
If you discover a defect in an authorized test, preserve a minimal
reproduction and report it through the owner’s stated process. Share only
the data needed to explain the problem. A reliable fix, bounded evidence,
and a clear permission boundary complete the engineering work.