From 1c98b29087bafd9f4526ef219a7d658251b26076 Mon Sep 17 00:00:00 2001 From: KnightMiner Date: Mon, 28 Dec 2020 14:59:09 -0500 Subject: [PATCH] Add ItemAttributeModifierEvent (#7484) --- .../net/minecraft/item/ItemStack.java.patch | 7 +- .../net/minecraftforge/common/ForgeHooks.java | 15 ++ .../event/ItemAttributeModifierEvent.java | 143 ++++++++++++++++++ .../debug/item/ItemAttributeModifierTest.java | 55 +++++++ src/test/resources/META-INF/mods.toml | 6 +- 5 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/main/java/net/minecraftforge/event/ItemAttributeModifierEvent.java create mode 100644 src/test/java/net/minecraftforge/debug/item/ItemAttributeModifierTest.java diff --git a/patches/minecraft/net/minecraft/item/ItemStack.java.patch b/patches/minecraft/net/minecraft/item/ItemStack.java.patch index 9c6194c20..8646ba396 100644 --- a/patches/minecraft/net/minecraft/item/ItemStack.java.patch +++ b/patches/minecraft/net/minecraft/item/ItemStack.java.patch @@ -214,7 +214,7 @@ return list; } -@@ -817,7 +841,7 @@ +@@ -817,9 +841,10 @@ } } } else { @@ -222,8 +222,11 @@ + multimap = this.func_77973_b().getAttributeModifiers(p_111283_1_, this); } ++ multimap = net.minecraftforge.common.ForgeHooks.getAttributeModifiers(this, p_111283_1_, multimap); return multimap; -@@ -954,6 +978,35 @@ + } + +@@ -954,6 +979,35 @@ return this.func_77973_b().func_219971_r(); } diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 6ced24e71..2c17b7f8d 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -33,6 +33,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Throwables; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; import com.google.common.collect.Queues; import com.google.common.collect.Sets; import com.google.gson.Gson; @@ -48,7 +49,10 @@ import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.advancements.Advancement; import net.minecraft.block.Block; import net.minecraft.block.material.Material; +import net.minecraft.entity.ai.attributes.Attribute; +import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.fluid.*; +import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.loot.LootContext; import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTableManager; @@ -125,6 +129,7 @@ import net.minecraftforge.common.world.MobSpawnInfoBuilder; import net.minecraftforge.event.AnvilUpdateEvent; import net.minecraftforge.event.DifficultyChangeEvent; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.ItemAttributeModifierEvent; import net.minecraftforge.event.ServerChatEvent; import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; import net.minecraftforge.event.entity.item.ItemTossEvent; @@ -1025,6 +1030,16 @@ public class ForgeHooks MinecraftForge.EVENT_BUS.post(new AdvancementEvent(player, advancement)); } + /** + * Hook to fire {@link ItemAttributeModifierEvent}. Modders should use {@link ItemStack#getAttributeModifiers(EquipmentSlotType)} instead. + */ + public static Multimap getAttributeModifiers(ItemStack stack, EquipmentSlotType equipmentSlot, Multimap attributes) + { + ItemAttributeModifierEvent event = new ItemAttributeModifierEvent(stack, equipmentSlot, attributes); + MinecraftForge.EVENT_BUS.post(event); + return event.getModifiers(); + } + /** * Used as the default implementation of {@link Item#getCreatorModId}. Call that method instead. */ diff --git a/src/main/java/net/minecraftforge/event/ItemAttributeModifierEvent.java b/src/main/java/net/minecraftforge/event/ItemAttributeModifierEvent.java new file mode 100644 index 000000000..15256bac3 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/ItemAttributeModifierEvent.java @@ -0,0 +1,143 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package net.minecraftforge.event; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimaps; +import net.minecraftforge.eventbus.api.Event; +import net.minecraft.entity.ai.attributes.Attribute; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.ItemStack; +import com.google.common.collect.Multimap; + +import javax.annotation.Nullable; +import java.util.Collection; + +/** + * This event is fired when the attributes for an ItemStack are being calculated. + * Attributes are calculated on the server when equipping and unequipping an item to add and remove attributes respectively, both must be consistent. + * Attributes are calculated on the client when rendering an item's tooltip to show relevant attributes. + *
+ * Note that this event is fired regardless of if the stack has NBT overriding attributes or not. If your attribute should be + * ignored when attributes are overridden, you can check for the presence of the {@code AttributeModifiers} tag. + *
+ * This event is fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}. + */ +public class ItemAttributeModifierEvent extends Event +{ + private final ItemStack stack; + private final EquipmentSlotType slotType; + private final Multimap originalModifiers; + private Multimap unmodifiableModifiers; + @Nullable + private Multimap modifiableModifiers; + + public ItemAttributeModifierEvent(ItemStack stack, EquipmentSlotType slotType, Multimap modifiers) + { + this.stack = stack; + this.slotType = slotType; + this.unmodifiableModifiers = this.originalModifiers = modifiers; + } + + /** + * Returns an unmodifiable view of the attribute multimap. Use other methods from this event to modify the attributes map. + * Note that adding attributes based on existing attributes may lead to inconsistent results between the tooltip (client) + * and the actual attributes (server) if the listener order is different. Using {@link #getOriginalModifiers()} instead will give more consistent results. + */ + public Multimap getModifiers() + { + return this.unmodifiableModifiers; + } + + /** + * Returns the attribute map before any changes from other event listeners was made. + */ + public Multimap getOriginalModifiers() + { + return this.originalModifiers; + } + + /** + * Gets a modifiable map instance, creating it if the current map is currently unmodifiable + */ + private Multimap getModifiableMap() + { + if (this.modifiableModifiers == null) + { + this.modifiableModifiers = HashMultimap.create(this.originalModifiers); + this.unmodifiableModifiers = Multimaps.unmodifiableMultimap(this.modifiableModifiers); + } + return this.modifiableModifiers; + } + + /** + * Adds a new attribute modifier to the given stack. + * Modifier must have a consistent UUID for consistency between equipping and unequipping items. + * Modifier name should clearly identify the mod that added the modifier. + * @param attribute Attribute + * @param modifier Modifier instance. + * @return True if the attribute was added, false if it was already present + */ + public boolean addModifier(Attribute attribute, AttributeModifier modifier) + { + return getModifiableMap().put(attribute, modifier); + } + + /** + * Removes a single modifier for the given attribute + * @param attribute Attribute + * @param modifier Modifier instance + * @return True if an attribute was removed, false if no change + */ + public boolean removeModifier(Attribute attribute, AttributeModifier modifier) + { + return getModifiableMap().remove(attribute, modifier); + } + + /** + * Removes all modifiers for the given attribute + * @param attribute Attribute + * @return Collection of removed modifiers + */ + public Collection removeAttribute(Attribute attribute) + { + return getModifiableMap().removeAll(attribute); + } + + /** + * Removes all modifiers for all attributes + */ + public void clearModifiers() + { + getModifiableMap().clear(); + } + + /** Gets the slot containing this stack */ + public EquipmentSlotType getSlotType() + { + return this.slotType; + } + + /** Gets the item stack instance */ + public ItemStack getItemStack() + { + return this.stack; + } +} \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/item/ItemAttributeModifierTest.java b/src/test/java/net/minecraftforge/debug/item/ItemAttributeModifierTest.java new file mode 100644 index 000000000..a34a2dafb --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/item/ItemAttributeModifierTest.java @@ -0,0 +1,55 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package net.minecraftforge.debug.item; + +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.AttributeModifier.Operation; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraftforge.event.ItemAttributeModifierEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod(ItemAttributeModifierTest.MOD_ID) +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE, modid = ItemAttributeModifierTest.MOD_ID) +public class ItemAttributeModifierTest +{ + public static final String MOD_ID = "item_modifier_test"; + public static final boolean ENABLED = true; + private static final AttributeModifier MODIFIER = new AttributeModifier(MOD_ID, 10f, Operation.ADDITION); + + @SubscribeEvent + public static void onItemAttribute(ItemAttributeModifierEvent event) + { + if (ENABLED && event.getSlotType() == EquipmentSlotType.MAINHAND) + { + final Item item = event.getItemStack().getItem(); + if (item == Items.APPLE) + { + event.addModifier(Attributes.ARMOR, MODIFIER); + } + else if (item == Items.GOLDEN_SWORD) + { + event.clearModifiers(); + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 2131c2b57..3eafb29be 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion="[28,)" license="LGPL v2.1" [[mods]] - modId="containertypetest" + modId="containertypetest" [[mods]] modId="potion_event_test" [[mods]] @@ -97,4 +97,6 @@ license="LGPL v2.1" [[mods]] modId="render_local_player_test" [[mods]] - modId="forge_world_type_test" \ No newline at end of file + modId="item_modifier_test" +[[mods]] + modId="forge_world_type_test"