-
Notifications
You must be signed in to change notification settings - Fork 1
Items
PowerLib ships a fluent ItemBuilder for assembling ItemStacks without the usual ItemMeta
boilerplate: names, lore, enchantments, potion effects, glow, custom model data, player/custom skulls,
dyed leather armour, placeholders, persistent data (PDC), and custom items from
ItemsAdder and Nexo.
Platform: Items are a Bukkit/Paper-only feature. Everything on this page lives in the
it.mycraft.powerlib.bukkit.item(and…bukkit.utils/…bukkit.events) packages and is not available on the Bungee or Velocity artifacts.
Targets paper-api 1.20.4, compiled for Java 17.
ItemBuilder is a mutable builder. Every setter returns the builder (this), so calls chain, and
build() produces a fresh ItemStack each time it is called. The builder is reusable: mutate it again
and call build() again, or take an independent copy with clone().
import it.mycraft.powerlib.bukkit.item.ItemBuilder;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
ItemStack sword = new ItemBuilder()
.setMaterial(Material.DIAMOND_SWORD)
.setName("&bExcalibur")
.setLore("&7A blade of legend.", "", "&eRight-click to smite.")
.setEnchantment(Enchantment.DAMAGE_ALL, 5)
.setEnchantment(Enchantment.FIRE_ASPECT, 2)
.setGlowing(true)
.setAmount(1)
.build();Colour codes in names and lore are translated automatically (legacy & codes through PowerLib's
ColorAPI; see Colors). Hex codes can be forced
with hex().
build() is intentionally fail-safe — it never returns null. A missing material defaults to STONE,
a missing name/lore to empty, and any unresolved custom item degrades to a BARRIER (see the relevant
sections). This keeps a malformed config from crashing a GUI build loop.
The material is the only mandatory property in practice (it defaults to STONE if you never set one).
setMaterial is overloaded four ways:
new ItemBuilder().setMaterial(Material.DIAMOND_SWORD); // enum (recommended)
new ItemBuilder().setMaterial("DIAMOND_SWORD"); // material name
new ItemBuilder().setMaterial(276); // legacy numeric ID (Diamond Sword)
new ItemBuilder().setMaterial(35, 14); // legacy ID + data (Red Wool)The int and int, int overloads resolve pre-1.13 item IDs through LegacyItemAPI, an exhaustive
lookup table mapping every old id / id:data pair to its modern Material. This is what lets the same
builder code target a wide range of Spigot versions.
import it.mycraft.powerlib.bukkit.item.LegacyItemAPI;
import org.bukkit.Material;
Material wool = LegacyItemAPI.getMaterial(35, 14); // RED_WOOL
Material disc = LegacyItemAPI.getMaterial("2256"); // MUSIC_DISC_13 (id:data string also accepted)LegacyItemAPI.getMaterial(...) returns null if no legacy entry matches. When called through
ItemBuilder.setMaterial(int), an unknown ID falls through to STONE at build time rather than failing.
setMaterial(String) has one quirk worth knowing. For strings longer than 11 characters that are
not an itemsadder: id, the builder validates the name against the Material enum and only stores it
if it resolves. Shorter strings, and the special prefixes below, are stored verbatim and resolved at
build() time. In normal use (real material names or prefixed custom ids) this is transparent.
A material string may carry a prefix that points at a third-party custom-item registry. The prefix is
resolved lazily inside build():
// Nexo custom item (see the Nexo Integration section)
ItemStack ruby = new ItemBuilder()
.setMaterial("nexo:ruby")
.setName("&cRuby")
.build();
// ItemsAdder custom item (namespace:id)
ItemStack wrench = new ItemBuilder()
.setMaterial("itemsadder:myitems:wrench")
.setName("&eWrench")
.build();-
nexo:<id>— resolved viaNexoUtils.itemStackFromId(...). If Nexo is absent or the id is unknown, the build degrades to aBARRIER. -
itemsadder:<namespace:id>— resolved through ItemsAdder'sCustomStackAPI when ItemsAdder is enabled. If the id is missing from the registry (or ItemsAdder is not installed), the build degrades to aBARRIER.
You can check ItemsAdder availability statically:
if (ItemBuilder.isUsingItemsAdder()) {
// ItemsAdder is enabled on this server
}Names, lore, enchantments, glow, PDC and build steps are all applied on top of the resolved custom item, so you can decorate a Nexo/ItemsAdder base freely.
ItemStack book = new ItemBuilder()
.setMaterial(Material.WRITTEN_BOOK)
.setName("&6&lQuest Log")
.setLore("&7Objectives:", " &8- &fFind the &ekey", " &8- &fOpen the &cgate")
.build();-
setName(String name)— sets the display name (colour-translated). -
setLore(String... lines)andsetLore(List<String> lines)— replace the lore (each line colour-translated). Both overloads replace the existing lore; the builder does not append.
An empty name or empty lore is simply not written to the meta.
ItemStack stack = new ItemBuilder()
.setMaterial(Material.EMERALD)
.setName("&aToken")
.setAmount(16)
.setCustomModelData(100123)
.setGlowing(true)
.build();| Property | Method | Notes |
|---|---|---|
| Amount | setAmount(int) |
Stack size. Values above 64 are accepted but unsupported by the client — use at your own risk. |
| Metadata / durability | setMetaData(short) |
Legacy data value / durability (e.g. wool colour pre-1.13, tool damage). |
| Custom model data | setCustomModelData(int) |
Written only when non-zero. Drives resource-pack models. |
| Glow | setGlowing(boolean) |
Adds a hidden enchantment glint with no visible enchant line. |
| Enchantment | setEnchantment(Enchantment, Integer) |
Call once per enchantment. Levels above the vanilla max are allowed. |
How glow and enchantments interact at build time:
- If
setGlowing(true)is set and no enchantments were added, the builder appliesDURABILITY(Unbreaking) I plus theHIDE_ENCHANTSflag, producing the glint without a visible line. - If you add real enchantments, they are written as-is. A glinting, enchanted item should generally just
rely on its real enchantments rather than
setGlowing.
ItemStack pick = new ItemBuilder()
.setMaterial(Material.NETHERITE_PICKAXE)
.setName("&dDrillbit")
.setEnchantment(Enchantment.DIG_SPEED, 10) // Efficiency X
.setEnchantment(Enchantment.DURABILITY, 3) // Unbreaking III
.build();When the material is a POTION, custom effects added via setPotionEffect(...) are written to the
PotionMeta. The duration is in ticks and the level argument is the human-readable amplifier
(level 1 = amplifier 0):
ItemStack brew = new ItemBuilder()
.setMaterial(Material.POTION)
.setName("&dSpeed Brew")
.setPotionEffect(PotionEffectType.SPEED, 20 * 60, 2) // Speed II for 60s
.build();Overloads let you control overwrite/ambient/particle flags:
setPotionEffect(PotionEffectType type, int duration, int level);
setPotionEffect(PotionEffectType type, int duration, int level, boolean overwrite);
setPotionEffect(PotionEffectType type, int duration, int level, boolean overwrite, boolean ambient);
setPotionEffect(PotionEffectType type, int duration, int level, boolean overwrite, boolean ambient, boolean particles);Player heads and custom-texture heads are first-class. Note the return types differ.
setPlayerHead returns the ItemBuilder (so it chains), and sets the material to a player head
owned by the given player:
ItemStack head = new ItemBuilder()
.setName("&eYour Head")
.setPlayerHead("Notch") // by name
.build();
ItemStack byUuid = new ItemBuilder()
.setPlayerHead(player.getUniqueId()) // by UUID
.build();customHeadBuild(String) is a terminal call: it returns the finished ItemStack directly
(not the builder). It accepts either a raw texture hash or a full Base64 texture value:
// texture hash (the part after textures.minecraft.net/texture/)
ItemStack arrowUp = new ItemBuilder()
.setName("&aScroll Up")
.customHeadBuild("d34e063cafb467a5c8de43ec78619399f369f4a52434da8017a983cdd92516a0");
// full Base64 texture value (ends with '=')
ItemStack textured = new ItemBuilder()
.setName("&bCustom")
.customHeadBuild("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly8uLi4ifX19");Set the name and lore on the builder before calling customHeadBuild / coloredArmorBuild, since
those methods build immediately.
coloredArmorBuild(int red, int green, int blue) is also terminal — it returns the dyed
ItemStack. Set the material to a leather piece and the RGB colour is applied to its
LeatherArmorMeta:
ItemStack tunic = new ItemBuilder()
.setMaterial(Material.LEATHER_CHESTPLATE)
.setName("&9Royal Tunic")
.setLore("&7Dyed deep blue.")
.coloredArmorBuild(25, 60, 200); // R, G, B (0–255)Placeholders let you template the name and lore and substitute values at build time. Register them with
addPlaceHolder(String placeholder, Object value) — the same placeholder mechanism used in the
Messages API. Every occurrence of the placeholder
token in the name and in each lore line is replaced (via value.toString()) when build() runs.
ItemStack reward = new ItemBuilder()
.setMaterial(Material.GOLD_INGOT)
.addPlaceHolder("%player%", player.getName())
.addPlaceHolder("%amount%", 64)
.setName("&6Reward for %player%")
.setLore("&7You received &e%amount%&7 gold.")
.build();Order does not matter — you may register placeholders before or after the name/lore; substitution happens at build time.
setPersistentData stores a typed value in the item's
PersistentDataContainer, written onto the meta during
build(). The signature is fully generic and mirrors Bukkit's PDC API:
public <T, Z> ItemBuilder setPersistentData(NamespacedKey key,
PersistentDataType<T, Z> type,
Z value);import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataType;
NamespacedKey rarity = new NamespacedKey(plugin, "rarity");
ItemStack gem = new ItemBuilder()
.setMaterial(Material.DIAMOND)
.setName("&bMythic Gem")
.setPersistentData(rarity, PersistentDataType.STRING, "MYTHIC")
.build();Read it back the standard Bukkit way:
String value = gem.getItemMeta()
.getPersistentDataContainer()
.get(rarity, PersistentDataType.STRING); // "MYTHIC"Each (key, type, value) triple is applied in the order Bukkit iterates the underlying map; call
setPersistentData once per key.
addBuildStep(Consumer<ItemMeta>) is the extension hook. The supplied consumer receives the item's
ItemMeta during build(), after the standard meta (name, lore, enchants, custom model data, PDC)
has been written, letting you reach API the core builder doesn't model directly:
public ItemBuilder addBuildStep(Consumer<ItemMeta> step);ItemStack tool = new ItemBuilder()
.setMaterial(Material.NETHERITE_PICKAXE)
.setName("&5Indestructible Pick")
.addBuildStep(meta -> meta.setUnbreakable(true))
.addBuildStep(meta -> meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES))
.build();Steps run in the order they were added, and you can register as many as you like.
This is exactly the seam the components module builds on. PowerLib's ComponentBuilder (fluent
wrappers around the DataComponent setters introduced in Spigot 1.20.5 — unbreakable, food, tool,
enchantmentGlintOverride, …) accumulates its mutations and hands them to an ItemBuilder through a
single build step:
// components module — applies all accumulated component mutations as one build step
public void applyTo(ItemBuilder builder) {
builder.addBuildStep(meta -> steps.forEach(s -> s.accept(meta)));
}So consuming the components module looks like:
import it.mycraft.powerlib.components.ComponentBuilder;
ItemBuilder builder = new ItemBuilder()
.setMaterial(Material.GOLDEN_APPLE)
.setName("&6Hearty Apple");
ComponentBuilder.create()
.food(8, 12.8f, true) // 1.20.5+ FoodComponent
.unbreakable(true)
.applyTo(builder); // registers a build step on the ItemBuilder
ItemStack apple = builder.build();Runtime note: the component setters require Minecraft 1.20.5+. They compile against older servers (the JVM resolves methods lazily) but throw
NoSuchMethodErrorthe first time the build step runs on a server that predates DataComponents.
clone(ItemStack) imports an existing item's properties into the builder (material, amount, durability,
name, lore, custom model data, potion effects, glow, and ItemsAdder data). AIR/null inputs are
ignored:
ItemStack original = new ItemStack(Material.GRASS_BLOCK);
ItemBuilder builder = new ItemBuilder().clone(original)
.setName("&aTurf")
.setGlowing(true);clone() (no args) returns an independent copy of the builder with deep-copied collections, so you
can branch a base template:
ItemBuilder base = new ItemBuilder()
.setMaterial(Material.PAPER)
.setName("&fTicket");
ItemStack vip = base.clone().setName("&6VIP Ticket").build();
ItemStack regular = base.clone().setName("&7Standard Ticket").build();hex() re-processes the current name and lore through PowerLib's hex translator (e.g. &#RRGGBB),
returning the builder:
ItemStack item = new ItemBuilder()
.setMaterial(Material.NETHER_STAR)
.setName("&#FF8800Sunburst")
.setLore("!CCFFIcy edge")
.hex()
.build();See Colors for the full colour syntax.
fromConfig reads an item definition out of a config section and pre-populates the builder — handy for
admin-editable GUIs. It accepts either a Bukkit FileConfiguration or PowerLib's native Configuration
(see Configurations):
public ItemBuilder fromConfig(FileConfiguration fileConfiguration, String path);
public ItemBuilder fromConfig(Configuration configuration, String path);It reads the keys material, name, lore, amount, metadata, glowing, customModelData, and a
legacy flag (when legacy: true, the material is looked up as a legacy LEGACY_… name):
# config.yml
menu-icon:
material: DIAMOND
name: "&bShop"
lore:
- "&7Click to browse"
amount: 1
glowing: true
customModelData: 1001ItemStack icon = new ItemBuilder()
.fromConfig(getConfig(), "menu-icon")
.build();These items drop straight into PowerLib menus — see Inventories.
Nexo custom items, furniture and custom blocks are supported through
it.mycraft.powerlib.bukkit.utils.NexoUtils, plus a dependency-free interact event.
PowerLib binds to Nexo entirely by reflection — there is no compile-time dependency on Nexo. This is deliberate:
- PowerLib is compiled for Java 17 so a single artifact runs across the whole supported Minecraft
range. Nexo is compiled for Java 21. Declaring Nexo as even a
compileOnlydependency would drag PowerLib's bytecode up to Java 21 (a JDK 17 compiler can't read Java 21 class files), breaking that range. - Reflection keeps PowerLib on Java 17 while still binding to Nexo's API at runtime — which is sound, because Nexo's Java-21 classes can only ever be present on a Java-21 server JVM, where this code runs anyway.
Consequences:
- On servers without Nexo — and on any Java 17 server, where Nexo cannot load at all — the bridge
stays inert: every accessor returns
null/false, andsetMaterial("nexo:…")degrades to aBARRIER. - Method handles are resolved once, at class load. "Nexo not installed" is silent; "Nexo installed but its API does not match" (version drift) is logged once and disables the integration, instead of being silently swallowed.
- Plugins that want the integration must
softdependon Nexo in theirplugin.yml, so Nexo's classes are reachable from the plugin's classloader:
# plugin.yml
softdepend: [Nexo]All methods are static and null-safe; they short-circuit to null / false when Nexo is unavailable.
| Method | Returns | Description |
|---|---|---|
isAvailable() |
boolean |
Whether Nexo's API was found and bound on this server. |
itemStackFromId(String nexoId) |
ItemStack |
Builds the Nexo item with that id, or null if unknown / Nexo unavailable. |
getNexoId(ItemStack item) |
String |
The Nexo id of an item, or null if it isn't a Nexo item. |
getNexoId(Block block) |
String |
The Nexo id of the furniture / custom block at this block, or null. |
getNexoId(Entity entity) |
String |
The Nexo id of a furniture entity, or null. |
isNexoItem(ItemStack item, String nexoId) |
boolean |
Whether item is the Nexo item with the given id (case-insensitive). |
import it.mycraft.powerlib.bukkit.utils.NexoUtils;
if (NexoUtils.isAvailable()) {
ItemStack ruby = NexoUtils.itemStackFromId("ruby"); // build by id
String id = NexoUtils.getNexoId(player.getInventory().getItemInMainHand());
boolean holdingRuby = NexoUtils.isNexoItem(
player.getInventory().getItemInMainHand(), "ruby");
}ItemBuilder.setMaterial("nexo:ruby") is just sugar over itemStackFromId("ruby") applied at build time
(with the BARRIER fallback).
PowerLib normalises Nexo furniture / custom-block right-clicks into a single dependency-free event,
it.mycraft.powerlib.bukkit.events.NexoFurnitureInteractEvent. The bundled NexoListener watches the
raw block/entity interactions, resolves the Nexo id via NexoUtils, and re-fires this event — so your
plugin can react to Nexo furniture without importing anything from Nexo.
The event carries the player, the furniture id, and the raw Nexo mechanic (as an Object, to avoid a
hard Nexo type), and is Cancellable — cancelling it cancels the underlying interaction.
import it.mycraft.powerlib.bukkit.events.NexoFurnitureInteractEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class FurnitureListener implements Listener {
@EventHandler
public void onFurniture(NexoFurnitureInteractEvent event) {
if (event.getFurnitureId().equals("custom_chair")) {
event.getPlayer().sendMessage("You sat on a chair!");
event.setCancelled(true); // cancels the underlying Nexo interaction
}
}
}The bridge listener is opt-in and only registers when Nexo is actually present. Register it once,
typically in onEnable:
import it.mycraft.powerlib.bukkit.listeners.NexoListener;
@Override
public void onEnable() {
NexoListener.register(this); // no-op if Nexo is unavailable
}NexoFurnitureInteractEvent accessors:
| Method | Returns | Description |
|---|---|---|
getPlayer() |
Player |
The player who interacted. |
getFurnitureId() |
String |
The Nexo id of the furniture / custom block. |
getNexoFurniture() |
Object |
The raw Nexo mechanic (cast only if you depend on Nexo yourself). |
isCancelled() / setCancelled(boolean)
|
boolean |
Cancellation, propagated to the source interaction. |
ItemsAdder custom items are referenced with the itemsadder: prefix on
the material string, in itemsadder:<namespace:id> form. Unlike Nexo, ItemsAdder is resolved through its
CustomStack API directly, gated on ItemBuilder.isUsingItemsAdder():
ItemStack custom = new ItemBuilder()
.setMaterial("itemsadder:myitems:magic_wand")
.setName("&dMagic Wand")
.setLore("&7Right-click to cast.")
.build();- If ItemsAdder is enabled and the id is in its registry, the custom item is produced and then decorated with your name/lore/enchants/etc.
- If the id is missing, or ItemsAdder isn't installed, the build degrades to a
BARRIER(rather than failing).
When you clone(ItemStack) an ItemsAdder item, its itemsadder NBT (namespace + id) is captured and
re-applied on the next build(), so the custom identity survives a round-trip through the builder.
it.mycraft.powerlib.bukkit.item.ItemUtils compares two ItemStacks by type, durability, meta presence
and full NBT, normalising legacy materials so a legacy stack and its modern equivalent compare equal:
public static boolean compare(ItemStack i1, ItemStack i2); // ignores amount
public static boolean compare(ItemStack i1, ItemStack i2, boolean ignoreAmount);import it.mycraft.powerlib.bukkit.item.ItemUtils;
boolean same = ItemUtils.compare(a, b); // amount ignored
boolean exactSame = ItemUtils.compare(a, b, false); // amount must match toocompare returns false if either item is null or AIR.
Builders return ItemBuilder (chainable) unless noted. build(), customHeadBuild(...) and
coloredArmorBuild(...) are terminal and return an ItemStack.
| Method | Returns | Description |
|---|---|---|
setMaterial(Material material) |
ItemBuilder |
Set material from an enum. |
setMaterial(String material) |
ItemBuilder |
Set material from a name or a nexo: / itemsadder: prefixed id. |
setMaterial(int id) |
ItemBuilder |
Set material from a legacy numeric ID (1.13+ resolution via LegacyItemAPI). |
setMaterial(int id, int data) |
ItemBuilder |
Set material from a legacy id + data value. |
setName(String name) |
ItemBuilder |
Set the display name (colour-translated). |
setLore(String... lore) |
ItemBuilder |
Replace lore from varargs (colour-translated). |
setLore(List<String> lore) |
ItemBuilder |
Replace lore from a list (colour-translated). |
setAmount(int amount) |
ItemBuilder |
Set the stack size. |
setMetaData(short metadata) |
ItemBuilder |
Set legacy data value / durability. |
setCustomModelData(int data) |
ItemBuilder |
Set custom model data (applied when non-zero). |
setGlowing(boolean glowing) |
ItemBuilder |
Toggle the enchantment-glint effect. |
setEnchantment(Enchantment ench, Integer level) |
ItemBuilder |
Add one enchantment at a level. |
setPotionEffect(PotionEffectType, int, int) |
ItemBuilder |
Add a custom potion effect (duration in ticks, human level). |
setPotionEffect(…, boolean overwrite[, ambient[, particles]]) |
ItemBuilder |
Same, with effect flags. |
setPersistentData(NamespacedKey, PersistentDataType<T,Z>, Z) |
ItemBuilder |
Store a typed PDC value (written at build). |
addBuildStep(Consumer<ItemMeta>) |
ItemBuilder |
Register a meta mutation applied at build (extension hook). |
addPlaceHolder(String placeholder, Object value) |
ItemBuilder |
Register a name/lore placeholder substitution. |
setPlayerHead(String playerName) |
ItemBuilder |
Make the item a player head owned by name. |
setPlayerHead(UUID uuid) |
ItemBuilder |
Make the item a player head owned by UUID. |
clone(ItemStack itemStack) |
ItemBuilder |
Import an existing item's properties into this builder. |
hex() |
ItemBuilder |
Re-translate hex colour codes in the current name & lore. |
fromConfig(FileConfiguration, String path) |
ItemBuilder |
Populate from a Bukkit config section. |
fromConfig(Configuration, String path) |
ItemBuilder |
Populate from a PowerLib native config section. |
build() |
ItemStack |
Build the item. Never returns null; degrades gracefully on bad input. |
customHeadBuild(String skinURL) |
ItemStack |
Terminal. Build a custom-texture head (hash or Base64). |
coloredArmorBuild(int r, int g, int b) |
ItemStack |
Terminal. Build dyed leather armour. |
clone() |
ItemBuilder |
Deep-copy this builder (independent collections). |
isUsingItemsAdder() |
boolean |
(static) Whether ItemsAdder is enabled on the server. |
- Configurations — driving item definitions from YAML.
- Inventories — placing built items into menus.
- Messages — the shared placeholder system.
- Colors — legacy and hex colour syntax.
Core (all platforms)
Bukkit / Paper
Commands