Unfinished improved configurability
All checks were successful
EpicKnarvik97/ClearOnWorldGuard/pipeline/head This commit looks good

This commit is contained in:
2025-12-17 15:08:00 +01:00
parent 9b69e80e8f
commit f75d74395b
16 changed files with 1059 additions and 102 deletions

33
Jenkinsfile vendored Normal file
View File

@@ -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
}
}
}
}

22
pom.xml
View File

@@ -65,13 +65,27 @@
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<snapshotRepository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -86,5 +100,11 @@
<version>24.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.2.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,6 +1,7 @@
package net.knarcraft.clearonworldguard;
import net.knarcraft.clearonworldguard.config.Configuration;
import net.knarcraft.clearonworldguard.config.ProtectionManager;
import net.knarcraft.clearonworldguard.listener.BlockListener;
import net.knarcraft.clearonworldguard.listener.ClearRegionListener;
import net.knarcraft.clearonworldguard.listener.EntityListener;
@@ -20,6 +21,7 @@ import java.util.logging.Logger;
public final class ClearOnWorldGuard extends JavaPlugin {
private static ClearOnWorldGuard instance;
private ProtectionManager protectionManager;
private boolean debugMode;
@Override
@@ -28,13 +30,15 @@ public final class ClearOnWorldGuard extends JavaPlugin {
this.saveDefaultConfig();
this.reloadConfig();
FileConfiguration fileConfiguration = this.getConfig();
protectionManager = new ProtectionManager("protection");
protectionManager.load(fileConfiguration);
// Register the WorldGuard listener
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);
Bukkit.getPluginManager().registerEvents(new ClearRegionListener(configuration), this);
Bukkit.getPluginManager().registerEvents(new PlayerListener(configuration, protectionManager), this);
Bukkit.getPluginManager().registerEvents(new EntityListener(configuration, protectionManager), this);
Bukkit.getPluginManager().registerEvents(new BlockListener(configuration, protectionManager), this);
Bukkit.getPluginManager().registerEvents(new ClearRegionListener(configuration, protectionManager), this);
this.debugMode = configuration.debugEnabled();
}
@@ -69,4 +73,13 @@ public final class ClearOnWorldGuard extends JavaPlugin {
return instance;
}
/**
* Gets the protection manager
*
* @return <p>The protection manager</p>
*/
public ProtectionManager getProtectionManager() {
return protectionManager;
}
}

View File

@@ -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 Set<BlockIgniteEvent.IgniteCause> exemptIgniteCauses;
private Set<Material> exemptMaterials;
public BlockIgniteProtection() {
}
}

View File

@@ -0,0 +1,130 @@
package net.knarcraft.clearonworldguard.config;
import org.jetbrains.annotations.NotNull;
/**
* Protections that don't directly involve a player, but protects everything in clear region areas
*/
public enum EnvironmentProtection implements Protection {
/**
* Prevents creatures from spawning in clear regions
*/
CREATURE_SPAWN("creatureSpawning", true),
/**
* Prevents droppers or dispensers from dispensing an item
*/
DISPENSE_ITEM("dispenseItem", true),
/**
* Prevents dispensers from dispensing armor
*/
DISPENSE_ARMOR("dispenseArmor", true),
/**
* Prevents blocks from burning
*/
BLOCK_BURN("blockBurn", true),
/**
* Prevents blocks from dropping xp
*/
BLOCK_XP("blockXp", true),
/**
* Prevents blocks from spreading
*/
BLOCK_SPREAD("blockSpread", false),
/**
* Prevents blocks from forming
*/
BLOCK_FORM("entityBlockForm", true),
/**
* Prevents pistons from extending across clear region borders
*/
PISTON_EXTEND_BORDER("pistonExtendBorder", true),
/**
* Prevents pistons from retracting across clear region borders
*/
PISTON_RETRACT_BORDER("pistonRetractBorder", true),
/**
* Prevents block from exploding
*/
BLOCK_EXPLODE("blockExplode", true),
/**
* Prevents entities from priming
*/
EXPLOSION_PRIME("explosionPrime", true),
/**
* Prevents entities from exploding
*/
ENTITY_EXPLODE("entityExplode", true),
/**
* Prevents entities from dropping items on death
*/
ENTITY_DEATH_DROPS("entityDeathDrops", true),
/**
* Prevents projectiles from being launched
*/
PROJECTILE_LAUNCH("projectileLaunch", true),
/**
* Prevents entities from combusting
*/
ENTITY_COMBUST("entityCombust", false),
/**
* Prevents entities from spawning
*/
ENTITY_SPAWN("entitySpawn", false),
/**
* Prevents spawners from spawning creatures
*/
SPAWNER_SPAWN("spawnerSpawn", false),
/**
* Prevents dropped items from spawning
*/
ITEM_SPAWN("itemSpawn", true),
;
private final String path;
private final boolean defaultEnabled;
/**
* Instantiates a new area protection
*
* @param path <p>The relative path of this protection in the configuration file</p>
* @param defaultEnabled <p>Whether this protection is enabled by default</p>
*/
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";
}
}

