diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
index f46fd65e0..cfc842419 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
@@ -16,19 +16,9 @@
package net.fabricmc.fabric.api.item.v1;
-import java.util.function.Consumer;
-
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
-
import net.fabricmc.fabric.impl.item.FabricItemInternals;
-
-import net.minecraft.entity.LivingEntity;
-
-import net.minecraftforge.common.extensions.IForgeItem;
-import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.Nullable;
-
import net.minecraft.block.BlockState;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.attribute.EntityAttribute;
@@ -37,9 +27,8 @@
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
-
-import net.fabricmc.fabric.impl.client.item.ItemApiClientEventHooks;
-import net.fabricmc.fabric.impl.item.ItemExtensions;
+import net.minecraftforge.common.extensions.IForgeItem;
+import org.jetbrains.annotations.ApiStatus;
/**
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
@@ -49,7 +38,7 @@
*
Note to maintainers: Functions should only be added to this interface if they are general-purpose enough,
* to be evaluated on a case-by-case basis. Otherwise, they are better suited for more specialized APIs.
*/
-public interface FabricItem extends IForgeItem {
+public interface FabricItem {
/**
* When the NBT of an item stack in the main hand or off hand changes, vanilla runs an "update animation".
* This function is called on the client side when the NBT or count of the stack has changed, but not the item,
@@ -62,7 +51,7 @@ public interface FabricItem extends IForgeItem {
* @return true to run the vanilla animation, false to cancel it.
*/
default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack oldStack, ItemStack newStack) {
- return !FabricItemInternals.allowForgeCall() || shouldCauseReequipAnimation(oldStack, newStack, false);
+ return !FabricItemInternals.allowForgeCall() || ((IForgeItem) this).shouldCauseReequipAnimation(oldStack, newStack, false);
}
/**
@@ -76,7 +65,7 @@ default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStac
* @return true to allow continuing block breaking, false to reset the progress.
*/
default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
- return FabricItemInternals.allowForgeCall() && !shouldCauseBlockBreakReset(oldStack, newStack);
+ return FabricItemInternals.allowForgeCall() && !((IForgeItem) this).shouldCauseBlockBreakReset(oldStack, newStack);
}
/**
@@ -91,7 +80,7 @@ default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldS
*/
default Multimap getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
if (FabricItemInternals.allowForgeCall()) {
- return getAttributeModifiers(slot, stack);
+ return ((IForgeItem) this).getAttributeModifiers(slot, stack);
}
return HashMultimap.create();
}
@@ -105,7 +94,7 @@ default Multimap getAttributeModifiers
* @return true if drops can be harvested
*/
default boolean isSuitableFor(ItemStack stack, BlockState state) {
- return FabricItemInternals.allowForgeCall() && isCorrectToolForDrops(stack, state);
+ return FabricItemInternals.allowForgeCall() && ((IForgeItem) this).isCorrectToolForDrops(stack, state);
}
/**
@@ -139,69 +128,8 @@ default boolean isSuitableFor(ItemStack stack, BlockState state) {
@ApiStatus.OverrideOnly
default ItemStack getRecipeRemainder(ItemStack stack) {
if (FabricItemInternals.allowForgeCall()) {
- return getCraftingRemainingItem(stack);
+ return ((IForgeItem) this).getCraftingRemainingItem(stack);
}
return ItemStack.EMPTY;
}
-
- // FFAPI: Forge default implementation
-
- @Override
- default ItemStack getCraftingRemainingItem(ItemStack stack) {
- ItemStack fabricRemainder = FabricItemInternals.nonRecursiveApiCall(() -> getRecipeRemainder(stack));
- if (!fabricRemainder.isEmpty()) {
- return fabricRemainder;
- }
- return IForgeItem.super.hasCraftingRemainingItem(stack) ? new ItemStack(((Item) this).getRecipeRemainder()) : ItemStack.EMPTY;
- }
-
- @Override
- default boolean hasCraftingRemainingItem(ItemStack stack) {
- return !FabricItemInternals.nonRecursiveApiCall(() -> getRecipeRemainder(stack)).isEmpty() || IForgeItem.super.hasCraftingRemainingItem(stack);
- }
-
- @Override
- default Multimap getAttributeModifiers(EquipmentSlot slot, ItemStack stack) {
- // Fetch forge attribute modifiers first
- Multimap modifiers = HashMultimap.create(IForgeItem.super.getAttributeModifiers(slot, stack));
- // Add all fabric attribute modifiers
- modifiers.putAll(FabricItemInternals.nonRecursiveApiCall(() -> getAttributeModifiers(stack, slot)));
- return modifiers;
- }
-
- @Override
- default boolean isCorrectToolForDrops(ItemStack stack, BlockState state) {
- return FabricItemInternals.nonRecursiveApiCall(() -> isSuitableFor(stack, state)) || IForgeItem.super.isCorrectToolForDrops(stack, state);
- }
-
- @Override
- default boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
- if (IForgeItem.super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged)) {
- PlayerEntity player = ItemApiClientEventHooks.getClientPlayerSafely();
- Hand hand = oldStack == player.getMainHandStack() ? Hand.MAIN_HAND : Hand.OFF_HAND;
- return FabricItemInternals.nonRecursiveApiCall(() -> allowNbtUpdateAnimation(player, hand, oldStack, newStack));
- }
- return false;
- }
-
- @Override
- default boolean shouldCauseBlockBreakReset(ItemStack oldStack, ItemStack newStack) {
- return IForgeItem.super.shouldCauseBlockBreakReset(oldStack, newStack) && FabricItemInternals.nonRecursiveApiCall(() -> !allowContinuingBlockBreaking(ItemApiClientEventHooks.getClientPlayerSafely(), oldStack, newStack));
- }
-
- @Override
- @Nullable
- default EquipmentSlot getEquipmentSlot(ItemStack stack) {
- EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) stack.getItem()).fabric_getEquipmentSlotProvider();
- return equipmentSlotProvider != null ? equipmentSlotProvider.getPreferredEquipmentSlot(stack) : IForgeItem.super.getEquipmentSlot(stack);
- }
-
- @Override
- default int damageItem(ItemStack stack, int amount, T entity, Consumer onBroken) {
- CustomDamageHandler handler = ((ItemExtensions) this).fabric_getCustomDamageHandler();
- if (handler != null) {
- return handler.damage(stack, amount, entity, (Consumer) onBroken);
- }
- return IForgeItem.super.damageItem(stack, amount, entity, onBroken);
- }
}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/impl/item/FabricItemImplHooks.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/impl/item/FabricItemImplHooks.java
new file mode 100644
index 000000000..5e8807547
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/impl/item/FabricItemImplHooks.java
@@ -0,0 +1,69 @@
+/*
+ * 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.impl.item;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import net.fabricmc.fabric.api.item.v1.FabricItem;
+import net.fabricmc.fabric.impl.client.item.ItemApiClientEventHooks;
+import net.minecraft.block.BlockState;
+import net.minecraft.entity.EquipmentSlot;
+import net.minecraft.entity.attribute.EntityAttribute;
+import net.minecraft.entity.attribute.EntityAttributeModifier;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Hand;
+
+@SuppressWarnings("unused")
+public final class FabricItemImplHooks {
+
+ public static ItemStack getCraftingRemainingItem(FabricItem item, ItemStack stack) {
+ ItemStack fabricRemainder = FabricItemInternals.nonRecursiveApiCall(() -> item.getRecipeRemainder(stack));
+ if (!fabricRemainder.isEmpty()) {
+ return fabricRemainder;
+ }
+ return null;
+ }
+
+ public static boolean hasCraftingRemainingItem(FabricItem item, ItemStack stack) {
+ return !FabricItemInternals.nonRecursiveApiCall(() -> item.getRecipeRemainder(stack)).isEmpty();
+ }
+
+ public static Multimap getAttributeModifiers(Multimap existing, FabricItem item, EquipmentSlot slot, ItemStack stack) {
+ // Fetch forge attribute modifiers first
+ Multimap modifiers = HashMultimap.create(existing);
+ // Add all fabric attribute modifiers
+ modifiers.putAll(FabricItemInternals.nonRecursiveApiCall(() -> item.getAttributeModifiers(stack, slot)));
+ return modifiers;
+ }
+
+ public static boolean isCorrectToolForDrops(FabricItem item, ItemStack stack, BlockState state) {
+ return FabricItemInternals.nonRecursiveApiCall(() -> item.isSuitableFor(stack, state));
+ }
+
+ public static boolean shouldCauseReequipAnimation(FabricItem item, ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
+ PlayerEntity player = ItemApiClientEventHooks.getClientPlayerSafely();
+ Hand hand = oldStack == player.getMainHandStack() ? Hand.MAIN_HAND : Hand.OFF_HAND;
+ return FabricItemInternals.nonRecursiveApiCall(() -> item.allowNbtUpdateAnimation(player, hand, oldStack, newStack));
+ }
+
+ public static boolean shouldCauseBlockBreakReset(boolean original, FabricItem item, ItemStack oldStack, ItemStack newStack) {
+ return original && FabricItemInternals.nonRecursiveApiCall(() -> !item.allowContinuingBlockBreaking(ItemApiClientEventHooks.getClientPlayerSafely(), oldStack, newStack));
+ }
+
+ private FabricItemImplHooks() {}
+}
diff --git a/fabric-item-api-v1/src/main/resources/META-INF/asm/implementFabricItem.js b/fabric-item-api-v1/src/main/resources/META-INF/asm/implementFabricItem.js
new file mode 100644
index 000000000..b6565e494
--- /dev/null
+++ b/fabric-item-api-v1/src/main/resources/META-INF/asm/implementFabricItem.js
@@ -0,0 +1,219 @@
+var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI');
+var Opcodes = Java.type('org.objectweb.asm.Opcodes');
+var MethodInsnNode = Java.type('org.objectweb.asm.tree.MethodInsnNode');
+var InsnNode = Java.type('org.objectweb.asm.tree.InsnNode');
+var JumpInsnNode = Java.type('org.objectweb.asm.tree.JumpInsnNode');
+var LabelNode = Java.type('org.objectweb.asm.tree.LabelNode');
+var VarInsnNode = Java.type('org.objectweb.asm.tree.VarInsnNode');
+var TypeInsnNode = Java.type('org.objectweb.asm.tree.TypeInsnNode');
+
+function initializeCoreMod() {
+ var yarn = ASMAPI.getSystemPropertyFlag('yarn');
+ return {
+ 'extendFabricItem': {
+ 'target': {
+ 'type': 'CLASS',
+ 'name': 'net.minecraftforge.common.extensions.IForgeItem'
+ },
+ 'transformer': function (node) {
+ node.interfaces.add('net/fabricmc/fabric/api/item/v1/FabricItem');
+ return node;
+ }
+ },
+ 'implementGetCraftingRemainingItem': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'getCraftingRemainingItem',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/ItemStack;' : '(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/item/ItemStack;'
+ },
+ 'transformer': function (node) {
+ var insn = ASMAPI.findFirstInstruction(node, Opcodes.NEW);
+ if (insn != null && insn.desc === (yarn ? 'net/minecraft/item/ItemStack' : 'net/minecraft/world/item/ItemStack')) {
+ var target = new LabelNode();
+ var callDesc = yarn ? '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/ItemStack;' : '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/item/ItemStack;';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'getCraftingRemainingItem', callDesc),
+ new InsnNode(Opcodes.DUP),
+ new JumpInsnNode(Opcodes.IFNULL, target),
+ new InsnNode(Opcodes.ARETURN),
+ target
+ );
+ node.instructions.insertBefore(insn, list);
+ }
+ return node;
+ }
+ },
+ 'implementHasCraftingRemainingItem': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'hasCraftingRemainingItem',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;)Z' : '(Lnet/minecraft/world/item/ItemStack;)Z'
+ },
+ 'transformer': function (node) {
+ var target = new LabelNode();
+ var callDesc = yarn ? '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/item/ItemStack;)Z' : '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/item/ItemStack;)Z';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'hasCraftingRemainingItem', callDesc),
+ new JumpInsnNode(Opcodes.IFEQ, target),
+ new InsnNode(Opcodes.ICONST_1),
+ new InsnNode(Opcodes.IRETURN),
+ target
+ );
+ node.instructions.insert(list);
+ return node;
+ }
+ },
+ 'implementGetAttributeModifiers': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'getAttributeModifiers',
+ 'methodDesc': yarn ? '(Lnet/minecraft/entity/EquipmentSlot;Lnet/minecraft/item/ItemStack;)Lcom/google/common/collect/Multimap;' : '(Lnet/minecraft/world/entity/EquipmentSlot;Lnet/minecraft/world/item/ItemStack;)Lcom/google/common/collect/Multimap;'
+ },
+ 'transformer': function (node) {
+ var insn = ASMAPI.findFirstInstruction(node, Opcodes.ARETURN);
+ if (insn != null) {
+ var callDesc = yarn ? '(Lcom/google/common/collect/Multimap;Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/entity/EquipmentSlot;Lnet/minecraft/item/ItemStack;)Lcom/google/common/collect/Multimap;' : '(Lcom/google/common/collect/Multimap;Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/entity/EquipmentSlot;Lnet/minecraft/world/item/ItemStack;)Lcom/google/common/collect/Multimap;';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new VarInsnNode(Opcodes.ALOAD, 2),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'getAttributeModifiers', callDesc)
+ );
+ node.instructions.insertBefore(insn, list);
+ }
+ return node;
+ }
+ },
+ 'implementIsCorrectToolForDrops': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'isCorrectToolForDrops',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z' : '(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/block/state/BlockState;)Z'
+ },
+ 'transformer': function (node) {
+ var target = new LabelNode();
+ var callDesc = yarn ? '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z' : '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/block/state/BlockState;)Z';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new VarInsnNode(Opcodes.ALOAD, 2),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'isCorrectToolForDrops', callDesc),
+ new JumpInsnNode(Opcodes.IFEQ, target),
+ new InsnNode(Opcodes.ICONST_1),
+ new InsnNode(Opcodes.IRETURN),
+ target
+ );
+ node.instructions.insert(list);
+ return node;
+ }
+ },
+ 'implementShouldCauseReequipAnimation': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'shouldCauseReequipAnimation',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;Z)Z' : '(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;Z)Z'
+ },
+ 'transformer': function (node) {
+ var insn = ASMAPI.findFirstInstruction(node, Opcodes.IFNE);
+ if (insn != null) {
+ var callDesc = yarn ? '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;Z)Z' : '(Lnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;Z)Z';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new VarInsnNode(Opcodes.ALOAD, 2),
+ new VarInsnNode(Opcodes.ILOAD, 3),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'shouldCauseReequipAnimation', callDesc),
+ new JumpInsnNode(Opcodes.IFEQ, insn.label)
+ );
+ node.instructions.insert(insn, list);
+ }
+ return node;
+ }
+ },
+ 'implementShouldCauseBlockBreakReset': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'shouldCauseBlockBreakReset',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z' : '(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z'
+ },
+ 'transformer': function (node) {
+ for (var i = node.instructions.size() - 1; i >= 0; i--) {
+ var insn = node.instructions.get(i);
+ if (insn.opcode === Opcodes.IRETURN) {
+ var callDesc = yarn ? '(ZLnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z' : '(ZLnet/fabricmc/fabric/api/item/v1/FabricItem;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z';
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new VarInsnNode(Opcodes.ALOAD, 2),
+ new MethodInsnNode(Opcodes.INVOKESTATIC, 'net/fabricmc/fabric/impl/item/FabricItemImplHooks', 'shouldCauseBlockBreakReset', callDesc)
+ );
+ node.instructions.insertBefore(insn, list);
+ }
+ }
+ return node;
+ }
+ },
+ 'implementGetEquipmentSlot': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'getEquipmentSlot',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/entity/EquipmentSlot;' : '(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/EquipmentSlot;'
+ },
+ 'transformer': function (node) {
+ var target = new LabelNode();
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new MethodInsnNode(Opcodes.INVOKEVIRTUAL, yarn ? 'net/minecraft/item/ItemStack' : 'net/minecraft/world/item/ItemStack', ASMAPI.mapMethod('m_41720_'), yarn ? '()Lnet/minecraft/item/Item;' : '()Lnet/minecraft/world/item/Item;'),
+ new TypeInsnNode(Opcodes.CHECKCAST, 'net/fabricmc/fabric/impl/item/ItemExtensions'),
+ new MethodInsnNode(Opcodes.INVOKEINTERFACE, 'net/fabricmc/fabric/impl/item/ItemExtensions', 'fabric_getEquipmentSlotProvider', '()Lnet/fabricmc/fabric/api/item/v1/EquipmentSlotProvider;'),
+ new InsnNode(Opcodes.DUP),
+ new JumpInsnNode(Opcodes.IFNULL, target),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new MethodInsnNode(Opcodes.INVOKEINTERFACE, 'net/fabricmc/fabric/api/item/v1/EquipmentSlotProvider', 'getPreferredEquipmentSlot', yarn ? '(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/entity/EquipmentSlot;' : '(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/EquipmentSlot;'),
+ new InsnNode(Opcodes.ARETURN),
+ target
+ );
+ node.instructions.insert(list);
+ return node;
+ }
+ },
+ 'implementDamageItem': {
+ 'target': {
+ 'type': 'METHOD',
+ 'class': 'net.minecraftforge.common.extensions.IForgeItem',
+ 'methodName': 'damageItem',
+ 'methodDesc': yarn ? '(Lnet/minecraft/item/ItemStack;ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)I' : '(Lnet/minecraft/world/item/ItemStack;ILnet/minecraft/world/entity/LivingEntity;Ljava/util/function/Consumer;)I'
+ },
+ 'transformer': function (node) {
+ var target = new LabelNode();
+ var list = ASMAPI.listOf(
+ new VarInsnNode(Opcodes.ALOAD, 0),
+ new TypeInsnNode(Opcodes.CHECKCAST, 'net/fabricmc/fabric/impl/item/ItemExtensions'),
+ new MethodInsnNode(Opcodes.INVOKEINTERFACE, 'net/fabricmc/fabric/impl/item/ItemExtensions', 'fabric_getCustomDamageHandler', '()Lnet/fabricmc/fabric/api/item/v1/CustomDamageHandler;'),
+ new InsnNode(Opcodes.DUP),
+ new JumpInsnNode(Opcodes.IFNULL, target),
+ new VarInsnNode(Opcodes.ALOAD, 1),
+ new VarInsnNode(Opcodes.ILOAD, 2),
+ new VarInsnNode(Opcodes.ALOAD, 3),
+ new VarInsnNode(Opcodes.ALOAD, 4),
+ new MethodInsnNode(Opcodes.INVOKEINTERFACE, 'net/fabricmc/fabric/api/item/v1/CustomDamageHandler', 'damage', yarn ? '(Lnet/minecraft/item/ItemStack;ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)I' : '(Lnet/minecraft/world/item/ItemStack;ILnet/minecraft/world/entity/LivingEntity;Ljava/util/function/Consumer;)I'),
+ new InsnNode(Opcodes.IRETURN),
+ target
+ );
+ node.instructions.insert(list);
+ return node;
+ }
+ }
+ }
+}
diff --git a/fabric-item-api-v1/src/main/resources/META-INF/coremods.json b/fabric-item-api-v1/src/main/resources/META-INF/coremods.json
new file mode 100644
index 000000000..6240ffa11
--- /dev/null
+++ b/fabric-item-api-v1/src/main/resources/META-INF/coremods.json
@@ -0,0 +1,3 @@
+{
+ "implementFabricItem": "META-INF/asm/implementFabricItem.js"
+}
diff --git a/gradle.properties b/gradle.properties
index 9c958860b..4b8465cd6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -68,5 +68,5 @@ fabric-client-tags-api-v1-version=1.1.1
loom.platform=forge
forge_version=1.20.1-47.1.3
pack_format=15
-forgified_version=1.9.14
+forgified_version=1.9.15
forge_fabric_loader_version=2.2.1+0.14.21+1.20.1