Skill Tree JSON Format
The format of a JSON file that defines a skill tree, placed in the skilltrees folder of your namespace (data/<namespace>/skill_trees/<name>.json).
The format of a JSON file that defines a skill tree, placed in the skill_trees folder of your namespace (data/<namespace>/skill_trees/<name>.json). A skill tree file only defines the tree itself. Its tab, background, and the powers every holder of the tree gets by default. The individual skills that appear in the tree are not defined here; each skill lives in the power file it unlocks, via Skill Tree Power Data (the same way Origins badges live on their powers).
How a tree reaches a player: by default (
auto_grant: true) every player has the tree. Setauto_grant: falseto hide it until it is explicitly granted with Grant Skill Tree (Entity Action Type) from an origin’s power, an item, another power’s action, or any other entity-action source. Revoke Skill Tree (Entity Action Type) takes it away again (purchased skills are remembered and come back if the tree is re-granted). Because grants are stored per player and only change on those actions, there is no per-tick condition checking. A tree can additionally carry aconditionfor the cases a stored grant can’t express — see Tying a tree to an origin.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
name | Text Component | empty | The display name of the tree’s tab. |
description | Text Component | empty | The description of the tree’s tab. |
icon | Icon | {"item": "minecraft:grass_block"} | The item or texture shown as the tree’s tab icon. |
frame | task | goal | challenge | task | The border drawn around the tree’s tab icon, matching the advancement frames. |
background | Identifier | optional | The texture used as the background of the tree screen. |
default_powers | Array of Identifier | optional | Powers granted to every player who has the tree (e.g. a point-earning power). Removed when the tree is revoked. |
order | Integer | 0 | Tab order among trees — lower values come first; ties keep load order. |
auto_grant | Boolean | true | If true, every player automatically has this tree (revoking has no lasting effect). If false, the tree is hidden until granted via the grant action. |
condition | Entity Condition Type | optional | If set, the tree only counts for players who fulfil it — see Tying a tree to an origin. |
refundable | Boolean | true | If true, players can shift-click a purchased skill in the tree screen to un-buy it — the power is removed and its point cost returned (only allowed while no purchased skill depends on it). Set false to make purchases permanent (admins can still unbuy via the /apoli:skill_tree command). |
Tying a tree to an origin
auto_grant and the grant/revoke actions are stored state: once a player has a tree, they keep it until something takes it away. condition is the live half — a tree whose condition is false still remembers everything the player bought, but none of its powers apply and its tab is hidden. When the condition becomes true again, the purchases come straight back.
That is what ties a tree to an origin, so its skills don’t follow the player into their next one:
{
"name": "Pyromancy",
"condition": {
"type": "origins:origin",
"layer": "origins:origin",
"origin": "my_pack:ember"
}
} A tree with no condition is global — every player keeps its powers through any number of origin changes, which is what you want for something like a server-wide progression tree.
The condition is re-evaluated whenever Origins reconciles a player’s origins (join, data-pack reload, orb, and every origin choice) — not every tick. A condition on something that changes constantly, like health or the time of day, will not track it in real time; gate on an origin, an advancement or a power instead.
Tree file or skill file?
A file in skill_trees/ is read as a skill — not a tree — when it has a parent field. That one field is the whole distinction:
| The file has… | It is… |
|---|---|
no parent | a tree; the fields above apply |
a parent | a skill inside a tree |
So a pack can define its skills either way:
- inside the power file, via Skill Tree Power Data — the power is the skill
- as its own file in
skill_trees/, naming the power(s) it unlocks
Both end up in the same tree and behave identically. Power-data skills win if both define the same id.
Skill file fields
| Field | Type | Default | Description |
|---|---|---|---|
parent | Identifier | required | The tree, or another skill, this one hangs off. |
power / powers | Identifier or Array of Identifier | none | The power(s) buying this skill grants. |
name | Text Component | skill.<ns>.<path>.name | Display name. |
description | Text Component | skill.<ns>.<path>.description | Display description. |
icon | Icon | {"item": "minecraft:grass_block"} | Node icon — an item id, an item stack, or a texture. |
frame | task | goal | challenge | task | Border drawn around the node, matching the advancement frames. |
cost | Integer | 0 | Points needed to buy it. |
excludes | Array of Identifier | [] | Skills that become unbuyable once this one is bought, and vice versa. |
condition | Entity Condition | optional | Must pass before the skill can be bought. |
visibility_condition | Entity Condition | optional | Must pass for the node to appear at all. |
order | Integer | 0 | Sort order among siblings. |
A skill with no power/powers is a free pass-through node: it can’t be bought, and its children treat it as already satisfied. Use it to branch a tree without charging for the branch point.
{
"parent":"example_pack:parkour_skills",
"power":"example_pack:double_jump",
"icon":{
"item":"minecraft:feather"
},
"cost":2,
"condition":{
"type":"apoli:advancement",
"advancement":"example_pack:learned_to_jump"
}
} Every skill’s
parentchain must end at a tree file. A skill whose chain never reaches one is dropped with a warning naming the file — it will not show up anywhere.
Example
{
"name": "Parkour Skills",
"description": "A skill tree that lets you earn parkour skills",
"icon": {
"item": "minecraft:stick"
},
"default_powers": [
"example_pack:gain_points"
],
"auto_grant": false
} This tree (example_pack:parkour_skills) stays hidden until something runs:
{
"type": "apoli:grant_skill_tree",
"skill_tree": "example_pack:parkour_skills"
} A skill is then attached to the tree from inside its power file:
{
"type":"apoli:multiple",
"skill":{
"parent":"example_pack:parkour_skills",
"icon":{
"item":"minecraft:feather"
},
"cost":2
}
} Screen controls
- Drag with the left mouse button to pan the tree.
- Mouse wheel zooms out/in (down to 25%), centered on the window. Hovering a node always pops a full-size info card, so small nodes stay readable while zoomed out. When the whole tree fits in the window it stays centered.
- Opening the screen requests a fresh skill state from the server, so per-skill conditions are up to date the moment it opens (an open screen also refreshes live whenever the server pushes a new state).