From 85719ab7b755fbe8144ead1c6d6b783eb7369507 Mon Sep 17 00:00:00 2001 From: frqnny <45723631+frqnny@users.noreply.github.com> Date: Fri, 20 May 2022 13:17:48 -0400 Subject: [PATCH] Add ModifyItemAttributeModifiersCallback (#2175) * Add ItemAttributeModifiersCallback * fix checkstyle errors * fix checkstyle errors v2 * Edit javadoc per Technician's review * Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/ItemAttributeModifiersCallback.java Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> * Change functional interface name per Juuz's review * Change name to ModifyItemAttributeModifiers per Juuz and Technician's request * Forgot to add callback to the name * Return mutable map * Remove unused import Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> --- .../ModifyItemAttributeModifiersCallback.java | 59 +++++++++++++++++++ .../fabric/mixin/item/ItemStackMixin.java | 8 ++- ...ifyItemAttributeModifiersCallbackTest.java | 38 ++++++++++++ .../src/testmod/resources/fabric.mod.json | 3 +- 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/ModifyItemAttributeModifiersCallback.java create mode 100644 fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/ModifyItemAttributeModifiersCallbackTest.java diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/ModifyItemAttributeModifiersCallback.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/ModifyItemAttributeModifiersCallback.java new file mode 100644 index 000000000..2c0a2475f --- /dev/null +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/ModifyItemAttributeModifiersCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.item.v1; + +import com.google.common.collect.Multimap; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.item.ItemStack; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +/** + * Stack-aware attribute modifier callback for foreign items. + * Instead of using Mixin to change attribute modifiers in items not in your mod, + * you can use this event instead, either checking the Item itself or using a tag. + * This event provides you with a guaranteed mutable map you can put attribute modifiers in. + * Do not use for your own Item classes; see {@link FabricItem#getAttributeModifiers} instead. + * For example, the following code modifies a Diamond Helmet to give you five extra hearts when wearing. + * + * <pre> + * {@code + * ModifyItemAttributeModifiersCallback.EVENT.register((stack, slot, attributeModifiers) -> { + * if (stack.isOf(Items.DIAMOND_HELMET) && slot.getEntitySlotId() == HEAD_SLOT_ID) { + * attributeModifiers.put(EntityAttributes.GENERIC_MAX_HEALTH, MODIFIER); + * } + * }); + * } + * </pre> + */ +@FunctionalInterface +public interface ModifyItemAttributeModifiersCallback { + void modifyAttributeModifiers(ItemStack stack, EquipmentSlot slot, Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers); + + Event<ModifyItemAttributeModifiersCallback> EVENT = EventFactory.createArrayBacked( + ModifyItemAttributeModifiersCallback.class, + callbacks -> (stack, slot, attributeModifiers) -> { + for (ModifyItemAttributeModifiersCallback callback : callbacks) { + callback.modifyAttributeModifiers(stack, slot, attributeModifiers); + } + } + ); +} diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java index e0b559dc4..570236465 100644 --- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java +++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java @@ -18,6 +18,7 @@ package net.fabricmc.fabric.mixin.item; import java.util.function.Consumer; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -37,6 +38,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.fabricmc.fabric.api.item.v1.CustomDamageHandler; +import net.fabricmc.fabric.api.item.v1.ModifyItemAttributeModifiersCallback; import net.fabricmc.fabric.impl.item.ItemExtensions; @Mixin(ItemStack.class) @@ -80,7 +82,11 @@ public abstract class ItemStackMixin { ) ) public Multimap<EntityAttribute, EntityAttributeModifier> hookGetAttributeModifiers(Item item, EquipmentSlot slot) { - return item.getAttributeModifiers((ItemStack) (Object) this, slot); + ItemStack stack = (ItemStack) (Object) this; + //we need to ensure it is modifiable for the callback + Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers = HashMultimap.create(item.getAttributeModifiers(stack, slot)); + ModifyItemAttributeModifiersCallback.EVENT.invoker().modifyAttributeModifiers(stack, slot, attributeModifiers); + return attributeModifiers; } @Redirect( diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/ModifyItemAttributeModifiersCallbackTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/ModifyItemAttributeModifiersCallbackTest.java new file mode 100644 index 000000000..72e54d9cd --- /dev/null +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/ModifyItemAttributeModifiersCallbackTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.item; + +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.entity.attribute.EntityAttributes; +import net.minecraft.item.Items; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.item.v1.ModifyItemAttributeModifiersCallback; + +public class ModifyItemAttributeModifiersCallbackTest implements ModInitializer { + public static final int HEAD_SLOT_ID = 3; + public static final EntityAttributeModifier MODIFIER = new EntityAttributeModifier("generic_max_health_modifier", 5.0, EntityAttributeModifier.Operation.ADDITION); + + @Override + public void onInitialize() { + ModifyItemAttributeModifiersCallback.EVENT.register((stack, slot, attributeModifiers) -> { + if (stack.isOf(Items.DIAMOND_HELMET) && slot.getEntitySlotId() == HEAD_SLOT_ID) { + attributeModifiers.put(EntityAttributes.GENERIC_MAX_HEALTH, MODIFIER); + } + }); + } +} diff --git a/fabric-item-api-v1/src/testmod/resources/fabric.mod.json b/fabric-item-api-v1/src/testmod/resources/fabric.mod.json index ec34716a1..2750b292a 100644 --- a/fabric-item-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-item-api-v1/src/testmod/resources/fabric.mod.json @@ -12,7 +12,8 @@ "main": [ "net.fabricmc.fabric.test.item.CustomDamageTest", "net.fabricmc.fabric.test.item.FabricItemSettingsTests", - "net.fabricmc.fabric.test.item.ItemUpdateAnimationTest" + "net.fabricmc.fabric.test.item.ItemUpdateAnimationTest", + "net.fabricmc.fabric.test.item.ModifyItemAttributeModifiersCallbackTest" ], "client": [ "net.fabricmc.fabric.test.item.client.TooltipTests"