Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack aware getFoodComponent #3295

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
package net.fabricmc.fabric.api.item.v1;

import com.google.common.collect.Multimap;

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.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;

/**
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
Expand All @@ -36,90 +37,97 @@
* to be evaluated on a case-by-case basis. Otherwise, they are better suited for more specialized APIs.
*/
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,
* and returning false cancels this animation.
*
* @param player the current player; this may be safely cast to {@link net.minecraft.client.network.ClientPlayerEntity} in client-only code
* @param hand the hand; this function applies both to the main hand and the off hand
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to run the vanilla animation, false to cancel it.
*/
default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack oldStack, ItemStack newStack) {
return true;
}

/**
* When the NBT of the selected stack changes, block breaking progress is reset.
* This function is called when the NBT of the selected stack has changed,
* and returning true allows the block breaking progress to continue.
*
* @param player the player breaking the block
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to allow continuing block breaking, false to reset the progress.
*/
default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return false;
}
/**
* 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,
* and returning false cancels this animation.
* @param player the current player; this may be safely cast to {@link net.minecraft.client.network.ClientPlayerEntity} in client-only code
* @param hand the hand; this function applies both to the main hand and the off hand
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to run the vanilla animation, false to cancel it.
*/
default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack oldStack, ItemStack newStack) {
return true;
}

/**
* When the NBT of the selected stack changes, block breaking progress is reset.
* This function is called when the NBT of the selected stack has changed,
* and returning true allows the block breaking progress to continue.
* @param player the player breaking the block
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to allow continuing block breaking, false to reset the progress.
*/
default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return false;
}

/**
* Return the attribute modifiers to apply when this stack is worn in a living entity equipment slot.
* Stack-aware version of {@link Item#getAttributeModifiers(EquipmentSlot)}.
*
* <p>Note that attribute modifiers are only updated when the stack changes, i.e. when {@code ItemStack.areEqual(old, new)} is false.
* @param stack the current stack
* @param slot the equipment slot this stack is in
* @return the attribute modifiers
*/
default Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
return ((Item) this).getAttributeModifiers(slot);
}

/**
* Determines if mining with this item allows drops to be harvested from the specified block state.
* Stack-aware version of {@link Item#isSuitableFor(BlockState)}.
* @param stack the current stack
* @param state the block state of the targeted block
* @return true if drops can be harvested
*/
default boolean isSuitableFor(ItemStack stack, BlockState state) {
return ((Item) this).isSuitableFor(state);
}

/**
* Return the attribute modifiers to apply when this stack is worn in a living entity equipment slot.
* Stack-aware version of {@link Item#getAttributeModifiers(EquipmentSlot)}.
*
* <p>Note that attribute modifiers are only updated when the stack changes, i.e. when {@code ItemStack.areEqual(old, new)} is false.
*
* @param stack the current stack
* @param slot the equipment slot this stack is in
* @return the attribute modifiers
*/
default Multimap<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot slot) {
return ((Item) this).getAttributeModifiers(slot);
}
/**
* Returns a leftover item stack after {@code stack} is consumed in a recipe.
* (This is also known as "recipe remainder".)
* For example, using a lava bucket in a furnace as fuel will leave an empty bucket.
*
* <p>Here is an example for a recipe remainder that increments the item's damage.
*
* <pre>{@code
* if (stack.getDamage() < stack.getMaxDamage() - 1) {
* ItemStack moreDamaged = stack.copy();
* moreDamaged.setDamage(stack.getDamage() + 1);
* return moreDamaged;
* }
*
* return ItemStack.EMPTY;
* }</pre>
*
*
* <p>This is a stack-aware version of {@link Item#getRecipeRemainder()}.
*
* <p>Note that simple item remainders can also be set via {@link Item.Settings#recipeRemainder(Item)}.
*
* <p>If you want to get a remainder for a stack,
* is recommended to use the stack version of this method: {@link FabricItemStack#getRecipeRemainder()}.
* @param stack the consumed {@link ItemStack}
* @return the leftover item stack
*/
default ItemStack getRecipeRemainder(ItemStack stack) {
return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY;
}

/**
* Determines if mining with this item allows drops to be harvested from the specified block state.
* Stack-aware version of {@link Item#isSuitableFor(BlockState)}.
*
* @param stack the current stack
* @param state the block state of the targeted block
* @return true if drops can be harvested
*/
default boolean isSuitableFor(ItemStack stack, BlockState state) {
return ((Item) this).isSuitableFor(state);
}
/**
* This is a stack-aware version of {@link Item#getFoodComponent()}.
* Note that simple item remainders can also be set via {@link Item.Settings#food(FoodComponent)}.
* If you want to get a remainder for a stack, is <b>recommended</b> to use the stack version of this method: {@link FabricItemStack#getFoodComponent()}.
* @return this item's {@link FoodComponent}, or {@code null} if none was set.
*/
default @Nullable FoodComponent getFoodComponent(ItemStack stack) {
return ((Item) this).getFoodComponent();
}

