Skip to content
Alberto Migliorato edited this page Jun 28, 2026 · 4 revisions

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

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.


Material

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)

Legacy numeric IDs

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.

Material from a String — the length rule

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.

Custom items — nexo: and itemsadder: prefixes

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 via NexoUtils.itemStackFromId(...). If Nexo is absent or the id is unknown, the build degrades to a BARRIER.
  • itemsadder:<namespace:id> — resolved through ItemsAdder's CustomStack API when ItemsAdder is enabled. If the id is missing from the registry (or ItemsAdder is not installed), the build degrades to a BARRIER.

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.


Name & lore

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) and setLore(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.


Amount, model data, glow & enchantments

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 applies DURABILITY (Unbreaking) I plus the HIDE_ENCHANTS flag, 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();

Potion effects

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);

Skulls

Player heads and custom-texture heads are first-class. Note the return types differ.

Player heads — setPlayerHead

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();

Custom-texture heads — customHeadBuild

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.


Leather armour

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

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.


Persistent data (PDC)

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.


Custom build steps (addBuildStep)

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 NoSuchMethodError the first time the build step runs on a server that predates DataComponents.


Cloning

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 colours

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("&#33CCFFIcy edge")
        .hex()
        .build();

See Colors for the full colour syntax.


Building from configuration

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: 1001
ItemStack icon = new ItemBuilder()
        .fromConfig(getConfig(), "menu-icon")
        .build();

These items drop straight into PowerLib menus — see Inventories.


Nexo integration

Nexo custom items, furniture and custom blocks are supported through it.mycraft.powerlib.bukkit.utils.NexoUtils, plus a dependency-free interact event.

Runtime-availability & reflection model

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 compileOnly dependency 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, and setMaterial("nexo:…") degrades to a BARRIER.
  • 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 softdepend on Nexo in their plugin.yml, so Nexo's classes are reachable from the plugin's classloader:
# plugin.yml
softdepend: [Nexo]

NexoUtils

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).

Furniture interact event

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 material prefix

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.


Comparing items — ItemUtils

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 too

compare returns false if either item is null or AIR.


ItemBuilder method reference

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.

See also

Clone this wiki locally