diff --git a/logs/latest.log b/logs/latest.log new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/io/github/fabriccommunity/events/impl/InteractionsImpl.java b/src/main/java/io/github/fabriccommunity/events/impl/InteractionsImpl.java new file mode 100644 index 0000000..b103baa --- /dev/null +++ b/src/main/java/io/github/fabriccommunity/events/impl/InteractionsImpl.java @@ -0,0 +1,27 @@ +package io.github.fabriccommunity.events.impl; + +import java.util.concurrent.atomic.AtomicReference; + +import io.github.fabriccommunity.events.play.ItemEvents; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.world.World; + +/** + * Some longer mixin implementations are done in classes such as this one in order to make debugging easier. + */ +public final class InteractionsImpl { + private InteractionsImpl() { + } + + public static ItemStack eatFood(LivingEntity self, World world, ItemStack original) { + AtomicReference eaten = new AtomicReference<>(original); + AtomicReference moddedResultReference = new AtomicReference<>(); + ActionResult eventResult = ItemEvents.EAT_FOOD.invoker().onPlayerEat(self, world, original, eaten, moddedResultReference); + + ItemStack defaultResult = eventResult == ActionResult.FAIL ? original : self.eatFood(world, eaten.get()); + ItemStack moddedResult = moddedResultReference.get(); + return moddedResult == null ? defaultResult : moddedResult; + } +} diff --git a/src/main/java/io/github/fabriccommunity/events/mixin/item/MixinItem.java b/src/main/java/io/github/fabriccommunity/events/mixin/item/MixinItem.java new file mode 100644 index 0000000..238126e --- /dev/null +++ b/src/main/java/io/github/fabriccommunity/events/mixin/item/MixinItem.java @@ -0,0 +1,22 @@ +package io.github.fabriccommunity.events.mixin.item; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import io.github.fabriccommunity.events.impl.InteractionsImpl; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +@Mixin(Item.class) +public class MixinItem { + @Redirect( + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;eatFood(Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/ItemStack;"), + method = "finishUsing" + ) + private ItemStack onEatFood(LivingEntity self, World world, ItemStack original) { + return InteractionsImpl.eatFood(self, world, original); + } +} diff --git a/src/main/java/io/github/fabriccommunity/events/play/ClientPlayerInteractionEvents.java b/src/main/java/io/github/fabriccommunity/events/play/ClientPlayerInteractionEvents.java index 57467ea..c9bba7b 100644 --- a/src/main/java/io/github/fabriccommunity/events/play/ClientPlayerInteractionEvents.java +++ b/src/main/java/io/github/fabriccommunity/events/play/ClientPlayerInteractionEvents.java @@ -50,9 +50,11 @@ public interface AttackKeyPress { * @param hitResult the result of the hit (miss/block/entity), might be null. * * @return - * {@code SUCCESS} or {@code CONSUME} cancels further event processing and swings the player's arms. - * {@code PASS} pass event handling on to further processing. If all listeners pass, vanilla behavior is executed instead. - * {@code FAIL} cancels further event processing without swinging the player's arms. + * */ ActionResult onAttackKeyPress(ClientPlayerEntity player, /* @Nullable */ HitResult hitResult); } @@ -70,9 +72,11 @@ public interface AttackKeyHold { * @param hitResult the result of the hit (miss/block/entity), might be null. * * @return - * {@code SUCCESS} or {@code CONSUME} cancels further event processing and swings the player's arms. - * {@code PASS} pass event handling on to further processing. If all listeners pass, vanilla behavior is executed instead. - * {@code FAIL} cancels further event processing without swinging the player's arms. + * */ ActionResult onAttackKeyHold(ClientPlayerEntity player, /* @Nullable */ HitResult hitResult); } diff --git a/src/main/java/io/github/fabriccommunity/events/play/ItemEvents.java b/src/main/java/io/github/fabriccommunity/events/play/ItemEvents.java new file mode 100644 index 0000000..5996a7c --- /dev/null +++ b/src/main/java/io/github/fabriccommunity/events/play/ItemEvents.java @@ -0,0 +1,53 @@ +package io.github.fabriccommunity.events.play; + +import java.util.concurrent.atomic.AtomicReference; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.world.World; + +/** + * Collection of events pertaining to items and item stacks in gameplay. + */ +public final class ItemEvents { + /** + * An event which runs when an entity tries to eat food. + */ + public static final Event EAT_FOOD = EventFactory.createArrayBacked(EatFood.class, listeners -> (entity, world, original, eaten, result) -> { + for (EatFood listener : listeners) { + ActionResult eventResult = listener.onPlayerEat(entity, world, original, eaten, result); + + if (eventResult != ActionResult.PASS) { + return eventResult; + } + } + + return ActionResult.PASS; + }); + + /** + * Called when an entity tries to eat an item. + * @author Valoeghese + */ + @FunctionalInterface + public interface EatFood { + /** + * @param entity the entity eating the food. + * @param world the world the entity is eating the food in. + * @param original the original stack of food to be eaten. + * @param eaten the stack of food which is to be eaten. If the value stored herein differs from {@code original}, the value has been modded.
+ * @param result {@code null} by default. if {@code null}, the stack returned from eating will be the result of the eat method. + * If this is altered the resulting stack will be the item stored in here, regardless of whether the event fails.
+ * @return + *
    + *
  • {@code SUCCESS} or {@code CONSUME} to cancel further event processing and eat the food in {@code stack}
    + *
  • {@code PASS} pass event handling on to further processing. If all listeners pass, it is treated as a {@code SUCCESS}. + *
  • {@code FAIL} to cancel further event processing and do not eat the food. + *
+ */ + ActionResult onPlayerEat(LivingEntity entity, World world, final ItemStack original, AtomicReference eaten, AtomicReference result); + } +} diff --git a/src/main/java/io/github/fabriccommunity/events/play/PlayerInteractionEvents.java b/src/main/java/io/github/fabriccommunity/events/play/PlayerInteractionEvents.java index 586390b..e13f624 100644 --- a/src/main/java/io/github/fabriccommunity/events/play/PlayerInteractionEvents.java +++ b/src/main/java/io/github/fabriccommunity/events/play/PlayerInteractionEvents.java @@ -11,6 +11,9 @@ * A collection of events for player interaction. */ public final class PlayerInteractionEvents { + /** + * An event which runs when the player picks up experience. + */ public static final Event XP_GAIN = EventFactory.createArrayBacked(XPGain.class, listeners -> (player, original, experience) -> { for (XPGain listener : listeners) { ActionResult result = listener.onXPGained(player, original, experience); @@ -34,9 +37,11 @@ public interface XPGain { * @param original the original experience to be gained. * @param experience the experience which will be gained. If the value stored in this is different to {@code original}, a mod has modified the experience gained. * @return - * {@code SUCCESS} or {@code CONSUME} to cancel further event processing and add the experience in {@code experience}. - * {@code PASS} pass event handling on to further processing. If all listeners pass, it is treated as a {@code SUCCESS}. - * {@code FAIL} to cancel further event processing and add the original experience. + *
    + *
  • {@code SUCCESS} or {@code CONSUME} to cancel further event processing and add the experience in {@code experience}.
    + *
  • {@code PASS} pass event handling on to further processing. If all listeners pass, it is treated as a {@code SUCCESS}. + *
  • {@code FAIL} to cancel further event processing and add the original experience. + *
*/ ActionResult onXPGained(PlayerEntity player, final int original, AtomicInteger experience); } diff --git a/src/main/resources/toomanyevents.mixins.json b/src/main/resources/toomanyevents.mixins.json index e89bf66..7f61501 100644 --- a/src/main/resources/toomanyevents.mixins.json +++ b/src/main/resources/toomanyevents.mixins.json @@ -7,6 +7,7 @@ "biomegen.MixinMultiNoiseBiomeSource", "biomegen.MixinTheEndBiomeSource", "biomegen.MixinVanillaLayeredBiomeSource", + "item.MixinItem", "player.MixinPlayerEntity", "player.MixinPlayerManager", "spawn.MixinEntityType", diff --git a/src/test/java/io/github/fabriccommunity/events/test/EatFoodTest.java b/src/test/java/io/github/fabriccommunity/events/test/EatFoodTest.java new file mode 100644 index 0000000..291d7b3 --- /dev/null +++ b/src/test/java/io/github/fabriccommunity/events/test/EatFoodTest.java @@ -0,0 +1,31 @@ +package io.github.fabriccommunity.events.test; + +import io.github.fabriccommunity.events.play.ItemEvents; +import net.fabricmc.api.ModInitializer; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.ActionResult; + +public class EatFoodTest implements ModInitializer { + public static final boolean ENABLED = true; + + @Override + public void onInitialize() { + if (ENABLED) { + // This code makes potatoes behave as if they were golden apples. + ItemEvents.EAT_FOOD.register((entity, world, original, eaten, result) -> { + if (original.getItem() == Items.POTATO) { + eaten.set(new ItemStack(Items.GOLDEN_APPLE)); // use golden apple eat effects + // Make it return what it would be if the original had been eaten. + // Otherwise it will return nothing due to eating a stack of 1 golden apples giving back 0 golden apples, which is nothing. + ItemStack resultstk = original.copy(); + resultstk.decrement(1); + result.set(resultstk); // + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + }); + } + } +} diff --git a/src/test/resources/fabric.mod.json b/src/test/resources/fabric.mod.json index bb7a4c0..096aaf6 100644 --- a/src/test/resources/fabric.mod.json +++ b/src/test/resources/fabric.mod.json @@ -16,6 +16,7 @@ "entrypoints": { "main": [ "io.github.fabriccommunity.events.test.BiomePlacementTest", + "io.github.fabriccommunity.events.test.EatFoodTest", "io.github.fabriccommunity.events.test.EntitySpawnTest", "io.github.fabriccommunity.events.test.PlayerInteractionTest" ],