add entity interaction events

This commit is contained in:
Adrian Siekierka 2018-12-03 10:17:07 +01:00
parent 4d3bd0d35f
commit b291a955de
6 changed files with 162 additions and 9 deletions

View file

@ -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() {
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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) {

View file

@ -5,6 +5,8 @@
"mixins": [
"commands.MixinServerCommandManager",
"events.MixinMinecraftServer",
"events.MixinServerPlayNetworkHandler",
"events.MixinServerPlayerEntity",
"events.MixinServerPlayerInteractionManager",
"events.MixinWorld",
"helpers.MixinBlock",