# Game Hacking Academy · Systems Field Guide — Full lesson text and code > Machine-readable export for documentation retrieval. Canonical rendered book: https://mustcodeal.github.io/rust-game-hacking-book/ This export contains the source of every numbered lesson and the glossary, including Rust, assembly, configuration, text-protocol, and command examples. Figures appear as component tags such as ; their `cells`, `groups`, and `caption` attributes carry the figure content. --- ## Lesson 1.1 — Reasoning from Evidence Canonical page: https://mustcodeal.github.io/rust-game-hacking-book/pages/1/01/ Category: Computer, Game, and Code Foundations Estimated reading time: 20 minutes Summary: Ask precise questions, judge evidence, test assumptions, correct mistakes, and build understanding through practice. You do not need to prove that you already know something before you study it. The next lesson starts with the parts of a computer and builds each idea from there. You do not need to look up its missing foundations before you begin. The lessons in this opening chapter connect four views of one game action: the computer runs instructions, the game checks whether a purchase is affordable, Rust expresses that check and subtraction, and memory holds the changing state. A Windows lab and a gold scan then make those ideas visible. The chapter ends with that first scan; the next chapter follows the value into the code that changes it. Read the sequence as a tutorial; later, the concept lessons are also places to return when an unfamiliar term appears. Here is one study loop that works well for unfamiliar technical ideas. It is a suggestion, not a requirement: the later lessons explain their subjects directly and do not depend on it, so use it when an idea will not stick and skip it when it does. 1. **Look up** the exact part you need. 2. **Close or hide** the answer. 3. **Explain** the idea in your own words. 4. **Check and correct** what you said. ```mermaid flowchart LR L["1. Look up
the exact part you need"] --> H["2. Hide
the source"] H --> E["3. Explain
in your own words"] E --> C{"4. Check against
the source"} C -->|"missing, incorrect,
or unclear"| L C -->|"correct"| N["next question"] ``` That loop gives you fast feedback. It also prevents a common mistake: reading the same paragraph several times and confusing recognition with understanding. Look up, hide, explain, check. Explaining from memory is the real test; rereading only tests recognition. ## Choose a goal you can recognize Give a study session a specific goal. “Understand game hacking” does not say what you should be able to do afterward. “Explain what I observed after one jump, and distinguish that observation from my guess about its cause” does. You can finish that goal with a short explanation and a repeatable observation; you do not need to understand the entire game first. Choose a goal that matters to the next task. If you want to follow a gold purchase, learning how to distinguish the stored amount from its display is useful. Collecting unrelated facts about every game engine can wait. As the book introduces code and tools, a goal can become more concrete: run one example, explain its inputs and result, or reproduce one error. ## Ask a question small enough to answer Broad questions make it hard to tell whether an answer is complete. ❌ **Too broad:** “How do games work?” ✅ **Better:** “When I press the jump button, what changes on the screen?” ✅ **Better:** “If I press jump while the character is standing still, does the character rise before landing again?” A useful technical question usually names: - the object you are studying; - the relationship you want to understand; - the evidence or example you can check. The jump question names the action you control, the change you want to observe, and a way to check it: try the same button press while standing still and watch the character. If nothing happens, you have a more useful next question: was the button received, or does this situation prevent jumping? You can investigate one possibility at a time. Treat a question like a bug report. It points at one thing to try, so a small test can answer it. Write down what you **know**, what you **do not know**, and what you are **assuming**. For the jump question, you know which button you pressed and whether the character visibly rose. You do not yet know whether the game received the input or which grounded-state or cooldown check allowed the jump. “The character was standing on the ground” may be an assumption until you check the starting position. These distinctions keep an attractive explanation from silently becoming a fact. ## Check whether the evidence answers your question A source can be accurate and still answer a different question. Instructions for another game version or another processor architecture may explain the general mechanism while giving the wrong steps for your experiment. Check the target, version, conditions, and actual claim before using the source. Prefer evidence you can inspect: the relevant code, the tool's documentation, or a controlled observation you can repeat. Each has limits. Documentation describes an intended contract; an experiment shows what happened under the conditions you recorded. One successful jump does not establish that jumping works from every position. Several sites repeating the same original claim also do not provide several independent checks. Consider another explanation before settling on the first one. If a jump fails, perhaps the input was missed, or perhaps the character was already airborne. Reset to a known position and repeat the press, then vary one condition. A useful test produces different predictions for the competing explanations. **Occam's razor** is a useful preference for an explanation that needs fewer unsupported assumptions while still accounting for the evidence. It helps you choose what to investigate first. Simplicity does not prove an explanation true; a more complicated cause may fit a later observation better. Checking the cheap explanation first saves time. A missed button press takes seconds to rule out; a theory about a hidden jump condition can take an afternoon. ## Use a worked example before making a prediction First follow an example whose starting point, action, and result are explained. Suppose a game shows 100 gold. Buying an item for 25 gold leaves 75: subtracting 25 from 100 gives 75. After seeing that worked through, you can predict what a second 25-gold purchase would do, then check in the game. Later lessons use this same visible change to ask where the value is kept and how code updates it. When you reach a code example, read its explanation before predicting a result. If you use an AI completion or copy a line, explain every accepted line. “It compiles” is useful feedback, but it is not the same as understanding. ## Make progress when you get stuck An error is information about this attempt. It does not establish that you cannot learn the subject. Keep the exact message or unexpected result, return to the last step you can explain, and reduce the task to one small example. For the jump problem, check one press from one known position before studying the whole movement system. Change one thing and record what happened. If you keep making the same guess, take a short break, explain the problem aloud, or ask another reader to walk through the example with you. ## Ask for debugging help someone can reproduce “It does not work” gives another person almost nothing to inspect. A useful help request includes: - what you expected; - what actually happened; - the smallest relevant code; - the exact error message; - the target version and architecture; - what you already tested. That information turns a complaint into a question someone can reproduce. Here is that list as a request you can fill in and send to a person or an assistant: I expected [what you expected]. Instead [what actually happened]. The smallest code that shows it is [the code]. The exact error message is "[the message]". I am using [the tool or game, its version, and 32-bit or 64-bit]. I already tried [what you tested]. What should I check next? Please say which part of my report your answer depends on. Ask for the missing observation or explanation directly: “I expected the character to rise from this position; the input was recorded but the position did not change. What should I check next?” When helping someone else, recognize what they already tested and ask a useful question about the remaining gap. Avoid treating a mistake as a judgment about the person. ## Use AI as a helper, not as the source of truth AI can create practice questions, compare your explanation with supplied source material, simplify a difficult paragraph, or suggest a small test. Give it the actual source, version, and code when those details matter. Always check important claims against the program, documentation, or source code. If the answer cannot show where a fact came from, treat it as a lead to verify—not as evidence. ## Optional study reference The study loop at the start of this lesson is optional, and nothing later in the book depends on it. If you want more detail on its steps, this reference expands them: what to do before answering, how to mark a correction, and a routine for one study session. ### Read the source before answering an unfamiliar question If you encounter an unfamiliar idea, read the explanation and its example before trying to answer a question about it. For a detail that depends on a particular tool or version, consult that tool's documentation too. Then hide the source and answer without looking at it. This matters because copying a sentence tests your eyes, while rebuilding its meaning tests your understanding. Suppose a lesson says: > A game's state is the information that describes what is happening now. A good answer in your own words could be: > State is what the game currently remembers, such as a character's health or > position. The wording changed, but the meaning stayed accurate. Do not add a claim the lesson did not support, such as “state is only what I can see on the screen.” ### Correct mistakes immediately After answering, compare your answer with the source or ask for a correction that uses the source. Mark each important part as: - **correct** — the meaning matches; - **missing** — an important part was left out; - **incorrect** — the answer says something the source does not support; - **unclear** — the idea may be right, but the wording hides it. Rewrite only the parts that need work. A correction should tell you *why* the old answer failed, not merely show a replacement sentence. If you miss the same question in the same study session, look it up again and retry with different wording. This tests whether you can express the idea in more than one way. If you retry on another day, answer as accurately as you can after reviewing it again. Save repeated weak spots for spaced review. Practice should include checking and correcting the result; repeatedly rehearsing an incorrect explanation preserves the mistake. Over time, keep a few worked examples and corrected errors that you can return to when the same difficulty appears. The four marks work as a request to a helper too: Here is a paragraph from a lesson: "[paste the paragraph]". Here is my explanation in my own words: "[paste your answer]". Using only the paragraph, mark each important part of my answer as correct, missing, incorrect, or unclear, and say why each part that is not correct fell short. Do not add facts the paragraph does not contain. ### Accuracy and flexible wording are different skills **Answering in your own words** means preserving the original meaning without copying the sentence. **Explaining it a different way** means keeping that meaning while changing the example, order, or comparison. For instance: - accurate answer: “Game state describes what is happening now.” - different explanation: “If a character loses health, the game's remembered health changes; the health bar may then show that change.” The first checks precision. The second checks whether you can use the idea. ### A short study session If you want a routine, this one fits in 20–40 minutes: 1. skim the lesson headings for a few minutes; 2. choose one question you can test; 3. read one explanation and one small example; 4. hide them and explain the idea; 5. run or inspect the example; 6. check your explanation against the source and retry the parts you missed. ## Checkpoint Before moving on, you should be able to: - turn a broad topic into a checkable question; - choose a useful goal and separate known facts from assumptions; - look up an unfamiliar idea before answering; - check that evidence fits the target and consider another explanation; - explain it without copying the source; - compare, correct, and retry your answer; - give a helper enough detail to reproduce your problem. --- ## Lesson 1.2 — How a Computer Runs a Game Canonical page: https://mustcodeal.github.io/rust-game-hacking-book/pages/1/02/ Category: Computer, Game, and Code Foundations Estimated reading time: 16 minutes Summary: Build a game from CPU instructions, a small program, stored values, and the services that make it interactive. Suppose a game shows 100 gold. You collect one coin, and the display changes to 101. What had to happen inside the computer for that small change to appear? We can answer without knowing a programming language yet. ## The parts work together The game's files stay on a **storage drive** when the game is closed. When you start it, the computer puts the code and the information needed soon into **RAM** (random-access memory). RAM is fast working storage; its contents are temporary. The **CPU** (central processing unit) runs the code and changes values such as your gold. A keyboard, mouse, or controller supplies input. The **GPU** (graphics processing unit) helps turn the game's drawing work into the picture you see. These parts have different jobs in our coin example. The stored game file contains the instructions for collecting a coin. While the game runs, RAM holds the current gold value. The CPU updates that value from 100 to 101, and the game draws the new number on the screen. The parts communicate through the computer's hardware; none of them is the whole game by itself. ```mermaid flowchart TD Drive["game files on storage"] --> RAM["code and current values in RAM"] RAM --> CPU["CPU runs instructions"] Input["keyboard or controller input"] --> CPU CPU --> Updated["updated gold in RAM"] Updated --> GPU["GPU helps draw the picture"] GPU --> Screen["screen"] ``` ## The CPU follows small instructions The CPU is sometimes called the computer's **brain**. That is a useful first picture of its central job, but the CPU does not understand a game as a person does. It executes **instructions**: small, specified operations such as copying a number, adding two numbers, comparing them, or going to another instruction. The CPU has tiny working places called **registers**. Imagine one register currently holds 100. An add-one instruction changes that register's value to 101. It did one small job; it did not decide what a coin means or draw the number. Later, the program can store the result in RAM for continued use. This is a description of the change, not executable CPU code: ```text working value starts at 100 add 1 to the working value working value is now 101 ``` The exact CPU instructions and register names depend on the machine. For now, follow the value: 100 went in, one was added, and 101 came out. The CPU only does tiny steps such as copy, add and compare. Everything a game does is built from those. ## Working values live at different distances from the CPU A **register** holds a value named by an instruction. A **cache** holds memory lines the processor may reuse; it is not another set of variables the game names. Common CPUs have L1, L2, and L3 cache levels. A cache hit can serve a memory request nearby; a miss may require a farther cache level or RAM. Intel's [cache explanation](https://www.intel.com/content/www/us/en/docs/vtune-profiler/user-guide/2024-2/cpu-metrics-reference.html) describes why those extra fetches can make the CPU wait. RAM holds the running program's resident code and data. An **SSD** or **HDD** keeps persistent files; the operating system performs file I/O to bring their contents into working memory. Their capacities and access times depend on the machine, so this hierarchy gives roles rather than fixed speed numbers. Changing gold in a working register or memory does not save the game. Persistent output needs a separate save operation. ## A program combines instructions A **program** is a collection of instructions arranged to do a larger job. Our example becomes a tiny program if we let a number come in and send the new number out: ```text input: 100 copy the input into a working value add 1 to the working value output: 101 ``` The **input** is the value supplied to this calculation. The **output** is its result. A running program can also remember values, cause effects, and keep working rather than stop after one output. In a game, the gold count is part of the game's changing **state**: information describing what is happening now. The input might be the event “collect a coin,” and the new state has 101 gold. ## A function names work we want to reuse A larger program may need to add one in many places. We can give that small behavior a name: **add_one**. A named piece of program behavior is a **function**. Instead of describing its steps every time, another part of the program can ask add_one to work on a number. ```text function add_one(number): result = number + 1 give back result add_one(100) gives back 101 ``` This is explanatory pseudocode, not code you can run as written. The name `number` stands for the input supplied on each use; `result` is the value after adding one. A function can also do work without taking an input or returning a useful value. Naming behavior lets the program reuse and organize it. ## A branch chooses which work to do What if a game can award a coin **or** charge one? We can name the second operation subtract_one. Both operations change the same starting number by one. Now the program needs another input: which action occurred? ```text starting number: 100 action "collect": add_one(100) gives back 101 action "spend": subtract_one(100) gives back 99 ``` For “collect,” the program follows the add path and ends at 101. For “spend,” it follows the subtract path and ends at 99. It must **compare** the action with the choices and then follow one path. This choice is a **branch**. A branch lets the same program respond differently to different input. It does not run both outcomes for one action. ```mermaid flowchart TD Start["starting number: 100"] --> Choice{"which action?"} Choice -->|"collect"| Add["add_one gives 101"] Choice -->|"spend"| Subtract["subtract_one gives 99"] ``` In a real game, spending may also need a rule such as “do not spend when gold is zero.” That is another comparison and branch. Lesson 1.4 will turn this small calculation into a program written in Rust. Picture a branch as a fork in a road: the program reads a sign, then walks down exactly one path. ## How the numbers are represented We have treated 100 and 101 as ordinary numbers. To find them in a running game, we also need to know how computers represent numbers. In familiar **decimal** notation, each place is worth ten times the place to its right. The number 101 means one hundred, zero tens, and one unit: 1 × 100 + 0 × 10 + 1. **Binary** uses only 0 and 1. Each place is worth twice the place to its right. One binary digit, either 0 or 1, is a **bit**. Eight bits make one **byte**. The eight places in a byte are worth 128, 64, 32, 16, 8, 4, 2, and 1. For example: ```text place value: 128 64 32 16 8 4 2 1 bits: 0 1 1 0 0 1 0 0 total: 64 + 32 + 4 = 100 ``` Writing eight bits for every byte gets tiring. **Hexadecimal**, or **hex**, uses sixteen digits: 0–9 followed by A–F for values ten through fifteen. One hex digit represents four bits, so two hex digits represent one byte. The byte above splits into `0110` (six) and `0100` (four), written `0x64`. The prefix `0x` tells you it is hex. Its value is still decimal 100: 6 × 16 + 4. Decimal, binary, and hex are different ways to *write* the same number, not three different kinds of gold. Memory tools often show bytes in hex because two digits fit one byte exactly. That is why you may meet 0x64 where the game says 100. One byte can represent 0 through 255. Larger values use several bytes. The memory lesson just before the first scan will show where those bytes sit and how a program reads them together. ## Languages help people write programs The CPU executes instructions represented by patterns of bits. Writing a whole game as bit patterns would be difficult. **Assembly language** gives human-readable names to operations such as moving and adding values. A **higher-level language** lets us express larger ideas, such as functions and branches, without spelling out every CPU operation. Our add-one function and collect-or-spend branch can be written in a language like Rust. A compiler translates source code into machine code for a chosen target. The translation is not necessarily one source line to one CPU instruction. Here is our same small function in Rust: ```rust fn add_one(number: u32) -> u32 { return number + 1; } ``` `fn` names a function. `number` is its input; `u32` means a non-negative whole number stored in 32 bits. The arrow says the function gives back a `u32`, and `return` sends back the sum. Calling `add_one(100)` therefore gives back 101, just as the earlier pseudocode did. This is a function fragment, not a whole program; Lesson 1.4 builds and runs the complete Rust example. Lesson 2.2 follows the later steps that turn source into a program file. ## The operating system helps programs use the computer Even our tiny add-one program needs more than arithmetic if a person must type a number and see an answer. It needs a way to receive input and display output. A game also needs files, sound, windows, and access to devices. An **operating system** (OS) provides common services for those jobs and coordinates programs that use the computer. This book uses Windows for its practical examples. A program written for Windows can ask Windows to read a file or show a window instead of controlling each device from scratch. An **application** is a program made for a user to do something. A game is an application: it uses the OS's services, runs its own instructions, and keeps its own changing state. ## From a file to a running game The Rust source a developer writes has already been translated into machine instructions and linked with other code before the native game file exists. Other games may load scripts that an interpreter runs while the game is open. Either way, the CPU eventually carries out instructions. [Lesson 2.2](/pages/1/10/) follows those routes in detail; here we start with the native game file. The game file on storage contains code and other saved information. Starting it creates a **process**: one running instance of that program, with its own memory and resources. Windows loads the code and needed data into memory and starts a **thread**, a path that executes instructions in the process. A process may have more than one thread, but one is enough to picture this first example. Many games repeat a broad **game loop** while you play: 1. receive input, such as the event “collect a coin”; 2. update state, changing gold from 100 to 101; 3. draw the current state, so the display shows 101; 4. repeat for the next action. ```mermaid flowchart TD Input["input: collect a coin"] --> CPU["CPU follows the game's instructions"] CPU --> State["state in RAM: gold is 101"] State --> Screen["draw 101 on screen"] Screen --> Next["next input"] ``` Actual games split this work among many systems and sometimes several threads. This loop is a first model for asking a useful question: did the input arrive, did the stored value change, and did the display show the changed value? ## The pieces together The CPU carries out individual instructions. A program arranges many of them into useful behavior: receive a value, apply the add-one operation, and produce a changed value. A function names that reusable behavior; a branch chooses whether to collect or spend. Storage keeps the game file, RAM holds the running program's state, and the operating system loads the file as a process the CPU can execute. The gold value changed while the game was running. The next question is what the game's rules do with it and how several players can each have their own value. [Game Fundamentals](/pages/1/03/) starts there. We will return to bytes and addresses just before the first memory scan in [Lesson 1.8](/pages/1/08/). ## Where these pieces are explained next --- ## Lesson 1.3 — Game Fundamentals Canonical page: https://mustcodeal.github.io/rust-game-hacking-book/pages/1/03/ Category: Computer, Game, and Code Foundations Estimated reading time: 39 minutes Summary: Follow actions through a game's entities, checks, space, and time; distinguish live state, display copies, server decisions, saves, and bytes. Lesson 1.2 followed a game action through the CPU and a simple loop. To understand why a displayed number changes, we need a model of the game around it: what the player does, which check or calculation runs, what the game remembers, and what it shows. The byte-level memory model comes just before our first scan in Lesson 1.9. Our small game has players, coins, and a shop. Call one player Ada. We will follow her purchases and movement, then ask what the game must keep while she plays and what it must save when she leaves. The examples describe chosen checks and updates for this game; another game can model the same activity differently. ## A game turns actions into a changing world A player supplies **input**, such as clicking the Buy button or pressing a movement key. The game interprets that input as a requested action. It checks its current **state**—the information it remembers about the world—and calculates any accepted change. It then draws or plays an **output** that the player can perceive. A common game loop repeats those steps as play continues: ```mermaid flowchart TD I["input: buy an item"] --> R["update: can the player pay?"] R --> S["state: gold 100 → 75"] S --> D["draw: show Gold: 75"] D --> N["next input and update"] ``` The example takes the successful purchase path. If the player has only 10 gold and the item costs 25, the affordability check fails and gold stays 10. The screen reports what the game decided; it does not decide the purchase itself. Later in this lesson we will see why the screen may even hold a separate copy of that result. Games can also update enemies, timers, motion, or music without a fresh button press. On each update, the game can subtract time from a countdown, move a projectile by its speed and elapsed time, or choose an enemy action. It then draws the changed world or plays a sound. An **engine** is reusable software for common game jobs. It commonly supplies the repeating loop, drawing, and loading resources such as images and sounds; the game supplies particular behavior, such as looking up an item price and rejecting a purchase when gold is below that price. Finding the right place to change a game starts by asking which side owns the behavior. [Lesson 4.1](/pages/1/11/) develops that boundary. A game remembers more than one number. It might track one player's gold, current health, and maximum health at the same time. How can it keep those values together and apply the same calculation to many players? ## One player has related values Suppose Ada starts with 100 gold, 80 health, and a maximum of 100 health. These values are part of the changing game state. ```text player: gold = 100 health = 80 max_health = 100 ``` The labels tell us what each value means. If the player buys an item for 25 gold, gold becomes 75, while health stays 80. If the player then takes 20 damage, health becomes 60, while gold stays 75. The changes belong to one player, but the purchase uses gold and the damage calculation uses health. A group of related named values is a **record**. Each value inside it is a **field**. The record lets the program talk about a player's gold without confusing it with that player's health. It also gives later memory work a question to ask: which nearby stored values belong to the same player? ## Give things identities and responsibilities Gold and health describe Ada, but the game also needs to distinguish Ada from another player, a coin, or the shop door. A thing the game treats as having its own identity is an **entity**. Depending on the game, entities can include characters, pickups, projectiles, doors, or even an invisible object that controls a round. The game can assign Ada an **identifier**, such as player ID 17. The ID answers “which player?” Her gold and health answer “what is true about that player now?” Buying an item changes her gold; it does not turn her into a different player. A display name is another field and need not be unique. Two players called Ada can still have different IDs. An entity can also need several kinds of information. Ada has health, money, a position, and a picture the game can draw. Games often group related information into **components**, each describing one responsibility: | Part of Ada's model | Information it needs | Work that uses it | | --- | --- | --- | | Health | Current health and maximum health | Apply damage or healing | | Wallet | Gold | Check a purchase | | Position | Location in the world | Move Ada and test nearby objects | | Appearance | Which image or model to use | Draw Ada | A component here means a part of the game's model, not necessarily one particular programming technique. One game might put all these fields in a single player record; another might keep separate component records associated with player ID 17. Both can implement the same gameplay. We need this distinction because the information visible on one character need not occupy one uninterrupted block of memory. An entity also has a **lifetime**: the interval from its creation to its removal. A coin entity stops being available after Ada picks it up. A later coin can reuse its storage without being the same coin. Some games reuse IDs too, so an ID is meaningful only within the identifier assignment and reuse policy of that game. Later memory lessons develop how to recognize a currently valid entity; the first question is already useful: does this information still belong to the thing we intended to observe? ## Shared assets create separate instances The game does not need a different image file for every coin. It can load one coin image and use it to draw many coin entities. An **asset** is a resource the game uses, such as an image, a three-dimensional model, a sound, a map, or a set of starting values. An **instance** is one particular use of a definition or resource in the running world. Suppose two coins use the same image. One coin is beside Ada and one is near the shop. Each has its own identity, position, and availability. If Ada collects the nearby coin, that instance disappears and her gold rises by one. The coin image remains loaded, and the coin near the shop remains available. Changing a shared image can change how both coins look; removing one coin instance affects that one coin. The same distinction applies to player setup. A character definition might specify a starting maximum health of 100. Ada's current health of 60 is state belonging to her running instance. A definition's default and an instance's current value can be equal when the instance starts, then diverge during play. ## A class defines fields and methods; an instance holds values In a language such as C++, a **class** describes a kind of object: which fields it has and which operations can use those fields. A function called through an object is a **method**. A player instance is one particular object created from that definition. Ada and Bo can use the same `damage` method while holding different health values. This explanatory pseudocode names the receiver `self`: the specific instance whose method was called. It is a sketch, not runnable Rust or C++: ```text class Player: gold health damage(amount): self.health = max(0, self.health - amount) Ada = Player(gold=75, health=60) Bo = Player(gold=40, health=50) Ada.damage(20) # Ada's health becomes 40; Bo's stays 50. ``` `max(0, value)` selects whichever is larger, so damage cannot make health negative in this model. `Ada.damage(20)` chooses Ada as `self`; it does not subtract 20 from every Player. A class definition does not mean that all instances share one set of field values. Games can also organize data using components rather than one class per entity, as we saw above. Choose a player or a damage amount below. Read the result first if useful, then step through how the receiver selects the changed field. A class defines fields and methods. Each instance holds its own field values; the receiver identifies which instance a method uses. ## One record becomes several players For the table below, add a second player called Bo. Bo has 40 gold and 50 health. Copying the first player's field names into `gold2`, `health2`, and so on would work for two players, but every new player would demand more special-case code. Instead, the game can keep several player records in a **collection**: | Player | Gold | Health | Maximum health | | --- | ---: | ---: | ---: | | Ada | 75 | 60 | 100 | | Bo | 40 | 50 | 80 | The table is a picture of two records, not a claim that RAM stores a table with headings. A compiled game stores values as bytes; the labels here tell *us* what each value means. Lesson 1.8 will show how a type tells code how to read those bytes. Suppose both players collect one coin. We could write “Ada's gold becomes 76; Bo's gold becomes 41.” For many players, repeating one calculation is simpler: ```text for each player in the collection: add 1 to that player's gold ``` That repeated pass is a **loop**. One pass handles Ada (75 becomes 76); the next handles Bo (40 becomes 41). The add-one calculation stays the same when the collection grows. This is the reason to introduce a collection and a loop, rather than a list of data-structure names to memorize. Think of a spreadsheet: write one formula, copy it down every row, and the same calculation covers two players or two hundred. ```mermaid flowchart TD Rule["add one coin"] --> Ada["Ada: 75 → 76"] Ada --> Bo["Bo: 40 → 41"] Bo --> More["next player, if any"] ``` **Class cheatsheet:** a class defines fields and operations; an instance holds one object’s current values; the receiver selects which instance a method changes. ## The same idea in Rust Rust gives a record type a name with `struct`. We can describe the three numeric fields from our example. This fragment declares a shape for player values; it does not yet run a purchase or create a player: ```rust struct Player { gold: u32, // Amount an accepted purchase can spend. health: u32, // Current health after damage or healing. max_health: u32, // Upper limit for a health update. } ``` `Player` is the record type. Each line inside the braces names a field, then gives its type after a colon. `u32` means a non-negative whole number held in 32 bits, or four of the eight-bit bytes from Lesson 1.2. The comma separates fields. This declaration describes the shape of a player; it does not create Ada or Bo. The text after `//` is a **comment** for the reader. It explains a field's purpose and does not add another field or change the program's behavior. Lesson 1.4 will create values, change fields, and write a loop in Rust. A game's actual layout may differ from this source-level sketch. A memory tool has to infer fields from the bytes and the instructions that use them. [Lessons 3.8–3.10](/pages/3/03/) develop that recovery in detail, including alternative layouts and objects that move or disappear. ## Checks keep health inside its permitted range Having fields together also lets the game check relationships between them. Ada's health of 60 fits her maximum of 100. A health value of 120 with a maximum of 100 would exceed the permitted range ```text 0 <= health <= max_health ``` Here, `<=` means “less than or equal to”: health must be at least zero and no greater than the maximum. Our damage and healing calculations should keep it inside that range. We do not need a new data structure to express this condition. It is a relationship the game should preserve when it applies damage or healing. Lesson 1.4 keeps both fields in its working player record. Later chapters call a condition that must remain true an **invariant** and show how to test one when recovering a game's data. An address alone does not prove that a player record is still the same player. The game can remove a player and use that memory for someone else. For this first pass, remember that an address tells you *where* to look now; it does not guarantee an object's identity forever. Lesson 3.6 develops ways to recognize identity and lifetime. ## Inputs request actions We have said “Ada buys an item,” but a computer first receives something more mechanical: a mouse click, a key press, or a controller button. The game maps that input to an **action**, such as “buy this item” or “move right.” Keeping the action separate from its physical input allows the same purchase handler to work with several devices and different key bindings. An action is a request. The purchase handler still compares the price with Ada's gold before accepting it. Context matters too: pressing the right arrow might move Ada during play but select the next item while a shop menu is open. The same physical input can request different actions in different parts of the game. A game also distinguishes a button being **pressed now** from being **held down**. In our game, one press of the buy button requests one purchase. Holding the movement button requests continued movement. If the purchase handler ran on every update while the button was held, one press could accidentally buy many items. This makes the input's timing part of the behavior we need to understand. ## Movement needs space and time To move Ada, the game needs a way to describe where she is. In a small two-dimensional world, use two numbers: `x` for horizontal position and `y` for vertical position. Together, `(x, y)` are **coordinates**. Our example chooses positive `x` toward the right, positive `y` upward, and one unit as one tile's width. Another game can choose these conventions differently. Ada starts at `(2, 3)`. Moving one unit right gives `(3, 3)`: `x` changes from 2 to 3 and `y` stays 3. These are **world coordinates**, describing Ada's location in the game. Her **screen coordinates** describe where her picture appears on the display. If the camera follows Ada, she can move through the world while staying near the middle of the screen. Moving the camera changes the view without moving Ada in the world. Movement also takes time. Suppose Ada's speed is 4 world units per second and she moves right for one quarter of a second. The distance is 4 × 0.25 = 1 unit. Starting at `(2, 3)`, she ends at `(3, 3)` if nothing blocks her. This pseudocode shows the calculation: ```text # Inputs: x = 2, speed = 4 units/second, elapsed_seconds = 0.25. distance = speed * elapsed_seconds x = x + distance # Result: distance = 1 unit and x = 3; y stays 3. ``` The `#` lines describe the inputs and result, and `*` means multiply. The elapsed time tells the game how much movement to apply. [Lesson 4.4](/pages/4/11/#extend-2d-coordinates-to-three-dimensions) adds a third axis and turns the difference between two positions into a distance and a direction; [Lesson 7.1](/pages/5/01/#screen-coordinates) follows a world position onto the screen. ## The world updates between pictures The work that advances movement, health, timers, and other changing state is the **simulation**. The work that makes an image of the current world is **rendering**. One displayed picture is a **frame**. A simulation update is often called a **tick**. These names let us distinguish advancing the world from drawing it. Why does the distinction matter? Suppose a game moves Ada one unit each time it draws a frame. A computer drawing 120 frames in one second would move her twice as far as one drawing 60. Our speed calculation instead asks how much game time passed. Sixty updates of 1/60 second each produce the same four units of unobstructed movement as 120 updates of 1/120 second each: both add up to one second. A game can use the measured time since the last update, or advance the simulation in fixed steps, such as 1/60 second. With fixed steps, it can run zero, one, or several simulation updates while preparing the next picture. These are two useful scheduling models; real games also decide how to handle stalls and paused time. A frame count alone cannot tell us how many gameplay updates occurred. This also explains change without input. Once a projectile has a speed, the simulation can move it on later ticks. A timer can count down, and an enemy can act. The player does not need to press a new button for each change. A pause menu might stop the simulation while still rendering the menu, so a visible animation is not proof that every game timer is running. ## Contact tests can block movement, award gold, or apply damage Our movement calculation proposes a new position. It has not yet checked whether Ada would pass through a wall or touch a coin. The game gives objects simplified shapes for detecting contact. The test for whether these shapes meet or overlap is **collision detection**. For a simple worked example, treat Ada as a point starting at `x = 2` and place a wall at `x = 3`. She requests movement to `x = 4`, keeping the same `y`. Looking only at her destination could miss the wall: the point at 4 is past it. Testing the movement from 2 to 4 finds the crossing. Our game then stops Ada just before the wall. Actual characters have width, but the point example shows why a path and a final position answer different questions. Detecting contact and deciding its consequence are separate jobs. A wall blocks movement. A coin allows movement but awards gold and removes its own entity. A trap allows movement but applies damage. All three can begin with a contact test and then produce different changes to the world. A **physics system** can calculate motion using speed, forces, gravity, and contact responses. A game can also use simple movement calculations without a general physics system. The drawn image and the collision shape may have different sizes or levels of detail, so touching an image is not always the same as touching the shape the contact test uses. ## Modes and events organize change The same contact need not have the same outcome in every situation. Our game gives Ada three modes: `alive`, `defeated`, and `respawning`. At health zero, she changes from alive to defeated and movement requests are ignored. After a two-second wait, she enters respawning. Placing her at the spawn point and restoring her health makes her alive again. A change between modes is a **transition**. A set of allowed modes and conditions and actions for moving between them is a **state machine**. Here, “state” names one behavior mode. Earlier, “game state” meant all the information the game remembers, including gold, health, position, and this mode. The mode is one part of that larger state. This game keeps Ada's player identity through the respawn; another could remove her character entity and create a new one, and the visible respawn alone does not tell us which. The game can also announce a particular occurrence: “Ada collected coin 42.” Such an occurrence is an **event**. An action requests something, current state describes what is true, and an event reports what happened. “Collect coin 42” is the request; “Ada has 76 gold” is the resulting state; “coin 42 was collected” is the event. If the coin was already removed, the game can reject a second request instead of awarding gold again. [Lesson 4.9](/pages/4/07/#polling-is-not-the-same-as-reacting) turns repeated readings of state into events and shows when to act on one. ## The display can hold a copy After Ada collects a coin, the game may store 76 as her current gold and also prepare the text `"Gold: 76"` for the screen. The text is derived from the number, but it is not necessarily the number that the purchase handler reads. ```mermaid flowchart LR Input["Ada collects one coin"] --> State["gameplay gold: 75 becomes 76"] State --> Rule["purchase check reads 76"] State --> Display["display text: Gold: 76"] ``` If someone changes only the display text to `"Gold: 999"`, the screen may show 999 while the purchase check still sees 76. Changing a displayed copy can affect appearance without changing gameplay. This is why observing a matching number in memory is only the start of an investigation. Later lessons compare what changes after a purchase with what the game actually uses to decide whether the purchase is allowed. ## Which copy decides the result? In our local example, the purchase check reads Ada's gameplay gold. That value is **authoritative** for the purchase: the check uses it as the amount Ada actually has. The display copy reports the result but does not decide it. Authority describes this role, not a special numeric type. In a multiplayer game, a **client** is the program a player uses to take part. A **server** is a program that receives player requests over a connection, checks them against the shared game state it maintains, and sends accepted results to the participating clients. In a common shop design, the client sends the item ID Ada wants. The server looks up that item's price, compares it with the gold it stores for Ada, and either rejects the request or subtracts the price and records the purchased item. Suppose the server has 76 gold for Ada and the client's display shows 999. The server accepts one 25-gold purchase and updates its value to 51. It sends the accepted result back to the client, which can then show 51. The display's 999 did not give the server more gold. A network delay can also leave a client temporarily showing an older result, even when neither program has a bug. Different games divide responsibility differently, so ask which side has authority over each particular decision. [Lesson 9.1](/pages/6/01/#client-and-server) follows such requests and replies, including a client that shows a predicted result and then corrects it, and [Lesson 9.2](/pages/15/02/#validate-an-action-in-the-current-world) checks a purchase against server-owned state. ## Save files preserve chosen state The running game holds **live state**: what it needs for play now. Closing the game ends that running instance. To continue later, it writes selected information to storage. Keeping information beyond the running process is **persistence**; a save file is one way to provide it. After Ada reaches 76 gold and 60 health, a save could keep those values, her maximum health, position, and which coins she has collected. It need not keep the pickup sound's playback position or the prepared text `"Gold: 76"`; the game can recreate the display from gold when it loads. Saving selects state at a particular moment. If the file stores 76 gold and Ada later spends 25 without saving again, live gold becomes 51 while the file still records 76. A save file and live state can disagree simply because they describe different moments. Loading creates new running records. A saved player ID can help the game reconnect related information, but a past RAM address does not give the new process a valid player record. [Lesson 10.1](/pages/9/01/#state-can-outlive-a-running-game) follows a real save from running memory to a file and back. ## From a world idea to stored bytes We can now connect the meanings we have used. First, a designer chooses an idea from a real or imagined world: a character owns coins and can spend them. The game represents the information its calculations need as entities and fields, such as player ID 17 with 76 gold. This chosen representation is a **game-data model**. Ada's health number likewise models how the game's damage and healing calculations use; it is not a complete description of a person's body. To save that information or send it to another program, the game chooses a **format**: a specification of which fields are included and how to write and read them. A save might include gold and collected coins. A movement message might include only a player ID and the requested direction. They serve different purposes, so they need not contain the same information or resemble the live record's layout. An **encoding** assigns byte patterns to the chosen values. Here is a small invented format for a player gold record. It accepts IDs and gold amounts from 0 to 255, writes the ID first, and uses one byte for each field. Given player ID 17 and gold 76, it produces two bytes: ```text # Toy format: one byte of player ID, followed by one byte of gold. meaning: player_id = 17 gold = 76 field order: player_id gold hex bytes: 11 4C ``` Lesson 1.2 taught hexadecimal: `0x11` is 1 × 16 + 1 = 17, and `0x4C` is 4 × 16 + 12 = 76. The reader of this toy format knows the first byte means an ID and the second means gold because the format says so. The two bytes alone do not label their own meanings. Writing the text digits `"76"` or choosing a four-byte number would require a different encoding. Real formats can include additional fields, larger values, and checks; this example only shows how a declared format gives bytes meaning. The animation follows the meaning through those design choices. Live fields in RAM are already represented by bytes too; a game does not have to turn its whole world into a save file on every tick. In memory, code uses a known layout and types. In a save or message, code uses that format's field order, widths, and encodings. Lesson 1.8 develops the memory representation in detail. Keep two questions separate when investigating a value: **how is it represented?** and **what role does it have?** A number stored as four bytes could be current gameplay gold, a display copy, a saved value, or a server result. Its byte representation alone does not establish its authority or tell us when it was last updated. Bytes carry no label. The same four bytes could be live gold, a display copy, a saved value or a server result, so ask what role it plays. ## How the game pieces fit Ada's input requests an action. Purchase checks and damage calculations read her current state and update the relevant fields. Simulation advances that world through time; rendering shows a view of it. Identity tells the game which entity a value belongs to, components organize its responsibilities, and assets can be shared by many separate instances. A collection lets one loop visit several player records. The gameplay amount, displayed text, server's gold value, and saved value can all describe Ada's gold while serving different roles. Understanding those roles comes before deciding what a matching number in memory means. The game model explains the information; its memory layout or save/message format explains how code represents it as bytes. We have described the update in words. [Lesson 1.4](/pages/1/04/) turns the same player's values and calculations into a small Rust program. ## Where these ideas continue --- ## Lesson 1.4 — Programming Fundamentals Canonical page: https://mustcodeal.github.io/rust-game-hacking-book/pages/1/04/ Category: Computer, Game, and Code Foundations Estimated reading time: 40 minutes Summary: Build a guarded gold-purchase function and use it to understand variables, branches, test cases, collections, and ways to organize code. We have already followed one value through a CPU instruction (Lesson 1.2) and grouped it with one player's other values (Lesson 1.3). Now we can write a small program that uses those ideas. We will start with 100 gold and ask what happens when an item costs 25. Lesson 1.8 will show the bytes this program uses before we try to find them in a running game. Call the two players in this lesson Ada and Bo. Ada starts with 100 gold and Bo with 40. The short Rust fragments each explain one idea, and the next lesson runs a separate complete program. Here we will also compare program logic, collections, and ways to organize code. ## Data, values, and types **Data** is information stored or sent as bytes. A **value** is what those bytes mean when interpreted, and a **type** tells the program how to interpret and use them. For example, the four bytes `50 00 00 00` can represent the number 80 when read as a little-endian `u32`. We will work through the byte order in Lesson 1.8. For now, notice that “four bytes” and “80 health” describe different levels of the same information. The number 100 is a **literal**: a value written directly in the code. A **variable** is a name for a value the program can use. This first fragment names the current gold and the cost: ```rust let mut gold: u32 = 100; let cost: u32 = 25; ``` `let` introduces a variable. `gold` and `cost` are names we chose. The colon gives each variable a **type**. Game Fundamentals used `u32` for a non-negative whole number stored in four bytes. The equals sign sets the starting value, and the semicolon ends the statement. `mut` means the value named `gold` may change. We do not need to change `cost`, so it has no `mut`. The type keeps different kinds of value apart. `80` can be a `u32` health amount, `"Ada"` can be text, and `true` can be a `bool` answer to a question. Adding two health amounts makes sense; adding the player name to health does not. Later, Lesson 3.1 will show how types can even distinguish two numbers that happen to use the same underlying bytes, such as a player ID and a health amount. At this point, gold is 100 and cost is 25. To calculate a purchase, the program reads both values, subtracts cost, and stores the result back in gold: ```rust gold = gold - cost; ``` The right side is calculated first: 100 − 25 = 75. The equals sign then assigns 75 to `gold`. The cost stays 25. ## State is information that can change The program's **state** is the information it remembers at one moment. Before the assignment, `gold` is 100; afterward, it is 75. The cost is an input to that calculation. The subtraction result is a temporary value until it is stored as the new gold. A screen might then show the derived text `"Gold: 75"`, but that text is not necessarily the stored amount the next purchase checks. ```mermaid flowchart LR Input["input: buy an item for 25"] --> Calc["calculate 100 − 25"] Calc --> State["store gold = 75"] State --> Display["draw Gold: 75"] ``` This input → calculation → stored state → display path helps us ask which value a later piece of code actually reads. Game Fundamentals first showed that the display may hold a copy; Hacking Fundamentals will turn that observation into an experiment. ## An algorithm is a repeatable procedure What if gold is only 10 and the cost is 25? A purchase should not be allowed. Because `u32` cannot represent a negative number, blindly subtracting would also be the wrong operation. We need the branch from Lesson 1.2: ```rust if gold >= cost { gold = gold - cost; } ``` `>=` asks whether gold is greater than or equal to cost. The answer is a **boolean** value, either `true` or `false`. `if` runs the instructions between the braces only when the answer is true. Starting with 100 gold, the comparison is true and gold becomes 75. Starting with 10, it is false and gold remains 10. The braces mark the group of statements controlled by the branch. ```mermaid flowchart TD Start["read gold and cost"] --> Check{"gold ≥ cost?"} Check -->|"yes"| Subtract["subtract cost from gold"] Check -->|"no"| Keep["keep the current gold"] Subtract --> Result["resulting gold"] Keep --> Result ``` This gives us a repeatable **algorithm**, meaning a procedure with ordered steps: read the current gold, compare it with the cost, and subtract only if the player can pay. The same procedure works for 100 gold and for 10 gold. An algorithm is the plan; Rust code is one exact way to express it. Its three building blocks are already visible here: do steps in **sequence**, use `if` to **select** a path, and later use a loop to **repeat** work for several players. = 25 is true, because >= also accepts equal values. The branch runs and stores 25 − 25 = 0, which is a valid u32.' }, { label: '25', why: 'That is what gold > cost would give: 25 > 25 is false. The comparison here is >=, which is also true when the two values are equal.' }, { label: 'The program stops: a u32 cannot hold the result', why: 'A u32 cannot hold a negative number, but 25 − 25 = 0 is not negative. Only a price above the gold would give a negative result, and the check skips that case.' }, ]} > An algorithm is a plan of ordered steps. Rust is just one exact way to write that plan down. You can first write the plan in **pseudocode**, ordinary steps arranged to look like code without committing to a language's exact syntax: ```text receive the current gold and the item cost if the gold is at least the cost: give back gold minus cost otherwise: give back the original gold ``` This is a sketch to discuss and trace, rather than a program Rust can run. With 100 and 25, it gives back 75. With 10 and 25, it gives back 10. Naming the inputs and both outcomes makes the next function easier to write. ## A function gives a name to a behavior A **function** gives a behavior a name so another part of the program can use it. This one takes a gold amount and a cost, then gives back the gold amount after a permitted purchase: = cost", "hint": "Permit spending when the available amount covers the whole price, including exact payment."}, {"text": "gold - cost", "hint": "Return the remaining amount after paying the price."}]}> ```rust fn spend_if_possible(gold: u32, cost: u32) -> u32 { if gold >= cost { return gold - cost; } return gold; } ``` `fn` begins the function definition. The names inside parentheses are its **parameters**: the two input values it will receive. Each has a `u32` type. The arrow `-> u32` says the returned value is also a `u32`. `return` sends the value back to whoever used the function. If there is enough gold, it returns the subtraction. If there is not, it returns the original amount. The final `return` also shows that functions do not have to run every line: the first return ends that call. For example, `spend_if_possible(100, 25)` returns 75. `spend_if_possible(10, 25)` returns 10. Those expressions **call** the same function with different inputs. They do not change a variable by themselves; the caller must store the returned value: ```rust gold = spend_if_possible(gold, cost); ``` This simple result does not tell the caller whether a purchase happened when the cost is zero. A later lesson introduces types that can represent success or failure explicitly. We only need the resulting gold amount here. ## A value can have more than one role Gold and cost are **inputs** to `spend_if_possible`; its returned amount is an **output**. The caller can store that output in its mutable `gold` variable. `cost` stays fixed during the calculation. Input, output, and mutable are therefore overlapping descriptions, not three separate kinds of variable. They answer “where does the value flow?” and “may it change?” Two other descriptions concern where a name is usable and how long its storage exists. A **local** name belongs to a scope such as one function. People often use **global** for a name available outside a single function, although the language's visibility controls which code can use it. A Rust **static** item has storage for the entire program run. That storage duration does not by itself mean its contents can never change; Rust separately checks how code obtains shared or editable access; a long lifetime does not grant permission to mutate. See the [Rust Reference's static-item explanation](https://doc.rust-lang.org/reference/items/static-items.html) for the complete access requirements. Our purchase calculation needs only its supplied inputs, so it does not need a shared global gold value.