Block break event (#980)

* Block break event

* License headers + after event

* Before and after events, testmod

* Fix checkstyle

* Version bump, fix client method call

* Move to one event class

* Expand event parameters + javadoc

* Add cancelation event and move javadocs

* Move JavaDoc + Make success have same function as pass

* Fix success bug

* Fix documentation again, change approach

* Fix checkstyle

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/BlockBreakEvents.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

* Fix checkstyle

* Rename stuff

* fixes

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/impl/event/interaction/InteractionEventsRouter.java

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Update fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/api/event/player/PlayerBlockBreakEvents.java

Co-authored-by: i509VCB <i509vcb@gmail.com>

* Rename Canceled Event Method Name

Co-authored-by: liach <7806504+liach@users.noreply.github.com>
Co-authored-by: i509VCB <i509vcb@gmail.com>
This commit is contained in:
Geometrically 2020-08-21 09:22:11 -07:00 committed by GitHub
parent 29679fd6fa
commit e2e6cdad60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 226 additions and 1 deletions

View file

@ -1,5 +1,5 @@
archivesBaseName = "fabric-events-interaction-v0"
version = getSubprojectVersion(project, "0.3.3")
version = getSubprojectVersion(project, "0.4.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')

View file

@ -0,0 +1,123 @@
/*
* 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.player;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class PlayerBlockBreakEvents {
private PlayerBlockBreakEvents() { }
/**
* Callback before a block is broken.
* Only called on the server, however updates are synced with the client.
*
* <p>If any listener cancels a block breaking action, that block breaking
* action is cancelled and {@link CANCELED} event is fired. Otherwise, the
* {@link AFTER} event is fired.</p>
*/
public static final Event<Before> BEFORE = EventFactory.createArrayBacked(Before.class,
(listeners) -> (world, player, pos, state, entity) -> {
for (Before event : listeners) {
boolean result = event.beforeBlockBreak(world, player, pos, state, entity);
if (!result) {
return false;
}
}
return true;
}
);
/**
* Callback after a block is broken.
*
* <p>Only called on a logical server.
*/
public static final Event<After> AFTER = EventFactory.createArrayBacked(After.class,
(listeners) -> (world, player, pos, state, entity) -> {
for (After event : listeners) {
event.afterBlockBreak(world, player, pos, state, entity);
}
}
);
/**
* Callback when a block break has been canceled.
*
* <p>Only called on a logical server. May be used to send packets to revert client-side block changes.
*/
public static final Event<Canceled> CANCELED = EventFactory.createArrayBacked(Canceled.class,
(listeners) -> (world, player, pos, state, entity) -> {
for (Canceled event : listeners) {
event.onBlockBreakCanceled(world, player, pos, state, entity);
}
}
);
@FunctionalInterface
public interface Before {
/**
* Called before a block is broken and allows cancelling the block breaking.
*
* <p>Implementations should not modify the world or assume the block break has completed or failed.</p>
*
* @param world the world in which the block is broken
* @param player the player breaking the block
* @param pos the position at which the block is broken
* @param state the block state <strong>before</strong> the block is broken
* @param blockEntity the block entity <strong>before</strong> the block is broken, can be {@code null}
* @return {@code false} to cancel block breaking action, or {@code true} to pass to next listener
*/
boolean beforeBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state, /* Nullable */ BlockEntity blockEntity);
}
@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 <strong>before</strong> the block was broken
* @param blockEntity the block entity of the broken block, can be {@code null}
*/
void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state, /* Nullable */ BlockEntity blockEntity);
}
@FunctionalInterface
public interface Canceled {
/**
* Called when a block break has been canceled.
*
* @param world the world where the block was going to be broken
* @param player the player who was going to break the block
* @param pos the position where the block was going to be broken
* @param state the block state of the block that was going to be broken
* @param blockEntity the block entity of the block that was going to be broken, can be {@code null}
*/
void onBlockBreakCanceled(World world, PlayerEntity player, BlockPos pos, BlockState state, /* Nullable */ BlockEntity blockEntity);
}
}

View file

@ -16,9 +16,13 @@
package net.fabricmc.fabric.impl.event.interaction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.block.BlockState;
import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.block.BlockAttackInteractionAware;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
@ -41,5 +45,22 @@ public class InteractionEventsRouter implements ModInitializer {
return ActionResult.PASS;
});
/*
* This code is for telling the client that the block wasn't actually broken.
* This covers a 3x3 area due to how vanilla redstone handles updates, as it considers
* important functions like quasi-connectivity and redstone dust logic
*/
PlayerBlockBreakEvents.CANCELED.register(((world, player, pos, state, blockEntity) -> {
BlockPos cornerPos = pos.add(-1, -1, -1);
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
for (int z = 0; z < 3; z++) {
((ServerPlayerEntity) player).networkHandler.sendPacket(new BlockUpdateS2CPacket(world, cornerPos.add(x, y, z)));
}
}
}
}));
}
}

View file

@ -22,7 +22,11 @@ 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.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
@ -40,6 +44,7 @@ import net.minecraft.world.World;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.event.player.UseItemCallback;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
@Mixin(ServerPlayerInteractionManager.class)
public class MixinServerPlayerInteractionManager {
@ -81,4 +86,20 @@ public class MixinServerPlayerInteractionManager {
return;
}
}
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBreak(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/entity/player/PlayerEntity;)V"), method = "tryBreakBlock", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
private void breakBlock(BlockPos pos, CallbackInfoReturnable<Boolean> cir, BlockState state, BlockEntity entity, Block block) {
boolean result = PlayerBlockBreakEvents.BEFORE.invoker().beforeBlockBreak(this.world, this.player, pos, state, entity);
if (!result) {
PlayerBlockBreakEvents.CANCELED.invoker().onBlockBreakCanceled(this.world, this.player, pos, state, entity);
cir.setReturnValue(false);
}
}
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), method = "tryBreakBlock", locals = LocalCapture.CAPTURE_FAILHARD)
private void onBlockBroken(BlockPos pos, CallbackInfoReturnable<Boolean> cir, BlockState state, BlockEntity entity, Block block, boolean b1) {
PlayerBlockBreakEvents.AFTER.invoker().afterBlockBreak(this.world, this.player, pos, state, entity);
}
}

View file

@ -0,0 +1,44 @@
/*
* 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.event.interaction;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.Blocks;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
public class PlayerBreakBlockTests implements ModInitializer {
public static final Logger LOGGER = LogManager.getLogger("InteractionEventsTest");
@Override
public void onInitialize() {
PlayerBlockBreakEvents.BEFORE.register(((world, player, pos, state, entity) -> {
return 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.AFTER.register(((world, player, pos, state, entity) -> {
LOGGER.info("Block broken at " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ());
}));
}
}

View file

@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"id": "fabric-events-interaction-v0-testmod",
"name": "Fabric Events Interaction (v0) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-events-interaction-v0": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.event.interaction.PlayerBreakBlockTests"
]
}
}