diff --git a/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/api/event/client/player/ClientPlayerBlockBreakEvents.java b/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/api/event/client/player/ClientPlayerBlockBreakEvents.java new file mode 100644 index 000000000..d817088c3 --- /dev/null +++ b/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/api/event/client/player/ClientPlayerBlockBreakEvents.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 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.minecraft.block.BlockState; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +/** + * Contains client side events triggered by block breaking. + * + *

For preventing block breaking client side and other purposes, see {@link net.fabricmc.fabric.api.event.player.AttackBlockCallback}. + * For server side block break events, see {@link net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents}. + */ +public class ClientPlayerBlockBreakEvents { + /** + * Callback after a block is broken client side. + * + *

Only called client side. For server side see {@link net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents#AFTER} + */ + public static final Event AFTER = EventFactory.createArrayBacked(After.class, + (listeners) -> (world, player, pos, state) -> { + for (After event : listeners) { + event.afterBlockBreak(world, player, pos, state); + } + } + ); + + @FunctionalInterface + public interface After { + /** + * Called after a block is successfully broken. + * + * @param world the world where the block was broken + * @param player the player who broke the block + * @param pos the position where the block was broken + * @param state the block state before the block was broken + */ + void afterBlockBreak(ClientWorld world, ClientPlayerEntity player, BlockPos pos, BlockState state); + } +} diff --git a/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/mixin/event/interaction/client/ClientPlayerInteractionManagerMixin.java b/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/mixin/event/interaction/client/ClientPlayerInteractionManagerMixin.java index 272ed6c48..ab3ba8b67 100644 --- a/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/mixin/event/interaction/client/ClientPlayerInteractionManagerMixin.java +++ b/fabric-events-interaction-v0/src/client/java/net/fabricmc/fabric/mixin/event/interaction/client/ClientPlayerInteractionManagerMixin.java @@ -24,7 +24,9 @@ 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; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; @@ -46,7 +48,9 @@ import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.world.GameMode; +import net.minecraft.world.World; +import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.AttackEntityCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback; @@ -90,6 +94,11 @@ public abstract class ClientPlayerInteractionManagerMixin { } } + @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), locals = LocalCapture.CAPTURE_FAILHARD) + private void fabric$onBlockBroken(BlockPos pos, CallbackInfoReturnable cir, World world, BlockState blockState) { + ClientPlayerBlockBreakEvents.AFTER.invoker().afterBlockBreak(client.world, client.player, pos, blockState); + } + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;sendSequencedPacket(Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V"), method = "interactBlock", cancellable = true) public void interactBlock(ClientPlayerEntity player, Hand hand, BlockHitResult blockHitResult, CallbackInfoReturnable info) { // hook interactBlock between the world border check and the actual block interaction to invoke the use block event first @@ -141,5 +150,5 @@ public abstract class ClientPlayerInteractionManagerMixin { } @Shadow - public abstract void sendSequencedPacket(ClientWorld clientWorld, SequencedPacketCreator supplier); + protected abstract void sendSequencedPacket(ClientWorld clientWorld, SequencedPacketCreator supplier); } diff --git a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java index 7e28b2f1e..c4dad44be 100644 --- a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java +++ b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java @@ -27,6 +27,9 @@ import net.minecraft.world.World; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; +/** + * Contains server side events triggered by block breaking. + */ public final class PlayerBlockBreakEvents { private PlayerBlockBreakEvents() { } @@ -53,10 +56,11 @@ public final class PlayerBlockBreakEvents { ); /** - * Callback after a block is broken. + * Callback after a block is broken server side. * - *

Only called on a logical server. + *

Only called on a logical server. For client side see {@link net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents#AFTER} */ + @SuppressWarnings("JavadocReference") public static final Event AFTER = EventFactory.createArrayBacked(After.class, (listeners) -> (world, player, pos, state, entity) -> { for (After event : listeners) { diff --git a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/PlayerBreakBlockTests.java b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/PlayerBreakBlockTests.java index 6a510ec62..dd148f94a 100644 --- a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/PlayerBreakBlockTests.java +++ b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/PlayerBreakBlockTests.java @@ -29,16 +29,10 @@ public class PlayerBreakBlockTests implements ModInitializer { @Override public void onInitialize() { - PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, entity) -> { - return state.getBlock() != Blocks.BEDROCK; - })); + PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, entity) -> state.getBlock() != Blocks.BEDROCK)); - PlayerBlockBreakEvents.CANCELED.register(((world, player, pos, state, entity) -> { - LOGGER.info("Block break event canceled at " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ()); - })); + PlayerBlockBreakEvents.CANCELED.register(((world, player, pos, state, entity) -> LOGGER.info("Block break event canceled at {}, {}, {} (client-side = {})", pos.getX(), pos.getY(), pos.getZ(), world.isClient()))); - PlayerBlockBreakEvents.AFTER.register(((world, player, pos, state, entity) -> { - LOGGER.info("Block broken at " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ()); - })); + PlayerBlockBreakEvents.AFTER.register(((world, player, pos, state, entity) -> LOGGER.info("Block broken at {}, {}, {} (client-side = {})", pos.getX(), pos.getY(), pos.getZ(), world.isClient()))); } } diff --git a/fabric-events-interaction-v0/src/testmod/resources/fabric.mod.json b/fabric-events-interaction-v0/src/testmod/resources/fabric.mod.json index f3ad498eb..0bbca916a 100644 --- a/fabric-events-interaction-v0/src/testmod/resources/fabric.mod.json +++ b/fabric-events-interaction-v0/src/testmod/resources/fabric.mod.json @@ -19,6 +19,7 @@ ], "client": [ "net.fabricmc.fabric.test.client.event.interaction.ClientPreAttackTests", + "net.fabricmc.fabric.test.client.event.interaction.ClientPlayerBlockBreakTests", "net.fabricmc.fabric.test.client.event.interaction.PlayerPickBlockTests" ] } diff --git a/fabric-events-interaction-v0/src/testmodClient/java/net/fabricmc/fabric/test/client/event/interaction/ClientPlayerBlockBreakTests.java b/fabric-events-interaction-v0/src/testmodClient/java/net/fabricmc/fabric/test/client/event/interaction/ClientPlayerBlockBreakTests.java new file mode 100644 index 000000000..c980e2779 --- /dev/null +++ b/fabric-events-interaction-v0/src/testmodClient/java/net/fabricmc/fabric/test/client/event/interaction/ClientPlayerBlockBreakTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 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.test.client.event.interaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents; + +public class ClientPlayerBlockBreakTests implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger(ClientPlayerBlockBreakTests.class); + + @Override + public void onInitializeClient() { + ClientPlayerBlockBreakEvents.AFTER.register(((world, player, pos, state) -> LOGGER.info("Block broken at {}, {}, {} (client-side = {})", pos.getX(), pos.getY(), pos.getZ(), world.isClient()))); + } +}