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 + * + * @returnAn 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 MapThe world to load regions for
- * @param worldsSectionThe configuration section listing all worlds and regions
- * @param worldIdThe 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; - } - - SetThe location the player moved or teleported from
- * @param toLocationThe location the player moved or teleported to
- * @param playerThe 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 location1The first location to check
- * @param location2The second location to check
- * @returnTrue 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)); - - SetThe entity to check
- * @returnTrue 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 blockThe block to check
- * @returnTrue 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 locationThe location to check
- * @returnTrue 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)); - SetThe world the player is currently in
- * @param playerRegionsThe regions the player is in or will be in
- * @returnAll clear regions found in playerRegions
- */ - private SetThe 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; + } + SetThe world to load regions for
+ * @param worldsSectionThe configuration section listing all worlds and regions
+ * @param worldIdThe 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; + } + + SetAll protected regions for the given world
+ */ + public @NotNull SetA region query
+ */ + public @NotNull RegionQuery getRegionQuery() { + return regionQuery; + } + + /** + * Gets whether debug mode is enabled + * + * @returnTrue 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 configurationThe 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(); + ListThe 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 configurationThe 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 configurationThe configuration to get regions and settings from
+ */ + public WorldGuardListener(@NotNull Configuration configuration) { + this.configuration = configuration; + } + + /** + * Recursively clears and removes entities connected together + * + * @param startEntityThe entity to start with
+ */ + protected void clearInventoriesAndRemoveRecursively(@NotNull Entity startEntity) { + ListThe location the player moved or teleported from
+ * @param toLocationThe location the player moved or teleported to
+ * @param playerThe 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 location1The first location to check
+ * @param location2The second location to check
+ * @returnTrue 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)); + + SetThe entity to check
+ * @returnTrue 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 blockThe block to check
+ * @returnTrue 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 locationThe location to check
+ * @returnTrue 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)); + SetThe world the player is currently in
+ * @param playerRegionsThe regions the player is in or will be in
+ * @returnAll clear regions found in playerRegions
+ */ + private SetThe 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