diff --git a/src/main/java/de/diddiz/LogBlock/LogBlock.java b/src/main/java/de/diddiz/LogBlock/LogBlock.java index 91acee3..cb7706b 100644 --- a/src/main/java/de/diddiz/LogBlock/LogBlock.java +++ b/src/main/java/de/diddiz/LogBlock/LogBlock.java @@ -152,6 +152,9 @@ public class LogBlock extends JavaPlugin { if (isLogging(Logging.SNOWFADE)) { pm.registerEvents(new SnowFadeLogging(this), this); } + if (isLogging(Logging.SCAFFOLDING)) { + pm.registerEvents(new ScaffoldingLogging(this), this); + } if (isLogging(Logging.CREEPEREXPLOSION) || isLogging(Logging.TNTEXPLOSION) || isLogging(Logging.GHASTFIREBALLEXPLOSION) || isLogging(Logging.ENDERDRAGON) || isLogging(Logging.MISCEXPLOSION)) { pm.registerEvents(new ExplosionLogging(this), this); } diff --git a/src/main/java/de/diddiz/LogBlock/Logging.java b/src/main/java/de/diddiz/LogBlock/Logging.java index 4357201..40a4e16 100644 --- a/src/main/java/de/diddiz/LogBlock/Logging.java +++ b/src/main/java/de/diddiz/LogBlock/Logging.java @@ -43,7 +43,8 @@ public enum Logging { BEDEXPLOSION(true), DRAGONEGGTELEPORT(true), DAYLIGHTDETECTORINTERACT, - LECTERNBOOKCHANGE(true); + LECTERNBOOKCHANGE(true), + SCAFFOLDING(true); public static final int length = Logging.values().length; private final boolean defaultEnabled; diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ScaffoldingLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ScaffoldingLogging.java new file mode 100644 index 0000000..ba44e8f --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/listeners/ScaffoldingLogging.java @@ -0,0 +1,170 @@ +package de.diddiz.LogBlock.listeners; + +import de.diddiz.LogBlock.Actor; +import de.diddiz.LogBlock.LogBlock; +import de.diddiz.LogBlock.Logging; +import java.util.ArrayDeque; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +import static de.diddiz.LogBlock.config.Config.isLogging; + +public class ScaffoldingLogging extends LoggingListener { + private final static long MAX_SCAFFOLDING_LOG_TIME_MS = 2000; + private final static EnumSet NEIGHBOURS_SIDES_AND_UP = EnumSet.of(BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); + private final static EnumSet NEIGHBOURS_SIDES_AND_BELOW = EnumSet.of(BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); + + private final ArrayDeque scaffoldingBreakersList = new ArrayDeque<>(); + private final HashMap scaffoldingBreakersByLocation = new HashMap<>(); + private final HashMap scaffoldingPlacersByLocation = new HashMap<>(); + + public ScaffoldingLogging(LogBlock lb) { + super(lb); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockFade(BlockFadeEvent event) { + Block block = event.getBlock(); + if (isLogging(block.getWorld(), Logging.SCAFFOLDING)) { + final Material type = block.getType(); + if (type == Material.SCAFFOLDING) { + Player placer = scaffoldingPlacersByLocation.get(block.getLocation()); + cleanupScaffoldingBreakers(); + if (placer != null) { + consumer.queueBlockReplace(Actor.actorFromEntity(placer), block.getState(), event.getNewState()); + return; + } + Player breaker = getScaffoldingBreaker(block); + if (breaker != null) { + for (BlockFace dir : NEIGHBOURS_SIDES_AND_UP) { + Block otherBlock = block.getRelative(dir); + if (otherBlock.getType() == Material.SCAFFOLDING) { + addScaffoldingBreaker(breaker, otherBlock); + } + } + } + consumer.queueBlockReplace(breaker == null ? new Actor("ScaffoldingFall") : Actor.actorFromEntity(breaker), block.getState(), event.getNewState()); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + Block block = event.getBlock(); + if (isLogging(block.getWorld(), Logging.SCAFFOLDING)) { + cleanupScaffoldingBreakers(); + Block otherBlock; + if (block.getType() == Material.SCAFFOLDING) { + for (BlockFace dir : NEIGHBOURS_SIDES_AND_UP) { + otherBlock = block.getRelative(dir); + if (otherBlock.getType() == Material.SCAFFOLDING) { + addScaffoldingBreaker(event.getPlayer(), otherBlock); + } + } + } else if ((otherBlock = block.getRelative(BlockFace.UP)).getType() == Material.SCAFFOLDING) { + addScaffoldingBreaker(event.getPlayer(), otherBlock); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent event) { + Block block = event.getBlock(); + if (isLogging(block.getWorld(), Logging.SCAFFOLDING)) { + cleanupScaffoldingBreakers(); + if (block.getType() == Material.SCAFFOLDING) { + scaffoldingPlacersByLocation.put(block.getLocation(), event.getPlayer()); + } + } + } + + private void addScaffoldingBreaker(Player player, Block block) { + ScaffoldingBreaker breaker = new ScaffoldingBreaker(player, block.getLocation()); + scaffoldingBreakersList.addLast(breaker); + scaffoldingBreakersByLocation.put(breaker.getLocation(), breaker); + + } + + private void cleanupScaffoldingBreakers() { + if (!scaffoldingPlacersByLocation.isEmpty()) { + scaffoldingPlacersByLocation.clear(); + } + if (!scaffoldingBreakersList.isEmpty()) { + long time = System.currentTimeMillis() - MAX_SCAFFOLDING_LOG_TIME_MS; + while (!scaffoldingBreakersList.isEmpty() && scaffoldingBreakersList.getFirst().getTime() < time) { + ScaffoldingBreaker breaker = scaffoldingBreakersList.removeFirst(); + scaffoldingBreakersByLocation.remove(breaker.getLocation(), breaker); + } + } + } + + private Player getScaffoldingBreaker(Block block) { + if (scaffoldingBreakersList.isEmpty()) { + return null; + } + + ScaffoldingBreaker breaker = scaffoldingBreakersByLocation.get(block.getLocation()); + if (breaker != null) { + return breaker.getBreaker(); + } + + // Search all connected scaffoldings + ArrayDeque front = new ArrayDeque<>(); + HashSet frontAndDone = new HashSet<>(); + front.addLast(block); + frontAndDone.add(block); + while (!front.isEmpty()) { + Block current = front.removeFirst(); + Location loc = current.getLocation(); + + breaker = scaffoldingBreakersByLocation.get(loc); + if (breaker != null) { + return breaker.getBreaker(); + } + + for (BlockFace dir : NEIGHBOURS_SIDES_AND_BELOW) { + Block otherBlock = current.getRelative(dir); + if (!frontAndDone.contains(otherBlock) && otherBlock.getType() == Material.SCAFFOLDING) { + front.addLast(otherBlock); + frontAndDone.add(otherBlock); + } + } + } + return null; + } + + class ScaffoldingBreaker { + protected final Player breaker; + protected final long time; + protected final Location location; + + public ScaffoldingBreaker(Player breaker, Location location) { + this.breaker = breaker; + this.location = location; + this.time = System.currentTimeMillis(); + } + + public Player getBreaker() { + return breaker; + } + + public Location getLocation() { + return location; + } + + public long getTime() { + return time; + } + } +}