Why
Your own advancement tab is a json datapack with criteria that almost never describe what you need. Usually you just want to "grant the advancement when the plugin decides".
EffectiveAdvancement is the visual layer only: tab, tree, toast, frame. No criteria — the plugin decides when by calling grant.
Minimal example
Root and child
object RootAdvancement : EffectiveAdvancement() {
override fun getNamespacedData() = MyPlugin.instance to "root"
override fun getParent() = null
override fun getDisplay() = DisplayData(
title = "My plugin",
description = "Start",
icon = IconData.fromItem(RubyItem.createItemStack()),
frame = FrameType.TASK,
background = "minecraft:textures/block/stone.png",
)
fun init() {}
}
object FirstRuby : EffectiveAdvancement() {
override fun getNamespacedData() = MyPlugin.instance to "first_ruby"
override fun getParent() = NamespacedKey(MyPlugin.instance, "root")
override fun getDisplay() = DisplayData(
title = "First ruby",
description = "Find a ruby",
icon = IconData(Material.EMERALD, hashMapOf()),
frame = FrameType.GOAL,
)
fun init() {}
}
if (!FirstRuby.isGrantedTo(player)) FirstRuby.grant(player)What to override
- getNamespacedData() *Plugin and id
- getParent() *Parent key or null for the root
- getDisplay() *Title, description, icon, frame, background, toast, announce, hidden
* required
Pitfalls
- The root needs a background — otherwise the tab has none.
- The parent must be initialised — registration goes parent → child.
- grant without checking isGrantedTo — the toast will not repeat, but there is no harm either.