Designing a Game That Is Hard to Cheat In
Place authority, disclosure, retries, evidence, and permissions into the game's design from the beginning.
Cheatsheet for Lesson 15.10
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
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.
Check each condition where state changes
Section titled “Check each condition where state changes”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.
Make retries safe
Section titled “Make retries safe”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.
Request Pick up coin C
Step 1: Client to Server (score 0, coin C)
The request carries an identity for this pickup.
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.
Request Same pickup again (retry)
Step 3: Client to Server (score 0, coin C)
The client sends the same request with the same identity.
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 checksResult: 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
Step 1, line 1. Start with the selected inputs and follow how the next operation changes the state.
schedule = together.
Step 2, line 2. Both requests name the same coin.
schedule = together; requests = 2; coin = available; reward = 0.
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.
Step 4, line 4. Apply one reward effect(s) in this illustrated schedule.
schedule = together; requests = 2; coin = consumed; reward = 5; checks_passed = 1.
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.
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.
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.
Keep observations small and relevant
Section titled “Keep observations small and relevant”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.
Plan enforcement and recovery together
Section titled “Plan enforcement and recovery together”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.
Permissions remain part of the project
Section titled “Permissions remain part of the project”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.
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?
Idempotence preserves one effect across repeated delivery. The coin and reward were committed together. A normal retry gets the recorded result while score stays 1.
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.
Plain Markdown, saved in this browser.
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.