Chapters
GameStages-style item progression for NeoForge 1.21.
Chapters
Full documentation (datapacks, KubeJS, FTB, examples): GitHub Wiki
items, fluids, Mekanism chemicals, and recipes is a progression mod for items, fluids, Mekanism chemicals, and recipes inspired by *GameStages* and *ItemStages*. It lets you split your modpack into named "chapters" (stages) and gate items, fluids, Mekanism chemicals, and recipes behind them, so players cannot interact with locked content until you unlock the stage.
It targets pack authors who want FTB for progression on modern NeoForge, with first-class support for FTB, FTB, FTB, FTB, and FTB (Library, Teams, Quests).
FTB Library, FTB Teams, and FTB Quests
When Stage Required is installed, Chapters registers as the FTB Stage Required (`StageHelper`). Stage Required can then use the built-in Stage Required (grant a chapter on claim), Stage Required (require a chapter), and Stage Required on quests or chapters, all using your chapter ids with no extra wiring.
With team-scoped as well, unlocks are team-scoped via FTB's `TEAM_STAGES`: the whole party shares chapter progress, inventory checks and JEI updates apply for every online member, and `/chapters`, KubeJS `PlayerStages`, and FTB Quests stage rewards all read and write the same team storage. Joining a team adopts that team's stages; leaving personal progress behind follows FTB's team model (see the wiki for migration details).
For branching logic (e.g. only grant if some other condition holds), combine FTB Quests Custom Reward with KubeJS. Example: `examples/kubejs/server_scripts/ftbquests_chapter_reward.js` in the GitHub repo.
What you can lock
- Whole mod at once: by id, by tag (`#minecraft:swords`), or whole mod (`@create`). - Whole mod at once: by id, tag, or mod. Buckets, placement, and NeoForge fluid transfers are checked where a server player applies. - Whole mod at once: gas / infusion / slurry / pigment by id, tag, or mod, on Mekanism `ChemicalUtils` paths when a server player is in scope. - Whole mod at once: by recipe id (`minecraft:diamond_pickaxe`, KubeJS `recipe:…`, etc.). Vanilla crafting-grid recipes are blocked server-side until unlocked. - Whole mod at once: `@modid` covers items, fluids, and chemicals from that namespace in one stage entry.
Locked items are auto-dropped from inventory while the player lacks the stage (tick while online, and on stage removal or reload), so stashing in shulkers does not bypass the lock.
JEI
With JEI, Chapters syncs stages to the client so JEI can hide locked ingredients, hide output recipes for locked items/fluids/chemicals (`TYPE_CHEMICAL` when Mekanism is present), hide recipes by locked recipe id, and show them again after unlock (`includeHidden()` on focus).
Commands
``` /chapters add <player> <stage> /chapters remove <player> <stage> /chapters list <player> /chapters check <player> <stage> /chapters reload ```
Datapack stages
Path:
``` data/<namespace>/chapters/stages/<stage_id>.json ```
Example `data/mypack/chapters/stages/tier1.json`:
```json { "items": [ "minecraft:netherite_ingot", "#minecraft:swords", "minecraft:enchanted_book" ], "fluids": [ "minecraft:lava", "#minecraft:water", "@mymodfluids" ], "chemicals": [ "mekanism:hydrogen", "#mekanism:gases", "@mekanism" ], "recipes": [ "minecraft:diamond_pickaxe" ] } ```
Syntax
| Prefix | Meaning | | --- | --- | | `minecraft:foo` | Single id | | `#namespace:tag` | Tag (items, fluids, chemicals) | | `@modid` | All items, fluids, and Mekanism chemicals from that mod |
Optional keys: `namespaces`, `fluid_namespaces`, `chemical_namespaces`.
If something appears in at least one stage file, the player needs at least one of the stages that list it (OR across definitions that mention the same ingredient), so packs and scripts can layer rules without overwriting each other.
KubeJS
```js ServerEvents.loaded(event => { ChaptersEvents.defineStage( 'mypack:tier1', [ 'minecraft:netherite_ingot', '#minecraft:swords', 'recipe:minecraft:diamond_pickaxe', 'fluid:minecraft:lava', 'chemical:mekanism:hydrogen', '@create' ] ) }) ```
- `ChaptersEvents.defineStage(stageId, entries)`. Multiple calls in the same tick batch into one index rebuild. - `PlayerStages.of(player).add(stageId)` / `.remove(stageId)` / `.has(stageId)` / `.get()`
Quick start
The jar ships no preset stages. Quick test: add a datapack stage gating `minecraft:netherite_ingot`, `/reload`, `/chapters check <you> namespace:stage` (false), try `/give`, then `/chapters add` (true). Sample scripts: no.
Compatibility
| Mod | Notes | | --- | --- | | NeoForge 1.21.1 (21.1.x) | Required | | Java 21 | Required | | JEI | Optional. Hides locked content client-side | | KubeJS | Optional. Bindings load automatically | | Mekanism | Optional. Chemical locking; without it, chemical keys in JSON are ignored at index time | | FTB Library | Optional. Stage provider for FTB Quests UI | | FTB Teams | Optional. Shared team stages when Library + Teams are both present | | FTB Quests | Optional. Stage Reward / Task / Stage Required |
Recipe blocking in the mixin targets the vanilla crafting grid (`CraftingMenu`) for now. Other stations may still be hidden in JEI when locked but not blocked server-side the same way.
Links
- License: https://github.com/GabinFqt/chapters - License: https://github.com/GabinFqt/chapters/releases - License: MIT
---