-
Notifications
You must be signed in to change notification settings - Fork 0
Building Advancement Bundles
The advancement viewer never reads datapack JSON. You compile a datapack into a bundle first, with mcw-adv-preprocess, and the widget loads the bundle. This page shows how.
The preprocessor parses and validates your advancement JSON the way the game does, builds the tree, runs the same layout algorithm the game runs, downloads an icon for every item you referenced, and writes the result. Layout happens here rather than in the browser, so the widget only has to draw.
git clone https://github.com/KabanFriends/mcwidgets.git
cd mcwidgets
npm ci
npm run build
You need Node 20.19 or later, or Node 22.12 or later. The build produces the preprocessor at packages/advancements/bin/mcw-adv-preprocess.mjs.
Building icons needs network access. The preprocessor downloads item renders and vanilla textures from public CDNs, pinned to one Minecraft version so two runs of the same input produce the same output.
Point --input at your datapack and --output at an empty directory.
node packages/advancements/bin/mcw-adv-preprocess.mjs \
--input ~/server/world/datapacks/my_pack \
--output ./public/advancements \
--bake-lang ~/server/lang/en_us.json
It reports what it built on stderr:
16 advancements -> 1 tabs / 16 nodes
wrote ./public/advancements/bundle.json
Two layouts, detected automatically.
A datapack. Any directory containing data/. Advancements are read from data/<namespace>/advancement/**/*.json, or advancements/ for older packs. The advancement id comes from the namespace and the path, so data/mypack/advancement/mining/deep.json becomes mypack:mining/deep.
A flat folder. Any other directory. Every .json file under it, at any depth, becomes minecraft:<relative path without .json>.
public/advancements/
bundle.json
icons/minecraft/grass_block.png
icons/minecraft/wooden_pickaxe.png
...
backgrounds/ (only for custom tab backgrounds)
bundle.json references the icons by relative path, and the widget resolves them against the bundle's own URL. Move or rename the directory freely. Split it up and the icons stop loading.
Serve the whole directory. If the page that runs the widget is on another origin, bundle.json needs Access-Control-Allow-Origin, and the icons beside it do not. See Embedding with an iframe for which origin actually matters.
| Option | Effect |
|---|---|
--input <dir> |
Datapack root or folder of advancement JSON. Required. |
--output <dir> |
Output directory. Default ./dist. |
--exclude <name> |
Skip JSON under any folder with this name. Repeatable. |
--bake-lang <file.json> |
Resolve translate components at build time. See Text and fonts. |
--tab-order <list> |
Comma-separated tab ids or path prefixes, in display order. |
--icon <id>=<file or url> |
Replace one item's icon. Repeatable. |
--icon-map <file.json> |
The same overrides as a JSON object of id to file or URL. |
--icon-source <ref> |
Git ref of the item render source. Default master. |
--source <string> |
A provenance string recorded in the bundle's metadata. |
--single-file |
Inline every icon and background as a data: URI. |
--quiet |
Suppress warnings. |
--json-errors |
Print errors and warnings as JSON. |
--version |
Print the preprocessor version. |
--help |
Print the option list. |
Vanilla ships one recipe-toast advancement per recipe, over a thousand of them, anchored under a root with no display. They produce no tab and nothing worth showing.
--exclude recipes
The name matches any path segment below --input, so this works for both datapack layouts.
Tabs are ordered by advancement id by default, because JSON files carry no order of their own. The game orders them by datapack registration order instead, so vanilla comes out alphabetical rather than in the familiar sequence.
Name the order you want. A key matches a root's full id, its path, or a path prefix.
--tab-order story,adventure,husbandry,nether,end
Roots you do not name keep their id order, after the named ones. A key matching nothing is reported as a warning.
By default every display.icon is fetched as the 3D item render the game draws in inventories. Custom items your datapack invents have no such render, so override them with a local PNG or a URL.
--icon mypack:soul_shard=./textures/soul_shard.png
--icon minecraft:diamond=https://example.com/my_diamond.png
An id with no namespace is read as minecraft:. For more than a handful, put them in a file:
{
"mypack:soul_shard": "./textures/soul_shard.png",
"mypack:frost_blade": "./textures/frost_blade.png"
}--icon-map ./icons.json
An icon that cannot be resolved becomes the purple and black missing-texture square, and the run prints a warning and continues. A missing icon is not worth failing a build over, but it is worth noticing.
Inlines every icon and background into bundle.json as a data: URI, so the bundle is one file with no directory around it. Convenient to drop on a host that only takes single files, and much larger over the wire, since base64 costs a third and the icons no longer cache separately.
It rejects the build, with the offending file named, when:
- Two advancements share an id.
- An advancement names a parent that does not exist.
- Parent links form a cycle.
-
criteriais missing or empty, or a criterion has notrigger. -
requirementsdoes not cover exactly the criteria that exist. -
displayis missingicon,title, ordescription. -
display.frameis nottask,goal, orchallenge. -
displaycontains an unknown field. This catches typos that would otherwise be silently ignored. - Every root advancement lacks a
display, leaving no tab to show.
Server-only fields such as rewards and sends_telemetry_event are ignored and are not published in the bundle.
An advancement with no display is invisible, and its children attach to the nearest visible ancestor instead. This is what the game does, and it is how a hidden organisational node disappears without orphaning the branch below it.
A root advancement with no display produces no tab. Its children still attach. The run prints a warning naming the root.
An advancement with "hidden": true is left out of the rendered tree until it is marked complete, matching the game, where the server never sends an unearned hidden advancement. Turn on showHidden to draw them anyway, which is usually what a guide wants. See Advancement viewer reference.
Tabs fill the top edge, then the bottom, then the left, then the right. How many fit depends on the size of the widget, not the size of the bundle. A tab with no room left is not drawn and cannot be reached.
At the minimum window size the capacities match the game exactly: 8 above, 8 below, 5 left, and 5 right. A larger widget fits more per side. If your pack has many roots, give the embed a larger box and check the result at the size your readers will see.
<iframe
src="https://mcwidgets.kaban.sh/advancements/embed.html?bundle=https://example.com/advancements/bundle.json"
style="width: 100%; height: 420px; border: 0;"
title="Server advancements"
></iframe>Or mount it yourself. See Using the JavaScript API.
Embedding
Reference
Building bundles
Running it yourself