Updated ItemizerX to the latest stable Paper version (1.21.8)

Given the complexity of the NMS update (including data components), I have decided to omit the support of older MC versions and maintain multiversion compatibility from 1.21.8 onwards from v2.4. I suspect this may not change much in the future, making it easier to maintain.

In the future, I may look into moving away from NMS provided Paper is now exposing them in Paper API via Data Components

This also closes #1
This commit is contained in:
Focusvity
2025-11-05 14:34:50 +11:00
parent 89b1143ab2
commit afd3223c24
22 changed files with 282 additions and 1635 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
dependencies {
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT")
}
@@ -0,0 +1,147 @@
package dev.plex.itemizerx.v1_21_R1;
import dev.plex.itemizerx.Attributes;
import dev.plex.itemizerx.IAttributeManager;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.EquipmentSlotGroup;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ItemAttributeModifiers;
import net.minecraft.world.item.component.ItemAttributeModifiers.Entry;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
public class AttributeManager implements IAttributeManager {
private final MiniMessage mm = MiniMessage.miniMessage();
@Override
public void addAttr(Player player, String[] args) {
if (args.length < 4)
{
player.sendMessage(mm.deserialize("<aqua>/itemizer attr add <<white>name<aqua>> <<white>strength<aqua>>" +
"[<white>slot<aqua>] <red>- <gold>Add an attribute"));
return;
}
Attributes a = Attributes.get(args[2]);
if (a == null)
{
player.sendMessage(mm.deserialize("<dark_red>\"" + args[2] + "\" is not a valid attribute type."));
return;
}
double amount;
try
{
amount = Double.parseDouble(args[3]);
}
catch (NumberFormatException ex)
{
player.sendMessage(mm.deserialize("<dark_red>\"" + args[3] + "\" is not a valid number."));
return;
}
if (Double.isNaN(amount))
{
player.sendMessage(mm.deserialize("<dark_red>Please do not use <white>'NaN (Not a Number)'"));
return;
}
ItemStack nms = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand());
final List<Entry> entries = nms.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY).modifiers();
for (Entry entry : entries)
{
if (entry.attribute().getRegisteredName().equalsIgnoreCase(args[2]))
{
player.sendMessage(mm.deserialize("<dark_red>An attribute with the name \"<white>" + args[2] + "<dark_red>\" already exists!"));
return;
}
}
AtomicReference<EquipmentSlotGroup> group = new AtomicReference<>(EquipmentSlotGroup.ANY);
if (args.length == 5)
{
EquipmentSlot slot;
try
{
slot = EquipmentSlot.byName(args[4].toLowerCase());
}
catch (IllegalArgumentException ignored)
{
player.sendMessage(mm.deserialize("<dark_green>Supported options:"));
player.sendMessage(mm.deserialize("<yellow>" + StringUtils.join(
Arrays.stream(EquipmentSlot.values()).map(s -> s.getName().toLowerCase()).toArray(), ", ")));
return;
}
group.set(EquipmentSlotGroup.bySlot(slot));
}
final ResourceLocation locationID = ResourceLocation.fromNamespaceAndPath("itemizerx", String.format("modifier_%s", a.mcName));
final AttributeModifier modifier = new AttributeModifier(locationID, amount, Operation.BY_ID.apply(a.op));
nms.update(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY, x ->
x.withModifierAdded(a.attributeHolder, modifier, group.get()));
final org.bukkit.inventory.ItemStack is = CraftItemStack.asCraftMirror(nms);
player.getInventory().setItemInMainHand(is);
player.sendMessage(mm.deserialize("<dark_aqua>Attribute added!"));
}
@Override
public void removeAttr(Player player, String string) {
Attributes a = Attributes.get(string);
if (a == null)
{
player.sendMessage(mm.deserialize("<dark_red>\"" + string + "\" is not a valid attribute type."));
return;
}
ItemStack nms = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand());
ItemAttributeModifiers attrModifiers = nms.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY);
final List<Entry> entries = attrModifiers.modifiers();
Optional<Entry> optionalEntry = entries.stream().filter(e -> e.attribute().equals(a.attributeHolder)).findFirst();
ItemAttributeModifiers.Builder builder = ItemAttributeModifiers.builder();
optionalEntry.ifPresentOrElse(entry -> {
entries.forEach(e -> {
if (e.attribute().equals(a.attributeHolder)) {
return;
}
builder.add(entry.attribute(), entry.modifier(), entry.slot(), entry.display());
});
nms.set(DataComponents.ATTRIBUTE_MODIFIERS, builder.build());
final org.bukkit.inventory.ItemStack is = CraftItemStack.asCraftMirror(nms);
player.getInventory().setItemInMainHand(is);
player.sendMessage(mm.deserialize("<dark_aqua>Attribute removed!"));
}, () -> player.sendMessage(mm.deserialize("<dark_red>The attribute \"" + string + "\" doesn't exist!")));
}
@Override
public void listAttr(Player player) {
final ItemStack nms = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand());
List<Entry> entries = nms.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY).modifiers();
if (entries.isEmpty()) {
player.sendMessage(mm.deserialize("<yellow>This item has no attributes."));
return;
}
player.sendMessage(mm.deserialize("<dark_green>Item attributes: "));
entries.forEach(entry -> {
player.sendMessage(mm.deserialize("<yellow>" + entry.attribute().getRegisteredName() + ", " + entry.modifier().amount() + ", " + entry.slot()));
});
}
}
@@ -0,0 +1,150 @@
package dev.plex.itemizerx.v1_21_R1;
import dev.plex.itemizerx.IEnchantmentManager;
import java.util.Objects;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class EnchantmentManager implements IEnchantmentManager {
private final MiniMessage mm = MiniMessage.miniMessage();
@Override
public void addEnchantment(Player player, ItemStack item, String[] args) {
if (args.length < 4)
{
player.sendMessage(mm.deserialize("<dark_aqua>===============<white>[<light_purple>Enchant Commands<white>]<dark_aqua>==============="));
player.sendMessage(mm.deserialize("<aqua>/itemizer enchant add <<white>name<aqua>> <<white>level<aqua>> <red>- <gold>Add an enchantment"));
return;
}
EnchantmentMap map = EnchantmentMap.fromName(args[2]);
if (map == null) {
player.sendMessage(mm.deserialize("<dark_red>The <white>'" + args[2] + "<white>' <dark_red>enchantment does not exist!"));
return;
}
Enchantment enchantment = map.asEnchantment();
ItemMeta meta = item.getItemMeta();
assert meta != null;
if (meta.getEnchants().containsKey(enchantment))
{
player.sendMessage(mm.deserialize("<dark_red>This item already have the <white>'" + enchantment.getKey().getKey() + "' <dark_red>enchantment!"));
return;
}
int level;
try
{
level = Integer.parseInt(args[3]);
}
catch (NumberFormatException ex)
{
player.sendMessage(mm.deserialize("<white>\"" + args[3] + "<white>\"<dark_red> is not a valid number!"));
return;
}
item.addUnsafeEnchantment(enchantment, level);
player.sendMessage(mm.deserialize("<dark_green>The enchant <white>'" + enchantment.getKey().getKey() + "' <dark_green>has been added to your item"));
}
@Override
public void removeEnchantment(Player player, ItemStack item, String[] args) {
if (args.length == 2)
{
player.sendMessage(mm.deserialize("<dark_aqua>===============<white>[<light_purple>Enchant Commands<white>]<dark_aqua>==============="));
player.sendMessage(mm.deserialize("<aqua>/itemizer enchant remove <<white>name<aqua>> <red>- <gold>Remove an enchantment"));
return;
}
EnchantmentMap map = EnchantmentMap.fromName(args[2]);
if (map == null) {
player.sendMessage(mm.deserialize("<dark_red>The <white>'" + args[2] + "<white>' <dark_red> enchantment does not exist!"));
return;
}
ItemMeta meta = item.getItemMeta();
assert meta != null;
if (Objects.requireNonNull(meta.getEnchants()).isEmpty())
{
player.sendMessage(mm.deserialize("<dark_red>This item doesn't hold any enchants"));
return;
}
Enchantment enchantment = map.asEnchantment();
if (!meta.getEnchants().containsKey(enchantment))
{
player.sendMessage(mm.deserialize("<dark_red>This item doesn't have the <white>'" + enchantment.getKey().getKey() + "' <dark_red>enchantment!"));
return;
}
item.removeEnchantment(enchantment);
player.sendMessage(mm.deserialize("<dark_green>The <white>'" + enchantment.getKey().getKey() + "' <dark_green>enchantment has been removed from your item"));
}
private enum EnchantmentMap {
PROTECTION(Enchantment.PROTECTION),
FIRE_PROTECTION(Enchantment.FIRE_PROTECTION),
FEATHER_FALLING(Enchantment.FEATHER_FALLING),
BLAST_PROTECTION(Enchantment.BLAST_PROTECTION),
PROJECTILE_PROTECTION(Enchantment.PROJECTILE_PROTECTION),
RESPIRATION(Enchantment.RESPIRATION),
AQUA_AFFINITY(Enchantment.AQUA_AFFINITY),
THORNS(Enchantment.THORNS),
DEPTH_STRIDER(Enchantment.DEPTH_STRIDER),
FROST_WALKER(Enchantment.FROST_WALKER),
BINDING_CURSE(Enchantment.BINDING_CURSE),
SHARPNESS(Enchantment.SHARPNESS),
SMITE(Enchantment.SMITE),
BANE_OF_ARTHROPODS(Enchantment.BANE_OF_ARTHROPODS),
KNOCKBACK(Enchantment.KNOCKBACK),
FIRE_ASPECT(Enchantment.FIRE_ASPECT),
LOOTING(Enchantment.LOOTING),
SWEEPING_EDGE(Enchantment.SWEEPING_EDGE),
EFFICIENCY(Enchantment.EFFICIENCY),
SILK_TOUCH(Enchantment.SILK_TOUCH),
UNBREAKING(Enchantment.UNBREAKING),
FORTUNE(Enchantment.FORTUNE),
POWER(Enchantment.POWER),
PUNCH(Enchantment.PUNCH),
FLAME(Enchantment.FLAME),
INFINITY(Enchantment.INFINITY),
LUCK_OF_THE_SEA(Enchantment.LUCK_OF_THE_SEA),
LURE(Enchantment.LURE),
LOYALTY(Enchantment.LOYALTY),
IMPALING(Enchantment.IMPALING),
RIPTIDE(Enchantment.RIPTIDE),
CHANNELING(Enchantment.CHANNELING),
MULTISHOT(Enchantment.MULTISHOT),
QUICK_CHARGE(Enchantment.QUICK_CHARGE),
PIERCING(Enchantment.PIERCING),
DENSITY(Enchantment.DENSITY),
BREACH(Enchantment.BREACH),
WIND_BURST(Enchantment.WIND_BURST),
MENDING(Enchantment.MENDING),
VANISHING_CURSE(Enchantment.VANISHING_CURSE),
SOUL_SPEED(Enchantment.SOUL_SPEED),
SWIFT_SNEAK(Enchantment.SWIFT_SNEAK);
private final Enchantment enchantment;
EnchantmentMap(Enchantment enchantment) {
this.enchantment = enchantment;
}
public static EnchantmentMap fromName(String name) {
for (EnchantmentMap map : values()) {
if (map.name().equalsIgnoreCase(name)) {
return map;
}
}
return null;
}
public Enchantment asEnchantment() {
return enchantment;
}
}
}
@@ -1,5 +0,0 @@
package dev.plex.itemizerx.v1_21_R1;
public class Main
{
}