API
Reading the trim catalogue, the halves a player combines by hand, their chosen trim, and the trims written onto armor.
ArmorTrimService is what another plugin reads and drives ExyliaArmorTrims through: the presets, the
patterns and materials a player combines by hand, the trim they chose, and the trims written onto
armor itself.
ExyliaAPI.get(ArmorTrimService.class).ifPresent(trims -> {
trims.select(player, ArmorPiece.CHESTPLATE, "sentry/netherite");
trims.trimItem("ruby").ifPresent(item -> player.getInventory().addItem(item));
});The artifact, the repository and the plugin.yml line are the same for every Exylia plugin and live
on the public API page.
Everything is addressed by trim id, matched without regard to case. That is either a preset the
server owner named, or the pattern/material form of a combination — the slash tells them apart, and
no registry key contains one. An id that names neither is not an error, so lookups answer empty and
writes answer false.
The catalogue
| Method | What it does |
|---|---|
trims() | Every preset the server declares, in the order a menu draws them. Presets only: combinations are too many to list. |
trim(String trimId) | One trim as an Optional<ArmorTrim>, taking a preset id or a pattern/material combination. Empty when the id names neither. |
exists(String trimId) | Whether the catalogue declares that preset. Presets only — a combination is valid whenever both halves are, so ask trim for one of those. |
patterns() | The patterns a player can combine by hand, in menu order. |
materials() | The materials a player can combine by hand, in menu order. |
Permissions
| Method | What it does |
|---|---|
canUse(Player, String trimId) | Whether they may wear the trim on at least one piece. |
canUse(Player, String trimId, ArmorPiece) | Whether they may wear it on that one piece. |
Both answer by the server's own rule, which includes the setting that turns permissions off entirely — a shop asking gets the same answer the menu would give, not a raw permission check. The per-piece form is separate because a rank can be sold one slot at a time: a player given the helmet node wears the trim on their head and nowhere else.
What the player chose
| Method | What it does |
|---|---|
mode() | Where the trims this server draws come from, as a TrimMode. |
selected(UUID player, ArmorPiece) | The trim id chosen for that slot, as an Optional<String>. Empty when they chose none, the menu is off, or their selection is still being read. |
select(Player, ArmorPiece, String trimId) | Sets the choice for that slot. true when it was stored. |
clear(Player, ArmorPiece) | Takes the chosen trim off one slot. true when something was there to remove. |
clearAll(Player) | Takes every chosen trim off. true when something was there to remove. |
selected is a choice, not a result: it stays set while the slot is empty, and the trim appears
again the moment the player equips armor that fits.
select is a set rather than a toggle — running it twice leaves the trim on. It is not gated by
permission, because the server owner's own setting says it should not be: a trim handed out by a
crate or a reward is owned whether or not a node was ever granted for it. That is the same rule
ExyliaArrows follows and the opposite of ExyliaArmorSkin. Ask canUse(Player, String, ArmorPiece)
first when you do want the permission to decide.
Trims on items
| Method | What it does |
|---|---|
trimOf(ItemStack armor) | The trim id written onto that armor, as an Optional<String>. Empty when it carries none. |
apply(ItemStack armor, String trimId) | Writes a trim onto the armor in place, keeping everything else it had. true when written. |
strip(ItemStack armor) | Takes the trim off and gives nothing back. true when a trim was there. |
trimItem(String trimId) | A trim item ready to be given out, as an Optional<ItemStack>. Empty when the id names no trim. |
isTrimItem(ItemStack) | Whether an item is one of this plugin's trim items. |
Ask isTrimItem before a container, a shop or a trade treats a stack as ordinary loot: a trim item
is drawn as whatever suits it and is not the material it looks like.
Redrawing
| Method | What it does |
|---|---|
refresh(Player) | Recomputes what the player looks like and re-sends it to everyone who can see them. |
Only needed after something the service does not know about changed — a permission granted at runtime, an item swapped by another plugin. Every write above already redraws.
Types
ArmorTrim — id, name (colour codes still in it), permission, pieces, pattern,
material and preset. supports(ArmorPiece) answers whether the trim fits a slot. A preset is an
entry the owner named and has one node; a combination has an empty permission, because it is
granted by the two halves behind it instead.
TrimOption — one half a player combines by hand: id, name, permission. Patterns and
materials share the record, and which half one is comes from the method that handed it over. They
exist because a combination is sold in halves: giving a player a preset never quietly gives them its
pattern to recombine.
ArmorPiece — HELMET, CHESTPLATE, LEGGINGS, BOOTS. slot() gives the Bukkit
EquipmentSlot, so a caller reading an inventory does not keep a switch of its own in step.
TrimMode — where a drawn trim comes from: ITEM (only what a trim item wrote onto the armor),
MENU (only what the wearer chose; trim items do nothing) or BOTH. usesItems() and usesMenu()
answer without a switch. Worth asking before writing anything: the half a server does not use answers
false or empty rather than throwing, so an integration written for both runs on either.
Everything returning a value reads the plugin's cache and is safe from a menu redraw or a placeholder. Everything that changes a selection writes to the database and redraws the player for everyone watching — call those on the main thread, and no more often than a player could.
What it does not expose
No events: ExyliaArmorTrims publishes none. Menu openers, the custom-tab editor flow, removers and raw configuration rows are left out on purpose — a public contract cannot be broken later, so it carries what an integration needs and nothing that only made sense inside one version.
If something you need is genuinely missing, ask on Discord.
Something missing on this page? Tell us on Discord