# Make an Emerald Sword: Minecraft modding from zero (Fabric, Minecraft 26.2)

This is the tutorial that goes with the code in this folder. You don't build the mod from scratch here; it's
already built and working. You read it in an order that makes sense, run it, change it, break it, and fix it.
By the end you understand every file in the repo and can add your own items, tools, and weapons.

Assumed: you can read code in *some* language and you've played Minecraft. No Java or modding experience needed.
Time: an evening for lessons 0-4; the rest as you need them.

```
Lesson 0   What a mod actually is (10 min read)
Lesson 1   Setup: JDK, IntelliJ, run the game from source
Lesson 2   Reading the skeleton: fabric.mod.json, the entrypoint, Gradle
Lesson 3   The simplest item: Tront Coin
Lesson 4   The Emerald Sword: materials, components, tags, recipes
Lesson 5   Proving it works without clicking: two kinds of tests
Lesson 6   Art: 16x16 rules, and turning an AI image into a texture
Lesson 7   Shipping it into your real game (and to friends)
Exercises
Appendix A  "Why doesn't the tutorial I found work?" 2026 name translation table
Appendix B  Where to go next, and what this template deliberately skips
Appendix C  Glossary
```

Markers like `TUTORIAL(4.2)` in the Java files point back to sections here.

---

## Lesson 0: What a mod actually is

Minecraft: Java Edition is a Java program. When you press Play, the launcher starts a JVM with the game's jar.
A **mod loader** (Fabric here; NeoForge is the other big one) is a small layer that starts *before* the game and
loads extra jars (mods) into the same JVM. Each mod gets a chance to run code at startup: that's your
**entrypoint**.

What your entrypoint does, 90% of the time, is **register** things. The game keeps big tables called
**registries**: one for items, one for blocks, one for entities, and so on. Every entry has an id like
`minecraft:diamond_sword`. Your mod adds `trontmod:emerald_sword` to the item registry, and from then on the game
treats it like any other item: it can be in inventories, on the ground, in recipes, in commands.

Registering is only half of it. Two other parts of the game need to know about your item:

- The **client** (the part that draws things) needs to know what it looks like: a model and a texture.
  Those are JSON and PNG files under `assets/`. Same format as a resource pack.
- The **server** (the part that runs the world rules, even in singleplayer) needs recipes, tags, loot tables.
  Those are JSON files under `data/`. Same format as a data pack.

So a "mod" is: a bit of Java that registers stuff + a resource pack + a data pack, all zipped into one jar.
Modern Minecraft leans hard on the data side: the Emerald Sword has about 25 lines of Java and 7 JSON files.

**Fabric vs NeoForge, one paragraph:** both work. Fabric is lighter, updates within days of a new Minecraft
version, and its docs are the clearest. NeoForge has a bigger API surface and the big "kitchen sink" modpacks
lean on it. For learning, Fabric. Concepts transfer.

**Why this template exists in 2026:** Minecraft 26.1 was the first version shipped *unobfuscated*, so the
community-made name lists (Yarn) were retired and Fabric renamed its own API to match Mojang's names.
Nearly every tutorial on YouTube predates that and uses names that no longer compile. Appendix A translates.

---

## Lesson 1: Setup

You need three things. Two are on this machine already; the third is the folder you're reading.

**1. A JDK (Java Development Kit), version 25.** Minecraft 26.2 is compiled for Java 25, so your mod must be too.
JDK = compiler + runtime. The launcher's bundled Java is a runtime only; it can't compile. This machine has
Temurin 25 extracted at `~/.jdks/jdk-25.0.4.1+1`. On another machine: https://adoptium.net, pick 25, or
`winget install EclipseAdoptium.Temurin.25.JDK`. Gradle finds JDKs in `~/.jdks` and the usual install dirs on its
own; you don't have to set `JAVA_HOME` to 25.

**2. IntelliJ IDEA (Community is free).** Any editor works, but IntelliJ understands Gradle projects, shows you
Minecraft's own source when you ctrl-click, and has a one-click "Minecraft Client" run button.
Install the **Minecraft Development** plugin (Settings > Plugins) for extra help with mod files. Optional.

