From 1e97382be2e7ac70d2fd9aa933aa508fa822e39c Mon Sep 17 00:00:00 2001 From: Walker Knapp <30479917+WalkerKnapp@users.noreply.github.com> Date: Tue, 22 Dec 2020 19:19:46 -0500 Subject: [PATCH] Implement PlayerInteractEvent Subclasses (#213) * Implement EntityInteractSpecific * Implement RightClickEmpty * Implement LeftClickBlock * Implement LeftClickEmpty * Unstub interaction hooks in ForgeHooks * Clean up PlayerInteractEvent javadocs --- .../entity/player/PlayerInteractEvent.java | 42 ++++++---- .../impl/event/entity/EntityEvents.java | 83 +++++++++++++++++-- .../event/entity/MixinMinecraftClient.java | 59 +++++++++++++ .../patchwork-events-entity.mixins.json | 3 +- .../net/minecraftforge/common/ForgeHooks.java | 32 +++---- 5 files changed, 174 insertions(+), 45 deletions(-) create mode 100644 patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinMinecraftClient.java diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java index 91942c0b..f47ea77e 100644 --- a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerInteractEvent.java @@ -33,6 +33,7 @@ import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; /** @@ -133,7 +134,7 @@ public void setCancellationResult(ActionResult result) { * *

If we are on the client and result is not {@link ActionResult#SUCCESS}, the client will then try {@link EntityInteract}.

*/ - /* TODO public static class EntityInteractSpecific extends PlayerInteractEvent { + public static class EntityInteractSpecific extends PlayerInteractEvent { private final Vec3d localPos; private final Entity target; @@ -149,7 +150,7 @@ public EntityInteractSpecific(PlayerEntity player, Hand hand, Entity target, Vec * [-width / 2, width / 2] while Y values will be in the range [0, height] * * @return The local position - TODO + */ public Vec3d getLocalPos() { return localPos; } @@ -157,7 +158,12 @@ public Vec3d getLocalPos() { public Entity getTarget() { return target; } - }*/ + + @Override + public boolean isCancelable() { + return true; + } + } /** * This event is fired on both sides when the player right clicks an entity. @@ -194,7 +200,7 @@ public boolean isCancelable() { * This event is fired on both sides whenever the player right clicks while targeting a block. * *

This event controls which of {@link net.minecraft.block.Block#activate} and/or {@link net.minecraft.item.Item#use} - * will be called after {@link net.minecraft.item.Item#onItemUseFirst} is called.

+ * will be called after {@link net.minecraftforge.common.extensions.IForgeItem#onItemUseFirst} is called.

* *

This event is cancellable. * Cancelling the event will cause none of the above noted methods to be called.

@@ -222,7 +228,7 @@ public Result getUseBlock() { /** * DENY: Block will never be used. - * DEFAULT: Default behaviour (sneak will not use block, unless all items return true in {@link net.minecraft.item.Item#doesSneakBypassUse}). + * DEFAULT: Default behaviour (sneak will not use block, unless all items return true in {@link net.minecraftforge.common.extensions.IForgeItem#doesSneakBypassUse}). * ALLOW: Block will always be used, regardless of sneaking and doesSneakBypassUse. */ public void setUseBlock(Result triggerBlock) { @@ -230,7 +236,7 @@ public void setUseBlock(Result triggerBlock) { } /** - * @return If {@link net.minecraft.item.Item#onItemUseFirst} and {@link net.minecraft.item.Item#use} should be called + * @return If {@link net.minecraftforge.common.extensions.IForgeItem#onItemUseFirst} and {@link net.minecraft.item.Item#use} should be called */ public Result getUseItem() { return useItem; @@ -285,16 +291,16 @@ public boolean isCancelable() { * *

This event is not cancellable.

*/ - /* TODO public static class RightClickEmpty extends PlayerInteractEvent { + public static class RightClickEmpty extends PlayerInteractEvent { public RightClickEmpty(PlayerEntity player, Hand hand) { super(player, hand, new BlockPos(player), null); } - }*/ + } /** * This event is fired when a player left clicks while targeting a block. * - *

This event controls which of {@link net.minecraft.block.Block#onBlockBreakStart(BlockState, World, BlockPos, PlayerEntity)} and/or the item harvesting methods will be called.

+ *

This event controls which of {@link net.minecraft.block.Block#onBlockBreakStart(net.minecraft.block.BlockState, World, BlockPos, PlayerEntity)} and/or the item harvesting methods will be called.

*

This event is cancellable. * Cancelling the event will cause none of the above noted methods to be called.

@@ -307,7 +313,7 @@ public RightClickEmpty(PlayerEntity player, Hand hand) { *

Also note that creative mode directly breaks the block without running any other logic. * Therefore, in creative mode, {@link #setUseBlock} and {@link #setUseItem} have no effect.

*/ - /* TODO public static class LeftClickBlock extends PlayerInteractEvent { + public static class LeftClickBlock extends PlayerInteractEvent { private Result useBlock = Result.DEFAULT; private Result useItem = Result.DEFAULT; @@ -316,8 +322,8 @@ public LeftClickBlock(PlayerEntity player, BlockPos pos, Direction face) { } /** - * @return If {@link net.minecraft.block.Block#onBlockClicked} should be called. Changing this has no effect in creative mode - TODO + * @return If {@link net.minecraft.block.Block#onBlockBreakStart(net.minecraft.block.BlockState, World, BlockPos, PlayerEntity)} should be called. Changing this has no effect in creative mode + */ public Result getUseBlock() { return useBlock; } @@ -328,7 +334,7 @@ public void setUseBlock(Result triggerBlock) { /** * @return If the block should be attempted to be mined with the current item. Changing this has no effect in creative mode - TODO + */ public Result getUseItem() { return useItem; } @@ -347,11 +353,11 @@ public void setCanceled(boolean canceled) { super.setCanceled(canceled); if (canceled) { - useBlock = DENY; - useItem = DENY; + useBlock = Result.DENY; + useItem = Result.DENY; } } - }*/ + } /** * This event is fired on the client side when the player left clicks empty space with any ItemStack. @@ -359,9 +365,9 @@ public void setCanceled(boolean canceled) { * *

This event is not cancellable.

*/ - /* TODO public static class LeftClickEmpty extends PlayerInteractEvent { + public static class LeftClickEmpty extends PlayerInteractEvent { public LeftClickEmpty(PlayerEntity player) { super(player, Hand.MAIN_HAND, new BlockPos(player), null); } - }*/ + } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index 2ed5f7a8..95b340c1 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -69,13 +69,18 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.IWorld; import net.minecraft.world.MobSpawnerLogic; import net.minecraft.world.World; import net.minecraft.world.dimension.DimensionType; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.player.UseEntityCallback; import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.patchworkmc.mixin.event.entity.StorageMinecartEntityAccessor; @@ -83,6 +88,20 @@ public class EntityEvents implements ModInitializer { private static final Logger LOGGER = LogManager.getLogger("patchwork-events-entity"); + public static ActionResult onInteractEntityAt(PlayerEntity player, Entity entity, HitResult ray, Hand hand) { + Vec3d vec3d = new Vec3d(ray.getPos().x - entity.x, ray.getPos().y - entity.y, ray.getPos().z - entity.z); + + return onInteractEntityAt(player, entity, vec3d, hand); + } + + public static ActionResult onInteractEntityAt(PlayerEntity player, Entity target, Vec3d localPos, Hand hand) { + PlayerInteractEvent event = new PlayerInteractEvent.EntityInteractSpecific(player, hand, target, localPos); + + MinecraftForge.EVENT_BUS.post(event); + + return event.isCanceled() ? event.getCancellationResult() : null; + } + public static ActionResult onInteractEntity(PlayerEntity player, Entity entity, Hand hand) { PlayerInteractEvent.EntityInteract event = new PlayerInteractEvent.EntityInteract(player, hand, entity); @@ -91,6 +110,38 @@ public static ActionResult onInteractEntity(PlayerEntity player, Entity entity, return event.isCanceled() ? event.getCancellationResult() : null; } + public static PlayerInteractEvent.RightClickItem onItemRightClick(PlayerEntity player, Hand hand) { + PlayerInteractEvent.RightClickItem event = new PlayerInteractEvent.RightClickItem(player, hand); + + MinecraftForge.EVENT_BUS.post(event); + + return event; + } + + public static PlayerInteractEvent.RightClickBlock onBlockRightClick(PlayerEntity player, Hand hand, BlockPos pos, Direction face) { + PlayerInteractEvent.RightClickBlock event = new PlayerInteractEvent.RightClickBlock(player, hand, pos, face); + + MinecraftForge.EVENT_BUS.post(event); + + return event; + } + + public static void onEmptyRightClick(PlayerEntity player, Hand hand) { + MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.RightClickEmpty(player, hand)); + } + + public static PlayerInteractEvent.LeftClickBlock onBlockLeftClick(PlayerEntity player, BlockPos pos, Direction face) { + PlayerInteractEvent.LeftClickBlock event = new PlayerInteractEvent.LeftClickBlock(player, pos, face); + + MinecraftForge.EVENT_BUS.post(event); + + return event; + } + + public static void onEmptyLeftClick(PlayerEntity player) { + MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.LeftClickEmpty(player)); + } + public static boolean onLivingDeath(LivingEntity entity, DamageSource src) { return MinecraftForge.EVENT_BUS.post(new LivingDeathEvent(entity, src)); } @@ -262,9 +313,7 @@ public void onInitialize() { return ActionResult.PASS; } - PlayerInteractEvent.RightClickItem event = new PlayerInteractEvent.RightClickItem(player, hand); - - MinecraftForge.EVENT_BUS.post(event); + PlayerInteractEvent.RightClickItem event = EntityEvents.onItemRightClick(player, hand); if (event.isCanceled() && event.getCancellationResult() == ActionResult.PASS) { // TODO: Fabric API doesn't have a way to express "cancelled, but return PASS" @@ -282,9 +331,7 @@ public void onInitialize() { return ActionResult.PASS; } - PlayerInteractEvent.RightClickBlock event = new PlayerInteractEvent.RightClickBlock(player, hand, hitResult.getBlockPos(), hitResult.getSide()); - - MinecraftForge.EVENT_BUS.post(event); + PlayerInteractEvent.RightClickBlock event = EntityEvents.onBlockRightClick(player, hand, hitResult.getBlockPos(), hitResult.getSide()); if (event.isCanceled()) { if (event.getCancellationResult() == ActionResult.PASS) { @@ -308,6 +355,28 @@ public void onInitialize() { return ActionResult.PASS; }); - // TODO: Note: UseEntityCallback is closer to EntityInteractSpecific. We're on our own for EntityInteract. + UseEntityCallback.EVENT.register(((playerEntity, world, hand, entity, entityHitResult) -> { + if (playerEntity.isSpectator()) { + return ActionResult.PASS; + } + + ActionResult result = EntityEvents.onInteractEntityAt(playerEntity, entity, entityHitResult, hand); + + if (result == null) { + return ActionResult.PASS; + } + + return result; + })); + + AttackBlockCallback.EVENT.register((playerEntity, world, hand, blockPos, direction) -> { + PlayerInteractEvent.LeftClickBlock event = EntityEvents.onBlockLeftClick(playerEntity, blockPos, direction); + + if (event.isCanceled() || (!playerEntity.isCreative() && event.getUseItem() == net.minecraftforge.eventbus.api.Event.Result.DENY)) { + return ActionResult.SUCCESS; + } else { + return ActionResult.PASS; + } + }); } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinMinecraftClient.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinMinecraftClient.java new file mode 100644 index 00000000..110a8a3e --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinMinecraftClient.java @@ -0,0 +1,59 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(MinecraftClient.class) +public class MixinMinecraftClient { + @Shadow + public HitResult crosshairTarget; + + @Shadow + public ClientPlayerEntity player; + + @Inject(method = "doItemUse", + at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isEmpty()Z", ordinal = 1), + locals = LocalCapture.CAPTURE_FAILHARD) + public void onItemUse(CallbackInfo ci, Hand[] hands, int handCount, int handIndex, Hand hand, ItemStack itemStack) { + if (itemStack.isEmpty() && (this.crosshairTarget == null || this.crosshairTarget.getType() == HitResult.Type.MISS)) { + EntityEvents.onEmptyRightClick(this.player, hand); + } + } + + @Inject(method = "doAttack", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;resetLastAttackedTicks()V", shift = At.Shift.AFTER)) + public void onAttackMiss(CallbackInfo ci) { + EntityEvents.onEmptyLeftClick(this.player); + } +} diff --git a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json index 895bcd44..387b262f 100644 --- a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json +++ b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json @@ -33,7 +33,8 @@ "MixinClientWorld", "MixinItemStack", "MixinOtherClientPlayerEntity", - "MixinPlayerEntityRenderer" + "MixinPlayerEntityRenderer", + "MixinMinecraftClient" ], "injectors": { "defaultRequire": 1 diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java index 35b29d3f..93bb2de5 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -29,6 +29,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.Event; import org.apache.commons.lang3.NotImplementedException; @@ -62,6 +63,7 @@ import net.minecraft.util.Hand; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.world.GameMode; import net.minecraft.world.IWorld; import net.minecraft.world.MobSpawnerLogic; @@ -270,46 +272,38 @@ public static boolean onTravelToDimension(Entity entity, DimensionType dimension return EntityEvents.onTravelToDimension(entity, dimensionType); } - // TODO: I left this here for now because it's a thin wrapper around the onInteractEntityAt below public static ActionResult onInteractEntityAt(PlayerEntity player, Entity entity, HitResult ray, Hand hand) { - Vec3d vec3d = new Vec3d(ray.getPos().x - entity.x, ray.getPos().y - entity.y, ray.getPos().z - entity.z); - return onInteractEntityAt(player, entity, vec3d, hand); + return EntityEvents.onInteractEntityAt(player, entity, ray, hand); } - @Stubbed public static ActionResult onInteractEntityAt(PlayerEntity player, Entity entity, Vec3d vec3d, Hand hand) { - throw new NotImplementedException("ForgeHooks stub"); + return EntityEvents.onInteractEntityAt(player, entity, vec3d, hand); } public static ActionResult onInteractEntity(PlayerEntity player, Entity entity, Hand hand) { return EntityEvents.onInteractEntity(player, entity, hand); } - @Stubbed public static ActionResult onItemRightClick(PlayerEntity player, Hand hand) { - throw new NotImplementedException("ForgeHooks stub"); + PlayerInteractEvent.RightClickItem event = EntityEvents.onItemRightClick(player, hand); + + return event.isCanceled() ? event.getCancellationResult() : null; } - /* - @Stubbed public static PlayerInteractEvent.LeftClickBlock onLeftClickBlock(PlayerEntity player, BlockPos pos, Direction face) { - throw new NotImplementedException("ForgeHooks stub"); - } */ + return EntityEvents.onBlockLeftClick(player, pos, face); + } - /* - @Stubbed public static PlayerInteractEvent.RightClickBlock onRightClickBlock(PlayerEntity player, Hand hand, BlockPos pos, Direction face) { - throw new NotImplementedException("ForgeHooks stub"); - } */ + return EntityEvents.onBlockRightClick(player, hand, pos, face); + } - @Stubbed public static void onEmptyClick(PlayerEntity player, Hand hand) { - throw new NotImplementedException("ForgeHooks stub"); + EntityEvents.onEmptyRightClick(player, hand); } - @Stubbed public static void onEmptyLeftClick(PlayerEntity player) { - throw new NotImplementedException("ForgeHooks stub"); + EntityEvents.onEmptyLeftClick(player); } //private static LootTableContext getLootTableContext() {