From b291a955de266af4998e1f3f7e54d182bcc749e6 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka <kontakt@asie.pl> Date: Mon, 3 Dec 2018 10:17:07 +0100 Subject: [PATCH] add entity interaction events --- .../fabric/events/PlayerInteractionEvent.java | 24 +++++++- .../MixinClientPlayerInteractionManager.java | 39 ++++++++++--- .../events/MixinServerPlayNetworkHandler.java | 56 +++++++++++++++++++ .../mixin/events/MixinServerPlayerEntity.java | 48 ++++++++++++++++ .../MixinServerPlayerInteractionManager.java | 2 +- .../net.fabricmc.fabric.mixins.common.json | 2 + 6 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayNetworkHandler.java create mode 100644 src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerEntity.java diff --git a/src/main/java/net/fabricmc/fabric/events/PlayerInteractionEvent.java b/src/main/java/net/fabricmc/fabric/events/PlayerInteractionEvent.java index 1af054161..ada16e512 100644 --- a/src/main/java/net/fabricmc/fabric/events/PlayerInteractionEvent.java +++ b/src/main/java/net/fabricmc/fabric/events/PlayerInteractionEvent.java @@ -23,12 +23,15 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Facing; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; /** * This is a class for INTERACTION EVENTS (think left-clicking/right-clicking). For block placement/break * events, look elsewhere - this just handles the interaction! * + * These hook in BEFORE the spectator checks, so make sure to check for the player's game mode as well! + * * CURRENT LIMITATIONS: * * - INTERACT_BLOCK/INTERACT_ITEM do not expect the ItemStack instance in the player's held hand to change! @@ -40,20 +43,39 @@ public final class PlayerInteractionEvent { ActionResult interact(PlayerEntity player, World world, Hand hand, BlockPos pos, Facing facing); } + @FunctionalInterface + public interface Entity { + ActionResult interact(PlayerEntity player, World world, Hand hand, net.minecraft.entity.Entity entity); + } + @FunctionalInterface public interface BlockPositioned { ActionResult interact(PlayerEntity player, World world, Hand hand, BlockPos pos, Facing facing, float hitX, float hitY, float hitZ); } + @FunctionalInterface + public interface EntityPositioned { + ActionResult interact(PlayerEntity player, World world, Hand hand, net.minecraft.entity.Entity entity, Vec3d hitPos); + } + @FunctionalInterface public interface Item { ActionResult interact(PlayerEntity player, World world, Hand hand); } - public static final HandlerRegistry<Block> BREAK_BLOCK = new HandlerList<>(); + public static final HandlerRegistry<Block> ATTACK_BLOCK = new HandlerList<>(); + public static final HandlerRegistry<Entity> ATTACK_ENTITY = new HandlerList<>(); + + // TODO: For completeness' sake, but requires us to add a custom packet. Is it worth the complexity? + /* public static final HandlerRegistry<Item> ATTACK_ITEM = new HandlerList<>(); */ + public static final HandlerRegistry<BlockPositioned> INTERACT_BLOCK = new HandlerList<>(); + public static final HandlerRegistry<EntityPositioned> INTERACT_ENTITY_POSITIONED = new HandlerList<>(); public static final HandlerRegistry<Item> INTERACT_ITEM = new HandlerList<>(); + @Deprecated + public static final HandlerRegistry<Block> BREAK_BLOCK = ATTACK_BLOCK; + private PlayerInteractionEvent() { } diff --git a/src/main/java/net/fabricmc/fabric/mixin/events/MixinClientPlayerInteractionManager.java b/src/main/java/net/fabricmc/fabric/mixin/events/MixinClientPlayerInteractionManager.java index 76c7602cd..a1cb04dd1 100644 --- a/src/main/java/net/fabricmc/fabric/mixin/events/MixinClientPlayerInteractionManager.java +++ b/src/main/java/net/fabricmc/fabric/mixin/events/MixinClientPlayerInteractionManager.java @@ -22,21 +22,18 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerInteractionManager; -import net.minecraft.client.network.packet.BlockUpdateClientPacket; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.ServerPlayerInteractionManager; import net.minecraft.server.network.packet.PlayerInteractBlockServerPacket; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.HitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Facing; import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.World; -import org.lwjgl.system.CallbackI; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -55,7 +52,7 @@ public class MixinClientPlayerInteractionManager { @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/world/GameMode;isCreative()Z", ordinal = 0), method = "attackBlock", cancellable = true) public void attackBlock(BlockPos pos, Facing facing, CallbackInfoReturnable<Boolean> info) { - for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.BREAK_BLOCK).getBackingArray()) { + for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.ATTACK_BLOCK).getBackingArray()) { PlayerInteractionEvent.Block event = (PlayerInteractionEvent.Block) handler; ActionResult result = event.interact(client.player, client.world, Hand.MAIN, pos, facing); if (result != ActionResult.PASS) { @@ -72,7 +69,7 @@ public class MixinClientPlayerInteractionManager { return; } - for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.BREAK_BLOCK).getBackingArray()) { + for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.ATTACK_BLOCK).getBackingArray()) { PlayerInteractionEvent.Block event = (PlayerInteractionEvent.Block) handler; ActionResult result = event.interact(client.player, client.world, Hand.MAIN, pos, facing); if (result != ActionResult.PASS) { @@ -118,4 +115,32 @@ public class MixinClientPlayerInteractionManager { } } } + + @Inject(at = @At("HEAD"), method = "attackEntity", cancellable = true) + public void attackEntity(PlayerEntity player, Entity entity, CallbackInfo info) { + for (Object handler : ((HandlerList<PlayerInteractionEvent.Entity>) PlayerInteractionEvent.ATTACK_ENTITY).getBackingArray()) { + PlayerInteractionEvent.Entity event = (PlayerInteractionEvent.Entity) handler; + ActionResult result = event.interact(player, player.getEntityWorld(), Hand.MAIN /* TODO */, entity); + if (result != ActionResult.PASS) { + info.cancel(); + return; + } + } + } + + @Inject(at = @At("HEAD"), method = "interactEntityAtLocation", cancellable = true) + public void interactEntityAtLocation(PlayerEntity player, Entity entity, HitResult hitResult, Hand hand, CallbackInfoReturnable<ActionResult> info) { + // TODO: Remove double Vec3d creation? + Vec3d hitVec = new Vec3d(hitResult.pos.x - entity.x, hitResult.pos.y - entity.y, hitResult.pos.z - entity.z); + + for (Object handler : ((HandlerList<PlayerInteractionEvent.EntityPositioned>) PlayerInteractionEvent.INTERACT_ENTITY_POSITIONED).getBackingArray()) { + PlayerInteractionEvent.EntityPositioned event = (PlayerInteractionEvent.EntityPositioned) handler; + ActionResult result = event.interact(player, player.getEntityWorld(), hand, entity, hitVec); + if (result != ActionResult.PASS) { + info.setReturnValue(result); + info.cancel(); + return; + } + } + } } diff --git a/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayNetworkHandler.java b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayNetworkHandler.java new file mode 100644 index 000000000..df76fb8c1 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayNetworkHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.events; + +import net.fabricmc.fabric.events.PlayerInteractionEvent; +import net.fabricmc.fabric.util.HandlerList; +import net.minecraft.client.network.packet.BlockUpdateClientPacket; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.server.network.packet.PlayerInteractEntityServerPacket; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Facing; +import net.minecraft.world.World; +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.CallbackInfoReturnable; + +@Mixin(ServerPlayNetworkHandler.class) +public class MixinServerPlayNetworkHandler { + @Shadow + public ServerPlayerEntity player; + + @Inject(method = "onPlayerInteractEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;interactAt(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/util/Hand;)Lnet/minecraft/util/ActionResult;"), cancellable = true) + public void onPlayerInteractEntity(PlayerInteractEntityServerPacket packet, CallbackInfo info) { + for (Object handler : ((HandlerList<PlayerInteractionEvent.EntityPositioned>) PlayerInteractionEvent.INTERACT_ENTITY_POSITIONED).getBackingArray()) { + PlayerInteractionEvent.EntityPositioned event = (PlayerInteractionEvent.EntityPositioned) handler; + ActionResult result = event.interact(player, player.getEntityWorld(), packet.getHand(), packet.getEntity(player.world), packet.getHitPosition()); + if (result != ActionResult.PASS) { + info.cancel(); + return; + } + } + } +} diff --git a/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerEntity.java b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerEntity.java new file mode 100644 index 000000000..427acbbe4 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerEntity.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.events; + +import net.fabricmc.fabric.events.PlayerInteractionEvent; +import net.fabricmc.fabric.util.HandlerList; +import net.minecraft.entity.Entity; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.packet.PlayerInteractEntityServerPacket; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +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; + +@Mixin(ServerPlayerEntity.class) +public class MixinServerPlayerEntity { + @Inject(method = "attack", at = @At("HEAD"), cancellable = true) + public void onPlayerInteractEntity(Entity target, CallbackInfo info) { + ServerPlayerEntity player = (ServerPlayerEntity) (Object) this; + + for (Object handler : ((HandlerList<PlayerInteractionEvent.Entity>) PlayerInteractionEvent.ATTACK_ENTITY).getBackingArray()) { + PlayerInteractionEvent.Entity event = (PlayerInteractionEvent.Entity) handler; + ActionResult result = event.interact(player, player.getEntityWorld(), Hand.MAIN, target); + if (result != ActionResult.PASS) { + info.cancel(); + return; + } + } + } +} diff --git a/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerInteractionManager.java b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerInteractionManager.java index dd73a6e02..e87e95a9e 100644 --- a/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerInteractionManager.java +++ b/src/main/java/net/fabricmc/fabric/mixin/events/MixinServerPlayerInteractionManager.java @@ -44,7 +44,7 @@ public class MixinServerPlayerInteractionManager { @Inject(at = @At("HEAD"), method = "method_14263", cancellable = true) public void startBlockBreak(BlockPos pos, Facing facing, CallbackInfo info) { - for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.BREAK_BLOCK).getBackingArray()) { + for (Object handler : ((HandlerList<PlayerInteractionEvent.Block>) PlayerInteractionEvent.ATTACK_BLOCK).getBackingArray()) { PlayerInteractionEvent.Block event = (PlayerInteractionEvent.Block) handler; ActionResult result = event.interact(player, world, Hand.MAIN, pos, facing); if (result != ActionResult.PASS) { diff --git a/src/main/resources/net.fabricmc.fabric.mixins.common.json b/src/main/resources/net.fabricmc.fabric.mixins.common.json index 68b0e490d..78b242835 100644 --- a/src/main/resources/net.fabricmc.fabric.mixins.common.json +++ b/src/main/resources/net.fabricmc.fabric.mixins.common.json @@ -5,6 +5,8 @@ "mixins": [ "commands.MixinServerCommandManager", "events.MixinMinecraftServer", + "events.MixinServerPlayNetworkHandler", + "events.MixinServerPlayerEntity", "events.MixinServerPlayerInteractionManager", "events.MixinWorld", "helpers.MixinBlock",