Bukkit物品比较时如何忽略属性修饰符UUID完成相似判断
问题原因
ItemStack#isSimilar() 做物品一致性比对时,会将属性修饰符(AttributeModifier)的UUID作为必校验字段。你代码中创建AttributeModifier时调用的是不传入UUID的构造方法,该构造方法会自动生成随机UUID,每次调用item()生成的新物品属性UUID都完全不同,自然无法和玩家手持的物品通过isSimilar()校验。
解决方案
方案1:为属性修饰符指定固定UUID(推荐,零侵入适配现有逻辑)
创建AttributeModifier时手动传入固定的UUID实例,只要两个物品的属性类型、修饰符UUID、名称、数值、运算规则完全一致,isSimilar()就能正常通过校验,不需要修改原有比对逻辑。
修改后的代码如下:
// 全局定义固定UUID,不要在生成物品的方法内重复创建 public static final UUID MODIFIER_UUID_ATTACK_DAMAGE = UUID.fromString("a616aa14-b86c-43a0-b00d-e4ee39a6a1c2"); public static final UUID MODIFIER_UUID_ATTACK_SPEED = UUID.fromString("28730ac1-7b0f-4d39-9e3a-7d23c943c7d8"); public static ItemStack item() { ItemStack item = new ItemStack(Material.STICK); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§2§lCool sword"); // 传入固定UUID构造属性修饰符 meta.addAttributeModifier(Attribute.GENERIC_ATTACK_DAMAGE, new AttributeModifier(MODIFIER_UUID_ATTACK_DAMAGE, "generic.attackDamage", 11.0, AttributeModifier.Operation.ADD_NUMBER)); meta.addAttributeModifier(Attribute.GENERIC_ATTACK_SPEED, new AttributeModifier(MODIFIER_UUID_ATTACK_SPEED, "generic.attackSpeed", 1.4, AttributeModifier.Operation.ADD_NUMBER)); meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); item.setItemMeta(meta); return item; }
注意:原代码中item()方法返回值为Object类型,这里直接调整为返回ItemStack,省去后续不必要的强转,避免类型转换异常。
方案2:自定义物品比对逻辑(适配无固定UUID的场景)
如果你的业务场景无法保证同配置物品的属性UUID完全一致,可以自行实现比对逻辑,跳过UUID校验,仅比对属性的核心特征(名称、数值、运算类型、生效槽位)即可。
核心实现参考:
public static boolean isSameCustomItem(ItemStack a, ItemStack b) { // 基础属性前置校验 if (a == null || b == null) return a == b; if (a.getType() != b.getType()) return false; ItemMeta metaA = a.getItemMeta(); ItemMeta metaB = b.getItemMeta(); if (metaA == null || metaB == null) return metaA == metaB; if (!metaA.getDisplayName().equals(metaB.getDisplayName())) return false; if (!metaA.getItemFlags().equals(metaB.getItemFlags())) return false; // 跳过UUID比对属性修饰符 for (Attribute attribute : Attribute.values()) { Collection<AttributeModifier> modsA = metaA.getAttributeModifiers(attribute); Collection<AttributeModifier> modsB = metaB.getAttributeModifiers(attribute); if (modsA == null && modsB == null) continue; if (modsA == null || modsB == null || modsA.size() != modsB.size()) return false; // 提取修饰符核心特征做比对 List<AttributeModifierEntry> entriesA = modsA.stream() .map(mod -> new AttributeModifierEntry(mod.getName(), mod.getAmount(), mod.getOperation(), mod.getSlot())) .toList(); List<AttributeModifierEntry> entriesB = modsB.stream() .map(mod -> new AttributeModifierEntry(mod.getName(), mod.getAmount(), mod.getOperation(), mod.getSlot())) .toList(); if (!entriesA.containsAll(entriesB)) return false; } return true; } // 用于比对属性特征的记录类 record AttributeModifierEntry(String name, double amount, AttributeModifier.Operation op, EquipmentSlot slot) {}
使用时将原有processEvent方法中的isSimilar替换为自定义方法即可:
public static void processEvent(Entity damager, LivingEntity entity) { if (!(damager instanceof Player)) return; Player player = (Player) damager; if (!isSameCustomItem(player.getItemInHand(), item())) return; entity.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING,200, 1, true, false)); }
补充:Minecraft原版物品的属性修饰符均使用固定UUID,随机UUID是插件调用无UUID参数的构造方法时自动生成的。使用固定UUID是服务端插件开发的通用最佳实践,除了解决比对问题外,还能避免属性重复生效、和其他插件属性冲突的兼容性问题。
内容的提问来源于stack exchange,提问作者Miskakao
相关产品推荐
相关产品推荐

