Skip to content
Lesson 15.10Defensive Design and Anti-Cheat

Designing a Game That Is Hard to Cheat In

Place authority, disclosure, retries, evidence, and permissions into the game's design from the beginning.

Lesson 15.10
Read or listenPrint chapter or book
About 8 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.10Key ideas, reference and questions

A one-page summary of “Designing a Game That Is Hard to Cheat In” you can keep beside your notes. Plain Markdown.

Lesson 15.10 — Designing a Game That Is Hard to Cheat In: cheatsheet

  • Remember: A repeatable decision and a recovery path protect honest players as well as the game.

Key terms

  • invariant: An invariant is a condition that must remain true across valid operations.
  • Idempotence: Idempotence means repeating an operation has the same effect as applying it once.

Review

  • Q: The server has already committed pickup C and score 1. The same request arrives again after a reply was lost. What preserves the invariant? A: Return the committed result without adding another reward
  • Q: What is an invariant? A: A condition preserved across valid operations
Download cheatsheet (.md)

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.
  1. Request Pick up coin C

    Step 1: Client to Server (score 0, coin C)

    The request carries an identity for this pickup.

  2. Reply Commit 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. Request Same pickup again (retry)

    Step 3: Client to Server (score 0, coin C)

    The client sends the same request with the same identity.

  4. Reply Stored 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.

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.

Final values: schedule = together; requests = 2; coin = consumed; reward = 5; checks_passed = 1; duplicate_refused = true.

Read every step of the lesson’s example
  1. Step 1, line 1. Start with the selected inputs and follow how the next operation changes the state.

    schedule = together.

  2. Step 2, line 2. Both requests name the same coin.

    schedule = together; requests = 2; coin = available; reward = 0.

  3. Step 3, line 3. The first accepted action consumes the coin before the next check.

    schedule = together; requests = 2; coin = available; reward = 0; checks_passed = 1.

  4. Step 4, line 4. Apply one reward effect(s) in this illustrated schedule.

    schedule = together; requests = 2; coin = consumed; reward = 5; checks_passed = 1.

  5. Step 5, line 5. The later request sees the consumed state.

    schedule = together; requests = 2; coin = consumed; reward = 5; checks_passed = 1; duplicate_refused = true.

  6. Step 6, line 6. Result: 5 reward. Validation must stay coupled to the state change; two valid earlier checks can still duplicate one effect.

    schedule = together; requests = 2; coin = consumed; reward = 5; checks_passed = 1; duplicate_refused = true.

Watch it happenApply a pickup once

Two requests for one coin produce only one reward.

Read the steps
  1. The server owns one coin, identified as C, and starts with score 0.
  2. A valid pickup request reaches the server. It checks availability and the player's right to collect C.
  3. Commit the reward and object consumption together. Zero plus one coin gives score 1.
  4. A repeated request arrives, perhaps because a reply was lost. Repetition alone is not proof of cheating.
  5. The server recognizes the already committed pickup. It returns the existing result without another reward.
  6. 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.

Knowledge check

One reward for one object

The server has already committed pickup C and score 1. The same request arrives again after a reply was lost. What preserves the invariant?

Finished Lesson 15.10?

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.