diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..2e16cba
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,33 @@
+pipeline {
+ agent any
+ tools {
+ jdk 'JDK17'
+ }
+ stages {
+ stage('Build') {
+ steps {
+ echo 'Building...'
+ sh 'mvn clean & mvn validate & mvn compile'
+ }
+ }
+ stage('Test') {
+ steps {
+ echo 'Testing...'
+ sh 'mvn test'
+ }
+ }
+ stage('Verify') {
+ steps {
+ echo 'Verifying...'
+ sh 'mvn verify -Dmaven.test.skip=true'
+ }
+ }
+ stage('Deploy') {
+ steps {
+ echo 'Deploying...'
+ sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
+ archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index dd8e4bc..8cafa86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,13 +65,27 @@
The protection manager
+ */ + public ProtectionManager getProtectionManager() { + return protectionManager; + } + } diff --git a/src/main/java/net/knarcraft/clearonworldguard/config/BlockIgniteProtection.java b/src/main/java/net/knarcraft/clearonworldguard/config/BlockIgniteProtection.java new file mode 100644 index 0000000..46ee6fd --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/config/BlockIgniteProtection.java @@ -0,0 +1,24 @@ +package net.knarcraft.clearonworldguard.config; + +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import org.bukkit.Material; +import org.bukkit.event.block.BlockIgniteEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; + +public class BlockIgniteProtection { + + private SetThe relative path of this protection in the configuration file
+ * @param defaultEnabledWhether this protection is enabled by default
+ */ + EnvironmentProtection(@NotNull String path, boolean defaultEnabled) { + this.path = path; + this.defaultEnabled = defaultEnabled; + } + + @Override + public @NotNull String getPath() { + return this.path; + } + + @Override + public boolean isDefaultEnabled() { + return this.defaultEnabled; + } + + @Override + public @NotNull String getRootNode() { + return "environment"; + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/config/PlayerProtection.java b/src/main/java/net/knarcraft/clearonworldguard/config/PlayerProtection.java new file mode 100644 index 0000000..93ca757 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/config/PlayerProtection.java @@ -0,0 +1,151 @@ +package net.knarcraft.clearonworldguard.config; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +/** + * Protections that might involve a player, and can be bypassed with a permission + */ +public enum PlayerProtection implements Protection { + + /** + * Prevents creature leashing in clear regions + */ + LEASHING("leashing", true), + + /** + * Prevents players from picking up items in clear regions + */ + ITEM_PICKUP("itemPickup", true), + + /** + * Prevent players from dropping items in clear regions + */ + ITEM_DROP("itemDrop", true), + + /** + * Prevents players from accessing ender chests in clear regions + */ + ENDER_CHEST("enderChest", true), + + /** + * Prevents usage of spawn eggs in clear regions + */ + SPAWN_EGG("spawnEgg", true), + + /** + * Prevents blocks from being ignited in clear regions + */ + BLOCK_IGNITE("blockIgnite", true), + + /** + * Prevents blocks from forming in clear regions + */ + ENTITY_BLOCK_FORM("blockForm", false), + + /** + * Prevents placement of blocks across a clear region border + */ + BLOCK_PLACE_BORDER("blockPlaceBorder", true), + + /** + * Prevents breaking of blocks across a clear region border + */ + BLOCK_BREAK_BORDER("blockBreakBorder", true), + + /** + * Prevents a block from dropping as an item in clear regions + */ + BLOCK_DROP_ITEM("blockDropItem", true), + + /** + * Prevents TNT from priming in clear regions + */ + TNT_PRIME("tntPrime", true), + + /** + * Prevents an entity from mounting another entity in clear regions + */ + ENTITY_MOUNT("entityMount", true), + + /** + * Prevents an entity from changing a block in clear regions + */ + ENTITY_CHANGE_BLOCK("entityChangeBlock", true), + + /** + * Prevents entities from damaging entities across clear region borders + */ + ENTITY_DAMAGE_ENTITY_BORDER("entityDamageBorder", true), + + /** + * Prevents entities from damaging other entities in clear regions + */ + ENTITY_DAMAGE_ENTITY("entityDamage", true), + + /** + * Prevents entities from being damaged by blocks in clear regions + */ + ENTITY_DAMAGE_BLOCK("entityDamageBlock", true), + + /** + * Deletes vehicles destroyed in clear regions + */ + VEHICLE_DESTROY_DELETE("vehicleDestroyDelete", true), + + /** + * Deletes vehicles leaving a clear region + */ + VEHICLE_BORDER_DELETE("vehicleBorderDelete", true), + + /** + * Clears a player's inventory, and makes players keep their XP when dying in a clear region + */ + PLAYER_DEATH_CLEAR("playerDeathClear", true), + + /** + * Prevents players from dropping items in creative mode + */ + CREATIVE_DROP("creativeItemDrop", true), + ; + + private final String path; + private final boolean defaultEnabled; + + /** + * Instantiates a new player protection + * + * @param pathThe relative path of this protection in the configuration file
+ * @param defaultEnabledWhether this protection is enabled by default
+ */ + PlayerProtection(@NotNull String path, boolean defaultEnabled) { + this.path = path; + this.defaultEnabled = defaultEnabled; + } + + @Override + public @NotNull String getPath() { + return this.path; + } + + @Override + public boolean isDefaultEnabled() { + return this.defaultEnabled; + } + + @Override + public @NotNull String getRootNode() { + return "player"; + } + + /** + * Checks whether the given player has the permission to bypass this protection + * + * @param playerThe player to check
+ * @returnTrue if the player can bypass this protection
+ */ + public boolean hasBypassPermission(@NotNull Player player) { + return player.hasPermission("clearonworldguard.bypass." + getPath()); + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/config/Protection.java b/src/main/java/net/knarcraft/clearonworldguard/config/Protection.java new file mode 100644 index 0000000..fb21080 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/config/Protection.java @@ -0,0 +1,31 @@ +package net.knarcraft.clearonworldguard.config; + +import org.jetbrains.annotations.NotNull; + +/** + * A protection option + */ +public interface Protection { + + /** + * Gets the relative path of this protection's configuration key + * + * @returnThe relative configuration path
+ */ + @NotNull String getPath(); + + /** + * Gets whether this protection is enabled by default + * + * @returnTrue if this protection is enabled by default
+ */ + boolean isDefaultEnabled(); + + /** + * Gets the configuration file root node used for all protections of this type + * + * @returnThe configuration file root node
+ */ + @NotNull String getRootNode(); + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/config/ProtectionManager.java b/src/main/java/net/knarcraft/clearonworldguard/config/ProtectionManager.java new file mode 100644 index 0000000..a340305 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/config/ProtectionManager.java @@ -0,0 +1,99 @@ +package net.knarcraft.clearonworldguard.config; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * A manager that keeps track of protections + */ +public class ProtectionManager { + + private final MapThe root node to load protections from
+ */ + public ProtectionManager(@NotNull String rootNode) { + this.rootNode = rootNode; + } + + /** + * Loads the given file configuration + * + * @param configurationThe file configuration to load
+ */ + public void load(@NotNull FileConfiguration configuration) { + this.protectionStatus.clear(); + + load(configuration, PlayerProtection.values()); + load(configuration, EnvironmentProtection.values()); + + //TODO: Load specific clear region overrides as well + } + + /** + * Loads the given protection values + * + * @param configurationThe configuration file to load protections from
+ * @param protectionsThe protections to load
+ */ + private void load(@NotNull FileConfiguration configuration, @NotNull Protection[] protections) { + for (Protection playerProtection : protections) { + protectionStatus.put(playerProtection, configuration.getBoolean(getEnabledNode(playerProtection), + playerProtection.isDefaultEnabled())); + } + } + + /** + * Saves to the given file configuration + * + * @param configurationThe file configuration to save to
+ */ + public void save(@NotNull FileConfiguration configuration) { + for (Map.EntryThe protection to check
+ * @returnTrue if the protection is enabled
+ */ + public boolean isEnabled(@NotNull Protection protection) { + Boolean status = protectionStatus.get(protection); + return status != null && status; + } + + /** + * Gets whether the given player can bypass the given permission + * + * @param playerThe player to check
+ * @param playerProtectionThe player protection to check
+ * @returnTrue if the player is able to bypass the protection
+ */ + public boolean canBypass(@NotNull Player player, @NotNull PlayerProtection playerProtection) { + return playerProtection.hasBypassPermission(player); + } + + /** + * Gets the configuration node required to check the enabled state of the given protection + * + * @param protectionThe protection to get the configuration node for
+ * @returnThe string path of the configuration node
+ */ + private @NotNull String getEnabledNode(@NotNull Protection protection) { + return rootNode + "." + protection.getRootNode() + "." + protection.getPath() + enabledKey; + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java index 91cf782..02673e2 100644 --- a/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java @@ -2,6 +2,7 @@ package net.knarcraft.clearonworldguard.listener; import net.knarcraft.clearonworldguard.ClearOnWorldGuard; import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.config.ProtectionManager; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; @@ -12,12 +13,14 @@ import org.bukkit.event.block.BlockDispenseArmorEvent; import org.bukkit.event.block.BlockDispenseEvent; import org.bukkit.event.block.BlockDropItemEvent; import org.bukkit.event.block.BlockExpEvent; +import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.EntityBlockFormEvent; import org.bukkit.event.block.TNTPrimeEvent; import org.jetbrains.annotations.NotNull; @@ -31,10 +34,11 @@ public class BlockListener extends WorldGuardListener { /** * Instantiates a new WorldGuard listener * - * @param configurationThe configuration to get regions and settings from
+ * @param configurationThe configuration to get regions and settings from
+ * @param protectionManagerThe protection manager to use for checking enabled protections
*/ - public BlockListener(@NotNull Configuration configuration) { - super(configuration); + public BlockListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) { + super(configuration, protectionManager); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -180,4 +184,21 @@ public class BlockListener extends WorldGuardListener { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityBlockForm(@NotNull EntityBlockFormEvent event) { + // Prevent blocks from forming + if (isInClearRegion(event.getBlock())) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockExplode(@NotNull BlockExplodeEvent event) { + // Prevent explosions in clear regions + if (isInClearRegion(event.getBlock()) || isInClearRegion(event.blockList())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from exploding"); + event.setCancelled(true); + } + } + } diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/ClearRegionListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/ClearRegionListener.java index ddb1136..5b3a37b 100644 --- a/src/main/java/net/knarcraft/clearonworldguard/listener/ClearRegionListener.java +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/ClearRegionListener.java @@ -3,6 +3,7 @@ package net.knarcraft.clearonworldguard.listener; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import net.knarcraft.clearonworldguard.ClearOnWorldGuard; import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.config.ProtectionManager; import net.knarcraft.clearonworldguard.event.EnterClearRegionEvent; import net.knarcraft.clearonworldguard.event.ExitClearRegionEvent; import net.knarcraft.clearonworldguard.manager.PlayerRegionTracker; @@ -22,9 +23,10 @@ public class ClearRegionListener extends WorldGuardListener { * Instantiates a new WorldGuard listener * * @param configurationThe configuration to get regions and settings from
+ * @param protectionManagerThe protection manager for protection management
*/ - public ClearRegionListener(@NotNull Configuration configuration) { - super(configuration); + public ClearRegionListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) { + super(configuration, protectionManager); } @EventHandler(priority = EventPriority.MONITOR) diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java index 6b99e27..ae887b3 100644 --- a/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java @@ -2,6 +2,7 @@ package net.knarcraft.clearonworldguard.listener; import net.knarcraft.clearonworldguard.ClearOnWorldGuard; import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.config.ProtectionManager; import net.knarcraft.clearonworldguard.manager.PlayerRegionTracker; import net.knarcraft.clearonworldguard.property.Permission; import org.bukkit.Location; @@ -15,16 +16,20 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityMountEvent; import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.entity.SpawnerSpawnEvent; import org.bukkit.event.vehicle.VehicleDestroyEvent; import org.bukkit.event.vehicle.VehicleMoveEvent; import org.jetbrains.annotations.NotNull; -import org.spigotmc.event.entity.EntityMountEvent; /** * Listeners for entity events @@ -34,16 +39,17 @@ public class EntityListener extends WorldGuardListener { /** * Instantiates a new WorldGuard listener * - * @param configurationThe configuration to get regions and settings from
+ * @param configurationThe configuration to get regions and settings from
+ * @param protectionManagerThe protection manager to use for checking enabled protections
*/ - public EntityListener(@NotNull Configuration configuration) { - super(configuration); + public EntityListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) { + super(configuration, protectionManager); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onEntityExplode(@NotNull EntityExplodeEvent event) { // Prevent explosions in clear regions - if (isInClearRegion(event.getEntity())) { + if (isInClearRegion(event.getEntity()) || isInClearRegion(event.blockList())) { ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from exploding"); event.setCancelled(true); } @@ -96,12 +102,19 @@ public class EntityListener extends WorldGuardListener { } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onEntityDamage(@NotNull EntityDamageByEntityEvent event) { - // Prevent entity damage from players in clear regions + public void onEntityDamageEntity(@NotNull EntityDamageByEntityEvent event) { + // Prevent entities from damaging each-other across clear regions + if (isInDifferentClearRegions(event.getDamager().getLocation(), event.getEntity().getLocation())) { + event.setCancelled(true); + return; + } + + // Only prevent entity damage from players in clear regions if (!(event.getDamager() instanceof Player player)) { return; } + // Prevent the damage, unless the if ((isInClearRegion(event.getEntity()) || PlayerRegionTracker.isInClearRegion(player)) && !player.hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from being damaged"); @@ -109,6 +122,13 @@ public class EntityListener extends WorldGuardListener { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityDamageBlock(@NotNull EntityDamageByBlockEvent event) { + if (isInClearRegion(event.getEntity())) { + event.setCancelled(true); + } + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onEntityDeath(@NotNull EntityDeathEvent event) { // Prevent mob loot and XP @@ -200,4 +220,29 @@ public class EntityListener extends WorldGuardListener { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemSpawn(@NotNull EntitySpawnEvent event) { + // Prevent entities from spawning in clear regions + if (isInClearRegion(event.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from spawning"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onSpawnerSpawn(@NotNull SpawnerSpawnEvent event) { + if (isInClearRegion(event.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from spawning from spawner"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onItemSpawn(@NotNull ItemSpawnEvent event) { + if (isInClearRegion(event.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented item " + event.getEntity() + " from spawning"); + event.setCancelled(true); + } + } + } diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/PlayerListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/PlayerListener.java index 2c90931..cc76ed8 100644 --- a/src/main/java/net/knarcraft/clearonworldguard/listener/PlayerListener.java +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/PlayerListener.java @@ -3,6 +3,8 @@ package net.knarcraft.clearonworldguard.listener; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import net.knarcraft.clearonworldguard.ClearOnWorldGuard; import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.config.PlayerProtection; +import net.knarcraft.clearonworldguard.config.ProtectionManager; import net.knarcraft.clearonworldguard.event.EnterClearRegionEvent; import net.knarcraft.clearonworldguard.event.ExitClearRegionEvent; import net.knarcraft.clearonworldguard.manager.PlayerRegionTracker; @@ -13,11 +15,15 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerLeashEntityEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryCreativeEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerDropItemEvent; @@ -31,7 +37,9 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SpawnEggMeta; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Set; +import java.util.function.Consumer; /** * Listeners for player events @@ -41,19 +49,107 @@ public class PlayerListener extends WorldGuardListener { /** * Instantiates a new WorldGuard listener * - * @param configurationThe configuration to get regions and settings from
+ * @param configurationThe configuration to get regions and settings from
+ * @param protectionManagerThe protection manager to use for checking enabled protections
*/ - public PlayerListener(@NotNull Configuration configuration) { - super(configuration); + public PlayerListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) { + super(configuration, protectionManager); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onLeash(@NotNull PlayerLeashEntityEvent event) { - // Prevent leashing of entities while in a clear region - if (PlayerRegionTracker.isInClearRegion(event.getPlayer()) && + checkEvent(event, PlayerProtection.LEASHING, event.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemDrop(@NotNull PlayerDropItemEvent event) { + checkEvent(event, PlayerProtection.ITEM_DROP, event.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerDeath(@NotNull PlayerDeathEvent event) { + Player player = event.getEntity().getPlayer(); + if (player == null) { + return; + } + + checkEvent(PlayerProtection.PLAYER_DEATH_CLEAR, player, (ignored) -> { + event.setKeepInventory(false); + event.setKeepLevel(true); + event.getEntity().getInventory().clear(); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onInteract(@NotNull PlayerInteractEvent event) { + // Prevent interacting with blocks outside the clear region, such as chests + Player player = event.getPlayer(); + Block clicked = event.getClickedBlock(); + if (clicked != null && isInDifferentClearRegions(player.getLocation(), clicked.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented player " + player + " from interacting across a clear" + + " region border"); + event.setCancelled(true); + return; + } + + // Block changing the creature in a spawner + if (event.getAction() == Action.RIGHT_CLICK_BLOCK && clicked != null && clicked.getType() == Material.SPAWNER && + isInClearRegion(clicked) && !player.hasPermission(Permission.BYPASS_MOB_SPAWNING.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented player " + player + " from interacting with mob spawner " + + clicked); + event.setCancelled(true); + return; + } + + // Block usage of spawn eggs + ItemStack item = event.getItem(); + if (item != null && PlayerRegionTracker.isInClearRegion(player)) { + ItemMeta meta = item.getItemMeta(); + if (meta instanceof SpawnEggMeta && !player.hasPermission(Permission.BYPASS_MOB_SPAWNING.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented player " + player + " from using the spawn egg " + item); + event.setCancelled(true); + return; + } + } + + /* Block usage of ender chests while in a clear region (the first check is this method takes care of the case + where the chest is outside the clear region) */ + if (clicked != null && PlayerRegionTracker.isInClearRegion(player) && + event.getAction() != Action.LEFT_CLICK_BLOCK && clicked.getType() == Material.ENDER_CHEST) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityInteract(@NotNull PlayerInteractEntityEvent event) { + // Allow interactions with human NPCs + if (event.getRightClicked() instanceof HumanEntity && !(event.getRightClicked() instanceof Player)) { + return; + } + + // Prevent entity interaction, such as trading or putting chests on donkeys + if ((PlayerRegionTracker.isInClearRegion(event.getPlayer()) || isInClearRegion(event.getRightClicked())) && !event.getPlayer().hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { - ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from being leashed by " + - event.getPlayer()); + ClearOnWorldGuard.logDebugMessage("Prevented player " + event.getPlayer() + + " from interacting with entity " + event.getRightClicked()); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBucketEmpty(@NotNull PlayerBucketEmptyEvent event) { + if (PlayerRegionTracker.isInClearRegion(event.getPlayer())) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCreativeDrop(@NotNull InventoryCreativeEvent event) { + ListThe triggered event
+ * @param protectionThe protection the event relates to
+ * @param playerThe player that triggered the event
+ */ + private void checkEvent(@NotNull Event event, @NotNull PlayerProtection protection, @NotNull Player player) { + if (event instanceof Cancellable cancellable) { + checkEvent(protection, player, (ignored) -> cancellable.setCancelled(true)); + } else { + throw new IllegalArgumentException("Event cannot be cancelled!"); } } - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerDeath(@NotNull PlayerDeathEvent event) { - // Prevent deaths in clear regions from causing items to be kept or dropped - if (PlayerRegionTracker.isInClearRegion(event.getEntity())) { - ClearOnWorldGuard.logDebugMessage("Prevented player " + event.getEntity() + " from dropping or keeping " + - "items on death"); - event.setKeepInventory(false); - event.setKeepLevel(true); - event.getEntity().getInventory().clear(); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onInteract(@NotNull PlayerInteractEvent event) { - // Prevent interacting with blocks outside the clear region, such as chests - Player player = event.getPlayer(); - Block clicked = event.getClickedBlock(); - if (clicked != null && isInDifferentClearRegions(player.getLocation(), clicked.getLocation())) { - ClearOnWorldGuard.logDebugMessage("Prevented player " + player + " from interacting across a clear" + - " region border"); - event.setCancelled(true); + /** + * Checks and cancels the given event if necessary + * + * @param protectionThe protection the event relates to
+ * @param playerThe player that triggered the event
+ * @param callbackThe callback to trigger to apply the protection
+ */ + private void checkEvent(@NotNull PlayerProtection protection, @NotNull Player player, + @NotNull Consumer