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 e6f129f9b8..5be9d9c3a4 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
@@ -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;
@@ -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 food component can also be set via {@link Item.Settings#food(FoodComponent)}.
+ * If you want to get a food component for a stack, is recommended 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();
+ }
}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java
index 0c646a6aad..4f1932ca00 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java
@@ -16,6 +16,9 @@
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;
@@ -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 item stack's {@link FoodComponent}, or {@code null} if none was set
+ */
+ default @Nullable FoodComponent getFoodComponent() {
+ return ((ItemStack) this).getItem().getFoodComponent(((ItemStack) this));
+ }
}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java
new file mode 100644
index 0000000000..bef3b5e09a
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/BlockItemMixin.java
@@ -0,0 +1,32 @@
+/*
+ * 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.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"))
+ private boolean isStackAwareFood(BlockItem instance, ItemUsageContext context) {
+ return context.getStack().isFood();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java
new file mode 100644
index 0000000000..45040e85a9
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CatEntityMixin.java
@@ -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();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java
new file mode 100644
index 0000000000..ebb69882ed
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/CookingRecipeJsonBuilderMixin.java
@@ -0,0 +1,32 @@
+/*
+ * 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.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.Item;
+
+@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();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java
new file mode 100644
index 0000000000..c8a7ea63ef
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/FoxEntityMixin.java
@@ -0,0 +1,46 @@
+/*
+ * 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.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))
+ private boolean isStackAwareFood(Item instance, ItemStack stack) {
+ return stack.isFood();
+ }
+
+ @Redirect(method = {"canPickupItem"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;isFood()Z", ordinal = 1))
+ private boolean isEquippedStackAwareFood(Item instance) {
+ return this.getEquippedStack(EquipmentSlot.MAINHAND).isFood();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java
new file mode 100644
index 0000000000..39836fad32
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/HungerManagerMixin.java
@@ -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"))
+ private boolean isStackAwareFood(Item instance, Item item, ItemStack stack) {
+ return stack.isFood();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java
index 8214295ee3..8d18b0b434 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/ItemMixin.java
@@ -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;
@@ -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();
+ }
}
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 89df9b5c81..0d311f3c33 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
@@ -28,6 +28,7 @@
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EquipmentSlot;
@@ -44,7 +45,8 @@
@Mixin(ItemStack.class)
public abstract class ItemStackMixin implements FabricItemStack {
- @Shadow public abstract Item getItem();
+ @Shadow
+ public abstract Item getItem();
@Unique
private LivingEntity fabric_damagingEntity;
@@ -100,4 +102,9 @@ public Multimap hookGetAttributeModifi
public boolean hookIsSuitableFor(Item item, BlockState state) {
return item.isSuitableFor((ItemStack) (Object) this, state);
}
+
+ @Inject(method = "isFood", at = @At("HEAD"), cancellable = true)
+ public void isStackAwareFood(CallbackInfoReturnable cir) {
+ cir.setReturnValue(this.getFoodComponent() != null);
+ }
}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java
index d147509d47..920468203a 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/LivingEntityMixin.java
@@ -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 info) {
EquipmentSlotProvider equipmentSlotProvider = ((ItemExtensions) stack.getItem()).fabric_getEquipmentSlotProvider();
@@ -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();
+ }
}
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java
new file mode 100644
index 0000000000..ca3245ee09
--- /dev/null
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/WolfEntityMixin.java
@@ -0,0 +1,61 @@
+/*
+ * 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.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.CallbackInfoReturnable;
+
+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.ActionResult;
+import net.minecraft.util.Hand;
+
+@Mixin(WolfEntity.class)
+class WolfEntityMixin {
+ @Unique
+ private static final ThreadLocal INTERACTION_STACK = new ThreadLocal<>();
+
+ @Inject(method = "interactMob", at = @At("HEAD"))
+ private void storeCopy(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) {
+ INTERACTION_STACK.set(player.getStackInHand(hand).copy());
+ }
+
+ @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) {
+ FoodComponent fc = INTERACTION_STACK.get().getFoodComponent();
+ INTERACTION_STACK.remove();
+ return fc;
+ }
+
+ @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))
+ private boolean isStackAwareFood(Item instance, ItemStack stack) {
+ return stack.isFood();
+ }
+}
diff --git a/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json b/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json
index 8993bf8b2b..59dae72028 100644
--- a/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json
+++ b/fabric-item-api-v1/src/main/resources/fabric-item-api-v1.mixins.json
@@ -5,11 +5,17 @@
"mixins": [
"AbstractFurnaceBlockEntityMixin",
"ArmorItemMixin",
+ "BlockItemMixin",
"BrewingStandBlockEntityMixin",
+ "CatEntityMixin",
+ "CookingRecipeJsonBuilderMixin",
+ "FoxEntityMixin",
+ "HungerManagerMixin",
"ItemMixin",
"ItemStackMixin",
"LivingEntityMixin",
- "RecipeMixin"
+ "RecipeMixin",
+ "WolfEntityMixin"
],
"injectors": {
"defaultRequire": 1
diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java
index 932259c3d1..fcd6768ee9 100644
--- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java
+++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java
@@ -22,10 +22,10 @@
import net.minecraft.item.ToolMaterials;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.potion.Potions;
-import net.minecraft.text.Text;
-import net.minecraft.util.Identifier;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java
new file mode 100644
index 0000000000..4d58c04dec
--- /dev/null
+++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/FoodGameInitializer.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.jetbrains.annotations.Nullable;
+
+import net.minecraft.item.FoodComponent;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.registry.Registries;
+import net.minecraft.registry.Registry;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
+
+public final class FoodGameInitializer implements ModInitializer {
+ public static final Item DAMAGE = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "damage_food"), new DamageFood(new FabricItemSettings().maxDamage(20)));
+ public static final Item NAME = Registry.register(Registries.ITEM, new Identifier("fabric-item-api-v1-testmod", "name_food"), new NameFood(new FabricItemSettings()));
+
+ @Override
+ public void onInitialize() {
+ }
+
+ public static class DamageFood extends Item {
+ public DamageFood(Settings settings) {
+ super(settings);
+ }
+
+ @Override
+ public @Nullable FoodComponent getFoodComponent(ItemStack stack) {
+ return new FoodComponent.Builder()
+ .hunger(20 - 20 * stack.getDamage() / stack.getMaxDamage())
+ .saturationModifier(0.5f)
+ .build();
+ }
+ }
+
+ public static class NameFood extends Item {
+ public NameFood(Settings settings) {
+ super(settings);
+ }
+
+ @Override
+ public @Nullable FoodComponent getFoodComponent(ItemStack stack) {
+ return Registries.ITEM.get(new Identifier(stack.getName().getString())).getFoodComponent(stack);
+ }
+ }
+}
diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java
new file mode 100644
index 0000000000..16938db7d5
--- /dev/null
+++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FoodGameTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gametest;
+
+import java.util.Objects;
+
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.passive.WolfEntity;
+import net.minecraft.entity.player.HungerManager;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.FoodComponent;
+import net.minecraft.item.FoodComponents;
+import net.minecraft.item.ItemStack;
+import net.minecraft.test.GameTest;
+import net.minecraft.test.TestContext;
+import net.minecraft.text.Text;
+import net.minecraft.util.Hand;
+import net.minecraft.util.math.Vec3d;
+
+import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
+import net.fabricmc.fabric.test.item.FoodGameInitializer;
+
+public final class FoodGameTest implements FabricGameTest {
+ @GameTest(templateName = EMPTY_STRUCTURE)
+ public void damageFoodTest(TestContext context) {
+ PlayerEntity player = context.createMockSurvivalPlayer();
+ HungerManager hungerManager = player.getHungerManager();
+
+ for (int damage : new int[]{0, 1, 10, 19}) {
+ hungerManager.setFoodLevel(0);
+ hungerManager.setSaturationLevel(0);
+ ItemStack foodStack = FoodGameInitializer.DAMAGE.getDefaultStack();
+ foodStack.setDamage(damage);
+ player.eatFood(player.getWorld(), foodStack.copy());
+ FoodComponent fc = Objects.requireNonNull(foodStack.getFoodComponent());
+ int foodActual = hungerManager.getFoodLevel();
+ int foodExpect = Math.min(20, fc.getHunger());
+ context.assertTrue(foodActual == foodExpect, "damage=%d, food actual %d, expect %d".formatted(damage, foodActual, foodExpect));
+ float satActual = hungerManager.getSaturationLevel();
+ float satExpect = Math.min(foodExpect, fc.getHunger() * fc.getSaturationModifier() * 2);
+ context.assertTrue(satActual == satExpect, "damage=%d, sat actual %f, expect %f".formatted(damage, satActual, satExpect));
+ }
+
+ context.complete();
+ }
+
+ @GameTest(templateName = EMPTY_STRUCTURE)
+ public void nameFoodTest(TestContext context) {
+ PlayerEntity player = context.createMockSurvivalPlayer();
+ HungerManager hungerManager = player.getHungerManager();
+ hungerManager.setFoodLevel(0);
+ hungerManager.setSaturationLevel(0);
+ ItemStack foodStack = FoodGameInitializer.NAME.getDefaultStack();
+ foodStack.setCustomName(Text.literal("enchanted_golden_apple"));
+ player.eatFood(player.getWorld(), foodStack.copy());
+ FoodComponent fc = FoodComponents.ENCHANTED_GOLDEN_APPLE;
+ int foodActual = hungerManager.getFoodLevel();
+ int foodExpect = Math.min(20, fc.getHunger());
+ context.assertTrue(foodActual == foodExpect, "enchanted_golden_apple, food actual %d, expect %d".formatted(foodActual, foodExpect));
+ float satActual = hungerManager.getSaturationLevel();
+ float satExpect = Math.min(foodExpect, fc.getHunger() * fc.getSaturationModifier() * 2);
+ context.assertTrue(satActual == satExpect, "enchanted_golden_apple, sat actual %f, expect %f".formatted(satActual, satExpect));
+ context.complete();
+ }
+
+ @GameTest(templateName = EMPTY_STRUCTURE)
+ public void nameMeatTest(TestContext context) {
+ PlayerEntity player = context.createMockSurvivalPlayer();
+ WolfEntity wolf = context.spawnEntity(EntityType.WOLF, context.getRelative(Vec3d.ZERO));
+ wolf.setTamed(true);
+ wolf.setOwner(player);
+ wolf.setHealth(1f);
+ ItemStack meat = FoodGameInitializer.NAME.getDefaultStack();
+ meat.setCustomName(Text.of("mutton"));
+ player.setStackInHand(Hand.MAIN_HAND, meat);
+ player.interact(wolf, Hand.MAIN_HAND);
+ float wolfHealth = wolf.getHealth();
+ context.assertTrue(wolfHealth > 0, "actual %f, expect > 0".formatted(wolfHealth));
+ context.complete();
+ }
+}
diff --git a/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json
new file mode 100644
index 0000000000..a254098776
--- /dev/null
+++ b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/damage_food.json
@@ -0,0 +1,3 @@
+{
+ "parent": "minecraft:item/wooden_pickaxe"
+}
diff --git a/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json
new file mode 100644
index 0000000000..70e896e705
--- /dev/null
+++ b/fabric-item-api-v1/src/testmod/resources/assets/fabric-item-api-v1-testmod/models/item/name_food.json
@@ -0,0 +1,3 @@
+{
+ "parent": "minecraft:item/book"
+}
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 56e2f60a6d..627b046066 100644
--- a/fabric-item-api-v1/src/testmod/resources/fabric.mod.json
+++ b/fabric-item-api-v1/src/testmod/resources/fabric.mod.json
@@ -14,7 +14,8 @@
"net.fabricmc.fabric.test.item.FabricItemSettingsTests",
"net.fabricmc.fabric.test.item.ItemUpdateAnimationTest",
"net.fabricmc.fabric.test.item.ModifyItemAttributeModifiersCallbackTest",
- "net.fabricmc.fabric.test.item.ArmorKnockbackResistanceTest"
+ "net.fabricmc.fabric.test.item.ArmorKnockbackResistanceTest",
+ "net.fabricmc.fabric.test.item.FoodGameInitializer"
],
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
@@ -22,7 +23,8 @@
"fabric-gametest" : [
"net.fabricmc.fabric.test.item.gametest.BrewingStandGameTest",
"net.fabricmc.fabric.test.item.gametest.FurnaceGameTest",
- "net.fabricmc.fabric.test.item.gametest.RecipeGameTest"
+ "net.fabricmc.fabric.test.item.gametest.RecipeGameTest",
+ "net.fabricmc.fabric.test.item.gametest.FoodGameTest"
]
},
"mixins": [