From b5b9f82997a81f1c59ae36f09b35e69445be08f6 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Sun, 10 Feb 2019 02:44:07 +0100 Subject: [PATCH] [0.2.0] add pick item callback (#82) --- .../api/block/ContextSensitivePickable.java | 28 +++++++++ .../api/entity/ContextSensitivePickable.java | 25 ++++++++ .../client/player/ClientPickItemCallback.java | 63 +++++++++++++++++++ .../impl/FabricAPIClientInitializer.java | 28 +++++++++ .../MixinMinecraftClient.java | 55 ++++++++++++++++ .../net.fabricmc.fabric.mixins.client.json | 1 + 6 files changed, 200 insertions(+) create mode 100644 src/main/java/net/fabricmc/fabric/api/block/ContextSensitivePickable.java create mode 100644 src/main/java/net/fabricmc/fabric/api/entity/ContextSensitivePickable.java create mode 100644 src/main/java/net/fabricmc/fabric/api/event/client/player/ClientPickItemCallback.java create mode 100644 src/main/java/net/fabricmc/fabric/mixin/events/playerinteraction/MixinMinecraftClient.java diff --git a/src/main/java/net/fabricmc/fabric/api/block/ContextSensitivePickable.java b/src/main/java/net/fabricmc/fabric/api/block/ContextSensitivePickable.java new file mode 100644 index 000000000..5bf3fb994 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/api/block/ContextSensitivePickable.java @@ -0,0 +1,28 @@ +/* + * 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.api.block; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; + +public interface ContextSensitivePickable { + ItemStack getPickStack(BlockState state, BlockView view, BlockPos pos, /* nullable */ PlayerEntity player, /* nullable */ HitResult result); +} diff --git a/src/main/java/net/fabricmc/fabric/api/entity/ContextSensitivePickable.java b/src/main/java/net/fabricmc/fabric/api/entity/ContextSensitivePickable.java new file mode 100644 index 000000000..709f602c8 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/api/entity/ContextSensitivePickable.java @@ -0,0 +1,25 @@ +/* + * 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.api.entity; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.hit.HitResult; + +public interface ContextSensitivePickable { + ItemStack getPickStack(/* nullable */ PlayerEntity player, /* nullable */ HitResult result); +} diff --git a/src/main/java/net/fabricmc/fabric/api/event/client/player/ClientPickItemCallback.java b/src/main/java/net/fabricmc/fabric/api/event/client/player/ClientPickItemCallback.java new file mode 100644 index 000000000..88df9d678 --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/api/event/client/player/ClientPickItemCallback.java @@ -0,0 +1,63 @@ +/* + * 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.api.event.client.player; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.hit.HitResult; + +/** + * This interaction event is called on the CLIENT SIDE ONLY when the player + * attempts to pick up an item. + * + * Use {@link Container} to change the picked stack. Return true if you + * wish for execution to continue, return false to cancel the item picking + * operation (for example, if you want to route to the server side, etc.) + */ +public interface ClientPickItemCallback { + public static final class Container { + private ItemStack stack; + + public Container(ItemStack stack) { + this.stack = stack; + } + + public ItemStack getStack() { + return stack; + } + + public void setStack(ItemStack stack) { + this.stack = stack; + } + } + + public static final Event EVENT = EventFactory.arrayBacked(ClientPickItemCallback.class, + (listeners) -> (player, result, container) -> { + for (ClientPickItemCallback event : listeners) { + if (!event.pick(player, result, container)) { + return false; + } + } + + return true; + } + ); + + boolean pick(PlayerEntity player, HitResult result, Container container); +} diff --git a/src/main/java/net/fabricmc/fabric/impl/FabricAPIClientInitializer.java b/src/main/java/net/fabricmc/fabric/impl/FabricAPIClientInitializer.java index 1bf2b03c2..2c5b1fc86 100644 --- a/src/main/java/net/fabricmc/fabric/impl/FabricAPIClientInitializer.java +++ b/src/main/java/net/fabricmc/fabric/impl/FabricAPIClientInitializer.java @@ -17,10 +17,18 @@ package net.fabricmc.fabric.impl; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.block.ContextSensitivePickable; +import net.fabricmc.fabric.api.event.client.player.ClientPickItemCallback; import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; import net.fabricmc.fabric.impl.client.gui.ScreenProviderRegistryImpl; import net.fabricmc.fabric.impl.registry.RegistrySyncManager; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; public class FabricAPIClientInitializer implements ClientModInitializer { @Override @@ -30,6 +38,26 @@ public class FabricAPIClientInitializer implements ClientModInitializer { RegistrySyncManager.receivePacket(ctx, buf, !MinecraftClient.getInstance().isInSingleplayer()); }); + ClientPickItemCallback.EVENT.register(((player, result, container) -> { + if (result instanceof BlockHitResult) { + BlockView view = player.getEntityWorld(); + BlockPos pos = ((BlockHitResult) result).getBlockPos(); + BlockState state = view.getBlockState(pos); + + if (state.getBlock() instanceof ContextSensitivePickable) { + container.setStack(((ContextSensitivePickable) state.getBlock()).getPickStack(state, view, pos, player, result)); + } + } else if (result instanceof EntityHitResult) { + Entity entity = ((EntityHitResult) result).getEntity(); + + if (entity instanceof net.fabricmc.fabric.api.entity.ContextSensitivePickable) { + container.setStack(((net.fabricmc.fabric.api.entity.ContextSensitivePickable) entity).getPickStack(player, result)); + } + } + + return true; + })); + ((ScreenProviderRegistryImpl) ScreenProviderRegistryImpl.INSTANCE).init(); } } diff --git a/src/main/java/net/fabricmc/fabric/mixin/events/playerinteraction/MixinMinecraftClient.java b/src/main/java/net/fabricmc/fabric/mixin/events/playerinteraction/MixinMinecraftClient.java new file mode 100644 index 000000000..99b06254c --- /dev/null +++ b/src/main/java/net/fabricmc/fabric/mixin/events/playerinteraction/MixinMinecraftClient.java @@ -0,0 +1,55 @@ +/* + * 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.playerinteraction; + +import net.fabricmc.fabric.api.event.client.player.ClientPickItemCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftClient.class) +public class MixinMinecraftClient { + private boolean fabric_itemPickCancelled; + + @ModifyVariable(at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isEmpty()Z", ordinal = 2), method = "doItemPick", ordinal = 0) + public ItemStack modifyItemPick(ItemStack stack) { + ClientPickItemCallback.Container ctr = new ClientPickItemCallback.Container(stack); + //noinspection ConstantConditions + MinecraftClient client = (MinecraftClient) (Object) this; + + boolean toContinue = ClientPickItemCallback.EVENT.invoker().pick(client.player, client.hitResult, ctr); + if (!toContinue) { + fabric_itemPickCancelled = true; + return ItemStack.EMPTY; + } else { + fabric_itemPickCancelled = false; + return ctr.getStack(); + } + } + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isEmpty()Z", ordinal = 2), method = "doItemPick", cancellable = true) + public void cancelItemPick(CallbackInfo info) { + if (fabric_itemPickCancelled) { + fabric_itemPickCancelled = false; + info.cancel(); + } + } +} diff --git a/src/main/resources/net.fabricmc.fabric.mixins.client.json b/src/main/resources/net.fabricmc.fabric.mixins.client.json index a56d34921..472eb022e 100644 --- a/src/main/resources/net.fabricmc.fabric.mixins.client.json +++ b/src/main/resources/net.fabricmc.fabric.mixins.client.json @@ -16,6 +16,7 @@ "client.render.MixinItemColorMap", "client.texture.MixinSpriteAtlasTexture", "events.playerinteraction.MixinClientPlayerInteractionManager", + "events.playerinteraction.MixinMinecraftClient", "events.tick.MixinMinecraftClient", "networking.MixinClientPlayNetworkHandler", "registry.client.MixinBlockColorMap",