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 7 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,12 +17,14 @@
package net.fabricmc.fabric.api.item.v1;

import com.google.common.collect.Multimap;
import org.jetbrains.annotations.Nullable;

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;
Expand Down Expand Up @@ -122,4 +124,15 @@ default boolean isSuitableFor(ItemStack stack, BlockState state) {
default ItemStack getRecipeRemainder(ItemStack stack) {
return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY;
}

/**
* 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()}.
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
*
* @return this item's {@link FoodComponent}, or {@code null} if none was set.
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
*/
default @Nullable FoodComponent getFoodComponent(ItemStack stack) {
return ((Item) this).getFoodComponent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

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

import org.jetbrains.annotations.Nullable;

import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

/*
/**
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
* Fabric-provided extensions for {@link ItemStack}.
* This interface is automatically implemented on all item stacks via Mixin and interface injection.
*/
Expand All @@ -36,4 +39,14 @@ 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,16 @@
package net.fabricmc.fabric.mixin.item;
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemUsageContext;

@Mixin(BlockItem.class)
class BlockItemMixin {
@Redirect(method = "useOnBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/BlockItem;isFood()Z"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could a lot of these mixins be improved by using WrapOperation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At that moment IJ Idea and Minecraft Dev plugin doesn't support mixin extra well, so I use redirect instead to avoid potential a mass of errors.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCDev has had full mixinextras support for a long time now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah? I had a try just now, and found that it still suggests incorrect return type wrapping putting field.
For example, for the following wrap:

@WrapOperation(method = "<clinit>", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/api/transfer/v1/fluid/FluidStorage;ITEM:Lnet/fabricmc/fabric/api/lookup/v1/item/ItemApiLookup;", opcode = Opcodes.PUTSTATIC))

the correct handler method signature should be

private static void customField(ItemApiLookup<Storage<FluidVariant>, ContainerItemContext> value, Operation<ItemApiLookup<Storage<FluidVariant>, ContainerItemContext>> original)

but minecraft dev plugin suggests that it should be

private static ItemApiLookup<Storage<FluidVariant>, ContainerItemContext> customField(ItemApiLookup<Storage<FluidVariant>, ContainerItemContext> value, Operation<ItemApiLookup<Storage<FluidVariant>, ContainerItemContext>> original)

The latter causes an error on game launching.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok you might be right about that one… will fix
Don’t know of any other such cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just found that fabric api project still remains loader 0.14.21... And fabric item api still depends on fabricloader >=0.4.0 in fabric.mod.json... Shall we update them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had the same problem in #3403 (comment) and decided on using the @reDIrect's in 1.20.1/.2 and then for .4 using ME. Quite happy to leave this as-is and when cherry-picking to 1.20.4 I can change this to use ME.

private boolean isStackAwareFood(BlockItem instance, ItemUsageContext context) {
return context.getStack().isFood();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.mixin.item;

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;

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;

@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();
}

@Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z"))
private boolean isStackAwareFood(Item instance, PlayerEntity player, Hand hand) {
return player.getStackInHand(hand).isFood();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package net.fabricmc.fabric.mixin.item;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import net.minecraft.data.server.recipe.CookingRecipeJsonBuilder;
import net.minecraft.item.BlockItem;

Check failure on line 8 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

Unused import - net.minecraft.item.BlockItem.
import net.minecraft.item.Item;
import net.minecraft.item.ItemUsageContext;

Check failure on line 10 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

Unused import - net.minecraft.item.ItemUsageContext.

@Mixin(CookingRecipeJsonBuilder.class)
class CookingRecipeJsonBuilderMixin {
@Redirect(method = "getSmeltingRecipeCategory", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z"))
private static boolean isStackAwareFood(Item instance) {
return instance.getDefaultStack().isFood();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.fabricmc.fabric.mixin.item;
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.passive.FoxEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

@Mixin(FoxEntity.class)
abstract class FoxEntityMixin extends AnimalEntity {
protected FoxEntityMixin(EntityType<? extends AnimalEntity> entityType, World world) {
super(entityType, world);
}

@Redirect(method = {"canEat", "canPickupItem"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z",ordinal = 0))

Check failure on line 21 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

',' is not followed by whitespace.
private boolean isStackAwareFood(Item instance, ItemStack stack) {
return stack.isFood();
}

Check failure on line 24 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

missing blank line after block at same indentation level
@Redirect(method = {"canPickupItem"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z",ordinal = 1))

Check failure on line 25 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

',' is not followed by whitespace.
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
private boolean isEquippedStackAwareFood(Item instance) {
return this.getEquippedStack(EquipmentSlot.MAINHAND).isFood();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.mixin.item;

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;

import net.minecraft.entity.player.HungerManager;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;

@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();
}

@Redirect(method = "eat", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z",ordinal = 0))

Check failure on line 36 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

',' is not followed by whitespace.
private boolean isStackAwareFood(Item instance, Item item, ItemStack stack) {
return stack.isFood();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@
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.entity.LivingEntity;
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 net.minecraft.world.World;

import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
Expand Down Expand Up @@ -67,4 +74,29 @@ 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();
}

@Redirect(method = {"getMaxUseTime", "getUseAction"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z"))
private boolean isStackAwareFood(Item instance, ItemStack stack) {
return stack.isFood();
}

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

@Redirect(method = "finishUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z"))
private boolean isStackAwareFood(Item instance, ItemStack stack, World world, LivingEntity user) {
return stack.isFood();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
Expand Down Expand Up @@ -100,4 +101,14 @@
public boolean hookIsSuitableFor(Item item, BlockState state) {
return item.isSuitableFor((ItemStack) (Object) this, state);
}

/**
* @author Phoupraw
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
* @reason use stack-aware {@link #getFoodComponent()} instead
*/
@Overwrite
Phoupraw marked this conversation as resolved.
Show resolved Hide resolved
public boolean isFood() {
return this.getFoodComponent() != null;
}

Check failure on line 112 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemStackMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

blank line before }

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,29 @@

package net.fabricmc.fabric.mixin.item;

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;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.impl.item.ItemExtensions;

@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,19 @@ 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();
}

@Redirect(method = "applyFoodEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z"))
private boolean isStackAwareFood(Item instance, ItemStack stack, World world, LivingEntity targetEntity) {
return stack.isFood();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.mixin.item;

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;

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;

@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();
}

@Redirect(method = "isBreedingItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z",ordinal = 0))

Check failure on line 43 in fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java

View workflow job for this annotation

GitHub Actions / build (17-jdk)

',' is not followed by whitespace.
private boolean isStackAwareFood(Item instance, ItemStack stack) {
return stack.isFood();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@
"mixins": [
"AbstractFurnaceBlockEntityMixin",
"ArmorItemMixin",
"BlockItemMixin",
"BrewingStandBlockEntityMixin",
"CatEntityMixin",
"CookingRecipeJsonBuilderMixin",
"FoxEntityMixin",
"HungerManagerMixin",
"ItemMixin",
"ItemStackMixin",
"LivingEntityMixin",
"RecipeMixin"
"RecipeMixin",
"WolfEntityMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down
Loading
Loading