diff --git a/src/main/java/net/knarcraft/clearonworldguard/ClearOnWorldGuard.java b/src/main/java/net/knarcraft/clearonworldguard/ClearOnWorldGuard.java index 580b242..2e9ce3e 100644 --- a/src/main/java/net/knarcraft/clearonworldguard/ClearOnWorldGuard.java +++ b/src/main/java/net/knarcraft/clearonworldguard/ClearOnWorldGuard.java @@ -1,9 +1,15 @@ package net.knarcraft.clearonworldguard; +import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.listener.BlockListener; +import net.knarcraft.clearonworldguard.listener.EntityListener; +import net.knarcraft.clearonworldguard.listener.PlayerListener; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -13,16 +19,21 @@ import java.util.logging.Logger; public final class ClearOnWorldGuard extends JavaPlugin { private static ClearOnWorldGuard instance; + private boolean debugMode; @Override public void onEnable() { instance = this; this.saveDefaultConfig(); this.reloadConfig(); - FileConfiguration configuration = this.getConfig(); + FileConfiguration fileConfiguration = this.getConfig(); // Register the WorldGuard listener - Bukkit.getPluginManager().registerEvents(new WorldGuardListener(configuration), this); + Configuration configuration = new Configuration(fileConfiguration); + Bukkit.getPluginManager().registerEvents(new PlayerListener(configuration), this); + Bukkit.getPluginManager().registerEvents(new EntityListener(configuration), this); + Bukkit.getPluginManager().registerEvents(new BlockListener(configuration), this); + this.debugMode = configuration.debugEnabled(); } /** @@ -34,4 +45,26 @@ public final class ClearOnWorldGuard extends JavaPlugin { return instance.getLogger(); } + /** + * Logs a debug message + * + * @param message

The message to debug

+ */ + public static void logDebugMessage(@NotNull String message) { + if (instance.debugMode) { + logger().log(Level.INFO, message); + } else { + logger().log(Level.FINEST, message); + } + } + + /** + * Gets an instance of this plugin + * + * @return

An instance of this plugin

+ */ + public static ClearOnWorldGuard getInstance() { + return instance; + } + } diff --git a/src/main/java/net/knarcraft/clearonworldguard/WorldGuardListener.java b/src/main/java/net/knarcraft/clearonworldguard/WorldGuardListener.java deleted file mode 100644 index 33a26ec..0000000 --- a/src/main/java/net/knarcraft/clearonworldguard/WorldGuardListener.java +++ /dev/null @@ -1,506 +0,0 @@ -package net.knarcraft.clearonworldguard; - -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.RegionContainer; -import com.sk89q.worldguard.protection.regions.RegionQuery; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockBurnEvent; -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.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.TNTPrimeEvent; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityDeathEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.EntityPickupItemEvent; -import org.bukkit.event.entity.ExplosionPrimeEvent; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.entity.ProjectileLaunchEvent; -import org.bukkit.event.player.PlayerDropItemEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SpawnEggMeta; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; - -/** - * A listener that listens to players entering and leaving WorldGuard regions - */ -public class WorldGuardListener implements Listener { - - private final RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); - private final RegionQuery query = regionContainer.createQuery(); - private final Map> protectedRegions = new HashMap<>(); - - /** - * Instantiates a new WorldGuard listener - */ - public WorldGuardListener(@NotNull FileConfiguration configuration) { - ConfigurationSection worldsSection = configuration.getConfigurationSection("clearRegions"); - if (worldsSection == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to find clearRegions sections in the " + - "configuration file!"); - return; - } - Set worlds = worldsSection.getKeys(false); - - for (String worldId : worlds) { - // Parse every world identifier - World world; - try { - UUID worldUUID = UUID.fromString(worldId); - world = Bukkit.getWorld(worldUUID); - } catch (IllegalArgumentException exception) { - world = Bukkit.getWorld(worldId); - } - if (world == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "The specified world: \"" + worldId + "\" is invalid."); - continue; - } - - loadRegions(world, worldsSection, worldId); - } - } - - /** - * Loads clear regions for a world - * - * @param world

The world to load regions for

- * @param worldsSection

The configuration section listing all worlds and regions

- * @param worldId

The user-specified identifier for the currently processed world

- */ - private void loadRegions(@NotNull World world, @NotNull ConfigurationSection worldsSection, @NotNull String worldId) { - // Get a region manager for the world - RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(world)); - if (regionManager == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to get region manager for world: " + - world.getName()); - return; - } - - Set regions = new HashSet<>(); - List regionNames = worldsSection.getStringList(worldId); - for (String regionName : regionNames) { - // Register the region if it's valid - ProtectedRegion region = regionManager.getRegion(regionName); - if (region == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "Region name " + regionName + " is invalid"); - } else { - regions.add(region); - } - } - protectedRegions.put(world, regions); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onMove(@NotNull PlayerMoveEvent event) { - if (event.getTo() == null || event.getFrom().getWorld() == null) { - return; - } - - clearIfClearRegionChange(event.getFrom(), event.getTo(), event.getPlayer()); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onTeleport(@NotNull PlayerTeleportEvent event) { - Location fromLocation = event.getFrom(); - Location toLocation = event.getTo(); - - if (toLocation == null || fromLocation.equals(toLocation)) { - return; - } - clearIfClearRegionChange(fromLocation, toLocation, event.getPlayer()); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onItemDrop(@NotNull PlayerDropItemEvent event) { - // Prevent a player from dropping an item while inside a clear region - if (isInClearRegion(event.getPlayer())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onXpBottle(@NotNull ProjectileLaunchEvent event) { - // Prevent a player from using an XP bottle in a clear region - if (isInClearRegion(event.getEntity())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onTNT(@NotNull TNTPrimeEvent event) { - // Prevent TNT from being primed in a clear region, or by a player in a clear region - Entity primingEntity = event.getPrimingEntity(); - if (primingEntity != null && isInClearRegion(primingEntity)) { - event.setCancelled(true); - return; - } - - Block primingBlock = event.getPrimingBlock(); - if (primingBlock != null && isInClearRegion(primingBlock)) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockPlace(@NotNull BlockPlaceEvent event) { - // Prevent placing a block outside a clear region - if (isInDifferentClearRegions(event.getPlayer().getLocation(), event.getBlockPlaced().getLocation())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockBreak(@NotNull BlockBreakEvent event) { - // Prevent breaking a block outside a clear region, while in the clear region - if (isInDifferentClearRegions(event.getPlayer().getLocation(), event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - - // Prevent item drops when the item is destroyed - if (isInClearRegion(event.getBlock())) { - event.setDropItems(false); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onExplosionPrime(@NotNull ExplosionPrimeEvent event) { - Location location = event.getEntity().getLocation(); - float radius = event.getRadius(); - if (isInClearRegion(location) || - isInClearRegion(location.clone().add(radius, radius, radius)) || - isInClearRegion(location.clone().add(-radius, radius, radius)) || - isInClearRegion(location.clone().add(-radius, radius, -radius)) || - isInClearRegion(location.clone().add(radius, radius, -radius)) || - isInClearRegion(location.clone().add(radius, -radius, radius)) || - isInClearRegion(location.clone().add(-radius, -radius, radius)) || - isInClearRegion(location.clone().add(-radius, -radius, -radius)) || - isInClearRegion(location.clone().add(radius, -radius, -radius))) { - event.setCancelled(true); - event.getEntity().remove(); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onEntityExplode(@NotNull EntityExplodeEvent event) { - // Prevent explosions in clear regions - if (isInClearRegion(event.getEntity())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onEntityChangeBlock(@NotNull EntityChangeBlockEvent event) { - // Prevent entities from changing blocks in clear regions - if (isInClearRegion(event.getBlock())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onCreatureSpawn(@NotNull CreatureSpawnEvent event) { - // Prevent creatures from spawning - if (isInClearRegion(event.getLocation()) && event.getEntityType() != EntityType.ARMOR_STAND) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockItemDrop(@NotNull BlockDropItemEvent event) { - // Block item drops for indirectly destroyed items - if (isInClearRegion(event.getBlock())) { - event.getItems().clear(); - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onBlockXP(@NotNull BlockExpEvent event) { - // Prevent dropping XP in clear regions - if (isInClearRegion(event.getBlock())) { - event.setExpToDrop(0); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onEntityDamage(@NotNull EntityDamageEvent event) { - // Prevent entity damage in clear regions - if (isInClearRegion(event.getEntity())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onDeath(@NotNull EntityDeathEvent event) { - // Prevent mob loot and XP - if (isInClearRegion(event.getEntity())) { - event.setDroppedExp(0); - event.getDrops().clear(); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onDispense(@NotNull BlockDispenseEvent event) { - // Prevent dispensers from dispensing and droppers from dropping - if (isInClearRegion(event.getBlock())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onArmorDispense(@NotNull BlockDispenseArmorEvent event) { - // Prevent dispensers from dispensing armors in clear regions - if (isInClearRegion(event.getBlock())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBurn(@NotNull BlockBurnEvent event) { - // Prevent blocks from burning in clear areas - Block block = event.getBlock(); - if (isInClearRegion(block)) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onIgnite(@NotNull BlockIgniteEvent event) { - // Prevent ignition in clear areas, unless done by flint and steel - Block block = event.getBlock(); - if (isInClearRegion(block) && event.getCause() != BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onDeath(@NotNull PlayerDeathEvent event) { - // Prevent deaths in clear regions from causing items to be kept or dropped - if (isInClearRegion(event.getEntity())) { - event.setKeepInventory(false); - event.setKeepLevel(true); - event.getEntity().getInventory().clear(); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onItemPickup(@NotNull EntityPickupItemEvent event) { - // Prevent item pickup in clear regions - if (!(event.getEntity() instanceof Player player)) { - return; - } - - if (isInClearRegion(player)) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockSpread(@NotNull BlockSpreadEvent event) { - // Prevent block spread in clear regions - if (isInClearRegion(event.getSource())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockForm(@NotNull BlockFormEvent event) { - // Prevent block forming in clear regions - if (isInClearRegion(event.getBlock())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onPistonExtend(@NotNull BlockPistonExtendEvent event) { - // Prevent pistons from pushing blocks across the clear region border - Block piston = event.getBlock(); - List blocks = event.getBlocks(); - for (Block block : blocks) { - if (isInDifferentClearRegions(piston.getLocation(), block.getLocation())) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onPistonRetract(@NotNull BlockPistonRetractEvent event) { - // Prevent pistons from pulling blocks across the clear region border - Block piston = event.getBlock(); - List blocks = event.getBlocks(); - for (Block block : blocks) { - if (isInDifferentClearRegions(piston.getLocation(), block.getLocation())) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onInteract(@NotNull PlayerInteractEvent event) { - // Prevent interacting with blocks outside the clear region, such as chests - Block clicked = event.getClickedBlock(); - if (clicked != null && isInDifferentClearRegions(event.getPlayer().getLocation(), clicked.getLocation())) { - 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)) { - event.setCancelled(true); - return; - } - - // Block usage of spawn eggs - ItemStack item = event.getItem(); - if (item != null && isInClearRegion(event.getPlayer()) && item.hasItemMeta()) { - ItemMeta meta = event.getItem().getItemMeta(); - if (meta instanceof SpawnEggMeta) { - event.setCancelled(true); - } - } - } - - /** - * Clears the given player's inventory if they are leaving or entering a clear region - * - * @param fromLocation

The location the player moved or teleported from

- * @param toLocation

The location the player moved or teleported to

- * @param player

The player that's moving

- */ - private void clearIfClearRegionChange(@NotNull Location fromLocation, @NotNull Location toLocation, - @NotNull Player player) { - if (isInDifferentClearRegions(fromLocation, toLocation)) { - player.getInventory().clear(); - } - } - - /** - * Checks whether the given locations are in different clear regions - * - *

If location1 is in a clear region and location2 is not, or if location1 is in a different clear region than - * region2, or if region 1 is in an additional clear region, this will return true.

- * - * @param location1

The first location to check

- * @param location2

The second location to check

- * @return

True if the two locations are in different clear regions

- */ - private boolean isInDifferentClearRegions(@NotNull Location location1, @NotNull Location location2) { - World fromWorld = location1.getWorld(); - if (fromWorld == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to check region change, as location " + - location1 + " has no world."); - return false; - } - - ApplicableRegionSet setFrom = query.getApplicableRegions(BukkitAdapter.adapt(location1)); - ApplicableRegionSet setTo = query.getApplicableRegions(BukkitAdapter.adapt(location2)); - - Set fromRegions = getOccupiedClearRegions(fromWorld, setFrom.getRegions()); - Set toRegions = getOccupiedClearRegions(fromWorld, setTo.getRegions()); - - // If the player is in one or more clear regions, clear unless the clear regions are the same - return (!fromRegions.isEmpty() || !toRegions.isEmpty()) && !fromRegions.equals(toRegions); - } - - /** - * Checks whether the given block is in a clear region - * - * @param entity

The entity to check

- * @return

True if the block is in a clear region

- */ - private boolean isInClearRegion(@NotNull Entity entity) { - return isInClearRegion(entity.getLocation()); - } - - /** - * Checks whether the given block is in a clear region - * - * @param block

The block to check

- * @return

True if the block is in a clear region

- */ - private boolean isInClearRegion(@NotNull Block block) { - return isInClearRegion(block.getLocation()); - } - - /** - * Checks whether the given location is in a clear region - * - * @param location

The location to check

- * @return

True if the location is in a clear region

- */ - private boolean isInClearRegion(@NotNull Location location) { - World playerWorld = location.getWorld(); - if (playerWorld == null) { - ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to check region change, as location " + - location + " has no world."); - return false; - } - - ApplicableRegionSet setFrom = query.getApplicableRegions(BukkitAdapter.adapt(location)); - Set fromRegions = getOccupiedClearRegions(playerWorld, setFrom.getRegions()); - return !fromRegions.isEmpty(); - } - - /** - * Gets all regions set as clear regions the player currently occupies - * - * @param playerWorld

The world the player is currently in

- * @param playerRegions

The regions the player is in or will be in

- * @return

All clear regions found in playerRegions

- */ - private Set getOccupiedClearRegions(@NotNull World playerWorld, - @NotNull Set playerRegions) { - Set possibleRegions = protectedRegions.get(playerWorld); - Set result = new HashSet<>(); - for (ProtectedRegion region : playerRegions) { - if (possibleRegions.contains(region)) { - result.add(region); - } - } - - return result; - } - -} diff --git a/src/main/java/net/knarcraft/clearonworldguard/config/Configuration.java b/src/main/java/net/knarcraft/clearonworldguard/config/Configuration.java new file mode 100644 index 0000000..9185e7c --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/config/Configuration.java @@ -0,0 +1,124 @@ +package net.knarcraft.clearonworldguard.config; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import com.sk89q.worldguard.protection.regions.RegionQuery; +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +/** + * A configuration that keeps track of loaded clear regions + */ +public class Configuration { + + private final RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer(); + private final RegionQuery regionQuery = regionContainer.createQuery(); + private final Map> protectedRegions = new HashMap<>(); + private final boolean debugEnabled; + + /** + * Instantiates a new configuration + * + * @param configuration

The file configuration to read

+ */ + public Configuration(@NotNull FileConfiguration configuration) { + debugEnabled = Boolean.parseBoolean(configuration.getString("debug")); + ConfigurationSection worldsSection = configuration.getConfigurationSection("clearRegions"); + if (worldsSection == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to find clearRegions sections in the " + + "configuration file!"); + return; + } + Set worlds = worldsSection.getKeys(false); + + for (String worldId : worlds) { + // Parse every world identifier + World world; + try { + UUID worldUUID = UUID.fromString(worldId); + world = Bukkit.getWorld(worldUUID); + } catch (IllegalArgumentException exception) { + world = Bukkit.getWorld(worldId); + } + if (world == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "The specified world: \"" + worldId + "\" is invalid."); + continue; + } + + loadRegions(world, worldsSection, worldId); + } + } + + /** + * Loads clear regions for a world + * + * @param world

The world to load regions for

+ * @param worldsSection

The configuration section listing all worlds and regions

+ * @param worldId

The user-specified identifier for the currently processed world

+ */ + private void loadRegions(@NotNull World world, @NotNull ConfigurationSection worldsSection, @NotNull String worldId) { + // Get a region manager for the world + RegionManager regionManager = regionContainer.get(BukkitAdapter.adapt(world)); + if (regionManager == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to get region manager for world: " + + world.getName()); + return; + } + + Set regions = new HashSet<>(); + List regionNames = worldsSection.getStringList(worldId); + for (String regionName : regionNames) { + // Register the region if it's valid + ProtectedRegion region = regionManager.getRegion(regionName); + if (region == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "Region name " + regionName + " is invalid"); + } else { + regions.add(region); + } + } + protectedRegions.put(world, regions); + } + + /** + * Gets all protected regions for the given world + * + * @return

All protected regions for the given world

+ */ + public @NotNull Set getProtectedRegions(@NotNull World world) { + return this.protectedRegions.get(world); + } + + /** + * Gets a region query + * + * @return

A region query

+ */ + public @NotNull RegionQuery getRegionQuery() { + return regionQuery; + } + + /** + * Gets whether debug mode is enabled + * + * @return

True if debug mode is enabled

+ */ + public boolean debugEnabled() { + return this.debugEnabled; + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java new file mode 100644 index 0000000..91cf782 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/BlockListener.java @@ -0,0 +1,183 @@ +package net.knarcraft.clearonworldguard.listener; + +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import net.knarcraft.clearonworldguard.config.Configuration; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +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.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.TNTPrimeEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Listeners for block events + */ +public class BlockListener extends WorldGuardListener { + + /** + * Instantiates a new WorldGuard listener + * + * @param configuration

The configuration to get regions and settings from

+ */ + public BlockListener(@NotNull Configuration configuration) { + super(configuration); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockItemDrop(@NotNull BlockDropItemEvent event) { + // Block item drops for indirectly destroyed items + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from dropping as an item"); + event.getItems().clear(); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockXP(@NotNull BlockExpEvent event) { + // Prevent dropping XP in clear regions + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from dropping any XP"); + event.setExpToDrop(0); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onDispense(@NotNull BlockDispenseEvent event) { + // Prevent dispensers from dispensing and droppers from dropping + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented dispenser " + event.getBlock() + " from dispensing"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onArmorDispense(@NotNull BlockDispenseArmorEvent event) { + // Prevent dispensers from dispensing armors in clear regions + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented armor from dispensing from " + event.getBlock()); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBurn(@NotNull BlockBurnEvent event) { + // Prevent blocks from burning in clear areas + Block block = event.getBlock(); + if (isInClearRegion(block)) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from burning"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onIgnite(@NotNull BlockIgniteEvent event) { + // Prevent ignition in clear areas, unless done by flint and steel + Block block = event.getBlock(); + if (isInClearRegion(block) && event.getCause() != BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from igniting"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockSpread(@NotNull BlockSpreadEvent event) { + // Prevent block spread in clear regions + if (isInClearRegion(event.getSource())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from spreading"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockForm(@NotNull BlockFormEvent event) { + // Prevent block forming in clear regions + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from forming"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPistonExtend(@NotNull BlockPistonExtendEvent event) { + // Prevent pistons from pushing blocks across the clear region border + Block piston = event.getBlock(); + List blocks = event.getBlocks(); + for (Block block : blocks) { + if (isInDifferentClearRegions(piston.getLocation(), block.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented piston " + piston + " from extending"); + event.setCancelled(true); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPistonRetract(@NotNull BlockPistonRetractEvent event) { + // Prevent pistons from pulling blocks across the clear region border + Block piston = event.getBlock(); + List blocks = event.getBlocks(); + for (Block block : blocks) { + if (isInDifferentClearRegions(piston.getLocation(), block.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented piston " + piston + " from retracting"); + event.setCancelled(true); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockPlace(@NotNull BlockPlaceEvent event) { + // Prevent placing a block outside a clear region + if (isInDifferentClearRegions(event.getPlayer().getLocation(), event.getBlockPlaced().getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + event.getBlock() + " from being placed"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(@NotNull BlockBreakEvent event) { + // Prevent breaking a block outside a clear region, while in the clear region + Block block = event.getBlock(); + if (isInDifferentClearRegions(event.getPlayer().getLocation(), block.getLocation())) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + block + " from being broken"); + event.setCancelled(true); + return; + } + + // Prevent item drops when the item is destroyed + if (isInClearRegion(block)) { + ClearOnWorldGuard.logDebugMessage("Prevented block " + block + " from dropping items"); + event.setDropItems(false); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onTNT(@NotNull TNTPrimeEvent event) { + // Prevent TNT from being primed in a clear region, or by a player in a clear region + Entity primingEntity = event.getPrimingEntity(); + Block block = event.getBlock(); + Block primingBlock = event.getPrimingBlock(); + if ((primingEntity != null && isInClearRegion(primingEntity)) || isInClearRegion(block) || + (primingBlock != null && isInClearRegion(primingBlock))) { + ClearOnWorldGuard.logDebugMessage("Prevented TNT " + event.getBlock() + " from being primed by entity " + + primingEntity + " or block " + primingBlock); + event.setCancelled(true); + } + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java new file mode 100644 index 0000000..02a8a40 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/EntityListener.java @@ -0,0 +1,192 @@ +package net.knarcraft.clearonworldguard.listener; + +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.property.Permission; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.EventHandler; +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.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +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 + */ +public class EntityListener extends WorldGuardListener { + + /** + * Instantiates a new WorldGuard listener + * + * @param configuration

The configuration to get regions and settings from

+ */ + public EntityListener(@NotNull Configuration configuration) { + super(configuration); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityExplode(@NotNull EntityExplodeEvent event) { + // Prevent explosions in clear regions + if (isInClearRegion(event.getEntity())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from exploding"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityMount(@NotNull EntityMountEvent event) { + // Prevent entity mounting in clear regions + Entity entity = event.getEntity(); + Entity mount = event.getMount(); + + // Don't restrict NPCs + if (entity instanceof Player player && player.hasMetadata("NPC")) { + return; + } + + if ((isInClearRegion(entity) || isInClearRegion(mount)) && + !entity.hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + entity + " from mounting " + mount); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityChangeBlock(@NotNull EntityChangeBlockEvent event) { + // Prevent entities from changing blocks in clear regions + if (isInClearRegion(event.getBlock())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from changing the block " + + event.getBlock()); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCreatureSpawn(@NotNull CreatureSpawnEvent event) { + // Prevent creatures from spawning, unless it's intentional + CreatureSpawnEvent.SpawnReason spawnReason = event.getSpawnReason(); + if (isInClearRegion(event.getLocation()) && (event.getEntityType() != EntityType.ARMOR_STAND && + spawnReason != CreatureSpawnEvent.SpawnReason.COMMAND) && + spawnReason != CreatureSpawnEvent.SpawnReason.SPAWNER_EGG + ) { + if (spawnReason != CreatureSpawnEvent.SpawnReason.NATURAL && + spawnReason != CreatureSpawnEvent.SpawnReason.BEEHIVE) { + ClearOnWorldGuard.logDebugMessage("Prevented creature " + event.getEntity() + " from spawning with " + + "spawn reason " + event.getSpawnReason()); + } + + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityDamage(@NotNull EntityDamageByEntityEvent event) { + // Prevent entity damage from players in clear regions + if (!(event.getDamager() instanceof Player player)) { + return; + } + + if ((isInClearRegion(event.getEntity()) || isInClearRegion(player)) && + !player.hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from being damaged"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityDeath(@NotNull EntityDeathEvent event) { + // Prevent mob loot and XP + if (isInClearRegion(event.getEntity())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from dropping items"); + event.setDroppedExp(0); + event.getDrops().clear(); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemPickup(@NotNull EntityPickupItemEvent event) { + // Prevent item pickup in clear regions + if (isInClearRegion(event.getEntity())) { + ClearOnWorldGuard.logDebugMessage("Prevented entity " + event.getEntity() + " from picking up an item"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onVehicleDestroy(@NotNull VehicleDestroyEvent event) { + // Prevent destroyed vehicles from dropping as items + if (isInClearRegion(event.getVehicle())) { + ClearOnWorldGuard.logDebugMessage("Prevented vehicle " + event.getVehicle() + " from dropping as an item"); + event.setCancelled(true); + clearInventoriesAndRemoveRecursively(event.getVehicle()); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onVehicleMove(@NotNull VehicleMoveEvent event) { + // Prevent chest minecarts or similar from leaving the clear region + if (event.getFrom().equals(event.getTo())) { + return; + } + + Vehicle vehicle = event.getVehicle(); + + if (isInDifferentClearRegions(event.getFrom(), event.getTo())) { + ClearOnWorldGuard.logDebugMessage("Destroyed vehicle " + vehicle + " crossing a clear region border"); + clearInventoriesAndRemoveRecursively(vehicle); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) { + // Prevent a player from using an XP bottle in a clear region + if (isInClearRegion(event.getEntity())) { + ClearOnWorldGuard.logDebugMessage("Prevented projectile " + event.getEntity()); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onExplosionPrime(@NotNull ExplosionPrimeEvent event) { + // Prevent explosion from priming if any exploded blocks are in a clear region + Location location = event.getEntity().getLocation(); + float radius = event.getRadius(); + if (isInClearRegion(location) || + isInClearRegion(location.clone().add(radius, radius, radius)) || + isInClearRegion(location.clone().add(-radius, radius, radius)) || + isInClearRegion(location.clone().add(-radius, radius, -radius)) || + isInClearRegion(location.clone().add(radius, radius, -radius)) || + isInClearRegion(location.clone().add(radius, -radius, radius)) || + isInClearRegion(location.clone().add(-radius, -radius, radius)) || + isInClearRegion(location.clone().add(-radius, -radius, -radius)) || + isInClearRegion(location.clone().add(radius, -radius, -radius))) { + ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from being primed"); + event.setCancelled(true); + event.getEntity().remove(); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCombust(@NotNull EntityCombustEvent event) { + // Prevent entities in clear regions from being put on fire + if (isInClearRegion(event.getEntity())) { + ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from combusting"); + 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 new file mode 100644 index 0000000..ccdb1a6 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/PlayerListener.java @@ -0,0 +1,147 @@ +package net.knarcraft.clearonworldguard.listener; + +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import net.knarcraft.clearonworldguard.config.Configuration; +import net.knarcraft.clearonworldguard.property.Permission; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +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.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.jetbrains.annotations.NotNull; + +/** + * Listeners for player events + */ +public class PlayerListener extends WorldGuardListener { + + /** + * Instantiates a new WorldGuard listener + * + * @param configuration

The configuration to get regions and settings from

+ */ + public PlayerListener(@NotNull Configuration configuration) { + super(configuration); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onLeash(@NotNull PlayerLeashEntityEvent event) { + // Prevent leashing of entities while in a clear region + if (isInClearRegion(event.getPlayer()) && + !event.getPlayer().hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented " + event.getEntity() + " from being leashed by " + + event.getPlayer()); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onMove(@NotNull PlayerMoveEvent event) { + Location from = event.getFrom(); + Location to = event.getTo(); + if (to == null || from.getWorld() == null) { + return; + } + Player player = event.getPlayer(); + + // Prevent player from smuggling items in a chest boat or similar + if (isInDifferentClearRegions(from, to)) { + ClearOnWorldGuard.logDebugMessage("Cleared items and vehicles of player " + player); + clearInventoriesAndRemoveRecursively(player); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTeleport(@NotNull PlayerTeleportEvent event) { + Location fromLocation = event.getFrom(); + Location toLocation = event.getTo(); + + if (toLocation == null || fromLocation.equals(toLocation)) { + return; + } + clearIfClearRegionChange(fromLocation, toLocation, event.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemDrop(@NotNull PlayerDropItemEvent event) { + // Prevent a player from dropping an item while inside a clear region + if (isInClearRegion(event.getPlayer())) { + ClearOnWorldGuard.logDebugMessage("Prevented player " + event.getPlayer() + " from dropping an item"); + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerDeath(@NotNull PlayerDeathEvent event) { + // Prevent deaths in clear regions from causing items to be kept or dropped + if (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); + 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 && isInClearRegion(player) && item.hasItemMeta()) { + ItemMeta meta = event.getItem().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); + } + } + } + + @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 ((isInClearRegion(event.getPlayer()) || isInClearRegion(event.getRightClicked())) && + !event.getPlayer().hasPermission(Permission.BYPASS_ENTITY_INTERACTION.toString())) { + ClearOnWorldGuard.logDebugMessage("Prevented player " + event.getPlayer() + + " from interacting with entity " + event.getRightClicked()); + event.setCancelled(true); + } + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/listener/WorldGuardListener.java b/src/main/java/net/knarcraft/clearonworldguard/listener/WorldGuardListener.java new file mode 100644 index 0000000..f4a4ec1 --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/listener/WorldGuardListener.java @@ -0,0 +1,194 @@ +package net.knarcraft.clearonworldguard.listener; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import net.knarcraft.clearonworldguard.ClearOnWorldGuard; +import net.knarcraft.clearonworldguard.config.Configuration; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Creature; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.logging.Level; + +/** + * A listener that listens to players entering and leaving WorldGuard regions + */ +public abstract class WorldGuardListener implements Listener { + + private final Configuration configuration; + + /** + * Instantiates a new WorldGuard listener + * + * @param configuration

The configuration to get regions and settings from

+ */ + public WorldGuardListener(@NotNull Configuration configuration) { + this.configuration = configuration; + } + + /** + * Recursively clears and removes entities connected together + * + * @param startEntity

The entity to start with

+ */ + protected void clearInventoriesAndRemoveRecursively(@NotNull Entity startEntity) { + List processed = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.add(startEntity); + + while (!queue.isEmpty()) { + Entity entity = queue.remove(); + // Prevent duplicate processing + if (processed.contains(entity)) { + continue; + } + + // Queue the entity's vehicle + if (entity.isInsideVehicle()) { + queue.add(entity.getVehicle()); + } + // Queue the entity's passengers + queue.addAll(entity.getPassengers()); + + // Add any nearby creatures leashed to the entity + Collection nearbyEntities = entity.getWorld().getNearbyEntities(entity.getLocation(), 10, 10, 10); + for (Entity nearby : nearbyEntities) { + if (nearby instanceof Creature creature && creature.isLeashed() && creature.getLeashHolder().equals(entity)) { + queue.add(nearby); + } + } + + // Clear inventory if necessary + if (entity instanceof InventoryHolder inventoryHolder) { + inventoryHolder.getInventory().clear(); + } + + processed.add(entity); + } + + // Eject and remove every entity + for (Entity entity : processed) { + if (!(entity instanceof Player)) { + entity.eject(); + entity.remove(); + } + } + } + + /** + * Clears the given player's inventory if they are leaving or entering a clear region + * + * @param fromLocation

The location the player moved or teleported from

+ * @param toLocation

The location the player moved or teleported to

+ * @param player

The player that's moving

+ */ + protected void clearIfClearRegionChange(@NotNull Location fromLocation, @NotNull Location toLocation, + @NotNull Player player) { + if (isInDifferentClearRegions(fromLocation, toLocation)) { + ClearOnWorldGuard.logDebugMessage("Cleared inventory of player " + player); + player.getInventory().clear(); + } + } + + /** + * Checks whether the given locations are in different clear regions + * + *

If location1 is in a clear region and location2 is not, or if location1 is in a different clear region than + * region2, or if region 1 is in an additional clear region, this will return true.

+ * + * @param location1

The first location to check

+ * @param location2

The second location to check

+ * @return

True if the two locations are in different clear regions

+ */ + protected boolean isInDifferentClearRegions(@NotNull Location location1, @NotNull Location location2) { + World fromWorld = location1.getWorld(); + if (fromWorld == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to check region change, as location " + + location1 + " has no world."); + return false; + } + + ApplicableRegionSet setFrom = configuration.getRegionQuery().getApplicableRegions(BukkitAdapter.adapt(location1)); + ApplicableRegionSet setTo = configuration.getRegionQuery().getApplicableRegions(BukkitAdapter.adapt(location2)); + + Set fromRegions = getOccupiedClearRegions(fromWorld, setFrom.getRegions()); + Set toRegions = getOccupiedClearRegions(fromWorld, setTo.getRegions()); + + // If the player is in one or more clear regions, clear unless the clear regions are the same + return (!fromRegions.isEmpty() || !toRegions.isEmpty()) && !fromRegions.equals(toRegions); + } + + /** + * Checks whether the given block is in a clear region + * + * @param entity

The entity to check

+ * @return

True if the block is in a clear region

+ */ + protected boolean isInClearRegion(@NotNull Entity entity) { + return isInClearRegion(entity.getLocation()); + } + + /** + * Checks whether the given block is in a clear region + * + * @param block

The block to check

+ * @return

True if the block is in a clear region

+ */ + protected boolean isInClearRegion(@NotNull Block block) { + return isInClearRegion(block.getLocation()); + } + + /** + * Checks whether the given location is in a clear region + * + * @param location

The location to check

+ * @return

True if the location is in a clear region

+ */ + protected boolean isInClearRegion(@NotNull Location location) { + World playerWorld = location.getWorld(); + if (playerWorld == null) { + ClearOnWorldGuard.logger().log(Level.WARNING, "Unable to check region change, as location " + + location + " has no world."); + return false; + } + + ApplicableRegionSet setFrom = configuration.getRegionQuery().getApplicableRegions(BukkitAdapter.adapt(location)); + Set fromRegions = getOccupiedClearRegions(playerWorld, setFrom.getRegions()); + return !fromRegions.isEmpty(); + } + + /** + * Gets all regions set as clear regions the player currently occupies + * + * @param playerWorld

The world the player is currently in

+ * @param playerRegions

The regions the player is in or will be in

+ * @return

All clear regions found in playerRegions

+ */ + private Set getOccupiedClearRegions(@NotNull World playerWorld, + @NotNull Set playerRegions) { + Set possibleRegions = configuration.getProtectedRegions(playerWorld); + Set result = new HashSet<>(); + for (ProtectedRegion region : playerRegions) { + if (possibleRegions.contains(region)) { + result.add(region); + } + } + + return result; + } + +} diff --git a/src/main/java/net/knarcraft/clearonworldguard/property/Permission.java b/src/main/java/net/knarcraft/clearonworldguard/property/Permission.java new file mode 100644 index 0000000..19a494c --- /dev/null +++ b/src/main/java/net/knarcraft/clearonworldguard/property/Permission.java @@ -0,0 +1,37 @@ +package net.knarcraft.clearonworldguard.property; + +import org.jetbrains.annotations.NotNull; + +/** + * A representation of manually checked permissions + */ +public enum Permission { + + /** + * The permission for bypassing entity interaction restrictions + */ + BYPASS_ENTITY_INTERACTION("clearonworldguard.bypass.entityinteraction"), + + /** + * The permission for bypassing mob spawning restrictions + */ + BYPASS_MOB_SPAWNING("clearonworldguard.bypass.mobspawning"), + ; + + private final String permissionString; + + /** + * Instantiates a new permission + * + * @param permissionString

The permission string this represents

+ */ + Permission(@NotNull String permissionString) { + this.permissionString = permissionString; + } + + @Override + public String toString() { + return permissionString; + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index cf39f17..ca1f72a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,4 +4,7 @@ clearRegions: # the square parentheses. world: [ ] world_nether: [ ] - world_the_end: [ ] \ No newline at end of file + world_the_end: [ ] + +# Whether to enable debug mode, which spams debug messages at log level INFO, rather than log level FINEST +debug: false \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 697f59a..28be972 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,4 +3,18 @@ version: '${project.version}' main: net.knarcraft.clearonworldguard.ClearOnWorldGuard api-version: '1.20' description: A custom KnarCraft plugin -depend: [ WorldGuard ] \ No newline at end of file +depend: [ WorldGuard ] + +permissions: + clearonworldguard.bypass.*: + default: false + description: Gives all bypass permissions + children: + - clearonworldguard.bypass.mobspawning + - clearonworldguard.bypass.entityinteraction + clearonworldguard.bypass.mobspawning: + default: false + description: Allows a player to spawn mobs in clear regions + clearonworldguard.bypass.entityinteraction: + default: false + description: Allows a player to interact with mobs in a clear region \ No newline at end of file