Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Commit

Permalink
Implement PlayerInteractEvent Subclasses (#213)
Browse files Browse the repository at this point in the history
* Implement EntityInteractSpecific

* Implement RightClickEmpty

* Implement LeftClickBlock

* Implement LeftClickEmpty

* Unstub interaction hooks in ForgeHooks

* Clean up PlayerInteractEvent javadocs
  • Loading branch information
WalkerKnapp authored Dec 23, 2020
1 parent 6638a9b commit 1e97382
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -133,7 +134,7 @@ public void setCancellationResult(ActionResult result) {
*
* <p>If we are on the client and result is not {@link ActionResult#SUCCESS}, the client will then try {@link EntityInteract}.</p>
*/
/* TODO public static class EntityInteractSpecific extends PlayerInteractEvent {
public static class EntityInteractSpecific extends PlayerInteractEvent {
private final Vec3d localPos;
private final Entity target;

Expand All @@ -149,15 +150,20 @@ 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;
}

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.
Expand Down Expand Up @@ -194,7 +200,7 @@ public boolean isCancelable() {
* This event is fired on both sides whenever the player right clicks while targeting a block.
*
* <p>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.</p>
* will be called after {@link net.minecraftforge.common.extensions.IForgeItem#onItemUseFirst} is called.</p>
*
* <p>This event is cancellable.
* Cancelling the event will cause none of the above noted methods to be called.</p>
Expand Down Expand Up @@ -222,15 +228,15 @@ 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) {
this.useBlock = 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;
Expand Down Expand Up @@ -285,16 +291,16 @@ public boolean isCancelable() {
*
* <p>This event is not cancellable.</p>
*/
/* 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.
*
* <p>This event controls which of {@link net.minecraft.block.Block#onBlockBreakStart(BlockState, World, BlockPos, PlayerEntity)} and/or the item harvesting methods will be called.</p>
* <p>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.</p>
* <p>This event is cancellable.
* Cancelling the event will cause none of the above noted methods to be called.</p>
Expand All @@ -307,7 +313,7 @@ public RightClickEmpty(PlayerEntity player, Hand hand) {
* <p>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.</p>
*/
/* TODO public static class LeftClickBlock extends PlayerInteractEvent {
public static class LeftClickBlock extends PlayerInteractEvent {
private Result useBlock = Result.DEFAULT;
private Result useItem = Result.DEFAULT;

Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -347,21 +353,21 @@ 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.
* The server is not aware of when the client left clicks empty space, you will need to tell the server yourself.
*
* <p>This event is not cancellable.</p>
*/
/* 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);
}
}*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,39 @@
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;

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);

Expand All @@ -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));
}
Expand Down Expand Up @@ -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"
Expand All @@ -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) {
Expand All @@ -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;
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"MixinClientWorld",
"MixinItemStack",
"MixinOtherClientPlayerEntity",
"MixinPlayerEntityRenderer"
"MixinPlayerEntityRenderer",
"MixinMinecraftClient"
],
"injectors": {
"defaultRequire": 1
Expand Down
Loading

0 comments on commit 1e97382

Please sign in to comment.