Make use of BlockFertilizeEvent, various BlockState lists.

Only mostly tested to work. BlockFertilize unfortunately doesn't let
us cancel the preceding stuff like StructureGrow and item use.

Also workaround Bukkit sending 2 events for trampling.
This commit is contained in:
wizjany 2019-09-18 15:41:15 -04:00
parent 5fca3b3c3d
commit 20db92541b
4 changed files with 66 additions and 63 deletions

View File

@ -19,6 +19,7 @@
package com.sk89q.worldguard.bukkit.event.block;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldguard.bukkit.cause.Cause;
@ -28,12 +29,15 @@
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.Event;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@ -44,7 +48,8 @@
abstract class AbstractBlockEvent extends DelegateEvent implements BulkEvent {
private final World world;
private final List<Block> blocks;
private List<Block> blocks;
private final List<BlockState> blockStates;
private final Material effectiveMaterial;
protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List<Block> blocks, Material effectiveMaterial) {
@ -55,6 +60,18 @@ protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, World w
this.world = world;
this.blocks = blocks;
this.effectiveMaterial = effectiveMaterial;
this.blockStates = null;
}
protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List<BlockState> blocks) {
super(originalEvent, cause);
checkNotNull(world);
checkNotNull(blocks);
checkArgument(!blocks.isEmpty());
this.world = world;
this.blockStates = blocks;
this.blocks = null;
this.effectiveMaterial = blocks.get(0).getType();
}
protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) {
@ -86,6 +103,9 @@ public World getWorld() {
* @return a list of affected block
*/
public List<Block> getBlocks() {
if (blocks == null) { // be lazy here because we often don't call getBlocks internally, just filter
blocks = blockStates.stream().map(BlockState::getBlock).collect(Collectors.toList());
}
return blocks;
}
@ -99,15 +119,21 @@ public List<Block> getBlocks() {
* @return true if one or more blocks were filtered out
*/
public boolean filter(Predicate<Location> predicate, boolean cancelEventOnFalse) {
boolean hasRemoval = false;
return blocks == null
? filterInternal(blockStates, BlockState::getLocation, predicate, cancelEventOnFalse)
: filterInternal(blocks, Block::getLocation, predicate, cancelEventOnFalse);
}
Iterator<Block> it = blocks.iterator();
private <B> boolean filterInternal(List<B> blockList, Function<B, Location> locFunc,
Predicate<Location> predicate, boolean cancelEventOnFalse) {
boolean hasRemoval = false;
Iterator<B> it = blockList.iterator();
while (it.hasNext()) {
if (!predicate.test(it.next().getLocation())) {
if (!predicate.test(locFunc.apply(it.next()))) {
hasRemoval = true;
if (cancelEventOnFalse) {
getBlocks().clear();
blockList.clear();
setCancelled(true);
break;
} else {
@ -115,7 +141,6 @@ public boolean filter(Predicate<Location> predicate, boolean cancelEventOnFalse)
}
}
}
return hasRemoval;
}
@ -147,7 +172,7 @@ public Material getEffectiveMaterial() {
@Override
public Result getResult() {
if (blocks.isEmpty()) {
if (blocks == null ? blockStates.isEmpty() : blocks.isEmpty()) {
return Result.DENY;
}
return super.getResult();

View File

@ -24,6 +24,7 @@
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@ -40,6 +41,10 @@ public class PlaceBlockEvent extends AbstractBlockEvent {
private static final HandlerList handlers = new HandlerList();
public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List<BlockState> blocks) {
super(originalEvent, cause, world, blocks);
}
public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List<Block> blocks, Material effectiveMaterial) {
super(originalEvent, cause, world, blocks, effectiveMaterial);
}

View File

@ -1,35 +0,0 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.bukkit.listener;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import javax.annotation.Nullable;
final class BlockStateAsBlockFunction {
private BlockStateAsBlockFunction() {
}
static Block apply(@Nullable BlockState blockState) {
return blockState != null ? blockState.getBlock() : null;
}
}

View File

@ -86,6 +86,7 @@
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockExpEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFertilizeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
@ -176,9 +177,8 @@ public void onBlockBreak(BlockBreakEvent event) {
@EventHandler(ignoreCancelled = true)
public void onBlockMultiPlace(BlockMultiPlaceEvent event) {
List<Block> blocks = event.getReplacedBlockStates().stream().map(BlockStateAsBlockFunction::apply).collect(Collectors.toList());
Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()),
event.getBlock().getWorld(), blocks, event.getBlock().getType()));
event.getBlock().getWorld(), event.getReplacedBlockStates()));
}
@EventHandler(ignoreCancelled = true)
@ -239,14 +239,11 @@ public void onBlockBurn(BlockBurnEvent event) {
@EventHandler(ignoreCancelled = true)
public void onStructureGrowEvent(StructureGrowEvent event) {
int originalCount = event.getBlocks().size();
List<Block> blockList = event.getBlocks().stream().map(BlockStateAsBlockFunction::apply).collect(Collectors.toList());
Player player = event.getPlayer();
if (player != null) {
Events.fireBulkEventToCancel(event, new PlaceBlockEvent(event, create(player), event.getLocation().getWorld(), blockList, Material.AIR));
} else {
Events.fireBulkEventToCancel(event, new PlaceBlockEvent(event, create(event.getLocation().getBlock()), event.getLocation().getWorld(), blockList, Material.AIR));
}
Events.fireBulkEventToCancel(event, new PlaceBlockEvent(event,
create(player == null ? event.getLocation() : player),
event.getLocation().getWorld(), event.getBlocks()));
if (!event.isCancelled() && event.getBlocks().size() != originalCount) {
event.getLocation().getBlock().setType(Material.AIR);
@ -257,20 +254,26 @@ public void onStructureGrowEvent(StructureGrowEvent event) {
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
Block block = event.getBlock();
Entity entity = event.getEntity();
Material to = event.getTo();
// Forget about Redstone ore, especially since we handle it in INTERACT
if (block.getType() == Material.REDSTONE_ORE && to == Material.REDSTONE_ORE) {
return;
}
Material toType = event.getTo();
Material fromType = block.getType();
Cause cause = create(entity);
// Fire two events: one as BREAK and one as PLACE
if (to != Material.AIR && block.getType() != Material.AIR) {
if (!Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), block))) {
Events.fireToCancel(event, new PlaceBlockEvent(event, create(entity), block.getLocation(), to));
if (toType != Material.AIR && fromType != Material.AIR) {
boolean trample = fromType == Material.FARMLAND && toType == Material.DIRT;
BreakBlockEvent breakDelagate = new BreakBlockEvent(event, cause, block);
if (trample) {
breakDelagate.getRelevantFlags().add(Flags.TRAMPLE_BLOCKS);
}
if (!Events.fireToCancel(event, breakDelagate)) {
PlaceBlockEvent placeDelegate = new PlaceBlockEvent(event, cause, block.getLocation(), toType);
if (trample) {
placeDelegate.getRelevantFlags().add(Flags.TRAMPLE_BLOCKS);
}
Events.fireToCancel(event, placeDelegate);
}
} else {
if (to == Material.AIR) {
if (toType == Material.AIR) {
// Track the source so later we can create a proper chain of causes
if (entity instanceof FallingBlock) {
Cause.trackParentCause(entity, block);
@ -279,13 +282,12 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) {
Events.fireToCancel(event, new SpawnEntityEvent(event, create(block), entity));
} else {
entityBreakBlockDebounce.debounce(
block, event.getEntity(), event, new BreakBlockEvent(event, create(entity), block));
block, event.getEntity(), event, new BreakBlockEvent(event, cause, block));
}
} else {
boolean wasCancelled = event.isCancelled();
Cause cause = create(entity);
Events.fireToCancel(event, new PlaceBlockEvent(event, cause, block.getLocation(), to));
Events.fireToCancel(event, new PlaceBlockEvent(event, cause, block.getLocation(), toType));
if (event.isCancelled() && !wasCancelled && entity instanceof FallingBlock) {
FallingBlock fallingBlock = (FallingBlock) entity;
@ -491,6 +493,12 @@ public void onEntityInteract(EntityInteractEvent event) {
event.getBlock()).setAllowed(hasInteractBypass(event.getBlock())));
}
@EventHandler(ignoreCancelled = true)
public void onBlockFertilize(BlockFertilizeEvent event) {
Cause cause = create(event.getPlayer(), event.getBlock());
Events.fireToCancel(event, new PlaceBlockEvent(event, cause, event.getBlock().getWorld(), event.getBlocks()));
}
@EventHandler(ignoreCancelled = true)
public void onBlockIgnite(BlockIgniteEvent event) {
Block block = event.getBlock();