Data Pack Basics
No code needed. Drop a JSON file into a data pack and Legacy picks it up.
Each definition is one JSON file, found the same way as advancements or loot tables. Any data pack, in any namespace, can add new files or override existing ones with the same path.
data/<namespace>/legacy/
titles/
my_title.json
journeys/
my_journey.json
milestones/
my_milestone.json
/legacy reload reloads titles live, a vanilla /reload refreshes all three. Every file is validated on load: a broken file logs a warning and is skipped, the rest keeps working.
Adding a Title
One JSON file and one language entry.
data/mypack/legacy/titles/blaze_hunter.json:
{
"translation_key": "title.mypack.blaze_hunter",
"category": "combat",
"icon": "minecraft:blaze_rod",
"rarity": "rare",
"placement": "suffix",
"trigger": {
"type": "statistic",
"stat_type": "minecraft:killed",
"stat": "minecraft:blaze",
"value": 20
}
}
Then add the name to assets/mypack/lang/en_us.json: "title.mypack.blaze_hunter": "Blaze Hunter". For a feminine form add the same key with .female at the end. If you do not need translations, use "title": "Blaze Hunter" instead of translation_key.
trigger (required): when the title unlocksrarity: common, uncommon, rare, epic, legendary or mythicplacement: prefix or suffix (default)series and stage: chain titles, the journal reveals one stage at a timehidden: true keeps it secret until unlockedicon, category, display_priority: how it is shown and sortedid: defaults to the file name
Adding a Journey
A first time worth remembering, with its own text right in the file.
data/mypack/legacy/journeys/first_elytra.json:
{
"category": "equipment",
"icon": "minecraft:elytra",
"title": "First Elytra",
"description": "Claim the elytra and take to the skies.",
"trigger": { "type": "item", "id": "minecraft:elytra" }
}
No language file needed. To translate it, add a translation_key and put <key> and <key>.desc in your language files. The literal text stays as fallback.
- Categories:
equipment, exploration, survival, farming, villagers, combat, magic, misc - Any other category gets its own header
Adding a Milestone
A world-first for your server.
data/mypack/legacy/milestones/first_wither.json:
{
"icon": "minecraft:nether_star",
"rarity": "legendary",
"shared": true,
"grace_period": 300,
"trigger": { "type": "kill", "id": "minecraft:wither" },
"retro_advancement": "minecraft:nether/summon_wither"
}
The name comes from milestone.<namespace>.<id> in your language files, unless you set a translation_key.
- Milestones use
kill, dimension or advancement triggers shared: others can join in during grace_period secondsretro_advancement: marks the milestone as taken on worlds that already had it
Trigger Types
All three systems share the same trigger block.
Titles and journeys are checked every few seconds. Milestones react to the event itself.
advancement: the player has an advancement, e.g. minecraft:end/kill_dragonstatistic: a vanilla statistic reaches a value, retroactivecounter: a Legacy counter reaches a value, e.g. harvest:crops, biomes_distinct, place:saplings, bonemeal:used, mine:#minecraft:logsitem: the player holds an item anywhere in the inventorycustom: a predicate registered by another modkill / dimension: milestones only, first kill of an entity or first visit to a dimension- Legacy counters start at 0 when the mod is installed
For Mod Developers
Custom triggers in Java, when JSON is not enough.
Register a predicate and use it from any data pack with "trigger": { "type": "custom", "id": "mymod:fate" }.
LegacyAPI.registerCustomTrigger("mymod:fate",
(serverPlayer, title) -> serverPlayer.getInventory().contains(new ItemStack(Items.NETHER_STAR)));
LegacyAPI.grant(player, id) and LegacyAPI.revoke(player, id) give and take titles directly.