**3. This folder.** To start *your own* mod, copy the whole folder, then rename three things:
- `settings.gradle`: `rootProject.name`
- `src/main/resources/fabric.mod.json`: `id`, `name`, `entrypoints`
- the Java package folders `xyz/tront/trontmod` and `MOD_ID` in `TrontMod.java`
The mod id is the important one: it's the namespace on every item (`trontmod:emerald_sword`), the folder name
under `assets/` and `data/`, and the prefix in the lang file. Lowercase letters, digits, underscores only.

### 1.1 First run

Open a terminal in the folder and run:

```
gradlew runClient
```

The first time, this downloads Gradle itself, the Loom plugin, Minecraft 26.2, its libraries, and Fabric API,
then decompiles nothing but *remaps* a lot. 45 s to 10 min depending on your connection. Every later run is
about 10 s. A Minecraft window opens, offline, with a fake player name. Create a world, press E: the
Combat tab has the Emerald Sword right after the diamond one; Ingredients has the Tront Coin.

That window is a *development* client. It uses `run/` inside the project for its saves and options, never your
real `.minecraft`. Close it whenever.

In IntelliJ: File > Open > this folder. Wait for "Gradle sync" to finish (bottom right). A run configuration named
**Minecraft Client** appears at the top; the green arrow does the same as `gradlew runClient`, with the debugger
attached.

### 1.2 The three commands you'll live in

```
gradlew runClient     start the dev game
gradlew build         compile + run all tests + make the jar (build/libs/)
gradlew test          just the fast tests (~5 s)
```

`gradlew` is the Gradle *wrapper*: a tiny script that downloads the exact Gradle version this project wants,
so nobody has to install Gradle. Windows runs `gradlew.bat`; typing `gradlew` finds it.

---

## Lesson 2: Reading the skeleton

Read these four files in this order. Everything else in the repo is either an item (lessons 3-4) or a test.

### 2.1 `src/main/resources/fabric.mod.json`: the mod's ID card

```json
"id": "trontmod",
"entrypoints": { "main": ["xyz.tront.trontmod.TrontMod"], "client": ["xyz.tront.trontmod.client.TrontModClient"] },
"depends": { "fabricloader": ">=0.19.3", "minecraft": "~26.2", "java": ">=25", "fabric-api": "*" }
```

Fabric reads this file out of the jar. `id` is your namespace. `entrypoints.main` is the class Fabric calls on
both client and server; `entrypoints.client` is called only on the client (rendering, keybinds, screens).
`depends` is enforced: launch with the wrong Minecraft and you get a clean error screen instead of a crash.
`${version}` gets filled in from `gradle.properties` at build time.

### 2.2 `TrontMod.java`: the entrypoint

```java
public class TrontMod implements ModInitializer {
    public static final String MOD_ID = "trontmod";
    public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

    @Override
    public void onInitialize() {
        ModItems.initialize();
        LOGGER.info("TrontMod loaded");
    }

    public static Identifier id(String path) {
        return Identifier.fromNamespaceAndPath(MOD_ID, path);
    }
}
```

`onInitialize()` runs once at startup, before any world exists. It's the right time to register things and the
wrong time to touch worlds, players, or rendering. `id("emerald_sword")` is a helper that builds
`trontmod:emerald_sword`; you'll use it constantly. `LOGGER.info` writes to the console and `logs/latest.log`;
grep for `TrontMod loaded` and you know your mod ran.

### 2.3 `ModItems.java`: where the mod actually lives

Read it now; lessons 3 and 4 walk through it line by line. It's 60 lines.

### 2.4 `build.gradle` + `gradle.properties`: the build

`gradle.properties` holds the four version numbers that matter:

```
minecraft_version=26.2
loader_version=0.19.3
loom_version=1.17-SNAPSHOT
fabric_api_version=0.158.0+26.2
```