View File

@@ -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 path <p>The relative path of this protection in the configuration file</p>
* @param defaultEnabled <p>Whether this protection is enabled by default</p>
*/
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 player <p>The player to check</p>
* @return <p>True if the player can bypass this protection</p>
*/
public boolean hasBypassPermission(@NotNull Player player) {
return player.hasPermission("clearonworldguard.bypass." + getPath());
}
}

View File

@@ -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
*
* @return <p>The relative configuration path</p>
*/
@NotNull String getPath();
/**
* Gets whether this protection is enabled by default
*
* @return <p>True if this protection is enabled by default</p>
*/
boolean isDefaultEnabled();
/**
* Gets the configuration file root node used for all protections of this type
*
* @return <p>The configuration file root node</p>
*/
@NotNull String getRootNode();
}

View File

@@ -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 Map<Protection, Boolean> protectionStatus = new HashMap<>();
private final String enabledKey = ".enabled";
private final String rootNode;
/**
* Instantiates a new protection manager
*
* @param rootNode <p>The root node to load protections from</p>
*/
public ProtectionManager(@NotNull String rootNode) {
this.rootNode = rootNode;
}
/**
* Loads the given file configuration
*
* @param configuration <p>The file configuration to load</p>
*/
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 configuration <p>The configuration file to load protections from</p>
* @param protections <p>The protections to load</p>
*/
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 configuration <p>The file configuration to save to</p>
*/
public void save(@NotNull FileConfiguration configuration) {
for (Map.Entry<Protection, Boolean> protectionEntry : protectionStatus.entrySet()) {
Protection protection = protectionEntry.getKey();
configuration.set(getEnabledNode(protection), protectionStatus.get(protection));
}
}
/**
* Checks whether the given protection is currently enabled
*
* @param protection <p>The protection to check</p>
* @return <p>True if the protection is enabled</p>
*/
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 player <p>The player to check</p>
* @param playerProtection <p>The player protection to check</p>
* @return <p>True if the player is able to bypass the protection</p>
*/
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 protection <p>The protection to get the configuration node for</p>
* @return <p>The string path of the configuration node</p>
*/
private @NotNull String getEnabledNode(@NotNull Protection protection) {
return rootNode + "." + protection.getRootNode() + "." + protection.getPath() + enabledKey;
}
}

View File

@@ -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 configuration <p>The configuration to get regions and settings from</p>
* @param configuration <p>The configuration to get regions and settings from</p>
* @param protectionManager <p>The protection manager to use for checking enabled protections</p>
*/
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);
}
}
}

View File

@@ -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 configuration <p>The configuration to get regions and settings from</p>
* @param protectionManager <p>The protection manager for protection management</p>
*/
public ClearRegionListener(@NotNull Configuration configuration) {
super(configuration);
public ClearRegionListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) {
super(configuration, protectionManager);
}
@EventHandler(priority = EventPriority.MONITOR)

View File

@@ -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 configuration <p>The configuration to get regions and settings from</p>
* @param configuration <p>The configuration to get regions and settings from</p>
* @param protectionManager <p>The protection manager to use for checking enabled protections</p>
*/
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);
}
}
}

View File

@@ -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 configuration <p>The configuration to get regions and settings from</p>
* @param configuration <p>The configuration to get regions and settings from</p>
* @param protectionManager <p>The protection manager to use for checking enabled protections</p>
*/
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) {
List<InventoryAction> relevantActions = List.of(InventoryAction.DROP_ALL_CURSOR, InventoryAction.DROP_ALL_SLOT,
InventoryAction.DROP_ONE_CURSOR, InventoryAction.DROP_ONE_SLOT);
HumanEntity entity = event.getWhoClicked();
if (entity instanceof Player player && PlayerRegionTracker.isInClearRegion(player) &&
relevantActions.contains(event.getAction())) {
event.setCancelled(true);
}
}
@@ -129,88 +225,41 @@ public class PlayerListener extends WorldGuardListener {
return false;
}
@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 (PlayerRegionTracker.isInClearRegion(event.getPlayer())) {
ClearOnWorldGuard.logDebugMessage("Prevented player " + event.getPlayer() + " from dropping an item");
event.setCancelled(true);
/**
* Checks and cancels the given event if necessary
*
* @param event <p>The triggered event</p>
* @param protection <p>The protection the event relates to</p>
* @param player <p>The player that triggered the event</p>
*/
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 protection <p>The protection the event relates to</p>
* @param player <p>The player that triggered the event</p>
* @param callback <p>The callback to trigger to apply the protection</p>
*/
private void checkEvent(@NotNull PlayerProtection protection, @NotNull Player player,
@NotNull Consumer<Object> callback) {
if (!isEnabled(protection, player)) {
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);
// Ignore if outside a clear region
if (!PlayerRegionTracker.isInClearRegion(player)) {
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 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);
}
ClearOnWorldGuard.logDebugMessage("Protected against " + protection.getPath() + " by " + player.getName());
callback.accept(null);
}
}