/**
* Returns a leftover item stack after {@code stack} is consumed in a recipe.
* (This is also known as "recipe remainder".)
* For example, using a lava bucket in a furnace as fuel will leave an empty bucket.
*
* <p>Here is an example for a recipe remainder that increments the item's damage.
*
* <pre>{@code
* if (stack.getDamage() < stack.getMaxDamage() - 1) {
* ItemStack moreDamaged = stack.copy();
* moreDamaged.setDamage(stack.getDamage() + 1);
* return moreDamaged;
* }
*
* return ItemStack.EMPTY;
* }</pre>
*
*
* <p>This is a stack-aware version of {@link Item#getRecipeRemainder()}.
*
* <p>Note that simple item remainders can also be set via {@link Item.Settings#recipeRemainder(Item)}.
*
* <p>If you want to get a remainder for a stack,
* is recommended to use the stack version of this method: {@link FabricItemStack#getRecipeRemainder()}.
*
* @param stack the consumed {@link ItemStack}
* @return the leftover item stack
*/
default ItemStack getRecipeRemainder(ItemStack stack) {
return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package net.fabricmc.fabric.api.item.v1;

import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import org.jetbrains.annotations.Nullable;

/*
* Fabric-provided extensions for {@link ItemStack}.
Expand All @@ -36,4 +38,13 @@ public interface FabricItemStack {
default ItemStack getRecipeRemainder() {
return ((ItemStack) this).getItem().getRecipeRemainder((ItemStack) this);
}

/**
* Stack-aware version of {@link Item#getFoodComponent()}.
* See {@link FabricItem#getFoodComponent(ItemStack)} for a more in depth description.
* @return this itemStack's {@link FoodComponent}, or {@code null} if none was set.
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
*/
default @Nullable FoodComponent getFoodComponent() {
return ((ItemStack) this).getItem().getFoodComponent(((ItemStack) this));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.fabricmc.fabric.mixin.item;

import net.minecraft.entity.passive.CatEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(CatEntity.class)
class CatEntityMixin {

@Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, PlayerEntity player, Hand hand) {
return player.getStackInHand(hand).getFoodComponent();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.fabricmc.fabric.mixin.item;

import net.minecraft.entity.player.HungerManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(HungerManager.class)
class HungerManagerMixin {

@Redirect(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance,Item item, ItemStack stack) {
return stack.getFoodComponent();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@

package net.fabricmc.fabric.mixin.item;

import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import net.minecraft.item.Item;
Expand Down Expand Up @@ -67,4 +73,14 @@ public CustomDamageHandler fabric_getCustomDamageHandler() {
public void fabric_setCustomDamageHandler(@Nullable CustomDamageHandler handler) {
this.customDamageHandler = handler;
}

@Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, World world, PlayerEntity user, Hand hand) {
return user.getStackInHand(hand).getFoodComponent();
}

@Redirect(method = "getMaxUseTime", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack) {
return stack.getFoodComponent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@

package net.fabricmc.fabric.mixin.item;

import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import net.minecraft.entity.EquipmentSlot;
Expand All @@ -30,6 +36,9 @@

@Mixin(LivingEntity.class)
abstract class LivingEntityMixin {

@Shadow protected ItemStack activeItemStack;

@Inject(method = "getPreferredEquipmentSlot", at = @At(value = "HEAD"), cancellable = true)
private static void onGetPreferredEquipmentSlot(ItemStack stack, CallbackInfoReturnable<EquipmentSlot> info) {
EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) stack.getItem()).fabric_getEquipmentSlotProvider();
Expand All @@ -38,4 +47,14 @@ private static void onGetPreferredEquipmentSlot(ItemStack stack, CallbackInfoRet
info.setReturnValue(equipmentSlotProvider.getPreferredEquipmentSlot(stack));
}
}

@Redirect(method = "shouldSpawnConsumptionEffects",at = @At(value = "INVOKE",target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance) {
return this.activeItemStack.getFoodComponent();
}

@Redirect(method = "applyFoodEffects",at = @At(value = "INVOKE",target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack, World world, LivingEntity targetEntity) {
return stack.getFoodComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.fabricmc.fabric.mixin.item;

import net.minecraft.entity.passive.WolfEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(WolfEntity.class)
class WolfEntityMixin {

@Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, PlayerEntity player, Hand hand) {
return player.getStackInHand(hand).getFoodComponent();
}

@Redirect(method = "isBreedingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;getFoodComponent()Lnet/minecraft/item/FoodComponent;"))
private @Nullable FoodComponent getStackAwareFoodComponent(Item instance, ItemStack stack) {
return stack.getFoodComponent();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
"AbstractFurnaceBlockEntityMixin",
"ArmorItemMixin",
"BrewingStandBlockEntityMixin",
"CatEntityMixin",
"HungerManagerMixin",
"ItemMixin",
"ItemStackMixin",
"LivingEntityMixin",
"RecipeMixin"
"RecipeMixin",
"WolfEntityMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down