When a new Minecraft drops, you change these (from https://fabricmc.net/develop), fix whatever stopped
compiling, done. `build.gradle` you rarely touch: it applies **Loom** (the Fabric Gradle plugin that knows how to
download Minecraft and set up run configs), declares dependencies, sets Java 25, and configures the two test
layers from lesson 5.

Two source folders: `src/main` is code that runs everywhere; `src/client` is client-only. Put rendering code in
`client` and it *cannot* accidentally crash a dedicated server.

---

## Lesson 3: The simplest item (Tront Coin)

Goal: understand the minimum an item needs. It's one Java line and four small files.

### 3.1 The Java line `TUTORIAL(3.1)`

```java
public static final Item TRONT_COIN = register("tront_coin", Item::new, new Item.Properties());
```

and the helper it calls:

```java
public static Item register(String name, Function<Item.Properties, Item> factory, Item.Properties props) {
    ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, TrontMod.id(name));
    Item item = factory.apply(props.setId(key));
    return Registry.register(BuiltInRegistries.ITEM, key, item);
}
```

Piece by piece:

- `Identifier` is `namespace:path`, here `trontmod:tront_coin`. Namespace = your mod id. Path = the item's name,
  and it must match the JSON/PNG filenames exactly.
- `ResourceKey<Item>` is an Identifier *plus which registry it belongs to*. Since 1.21.2 an item must know its
  own key before it's constructed, hence `props.setId(key)`.
- `Item.Properties` is a bag of settings: stack size, durability, food, rarity... For the coin it's empty,
  which means "stacks to 64, does nothing".
- `Item::new` is the constructor of the plain `Item` class. Want custom behaviour (right-click does something)?
  Write `class MyItem extends Item`, override a method, pass `MyItem::new` here.
- `Registry.register(BuiltInRegistries.ITEM, key, item)` puts it in the item registry. This is the moment the
  item exists.

Why `static final` fields? They run exactly once, when the class is first touched. `ModItems.initialize()` is
called from the entrypoint precisely to touch the class at the right moment (after the game is ready for
registration, before registries freeze). Registering twice, or too early, or too late, all crash; this pattern
avoids all three.

### 3.2 The four files the client needs `TUTORIAL(3.2)`

All under `src/main/resources/assets/trontmod/`:

| File | What it says |
|---|---|
| `items/tront_coin.json` | "to draw `trontmod:tront_coin`, use model `trontmod:item/tront_coin`" (the *client item definition*, new since 1.21.4; it's where fancier logic like "different model when damaged" goes) |
| `models/item/tront_coin.json` | `"parent": "minecraft:item/generated"` + the texture. `generated` = flat sprite extruded 1 px, like most items. Tools use `minecraft:item/handheld` which adds the in-hand tilt. |
| `textures/item/tront_coin.png` | 16x16 PNG with transparency |
| `lang/en_us.json` | `"item.trontmod.tront_coin": "Tront Coin"`. The key is `item.<namespace>.<path>`. No lang entry = the raw key shows in-game. |

Nothing here is Fabric-specific; it's exactly the resource pack format. If you've made a texture pack, you've
done this.

### 3.3 Creative tab `TUTORIAL(3.3)`

Registered items don't appear in the creative menu on their own. Fabric API gives you an event per tab:

```java
CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.INGREDIENTS)
    .register(output -> output.accept(TRONT_COIN));
```

`accept` appends; `insertAfter(Items.DIAMOND_SWORD, X)` and `insertBefore` place it. Without this the item still
exists (`/give` works, search works) but nobody finds it.

### 3.4 Try it

`gradlew runClient`, new creative world, `/give @s trontmod:tront_coin`. Then:
- change the name in `lang/en_us.json`, run `gradlew processResources`, press **F3+T** in-game. Name updates.
- change `new Item.Properties()` to `new Item.Properties().stacksTo(1)`, relaunch. Doesn't stack.
- rename the PNG to something else, relaunch. Purple-black checker: that's what "missing texture" looks like,
  and you'll see it a hundred times. Check the filename against the model JSON.

---

## Lesson 4: The Emerald Sword

Goal: a real weapon. Same skeleton as the coin plus: a material, weapon stats, a crafting recipe, and two tags.

### 4.1 Items are bags of data now `TUTORIAL(4.1)`

In old tutorials a sword is `new SwordItem(ToolMaterials.DIAMOND, 3, -2.4F, ...)`. That class is gone. Since 1.21.5
an item's behaviour is mostly **data components** attached to it: `MAX_DAMAGE`, `ATTRIBUTE_MODIFIERS`, `TOOL`,
`WEAPON`, `REPAIRABLE`, `ENCHANTABLE`, `FOOD`... `Item.Properties` has builder methods that set them, and the
plain `Item` class reads them. So vanilla's diamond sword is literally:

```java
registerItem(ItemIds.DIAMOND_SWORD, new Item.Properties().sword(ToolMaterial.DIAMOND, 3.0F, -2.4F));
```

and ours is the same shape:

```java
public static final Item EMERALD_SWORD = register("emerald_sword", Item::new,
    new Item.Properties().sword(EMERALD_MATERIAL, 3.0F, -2.4F));
```

`.sword(material, attackDamage, attackSpeed)` attaches, in one call: durability, repair items and enchantability
(from the material), an attack-damage and attack-speed attribute modifier, a `TOOL` component (instant-mines
cobwebs, 1.5x on bamboo etc., 2 durability per block), and a `WEAPON` component (1 durability per hit, disables
shields). Damage shown in-game = `attackDamage` + material bonus + the player's base 1. For us: 3 + 3 + 1 = 7,
same as diamond.

Same idea for other tools: `.pickaxe(material, 1.0F, -2.8F)` on a plain `Item`. Shovels, axes and hoes still use
small subclasses (`ShovelItem`, `AxeItem`, `HoeItem`) because they have right-click behaviour (path blocks, strip
logs, till dirt). Look at `Items.java` in the decompiled source: ctrl-click `Items.DIAMOND_SHOVEL` from IntelliJ.

### 4.2 The material `TUTORIAL(4.2)`

```java
public static final ToolMaterial EMERALD_MATERIAL = new ToolMaterial(
    BlockTags.INCORRECT_FOR_DIAMOND_TOOL, 1200, 8.0F, 3.0F, 20, EMERALD_TOOL_MATERIALS);
```

`ToolMaterial` is a plain record with six fields. Vanilla, for calibration:

| material | can't mine | durability | mining speed | damage bonus | enchantability | repairs with |
|---|---|---|---|---|---|---|
| IRON | `INCORRECT_FOR_IRON_TOOL` | 250 | 6 | 2 | 14 | `#iron_tool_materials` |
| DIAMOND | `INCORRECT_FOR_DIAMOND_TOOL` | 1561 | 8 | 3 | 10 | `#diamond_tool_materials` |
| NETHERITE | `INCORRECT_FOR_NETHERITE_TOOL` | 2031 | 9 | 4 | 15 | `#netherite_tool_materials` |
| **emerald (ours)** | `INCORRECT_FOR_DIAMOND_TOOL` | 1200 | 8 | 3 | 20 | `#trontmod:emerald_tool_materials` |

"Can't mine" is a block tag listing what this tier *fails* to drop (obsidian for iron, nothing extra for
diamond). "Repairs with" is an **item tag**, which brings us to tags.

### 4.3 Tags: named lists, defined in JSON `TUTORIAL(4.3)`

A tag is a list of ids with a name, living in a data pack. Two in this mod:

`data/trontmod/tags/item/emerald_tool_materials.json` (ours, referenced by the material):
```json
{ "values": ["minecraft:emerald"] }
```

`data/minecraft/tags/item/swords.json` (note the `minecraft` folder: this *adds to vanilla's* `#minecraft:swords`
tag; tags from all packs merge unless a file says `"replace": true`):
```json
{ "values": ["trontmod:emerald_sword"] }
```

Why join `#minecraft:swords`? Vanilla's enchantment rules are tag-based: `enchantable/melee_weapon`,
`enchantable/durability` and `enchantable/sweeping` all include `#minecraft:swords`. One line and Sharpness,
Unbreaking and Sweeping Edge work on our sword. Miss it and the enchanting table offers nothing.

In Java, a tag is referenced by a `TagKey`:
```java
public static final TagKey<Item> EMERALD_TOOL_MATERIALS = TagKey.create(Registries.ITEM, TrontMod.id("emerald_tool_materials"));
```
The Java side never lists the contents; the JSON does. Server loads it, syncs it to clients.

### 4.4 The recipe `TUTORIAL(4.4)`

`data/trontmod/recipe/emerald_sword.json`, copied from vanilla's diamond sword recipe with two ids changed:

```json
{
  "type": "minecraft:crafting_shaped",
  "category": "equipment",
  "key": { "#": "minecraft:stick", "X": "#trontmod:emerald_tool_materials" },
  "pattern": ["X", "X", "#"],
  "result": { "id": "trontmod:emerald_sword" }
}
```

A `#` in front of an ingredient means "any item in this tag". No Java at all. This is the single most useful
habit in modding: **find the vanilla file that does what you want and copy it.** Every vanilla recipe, tag,
model, loot table and advancement is a JSON file inside the Minecraft jar. In this project they're at
`~/.gradle/caches/fabric-loom/26.2/minecraft-common.jar` (data) and `minecraft-client-only.jar` (assets); open
with any zip tool. In IntelliJ, External Libraries > minecraft shows the same.

Recipes are data-pack content, so the game only re-reads them with `/reload` (or a relaunch), not F3+T.

### 4.5 Model and tab

`models/item/emerald_sword.json` uses `"parent": "minecraft:item/handheld"` so it tilts in the hand like a
sword instead of lying flat like a coin. The creative hook uses `insertAfter(Items.DIAMOND_SWORD, EMERALD_SWORD)`
in the Combat tab.

### 4.6 Try it

Survival world: `/give @s minecraft:emerald 2`, a stick, craft it in a table. Hit something: 7 damage. Anvil it
with an emerald: repairs. Enchanting table: Sharpness shows up. Hover: durability 1200.

Then change `1200` to `2500` and `20` to `30`, `gradlew runClient`, check the tooltip. That's the whole loop.

---

## Lesson 5: Proving it works without clicking

Launching the game and clicking around is slow, and you'll forget to check something. This template has two
kinds of automated tests; `gradlew build` runs both and refuses to make a jar if either fails.

### 5.1 Unit tests (`src/test/`, ~5 s)

`ModItemsTest.java` boots just enough of Minecraft to fill the registries, registers our items, and checks:
the ids exist, the sword has 7 attack damage, 1200 durability, the `WEAPON` component, and repairs with our tag.
No window, no world.

There's one non-obvious dance in `@BeforeAll`, and it's worth understanding because you'll hit it:
since 26.x, an item's components aren't attached when it's registered; the server attaches them when a world
loads, because some items reference data-pack registries (trim materials, enchantments). A unit test has no
world, so it does what the server does:

```java
BuiltInRegistries.DATA_COMPONENT_INITIALIZERS.build(VanillaRegistries.createLookup())
    .forEach(DataComponentInitializers.PendingComponents::apply);
```

Skip it and `new ItemStack(...)` throws `Components not bound yet`. Now you know what that message means.

### 5.2 Game tests (`src/gametest/`, ~10 s)

`TrontModGameTest.java` runs inside a real headless server with the mod's `data/` loaded. That's the only place
recipes and tags can be checked, because they're data-pack content. Three tests: the stats again (now with real
components), "is the sword in `#minecraft:swords`", "did the recipe load". A `@GameTest` method gets a
`GameTestHelper`; call `helper.succeed()` at the end or it fails after a timeout.

`build.gradle` has `eula = true` in the `configureTests` block: that accepts the Minecraft EULA for the throwaway
test server.

### 5.3 The habit

Change something, `gradlew build`. Green: launch the game and enjoy it. Red: the message tells you which
assertion, before you spent two minutes loading a world.

---

## Lesson 6: Art

### 6.1 The rules of a 16x16 Minecraft item

Look at vanilla's swords (`assets/minecraft/textures/item/diamond_sword.png` in the client jar):
- Diagonal, pommel bottom-left, tip top-right, touching both corners.
- 3 to 4 tones per material, no gradients, no anti-aliasing.
- Light from top-left: highlight on the upper-left edge, shadow on the lower-right edge.
- **No black outline.** Edges are the darkest tone *of that material*.
- Any power-of-two size works (32, 64), but 16 is what sits next to vanilla without looking imported.

Tools: Aseprite (paid, the standard), Piskel (free, browser), Blockbench (free, also does 3D models).

### 6.2 From an AI image to a texture (`art/`)

AI image generators produce "fake pixel art": it looks pixelated but the grid is uneven and there are hundreds of
colours. `art/unfake_texture.py` fixes that: detects the grid, snaps every cell to one colour, removes the
background, recolours the black outline into material-dark, and downscales with a palette lock to Mojang's own
emerald/iron/wood tones:

```
python art/unfake_texture.py art/emerald_sword_source.png art/emerald_sword --outline-tint "#0a4a22" --tint-mix 0.6 --palette vanilla-emerald
```

Outputs a faithful 64x64 and mechanical 32/16 downscales. Honest result: the 64 keeps everything, the 16 turns
to mush. So the shipped 16x16 was built differently: `art/vanilla_sword_16.py` takes vanilla's diamond sword
skeleton and remaps its tones (emerald blade, iron guard). Design your own, or start from vanilla's silhouette;
both are in `art/` and the README shows the one-line swap.

### 6.3 The texture loop

Edit PNG → `gradlew processResources` → **F3+T** in the running game. No relaunch.

---

## Lesson 7: Shipping

```
gradlew build
```
makes `build/libs/trontmod-0.1.0.jar`. Everything the game needs is inside: classes, assets, data,
fabric.mod.json. (`-sources.jar` is for other developers; ignore it.)

**Your real game:** install Fabric Loader once (https://fabricmc.net/use, pick 26.2; it adds a "fabric-loader"
profile to the launcher; already done on this machine). Put the jar plus `fabric-api-<version>.jar` (from
https://modrinth.com/mod/fabric-api, matching 26.2) in `%APPDATA%\.minecraft\mods\`. Launch the Fabric profile.

**Friends:** send them both jars, same folder. Everyone in a multiplayer world needs the mod (it adds an item;
the server would reject a client without it).

**Publishing:** Modrinth (https://modrinth.com) is where Fabric mods live. You'll need a license in
`fabric.mod.json` (currently MIT), an icon (`assets/trontmod/icon.png`), and a version bump in
`gradle.properties` per release.

---

## Exercises

Do them in order; each one is a few lines.

1. **A third item.** Add `ruby` to `ModItems`, the four asset files, the lang line. Put it in Ingredients.
   Check: `gradlew test` still green, `/give @s trontmod:ruby` works. (Draw an 8-colour 16x16 diamond shape; or
   copy `tront_coin.png` and recolour it.)
2. **Stronger sword.** Make it hit for 8 without touching the material. (Hint: the `3.0F` in `.sword(...)`.)
   Fix the unit test and game test that now fail; they assert 7.
3. **Emerald pickaxe.** `new Item.Properties().pickaxe(EMERALD_MATERIAL, 1.0F, -2.8F)`, model parent `handheld`,
   recipe copied from `data/minecraft/recipe/diamond_pickaxe.json`, and add it to `#minecraft:pickaxes`
   (`data/minecraft/tags/item/pickaxes.json`). Mine obsidian: it should work (it's a diamond-tier material).
4. **Cheaper recipe.** Make the sword cost one emerald and two sticks. No Java.
5. **A tab of your own.** Create a creative tab for the mod instead of piggybacking on Combat. Look up
   `FabricCreativeModeTab` / `CreativeModeTab.builder()` in the Fabric docs; register it in
   `BuiltInRegistries.CREATIVE_MODE_TAB` the same way items are registered.
6. **Behaviour.** `class EmeraldSwordItem extends Item`, override `hurtEnemy(...)` to give the target Glowing
   for 3 seconds, pass `EmeraldSwordItem::new` to `register`. First real Java; use IntelliJ's ctrl-O to see what
   `Item` lets you override.

---

## Appendix A: "Why doesn't the tutorial I found work?"

Minecraft 26.1 shipped unobfuscated; Fabric dropped Yarn and renamed its API to Mojang's names. Any tutorial,
video or StackOverflow answer from before March 2026 is in the old dialect. Same concepts, different words:

| Old (Yarn / 1.21 and earlier) | Now (26.x, Mojang names) | Notes |
|---|---|---|
| `Identifier.of("mod", "x")` / `new Identifier(...)` | `Identifier.fromNamespaceAndPath("mod", "x")` | class was `ResourceLocation` in Mojmap 1.21 |
| `Registries.ITEM` (the registry) | `BuiltInRegistries.ITEM` | `Registries.ITEM` now means the registry *key* |
| `RegistryKey.of(RegistryKeys.ITEM, id)` | `ResourceKey.create(Registries.ITEM, id)` | |
| `new SwordItem(ToolMaterials.DIAMOND, 3, -2.4F, settings)` | `new Item(new Item.Properties().sword(ToolMaterial.DIAMOND, 3.0F, -2.4F))` | `SwordItem` removed 1.21.5 |
| `ToolMaterials.DIAMOND` (enum) | `ToolMaterial.DIAMOND` (record) | custom: `new ToolMaterial(...)` |
| `Item.Settings` | `Item.Properties` | |
| `settings.maxCount(1)` | `props.stacksTo(1)` | |
| `settings.maxDamage(n)` | `props.durability(n)` | |
| `ItemGroupEvents.modifyEntriesEvent(ItemGroups.COMBAT)` | `CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.COMBAT)` | package `net.fabricmc.fabric.api.creativetab.v1` |
| `entries.add(item)` | `output.accept(item)` | also `insertAfter/insertBefore` |
| `Registry.register(Registries.ITEM, id, item)` | `Registry.register(BuiltInRegistries.ITEM, key, item)` | item must have `setId(key)` first |
| `TagKey.of(RegistryKeys.ITEM, id)` | `TagKey.create(Registries.ITEM, id)` | |
| `BlockTags.INCORRECT_FOR_DIAMOND_TOOL` | same | tags kept their names |
| `Text.translatable(...)` | `Component.translatable(...)` | |
| `PlayerEntity`, `World`, `BlockPos`, `ItemStack` | `Player`, `Level`, `BlockPos`, `ItemStack` | |
| `MinecraftClient.getInstance()` | `Minecraft.getInstance()` | |
| `net.minecraft.util.Identifier` | `net.minecraft.resources.Identifier` | |
| `yarn_mappings=` in gradle.properties | gone | no mappings line at all |
| `loom.officialMojangMappings()` / `mappings "net.fabricmc:yarn..."` | gone | Loom uses the unobfuscated jar |

The fastest way to translate anything else: the concept still exists, so open the decompiled vanilla source
(ctrl-click from IntelliJ, or `gradlew genSources` then browse `.gradle/loom-cache/`) and search for what vanilla
calls it. Fabric's own porting guide: https://docs.fabricmc.net/develop/porting/mappings/

## Appendix B: Where next

Read, in this order:
- Fabric docs: https://docs.fabricmc.net/develop/ (items, blocks, entities, commands, networking; current for 26.x)
- Fabric wiki tutorials: https://wiki.fabricmc.net/tutorial:start (older, still useful for concepts)
- Kaupenjoe's Fabric 26.X series: https://github.com/Tutorials-By-Kaupenjoe/Fabric-Tutorial-26.X (video +
  code per episode; blocks, ores, armor, entities, GUIs, world gen)
- The Fabric Discord (link on fabricmc.net) for "why does this crash": paste `logs/latest.log`
- Misode's generators https://misode.github.io for recipe/loot/advancement JSON, Blockbench for models
- The vanilla source. Seriously. It's all there and unobfuscated now.

What this template deliberately does not cover, and where each lives:
- **Blocks** (`Block`, `BlockItem`, blockstates + block models): Fabric docs "Blocks"
- **Entities and mobs**: Fabric docs "Entities"; big topic
- **Mixins** (patching vanilla code): the template stripped the example mixins; Fabric wiki "Mixin introduction".
  Use only when no event/API exists.
- **Networking, GUIs, config, world gen, armor**: Fabric docs, Kaupenjoe
- **NeoForge**: same concepts, `DeferredRegister` instead of static fields, `MDK-26.2` template on GitHub

## Appendix C: Glossary

- **Registry**: the game's table of all things of one kind (items, blocks...). Adding = registering.
- **Identifier**: `namespace:path`. Namespace = mod id. Also the filename convention for every asset.
- **ResourceKey**: an Identifier that also says which registry. Items need theirs before construction.
- **Data component**: a typed piece of data on an item (or stack): durability, food, attributes, custom name.
  Replaced NBT for items in 1.20.5.
- **Tag**: a named list of ids, defined by JSON, merged across packs, referenced in code by `TagKey`.
- **Resource pack / assets**: client-side files (models, textures, lang, sounds). Reload with F3+T.
- **Data pack / data**: server-side files (recipes, tags, loot tables, advancements). Reload with `/reload`.
- **Entrypoint**: the class Fabric calls when your mod starts. Declared in `fabric.mod.json`.
- **Fabric API**: the mod that provides events and helpers (creative tabs, callbacks). Separate jar, required.
- **Fabric Loader**: the thing that loads mods. Installed into the launcher; in dev, Loom handles it.
- **Loom**: the Gradle plugin that sets up Minecraft as a dependency and generates run configs.
- **Gradle / gradlew**: the build tool / its self-downloading wrapper.
- **Mixin**: bytecode patching of vanilla classes. Powerful, fragile, last resort.
- **Mappings**: historical: names for obfuscated classes. Irrelevant since 26.1; if a tutorial mentions Yarn,
  it's old.
- **Client / server**: even singleplayer runs an internal server. Code that draws is client; code that decides
  is server. `src/client` vs `src/main`.
