- Find: text search, grouped search, undo, went up/down by, where to search, before/after compare - Debug tab: breakpoints, stepping, registers, stack, threads, what code touches, structure compare, pointer re-check - Engine tab: Unreal, Unity, Godot, GameMaker and C++ class names; hidden settings and switches - Code cheats with labels, variables, references and shared code; rule builder; script commands - Cheats kept on by the PS5 without the browser - Learn section with twelve lessons and an in-page practice game - Section (master code) community cheats; SHN and MC4 export - Groups, dropdowns, hotkeys, first-cheat guide, re-find after update, restore everything - MCP server: 25 tools - Quick start, wiki, contributor and security guides, changelog
Lessons for CheatSpecter's Learn section
This folder is what the Learn page in CheatSpecter shows. The app does not ship with
lessons: when someone opens Learn and presses "Load lessons", their browser downloads
index.json and then each lesson from this folder on GitHub. Change a file here and everyone
gets the new version the next time they open the page.
Lessons are data, never code. A lesson cannot contain HTML, scripts, links or images. The app checks every file it downloads against the format below and refuses the whole file if anything is off, so a typo here means a lesson that will not open. Run the checker before you commit:
node scripts/check-lessons.mjs
It prints what is wrong and where (lessons/find-a-number.json: lesson.steps[2].goal.field: …),
or 12 lessons, 74 steps: all fine. The test suite (test/learn.test.mjs) runs the same check,
and then has a scripted learner complete every practice step, so a goal that cannot be reached
fails the tests.
To try your lessons in the app before they are on GitHub, run npm run demo (or npm run dev):
with the demo console running, the app loads this folder from your computer instead.
Files
learn/index.json the courses, and which lessons are in them, in order
learn/lessons/<id>.json one lesson; <id> is the lesson's id
index.json
{
"format": "cheatspecter-lessons",
"version": 1,
"edition": "2026-10",
"courses": [
{
"id": "values",
"title": "Finding and changing values",
"summary": "One or two sentences shown under the course title.",
"lessons": [
{ "id": "find-a-number", "title": "Find a number you can see", "summary": "…", "minutes": 8, "practice": true }
]
}
]
}
title, summary and minutes must be the same as in the lesson's own file (the list of
lessons is drawn from the index alone). practice is true when the lesson has at least one
practice step. edition is shown at the bottom of the list; change it when you publish. Ids are
lower-case letters, digits and dashes.
A lesson
{
"format": "cheatspecter-lesson",
"version": 1,
"id": "find-a-number",
"title": "Find a number you can see",
"summary": "Search for your ammo, change it in the game, search again, and edit what is left.",
"minutes": 8,
"games": { "range": { … }, "shop": { … } },
"steps": [ … ]
}
games holds the practice games the lesson's steps use, by a name of your choosing. steps
are shown one at a time, in order.
Text
Wherever a step has text (and in done and explain), this small markup works. Anything
else is shown as the characters you typed.
| You write | You get |
|---|---|
| a blank line | a new paragraph |
# Heading on its own |
a heading |
lines starting with - |
a bulleted list |
lines starting with 1. , 2. |
a numbered list |
> note: …, > tip: …, > warning: … |
a callout |
**bold** |
bold, for the names of buttons and for new words |
`code` |
code, for instructions, addresses and file names |
Write for someone who has never done this. Name buttons exactly as the app shows them, and say what the learner will see after each action.
Steps
Every step has a kind and a title.
read: something to read. text.
choice: a multiple-choice question. question, options (2 to 6 strings), answer
(the position of the right one, counting from 0), explain (shown once they get it right), and
optionally text above the question.
answer: a question answered by typing. question, accept (the answers that count;
capitals, spacing and a final period are ignored), explain, optionally text.
practice: the learner does something in a practice game with the real tools.
{
"kind": "practice",
"title": "Fire, then narrow down",
"text": "What to do, step by step.",
"game": "range",
"tool": "find",
"goal": { "check": "narrowed", "label": "Narrow the list down to one match", "field": "player.ammo", "atMost": 1 },
"hints": ["A nudge.", "A stronger nudge.", "Nearly the answer."],
"done": "Shown when the goal is reached: what just happened, and why it matters."
}
game: which of the lesson's games. Steps that follow each other in the same game carry on in it (the learner's search and cheats are still there), unless a step has"fresh": true, which starts a new game with no cheats.tool: the tab shown when the step opens:find,memory,codeorcheats.hints: one to six, revealed one at a time, weakest first."challenge": truemarks a step the learner does alone. Give the goal and no steps. Every practical lesson should end with one, in a game with different numbers from the guided part.
The learner also gets a Show me button. It reaches the goal with the same tools and writes down what it did; you do not script it.
soon: text explaining practice that the app cannot offer yet.
Goals
A goal is what the app checks for. Each has a label: the sentence the learner sees with a
circle or a check mark next to it. field means a value in the game, written object.field;
action means one of the game's actions.
check |
Reached when | Also needs |
|---|---|---|
narrowed |
the learner's search results contain the value and are at most atMost long |
field, atMost |
value |
the value is atLeast, atMost or exactly a number |
field, is, value |
held |
the value is unchanged after each of times uses of an action in a row (frozen, or its code is switched off) |
field, action, times |
steady |
a value the game changes by itself has not moved for seconds |
field, seconds |
pressed |
an action has been used times times |
action, times |
restarted |
the game has been restarted | |
updated |
the game's update has been installed (the game needs "update": true) |
|
caught |
the debugger has reported the code that writes the value ("Find what writes to this") | field |
off |
the code behind the action's first effect is replaced with no-ops | action |
saved |
"Your cheats" has a cheat of type value (for a field) or patch (for an action). Add "named": true to require a name other than the automatic one, "grouped": true to require a group, "location": "pointer" to require a pointer path |
type, and field or action |
To ask for several things, use { "check": "all", "of": [ … ] } with two to five goals. They
count in order: a goal is only looked at once the ones before it are reached, and what it
counts (restarts, uses of an action) is counted from that moment. So "restart, then switch
the cheat on again" is restarted followed by off.
Practice games
A practice game is a description; the app builds a small pretend game from it, with real memory to search and real machine code behind every action.
{
"name": "Specter Range",
"objects": [
{ "id": "player", "fields": [
{ "id": "ammo", "type": "u32", "value": 30, "offset": 40 },
{ "id": "shots", "type": "u32", "value": 0, "offset": 64 }
] }
],
"actions": [
{ "id": "fire", "label": "Fire", "effects": [
{ "field": "player.ammo", "op": "sub", "by": 1, "min": 0, "refill": 30 },
{ "field": "player.shots", "op": "add", "by": 1 }
] }
],
"hud": [ { "label": "Ammo", "field": "player.ammo", "show": "number" } ],
"decoys": [ { "type": "u32", "value": 30, "count": 12 } ],
"copies": [ { "of": "player.ammo", "count": 3, "follow": "fade" } ],
"drifting": 0,
"update": false
}
objects(1 to 8) are the things the game keeps in memory. Eachfieldhas atype(u8,u16,u32,float,double, orpointer), a startingvalue, and optionally anoffsetfrom the start of the object (a multiple of the type's size; left out, fields follow each other from 16). Apointerhasto(another object's id) instead of a value. Objects land somewhere new every time the game starts. One with"static": truelives in the game's own data, at the same place every time: give it apointerto another object and there is a pointer path to find.actions(up to 8) become buttons. Eacheffectadds to or subtracts from au32,floatordoublefield. A subtraction that would go belowminsets the value torefill(or stops atmin); an addition stops atmax. An action with"every": 1000runs by itself every that many milliseconds and has no button ("hidden": truehides one without a timer). Each effect is its own few bytes of code, which "Find what writes to this" finds and no-ops switch off. Effects that name the same"site"share one piece of code, as "take damage" for the player and for an enemy would; they must do the same thing to the same field of their objects.hud(up to 6) is what the game shows: anumber(withdigitsdecimals), abar(filled atmax; the number is not shown, which is the point), or atime(seconds shown as m:ss).decoysput the same number in unrelated places, so a first search finds more than one.copiesfollow a value for a few changes and then stop, as a game's own copies of a number do;count3 means narrowing down takes about four rounds.driftingis how many values change on their own, which gives "stayed the same" a job in searches that start from "Unknown".updatetruegives the game an "Install game update" button that moves its code.
If someone overwrites a game's code with anything other than whole instructions of no-ops, the game "crashes" the next time that code runs, and "Restart game" brings it back.
Changing the format
version is 1. The app refuses files with a higher version and tells the user to update, so
new kinds of steps or goals mean a new version number and a new app release first. The types
and the checker are in src/lib/learn.ts; the practice game is in src/lib/practice/.