View File

@@ -4,6 +4,9 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter;
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.Protection;
import net.knarcraft.clearonworldguard.config.ProtectionManager;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
@@ -29,14 +32,17 @@ import java.util.logging.Level;
public abstract class WorldGuardListener implements Listener {
private final Configuration configuration;
private final ProtectionManager protectionManager;
/**
* Instantiates a new WorldGuard listener
*
* @param configuration <p>The configuration to get regions and settings from</p>
* @param configuration <p>The configuration to get regions and settings from</p>
* @param protectionManager <p>The protection manager to use for checking enabled protections</p>
*/
public WorldGuardListener(@NotNull Configuration configuration) {
public WorldGuardListener(@NotNull Configuration configuration, @NotNull ProtectionManager protectionManager) {
this.configuration = configuration;
this.protectionManager = protectionManager;
}
/**
@@ -160,6 +166,21 @@ public abstract class WorldGuardListener implements Listener {
return isInClearRegion(block.getLocation());
}
/**
* Checks whether any of the given blocks are in a clear region
*
* @param blocks <p>The blocks to check</p>
* @return <p>True if any of the given blocks are in a clear region</p>
*/
protected boolean isInClearRegion(@NotNull List<Block> blocks) {
for (Block block : blocks) {
if (isInClearRegion(block)) {
return true;
}
}
return false;
}
/**
* Checks whether the given location is in a clear region
*
@@ -200,4 +221,26 @@ public abstract class WorldGuardListener implements Listener {
return configuration.getRegionQuery().getApplicableRegions(BukkitAdapter.adapt(location)).getRegions();
}
/**
* Checks whether the given protection is enabled
*
* @param protection <p>The protection to check</p>
* @return <p>True if the protection is enabled</p>
*/
protected boolean isEnabled(@NotNull Protection protection) {
return protectionManager.isEnabled(protection);
}
/**
* Checks whether the given player protection is enabled
*
* @param protection <p>The protection to check</p>
* @param player <p>The player involved</p>
* @return <p>True if the protection is enabled</p>
*/
protected boolean isEnabled(@NotNull PlayerProtection protection, @NotNull Player player) {
return protectionManager.isEnabled(protection) &&
!protectionManager.canBypass(player, protection);
}
}

View File

@@ -0,0 +1,36 @@
package net.knarcraft.clearonworldguard.util;
import net.knarcraft.clearonworldguard.ClearOnWorldGuard;
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;
/**
* A helper class for parsing list of strings into their proper type
*/
public class ListParser {
/**
* Parses a list of strings into a list of ignite causes
*
* @param igniteCauseNames <p>The names of ignite causes</p>
* @return <p>A list of ignite causes</p>
*/
private @NotNull Set<BlockIgniteEvent.IgniteCause> parseIgniteCauses(@NotNull List<String> igniteCauseNames) {
Set<BlockIgniteEvent.IgniteCause> igniteCauseExemptions = new HashSet<>();
for (String igniteCauseName : igniteCauseNames) {
try {
igniteCauseExemptions.add(BlockIgniteEvent.IgniteCause.valueOf(
igniteCauseName.toUpperCase().replace("-", "_")));
} catch (IllegalArgumentException exception) {
ClearOnWorldGuard.logger().log(Level.WARNING, "Invalid ignite cause exception " + igniteCauseName);
}
}
return igniteCauseExemptions;
}
}

View File

@@ -1,10 +1,267 @@
# The regions for each world that should
clearRegions:
# The name or UUID of a world. Add any regions that should force inventory clearing as a comma-separated list inside
# the square parentheses.
# The name or UUID of a world. Add any clear regions as a comma-separated list inside the square parentheses.
world: [ ]
world_nether: [ ]
world_the_end: [ ]
# Behavior for when players try to enter or exit a clear region
behavior:
# Whether to clear a player's current items when they enter a clear region
clearInventoryOnEntry: false
# Whether to clear a player current items when they exit a clear region
clearInventoryOnExit: true
# Whether to clear XP when a player enters a clear region
clearXPOnEntry: false
# Whether to clear XP when a player exits a clear region
clearXPOnExit: false
# Whether to require a player to have an empty inventory before entering a clear region
requireEmptyInventoryToEnter: true
# Whether to require a player to have no XP before entering a clear region
requireNoXPToEnter: false
# Protection overrides for specific regions
regionProtection:
# Example region protection
# world: # The world the region is in
# example: # The name of a clear region
# player: # Player protection section
# leashing: # The protection to override
# enabled: false ¤ The override itself
# environment: # Environment protection section
# dispenseItem: # The protection to override
# enabled: false ¤ The override itself
# Protection settings for all clear regions all protections have a description describing why they might be needed
protection:
# Protections that might involve a player, and that can be bypassed using clearonworldguard.bypass.name (e.x. clearonworldguard.bypass.itemPickup)
player:
# Prevents player from attaching leashes to mobs
# (leashing entities could be used to export spawned creatures and any items they carry)
leashing:
enabled: true
# Prevents player from picking up items
# (the player could end up with items belonging to a player outside the clear region)
itemPickup:
enabled: true
# Prevents player from dropping items'
# (items could be thrown outside the clear region)
itemDrop:
enabled: true
# Prevents player from accessing ender chests
# (ender chests can be used to store creative items)
enderChest:
enabled: true
# Prevents player from using spawn eggs, unless they've got the bypass permission "mobspawning"
# (spawn eggs can be used to crash the server, or get rare creatures)
spawnEgg:
enabled: true
# Prevent blocks from igniting
# (fire can be used to grief buildings if blockBurn protection is disabled)
blockIgnite:
enabled: true
# Ignite causes that should be allowed
igniteCauseExempt: [
#- ARROW
#- ENDER_CRYSTAL
#- EXPLOSION
#- FIREBALL
#- FLINT_AND_STEEL
#- LAVA
#- LIGHTNING
#- SPREAD
]
# Materials (https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html) players are allowed to ignite.
# A "+" in front of the name specifies a material tag (https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Tag.html).
materialExempt:
- +CANDLES
- +CAMPFIRES
- NETHERRACK
- SOUL_SAND
- SOUL_SOIL
# Prevents entities (like snowmen, or players with frost walker) from forming blocks
# (if snowmen can spawn, they might end up spawning snow everywhere)
entityBlockForm:
enabled: true
# Prevents placement of blocks across clear region borders
# (blocks placed outside clear regions might be valuable, and blocks placed from outside will be lost)
blockPlaceBorder:
enabled: true
# Prevents breaking of blocks across clear region borders
# (blocks broken from outside clear regions won't produce the expected drop, and blocks broken from the inside might
# not normally be destroyable, e.g. bedrock)
blockBreakBorder:
enabled: true
# Blocks any blocks in clear regions from dropping as an item
# (item drops could be picked up by someone outside the clear region)
blockDropItem:
enabled: true
# Prevents TNT from being primed
# (if TNT isn't blocked by other means, TNT could be used to massively grief the clear region)
tntPrime:
enabled: true
# Prevents an entity from mounting another entity
# (mounting an entity could in some cases be used to aid in creature smuggling)
entityMount:
enabled: true
# Prevents an entity from changing a block
# (mobs can grief by changing blocks, or players can crash the server by making sand into falling sand)
entityChangeBlock:
enabled: true
# Prevents entities from damaging each-other across clear region borders
# (entities, including players, could be killed with enchantments the player has spawned in)
entityDamageBorder:
enabled: true
# Prevents entities from damaging other entities in clear regions
# (it might be undesirable to allow players to kill every living creature in a clear region)
entityDamage:
enabled: true
# Whether to only block players from damaging entities
ignoreNonPlayers: true
# Prevents entities from taking damage from blocks
# (blocks can be placed that damage and kill entities, which might not be wanted)
entityDamageBlock:
enabled: true
# Deletes destroyed vehicles to prevent the vehicle or the contents from dropping as items
# (vehicles could potentially drop valuable items when destroyed)
vehicleDestroyDelete:
enabled: true
# Deletes a vehicle leaving a clear region
# (vehicles could potentially be used to transport valuable items out of a clear region)
vehicleBorderDelete:
enabled: true
# Prevents player from dropping items or XP on death, and prevents the player from keeping their inventory
# (if some method allows the player to die, it might drop items that can be picked up by players outside the region)
playerDeathClear:
enabled: true
# Environment protections that cannot be bypassed by individual players
environment:
# Prevents creatures from spawning in a clear regions
# (creature spawning can be used to crash the server)
creatureSpawning:
enabled: true
# Any spawn reasons (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/CreatureSpawnEvent.SpawnReason.html)
# that should be exempt from this protection
spawnReasonExempt:
- BEEHIVE
#- BREEDING
#- BUILD_IRONGOLEM
#- BUILD_SNOWMAN
#- BUILD_WITHER
- COMMAND # Any players with access to the summon command should probably be allowed to
#- CURED
- CUSTOM
#- DEFAULT
#- DISPENSE_EGG
- DROWNED
#- DUPLICATION
#- EGG
#- ENDER_PEARL
#- EXPLOSION
#- FROZEN
#- INFECTION
#- JOCKEY
#- LIGHTNING
#- METAMORPHOSIS
#- MOUNT
- NATURAL
#- NETHER_PORTAL
#- OCELOT_BABY
#- PATROL
#- PIGLIN_ZOMBIFIED
#- RAID
#- REINFORCEMENTS
#- SHEARED
#- SHOULDER_ENTITY
#- SILVERFISH_BLOCK
#- SLIME_SPLIT
#- SPAWNER
- SPAWNER_EGG # This is already restricted with spawnEgg, so any use can be seen as intentional
#- SPELL
#- TRAP
#- VILLAGE_DEFENSE
#- VILLAGE_INVASION
# Any entity types (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/EntityType.html) that should be
# exempt from this protection
entityTypeExempt: [ ]
# Prevents blocks in clear regions from producing xp
# (block xp could be used for farming undeserved experience levels)
blockXp:
enabled: true
# Prevents items from being dispensed in clear regions
# (items could be dispensed outside the region, allowing illegal item export)
dispenseItem:
enabled: true
# Prevent armor from being dispensed in clear regions
# (armor could be dispensed outside the region, allowing illegal item export)
dispenseArmor:
enabled: true
# Prevent blocks from burning and disappearing in clear regions
# (fire can be used to grief buildings)
blockBurn:
enabled: true
# Prevent blocks from spreading in a clear region
# (for example fire spreading can be bad if other fire-related protections are disabled)
blockSpread:
enabled: false
# A more generic event than blockSpread and entityBlockForm, which cancels both
# (lots of blocks forming at once could potentially cause lag, or be used to lava-grief)
blockForm:
enabled: false
# Prevents pistons from extending across clear region borders
# (pistons can push valuable blocks, like netherite blocks, outside the clear region)
pistonExtendBorder:
enabled: true
# Prevents pistons from retracting across clear region borders
# (pistons can pull valuable blocks from a clear region)
pistonRetractBorder:
enabled: true
# Prevents blocks from exploding
# (explosions can be used to grief, and could potentially create item drops)
blockExplode:
enabled: true
# Prevents an entity from being primed
# (entities, like creepers could potentially be used to grief clear regions)
explosionPrime:
enabled: true
# Whether the primed entity should be removed from the game
removeEntity: true
# Prevents an entity from exploding (this might be redundant if explosionPrime protection is enabled)
# (entities, like creepers could potentially be used to grief clear regions)
entityExplode:
enabled: true
# Prevents entities from dropping items or xp on death
# (items dropping outside of clear regions can potentially be picked up, and xp can be farmed)
entityDeathDrops:
enabled: true
# Whether entities should be prevented from dropping xp
preventXP: true
# Whether entities should be prevented from dropping items
preventItems: true
# Prevents players from launching projectiles
# (as launched projectiles might end up outside clear regions, they can potentially damage entities or blocks)
projectileLaunch:
enabled: true
# Prevents entities from combusting
# (combustion could potentially be used to kill entities)
entityCombust:
enabled: false
# Prevents all entity spawning
# (entities spawning can cause lag, and some entities may be used to damage blocks or other entities)
entitySpawn:
enabled: false
# Prevents entities from spawning from spawners
# (if other protection is disabled, spawners might be used to farm XP or specific items)
spawnerSpawn:
enabled: false
# Prevents dropped items from spawning
# (items spawning could cause lag, or could be transported out of the clear region using water)
itemSpawn:
enabled: true
# Prevents players from dropping items from their creative inventory
# (Items dropped by creative players can potentially be picked up by survival players)
creativeItemDrop:
enabled: true
# Whether to enable debug mode, which spams debug messages at log level INFO, rather than log level FINEST
debug: false

View File

@@ -17,4 +17,7 @@ permissions:
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
description: Allows a player to interact with mobs in a clear region
children:
- clearonworldguard.bypass.leashing
- clearonworldguard.bypass.