Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add generic block broken event #44

Open
wants to merge 1 commit into
base: 1.20
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/xyz/nucleoid/stimuli/StimuliInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void onInitialize() {
PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, entity) -> {
if (player instanceof ServerPlayerEntity serverPlayer) {
try (var invokers = Stimuli.select().forEntityAt(player, pos)) {
return invokers.get(BlockBreakEvent.EVENT).onBreak(serverPlayer, (ServerWorld) world, pos) != ActionResult.FAIL;
return invokers.get(BlockBreakEvent.PLAYER).onBreak(serverPlayer, (ServerWorld) world, pos) != ActionResult.FAIL;
}
}
return true;
Expand Down
67 changes: 53 additions & 14 deletions src/main/java/xyz/nucleoid/stimuli/event/block/BlockBreakEvent.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
package xyz.nucleoid.stimuli.event.block;

import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.stimuli.event.StimulusEvent;

/**
* Called when any {@link ServerPlayerEntity} attempts to break a block.
*
* <p>Upon return:
* <ul>
* <li>{@link ActionResult#SUCCESS} cancels further processing and allows the break.
* <li>{@link ActionResult#FAIL} cancels further processing and cancels the break.
* <li>{@link ActionResult#PASS} moves on to the next listener.</ul>
* <p>
* If all listeners return {@link ActionResult#PASS}, the break succeeds.
*/
public interface BlockBreakEvent {
StimulusEvent<BlockBreakEvent> EVENT = StimulusEvent.create(BlockBreakEvent.class, ctx -> (player, world, pos) -> {
public final class BlockBreakEvent {
/**
* Called when a block is broken in a {@link net.minecraft.world.World}, regardless of the breaker.
*
* <p>Upon return:
* <ul>
* <li>{@link ActionResult#SUCCESS} cancels further processing and allows the break.
* <li>{@link ActionResult#FAIL} cancels further processing and cancels the break.
* <li>{@link ActionResult#PASS} moves on to the next listener.</ul>
* <p>
* If all listeners return {@link ActionResult#PASS}, the break succeeds and proceeds with normal logic.
*/
public static final StimulusEvent<World> WORLD = StimulusEvent.create(World.class, ctx -> (pos, drop, breakingEntity, maxUpdateDepth) -> {
try {
for (var listener : ctx.getListeners()) {
var result = listener.onBreak(pos, drop, breakingEntity, maxUpdateDepth);
if (result != ActionResult.PASS) {
return result;
}
}
} catch (Throwable t) {
ctx.handleException(t);
}
return ActionResult.PASS;
});

/**
* Called when any {@link ServerPlayerEntity} attempts to break a block.
*
* <p>Upon return:
* <ul>
* <li>{@link ActionResult#SUCCESS} cancels further processing and allows the break.
* <li>{@link ActionResult#FAIL} cancels further processing and cancels the break.
* <li>{@link ActionResult#PASS} moves on to the next listener.</ul>
* <p>
* If all listeners return {@link ActionResult#PASS}, the break succeeds.
*/
public static final StimulusEvent<Player> PLAYER = StimulusEvent.create(Player.class, ctx -> (player, world, pos) -> {
try {
for (var listener : ctx.getListeners()) {
var result = listener.onBreak(player, world, pos);
Expand All @@ -32,5 +59,17 @@ public interface BlockBreakEvent {
return ActionResult.PASS;
});

ActionResult onBreak(ServerPlayerEntity player, ServerWorld world, BlockPos pos);
/**
* @deprecated Use {@link #PLAYER} instead.
*/
@Deprecated
public static final StimulusEvent<Player> EVENT = PLAYER;

public interface Player {
ActionResult onBreak(ServerPlayerEntity player, ServerWorld world, BlockPos pos);
}

public interface World {
ActionResult onBreak(BlockPos pos, boolean drop, @Nullable Entity breakingEntity, int maxUpdateDepth);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private void breakFarmland(World world, BlockState state, BlockPos pos, Entity e
}

if (livingEntity instanceof ServerPlayerEntity player) {
var breakResult = invokers.get(BlockBreakEvent.EVENT).onBreak(player, serverWorld, pos);
var breakResult = invokers.get(BlockBreakEvent.PLAYER).onBreak(player, serverWorld, pos);
if (breakResult == ActionResult.FAIL) {
ci.cancel();
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/xyz/nucleoid/stimuli/mixin/world/WorldMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package xyz.nucleoid.stimuli.mixin.world;

import net.minecraft.entity.Entity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
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.callback.CallbackInfoReturnable;
import xyz.nucleoid.stimuli.Stimuli;
import xyz.nucleoid.stimuli.event.block.BlockBreakEvent;

@Mixin(World.class)
public class WorldMixin {
@Inject(
method = "breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z",
at = @At("HEAD"),
cancellable = true
)
private void onBreakBlock(BlockPos pos, boolean drop, @Nullable Entity breakingEntity, int maxUpdateDepth, CallbackInfoReturnable<Boolean> cir) {
var world = (World) (Object) this;
var events = Stimuli.select();

try (var invokers = breakingEntity != null ? events.forEntityAt(breakingEntity, pos) : events.at(world, pos)) {
var result = invokers.get(BlockBreakEvent.WORLD).onBreak(pos, drop, breakingEntity, maxUpdateDepth);
if (result == ActionResult.FAIL) {
cir.setReturnValue(false);
}
}
}
}